commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-new / civicrm / packages / ezc / Mail / src / mail.php
1 <?php
2 /**
3 * File containing the ezcMail class
4 *
5 * @package Mail
6 * @version 1.7beta1
7 * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
8 * @license http://ez.no/licenses/new_bsd New BSD License
9 */
10
11 /**
12 * The main mail class.
13 *
14 * You can use ezcMail together with the other classes derived from ezcMailPart
15 * to build email messages. When the mail is built, use the Transport classes
16 * to send the mail.
17 *
18 * This example builds and sends a simple text mail message:
19 * <code>
20 * $mail = new ezcMail;
21 * $mail->from = new ezcMailAddress( 'sender@example.com', 'Adrian Ripburger' );
22 * $mail->addTo( new ezcMailAddress( 'receiver@example.com', 'Maureen Corley' ) );
23 * $mail->subject = "Hi";
24 * $mail->body = new ezcMailText( "I just mail to say I love you!" );
25 * $transport = new ezcMailMtaTransport();
26 * $transport->send( $mail );
27 * </code>
28 *
29 * You can also derive your own mail classes from this class if you have
30 * special requirements. An example of this is the ezcMailComposer class which
31 * is a convenience class to send simple mail structures and HTML mail.
32 *
33 * There are several headers you can set on the mail object to achieve various
34 * effects:
35 * - Reply-To - Set this to an email address if you want people to reply to an
36 * address other than the from address.
37 * - Errors-To - If the mail can not be delivered the error message will be
38 * sent to this address.
39 *
40 * @property ezcMailAddress $from Contains the from address as an
41 * ezcMailAddress object.
42 * @property array(ezcMailAddress) $to Contains an array of ezcMailAddress objects.
43 * @property array(ezcMailAddress) $cc Contains an array of ezcMailAddress objects.
44 * @property array(ezcMailAddress) $bcc Contains an array of ezcMailAddress objects.
45 * @property string $subject
46 * Contains the subject of the e-mail.
47 * Use setSubject if you require a
48 * special encoding.
49 * @property string $subjectCharset
50 * The encoding of the subject.
51 * @property ezcMailPart $body The body part of the message.
52 *
53 * @property-read string $messageId
54 * The message ID of the message. Treat
55 * as read-only unless you're 100% sure
56 * what you're doing. Also accessible through
57 * the deprecated property messageID.
58 * @property-read integer $timestamp
59 * The date/time of when the message was
60 * sent as Unix Timestamp.
61 * @property ezcMailAddress $returnPath Contains the Return-Path address as an
62 * ezcMailAddress object.
63 *
64 * @apichange Remove the support for the deprecated property messageID.
65 *
66 * @package Mail
67 * @version 1.7beta1
68 * @mainclass
69 */
70 class ezcMail extends ezcMailPart
71 {
72 /**
73 * 7 bit encoding.
74 */
75 const SEVEN_BIT = "7bit";
76
77 /**
78 * 8 bit encoding.
79 */
80 const EIGHT_BIT = "8bit";
81
82 /**
83 * Binary encoding.
84 */
85 const BINARY = "binary";
86
87 /**
88 * Quoted printable encoding.
89 */
90 const QUOTED_PRINTABLE = "quoted-printable";
91
92 /**
93 * Base 64 encoding.
94 */
95 const BASE64 = "base64";
96
97 /**
98 * Constructs an empty ezcMail object.
99 */
100 public function __construct()
101 {
102 parent::__construct();
103
104 $this->properties['from'] = null;
105 $this->properties['to'] = array();
106 $this->properties['cc'] = array();
107 $this->properties['bcc'] = array();
108 $this->properties['subject'] = null;
109 $this->properties['subjectCharset'] = 'us-ascii';
110 $this->properties['body'] = null;
111 $this->properties['messageId'] = null;
112 $this->properties['returnPath'] = null;
113 }
114
115 /**
116 * Sets the property $name to $value.
117 *
118 * @throws ezcBasePropertyNotFoundException
119 * if the property does not exist.
120 * @throws ezcBasePropertyPermissionException
121 * if the property is read-only.
122 * @param string $name
123 * @param mixed $value
124 * @ignore
125 */
126 public function __set( $name, $value )
127 {
128 switch ( $name )
129 {
130 case 'from':
131 case 'returnPath':
132 if ( $value !== null && !$value instanceof ezcMailAddress )
133 {
134 throw new ezcBaseValueException( $name, $value, 'ezcMailAddress or null' );
135 }
136 $this->properties[$name] = $value;
137 break;
138
139 case 'to':
140 case 'cc':
141 case 'bcc':
142 if ( !is_array( $value ) )
143 {
144 throw new ezcBaseValueException( $name, $value, 'array( ezcMailAddress )' );
145 }
146 foreach ( $value as $key => $obj )
147 {
148 if ( !$obj instanceof ezcMailAddress )
149 {
150 throw new ezcBaseValueException( "{$name}[{$key}]", $obj, 'ezcMailAddress' );
151 }
152 }
153 $this->properties[$name] = $value;
154 break;
155
156 case 'subject':
157 $this->properties['subject'] = trim( $value );
158 break;
159
160 case 'subjectCharset':
161 $this->properties['subjectCharset'] = $value;
162 break;
163
164 case 'body':
165 if ( !$value instanceof ezcMailPart )
166 {
167 throw new ezcBaseValueException( $name, $value, 'ezcMailPart' );
168 }
169 $this->properties['body'] = $value;
170 break;
171
172 case 'messageId':
173 case 'messageID':
174 $this->properties['messageId'] = $value;
175 break;
176
177 case 'timestamp':
178 throw new ezcBasePropertyPermissionException( $name, ezcBasePropertyPermissionException::READ );
179 break;
180
181 default:
182 parent::__set( $name, $value );
183 break;
184 }
185 }
186
187 /**
188 * Returns the property $name.
189 *
190 * @throws ezcBasePropertyNotFoundException
191 * if the property does not exist.
192 * @param string $name
193 * @return mixed
194 * @ignore
195 */
196 public function __get( $name )
197 {
198 switch ( $name )
199 {
200 case 'to':
201 case 'cc':
202 case 'bcc':
203 return (array) $this->properties[$name];
204
205 case 'from':
206 case 'subject':
207 case 'subjectCharset':
208 case 'body':
209 case 'messageId':
210 case 'returnPath':
211 return $this->properties[$name];
212
213 case 'messageID': // deprecated version
214 return $this->properties['messageId'];
215
216 case 'timestamp':
217 return strtotime( $this->getHeader( "Date" ) );
218
219 default:
220 return parent::__get( $name );
221 }
222 }
223
224 /**
225 * Returns true if the property $name is set, otherwise false.
226 *
227 * @param string $name
228 * @return bool
229 * @ignore
230 */
231 public function __isset( $name )
232 {
233 switch ( $name )
234 {
235 case 'to':
236 case 'cc':
237 case 'bcc':
238 case 'from':
239 case 'subject':
240 case 'subjectCharset':
241 case 'body':
242 case 'messageId':
243 case 'returnPath':
244 return isset( $this->properties[$name] );
245
246 case 'messageID': // deprecated version
247 return isset( $this->properties['messageId'] );
248
249 case 'timestamp':
250 return $this->getHeader( "Date" ) != null;
251
252 default:
253 return parent::__isset( $name );
254 }
255 }
256
257 /**
258 * Adds the ezcMailAddress $address to the list of 'to' recipients.
259 *
260 * @param ezcMailAddress $address
261 */
262 public function addTo( ezcMailAddress $address )
263 {
264 $this->properties['to'][] = $address;
265 }
266
267 /**
268 * Adds the ezcMailAddress $address to the list of 'cc' recipients.
269 *
270 * @param ezcMailAddress $address
271 */
272 public function addCc( ezcMailAddress $address )
273 {
274 $this->properties['cc'][] = $address;
275 }
276
277 /**
278 * Adds the ezcMailAddress $address to the list of 'bcc' recipients.
279 *
280 * @param ezcMailAddress $address
281 */
282 public function addBcc( ezcMailAddress $address )
283 {
284 $this->properties['bcc'][] = $address;
285 }
286
287 /**
288 * Returns the generated body part of this mail.
289 *
290 * Returns an empty string if no body has been set.
291 *
292 * @return string
293 */
294 public function generateBody()
295 {
296 if ( is_subclass_of( $this->body, 'ezcMailPart' ) )
297 {
298 return $this->body->generateBody();
299 }
300 return '';
301 }
302
303 /**
304 * Returns the generated headers for the mail.
305 *
306 * This method is called automatically when the mail message is built.
307 * You can re-implement this method in subclasses if you wish to set
308 * different mail headers than ezcMail.
309 *
310 * @return string
311 */
312 public function generateHeaders()
313 {
314 // set our headers first.
315 if ( $this->from !== null )
316 {
317 $this->setHeader( "From", ezcMailTools::composeEmailAddress( $this->from ) );
318 }
319
320 if ( $this->to !== null )
321 {
322 $this->setHeader( "To", ezcMailTools::composeEmailAddresses( $this->to ) );
323 }
324 if ( count( $this->cc ) )
325 {
326 $this->setHeader( "Cc", ezcMailTools::composeEmailAddresses( $this->cc ) );
327 }
328 if ( count( $this->bcc ) )
329 {
330 $this->setHeader( "Bcc", ezcMailTools::composeEmailAddresses( $this->bcc ) );
331 }
332
333 $this->setHeader( 'Subject', $this->subject, $this->subjectCharset );
334
335 $this->setHeader( 'MIME-Version', '1.0' );
336 $this->setHeader( 'User-Agent', 'eZ Components' );
337 $this->setHeader( 'Date', date( 'r' ) );
338 $idhost = $this->from != null && $this->from->email != '' ? $this->from->email : 'localhost';
339 if ( is_null( $this->messageId ) )
340 {
341 $this->setHeader( 'Message-Id', '<' . ezcMailTools::generateMessageId( $idhost ) . '>' );
342 }
343 else
344 {
345 $this->setHeader( 'Message-Id', $this->messageID );
346 }
347
348 // if we have a body part, include the headers of the body
349 if ( is_subclass_of( $this->body, "ezcMailPart" ) )
350 {
351 return parent::generateHeaders() . $this->body->generateHeaders();
352 }
353 return parent::generateHeaders();
354 }
355
356 /**
357 * Returns an array of mail parts from the current mail.
358 *
359 * The array returned contains objects of classes:
360 * - ezcMailText
361 * - ezcMailFile
362 * - ezcMailRfc822Digest
363 * If the method is called with $includeDigests as true, then the returned
364 * array will not contain ezcMailRfc822Digest objects, but instead the mail
365 * parts inside the digests.
366 * The parameter $filter can be used to restrict the returned mail parts,
367 * eg. $filter = array( 'ezcMailFile' ) to return only file mail parts.
368 *
369 * A typical use for this function is to get a list of attachments from a mail.
370 * Example:
371 * <code>
372 * // $mail is an ezcMail object
373 * $parts = $mail->fetchParts();
374 * // after the above line is executed, $parts will contain an array of mail parts objects,
375 * // for example one ezcMailText object ($parts[0]) and two ezcMailRfc822Digest objects ($parts[1] and $parts[2]).
376 * // the ezcMailText object will be used to render the mail text, and the
377 * // other two objects will be displayed as links ("view attachment")
378 *
379 * // when user clicks on one of the two attachments, the parts of that attachment
380 * // must be retrieved in order to render the attached digest:
381 * $subparts = $parts[1]->mail->fetchParts();
382 * // after the above line is executed, $subparts will contain an array of mail parts objects,
383 * // for example one ezcMailText object and one ezcMailFile object
384 * </code>
385 *
386 * @param array(string) $filter
387 * @param bool $includeDigests
388 * @return array(ezcMailPart)
389 */
390 public function fetchParts( $filter = null, $includeDigests = false )
391 {
392 $context = new ezcMailPartWalkContext( array( __CLASS__, 'collectPart' ) );
393 $context->includeDigests = $includeDigests;
394 $context->filter = $filter;
395 $context->level = 0;
396 $this->walkParts( $context, $this );
397 return $context->getParts();
398 }
399
400 /**
401 * Walks recursively through the mail parts in the specified mail object.
402 *
403 * $context is an object of class ezcMailPartWalkContext, which must contain
404 * a valid callback function name to be applied to all mail parts. You can use
405 * the collectPart() method, or create your own callback function which can
406 * for example save the mail parts to disk or to a database.
407 *
408 * For the properties you can set to the walk context see: {@link ezcMailPartWalkContext}
409 *
410 * Example:
411 * <code>
412 * class App
413 * {
414 * public static function saveMailPart( $context, $mailPart )
415 * {
416 * // code to save the $mailPart object to disk
417 * }
418 * }
419 *
420 * // use the saveMailPart() function as a callback in walkParts()
421 * // where $mail is an ezcMail object.
422 * $context = new ezcMailPartWalkContext( array( 'App', 'saveMailPart' ) );
423 * $context->includeDigests = true; // if you want to go through the digests in the mail
424 * $mail->walkParts( $context, $mail );
425 * </code>
426 *
427 * @param ezcMailPartWalkContext $context
428 * @param ezcMailPart $mail
429 */
430 public function walkParts( ezcMailPartWalkContext $context, ezcMailPart $mail )
431 {
432 $className = get_class( $mail );
433 $context->level++;
434 switch ( $className )
435 {
436 case 'ezcMail':
437 case 'ezcMailComposer':
438 if ( $mail->body !== null )
439 {
440 $this->walkParts( $context, $mail->body );
441 }
442 break;
443
444 case 'ezcMailMultipartMixed':
445 case 'ezcMailMultipartAlternative':
446 case 'ezcMailMultipartDigest':
447 case 'ezcMailMultipartReport':
448 foreach ( $mail->getParts() as $part )
449 {
450 $this->walkParts( $context, $part );
451 }
452 break;
453
454 case 'ezcMailMultipartRelated':
455 $this->walkParts( $context, $mail->getMainPart() );
456 foreach ( $mail->getRelatedParts() as $part )
457 {
458 $this->walkParts( $context, $part );
459 }
460 break;
461
462 case 'ezcMailRfc822Digest':
463 if ( $context->includeDigests )
464 {
465 $this->walkParts( $context, $mail->mail );
466 }
467 elseif ( empty( $context->filter ) || in_array( $className, $context->filter ) )
468 {
469 call_user_func( $context->callbackFunction, $context, $mail );
470 }
471 break;
472
473 case 'ezcMailText':
474 case 'ezcMailFile':
475 case 'ezcMailDeliveryStatus':
476 if ( empty( $context->filter ) || in_array( $className, $context->filter ) )
477 {
478 call_user_func( $context->callbackFunction, $context, $mail );
479 }
480 break;
481
482 default:
483 // for cases where a custom mail class has been specified with $parser->options->mailClass
484 if ( in_array( 'ezcMail', class_parents( $className ) ) )
485 {
486 if ( $mail->body !== null )
487 {
488 $this->walkParts( $context, $mail->body );
489 }
490 }
491
492 // for cases where a custom file class has been specified with $parser->options->fileClass
493 if ( in_array( 'ezcMailFile', class_parents( $className ) ) )
494 {
495 if ( empty( $context->filter ) || in_array( $className, $context->filter ) )
496 {
497 call_user_func( $context->callbackFunction, $context, $mail );
498 }
499 }
500 }
501 $context->level--;
502 }
503
504 /**
505 * Saves $mail in the $context object.
506 *
507 * This function is used as a callback in the fetchParts() method.
508 *
509 * @param ezcMailPartWalkContext $context
510 * @param ezcMailPart $mail
511 */
512 protected static function collectPart( ezcMailPartWalkContext $context, ezcMailPart $mail )
513 {
514 $context->appendPart( $mail );
515 }
516 }
517 ?>