1<?php
2/**
3 * Zend Framework
4 *
5 * LICENSE
6 *
7 * This source file is subject to the new BSD license that is bundled
8 * with this package in the file LICENSE.txt.
9 * It is also available through the world-wide-web at this URL:
10 * http://framework.zend.com/license/new-bsd
11 * If you did not receive a copy of the license and are unable to
12 * obtain it through the world-wide-web, please send an email
13 * to license@zend.com so we can send you a copy immediately.
14 *
15 * @category   Zend
16 * @package    Zend_Mail
17 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
18 * @license    http://framework.zend.com/license/new-bsd     New BSD License
19 * @version    $Id$
20 */
21
22
23/**
24 * @see Zend_Mail_Transport_Abstract
25 */
26
27/**
28 * @see Zend_Mime
29 */
30
31/**
32 * @see Zend_Mime_Message
33 */
34
35/**
36 * @see Zend_Mime_Part
37 */
38
39
40/**
41 * Class for sending an email.
42 *
43 * @category   Zend
44 * @package    Zend_Mail
45 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
46 * @license    http://framework.zend.com/license/new-bsd     New BSD License
47 */
48class Zend_Mail extends Zend_Mime_Message
49{
50    /**#@+
51     * @access protected
52     */
53
54    /**
55     * @var Zend_Mail_Transport_Abstract
56     * @static
57     */
58    protected static $_defaultTransport = null;
59
60    /**
61     * @var array
62     * @static
63     */
64    protected static $_defaultFrom;
65
66    /**
67     * @var array
68     * @static
69     */
70    protected static $_defaultReplyTo;
71
72    /**
73     * Mail character set
74     * @var string
75     */
76    protected $_charset = 'iso-8859-1';
77
78    /**
79     * Mail headers
80     * @var array
81     */
82    protected $_headers = array();
83
84    /**
85     * Encoding of Mail headers
86     * @var string
87     */
88    protected $_headerEncoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE;
89
90    /**
91     * From: address
92     * @var string
93     */
94    protected $_from = null;
95
96    /**
97     * To: addresses
98     * @var array
99     */
100    protected $_to = array();
101
102    /**
103     * Array of all recipients
104     * @var array
105     */
106    protected $_recipients = array();
107
108    /**
109     * Reply-To header
110     * @var string
111     */
112    protected $_replyTo = null;
113
114    /**
115     * Return-Path header
116     * @var string
117     */
118    protected $_returnPath = null;
119
120    /**
121     * Subject: header
122     * @var string
123     */
124    protected $_subject = null;
125
126    /**
127     * Date: header
128     * @var string
129     */
130    protected $_date = null;
131
132    /**
133     * Message-ID: header
134     * @var string
135     */
136    protected $_messageId = null;
137
138    /**
139     * text/plain MIME part
140     * @var false|Zend_Mime_Part
141     */
142    protected $_bodyText = false;
143
144    /**
145     * text/html MIME part
146     * @var false|Zend_Mime_Part
147     */
148    protected $_bodyHtml = false;
149
150    /**
151     * MIME boundary string
152     * @var string
153     */
154    protected $_mimeBoundary = null;
155
156    /**
157     * Content type of the message
158     * @var string
159     */
160    protected $_type = null;
161
162    /**#@-*/
163
164    /**
165     * Flag: whether or not email has attachments
166     * @var boolean
167     */
168    public $hasAttachments = false;
169
170
171    /**
172     * Sets the default mail transport for all following uses of
173     * Zend_Mail::send();
174     *
175     * @todo Allow passing a string to indicate the transport to load
176     * @todo Allow passing in optional options for the transport to load
177     * @param  Zend_Mail_Transport_Abstract $transport
178     */
179    public static function setDefaultTransport(Zend_Mail_Transport_Abstract $transport)
180    {
181        self::$_defaultTransport = $transport;
182    }
183
184    /**
185     * Gets the default mail transport for all following uses of
186     * unittests
187     *
188     * @todo Allow passing a string to indicate the transport to load
189     * @todo Allow passing in optional options for the transport to load
190     */
191    public static function getDefaultTransport()
192    {
193        return self::$_defaultTransport;
194    }
195
196    /**
197     * Clear the default transport property
198     */
199    public static function clearDefaultTransport()
200    {
201        self::$_defaultTransport = null;
202    }
203
204    /**
205     * Public constructor
206     *
207     * @param  string $charset
208     */
209    public function __construct($charset = null)
210    {
211        if ($charset != null) {
212            $this->_charset = $charset;
213        }
214    }
215
216    /**
217     * Return charset string
218     *
219     * @return string
220     */
221    public function getCharset()
222    {
223        return $this->_charset;
224    }
225
226    /**
227     * Set content type
228     *
229     * Should only be used for manually setting multipart content types.
230     *
231     * @param  string $type Content type
232     * @return Zend_Mail Implements fluent interface
233     * @throws Zend_Mail_Exception for types not supported by Zend_Mime
234     */
235    public function setType($type)
236    {
237        $allowed = array(
238            Zend_Mime::MULTIPART_ALTERNATIVE,
239            Zend_Mime::MULTIPART_MIXED,
240            Zend_Mime::MULTIPART_RELATED,
241        );
242        if (!in_array($type, $allowed)) {
243            /**
244             * @see Zend_Mail_Exception
245             */
246            throw new Zend_Mail_Exception('Invalid content type "' . $type . '"');
247        }
248
249        $this->_type = $type;
250        return $this;
251    }
252
253    /**
254     * Get content type of the message
255     *
256     * @return string
257     */
258    public function getType()
259    {
260        return $this->_type;
261    }
262
263    /**
264     * Set an arbitrary mime boundary for the message
265     *
266     * If not set, Zend_Mime will generate one.
267     *
268     * @param  string    $boundary
269     * @return Zend_Mail Provides fluent interface
270     */
271    public function setMimeBoundary($boundary)
272    {
273        $this->_mimeBoundary = $boundary;
274
275        return $this;
276    }
277
278    /**
279     * Return the boundary string used for the message
280     *
281     * @return string
282     */
283    public function getMimeBoundary()
284    {
285        return $this->_mimeBoundary;
286    }
287
288    /**
289     * Return encoding of mail headers
290     *
291     * @deprecated use {@link getHeaderEncoding()} instead
292     * @return string
293     */
294    public function getEncodingOfHeaders()
295    {
296        return $this->getHeaderEncoding();
297    }
298
299    /**
300     * Return the encoding of mail headers
301     *
302     * Either Zend_Mime::ENCODING_QUOTEDPRINTABLE or Zend_Mime::ENCODING_BASE64
303     *
304     * @return string
305     */
306    public function getHeaderEncoding()
307    {
308        return $this->_headerEncoding;
309    }
310
311    /**
312     * Set the encoding of mail headers
313     *
314     * @deprecated Use {@link setHeaderEncoding()} instead.
315     * @param  string $encoding
316     * @return Zend_Mail
317     */
318    public function setEncodingOfHeaders($encoding)
319    {
320        return $this->setHeaderEncoding($encoding);
321    }
322
323    /**
324     * Set the encoding of mail headers
325     *
326     * @param  string $encoding Zend_Mime::ENCODING_QUOTEDPRINTABLE or
327     *                          Zend_Mime::ENCODING_BASE64
328     * @return Zend_Mail Provides fluent interface
329     * @throws Zend_Mail_Exception
330     */
331    public function setHeaderEncoding($encoding)
332    {
333        $allowed = array(
334            Zend_Mime::ENCODING_BASE64,
335            Zend_Mime::ENCODING_QUOTEDPRINTABLE
336        );
337        if (!in_array($encoding, $allowed)) {
338            /**
339             * @see Zend_Mail_Exception
340             */
341            throw new Zend_Mail_Exception('Invalid encoding "' . $encoding . '"');
342        }
343        $this->_headerEncoding = $encoding;
344
345        return $this;
346    }
347
348    /**
349     * Sets the text body for the message.
350     *
351     * @param  string $txt
352     * @param  string $charset
353     * @param  string $encoding
354     * @return Zend_Mail Provides fluent interface
355    */
356    public function setBodyText($txt, $charset = null, $encoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE)
357    {
358        if ($charset === null) {
359            $charset = $this->_charset;
360        }
361
362        $mp = new Zend_Mime_Part($txt);
363        $mp->encoding = $encoding;
364        $mp->type = Zend_Mime::TYPE_TEXT;
365        $mp->disposition = Zend_Mime::DISPOSITION_INLINE;
366        $mp->charset = $charset;
367
368        $this->_bodyText = $mp;
369
370        return $this;
371    }
372
373    /**
374     * Return text body Zend_Mime_Part or string
375     *
376     * @param  bool $textOnly Whether to return just the body text content or
377     *                        the MIME part; defaults to false, the MIME part
378     * @return false|Zend_Mime_Part|string
379     */
380    public function getBodyText($textOnly = false)
381    {
382        if ($textOnly && $this->_bodyText) {
383            $body = $this->_bodyText;
384            return $body->getContent();
385        }
386
387        return $this->_bodyText;
388    }
389
390    /**
391     * Sets the HTML body for the message
392     *
393     * @param  string    $html
394     * @param  string    $charset
395     * @param  string    $encoding
396     * @return Zend_Mail Provides fluent interface
397     */
398    public function setBodyHtml($html, $charset = null, $encoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE)
399    {
400        if ($charset === null) {
401            $charset = $this->_charset;
402        }
403
404        $mp = new Zend_Mime_Part($html);
405        $mp->encoding = $encoding;
406        $mp->type = Zend_Mime::TYPE_HTML;
407        $mp->disposition = Zend_Mime::DISPOSITION_INLINE;
408        $mp->charset = $charset;
409
410        $this->_bodyHtml = $mp;
411
412        return $this;
413    }
414
415    /**
416     * Return Zend_Mime_Part representing body HTML
417     *
418     * @param  bool $htmlOnly Whether to return the body HTML only, or the MIME part; defaults to false, the MIME part
419     * @return false|Zend_Mime_Part|string
420     */
421    public function getBodyHtml($htmlOnly = false)
422    {
423        if ($htmlOnly && $this->_bodyHtml) {
424            $body = $this->_bodyHtml;
425            return $body->getContent();
426        }
427
428        return $this->_bodyHtml;
429    }
430
431    /**
432     * Adds an existing attachment to the mail message
433     *
434     * @param  Zend_Mime_Part $attachment
435     * @return Zend_Mail Provides fluent interface
436     */
437    public function addAttachment(Zend_Mime_Part $attachment)
438    {
439        $this->addPart($attachment);
440        $this->hasAttachments = true;
441
442        return $this;
443    }
444
445    /**
446     * Creates a Zend_Mime_Part attachment
447     *
448     * Attachment is automatically added to the mail object after creation. The
449     * attachment object is returned to allow for further manipulation.
450     *
451     * @param  string         $body
452     * @param  string         $mimeType
453     * @param  string         $disposition
454     * @param  string         $encoding
455     * @param  string         $filename OPTIONAL A filename for the attachment
456     * @return Zend_Mime_Part Newly created Zend_Mime_Part object (to allow
457     * advanced settings)
458     */
459    public function createAttachment($body,
460                                     $mimeType    = Zend_Mime::TYPE_OCTETSTREAM,
461                                     $disposition = Zend_Mime::DISPOSITION_ATTACHMENT,
462                                     $encoding    = Zend_Mime::ENCODING_BASE64,
463                                     $filename    = null)
464    {
465
466        $mp = new Zend_Mime_Part($body);
467        $mp->encoding = $encoding;
468        $mp->type = $mimeType;
469        $mp->disposition = $disposition;
470        $mp->filename = $filename;
471
472        $this->addAttachment($mp);
473
474        return $mp;
475    }
476
477    /**
478     * Return a count of message parts
479     *
480     * @return integer
481     */
482    public function getPartCount()
483    {
484        return count($this->_parts);
485    }
486
487    /**
488     * Encode header fields
489     *
490     * Encodes header content according to RFC1522 if it contains non-printable
491     * characters.
492     *
493     * @param  string $value
494     * @return string
495     */
496    protected function _encodeHeader($value)
497    {
498        if (Zend_Mime::isPrintable($value) === false) {
499            if ($this->getHeaderEncoding() === Zend_Mime::ENCODING_QUOTEDPRINTABLE) {
500                $value = Zend_Mime::encodeQuotedPrintableHeader($value, $this->getCharset(), Zend_Mime::LINELENGTH, Zend_Mime::LINEEND);
501            } else {
502                $value = Zend_Mime::encodeBase64Header($value, $this->getCharset(), Zend_Mime::LINELENGTH, Zend_Mime::LINEEND);
503            }
504        }
505
506        return $value;
507    }
508
509    /**
510     * Add a header to the message
511     *
512     * Adds a header to this message. If append is true and the header already
513     * exists, raises a flag indicating that the header should be appended.
514     *
515     * @param string  $headerName
516     * @param string  $value
517     * @param bool $append
518     */
519    protected function _storeHeader($headerName, $value, $append = false)
520    {
521        if (isset($this->_headers[$headerName])) {
522            $this->_headers[$headerName][] = $value;
523        } else {
524            $this->_headers[$headerName] = array($value);
525        }
526
527        if ($append) {
528            $this->_headers[$headerName]['append'] = true;
529        }
530
531    }
532
533    /**
534     * Clear header from the message
535     *
536     * @param string $headerName
537     * @deprecated use public method directly
538     */
539    protected function _clearHeader($headerName)
540    {
541        $this->clearHeader($headerName);
542    }
543
544    /**
545     * Helper function for adding a recipient and the corresponding header
546     *
547     * @param string $headerName
548     * @param string $email
549     * @param string $name
550     */
551    protected function _addRecipientAndHeader($headerName, $email, $name)
552    {
553        $email = $this->_filterEmail($email);
554        $name  = $this->_filterName($name);
555        // prevent duplicates
556        $this->_recipients[$email] = 1;
557        $this->_storeHeader($headerName, $this->_formatAddress($email, $name), true);
558    }
559
560    /**
561     * Adds To-header and recipient, $email can be an array, or a single string
562     * address
563     *
564     * @param  string|array $email
565     * @param  string $name
566     * @return Zend_Mail Provides fluent interface
567     */
568    public function addTo($email, $name='')
569    {
570        if (!is_array($email)) {
571            $email = array($name => $email);
572        }
573
574        foreach ($email as $n => $recipient) {
575            $this->_addRecipientAndHeader('To', $recipient, is_int($n) ? '' : $n);
576            $this->_to[] = $recipient;
577        }
578
579        return $this;
580    }
581
582    /**
583     * Adds Cc-header and recipient, $email can be an array, or a single string
584     * address
585     *
586     * @param  string|array    $email
587     * @param  string    $name
588     * @return Zend_Mail Provides fluent interface
589     */
590    public function addCc($email, $name='')
591    {
592        if (!is_array($email)) {
593            $email = array($name => $email);
594        }
595
596        foreach ($email as $n => $recipient) {
597            $this->_addRecipientAndHeader('Cc', $recipient, is_int($n) ? '' : $n);
598        }
599
600        return $this;
601    }
602
603    /**
604     * Adds Bcc recipient, $email can be an array, or a single string address
605     *
606     * @param  string|array    $email
607     * @return Zend_Mail Provides fluent interface
608     */
609    public function addBcc($email)
610    {
611        if (!is_array($email)) {
612            $email = array($email);
613        }
614
615        foreach ($email as $recipient) {
616            $this->_addRecipientAndHeader('Bcc', $recipient, '');
617        }
618
619        return $this;
620    }
621
622    /**
623     * Return list of recipient email addresses
624     *
625     * @return array (of strings)
626     */
627    public function getRecipients()
628    {
629        return array_keys($this->_recipients);
630    }
631
632    /**
633     * Clear header from the message
634     *
635     * @param string $headerName
636     * @return Zend_Mail Provides fluent inter
637     */
638    public function clearHeader($headerName)
639    {
640        if (isset($this->_headers[$headerName])){
641            unset($this->_headers[$headerName]);
642        }
643        return $this;
644    }
645
646    /**
647     * Clears list of recipient email addresses
648     *
649     * @return Zend_Mail Provides fluent interface
650     */
651    public function clearRecipients()
652    {
653        $this->_recipients = array();
654        $this->_to = array();
655
656        $this->clearHeader('To');
657        $this->clearHeader('Cc');
658        $this->clearHeader('Bcc');
659
660        return $this;
661    }
662
663    /**
664     * Sets From-header and sender of the message
665     *
666     * @param  string    $email
667     * @param  string    $name
668     * @return Zend_Mail Provides fluent interface
669     * @throws Zend_Mail_Exception if called subsequent times
670     */
671    public function setFrom($email, $name = null)
672    {
673        if (null !== $this->_from) {
674            /**
675             * @see Zend_Mail_Exception
676             */
677            throw new Zend_Mail_Exception('From Header set twice');
678        }
679
680        $email = $this->_filterEmail($email);
681        $name  = $this->_filterName($name);
682        $this->_from = $email;
683        $this->_storeHeader('From', $this->_formatAddress($email, $name), true);
684
685        return $this;
686    }
687
688    /**
689     * Set Reply-To Header
690     *
691     * @param string $email
692     * @param string $name
693     * @return Zend_Mail
694     * @throws Zend_Mail_Exception if called more than one time
695     */
696    public function setReplyTo($email, $name = null)
697    {
698        if (null !== $this->_replyTo) {
699            /**
700             * @see Zend_Mail_Exception
701             */
702            throw new Zend_Mail_Exception('Reply-To Header set twice');
703        }
704
705        $email = $this->_filterEmail($email);
706        $name  = $this->_filterName($name);
707        $this->_replyTo = $email;
708        $this->_storeHeader('Reply-To', $this->_formatAddress($email, $name), true);
709
710        return $this;
711    }
712
713    /**
714     * Returns the sender of the mail
715     *
716     * @return string
717     */
718    public function getFrom()
719    {
720        return $this->_from;
721    }
722
723    /**
724     * Returns the current Reply-To address of the message
725     *
726     * @return string|null Reply-To address, null when not set
727     */
728    public function getReplyTo()
729    {
730        return $this->_replyTo;
731    }
732
733    /**
734     * Clears the sender from the mail
735     *
736     * @return Zend_Mail Provides fluent interface
737     */
738    public function clearFrom()
739    {
740        $this->_from = null;
741        $this->clearHeader('From');
742
743        return $this;
744    }
745
746     /**
747      * Clears the current Reply-To address from the message
748      *
749      * @return Zend_Mail Provides fluent interface
750      */
751    public function clearReplyTo()
752    {
753        $this->_replyTo = null;
754        $this->clearHeader('Reply-To');
755
756        return $this;
757    }
758
759    /**
760     * Sets Default From-email and name of the message
761     *
762     * @param  string $email
763     * @param  string $name optional
764     * @return void
765     */
766    public static function setDefaultFrom($email, $name = null)
767    {
768        self::$_defaultFrom = array('email' => $email, 'name' => $name);
769    }
770
771    /**
772     * Returns the default sender of the mail
773     *
774     * @return null|array   Null if none was set.
775     */
776    public static function getDefaultFrom()
777    {
778        return self::$_defaultFrom;
779    }
780
781    /**
782     * Clears the default sender from the mail
783     *
784     * @return void
785     */
786    public static function clearDefaultFrom()
787    {
788        self::$_defaultFrom = null;
789    }
790
791    /**
792     * Sets From-name and -email based on the defaults
793     *
794     * @return Zend_Mail Provides fluent interface
795     * @throws Zend_Mail_Exception
796     */
797    public function setFromToDefaultFrom() {
798        $from = self::getDefaultFrom();
799        if($from === null) {
800            throw new Zend_Mail_Exception(
801                'No default From Address set to use');
802        }
803
804        $this->setFrom($from['email'], $from['name']);
805
806        return $this;
807    }
808
809    /**
810     * Sets Default ReplyTo-address and -name of the message
811     *
812     * @param  string $email
813     * @param  string $name optional
814     * @return void
815     */
816    public static function setDefaultReplyTo($email, $name = null)
817    {
818        self::$_defaultReplyTo = array('email' => $email, 'name' => $name);
819    }
820
821    /**
822     * Returns the default Reply-To Address and Name of the mail
823     *
824     * @return null|array   Null if none was set.
825     */
826    public static function getDefaultReplyTo()
827    {
828        return self::$_defaultReplyTo;
829    }
830
831    /**
832     * Clears the default ReplyTo-address and -name from the mail
833     *
834     * @return void
835     */
836    public static function clearDefaultReplyTo()
837    {
838        self::$_defaultReplyTo = null;
839    }
840
841    /**
842     * Sets ReplyTo-name and -email based on the defaults
843     *
844     * @return Zend_Mail Provides fluent interface
845     * @throws Zend_Mail_Exception
846     */
847    public function setReplyToFromDefault() {
848        $replyTo = self::getDefaultReplyTo();
849        if($replyTo === null) {
850            throw new Zend_Mail_Exception(
851                'No default Reply-To Address set to use');
852        }
853
854        $this->setReplyTo($replyTo['email'], $replyTo['name']);
855
856        return $this;
857    }
858
859    /**
860     * Sets the Return-Path header of the message
861     *
862     * @param  string    $email
863     * @return Zend_Mail Provides fluent interface
864     * @throws Zend_Mail_Exception if set multiple times
865     */
866    public function setReturnPath($email)
867    {
868        if ($this->_returnPath === null) {
869            $email = $this->_filterEmail($email);
870            $this->_returnPath = $email;
871            $this->_storeHeader('Return-Path', $email, false);
872        } else {
873            /**
874             * @see Zend_Mail_Exception
875             */
876            throw new Zend_Mail_Exception('Return-Path Header set twice');
877        }
878        return $this;
879    }
880
881    /**
882     * Returns the current Return-Path address of the message
883     *
884     * If no Return-Path header is set, returns the value of {@link $_from}.
885     *
886     * @return string
887     */
888    public function getReturnPath()
889    {
890        if (null !== $this->_returnPath) {
891            return $this->_returnPath;
892        }
893
894        return $this->_from;
895    }
896
897    /**
898     * Clears the current Return-Path address from the message
899     *
900     * @return Zend_Mail Provides fluent interface
901     */
902    public function clearReturnPath()
903    {
904        $this->_returnPath = null;
905        $this->clearHeader('Return-Path');
906
907        return $this;
908    }
909
910    /**
911     * Sets the subject of the message
912     *
913     * @param   string    $subject
914     * @return  Zend_Mail Provides fluent interface
915     * @throws  Zend_Mail_Exception
916     */
917    public function setSubject($subject)
918    {
919        if ($this->_subject === null) {
920            $subject = $this->_filterOther($subject);
921            $this->_subject = $this->_encodeHeader($subject);
922            $this->_storeHeader('Subject', $this->_subject);
923        } else {
924            /**
925             * @see Zend_Mail_Exception
926             */
927            throw new Zend_Mail_Exception('Subject set twice');
928        }
929        return $this;
930    }
931
932    /**
933     * Returns the encoded subject of the message
934     *
935     * @return string
936     */
937    public function getSubject()
938    {
939        return $this->_subject;
940    }
941
942    /**
943     * Clears the encoded subject from the message
944     *
945     * @return  Zend_Mail Provides fluent interface
946     */
947    public function clearSubject()
948    {
949        $this->_subject = null;
950        $this->clearHeader('Subject');
951
952        return $this;
953    }
954
955    /**
956     * Sets Date-header
957     *
958     * @param  int|string|Zend_Date $date
959     * @return Zend_Mail Provides fluent interface
960     * @throws Zend_Mail_Exception if called subsequent times or wrong date
961     *                             format.
962     */
963    public function setDate($date = null)
964    {
965        if ($this->_date === null) {
966            if ($date === null) {
967                $date = date('r');
968            } else if (is_int($date)) {
969                $date = date('r', $date);
970            } else if (is_string($date)) {
971                $date = strtotime($date);
972                if ($date === false || $date < 0) {
973                    /**
974                     * @see Zend_Mail_Exception
975                     */
976                    throw new Zend_Mail_Exception('String representations of Date Header must be ' .
977                                                  'strtotime()-compatible');
978                }
979                $date = date('r', $date);
980            } else if ($date instanceof Zend_Date) {
981                $date = $date->get(Zend_Date::RFC_2822);
982            } else {
983                /**
984                 * @see Zend_Mail_Exception
985                 */
986                throw new Zend_Mail_Exception(__METHOD__ . ' only accepts UNIX timestamps, Zend_Date objects, ' .
987                                              ' and strtotime()-compatible strings');
988            }
989            $this->_date = $date;
990            $this->_storeHeader('Date', $date);
991        } else {
992            /**
993             * @see Zend_Mail_Exception
994             */
995            throw new Zend_Mail_Exception('Date Header set twice');
996        }
997        return $this;
998    }
999
1000    /**
1001     * Returns the formatted date of the message
1002     *
1003     * @return string
1004     */
1005    public function getDate()
1006    {
1007        return $this->_date;
1008    }
1009
1010    /**
1011     * Clears the formatted date from the message
1012     *
1013     * @return Zend_Mail Provides fluent interface
1014     */
1015    public function clearDate()
1016    {
1017        $this->_date = null;
1018        $this->clearHeader('Date');
1019
1020        return $this;
1021    }
1022
1023    /**
1024     * Sets the Message-ID of the message
1025     *
1026     * @param   boolean|string  $id
1027     * true  :Auto
1028     * false :No set
1029     * null  :No set
1030     * string:Sets given string (Angle brackets is not necessary)
1031     * @return  Zend_Mail Provides fluent interface
1032     * @throws  Zend_Mail_Exception
1033     */
1034    public function setMessageId($id = true)
1035    {
1036        if ($id === null || $id === false) {
1037            return $this;
1038        } elseif ($id === true) {
1039            $id = $this->createMessageId();
1040        }
1041
1042        if ($this->_messageId === null) {
1043            $id = $this->_filterOther($id);
1044            $this->_messageId = $id;
1045            $this->_storeHeader('Message-Id', '<' . $this->_messageId . '>');
1046        } else {
1047            /**
1048             * @see Zend_Mail_Exception
1049             */
1050            throw new Zend_Mail_Exception('Message-ID set twice');
1051        }
1052
1053        return $this;
1054    }
1055
1056    /**
1057     * Returns the Message-ID of the message
1058     *
1059     * @return string
1060     */
1061    public function getMessageId()
1062    {
1063        return $this->_messageId;
1064    }
1065
1066
1067    /**
1068     * Clears the Message-ID from the message
1069     *
1070     * @return Zend_Mail Provides fluent interface
1071     */
1072    public function clearMessageId()
1073    {
1074        $this->_messageId = null;
1075        $this->clearHeader('Message-Id');
1076
1077        return $this;
1078    }
1079
1080    /**
1081     * Creates the Message-ID
1082     *
1083     * @return string
1084     */
1085    public function createMessageId() {
1086
1087        $time = time();
1088
1089        if ($this->_from !== null) {
1090            $user = $this->_from;
1091        } elseif (isset($_SERVER['REMOTE_ADDR'])) {
1092            $user = $_SERVER['REMOTE_ADDR'];
1093        } else {
1094            $user = getmypid();
1095        }
1096
1097        $rand = mt_rand();
1098
1099        if ($this->_recipients !== array()) {
1100            $recipient = array_rand($this->_recipients);
1101        } else {
1102            $recipient = 'unknown';
1103        }
1104
1105        if (isset($_SERVER["SERVER_NAME"])) {
1106            $hostName = $_SERVER["SERVER_NAME"];
1107        } else {
1108            $hostName = php_uname('n');
1109        }
1110
1111        return sha1($time . $user . $rand . $recipient) . '@' . $hostName;
1112    }
1113
1114    /**
1115     * Add a custom header to the message
1116     *
1117     * @param  string              $name
1118     * @param  string              $value
1119     * @param  boolean             $append
1120     * @return Zend_Mail           Provides fluent interface
1121     * @throws Zend_Mail_Exception on attempts to create standard headers
1122     */
1123    public function addHeader($name, $value, $append = false)
1124    {
1125        $prohibit = array('to', 'cc', 'bcc', 'from', 'subject',
1126                          'reply-to', 'return-path',
1127                          'date', 'message-id',
1128                         );
1129        if (in_array(strtolower($name), $prohibit)) {
1130            /**
1131             * @see Zend_Mail_Exception
1132             */
1133            throw new Zend_Mail_Exception('Cannot set standard header from addHeader()');
1134        }
1135
1136        $value = $this->_filterOther($value);
1137        $value = $this->_encodeHeader($value);
1138        $this->_storeHeader($name, $value, $append);
1139
1140        return $this;
1141    }
1142
1143    /**
1144     * Return mail headers
1145     *
1146     * @return array
1147     */
1148    public function getHeaders()
1149    {
1150        return $this->_headers;
1151    }
1152
1153    /**
1154     * Sends this email using the given transport or a previously
1155     * set DefaultTransport or the internal mail function if no
1156     * default transport had been set.
1157     *
1158     * @param  Zend_Mail_Transport_Abstract $transport
1159     * @return Zend_Mail                    Provides fluent interface
1160     */
1161    public function send($transport = null)
1162    {
1163        if ($transport === null) {
1164            if (! self::$_defaultTransport instanceof Zend_Mail_Transport_Abstract) {
1165                $transport = new Zend_Mail_Transport_Sendmail();
1166            } else {
1167                $transport = self::$_defaultTransport;
1168            }
1169        }
1170
1171        if ($this->_date === null) {
1172            $this->setDate();
1173        }
1174
1175        if(null === $this->_from && null !== self::getDefaultFrom()) {
1176            $this->setFromToDefaultFrom();
1177        }
1178
1179        if(null === $this->_replyTo && null !== self::getDefaultReplyTo()) {
1180            $this->setReplyToFromDefault();
1181        }
1182
1183        $transport->send($this);
1184
1185        return $this;
1186    }
1187
1188    /**
1189     * Filter of email data
1190     *
1191     * @param string $email
1192     * @return string
1193     */
1194    protected function _filterEmail($email)
1195    {
1196        $rule = array("\r" => '',
1197                      "\n" => '',
1198                      "\t" => '',
1199                      '"'  => '',
1200                      ','  => '',
1201                      '<'  => '',
1202                      '>'  => '',
1203        );
1204
1205        return strtr($email, $rule);
1206    }
1207
1208    /**
1209     * Filter of name data
1210     *
1211     * @param string $name
1212     * @return string
1213     */
1214    protected function _filterName($name)
1215    {
1216        $rule = array("\r" => '',
1217                      "\n" => '',
1218                      "\t" => '',
1219                      '"'  => "'",
1220                      '<'  => '[',
1221                      '>'  => ']',
1222        );
1223
1224        return trim(strtr($name, $rule));
1225    }
1226
1227    /**
1228     * Filter of other data
1229     *
1230     * @param string $data
1231     * @return string
1232     */
1233    protected function _filterOther($data)
1234    {
1235        $rule = array("\r" => '',
1236                      "\n" => '',
1237                      "\t" => '',
1238        );
1239
1240        return strtr($data, $rule);
1241    }
1242
1243    /**
1244     * Formats e-mail address
1245     *
1246     * @param string $email
1247     * @param string $name
1248     * @return string
1249     */
1250    protected function _formatAddress($email, $name)
1251    {
1252        if ($name === '' || $name === null || $name === $email) {
1253            return $email;
1254        } else {
1255            $encodedName = $this->_encodeHeader($name);
1256            if ($encodedName === $name  &&  strcspn($name, '()<>[]:;@\\,.') != strlen($name)) {
1257                $format = '"%s" <%s>';
1258            } else {
1259                $format = '%s <%s>';
1260            }
1261            return sprintf($format, $encodedName, $email);
1262        }
1263    }
1264
1265}
1266