commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-new / civicrm / packages / ezc / Mail / src / interfaces / part.php
1 <?php
2 /**
3 * File containing the ezcMailPart 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 * Abstract base class for all mail MIME parts.
13 *
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.
18 *
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
26 * when sending mail.
27 * @property int $size
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.
34 *
35 * @package Mail
36 * @version 1.7beta1
37 */
38 abstract class ezcMailPart
39 {
40 /**
41 * An associative array containing all the headers set for this part.
42 *
43 * @var ezcMailHeadersHolder
44 */
45 private $headers = null;
46
47 /**
48 * An associative array containing the charsets for the headers in this
49 * part.
50 *
51 * @var array(string=>string)
52 */
53 private $headerCharsets = array();
54
55 /**
56 * An array of headers to exclude when generating the headers.
57 *
58 * @var array(string)
59 */
60 private $excludeHeaders = array();
61
62 /**
63 * Holds the properties of this class.
64 *
65 * @var array(string=>mixed)
66 */
67 protected $properties = array();
68
69 /**
70 * Constructs a new mail part.
71 */
72 public function __construct()
73 {
74 $this->headers = new ezcMailHeadersHolder();
75 }
76
77 /**
78 * Sets the property $name to $value.
79 *
80 * @throws ezcBasePropertyNotFoundException
81 * if the property does not exist.
82 * @throws ezcBasePropertyPermissionException
83 * if the property is read-only.
84 * @param string $name
85 * @param mixed $value
86 * @ignore
87 */
88 public function __set( $name, $value )
89 {
90 switch ( $name )
91 {
92 case 'contentDisposition':
93 case 'size':
94 $this->properties[$name] = $value;
95 break;
96
97 case 'headers':
98 throw new ezcBasePropertyPermissionException( $name, ezcBasePropertyPermissionException::READ );
99
100 default:
101 throw new ezcBasePropertyNotFoundException( $name );
102 }
103 }
104
105 /**
106 * Returns the property $name.
107 *
108 * @throws ezcBasePropertyNotFoundException
109 * if the property does not exist.
110 * @param string $name
111 * @return mixed
112 * @ignore
113 */
114 public function __get( $name )
115 {
116 switch ( $name )
117 {
118 case 'contentDisposition':
119 case 'size':
120 return isset( $this->properties[$name] ) ? $this->properties[$name] : null;
121
122 case "headers":
123 return $this->headers;
124
125 default:
126 throw new ezcBasePropertyNotFoundException( $name );
127
128 }
129 }
130
131 /**
132 * Returns true if the property $name is set, otherwise false.
133 *
134 * @param string $name
135 * @return bool
136 * @ignore
137 */
138 public function __isset( $name )
139 {
140 switch ( $name )
141 {
142 case 'contentDisposition':
143 case 'size':
144 return isset( $this->properties[$name] );
145
146 case "headers":
147 return isset( $this->headers );
148
149 default:
150 return false;
151 }
152 }
153
154 /**
155 * Returns the RAW value of the header $name.
156 *
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'.
161 *
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.
165 *
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.
170 *
171 * @param string $name
172 * @param bool $returnAllValues
173 * @return mixed
174 */
175 public function getHeader( $name, $returnAllValues = false )
176 {
177 if ( isset( $this->headers[$name] ) )
178 {
179 if ( $returnAllValues === true )
180 {
181 return $this->headers[$name];
182 }
183 else if ( is_array( $this->headers[$name] ) )
184 {
185 // return only the first value in order to not break compatibility
186 // see issue #14257
187 return $this->headers[$name][0];
188 }
189 else
190 {
191 return $this->headers[$name];
192 }
193 }
194 return '';
195 }
196
197 /**
198 * Sets the header $name to the value $value and its charset to $charset.
199 *
200 * If the header is already set it will override the old value.
201 *
202 * Headers set should be folded at 76 or 998 characters according to
203 * the folding rules described in RFC 2822.
204 *
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().
208 *
209 * Note: The header Content-Disposition will be overwritten by the
210 * contents of the contentsDisposition property if set.
211 *
212 * @see generateHeaders()
213 *
214 * @param string $name
215 * @param string $value
216 * @param string $charset
217 */
218 public function setHeader( $name, $value, $charset = 'us-ascii' )
219 {
220 $this->headers[$name] = $value;
221 $this->setHeaderCharset( $name, $charset );
222 }
223
224 /**
225 * Adds the headers $headers.
226 *
227 * The headers specified in the associative array $headers will overwrite
228 * any existing header values.
229 *
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'
235 *
236 * Headers set should be folded at 76 or 998 characters according to
237 * the folding rules described in RFC 2822.
238 *
239 * @param array(string=>mixed) $headers
240 */
241 public function setHeaders( array $headers )
242 {
243 foreach ( $headers as $key => $value )
244 {
245 if ( is_array( $value ) )
246 {
247 $this->headers[$key] = $value[0];
248 $charset = isset( $value[1] ) ? $value[1] : 'us-ascii';
249 $this->setHeaderCharset( $key, $charset );
250 }
251 else
252 {
253 $this->headers[$key] = $value;
254 $this->setHeaderCharset( $key );
255 }
256 }
257 }
258
259 /**
260 * Returns the headers set for this part as a RFC 822 string.
261 *
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.
265 *
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.
269 *
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.
273 *
274 * @see setHeader()
275 *
276 * @return string
277 */
278 public function generateHeaders()
279 {
280 // set content disposition header
281 if ( $this->contentDisposition !== null &&
282 ( $this->contentDisposition instanceof ezcMailContentDispositionHeader ) )
283 {
284 $cdHeader = $this->contentDisposition;
285 $cd = "{$cdHeader->disposition}";
286 if ( $cdHeader->fileName !== null )
287 {
288 $fileInfo = null;
289 if ( $cdHeader->fileNameCharSet !== null )
290 {
291 $fileInfo .= "*0*=\"{$cdHeader->fileNameCharSet}";
292 if ( $cdHeader->fileNameLanguage !== null )
293 {
294 $fileInfo .= "'{$cdHeader->fileNameLanguage}'";
295 }
296 else
297 {
298 // RFC 2184: the single quote delimiters MUST be present
299 // even when one of the field values is omitted
300 $fileInfo .= "''";
301 }
302 }
303 if ( $fileInfo !== null )
304 {
305 $cd .= "; filename{$fileInfo}{$cdHeader->fileName}\"";
306 }
307 else
308 {
309 $cd .= "; filename=\"{$cdHeader->fileName}\"";
310 }
311 }
312
313 if ( $cdHeader->creationDate !== null )
314 {
315 $cd .= "; creation-date=\"{$cdHeader->creationDate}\"";
316 }
317
318 if ( $cdHeader->modificationDate !== null )
319 {
320 $cd .= "; modification-date=\"{$cdHeader->modificationDate}\"";
321 }
322
323 if ( $cdHeader->readDate !== null )
324 {
325 $cd .= "; read-date=\"{$cdHeader->readDate}\"";
326 }
327
328 if ( $cdHeader->size !== null )
329 {
330 $cd .= "; size={$cdHeader->size}";
331 }
332
333 foreach ( $cdHeader->additionalParameters as $addKey => $addValue )
334 {
335 $cd .="; {$addKey}=\"{$addValue}\"";
336 }
337
338 $this->setHeader( 'Content-Disposition', $cd );
339 }
340
341 // generate headers
342 $text = "";
343 foreach ( $this->headers->getCaseSensitiveArray() as $header => $value )
344 {
345 if ( is_array( $value ) )
346 {
347 $value = $value[0];
348 }
349
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 ) )
355 {
356 case 'us-ascii':
357 $value = ezcMailHeaderFolder::foldAny( $value );
358 break;
359
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':
365 case 'utf-8':
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 )
367 {
368 $value = ezcMailHeaderFolder::foldAny( $value );
369 break;
370 }
371 // break intentionally missing
372
373 default:
374 $preferences = array(
375 'input-charset' => $charset,
376 'output-charset' => $charset,
377 'line-length' => ezcMailHeaderFolder::getLimit(),
378 'scheme' => 'Q',
379 'line-break-chars' => ezcMailTools::lineBreak()
380 );
381 $value = iconv_mime_encode( 'dummy', $value, $preferences );
382 $value = substr( $value, 7 ); // "dummy: " + 1
383
384 // just to keep compatibility with code which might read
385 // the headers after generateHeaders() has been called
386 $this->setHeader( $header, $value, $charset );
387 break;
388 }
389
390 if ( in_array( strtolower( $header ), $this->excludeHeaders ) === false )
391 {
392 $text .= "$header: $value" . ezcMailTools::lineBreak();
393 }
394 }
395
396 return $text;
397 }
398
399 /**
400 * The array $headers will be excluded when the headers are generated.
401 *
402 * @see generateHeaders()
403 *
404 * @param array(string) $headers
405 */
406 public function appendExcludeHeaders( array $headers )
407 {
408 $lowerCaseHeaders = array();
409 foreach ( $headers as $header )
410 {
411 $lowerCaseHeaders[] = strtolower( $header );
412 }
413 $this->excludeHeaders = array_merge( $this->excludeHeaders, $lowerCaseHeaders );
414 }
415
416 /**
417 * Returns the body of this part as a string.
418 *
419 * This method is called automatically by generate() and subclasses must
420 * implement it.
421 *
422 * @return string
423 */
424 abstract public function generateBody();
425
426 /**
427 * Returns the complete mail part including both the header and the body
428 * as a string.
429 *
430 * @return string
431 */
432 public function generate()
433 {
434 return $this->generateHeaders() . ezcMailTools::lineBreak() . $this->generateBody();
435 }
436
437 /**
438 * Returns the charset registered for the header $name.
439 *
440 * @param string $name
441 * @return string
442 */
443 protected function getHeaderCharset( $name )
444 {
445 if ( isset( $this->headerCharsets[$name] ) )
446 {
447 return $this->headerCharsets[$name];
448 }
449
450 // if no charset is set then return 'us-ascii'
451 return 'us-ascii';
452 }
453
454 /**
455 * Sets the charset of the header $name to $value.
456 *
457 * If $value is not specified it defaults to 'us-ascii'.
458 *
459 * @param string $name
460 * @param string $value
461 */
462 protected function setHeaderCharset( $name, $value = 'us-ascii' )
463 {
464 $this->headerCharsets[$name] = $value;
465 }
466 }
467 ?>