Overview

Packages

  • Mime

Classes

  • Horde_Mime
  • Horde_Mime_Address
  • Horde_Mime_Exception
  • Horde_Mime_Headers
  • Horde_Mime_Magic
  • Horde_Mime_Mail
  • Horde_Mime_Mdn
  • Horde_Mime_Part
  • Horde_Mime_Translation
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The Horde_Mime_Mail:: class wraps around the various MIME library classes
  4:  * to provide a simple interface for creating and sending MIME messages.
  5:  *
  6:  * All content has to be passed UTF-8 encoded. The charset parameters is used
  7:  * for the generated message only.
  8:  *
  9:  * Copyright 2007-2012 Horde LLC (http://www.horde.org/)
 10:  *
 11:  * See the enclosed file COPYING for license information (LGPL). If you
 12:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 13:  *
 14:  * @author   Jan Schneider <jan@horde.org>
 15:  * @category Horde
 16:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 17:  * @package  Mime
 18:  */
 19: class Horde_Mime_Mail
 20: {
 21:     /**
 22:      * The message headers.
 23:      *
 24:      * @var Horde_Mime_Headers
 25:      */
 26:     protected $_headers;
 27: 
 28:     /**
 29:      * The base MIME part.
 30:      *
 31:      * @var Horde_Mime_Part
 32:      */
 33:     protected $_base;
 34: 
 35:     /**
 36:      * The main body part.
 37:      *
 38:      * @var Horde_Mime_Part
 39:      */
 40:     protected $_body;
 41: 
 42:     /**
 43:      * The main HTML body part.
 44:      *
 45:      * @var Horde_Mime_Part
 46:      */
 47:     protected $_htmlBody;
 48: 
 49:     /**
 50:      * The message recipients.
 51:      *
 52:      * @var array
 53:      */
 54:     protected $_recipients = array();
 55: 
 56:     /**
 57:      * Bcc recipients.
 58:      *
 59:      * @var string
 60:      */
 61:     protected $_bcc;
 62: 
 63:     /**
 64:      * All MIME parts except the main body part.
 65:      *
 66:      * @var array
 67:      */
 68:     protected $_parts = array();
 69: 
 70:     /**
 71:      * The Mail driver name.
 72:      *
 73:      * @link http://pear.php.net/Mail
 74:      * @var string
 75:      */
 76:     protected $_mailer_driver = 'smtp';
 77: 
 78:     /**
 79:      * The charset to use for the message.
 80:      *
 81:      * @var string
 82:      */
 83:     protected $_charset = 'UTF-8';
 84: 
 85:     /**
 86:      * The Mail driver parameters.
 87:      *
 88:      * @link http://pear.php.net/Mail
 89:      * @var array
 90:      */
 91:     protected $_mailer_params = array();
 92: 
 93:     /**
 94:      * Constructor.
 95:      *
 96:      * @param array $params  A hash with basic message information. 'charset'
 97:      *                       is the character set of the message.  'body' is
 98:      *                       the message body. All other parameters are
 99:      *                       assumed to be message headers.
100:      *
101:      * @throws Horde_Mime_Exception
102:      */
103:     public function __construct($params = array())
104:     {
105:         /* Set SERVER_NAME. */
106:         if (!isset($_SERVER['SERVER_NAME'])) {
107:             $_SERVER['SERVER_NAME'] = php_uname('n');
108:         }
109: 
110:         $this->_headers = new Horde_Mime_Headers();
111: 
112:         if (isset($params['charset'])) {
113:             $this->_charset = $params['charset'];
114:             unset($params['charset']);
115:         }
116: 
117:         if (isset($params['body'])) {
118:             $this->setBody($params['body'], $this->_charset);
119:             unset($params['body']);
120:         }
121: 
122:         $this->addHeaders($params);
123:     }
124: 
125:     /**
126:      * Adds several message headers at once.
127:      *
128:      * @param array $header    Hash with header names as keys and header
129:      *                         contents as values.
130:      *
131:      * @throws Horde_Mime_Exception
132:      */
133:     public function addHeaders($headers = array())
134:     {
135:         foreach ($headers as $header => $value) {
136:             $this->addHeader($header, $value);
137:         }
138:     }
139: 
140:     /**
141:      * Adds a message header.
142:      *
143:      * @param string $header      The header name.
144:      * @param string $value       The header value.
145:      * @param string $charset     The header value's charset.
146:      * @param boolean $overwrite  If true, an existing header of the same name
147:      *                            is being overwritten; if false, multiple
148:      *                            headers are added; if null, the correct
149:      *                            behaviour is automatically chosen depending
150:      *                            on the header name.
151:      *
152:      * @throws Horde_Mime_Exception
153:      */
154:     public function addHeader($header, $value, $overwrite = null)
155:     {
156:         $lc_header = Horde_String::lower($header);
157: 
158:         if (is_null($overwrite) &&
159:             in_array($lc_header, $this->_headers->singleFields(true))) {
160:             $overwrite = true;
161:         }
162: 
163:         if ($overwrite) {
164:             $this->_headers->removeHeader($header);
165:         }
166: 
167:         if ($lc_header === 'bcc') {
168:             $this->_bcc = $value;
169:         } else {
170:             $this->_headers->addHeader($header, $value);
171:         }
172:     }
173: 
174:     /**
175:      * Removes a message header.
176:      *
177:      * @param string $header  The header name.
178:      */
179:     public function removeHeader($header)
180:     {
181:         if (Horde_String::lower($header) === 'bcc') {
182:             unset($this->_bcc);
183:         } else {
184:             $this->_headers->removeHeader($header);
185:         }
186:     }
187: 
188:     /**
189:      * Sets the message body text.
190:      *
191:      * @param string $body             The message content.
192:      * @param string $charset          The character set of the message.
193:      * @param boolean|integer $wrap    If true, wrap the message at column 76;
194:      *                                 If an integer wrap the message at that
195:      *                                 column. Don't use wrapping if sending
196:      *                                 flowed messages.
197:      */
198:     public function setBody($body, $charset = null, $wrap = false)
199:     {
200:         if (!$charset) {
201:             $charset = $this->_charset;
202:         }
203:         $body = Horde_String::convertCharset($body, 'UTF-8', $charset);
204:         if ($wrap) {
205:             $body = Horde_String::wrap($body, $wrap === true ? 76 : $wrap);
206:         }
207:         $this->_body = new Horde_Mime_Part();
208:         $this->_body->setType('text/plain');
209:         $this->_body->setCharset($charset);
210:         $this->_body->setContents($body);
211:     }
212: 
213:     /**
214:      * Sets the HTML message body text.
215:      *
216:      * @param string $body          The message content.
217:      * @param string $charset       The character set of the message.
218:      * @param boolean $alternative  If true, a multipart/alternative message is
219:      *                              created and the text/plain part is
220:      *                              generated automatically. If false, a
221:      *                              text/html message is generated.
222:      */
223:     public function setHtmlBody($body, $charset = null, $alternative = true)
224:     {
225:         if (!$charset) {
226:             $charset = $this->_charset;
227:         }
228:         $this->_htmlBody = new Horde_Mime_Part();
229:         $this->_htmlBody->setType('text/html');
230:         $this->_htmlBody->setCharset($charset);
231:         $this->_htmlBody->setContents($body);
232:         if ($alternative) {
233:             $this->setBody(Horde_Text_Filter::filter($body, 'Html2text', array('charset' => $charset, 'wrap' => false)), $charset);
234:         }
235:     }
236: 
237:     /**
238:      * Adds a message part.
239:      *
240:      * @param string $mime_type    The content type of the part.
241:      * @param string $content      The content of the part.
242:      * @param string $charset      The character set of the part.
243:      * @param string $disposition  The content disposition of the part.
244:      *
245:      * @return integer  The part number.
246:      */
247:     public function addPart($mime_type, $content, $charset = 'us-ascii',
248:                             $disposition = null)
249:     {
250:         $part = new Horde_Mime_Part();
251:         $part->setType($mime_type);
252:         $part->setCharset($charset);
253:         $part->setDisposition($disposition);
254:         $part->setContents($content);
255:         return $this->addMimePart($part);
256:     }
257: 
258:     /**
259:      * Adds a MIME message part.
260:      *
261:      * @param Horde_Mime_Part $part  A Horde_Mime_Part object.
262:      *
263:      * @return integer  The part number.
264:      */
265:     public function addMimePart($part)
266:     {
267:         $this->_parts[] = $part;
268:         return count($this->_parts) - 1;
269:     }
270: 
271:     /**
272:      * Sets the base MIME part.
273:      *
274:      * If the base part is set, any text bodies will be ignored when building
275:      * the message.
276:      *
277:      * @param Horde_Mime_Part $part  A Horde_Mime_Part object.
278:      */
279:     public function setBasePart($part)
280:     {
281:         $this->_base = $part;
282:     }
283: 
284:     /**
285:      * Adds an attachment.
286:      *
287:      * @param string $file     The path to the file.
288:      * @param string $name     The file name to use for the attachment.
289:      * @param string $type     The content type of the file.
290:      * @param string $charset  The character set of the part (only relevant for
291:      *                         text parts.
292:      *
293:      * @return integer  The part number.
294:      */
295:     public function addAttachment($file, $name = null, $type = null,
296:                                   $charset = 'us-ascii')
297:     {
298:         if (empty($name)) {
299:             $name = basename($file);
300:         }
301: 
302:         if (empty($type)) {
303:             $type = Horde_Mime_Magic::filenameToMime($file, false);
304:         }
305: 
306:         $num = $this->addPart($type, file_get_contents($file), $charset, 'attachment');
307:         $this->_parts[$num]->setName($name);
308:         return $num;
309:     }
310: 
311:     /**
312:      * Removes a message part.
313:      *
314:      * @param integer $part  The part number.
315:      */
316:     public function removePart($part)
317:     {
318:         if (isset($this->_parts[$part])) {
319:             unset($this->_parts[$part]);
320:         }
321:     }
322: 
323:     /**
324:      * Removes all (additional) message parts but leaves the body parts
325:      * untouched.
326:      *
327:      * @since Horde_Mime 1.2.0
328:      */
329:     public function clearParts()
330:     {
331:         $this->_parts = array();
332:     }
333: 
334:     /**
335:      * Adds message recipients.
336:      *
337:      * Recipients specified by To:, Cc:, or Bcc: headers are added
338:      * automatically.
339:      *
340:      * @param string|array  List of recipients, either as a comma separated
341:      *                      list or as an array of email addresses.
342:      *
343:      * @throws Horde_Mime_Exception
344:      */
345:     public function addRecipients($recipients)
346:     {
347:         $this->_recipients = array_merge($this->_recipients, $this->_buildRecipients($recipients));
348:     }
349: 
350:     /**
351:      * Removes message recipients.
352:      *
353:      * @param string|array  List of recipients, either as a comma separated
354:      *                      list or as an array of email addresses.
355:      *
356:      * @throws Horde_Mime_Exception
357:      */
358:     public function removeRecipients($recipients)
359:     {
360:         $this->_recipients = array_diff($this->_recipients, $this->_buildRecipients($recipients));
361:     }
362: 
363:     /**
364:      * Removes all message recipients.
365:      */
366:     public function clearRecipients()
367:     {
368:         $this->_recipients = array();
369:     }
370: 
371:     /**
372:      * Builds a recipients list.
373:      *
374:      * @param string|array  List of recipients, either as a comma separated
375:      *                      list or as an array of email addresses.
376:      *
377:      * @return array  Normalized list of recipients.
378:      * @throws Horde_Mime_Exception
379:      */
380:     protected function _buildRecipients($recipients)
381:     {
382:         if (is_string($recipients)) {
383:             $recipients = Horde_Mime_Address::explode($recipients, ',');
384:         }
385:         $recipients = array_filter(array_map('trim', $recipients));
386: 
387:         $addrlist = array();
388:         foreach ($recipients as $email) {
389:             if (!empty($email)) {
390:                 $unique = Horde_Mime_Address::bareAddress($email);
391:                 if ($unique) {
392:                     $addrlist[$unique] = $email;
393:                 } else {
394:                     $addrlist[$email] = $email;
395:                 }
396:             }
397:         }
398: 
399:         foreach (Horde_Mime_Address::bareAddress(implode(', ', $addrlist), null, true) as $val) {
400:             if (Horde_Mime::is8bit($val)) {
401:                 throw new Horde_Mime_Exception(sprintf(Horde_Mime_Translation::t("Invalid character in e-mail address: %s."), $val));
402:             }
403:         }
404: 
405:         return $addrlist;
406:     }
407: 
408:     /**
409:      * Sends this message.
410:      *
411:      * @param Mail $mailer     A Mail object.
412:      * @param boolean $resend  If true, the message id and date are re-used;
413:      *                         If false, they will be updated.
414:      * @param boolean $flowed  Send message in flowed text format.
415:      *
416:      * @throws Horde_Mime_Exception
417:      */
418:     public function send($mailer, $resend = false, $flowed = true)
419:     {
420:         /* Add mandatory headers if missing. */
421:         $has_header = $this->_headers->getValue('Message-ID');
422:         if (!$resend || !$has_header) {
423:             if ($has_header) {
424:                 $this->_headers->removeHeader('Message-ID');
425:             }
426:             $this->_headers->addMessageIdHeader();
427:         }
428:         if (!$this->_headers->getValue('User-Agent')) {
429:             $this->_headers->addUserAgentHeader();
430:         }
431:         $has_header = $this->_headers->getValue('Date');
432:         if (!$resend || !$has_header) {
433:             if ($has_header) {
434:                 $this->_headers->removeHeader('Date');
435:             }
436:             $this->_headers->addHeader('Date', date('r'));
437:         }
438: 
439:         if (isset($this->_base)) {
440:             $basepart = $this->_base;
441:         } else {
442:             /* Send in flowed format. */
443:             if ($flowed && !empty($this->_body)) {
444:                 $flowed = new Horde_Text_Flowed($this->_body->getContents(), $this->_body->getCharset());
445:                 $flowed->setDelSp(true);
446:                 $this->_body->setContentTypeParameter('format', 'flowed');
447:                 $this->_body->setContentTypeParameter('DelSp', 'Yes');
448:                 $this->_body->setContents($flowed->toFlowed());
449:             }
450: 
451:             /* Build mime message. */
452:             $body = new Horde_Mime_Part();
453:             if (!empty($this->_body) && !empty($this->_htmlBody)) {
454:                 $body->setType('multipart/alternative');
455:                 $this->_body->setDescription(Horde_Mime_Translation::t("Plaintext Version of Message"));
456:                 $body->addPart($this->_body);
457:                 $this->_htmlBody->setDescription(Horde_Mime_Translation::t("HTML Version of Message"));
458:                 $body->addPart($this->_htmlBody);
459:             } elseif (!empty($this->_htmlBody)) {
460:                 $body = $this->_htmlBody;
461:             } elseif (!empty($this->_body)) {
462:                 $body = $this->_body;
463:             }
464:             if (count($this->_parts)) {
465:                 $basepart = new Horde_Mime_Part();
466:                 $basepart->setType('multipart/mixed');
467:                 if ($body) {
468:                     $basepart->addPart($body);
469:                 }
470:                 foreach ($this->_parts as $mime_part) {
471:                     $basepart->addPart($mime_part);
472:                 }
473:             } else {
474:                 $basepart = $body;
475:             }
476:         }
477:         $basepart->setHeaderCharset($this->_charset);
478: 
479:         /* Build recipients. */
480:         $recipients = $this->_recipients;
481:         foreach (array('to', 'cc') as $header) {
482:             $value = $this->_headers->getValue($header, Horde_Mime_Headers::VALUE_BASE);
483:             if (is_null($value)) {
484:                 continue;
485:             }
486:             $value = Horde_Mime::encodeAddress($value, $this->_charset);
487:             $recipients = array_merge($recipients, $this->_buildRecipients($value));
488:         }
489:         if ($this->_bcc) {
490:             $recipients = array_merge($recipients, $this->_buildRecipients(Horde_Mime::encodeAddress($this->_bcc, $this->_charset)));
491:         }
492: 
493:         /* Trick Horde_Mime_Part into re-generating the message headers. */
494:         $this->_headers->removeHeader('MIME-Version');
495: 
496:         /* Send message. */
497:         $basepart->send(implode(', ', $recipients), $this->_headers, $mailer);
498:     }
499: 
500: }
501: 
API documentation generated by ApiGen