3 * File containing the ezcMailPart class.
7 * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
8 * @license http://ez.no/licenses/new_bsd New BSD License
12 * Abstract base class for all mail MIME parts.
14 * This base class provides functionality to store headers and to generate
15 * the mail part. Implementations of this class must handle the body of that
16 * parts themselves. They must also implement {@link generateBody()} which is
17 * called when the message part is generated.
19 * @property ezcMailContentDispositionHeader $contentDisposition
20 * Contains the information from the Content-Disposition field of
21 * this mail. This useful especially when you are investigating
22 * retrieved mail to see if a part is an attachment or should be
23 * displayed inline. However, it can also be used to set the same
24 * on outgoing mail. Note that the ezcMailFile part sets the
25 * Content-Disposition field itself based on it's own properties
28 * The size of the mail part in bytes. It is set when parsing a
29 * mail {@link ezcMailParser->parseMail()}.
30 * @property-read ezcMailHeadersHolder $headers
31 * Contains the header holder object, taking care of the
32 * headers of this part. Can be retreived for reasons of
33 * extending this class and its derivals.
38 abstract class ezcMailPart
41 * An associative array containing all the headers set for this part.
43 * @var ezcMailHeadersHolder
45 private $headers = null;
48 * An associative array containing the charsets for the headers in this
51 * @var array(string=>string)
53 private $headerCharsets = array();
56 * An array of headers to exclude when generating the headers.
60 private $excludeHeaders = array();
63 * Holds the properties of this class.
65 * @var array(string=>mixed)
67 protected $properties = array();
70 * Constructs a new mail part.
72 public function __construct()
74 $this->headers
= new ezcMailHeadersHolder();
78 * Sets the property $name to $value.
80 * @throws ezcBasePropertyNotFoundException
81 * if the property does not exist.
82 * @throws ezcBasePropertyPermissionException
83 * if the property is read-only.
88 public function __set( $name, $value )
92 case 'contentDisposition':
94 $this->properties
[$name] = $value;
98 throw new ezcBasePropertyPermissionException( $name, ezcBasePropertyPermissionException
::READ
);
101 throw new ezcBasePropertyNotFoundException( $name );
106 * Returns the property $name.
108 * @throws ezcBasePropertyNotFoundException
109 * if the property does not exist.
110 * @param string $name
114 public function __get( $name )
118 case 'contentDisposition':
120 return isset( $this->properties
[$name] ) ?
$this->properties
[$name] : null;
123 return $this->headers
;
126 throw new ezcBasePropertyNotFoundException( $name );
132 * Returns true if the property $name is set, otherwise false.
134 * @param string $name
138 public function __isset( $name )
142 case 'contentDisposition':
144 return isset( $this->properties
[$name] );
147 return isset( $this->headers
);
155 * Returns the RAW value of the header $name.
157 * Returns an empty string if the header is not found.
158 * Getting headers is case insensitive. Getting the header
159 * 'Message-Id' will match both 'Message-ID' and 'MESSAGE-ID'
160 * as well as 'Message-Id'.
162 * The raw value is MIME-encoded, so if you want to decode it,
163 * use {@link ezcMailTools::mimeDecode()} or implement your own
164 * MIME-decoding function.
166 * If $returnAllValues is true, the function will return all
167 * the values of the header $name from the mail in an array. If
168 * it is false it will return only the first value as a string
169 * if there are multiple values present in the mail.
171 * @param string $name
172 * @param bool $returnAllValues
175 public function getHeader( $name, $returnAllValues = false )
177 if ( isset( $this->headers
[$name] ) )
179 if ( $returnAllValues === true )
181 return $this->headers
[$name];
183 else if ( is_array( $this->headers
[$name] ) )
185 // return only the first value in order to not break compatibility
187 return $this->headers
[$name][0];
191 return $this->headers
[$name];
198 * Sets the header $name to the value $value and its charset to $charset.
200 * If the header is already set it will override the old value.
202 * Headers set should be folded at 76 or 998 characters according to
203 * the folding rules described in RFC 2822.
205 * If $charset is specified, it is associated with the header $name. It
206 * defaults to 'us-ascii' if not specified. The text in $value is encoded
207 * with $charset after calling generateHeaders().
209 * Note: The header Content-Disposition will be overwritten by the
210 * contents of the contentsDisposition property if set.
212 * @see generateHeaders()
214 * @param string $name
215 * @param string $value
216 * @param string $charset
218 public function setHeader( $name, $value, $charset = 'us-ascii' )
220 $this->headers
[$name] = $value;
221 $this->setHeaderCharset( $name, $charset );
225 * Adds the headers $headers.
227 * The headers specified in the associative array $headers will overwrite
228 * any existing header values.
230 * The array $headers can have one of these 2 forms:
231 * - array( header_name => header_value ) - by default the 'us-ascii' charset
232 * will be associated with all headers
233 * - array( header_name => array( header_value, header_charset ) ) - if
234 * header_charset is missing it will default to 'us-ascii'
236 * Headers set should be folded at 76 or 998 characters according to
237 * the folding rules described in RFC 2822.
239 * @param array(string=>mixed) $headers
241 public function setHeaders( array $headers )
243 foreach ( $headers as $key => $value )
245 if ( is_array( $value ) )
247 $this->headers
[$key] = $value[0];
248 $charset = isset( $value[1] ) ?
$value[1] : 'us-ascii';
249 $this->setHeaderCharset( $key, $charset );
253 $this->headers
[$key] = $value;
254 $this->setHeaderCharset( $key );
260 * Returns the headers set for this part as a RFC 822 string.
262 * Each header is separated by a line break.
263 * This method does not add the required two lines of space
264 * to separate the headers from the body of the part.
266 * It also encodes the headers (with the 'Q' encoding) if the charset
267 * associated with the header is different than 'us-ascii' or if it
268 * contains characters not allowed in mail headers.
270 * This function is called automatically by generate() and
271 * subclasses can override this method if they wish to set additional
272 * headers when the mail is generated.
278 public function generateHeaders()
280 // set content disposition header
281 if ( $this->contentDisposition
!== null &&
282 ( $this->contentDisposition
instanceof ezcMailContentDispositionHeader
) )
284 $cdHeader = $this->contentDisposition
;
285 $cd = "{$cdHeader->disposition}";
286 if ( $cdHeader->fileName
!== null )
289 if ( $cdHeader->fileNameCharSet
!== null )
291 $fileInfo .= "*0*=\"{$cdHeader->fileNameCharSet}";
292 if ( $cdHeader->fileNameLanguage
!== null )
294 $fileInfo .= "'{$cdHeader->fileNameLanguage}'";
298 // RFC 2184: the single quote delimiters MUST be present
299 // even when one of the field values is omitted
303 if ( $fileInfo !== null )
305 $cd .= "; filename{$fileInfo}{$cdHeader->fileName}\"";
309 $cd .= "; filename=\"{$cdHeader->fileName}\"";
313 if ( $cdHeader->creationDate
!== null )
315 $cd .= "; creation-date=\"{$cdHeader->creationDate}\"";
318 if ( $cdHeader->modificationDate
!== null )
320 $cd .= "; modification-date=\"{$cdHeader->modificationDate}\"";
323 if ( $cdHeader->readDate
!== null )
325 $cd .= "; read-date=\"{$cdHeader->readDate}\"";
328 if ( $cdHeader->size
!== null )
330 $cd .= "; size={$cdHeader->size}";
333 foreach ( $cdHeader->additionalParameters
as $addKey => $addValue )
335 $cd .="; {$addKey}=\"{$addValue}\"";
338 $this->setHeader( 'Content-Disposition', $cd );
343 foreach ( $this->headers
->getCaseSensitiveArray() as $header => $value )
345 if ( is_array( $value ) )
350 // here we encode every header, even the ones that we don't add to
351 // the header set directly. We do that so that transports sill see
352 // all the encoded headers which they then can use accordingly.
353 $charset = $this->getHeaderCharset( $header );
354 switch ( strtolower( $charset ) )
357 $value = ezcMailHeaderFolder
::foldAny( $value );
360 case 'iso-8859-1': case 'iso-8859-2': case 'iso-8859-3': case 'iso-8859-4':
361 case 'iso-8859-5': case 'iso-8859-6': case 'iso-8859-7': case 'iso-8859-8':
362 case 'iso-8859-9': case 'iso-8859-10': case 'iso-8859-11': case 'iso-8859-12':
363 case 'iso-8859-13': case 'iso-8859-14': case 'iso-8859-15' :case 'iso-8859-16':
364 case 'windows-1250': case 'windows-1251': case 'windows-1252':
366 if ( strpbrk( $value, "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" ) === false )
368 $value = ezcMailHeaderFolder
::foldAny( $value );
371 // break intentionally missing
374 $preferences = array(
375 'input-charset' => $charset,
376 'output-charset' => $charset,
377 'line-length' => ezcMailHeaderFolder
::getLimit(),
379 'line-break-chars' => ezcMailTools
::lineBreak()
381 $value = iconv_mime_encode( 'dummy', $value, $preferences );
382 $value = substr( $value, 7 ); // "dummy: " + 1
384 // just to keep compatibility with code which might read
385 // the headers after generateHeaders() has been called
386 $this->setHeader( $header, $value, $charset );
390 if ( in_array( strtolower( $header ), $this->excludeHeaders
) === false )
392 $text .= "$header: $value" . ezcMailTools
::lineBreak();
400 * The array $headers will be excluded when the headers are generated.
402 * @see generateHeaders()
404 * @param array(string) $headers
406 public function appendExcludeHeaders( array $headers )
408 $lowerCaseHeaders = array();
409 foreach ( $headers as $header )
411 $lowerCaseHeaders[] = strtolower( $header );
413 $this->excludeHeaders
= array_merge( $this->excludeHeaders
, $lowerCaseHeaders );
417 * Returns the body of this part as a string.
419 * This method is called automatically by generate() and subclasses must
424 abstract public function generateBody();
427 * Returns the complete mail part including both the header and the body
432 public function generate()
434 return $this->generateHeaders() . ezcMailTools
::lineBreak() . $this->generateBody();
438 * Returns the charset registered for the header $name.
440 * @param string $name
443 protected function getHeaderCharset( $name )
445 if ( isset( $this->headerCharsets
[$name] ) )
447 return $this->headerCharsets
[$name];
450 // if no charset is set then return 'us-ascii'
455 * Sets the charset of the header $name to $value.
457 * If $value is not specified it defaults to 'us-ascii'.
459 * @param string $name
460 * @param string $value
462 protected function setHeaderCharset( $name, $value = 'us-ascii' )
464 $this->headerCharsets
[$name] = $value;