svncommit
2008-09-18 d0b973cf6aed4a7cb705f706624d25b31d19ed52
commit | author | age
4e17e6 1 <?php
ab6f80 2 /**
T 3  * The Mail_Mime class is used to create MIME E-mail messages
4  *
5  * The Mail_Mime class provides an OO interface to create MIME
6  * enabled email messages. This way you can create emails that
7  * contain plain-text bodies, HTML bodies, attachments, inline
8  * images and specific headers.
9  *
10  * Compatible with PHP versions 4 and 5
11  *
12  * LICENSE: This LICENSE is in the BSD license style.
13  * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org>
14  * Copyright (c) 2003-2006, PEAR <pear-group@php.net>
15  * All rights reserved.
16  *
17  * Redistribution and use in source and binary forms, with or
18  * without modification, are permitted provided that the following
19  * conditions are met:
20  *
21  * - Redistributions of source code must retain the above copyright
22  *   notice, this list of conditions and the following disclaimer.
23  * - Redistributions in binary form must reproduce the above copyright
24  *   notice, this list of conditions and the following disclaimer in the
25  *   documentation and/or other materials provided with the distribution.
26  * - Neither the name of the authors, nor the names of its contributors 
27  *   may be used to endorse or promote products derived from this 
28  *   software without specific prior written permission.
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
31  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
34  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
40  * THE POSSIBILITY OF SUCH DAMAGE.
41  *
ee289d 42  * @category  Mail
A 43  * @package   Mail_Mime
44  * @author    Richard Heyes  <richard@phpguru.org>
45  * @author    Tomas V.V. Cox <cox@idecnet.com>
46  * @author    Cipriano Groenendal <cipri@php.net>
47  * @author    Sean Coates <sean@php.net>
48  * @copyright 2003-2006 PEAR <pear-group@php.net>
49  * @license   http://www.opensource.org/licenses/bsd-license.php BSD License
50  * @version   CVS: $Id$
51  * @link      http://pear.php.net/package/Mail_mime
52  *
53  *            This class is based on HTML Mime Mail class from
54  *            Richard Heyes <richard@phpguru.org> which was based also
55  *            in the mime_mail.class by Tobias Ratschiller <tobias@dnet.it>
56  *            and Sascha Schumann <sascha@schumann.cx>
ab6f80 57  */
4e17e6 58
T 59
60 /**
ab6f80 61  * require PEAR
4e17e6 62  *
ab6f80 63  * This package depends on PEAR to raise errors.
T 64  */
ee289d 65 require_once 'PEAR.php';
ab6f80 66
T 67 /**
68  * require Mail_mimePart
4e17e6 69  *
ab6f80 70  * Mail_mimePart contains the code required to
T 71  * create all the different parts a mail can
72  * consist of.
73  */
ee289d 74 require_once 'Mail/mimePart.php';
ab6f80 75
T 76
77 /**
78  * The Mail_Mime class provides an OO interface to create MIME
79  * enabled email messages. This way you can create emails that
80  * contain plain-text bodies, HTML bodies, attachments, inline
81  * images and specific headers.
82  *
ee289d 83  * @category  Mail
A 84  * @package   Mail_Mime
85  * @author    Richard Heyes  <richard@phpguru.org>
86  * @author    Tomas V.V. Cox <cox@idecnet.com>
87  * @author    Cipriano Groenendal <cipri@php.net>
88  * @author    Sean Coates <sean@php.net>
89  * @copyright 2003-2006 PEAR <pear-group@php.net>
90  * @license   http://www.opensource.org/licenses/bsd-license.php BSD License
91  * @version   Release: @package_version@
92  * @link      http://pear.php.net/package/Mail_mime
4e17e6 93  */
T 94 class Mail_mime
95 {
96     /**
97      * Contains the plain text part of the email
ab6f80 98      *
4e17e6 99      * @var string
ab6f80 100      * @access private
4e17e6 101      */
T 102     var $_txtbody;
ab6f80 103
4e17e6 104     /**
T 105      * Contains the html part of the email
ab6f80 106      *
4e17e6 107      * @var string
ab6f80 108      * @access private
4e17e6 109      */
T 110     var $_htmlbody;
ab6f80 111
4e17e6 112     /**
T 113      * contains the mime encoded text
ab6f80 114      *
4e17e6 115      * @var string
ab6f80 116      * @access private
4e17e6 117      */
T 118     var $_mime;
ab6f80 119
4e17e6 120     /**
T 121      * contains the multipart content
ab6f80 122      *
4e17e6 123      * @var string
ab6f80 124      * @access private
4e17e6 125      */
T 126     var $_multipart;
ab6f80 127
4e17e6 128     /**
T 129      * list of the attached images
ab6f80 130      *
4e17e6 131      * @var array
ab6f80 132      * @access private
4e17e6 133      */
T 134     var $_html_images = array();
ab6f80 135
4e17e6 136     /**
T 137      * list of the attachements
ab6f80 138      *
4e17e6 139      * @var array
ab6f80 140      * @access private
4e17e6 141      */
T 142     var $_parts = array();
ab6f80 143
4e17e6 144     /**
T 145      * Build parameters
ab6f80 146      *
4e17e6 147      * @var array
ab6f80 148      * @access private
4e17e6 149      */
T 150     var $_build_params = array();
ab6f80 151
4e17e6 152     /**
T 153      * Headers for the mail
ab6f80 154      *
4e17e6 155      * @var array
ab6f80 156      * @access private
4e17e6 157      */
T 158     var $_headers = array();
ab6f80 159
4e17e6 160     /**
T 161      * End Of Line sequence (for serialize)
ab6f80 162      *
4e17e6 163      * @var string
ab6f80 164      * @access private
4e17e6 165      */
T 166     var $_eol;
167
168
169     /**
ab6f80 170      * Constructor function.
T 171      *
ee289d 172      * @param string $crlf what type of linebreak to use.
A 173      *                     Defaults to "\r\n"
174      *
ab6f80 175      * @return void
4e17e6 176      *
T 177      * @access public
178      */
179     function Mail_mime($crlf = "\r\n")
180     {
181         $this->_setEOL($crlf);
182         $this->_build_params = array(
a8435b 183                                      'head_encoding' => 'quoted-printable',
4e17e6 184                                      'text_encoding' => '7bit',
T 185                                      'html_encoding' => 'quoted-printable',
186                                      '7bit_wrap'     => 998,
187                                      'html_charset'  => 'ISO-8859-1',
188                                      'text_charset'  => 'ISO-8859-1',
189                                      'head_charset'  => 'ISO-8859-1'
190                                     );
191     }
192
193     /**
ab6f80 194      * wakeup function called by unserialize. It re-sets the EOL constant
4e17e6 195      *
T 196      * @access private
ee289d 197      * @return void
4e17e6 198      */
T 199     function __wakeup()
200     {
201         $this->_setEOL($this->_eol);
202     }
203
ab6f80 204
4e17e6 205     /**
T 206      * Accessor function to set the body text. Body text is used if
207      * it's not an html mail being sent or else is used to fill the
208      * text/plain part that emails clients who don't support
209      * html should show.
210      *
ee289d 211      * @param string $data   Either a string or
A 212      *                        the file name with the contents
213      * @param bool   $isfile If true the first param should be treated
214      *                        as a file name, else as a string (default)
215      * @param bool   $append If true the text or file is appended to
216      *                        the existing body, else the old body is
217      *                        overwritten
218      *
4e17e6 219      * @return mixed   true on success or PEAR_Error object
T 220      * @access public
221      */
222     function setTXTBody($data, $isfile = false, $append = false)
223     {
224         if (!$isfile) {
225             if (!$append) {
226                 $this->_txtbody = $data;
227             } else {
228                 $this->_txtbody .= $data;
229             }
230         } else {
231             $cont = $this->_file2str($data);
232             if (PEAR::isError($cont)) {
233                 return $cont;
234             }
235             if (!$append) {
236                 $this->_txtbody = $cont;
237             } else {
238                 $this->_txtbody .= $cont;
239             }
240         }
241         return true;
242     }
243
244     /**
ab6f80 245      * Adds a html part to the mail.
4e17e6 246      *
ee289d 247      * @param string $data   either a string or the file name with the
A 248      *                        contents
249      * @param bool   $isfile a flag that determines whether $data is a
250      *                        filename, or a string(false, default)
251      *
ab6f80 252      * @return bool    true on success
4e17e6 253      * @access public
T 254      */
255     function setHTMLBody($data, $isfile = false)
256     {
257         if (!$isfile) {
258             $this->_htmlbody = $data;
259         } else {
260             $cont = $this->_file2str($data);
261             if (PEAR::isError($cont)) {
262                 return $cont;
263             }
264             $this->_htmlbody = $cont;
265         }
266
267         return true;
268     }
269
270     /**
271      * Adds an image to the list of embedded images.
272      *
ee289d 273      * @param string $file   the image file name OR image data itself
A 274      * @param string $c_type the content type
275      * @param string $name   the filename of the image.
276      *                        Only used if $file is the image data.
277      * @param bool   $isfile whether $file is a filename or not.
278      *                        Defaults to true
279      *
280      * @return bool          true on success
4e17e6 281      * @access public
T 282      */
283     function addHTMLImage($file, $c_type='application/octet-stream',
ab6f80 284                           $name = '', $isfile = true)
4e17e6 285     {
ab6f80 286         $filedata = ($isfile === true) ? $this->_file2str($file)
4e17e6 287                                            : $file;
ab6f80 288         if ($isfile === true) {
856110 289             $filename = ($name == '' ? $file : $name);
4e17e6 290         } else {
856110 291             $filename = $name;
4e17e6 292         }
T 293         if (PEAR::isError($filedata)) {
294             return $filedata;
295         }
296         $this->_html_images[] = array(
297                                       'body'   => $filedata,
298                                       'name'   => $filename,
299                                       'c_type' => $c_type,
ab6f80 300                                       'cid'    => md5(uniqid(time()))
4e17e6 301                                      );
T 302         return true;
303     }
304
305     /**
306      * Adds a file to the list of attachments.
307      *
ee289d 308      * @param string $file        The file name of the file to attach
A 309      *                             OR the file contents itself
310      * @param string $c_type      The content type
311      * @param string $name        The filename of the attachment
312      *                             Only use if $file is the contents
313      * @param bool   $isfile      Whether $file is a filename or not
314      *                             Defaults to true
315      * @param string $encoding    The type of encoding to use.
316      *                             Defaults to base64.
317      *                             Possible values: 7bit, 8bit, base64, 
318      *                             or quoted-printable.
319      * @param string $disposition The content-disposition of this file
320      *                             Defaults to attachment.
321      *                             Possible values: attachment, inline.
322      * @param string $charset     The character set used in the filename
323      *                             of this attachment.
324      * @param string $language    The language of the attachment
325      * @param string $location    The RFC 2557.4 location of the attachment
326      *
4e17e6 327      * @return mixed true on success or PEAR_Error object
T 328      * @access public
329      */
ee289d 330     function addAttachment($file,
A 331                            $c_type      = 'application/octet-stream',
332                            $name        = '',
333                             $isfile     = true,
334                            $encoding    = 'base64',
335                            $disposition = 'attachment',
336                            $charset     = '',
337                             $language   = '',
338                            $location    = '')
4e17e6 339     {
ab6f80 340         $filedata = ($isfile === true) ? $this->_file2str($file)
4e17e6 341                                            : $file;
ab6f80 342         if ($isfile === true) {
4e17e6 343             // Force the name the user supplied, otherwise use $file
ee289d 344             $filename = (strlen($name)) ? $name : $file;
4e17e6 345         } else {
T 346             $filename = $name;
347         }
ee289d 348         if (!strlen($filename)) {
A 349             $msg = "The supplied filename for the attachment can't be empty";
350             $err = PEAR::raiseError($msg);
351             return $err;
4e17e6 352         }
3b0eda 353         $filename = substr('s_'.basename($filename), 2);
4e17e6 354         if (PEAR::isError($filedata)) {
T 355             return $filedata;
356         }
357
358         $this->_parts[] = array(
856110 359                                 'body'        => $filedata,
S 360                                 'name'        => $filename,
361                                 'c_type'      => $c_type,
362                                 'encoding'    => $encoding,
a8435b 363                                 'charset'     => $charset,
ee289d 364                                 'language'    => $language,
A 365                                 'location'    => $location,
856110 366                                 'disposition' => $disposition
4e17e6 367                                );
T 368         return true;
369     }
370
371     /**
372      * Get the contents of the given file name as string
373      *
ee289d 374      * @param string $file_name path of file to process
A 375      *
4e17e6 376      * @return string  contents of $file_name
T 377      * @access private
378      */
379     function &_file2str($file_name)
380     {
ee289d 381         //Check state of file and raise an error properly
A 382         if (!file_exists($file_name)) {
383             $err = PEAR::raiseError('File not found: ' . $file_name);
384             return $err;
385         }
386         if (!is_file($file_name)) {
387             $err = PEAR::raiseError('Not a regular file: ' . $file_name);
388             return $err;
389         }
4e17e6 390         if (!is_readable($file_name)) {
ee289d 391             $err = PEAR::raiseError('File is not readable: ' . $file_name);
856110 392             return $err;
4e17e6 393         }
ee289d 394         
A 395         //Temporarily reset magic_quotes_runtime and read file contents
396         if ($magic_quote_setting = get_magic_quotes_runtime()) {
397             set_magic_quotes_runtime(0);
4e17e6 398         }
ee289d 399         $cont = file_get_contents($file_name);        
A 400         if ($magic_quote_setting) {
401             set_magic_quotes_runtime($magic_quote_setting);
15fee7 402         }
ee289d 403         
4e17e6 404         return $cont;
T 405     }
406
407     /**
408      * Adds a text subpart to the mimePart object and
409      * returns it during the build process.
410      *
ee289d 411      * @param mixed  &$obj The object to add the part to, or
A 412      *                      null if a new object is to be created.
413      * @param string $text The text to add.
414      *
4e17e6 415      * @return object  The text mimePart object
T 416      * @access private
417      */
418     function &_addTextPart(&$obj, $text)
419     {
420         $params['content_type'] = 'text/plain';
421         $params['encoding']     = $this->_build_params['text_encoding'];
422         $params['charset']      = $this->_build_params['text_charset'];
423         if (is_object($obj)) {
856110 424             $ret = $obj->addSubpart($text, $params);
S 425             return $ret;
4e17e6 426         } else {
856110 427             $ret = new Mail_mimePart($text, $params);
S 428             return $ret;
4e17e6 429         }
T 430     }
431
432     /**
433      * Adds a html subpart to the mimePart object and
434      * returns it during the build process.
435      *
ee289d 436      * @param mixed &$obj The object to add the part to, or
A 437      *                     null if a new object is to be created.
438      *
439      * @return object The html mimePart object
4e17e6 440      * @access private
T 441      */
442     function &_addHtmlPart(&$obj)
443     {
444         $params['content_type'] = 'text/html';
445         $params['encoding']     = $this->_build_params['html_encoding'];
446         $params['charset']      = $this->_build_params['html_charset'];
447         if (is_object($obj)) {
856110 448             $ret = $obj->addSubpart($this->_htmlbody, $params);
S 449             return $ret;
4e17e6 450         } else {
856110 451             $ret = new Mail_mimePart($this->_htmlbody, $params);
S 452             return $ret;
4e17e6 453         }
T 454     }
455
456     /**
457      * Creates a new mimePart object, using multipart/mixed as
458      * the initial content-type and returns it during the
459      * build process.
460      *
ee289d 461      * @return object The multipart/mixed mimePart object
4e17e6 462      * @access private
T 463      */
464     function &_addMixedPart()
465     {
ee289d 466         $params                 = array();
4e17e6 467         $params['content_type'] = 'multipart/mixed';
ee289d 468         
A 469         //Create empty multipart/mixed Mail_mimePart object to return
856110 470         $ret = new Mail_mimePart('', $params);
S 471         return $ret;
4e17e6 472     }
T 473
474     /**
475      * Adds a multipart/alternative part to a mimePart
476      * object (or creates one), and returns it during
477      * the build process.
478      *
ee289d 479      * @param mixed &$obj The object to add the part to, or
A 480      *                     null if a new object is to be created.
481      *
4e17e6 482      * @return object  The multipart/mixed mimePart object
T 483      * @access private
484      */
485     function &_addAlternativePart(&$obj)
486     {
487         $params['content_type'] = 'multipart/alternative';
488         if (is_object($obj)) {
489             return $obj->addSubpart('', $params);
490         } else {
856110 491             $ret = new Mail_mimePart('', $params);
S 492             return $ret;
4e17e6 493         }
T 494     }
495
496     /**
497      * Adds a multipart/related part to a mimePart
498      * object (or creates one), and returns it during
499      * the build process.
500      *
ee289d 501      * @param mixed &$obj The object to add the part to, or
A 502      *                     null if a new object is to be created
503      *
4e17e6 504      * @return object  The multipart/mixed mimePart object
T 505      * @access private
506      */
507     function &_addRelatedPart(&$obj)
508     {
509         $params['content_type'] = 'multipart/related';
510         if (is_object($obj)) {
511             return $obj->addSubpart('', $params);
512         } else {
856110 513             $ret = new Mail_mimePart('', $params);
S 514             return $ret;
4e17e6 515         }
T 516     }
517
518     /**
519      * Adds an html image subpart to a mimePart object
520      * and returns it during the build process.
521      *
ee289d 522      * @param object &$obj  The mimePart to add the image to
A 523      * @param array  $value The image information
524      *
4e17e6 525      * @return object  The image mimePart object
T 526      * @access private
527      */
528     function &_addHtmlImagePart(&$obj, $value)
529     {
ee289d 530         $params['content_type'] = $value['c_type'];
4e17e6 531         $params['encoding']     = 'base64';
T 532         $params['disposition']  = 'inline';
533         $params['dfilename']    = $value['name'];
534         $params['cid']          = $value['cid'];
ee289d 535         
856110 536         $ret = $obj->addSubpart($value['body'], $params);
S 537         return $ret;
ee289d 538     
4e17e6 539     }
T 540
541     /**
542      * Adds an attachment subpart to a mimePart object
543      * and returns it during the build process.
544      *
ee289d 545      * @param object &$obj  The mimePart to add the image to
A 546      * @param array  $value The attachment information
547      *
4e17e6 548      * @return object  The image mimePart object
T 549      * @access private
550      */
551     function &_addAttachmentPart(&$obj, $value)
552     {
ee289d 553         $params['dfilename'] = $value['name'];
A 554         $params['encoding']  = $value['encoding'];
a8435b 555         if ($value['charset']) {
S 556             $params['charset'] = $value['charset'];
557         }
ee289d 558         if ($value['language']) {
A 559             $params['language'] = $value['language'];
560         }
561         if ($value['location']) {
562             $params['location'] = $value['location'];
563         }
564         $params['content_type'] = $value['c_type'];
856110 565         $params['disposition']  = isset($value['disposition']) ? 
S 566                                   $value['disposition'] : 'attachment';
567         $ret = $obj->addSubpart($value['body'], $params);
568         return $ret;
4e17e6 569     }
856110 570
S 571     /**
572      * Returns the complete e-mail, ready to send using an alternative
573      * mail delivery method. Note that only the mailpart that is made
574      * with Mail_Mime is created. This means that,
575      * YOU WILL HAVE NO TO: HEADERS UNLESS YOU SET IT YOURSELF 
576      * using the $xtra_headers parameter!
577      * 
ee289d 578      * @param string $separation   The separation etween these two parts.
A 579      * @param array  $build_params The Build parameters passed to the
580      *                             &get() function. See &get for more info.
581      * @param array  $xtra_headers The extra headers that should be passed
582      *                             to the &headers() function.
583      *                             See that function for more info.
584      * @param bool   $overwrite    Overwrite the existing headers with new.
585      *
856110 586      * @return string The complete e-mail.
S 587      * @access public
588      */
ee289d 589     function getMessage(
A 590                         $separation   = null, 
591                         $build_params = null, 
592                         $xtra_headers = null, 
593                         $overwrite    = false
594                        )
856110 595     {
ee289d 596         if ($separation === null) {
856110 597             $separation = MAIL_MIME_CRLF;
S 598         }
599         $body = $this->get($build_params);
600         $head = $this->txtHeaders($xtra_headers, $overwrite);
601         $mail = $head . $separation . $body;
602         return $mail;
603     }
604
4e17e6 605
T 606     /**
607      * Builds the multipart message from the list ($this->_parts) and
608      * returns the mime content.
609      *
ee289d 610      * @param array $build_params Build parameters that change the way the email
A 611      *                             is built. Should be associative. Can contain:
a8435b 612      *                head_encoding  -  What encoding to use for the headers. 
S 613      *                                  Options: quoted-printable or base64
614      *                                  Default is quoted-printable
4e17e6 615      *                text_encoding  -  What encoding to use for plain text
ee289d 616      *                                  Options: 7bit, 8bit,
A 617      *                                  base64, or quoted-printable
4e17e6 618      *                                  Default is 7bit
T 619      *                html_encoding  -  What encoding to use for html
ee289d 620      *                                  Options: 7bit, 8bit,
A 621      *                                  base64, or quoted-printable
4e17e6 622      *                                  Default is quoted-printable
T 623      *                7bit_wrap      -  Number of characters before text is
624      *                                  wrapped in 7bit encoding
625      *                                  Default is 998
626      *                html_charset   -  The character set to use for html.
627      *                                  Default is iso-8859-1
628      *                text_charset   -  The character set to use for text.
629      *                                  Default is iso-8859-1
630      *                head_charset   -  The character set to use for headers.
631      *                                  Default is iso-8859-1
ee289d 632      *
4e17e6 633      * @return string The mime content
T 634      * @access public
635      */
636     function &get($build_params = null)
637     {
638         if (isset($build_params)) {
639             while (list($key, $value) = each($build_params)) {
640                 $this->_build_params[$key] = $value;
641             }
642         }
ee289d 643         
A 644         if (isset($this->_headers['From'])){
645             //Bug #11381: Illegal characters in domain ID
646             if (preg_match("|(@[0-9a-zA-Z\-\.]+)|", $this->_headers['From'], $matches)){
647                 $domainID = $matches[1];
648             }else{
649                 $domainID = "@localhost";
650             }
651             foreach($this->_html_images as $i => $img){
652                 $this->_html_images[$i]['cid'] = $this->_html_images[$i]['cid'] . $domainID;
653             }
654         }
655         
656         if (count($this->_html_images) AND isset($this->_htmlbody)) {
856110 657             foreach ($this->_html_images as $key => $value) {
ee289d 658                 $regex   = array();
856110 659                 $regex[] = '#(\s)((?i)src|background|href(?-i))\s*=\s*(["\']?)' .
S 660                             preg_quote($value['name'], '#') . '\3#';
661                 $regex[] = '#(?i)url(?-i)\(\s*(["\']?)' .
662                             preg_quote($value['name'], '#') . '\1\s*\)#';
ee289d 663
A 664                 $rep   = array();
856110 665                 $rep[] = '\1\2=\3cid:' . $value['cid'] .'\3';
ee289d 666                 $rep[] = 'url(\1cid:' . $value['cid'] . '\1)';
A 667
668                 $this->_htmlbody = preg_replace($regex, $rep, $this->_htmlbody);
669                 $this->_html_images[$key]['name'] = 
3b0eda 670                     substr(basename('s_'.$this->_html_images[$key]['name']), 2);
4e17e6 671             }
T 672         }
673
674         $null        = null;
ee289d 675         $attachments = count($this->_parts)                 ? true : false;
A 676         $html_images = count($this->_html_images)           ? true : false;
677         $html        = strlen($this->_htmlbody)             ? true : false;
678         $text        = (!$html AND strlen($this->_txtbody)) ? true : false;
4e17e6 679
T 680         switch (true) {
681         case $text AND !$attachments:
682             $message =& $this->_addTextPart($null, $this->_txtbody);
683             break;
684
685         case !$text AND !$html AND $attachments:
686             $message =& $this->_addMixedPart();
687             for ($i = 0; $i < count($this->_parts); $i++) {
688                 $this->_addAttachmentPart($message, $this->_parts[$i]);
689             }
690             break;
691
692         case $text AND $attachments:
693             $message =& $this->_addMixedPart();
694             $this->_addTextPart($message, $this->_txtbody);
695             for ($i = 0; $i < count($this->_parts); $i++) {
696                 $this->_addAttachmentPart($message, $this->_parts[$i]);
697             }
698             break;
699
700         case $html AND !$attachments AND !$html_images:
701             if (isset($this->_txtbody)) {
702                 $message =& $this->_addAlternativePart($null);
703                 $this->_addTextPart($message, $this->_txtbody);
704                 $this->_addHtmlPart($message);
705             } else {
706                 $message =& $this->_addHtmlPart($null);
707             }
708             break;
709
710         case $html AND !$attachments AND $html_images:
ee289d 711             $message =& $this->_addRelatedPart($null);
4e17e6 712             if (isset($this->_txtbody)) {
ee289d 713                 $alt =& $this->_addAlternativePart($message);
A 714                 $this->_addTextPart($alt, $this->_txtbody);
715                 $this->_addHtmlPart($alt);
4e17e6 716             } else {
ee289d 717                 $this->_addHtmlPart($message);
4e17e6 718             }
T 719             for ($i = 0; $i < count($this->_html_images); $i++) {
ee289d 720                 $this->_addHtmlImagePart($message, $this->_html_images[$i]);
4e17e6 721             }
T 722             break;
723
724         case $html AND $attachments AND !$html_images:
725             $message =& $this->_addMixedPart();
726             if (isset($this->_txtbody)) {
727                 $alt =& $this->_addAlternativePart($message);
728                 $this->_addTextPart($alt, $this->_txtbody);
729                 $this->_addHtmlPart($alt);
730             } else {
731                 $this->_addHtmlPart($message);
732             }
733             for ($i = 0; $i < count($this->_parts); $i++) {
734                 $this->_addAttachmentPart($message, $this->_parts[$i]);
735             }
736             break;
737
738         case $html AND $attachments AND $html_images:
739             $message =& $this->_addMixedPart();
740             if (isset($this->_txtbody)) {
741                 $alt =& $this->_addAlternativePart($message);
742                 $this->_addTextPart($alt, $this->_txtbody);
743                 $rel =& $this->_addRelatedPart($alt);
744             } else {
745                 $rel =& $this->_addRelatedPart($message);
746             }
747             $this->_addHtmlPart($rel);
748             for ($i = 0; $i < count($this->_html_images); $i++) {
749                 $this->_addHtmlImagePart($rel, $this->_html_images[$i]);
750             }
751             for ($i = 0; $i < count($this->_parts); $i++) {
752                 $this->_addAttachmentPart($message, $this->_parts[$i]);
753             }
754             break;
755
756         }
757
758         if (isset($message)) {
759             $output = $message->encode();
ee289d 760             
4e17e6 761             $this->_headers = array_merge($this->_headers,
T 762                                           $output['headers']);
856110 763             $body = $output['body'];
S 764             return $body;
4e17e6 765
T 766         } else {
856110 767             $ret = false;
S 768             return $ret;
4e17e6 769         }
T 770     }
771
772     /**
773      * Returns an array with the headers needed to prepend to the email
774      * (MIME-Version and Content-Type). Format of argument is:
775      * $array['header-name'] = 'header-value';
776      *
ee289d 777      * @param array $xtra_headers Assoc array with any extra headers.
4e17e6 778      *                             Optional.
ee289d 779      * @param bool  $overwrite    Overwrite already existing headers.
A 780      * 
4e17e6 781      * @return array Assoc array with the mime headers
T 782      * @access public
783      */
856110 784     function &headers($xtra_headers = null, $overwrite = false)
4e17e6 785     {
T 786         // Content-Type header should already be present,
787         // So just add mime version header
788         $headers['MIME-Version'] = '1.0';
789         if (isset($xtra_headers)) {
790             $headers = array_merge($headers, $xtra_headers);
791         }
ee289d 792         if ($overwrite) {
856110 793             $this->_headers = array_merge($this->_headers, $headers);
ee289d 794         } else {
856110 795             $this->_headers = array_merge($headers, $this->_headers);
S 796         }
4e17e6 797
856110 798         $encodedHeaders = $this->_encodeHeaders($this->_headers);
S 799         return $encodedHeaders;
4e17e6 800     }
T 801
802     /**
803      * Get the text version of the headers
804      * (usefull if you want to use the PHP mail() function)
805      *
ee289d 806      * @param array $xtra_headers Assoc array with any extra headers.
A 807      *                             Optional.
808      * @param bool  $overwrite    Overwrite the existing heaers with new.
809      *
4e17e6 810      * @return string  Plain text headers
T 811      * @access public
812      */
856110 813     function txtHeaders($xtra_headers = null, $overwrite = false)
4e17e6 814     {
856110 815         $headers = $this->headers($xtra_headers, $overwrite);
ee289d 816         
4e17e6 817         $ret = '';
T 818         foreach ($headers as $key => $val) {
819             $ret .= "$key: $val" . MAIL_MIME_CRLF;
820         }
821         return $ret;
822     }
823
824     /**
825      * Sets the Subject header
826      *
ee289d 827      * @param string $subject String to set the subject to.
A 828      *
829      * @return void
830      * @access public
4e17e6 831      */
T 832     function setSubject($subject)
833     {
834         $this->_headers['Subject'] = $subject;
835     }
836
837     /**
838      * Set an email to the From (the sender) header
839      *
ee289d 840      * @param string $email The email address to use
A 841      *
842      * @return void
4e17e6 843      * @access public
T 844      */
845     function setFrom($email)
846     {
847         $this->_headers['From'] = $email;
848     }
849
850     /**
851      * Add an email to the Cc (carbon copy) header
852      * (multiple calls to this method are allowed)
853      *
ee289d 854      * @param string $email The email direction to add
A 855      *
856      * @return void
4e17e6 857      * @access public
T 858      */
859     function addCc($email)
860     {
861         if (isset($this->_headers['Cc'])) {
862             $this->_headers['Cc'] .= ", $email";
863         } else {
864             $this->_headers['Cc'] = $email;
865         }
866     }
867
868     /**
869      * Add an email to the Bcc (blank carbon copy) header
870      * (multiple calls to this method are allowed)
871      *
ee289d 872      * @param string $email The email direction to add
A 873      *
874      * @return void
4e17e6 875      * @access public
T 876      */
877     function addBcc($email)
878     {
879         if (isset($this->_headers['Bcc'])) {
880             $this->_headers['Bcc'] .= ", $email";
881         } else {
882             $this->_headers['Bcc'] = $email;
883         }
884     }
885
886     /**
a8435b 887      * Since the PHP send function requires you to specifiy 
S 888      * recipients (To: header) separately from the other
889      * headers, the To: header is not properly encoded.
890      * To fix this, you can use this public method to 
891      * encode your recipients before sending to the send
892      * function
893      *
ee289d 894      * @param string $recipients A comma-delimited list of recipients
A 895      *
a8435b 896      * @return string Encoded data
S 897      * @access public
898      */
899     function encodeRecipients($recipients)
900     {
901         $input = array("To" => $recipients);
902         $retval = $this->_encodeHeaders($input);
903         return $retval["To"] ;
904     }
905
906     /**
856110 907      * Encodes a header as per RFC2047
S 908      *
ee289d 909      * @param array $input  The header data to encode
A 910      * @param array $params Extra build parameters
911      *
a8435b 912      * @return array Encoded data
856110 913      * @access private
S 914      */
ab6f80 915     function _encodeHeaders($input, $params = array())
4e17e6 916     {
ab6f80 917         
T 918         $build_params = $this->_build_params;
919         while (list($key, $value) = each($params)) {
920             $build_params[$key] = $value;
921         }
ee289d 922         //$hdr_name: Name of the heaer
A 923         //$hdr_value: Full line of header value.
924         //$atoms: The $hdr_value split into atoms*
925         //$atom: A single atom to encode.*
926         //$hdr_value_out: The recombined $hdr_val-atoms, or the encoded string.
927         //Note: Atom as specified here is not exactly the same as an RFC822 atom,
928         //as $atom's may contain just a single space.
929                 
930         $useIconv = true;        
931         if (isset($build_params['ignore-iconv'])) {
932             $useIconv = !$build_params['ignore-iconv'];
933         }            
4e17e6 934         foreach ($input as $hdr_name => $hdr_value) {
ee289d 935             /*
A 936             $parts = preg_split('/([ ])/', $hdr_value, -1, PREG_SPLIT_DELIM_CAPTURE);
937             $atoms = array();
938             foreach ($parts as $part){
939                 $atom .= $part;
940                 $quoteMatch = preg_match_all('|"|', $atom, $matches) % 2;
941                 if (!$quoteMatch){
942                     $atoms[] = $atom;
943                     $atom = null;
ab6f80 944                 }
ee289d 945             }
A 946             if ($atom){
947                 $atoms[] = $atom;
948             }
949             foreach ($atoms as $atom){
950             */
951             if (preg_match('#([\x80-\xFF]){1}#', $hdr_value)) {
952                 if (function_exists('iconv_mime_encode') && $useIconv) {
953                     $imePrefs = array();
954                     if ($build_params['head_encoding'] == 'base64') {
ab6f80 955                         $imePrefs['scheme'] = 'B';
ee289d 956                     } else {
ab6f80 957                         $imePrefs['scheme'] = 'Q';
a8435b 958                     }
ab6f80 959                     $imePrefs['input-charset']  = $build_params['head_charset'];
T 960                     $imePrefs['output-charset'] = $build_params['head_charset'];
ee289d 961                     $imePrefs['line-length'] = 74;
A 962                     $imePrefs['line-break-chars'] = "\r\n"; //Specified in RFC2047
963                     
964                     $hdr_value = iconv_mime_encode($hdr_name, $hdr_value, $imePrefs);
965                     $hdr_value = preg_replace("#^{$hdr_name}\:\ #", "", $hdr_value);
966                 } elseif ($build_params['head_encoding'] == 'base64') {
967                     //Base64 encoding has been selected.
968                     //Base64 encode the entire string
969                     $hdr_value = base64_encode($hdr_value);
970                     
971                     //Generate the header using the specified params and dynamicly 
972                     //determine the maximum length of such strings.
973                     //75 is the value specified in the RFC. The first -2 is there so 
974                     //the later regexp doesn't break any of the translated chars.
975                     //The -2 on the first line-regexp is to compensate for the ": "
976                     //between the header-name and the header value
977                     $prefix = '=?' . $build_params['head_charset'] . '?B?';
978                     $suffix = '?=';
979                     $maxLength = 75 - strlen($prefix . $suffix) - 2;
980                     $maxLength1stLine = $maxLength - strlen($hdr_name) - 2;
981
982                     //We can cut base4 every 4 characters, so the real max
983                     //we can get must be rounded down.
984                     $maxLength = $maxLength - ($maxLength % 4);
985                     $maxLength1stLine = $maxLength1stLine - ($maxLength1stLine % 4);
986                     
987                     $cutpoint = $maxLength1stLine;
988                     $hdr_value_out = $hdr_value;
ab6f80 989                     $output = "";
ee289d 990                     while ($hdr_value_out) {
ab6f80 991                         //Split translated string at every $maxLength
ee289d 992                         $part = substr($hdr_value_out, 0, $cutpoint);
A 993                         $hdr_value_out = substr($hdr_value_out, $cutpoint);
994                         $cutpoint = $maxLength;
995                         //RFC 2047 specifies that any split header should 
996                         //be seperated by a CRLF SPACE. 
997                         if ($output) {
ab6f80 998                             $output .=  "\r\n ";
T 999                         }
1000                         $output .= $prefix . $part . $suffix;
1001                     }
ee289d 1002                     $hdr_value = $output;
A 1003                 } else {
1004                     //quoted-printable encoding has been selected
856110 1005
ee289d 1006                     //Fix for Bug #10298, Ota Mares <om@viazenetti.de>
A 1007                     //Check if there is a double quote at beginning or end of
1008                     //the string to prevent that an open or closing quote gets 
1009                     //ignored because it is encapsuled by an encoding pre/suffix.
1010                     //Remove the double quote and set the specific prefix or 
1011                     //suffix variable so that we can concat the encoded string and
1012                     //the double quotes back together to get the intended string.
1013                     $quotePrefix = $quoteSuffix = '';
1014                     if ($hdr_value{0} == '"') {
1015                         $hdr_value = substr($hdr_value, 1);
1016                         $quotePrefix = '"';
1017                     }
1018                     if ($hdr_value{strlen($hdr_value)-1} == '"') {
1019                         $hdr_value = substr($hdr_value, 0, -1);
1020                         $quoteSuffix = '"';
1021                     }
1022                     
1023                     //Generate the header using the specified params and dynamicly 
1024                     //determine the maximum length of such strings.
1025                     //75 is the value specified in the RFC. The -2 is there so 
1026                     //the later regexp doesn't break any of the translated chars.
1027                     //The -2 on the first line-regexp is to compensate for the ": "
1028                     //between the header-name and the header value
1029                     $prefix = '=?' . $build_params['head_charset'] . '?Q?';
1030                     $suffix = '?=';
1031                     $maxLength = 75 - strlen($prefix . $suffix) - 2 - 1;
1032                     $maxLength1stLine = $maxLength - strlen($hdr_name) - 2;
1033                     $maxLength = $maxLength - 1;
1034                     
1035                     //Replace all special characters used by the encoder.
1036                     $search  = array('=',   '_',   '?',   ' ');
1037                     $replace = array('=3D', '=5F', '=3F', '_');
1038                     $hdr_value = str_replace($search, $replace, $hdr_value);
1039                     
1040                     //Replace all extended characters (\x80-xFF) with their
1041                     //ASCII values.
1042                     $hdr_value = preg_replace('#([\x80-\xFF])#e',
1043                         '"=" . strtoupper(dechex(ord("\1")))',
1044                         $hdr_value);
1045
1046                     //This regexp will break QP-encoded text at every $maxLength
1047                     //but will not break any encoded letters.
1048                     $reg1st = "|(.{0,$maxLength1stLine}[^\=][^\=])|";
1049                     $reg2nd = "|(.{0,$maxLength}[^\=][^\=])|";
1050                     //Fix for Bug #10298, Ota Mares <om@viazenetti.de>
1051                     //Concat the double quotes and encoded string together
1052                     $hdr_value = $quotePrefix . $hdr_value . $quoteSuffix;
1053                     
1054
1055                     $hdr_value_out = $hdr_value;
1056                     $realMax = $maxLength1stLine + strlen($prefix . $suffix);
1057                     if (strlen($hdr_value_out) >= $realMax) {
1058                         //Begin with the regexp for the first line.
1059                         $reg = $reg1st;
1060                         $output = "";
1061                         while ($hdr_value_out) {
1062                             //Split translated string at every $maxLength
1063                             //But make sure not to break any translated chars.
1064                             $found = preg_match($reg, $hdr_value_out, $matches);
1065                             
1066                             //After this first line, we need to use a different
1067                             //regexp for the first line.
1068                             $reg = $reg2nd;
1069                             
1070                             //Save the found part and encapsulate it in the
1071                             //prefix & suffix. Then remove the part from the
1072                             //$hdr_value_out variable.
1073                             if ($found) {
1074                                 $part = $matches[0];
1075                                 $len = strlen($matches[0]);
1076                                 $hdr_value_out = substr($hdr_value_out, $len);
1077                             } else {
1078                                 $part = $hdr_value_out;
1079                                 $hdr_value_out = "";
1080                             }
1081                             
1082                             //RFC 2047 specifies that any split header should 
1083                             //be seperated by a CRLF SPACE
1084                             if ($output) {
1085                                 $output .=  "\r\n ";
1086                             }
1087                             $output .= $prefix . $part . $suffix;
1088                         }
1089                         $hdr_value_out = $output;
1090                     } else {
1091                         $hdr_value_out = $prefix . $hdr_value_out . $suffix;
1092                     }
1093                     $hdr_value = $hdr_value_out;
1094                 }
1095             }
1096             $input[$hdr_name] = $hdr_value;
1097         }
4e17e6 1098         return $input;
T 1099     }
bb5ddf 1100
4e17e6 1101     /**
ee289d 1102      * Set the object's end-of-line and define the constant if applicable.
4e17e6 1103      *
T 1104      * @param string $eol End Of Line sequence
ee289d 1105      *
A 1106      * @return void
4e17e6 1107      * @access private
T 1108      */
1109     function _setEOL($eol)
1110     {
1111         $this->_eol = $eol;
1112         if (!defined('MAIL_MIME_CRLF')) {
1113             define('MAIL_MIME_CRLF', $this->_eol, true);
1114         }
1115     }
1116
1117     
1118
1119 } // End of class