Ignore:
Timestamp:
2011/01/17 14:34:18 (13 years ago)
Author:
Seasoft
Message:

#403(インクルードしているライブラリ群をバージョンアップする)

  • 一部に不正と思われる文字エンコードがあり開発に支障があるため、バージョンアップで解決を図る。
  • Mail_Mime 1.8.1
  • Mail_mimeDecode 1.5.5
Location:
branches/version-2_5-dev/data/module/Mail
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • branches/version-2_5-dev/data/module/Mail/mime.php

    r15532 r19941  
    11<?php 
    2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 
    3 // +-----------------------------------------------------------------------+ 
    4 // | Copyright (c) 2002-2003  Richard Heyes                                | 
    5 // | Copyright (c) 2003-2005  The PHP Group                                | 
    6 // | All rights reserved.                                                  | 
    7 // |                                                                       | 
    8 // | Redistribution and use in source and binary forms, with or without    | 
    9 // | modification, are permitted provided that the following conditions    | 
    10 // | are met:                                                              | 
    11 // |                                                                       | 
    12 // | o Redistributions of source code must retain the above copyright      | 
    13 // |   notice, this list of conditions and the following disclaimer.       | 
    14 // | o Redistributions in binary form must reproduce the above copyright   | 
    15 // |   notice, this list of conditions and the following disclaimer in the | 
    16 // |   documentation and/or other materials provided with the distribution.| 
    17 // | o The names of the authors may not be used to endorse or promote      | 
    18 // |   products derived from this software without specific prior written  | 
    19 // |   permission.                                                         | 
    20 // |                                                                       | 
    21 // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   | 
    22 // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     | 
    23 // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
    24 // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  | 
    25 // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
    26 // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      | 
    27 // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
    28 // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
    29 // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   | 
    30 // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
    31 // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  | 
    32 // |                                                                       | 
    33 // +-----------------------------------------------------------------------+ 
    34 // | Author: Richard Heyes <richard@phpguru.org>                           | 
    35 // |         Tomas V.V.Cox <cox@idecnet.com> (port to PEAR)                | 
    36 // +-----------------------------------------------------------------------+ 
    37 // 
    38 // $Id$ 
    39  
    40 require_once(dirname(__FILE__) . '/../PEAR.php'); 
    41 require_once(dirname(__FILE__) . '/mimePart.php'); 
    42  
    432/** 
    44  * Mime mail composer class. Can handle: text and html bodies, embedded html 
    45  * images and attachments. 
    46  * Documentation and examples of this class are avaible here: 
    47  * http://pear.php.net/manual/ 
     3 * The Mail_Mime class is used to create MIME E-mail messages 
    484 * 
    49  * @notes This class is based on HTML Mime Mail class from 
    50  *   Richard Heyes <richard@phpguru.org> which was based also 
    51  *   in the mime_mail.class by Tobias Ratschiller <tobias@dnet.it> and 
    52  *   Sascha Schumann <sascha@schumann.cx> 
     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. 
    539 * 
    54  * @author   Richard Heyes <richard.heyes@heyes-computing.net> 
    55  * @author   Tomas V.V.Cox <cox@idecnet.com> 
    56  * @package  Mail 
    57  * @access   public 
     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 * 
     42 * @category  Mail 
     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 * @author    Aleksander Machniak <alec@php.net> 
     49 * @copyright 2003-2006 PEAR <pear-group@php.net> 
     50 * @license   http://www.opensource.org/licenses/bsd-license.php BSD License 
     51 * @version   CVS: $Id$ 
     52 * @link      http://pear.php.net/package/Mail_mime 
     53 * 
     54 *            This class is based on HTML Mime Mail class from 
     55 *            Richard Heyes <richard@phpguru.org> which was based also 
     56 *            in the mime_mail.class by Tobias Ratschiller <tobias@dnet.it> 
     57 *            and Sascha Schumann <sascha@schumann.cx> 
     58 */ 
     59 
     60 
     61/** 
     62 * require PEAR 
     63 * 
     64 * This package depends on PEAR to raise errors. 
     65 */ 
     66require_once 'PEAR.php'; 
     67 
     68/** 
     69 * require Mail_mimePart 
     70 * 
     71 * Mail_mimePart contains the code required to 
     72 * create all the different parts a mail can 
     73 * consist of. 
     74 */ 
     75require_once 'Mail/mimePart.php'; 
     76 
     77 
     78/** 
     79 * The Mail_Mime class provides an OO interface to create MIME 
     80 * enabled email messages. This way you can create emails that 
     81 * contain plain-text bodies, HTML bodies, attachments, inline 
     82 * images and specific headers. 
     83 * 
     84 * @category  Mail 
     85 * @package   Mail_Mime 
     86 * @author    Richard Heyes  <richard@phpguru.org> 
     87 * @author    Tomas V.V. Cox <cox@idecnet.com> 
     88 * @author    Cipriano Groenendal <cipri@php.net> 
     89 * @author    Sean Coates <sean@php.net> 
     90 * @copyright 2003-2006 PEAR <pear-group@php.net> 
     91 * @license   http://www.opensource.org/licenses/bsd-license.php BSD License 
     92 * @version   Release: @package_version@ 
     93 * @link      http://pear.php.net/package/Mail_mime 
    5894 */ 
    5995class Mail_mime 
     
    6197    /** 
    6298     * Contains the plain text part of the email 
     99     * 
    63100     * @var string 
     101     * @access private 
    64102     */ 
    65103    var $_txtbody; 
     104 
    66105    /** 
    67106     * Contains the html part of the email 
     107     * 
    68108     * @var string 
     109     * @access private 
    69110     */ 
    70111    var $_htmlbody; 
    71     /** 
    72      * contains the mime encoded text 
    73      * @var string 
    74      */ 
    75     var $_mime; 
    76     /** 
    77      * contains the multipart content 
    78      * @var string 
    79      */ 
    80     var $_multipart; 
     112 
    81113    /** 
    82114     * list of the attached images 
     115     * 
    83116     * @var array 
     117     * @access private 
    84118     */ 
    85119    var $_html_images = array(); 
     120 
    86121    /** 
    87122     * list of the attachements 
     123     * 
    88124     * @var array 
     125     * @access private 
    89126     */ 
    90127    var $_parts = array(); 
     128 
     129    /** 
     130     * Headers for the mail 
     131     * 
     132     * @var array 
     133     * @access private 
     134     */ 
     135    var $_headers = array(); 
     136 
    91137    /** 
    92138     * Build parameters 
     139     * 
    93140     * @var array 
    94      */ 
    95     var $_build_params = array(); 
    96     /** 
    97      * Headers for the mail 
    98      * @var array 
    99      */ 
    100     var $_headers = array(); 
    101     /** 
    102      * End Of Line sequence (for serialize) 
    103      * @var string 
    104      */ 
    105     var $_eol; 
    106  
     141     * @access private 
     142     */ 
     143    var $_build_params = array( 
     144        // What encoding to use for the headers 
     145        // Options: quoted-printable or base64 
     146        'head_encoding' => 'quoted-printable', 
     147        // What encoding to use for plain text 
     148        // Options: 7bit, 8bit, base64, or quoted-printable 
     149        'text_encoding' => 'quoted-printable', 
     150        // What encoding to use for html 
     151        // Options: 7bit, 8bit, base64, or quoted-printable 
     152        'html_encoding' => 'quoted-printable', 
     153        // The character set to use for html 
     154        'html_charset'  => 'ISO-8859-1', 
     155        // The character set to use for text 
     156        'text_charset'  => 'ISO-8859-1', 
     157        // The character set to use for headers 
     158        'head_charset'  => 'ISO-8859-1', 
     159        // End-of-line sequence 
     160        'eol'           => "\r\n", 
     161        // Delay attachment files IO until building the message 
     162        'delay_file_io' => false 
     163    ); 
    107164 
    108165    /** 
    109166     * Constructor function 
    110167     * 
    111      * @access public 
    112      */ 
    113     function Mail_mime($crlf = "\r\n") 
    114     { 
    115         $this->_setEOL($crlf); 
    116         $this->_build_params = array( 
    117                                      'text_encoding' => '7bit', 
    118                                      'html_encoding' => 'quoted-printable', 
    119                                      '7bit_wrap'     => 998, 
    120                                      'html_charset'  => 'ISO-8859-1', 
    121                                      'text_charset'  => 'ISO-8859-1', 
    122                                      'head_charset'  => 'ISO-8859-1' 
    123                                     ); 
    124     } 
    125  
    126     /** 
    127      * Wakeup (unserialize) - re-sets EOL constant 
    128      * 
    129      * @access private 
    130      */ 
    131     function __wakeup() 
    132     { 
    133         $this->_setEOL($this->_eol); 
     168     * @param mixed $params Build parameters that change the way the email 
     169     *                      is built. Should be an associative array. 
     170     *                      See $_build_params. 
     171     * 
     172     * @return void 
     173     * @access public 
     174     */ 
     175    function Mail_mime($params = array()) 
     176    { 
     177        // Backward-compatible EOL setting 
     178        if (is_string($params)) { 
     179            $this->_build_params['eol'] = $params; 
     180        } else if (defined('MAIL_MIME_CRLF') && !isset($params['eol'])) { 
     181            $this->_build_params['eol'] = MAIL_MIME_CRLF; 
     182        } 
     183 
     184        // Update build parameters 
     185        if (!empty($params) && is_array($params)) { 
     186            while (list($key, $value) = each($params)) { 
     187                $this->_build_params[$key] = $value; 
     188            } 
     189        } 
     190    } 
     191 
     192    /** 
     193     * Set build parameter value 
     194     * 
     195     * @param string $name  Parameter name 
     196     * @param string $value Parameter value 
     197     * 
     198     * @return void 
     199     * @access public 
     200     * @since 1.6.0 
     201     */ 
     202    function setParam($name, $value) 
     203    { 
     204        $this->_build_params[$name] = $value; 
     205    } 
     206 
     207    /** 
     208     * Get build parameter value 
     209     * 
     210     * @param string $name Parameter name 
     211     * 
     212     * @return mixed Parameter value 
     213     * @access public 
     214     * @since 1.6.0 
     215     */ 
     216    function getParam($name) 
     217    { 
     218        return isset($this->_build_params[$name]) ? $this->_build_params[$name] : null; 
    134219    } 
    135220 
     
    140225     * html should show. 
    141226     * 
    142      * @param  string  $data   Either a string or 
    143      *                         the file name with the contents 
    144      * @param  bool    $isfile If true the first param should be treated 
    145      *                         as a file name, else as a string (default) 
    146      * @param  bool    $append If true the text or file is appended to 
    147      *                         the existing body, else the old body is 
    148      *                         overwritten 
    149      * @return mixed   true on success or PEAR_Error object 
     227     * @param string $data   Either a string or 
     228     *                       the file name with the contents 
     229     * @param bool   $isfile If true the first param should be treated 
     230     *                       as a file name, else as a string (default) 
     231     * @param bool   $append If true the text or file is appended to 
     232     *                       the existing body, else the old body is 
     233     *                       overwritten 
     234     * 
     235     * @return mixed         True on success or PEAR_Error object 
    150236     * @access public 
    151237     */ 
     
    173259 
    174260    /** 
    175      * Adds a html part to the mail 
    176      * 
    177      * @param  string  $data   Either a string or the file name with the 
    178      *                         contents 
    179      * @param  bool    $isfile If true the first param should be treated 
    180      *                         as a file name, else as a string (default) 
    181      * @return mixed   true on success or PEAR_Error object 
     261     * Get message text body 
     262     * 
     263     * @return string Text body 
     264     * @access public 
     265     * @since 1.6.0 
     266     */ 
     267    function getTXTBody() 
     268    { 
     269        return $this->_txtbody; 
     270    } 
     271 
     272    /** 
     273     * Adds a html part to the mail. 
     274     * 
     275     * @param string $data   Either a string or the file name with the 
     276     *                       contents 
     277     * @param bool   $isfile A flag that determines whether $data is a 
     278     *                       filename, or a string(false, default) 
     279     * 
     280     * @return bool          True on success 
    182281     * @access public 
    183282     */ 
     
    198297 
    199298    /** 
     299     * Get message HTML body 
     300     * 
     301     * @return string HTML body 
     302     * @access public 
     303     * @since 1.6.0 
     304     */ 
     305    function getHTMLBody() 
     306    { 
     307        return $this->_htmlbody; 
     308    } 
     309 
     310    /** 
    200311     * Adds an image to the list of embedded images. 
    201312     * 
    202      * @param  string  $file       The image file name OR image data itself 
    203      * @param  string  $c_type     The content type 
    204      * @param  string  $name       The filename of the image. 
    205      *                             Only use if $file is the image data 
    206      * @param  bool    $isfilename Whether $file is a filename or not 
    207      *                             Defaults to true 
    208      * @return mixed   true on success or PEAR_Error object 
    209      * @access public 
    210      */ 
    211     function addHTMLImage($file, $c_type='application/octet-stream', 
    212                           $name = '', $isfilename = true) 
    213     { 
    214         $filedata = ($isfilename === true) ? $this->_file2str($file) 
    215                                            : $file; 
    216         if ($isfilename === true) { 
    217             $filename = ($name == '' ? basename($file) : basename($name)); 
    218         } else { 
    219             $filename = basename($name); 
    220         } 
    221         if (PEAR::isError($filedata)) { 
    222             return $filedata; 
    223         } 
     313     * @param string $file       The image file name OR image data itself 
     314     * @param string $c_type     The content type 
     315     * @param string $name       The filename of the image. 
     316     *                           Only used if $file is the image data. 
     317     * @param bool   $isfile     Whether $file is a filename or not. 
     318     *                           Defaults to true 
     319     * @param string $content_id Desired Content-ID of MIME part 
     320     *                           Defaults to generated unique ID 
     321     * 
     322     * @return bool          True on success 
     323     * @access public 
     324     */ 
     325    function addHTMLImage($file, 
     326        $c_type='application/octet-stream', 
     327        $name = '', 
     328        $isfile = true, 
     329        $content_id = null 
     330    ) { 
     331        $bodyfile = null; 
     332 
     333        if ($isfile) { 
     334            // Don't load file into memory 
     335            if ($this->_build_params['delay_file_io']) { 
     336                $filedata = null; 
     337                $bodyfile = $file; 
     338            } else { 
     339                if (PEAR::isError($filedata = $this->_file2str($file))) { 
     340                    return $filedata; 
     341                } 
     342            } 
     343            $filename = ($name ? $name : $file); 
     344        } else { 
     345            $filedata = $file; 
     346            $filename = $name; 
     347        } 
     348 
     349        if (!$content_id) { 
     350            $content_id = md5(uniqid(time())); 
     351        } 
     352 
    224353        $this->_html_images[] = array( 
    225                                       'body'   => $filedata, 
    226                                       'name'   => $filename, 
    227                                       'c_type' => $c_type, 
    228                                       'cid'    => md5(uniqid(time())) 
    229                                      ); 
     354            'body'      => $filedata, 
     355            'body_file' => $bodyfile, 
     356            'name'      => $filename, 
     357            'c_type'    => $c_type, 
     358            'cid'       => $content_id 
     359        ); 
     360 
    230361        return true; 
    231362    } 
     
    234365     * Adds a file to the list of attachments. 
    235366     * 
    236      * @param  string  $file       The file name of the file to attach 
    237      *                             OR the file data itself 
    238      * @param  string  $c_type     The content type 
    239      * @param  string  $name       The filename of the attachment 
    240      *                             Only use if $file is the file data 
    241      * @param  bool    $isFilename Whether $file is a filename or not 
    242      *                             Defaults to true 
    243      * @return mixed true on success or PEAR_Error object 
    244      * @access public 
    245      */ 
    246     function addAttachment($file, $c_type = 'application/octet-stream', 
    247                            $name = '', $isfilename = true, 
    248                            $encoding = 'base64') 
    249     { 
    250         $filedata = ($isfilename === true) ? $this->_file2str($file) 
    251                                            : $file; 
    252         if ($isfilename === true) { 
     367     * @param string $file        The file name of the file to attach 
     368     *                            or the file contents itself 
     369     * @param string $c_type      The content type 
     370     * @param string $name        The filename of the attachment 
     371     *                            Only use if $file is the contents 
     372     * @param bool   $isfile      Whether $file is a filename or not. Defaults to true 
     373     * @param string $encoding    The type of encoding to use. Defaults to base64. 
     374     *                            Possible values: 7bit, 8bit, base64 or quoted-printable. 
     375     * @param string $disposition The content-disposition of this file 
     376     *                            Defaults to attachment. 
     377     *                            Possible values: attachment, inline. 
     378     * @param string $charset     The character set of attachment's content. 
     379     * @param string $language    The language of the attachment 
     380     * @param string $location    The RFC 2557.4 location of the attachment 
     381     * @param string $n_encoding  Encoding of the attachment's name in Content-Type 
     382     *                            By default filenames are encoded using RFC2231 method 
     383     *                            Here you can set RFC2047 encoding (quoted-printable 
     384     *                            or base64) instead 
     385     * @param string $f_encoding  Encoding of the attachment's filename 
     386     *                            in Content-Disposition header. 
     387     * @param string $description Content-Description header 
     388     * @param string $h_charset   The character set of the headers e.g. filename 
     389     *                            If not specified, $charset will be used 
     390     * 
     391     * @return mixed              True on success or PEAR_Error object 
     392     * @access public 
     393     */ 
     394    function addAttachment($file, 
     395        $c_type      = 'application/octet-stream', 
     396        $name        = '', 
     397        $isfile      = true, 
     398        $encoding    = 'base64', 
     399        $disposition = 'attachment', 
     400        $charset     = '', 
     401        $language    = '', 
     402        $location    = '', 
     403        $n_encoding  = null, 
     404        $f_encoding  = null, 
     405        $description = '', 
     406        $h_charset   = null 
     407    ) { 
     408        $bodyfile = null; 
     409 
     410        if ($isfile) { 
     411            // Don't load file into memory 
     412            if ($this->_build_params['delay_file_io']) { 
     413                $filedata = null; 
     414                $bodyfile = $file; 
     415            } else { 
     416                if (PEAR::isError($filedata = $this->_file2str($file))) { 
     417                    return $filedata; 
     418                } 
     419            } 
    253420            // Force the name the user supplied, otherwise use $file 
    254             $filename = (!empty($name)) ? $name : $file; 
    255         } else { 
     421            $filename = ($name ? $name : $file); 
     422        } else { 
     423            $filedata = $file; 
    256424            $filename = $name; 
    257425        } 
    258         if (empty($filename)) { 
    259             return PEAR::raiseError( 
    260               'The supplied filename for the attachment can\'t be empty' 
    261             ); 
    262         } 
    263         $filename = basename($filename); 
    264         if (PEAR::isError($filedata)) { 
    265             return $filedata; 
    266         } 
     426 
     427        if (!strlen($filename)) { 
     428            $msg = "The supplied filename for the attachment can't be empty"; 
     429            $err = PEAR::raiseError($msg); 
     430            return $err; 
     431        } 
     432        $filename = $this->_basename($filename); 
    267433 
    268434        $this->_parts[] = array( 
    269                                 'body'     => $filedata, 
    270                                 'name'     => $filename, 
    271                                 'c_type'   => $c_type, 
    272                                 'encoding' => $encoding 
    273                                ); 
     435            'body'        => $filedata, 
     436            'body_file'   => $bodyfile, 
     437            'name'        => $filename, 
     438            'c_type'      => $c_type, 
     439            'charset'     => $charset, 
     440            'encoding'    => $encoding, 
     441            'language'    => $language, 
     442            'location'    => $location, 
     443            'disposition' => $disposition, 
     444            'description' => $description, 
     445            'name_encoding'     => $n_encoding, 
     446            'filename_encoding' => $f_encoding, 
     447            'headers_charset'   => $h_charset, 
     448        ); 
     449 
    274450        return true; 
    275451    } 
     
    278454     * Get the contents of the given file name as string 
    279455     * 
    280      * @param  string  $file_name  path of file to process 
    281      * @return string  contents of $file_name 
     456     * @param string $file_name Path of file to process 
     457     * 
     458     * @return string           Contents of $file_name 
    282459     * @access private 
    283460     */ 
    284461    function &_file2str($file_name) 
    285462    { 
     463        // Check state of file and raise an error properly 
     464        if (!file_exists($file_name)) { 
     465            $err = PEAR::raiseError('File not found: ' . $file_name); 
     466            return $err; 
     467        } 
     468        if (!is_file($file_name)) { 
     469            $err = PEAR::raiseError('Not a regular file: ' . $file_name); 
     470            return $err; 
     471        } 
    286472        if (!is_readable($file_name)) { 
    287             return PEAR::raiseError('File is not readable ' . $file_name); 
    288         } 
    289         if (!$fd = fopen($file_name, 'rb')) { 
    290             return PEAR::raiseError('Could not open ' . $file_name); 
    291         } 
    292         $filesize = filesize($file_name); 
    293         if ($filesize == 0){ 
    294             $cont =  ""; 
    295         }else{ 
    296             $cont = fread($fd, $filesize); 
    297         } 
    298         fclose($fd); 
     473            $err = PEAR::raiseError('File is not readable: ' . $file_name); 
     474            return $err; 
     475        } 
     476 
     477        // Temporarily reset magic_quotes_runtime and read file contents 
     478        if ($magic_quote_setting = get_magic_quotes_runtime()) { 
     479            @ini_set('magic_quotes_runtime', 0); 
     480        } 
     481        $cont = file_get_contents($file_name); 
     482        if ($magic_quote_setting) { 
     483            @ini_set('magic_quotes_runtime', $magic_quote_setting); 
     484        } 
     485 
    299486        return $cont; 
    300487    } 
     
    304491     * returns it during the build process. 
    305492     * 
    306      * @param mixed    The object to add the part to, or 
    307      *                 null if a new object is to be created. 
    308      * @param string   The text to add. 
    309      * @return object  The text mimePart object 
     493     * @param mixed  &$obj The object to add the part to, or 
     494     *                     null if a new object is to be created. 
     495     * @param string $text The text to add. 
     496     * 
     497     * @return object      The text mimePart object 
    310498     * @access private 
    311499     */ 
     
    315503        $params['encoding']     = $this->_build_params['text_encoding']; 
    316504        $params['charset']      = $this->_build_params['text_charset']; 
     505        $params['eol']          = $this->_build_params['eol']; 
     506 
    317507        if (is_object($obj)) { 
    318             return $obj->addSubpart($text, $params); 
    319         } else { 
    320             return new Mail_mimePart($text, $params); 
     508            $ret = $obj->addSubpart($text, $params); 
     509            return $ret; 
     510        } else { 
     511            $ret = new Mail_mimePart($text, $params); 
     512            return $ret; 
    321513        } 
    322514    } 
     
    326518     * returns it during the build process. 
    327519     * 
    328      * @param  mixed   The object to add the part to, or 
    329      *                 null if a new object is to be created. 
    330      * @return object  The html mimePart object 
     520     * @param mixed &$obj The object to add the part to, or 
     521     *                    null if a new object is to be created. 
     522     * 
     523     * @return object     The html mimePart object 
    331524     * @access private 
    332525     */ 
     
    336529        $params['encoding']     = $this->_build_params['html_encoding']; 
    337530        $params['charset']      = $this->_build_params['html_charset']; 
     531        $params['eol']          = $this->_build_params['eol']; 
     532 
    338533        if (is_object($obj)) { 
    339             return $obj->addSubpart($this->_htmlbody, $params); 
    340         } else { 
    341             return new Mail_mimePart($this->_htmlbody, $params); 
     534            $ret = $obj->addSubpart($this->_htmlbody, $params); 
     535            return $ret; 
     536        } else { 
     537            $ret = new Mail_mimePart($this->_htmlbody, $params); 
     538            return $ret; 
    342539        } 
    343540    } 
     
    348545     * build process. 
    349546     * 
    350      * @return object  The multipart/mixed mimePart object 
     547     * @return object The multipart/mixed mimePart object 
    351548     * @access private 
    352549     */ 
    353550    function &_addMixedPart() 
    354551    { 
     552        $params                 = array(); 
    355553        $params['content_type'] = 'multipart/mixed'; 
    356         return new Mail_mimePart('', $params); 
     554        $params['eol']          = $this->_build_params['eol']; 
     555 
     556        // Create empty multipart/mixed Mail_mimePart object to return 
     557        $ret = new Mail_mimePart('', $params); 
     558        return $ret; 
    357559    } 
    358560 
     
    362564     * the build process. 
    363565     * 
    364      * @param  mixed   The object to add the part to, or 
    365      *                 null if a new object is to be created. 
    366      * @return object  The multipart/mixed mimePart object 
     566     * @param mixed &$obj The object to add the part to, or 
     567     *                    null if a new object is to be created. 
     568     * 
     569     * @return object     The multipart/mixed mimePart object 
    367570     * @access private 
    368571     */ 
     
    370573    { 
    371574        $params['content_type'] = 'multipart/alternative'; 
     575        $params['eol']          = $this->_build_params['eol']; 
     576 
    372577        if (is_object($obj)) { 
    373578            return $obj->addSubpart('', $params); 
    374579        } else { 
    375             return new Mail_mimePart('', $params); 
     580            $ret = new Mail_mimePart('', $params); 
     581            return $ret; 
    376582        } 
    377583    } 
     
    382588     * the build process. 
    383589     * 
    384      * @param mixed    The object to add the part to, or 
    385      *                 null if a new object is to be created 
    386      * @return object  The multipart/mixed mimePart object 
     590     * @param mixed &$obj The object to add the part to, or 
     591     *                    null if a new object is to be created 
     592     * 
     593     * @return object     The multipart/mixed mimePart object 
    387594     * @access private 
    388595     */ 
     
    390597    { 
    391598        $params['content_type'] = 'multipart/related'; 
     599        $params['eol']          = $this->_build_params['eol']; 
     600 
    392601        if (is_object($obj)) { 
    393602            return $obj->addSubpart('', $params); 
    394603        } else { 
    395             return new Mail_mimePart('', $params); 
     604            $ret = new Mail_mimePart('', $params); 
     605            return $ret; 
    396606        } 
    397607    } 
     
    401611     * and returns it during the build process. 
    402612     * 
    403      * @param  object  The mimePart to add the image to 
    404      * @param  array   The image information 
    405      * @return object  The image mimePart object 
     613     * @param object &$obj  The mimePart to add the image to 
     614     * @param array  $value The image information 
     615     * 
     616     * @return object       The image mimePart object 
    406617     * @access private 
    407618     */ 
     
    411622        $params['encoding']     = 'base64'; 
    412623        $params['disposition']  = 'inline'; 
    413         $params['dfilename']    = $value['name']; 
     624        $params['filename']     = $value['name']; 
    414625        $params['cid']          = $value['cid']; 
    415         $obj->addSubpart($value['body'], $params); 
     626        $params['body_file']    = $value['body_file']; 
     627        $params['eol']          = $this->_build_params['eol']; 
     628 
     629        if (!empty($value['name_encoding'])) { 
     630            $params['name_encoding'] = $value['name_encoding']; 
     631        } 
     632        if (!empty($value['filename_encoding'])) { 
     633            $params['filename_encoding'] = $value['filename_encoding']; 
     634        } 
     635 
     636        $ret = $obj->addSubpart($value['body'], $params); 
     637        return $ret; 
    416638    } 
    417639 
     
    420642     * and returns it during the build process. 
    421643     * 
    422      * @param  object  The mimePart to add the image to 
    423      * @param  array   The attachment information 
    424      * @return object  The image mimePart object 
     644     * @param object &$obj  The mimePart to add the image to 
     645     * @param array  $value The attachment information 
     646     * 
     647     * @return object       The image mimePart object 
    425648     * @access private 
    426649     */ 
    427650    function &_addAttachmentPart(&$obj, $value) 
    428651    { 
     652        $params['eol']          = $this->_build_params['eol']; 
     653        $params['filename']     = $value['name']; 
     654        $params['encoding']     = $value['encoding']; 
    429655        $params['content_type'] = $value['c_type']; 
    430         $params['encoding']     = $value['encoding']; 
    431         $params['disposition']  = 'attachment'; 
    432         $params['dfilename']    = $value['name']; 
    433         $obj->addSubpart($value['body'], $params); 
     656        $params['body_file']    = $value['body_file']; 
     657        $params['disposition']  = isset($value['disposition']) ?  
     658                                  $value['disposition'] : 'attachment'; 
     659 
     660        // content charset 
     661        if (!empty($value['charset'])) { 
     662            $params['charset'] = $value['charset']; 
     663        } 
     664        // headers charset (filename, description) 
     665        if (!empty($value['headers_charset'])) { 
     666            $params['headers_charset'] = $value['headers_charset']; 
     667        } 
     668        if (!empty($value['language'])) { 
     669            $params['language'] = $value['language']; 
     670        } 
     671        if (!empty($value['location'])) { 
     672            $params['location'] = $value['location']; 
     673        } 
     674        if (!empty($value['name_encoding'])) { 
     675            $params['name_encoding'] = $value['name_encoding']; 
     676        } 
     677        if (!empty($value['filename_encoding'])) { 
     678            $params['filename_encoding'] = $value['filename_encoding']; 
     679        } 
     680        if (!empty($value['description'])) { 
     681            $params['description'] = $value['description']; 
     682        } 
     683 
     684        $ret = $obj->addSubpart($value['body'], $params); 
     685        return $ret; 
     686    } 
     687 
     688    /** 
     689     * Returns the complete e-mail, ready to send using an alternative 
     690     * mail delivery method. Note that only the mailpart that is made 
     691     * with Mail_Mime is created. This means that, 
     692     * YOU WILL HAVE NO TO: HEADERS UNLESS YOU SET IT YOURSELF  
     693     * using the $headers parameter! 
     694     *  
     695     * @param string $separation The separation between these two parts. 
     696     * @param array  $params     The Build parameters passed to the 
     697     *                           &get() function. See &get for more info. 
     698     * @param array  $headers    The extra headers that should be passed 
     699     *                           to the &headers() function. 
     700     *                           See that function for more info. 
     701     * @param bool   $overwrite  Overwrite the existing headers with new. 
     702     * 
     703     * @return mixed The complete e-mail or PEAR error object 
     704     * @access public 
     705     */ 
     706    function getMessage($separation = null, $params = null, $headers = null, 
     707        $overwrite = false 
     708    ) { 
     709        if ($separation === null) { 
     710            $separation = $this->_build_params['eol']; 
     711        } 
     712 
     713        $body = $this->get($params); 
     714 
     715        if (PEAR::isError($body)) { 
     716            return $body; 
     717        } 
     718 
     719        $head = $this->txtHeaders($headers, $overwrite); 
     720        $mail = $head . $separation . $body; 
     721        return $mail; 
     722    } 
     723 
     724    /** 
     725     * Returns the complete e-mail body, ready to send using an alternative 
     726     * mail delivery method. 
     727     *  
     728     * @param array $params The Build parameters passed to the 
     729     *                      &get() function. See &get for more info. 
     730     * 
     731     * @return mixed The e-mail body or PEAR error object 
     732     * @access public 
     733     * @since 1.6.0 
     734     */ 
     735    function getMessageBody($params = null) 
     736    { 
     737        return $this->get($params, null, true); 
     738    } 
     739 
     740    /** 
     741     * Writes (appends) the complete e-mail into file. 
     742     *  
     743     * @param string $filename  Output file location 
     744     * @param array  $params    The Build parameters passed to the 
     745     *                          &get() function. See &get for more info. 
     746     * @param array  $headers   The extra headers that should be passed 
     747     *                          to the &headers() function. 
     748     *                          See that function for more info. 
     749     * @param bool   $overwrite Overwrite the existing headers with new. 
     750     * 
     751     * @return mixed True or PEAR error object 
     752     * @access public 
     753     * @since 1.6.0 
     754     */ 
     755    function saveMessage($filename, $params = null, $headers = null, $overwrite = false) 
     756    { 
     757        // Check state of file and raise an error properly 
     758        if (file_exists($filename) && !is_writable($filename)) { 
     759            $err = PEAR::raiseError('File is not writable: ' . $filename); 
     760            return $err; 
     761        } 
     762 
     763        // Temporarily reset magic_quotes_runtime and read file contents 
     764        if ($magic_quote_setting = get_magic_quotes_runtime()) { 
     765            @ini_set('magic_quotes_runtime', 0); 
     766        } 
     767 
     768        if (!($fh = fopen($filename, 'ab'))) { 
     769            $err = PEAR::raiseError('Unable to open file: ' . $filename); 
     770            return $err; 
     771        } 
     772 
     773        // Write message headers into file (skipping Content-* headers) 
     774        $head = $this->txtHeaders($headers, $overwrite, true); 
     775        if (fwrite($fh, $head) === false) { 
     776            $err = PEAR::raiseError('Error writing to file: ' . $filename); 
     777            return $err; 
     778        } 
     779 
     780        fclose($fh); 
     781 
     782        if ($magic_quote_setting) { 
     783            @ini_set('magic_quotes_runtime', $magic_quote_setting); 
     784        } 
     785 
     786        // Write the rest of the message into file 
     787        $res = $this->get($params, $filename); 
     788 
     789        return $res ? $res : true; 
     790    } 
     791 
     792    /** 
     793     * Writes (appends) the complete e-mail body into file. 
     794     *  
     795     * @param string $filename Output file location 
     796     * @param array  $params   The Build parameters passed to the 
     797     *                         &get() function. See &get for more info. 
     798     * 
     799     * @return mixed True or PEAR error object 
     800     * @access public 
     801     * @since 1.6.0 
     802     */ 
     803    function saveMessageBody($filename, $params = null) 
     804    { 
     805        // Check state of file and raise an error properly 
     806        if (file_exists($filename) && !is_writable($filename)) { 
     807            $err = PEAR::raiseError('File is not writable: ' . $filename); 
     808            return $err; 
     809        } 
     810 
     811        // Temporarily reset magic_quotes_runtime and read file contents 
     812        if ($magic_quote_setting = get_magic_quotes_runtime()) { 
     813            @ini_set('magic_quotes_runtime', 0); 
     814        } 
     815 
     816        if (!($fh = fopen($filename, 'ab'))) { 
     817            $err = PEAR::raiseError('Unable to open file: ' . $filename); 
     818            return $err; 
     819        } 
     820 
     821        // Write the rest of the message into file 
     822        $res = $this->get($params, $filename, true); 
     823 
     824        return $res ? $res : true; 
    434825    } 
    435826 
     
    438829     * returns the mime content. 
    439830     * 
    440      * @param  array  Build parameters that change the way the email 
    441      *                is built. Should be associative. Can contain: 
    442      *                text_encoding  -  What encoding to use for plain text 
    443      *                                  Default is 7bit 
    444      *                html_encoding  -  What encoding to use for html 
    445      *                                  Default is quoted-printable 
    446      *                7bit_wrap      -  Number of characters before text is 
    447      *                                  wrapped in 7bit encoding 
    448      *                                  Default is 998 
    449      *                html_charset   -  The character set to use for html. 
    450      *                                  Default is iso-8859-1 
    451      *                text_charset   -  The character set to use for text. 
    452      *                                  Default is iso-8859-1 
    453      *                head_charset   -  The character set to use for headers. 
    454      *                                  Default is iso-8859-1 
    455      * @return string The mime content 
    456      * @access public 
    457      */ 
    458     function &get($build_params = null) 
    459     { 
    460         if (isset($build_params)) { 
    461             while (list($key, $value) = each($build_params)) { 
     831     * @param array    $params    Build parameters that change the way the email 
     832     *                            is built. Should be associative. See $_build_params. 
     833     * @param resource $filename  Output file where to save the message instead of 
     834     *                            returning it 
     835     * @param boolean  $skip_head True if you want to return/save only the message 
     836     *                            without headers 
     837     * 
     838     * @return mixed The MIME message content string, null or PEAR error object 
     839     * @access public 
     840     */ 
     841    function &get($params = null, $filename = null, $skip_head = false) 
     842    { 
     843        if (isset($params)) { 
     844            while (list($key, $value) = each($params)) { 
    462845                $this->_build_params[$key] = $value; 
    463846            } 
    464847        } 
    465848 
    466         if (!empty($this->_html_images) AND isset($this->_htmlbody)) { 
    467             foreach ($this->_html_images as $value) { 
    468                 $regex = '#(\s)((?i)src|background|href(?-i))\s*=\s*(["\']?)' . preg_quote($value['name'], '#') . 
    469                          '\3#'; 
    470                 $rep = '\1\2=\3cid:' . $value['cid'] .'\3'; 
    471                 $this->_htmlbody = preg_replace($regex, $rep, 
    472                                        $this->_htmlbody 
    473                                    ); 
    474             } 
    475         } 
     849        if (isset($this->_headers['From'])) { 
     850            // Bug #11381: Illegal characters in domain ID 
     851            if (preg_match('#(@[0-9a-zA-Z\-\.]+)#', $this->_headers['From'], $matches)) { 
     852                $domainID = $matches[1]; 
     853            } else { 
     854                $domainID = '@localhost'; 
     855            } 
     856            foreach ($this->_html_images as $i => $img) { 
     857                $cid = $this->_html_images[$i]['cid'];  
     858                if (!preg_match('#'.preg_quote($domainID).'$#', $cid)) { 
     859                    $this->_html_images[$i]['cid'] = $cid . $domainID; 
     860                } 
     861            } 
     862        } 
     863 
     864        if (count($this->_html_images) && isset($this->_htmlbody)) { 
     865            foreach ($this->_html_images as $key => $value) { 
     866                $regex   = array(); 
     867                $regex[] = '#(\s)((?i)src|background|href(?-i))\s*=\s*(["\']?)' . 
     868                            preg_quote($value['name'], '#') . '\3#'; 
     869                $regex[] = '#(?i)url(?-i)\(\s*(["\']?)' . 
     870                            preg_quote($value['name'], '#') . '\1\s*\)#'; 
     871 
     872                $rep   = array(); 
     873                $rep[] = '\1\2=\3cid:' . $value['cid'] .'\3'; 
     874                $rep[] = 'url(\1cid:' . $value['cid'] . '\1)'; 
     875 
     876                $this->_htmlbody = preg_replace($regex, $rep, $this->_htmlbody); 
     877                $this->_html_images[$key]['name'] 
     878                    = $this->_basename($this->_html_images[$key]['name']); 
     879            } 
     880        } 
     881 
     882        $this->_checkParams(); 
    476883 
    477884        $null        = null; 
    478         $attachments = !empty($this->_parts)                ? true : false; 
    479         $html_images = !empty($this->_html_images)          ? true : false; 
    480         $html        = !empty($this->_htmlbody)             ? true : false; 
    481         $text        = (!$html AND !empty($this->_txtbody)) ? true : false; 
     885        $attachments = count($this->_parts)                 ? true : false; 
     886        $html_images = count($this->_html_images)           ? true : false; 
     887        $html        = strlen($this->_htmlbody)             ? true : false; 
     888        $text        = (!$html && strlen($this->_txtbody)) ? true : false; 
    482889 
    483890        switch (true) { 
    484         case $text AND !$attachments: 
     891        case $text && !$attachments: 
    485892            $message =& $this->_addTextPart($null, $this->_txtbody); 
    486893            break; 
    487894 
    488         case !$text AND !$html AND $attachments: 
     895        case !$text && !$html && $attachments: 
    489896            $message =& $this->_addMixedPart(); 
    490897            for ($i = 0; $i < count($this->_parts); $i++) { 
     
    493900            break; 
    494901 
    495         case $text AND $attachments: 
     902        case $text && $attachments: 
    496903            $message =& $this->_addMixedPart(); 
    497904            $this->_addTextPart($message, $this->_txtbody); 
     
    501908            break; 
    502909 
    503         case $html AND !$attachments AND !$html_images: 
     910        case $html && !$attachments && !$html_images: 
    504911            if (isset($this->_txtbody)) { 
    505912                $message =& $this->_addAlternativePart($null); 
     
    511918            break; 
    512919 
    513         case $html AND !$attachments AND $html_images: 
     920        case $html && !$attachments && $html_images: 
     921            // * Content-Type: multipart/alternative; 
     922            //    * text 
     923            //    * Content-Type: multipart/related; 
     924            //       * html 
     925            //       * image... 
    514926            if (isset($this->_txtbody)) { 
    515927                $message =& $this->_addAlternativePart($null); 
    516928                $this->_addTextPart($message, $this->_txtbody); 
    517                 $related =& $this->_addRelatedPart($message); 
     929 
     930                $ht =& $this->_addRelatedPart($message); 
     931                $this->_addHtmlPart($ht); 
     932                for ($i = 0; $i < count($this->_html_images); $i++) { 
     933                    $this->_addHtmlImagePart($ht, $this->_html_images[$i]); 
     934                } 
    518935            } else { 
     936                // * Content-Type: multipart/related; 
     937                //    * html 
     938                //    * image... 
    519939                $message =& $this->_addRelatedPart($null); 
    520                 $related =& $message; 
    521             } 
    522             $this->_addHtmlPart($related); 
     940                $this->_addHtmlPart($message); 
     941                for ($i = 0; $i < count($this->_html_images); $i++) { 
     942                    $this->_addHtmlImagePart($message, $this->_html_images[$i]); 
     943                } 
     944            } 
     945            /* 
     946            // #13444, #9725: the code below was a non-RFC compliant hack 
     947            // * Content-Type: multipart/related; 
     948            //    * Content-Type: multipart/alternative; 
     949            //        * text 
     950            //        * html 
     951            //    * image... 
     952            $message =& $this->_addRelatedPart($null); 
     953            if (isset($this->_txtbody)) { 
     954                $alt =& $this->_addAlternativePart($message); 
     955                $this->_addTextPart($alt, $this->_txtbody); 
     956                $this->_addHtmlPart($alt); 
     957            } else { 
     958                $this->_addHtmlPart($message); 
     959            } 
    523960            for ($i = 0; $i < count($this->_html_images); $i++) { 
    524                 $this->_addHtmlImagePart($related, $this->_html_images[$i]); 
    525             } 
     961                $this->_addHtmlImagePart($message, $this->_html_images[$i]); 
     962            } 
     963            */ 
    526964            break; 
    527965 
    528         case $html AND $attachments AND !$html_images: 
     966        case $html && $attachments && !$html_images: 
    529967            $message =& $this->_addMixedPart(); 
    530968            if (isset($this->_txtbody)) { 
     
    540978            break; 
    541979 
    542         case $html AND $attachments AND $html_images: 
     980        case $html && $attachments && $html_images: 
    543981            $message =& $this->_addMixedPart(); 
    544982            if (isset($this->_txtbody)) { 
     
    560998        } 
    561999 
    562         if (isset($message)) { 
    563             $output = $message->encode(); 
    564             $this->_headers = array_merge($this->_headers, 
    565                                           $output['headers']); 
    566             return $output['body']; 
    567  
    568         } else { 
    569             return false; 
     1000        if (!isset($message)) { 
     1001            $ret = null; 
     1002            return $ret; 
     1003        } 
     1004 
     1005        // Use saved boundary 
     1006        if (!empty($this->_build_params['boundary'])) { 
     1007            $boundary = $this->_build_params['boundary']; 
     1008        } else { 
     1009            $boundary = null; 
     1010        } 
     1011 
     1012        // Write output to file 
     1013        if ($filename) { 
     1014            // Append mimePart message headers and body into file 
     1015            $headers = $message->encodeToFile($filename, $boundary, $skip_head); 
     1016            if (PEAR::isError($headers)) { 
     1017                return $headers; 
     1018            } 
     1019            $this->_headers = array_merge($this->_headers, $headers); 
     1020            $ret = null; 
     1021            return $ret; 
     1022        } else { 
     1023            $output = $message->encode($boundary, $skip_head); 
     1024            if (PEAR::isError($output)) { 
     1025                return $output; 
     1026            } 
     1027            $this->_headers = array_merge($this->_headers, $output['headers']); 
     1028            $body = $output['body']; 
     1029            return $body; 
    5701030        } 
    5711031    } 
     
    5761036     * $array['header-name'] = 'header-value'; 
    5771037     * 
    578      * @param  array $xtra_headers Assoc array with any extra headers. 
    579      *                             Optional. 
    580      * @return array Assoc array with the mime headers 
    581      * @access public 
    582      */ 
    583     function &headers($xtra_headers = null) 
    584     { 
    585         // Content-Type header should already be present, 
    586         // So just add mime version header 
     1038     * @param array $xtra_headers Assoc array with any extra headers (optional) 
     1039     *                            (Don't set Content-Type for multipart messages here!) 
     1040     * @param bool  $overwrite    Overwrite already existing headers. 
     1041     * @param bool  $skip_content Don't return content headers: Content-Type, 
     1042     *                            Content-Disposition and Content-Transfer-Encoding 
     1043     *  
     1044     * @return array              Assoc array with the mime headers 
     1045     * @access public 
     1046     */ 
     1047    function &headers($xtra_headers = null, $overwrite = false, $skip_content = false) 
     1048    { 
     1049        // Add mime version header 
    5871050        $headers['MIME-Version'] = '1.0'; 
    588         if (isset($xtra_headers)) { 
     1051 
     1052        // Content-Type and Content-Transfer-Encoding headers should already 
     1053        // be present if get() was called, but we'll re-set them to make sure 
     1054        // we got them when called before get() or something in the message 
     1055        // has been changed after get() [#14780] 
     1056        if (!$skip_content) { 
     1057            $headers += $this->_contentHeaders(); 
     1058        } 
     1059 
     1060        if (!empty($xtra_headers)) { 
    5891061            $headers = array_merge($headers, $xtra_headers); 
    5901062        } 
    591         $this->_headers = array_merge($headers, $this->_headers); 
    592  
    593         return $this->_encodeHeaders($this->_headers); 
     1063 
     1064        if ($overwrite) { 
     1065            $this->_headers = array_merge($this->_headers, $headers); 
     1066        } else { 
     1067            $this->_headers = array_merge($headers, $this->_headers); 
     1068        } 
     1069 
     1070        $headers = $this->_headers; 
     1071 
     1072        if ($skip_content) { 
     1073            unset($headers['Content-Type']); 
     1074            unset($headers['Content-Transfer-Encoding']); 
     1075            unset($headers['Content-Disposition']); 
     1076        } else if (!empty($this->_build_params['ctype'])) { 
     1077            $headers['Content-Type'] = $this->_build_params['ctype']; 
     1078        } 
     1079 
     1080        $encodedHeaders = $this->_encodeHeaders($headers); 
     1081        return $encodedHeaders; 
    5941082    } 
    5951083 
     
    5981086     * (usefull if you want to use the PHP mail() function) 
    5991087     * 
    600      * @param  array   $xtra_headers Assoc array with any extra headers. 
    601      *                               Optional. 
    602      * @return string  Plain text headers 
    603      * @access public 
    604      */ 
    605     function txtHeaders($xtra_headers = null) 
    606     { 
    607         $headers = $this->headers($xtra_headers); 
     1088     * @param array $xtra_headers Assoc array with any extra headers (optional) 
     1089     *                            (Don't set Content-Type for multipart messages here!) 
     1090     * @param bool  $overwrite    Overwrite the existing headers with new. 
     1091     * @param bool  $skip_content Don't return content headers: Content-Type, 
     1092     *                            Content-Disposition and Content-Transfer-Encoding 
     1093     * 
     1094     * @return string             Plain text headers 
     1095     * @access public 
     1096     */ 
     1097    function txtHeaders($xtra_headers = null, $overwrite = false, $skip_content = false) 
     1098    { 
     1099        $headers = $this->headers($xtra_headers, $overwrite, $skip_content); 
     1100 
     1101        // Place Received: headers at the beginning of the message 
     1102        // Spam detectors often flag messages with it after the Subject: as spam 
     1103        if (isset($headers['Received'])) { 
     1104            $received = $headers['Received']; 
     1105            unset($headers['Received']); 
     1106            $headers = array('Received' => $received) + $headers; 
     1107        } 
     1108 
    6081109        $ret = ''; 
     1110        $eol = $this->_build_params['eol']; 
     1111 
    6091112        foreach ($headers as $key => $val) { 
    610             $ret .= "$key: $val" . MAIL_MIME_CRLF; 
    611         } 
     1113            if (is_array($val)) { 
     1114                foreach ($val as $value) { 
     1115                    $ret .= "$key: $value" . $eol; 
     1116                } 
     1117            } else { 
     1118                $ret .= "$key: $val" . $eol; 
     1119            } 
     1120        } 
     1121 
    6121122        return $ret; 
    6131123    } 
    6141124 
    6151125    /** 
     1126     * Sets message Content-Type header. 
     1127     * Use it to build messages with various content-types e.g. miltipart/raport 
     1128     * not supported by _contentHeaders() function. 
     1129     * 
     1130     * @param string $type   Type name 
     1131     * @param array  $params Hash array of header parameters 
     1132     * 
     1133     * @return void 
     1134     * @access public 
     1135     * @since 1.7.0 
     1136     */ 
     1137    function setContentType($type, $params = array()) 
     1138    { 
     1139        $header = $type; 
     1140 
     1141        $eol = !empty($this->_build_params['eol']) 
     1142            ? $this->_build_params['eol'] : "\r\n"; 
     1143 
     1144        // add parameters 
     1145        $token_regexp = '#([^\x21,\x23-\x27,\x2A,\x2B,\x2D' 
     1146            . ',\x2E,\x30-\x39,\x41-\x5A,\x5E-\x7E])#'; 
     1147        if (is_array($params)) { 
     1148            foreach ($params as $name => $value) { 
     1149                if ($name == 'boundary') { 
     1150                    $this->_build_params['boundary'] = $value; 
     1151                } 
     1152                if (!preg_match($token_regexp, $value)) { 
     1153                    $header .= ";$eol $name=$value"; 
     1154                } else { 
     1155                    $value = addcslashes($value, '\\"'); 
     1156                    $header .= ";$eol $name=\"$value\""; 
     1157                } 
     1158            } 
     1159        } 
     1160 
     1161        // add required boundary parameter if not defined 
     1162        if (preg_match('/^multipart\//i', $type)) { 
     1163            if (empty($this->_build_params['boundary'])) { 
     1164                $this->_build_params['boundary'] = '=_' . md5(rand() . microtime()); 
     1165            } 
     1166 
     1167            $header .= ";$eol boundary=\"".$this->_build_params['boundary']."\""; 
     1168        } 
     1169 
     1170        $this->_build_params['ctype'] = $header; 
     1171    } 
     1172 
     1173    /** 
    6161174     * Sets the Subject header 
    6171175     * 
    618      * @param  string $subject String to set the subject to 
    619      * access  public 
     1176     * @param string $subject String to set the subject to. 
     1177     * 
     1178     * @return void 
     1179     * @access public 
    6201180     */ 
    6211181    function setSubject($subject) 
     
    6271187     * Set an email to the From (the sender) header 
    6281188     * 
    629      * @param  string $email The email direction to add 
     1189     * @param string $email The email address to use 
     1190     * 
     1191     * @return void 
    6301192     * @access public 
    6311193     */ 
     
    6331195    { 
    6341196        $this->_headers['From'] = $email; 
     1197    } 
     1198 
     1199    /** 
     1200     * Add an email to the To header 
     1201     * (multiple calls to this method are allowed) 
     1202     * 
     1203     * @param string $email The email direction to add 
     1204     * 
     1205     * @return void 
     1206     * @access public 
     1207     */ 
     1208    function addTo($email) 
     1209    { 
     1210        if (isset($this->_headers['To'])) { 
     1211            $this->_headers['To'] .= ", $email"; 
     1212        } else { 
     1213            $this->_headers['To'] = $email; 
     1214        } 
    6351215    } 
    6361216 
     
    6391219     * (multiple calls to this method are allowed) 
    6401220     * 
    641      * @param  string $email The email direction to add 
     1221     * @param string $email The email direction to add 
     1222     * 
     1223     * @return void 
    6421224     * @access public 
    6431225     */ 
     
    6551237     * (multiple calls to this method are allowed) 
    6561238     * 
    657      * @param  string $email The email direction to add 
     1239     * @param string $email The email direction to add 
     1240     * 
     1241     * @return void 
    6581242     * @access public 
    6591243     */ 
     
    6681252 
    6691253    /** 
     1254     * Since the PHP send function requires you to specify 
     1255     * recipients (To: header) separately from the other 
     1256     * headers, the To: header is not properly encoded. 
     1257     * To fix this, you can use this public method to  
     1258     * encode your recipients before sending to the send 
     1259     * function 
     1260     * 
     1261     * @param string $recipients A comma-delimited list of recipients 
     1262     * 
     1263     * @return string            Encoded data 
     1264     * @access public 
     1265     */ 
     1266    function encodeRecipients($recipients) 
     1267    { 
     1268        $input = array("To" => $recipients); 
     1269        $retval = $this->_encodeHeaders($input); 
     1270        return $retval["To"] ; 
     1271    } 
     1272 
     1273    /** 
     1274     * Encodes headers as per RFC2047 
     1275     * 
     1276     * @param array $input  The header data to encode 
     1277     * @param array $params Extra build parameters 
     1278     * 
     1279     * @return array        Encoded data 
     1280     * @access private 
     1281     */ 
     1282    function _encodeHeaders($input, $params = array()) 
     1283    { 
     1284        $build_params = $this->_build_params; 
     1285        while (list($key, $value) = each($params)) { 
     1286            $build_params[$key] = $value; 
     1287        } 
     1288 
     1289        foreach ($input as $hdr_name => $hdr_value) { 
     1290            if (is_array($hdr_value)) { 
     1291                foreach ($hdr_value as $idx => $value) { 
     1292                    $input[$hdr_name][$idx] = $this->encodeHeader( 
     1293                        $hdr_name, $value, 
     1294                        $build_params['head_charset'], $build_params['head_encoding'] 
     1295                    ); 
     1296                } 
     1297            } else { 
     1298                $input[$hdr_name] = $this->encodeHeader( 
     1299                    $hdr_name, $hdr_value, 
     1300                    $build_params['head_charset'], $build_params['head_encoding'] 
     1301                ); 
     1302            } 
     1303        } 
     1304 
     1305        return $input; 
     1306    } 
     1307 
     1308    /** 
    6701309     * Encodes a header as per RFC2047 
    6711310     * 
    672      * @param  string  $input The header data to encode 
    673      * @return string  Encoded data 
    674      * @access private 
    675      */ 
    676     function _encodeHeaders($input) 
    677     { 
    678         foreach ($input as $hdr_name => $hdr_value) { 
    679             preg_match_all('/(\w*[\x80-\xFF]+\w*)/', $hdr_value, $matches); 
    680             foreach ($matches[1] as $value) { 
    681                 $replacement = preg_replace('/([\x80-\xFF])/e', 
    682                                             '"=" . 
    683                                             strtoupper(dechex(ord("\1")))', 
    684                                             $value); 
    685                 $hdr_value = str_replace($value, '=?' . 
    686                                          $this->_build_params['head_charset'] . 
    687                                          '?Q?' . $replacement . '?=', 
    688                                          $hdr_value); 
    689             } 
    690             $input[$hdr_name] = $hdr_value; 
    691         } 
    692  
    693         return $input; 
    694     } 
    695  
    696     /** 
    697      * Set the object's end-of-line and define the constant if applicable 
    698      * 
    699      * @param string $eol End Of Line sequence 
    700      * @access private 
    701      */ 
    702     function _setEOL($eol) 
    703     { 
    704         $this->_eol = $eol; 
    705         if (!defined('MAIL_MIME_CRLF')) { 
    706             define('MAIL_MIME_CRLF', $this->_eol, true); 
    707         } 
    708     } 
    709  
    710      
     1311     * @param string $name     The header name 
     1312     * @param string $value    The header data to encode 
     1313     * @param string $charset  Character set name 
     1314     * @param string $encoding Encoding name (base64 or quoted-printable) 
     1315     * 
     1316     * @return string          Encoded header data (without a name) 
     1317     * @access public 
     1318     * @since 1.5.3 
     1319     */ 
     1320    function encodeHeader($name, $value, $charset, $encoding) 
     1321    { 
     1322        return Mail_mimePart::encodeHeader( 
     1323            $name, $value, $charset, $encoding, $this->_build_params['eol'] 
     1324        ); 
     1325    } 
     1326 
     1327    /** 
     1328     * Get file's basename (locale independent)  
     1329     * 
     1330     * @param string $filename Filename 
     1331     * 
     1332     * @return string          Basename 
     1333     * @access private 
     1334     */ 
     1335    function _basename($filename) 
     1336    { 
     1337        // basename() is not unicode safe and locale dependent 
     1338        if (stristr(PHP_OS, 'win') || stristr(PHP_OS, 'netware')) { 
     1339            return preg_replace('/^.*[\\\\\\/]/', '', $filename); 
     1340        } else { 
     1341            return preg_replace('/^.*[\/]/', '', $filename); 
     1342        } 
     1343    } 
     1344 
     1345    /** 
     1346     * Get Content-Type and Content-Transfer-Encoding headers of the message 
     1347     * 
     1348     * @return array Headers array 
     1349     * @access private 
     1350     */ 
     1351    function _contentHeaders() 
     1352    { 
     1353        $attachments = count($this->_parts)                 ? true : false; 
     1354        $html_images = count($this->_html_images)           ? true : false; 
     1355        $html        = strlen($this->_htmlbody)             ? true : false; 
     1356        $text        = (!$html && strlen($this->_txtbody))  ? true : false; 
     1357        $headers     = array(); 
     1358 
     1359        // See get() 
     1360        switch (true) { 
     1361        case $text && !$attachments: 
     1362            $headers['Content-Type'] = 'text/plain'; 
     1363            break; 
     1364 
     1365        case !$text && !$html && $attachments: 
     1366        case $text && $attachments: 
     1367        case $html && $attachments && !$html_images: 
     1368        case $html && $attachments && $html_images: 
     1369            $headers['Content-Type'] = 'multipart/mixed'; 
     1370            break; 
     1371 
     1372        case $html && !$attachments && !$html_images && isset($this->_txtbody): 
     1373        case $html && !$attachments && $html_images && isset($this->_txtbody): 
     1374            $headers['Content-Type'] = 'multipart/alternative'; 
     1375            break; 
     1376 
     1377        case $html && !$attachments && !$html_images && !isset($this->_txtbody): 
     1378            $headers['Content-Type'] = 'text/html'; 
     1379            break; 
     1380 
     1381        case $html && !$attachments && $html_images && !isset($this->_txtbody): 
     1382            $headers['Content-Type'] = 'multipart/related'; 
     1383            break; 
     1384 
     1385        default: 
     1386            return $headers; 
     1387        } 
     1388 
     1389        $this->_checkParams(); 
     1390 
     1391        $eol = !empty($this->_build_params['eol']) 
     1392            ? $this->_build_params['eol'] : "\r\n"; 
     1393 
     1394        if ($headers['Content-Type'] == 'text/plain') { 
     1395            // single-part message: add charset and encoding 
     1396            $charset = 'charset=' . $this->_build_params['text_charset']; 
     1397            // place charset parameter in the same line, if possible 
     1398            // 26 = strlen("Content-Type: text/plain; ") 
     1399            $headers['Content-Type'] 
     1400                .= (strlen($charset) + 26 <= 76) ? "; $charset" : ";$eol $charset"; 
     1401            $headers['Content-Transfer-Encoding'] 
     1402                = $this->_build_params['text_encoding']; 
     1403        } else if ($headers['Content-Type'] == 'text/html') { 
     1404            // single-part message: add charset and encoding 
     1405            $charset = 'charset=' . $this->_build_params['html_charset']; 
     1406            // place charset parameter in the same line, if possible 
     1407            $headers['Content-Type'] 
     1408                .= (strlen($charset) + 25 <= 76) ? "; $charset" : ";$eol $charset"; 
     1409            $headers['Content-Transfer-Encoding'] 
     1410                = $this->_build_params['html_encoding']; 
     1411        } else { 
     1412            // multipart message: and boundary 
     1413            if (!empty($this->_build_params['boundary'])) { 
     1414                $boundary = $this->_build_params['boundary']; 
     1415            } else if (!empty($this->_headers['Content-Type']) 
     1416                && preg_match('/boundary="([^"]+)"/', $this->_headers['Content-Type'], $m) 
     1417            ) { 
     1418                $boundary = $m[1]; 
     1419            } else { 
     1420                $boundary = '=_' . md5(rand() . microtime()); 
     1421            } 
     1422 
     1423            $this->_build_params['boundary'] = $boundary; 
     1424            $headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; 
     1425        } 
     1426 
     1427        return $headers; 
     1428    } 
     1429 
     1430    /** 
     1431     * Validate and set build parameters 
     1432     * 
     1433     * @return void 
     1434     * @access private 
     1435     */ 
     1436    function _checkParams() 
     1437    { 
     1438        $encodings = array('7bit', '8bit', 'base64', 'quoted-printable'); 
     1439 
     1440        $this->_build_params['text_encoding'] 
     1441            = strtolower($this->_build_params['text_encoding']); 
     1442        $this->_build_params['html_encoding'] 
     1443            = strtolower($this->_build_params['html_encoding']); 
     1444 
     1445        if (!in_array($this->_build_params['text_encoding'], $encodings)) { 
     1446            $this->_build_params['text_encoding'] = '7bit'; 
     1447        } 
     1448        if (!in_array($this->_build_params['html_encoding'], $encodings)) { 
     1449            $this->_build_params['html_encoding'] = '7bit'; 
     1450        } 
     1451 
     1452        // text body 
     1453        if ($this->_build_params['text_encoding'] == '7bit' 
     1454            && !preg_match('/ascii/i', $this->_build_params['text_charset']) 
     1455            && preg_match('/[^\x00-\x7F]/', $this->_txtbody) 
     1456        ) { 
     1457            $this->_build_params['text_encoding'] = 'quoted-printable'; 
     1458        } 
     1459        // html body 
     1460        if ($this->_build_params['html_encoding'] == '7bit' 
     1461            && !preg_match('/ascii/i', $this->_build_params['html_charset']) 
     1462            && preg_match('/[^\x00-\x7F]/', $this->_htmlbody) 
     1463        ) { 
     1464            $this->_build_params['html_encoding'] = 'quoted-printable'; 
     1465        } 
     1466    } 
    7111467 
    7121468} // End of class 
    713 ?> 
  • branches/version-2_5-dev/data/module/Mail/mimeDecode.php

    r16300 r19941  
    11<?php 
    2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 
    3 // +-----------------------------------------------------------------------+ 
    4 // | Copyright (c) 2002-2003  Richard Heyes                                | 
    5 // | Copyright (c) 2003-2005  The PHP Group                                | 
    6 // | All rights reserved.                                                  | 
    7 // |                                                                       | 
    8 // | Redistribution and use in source and binary forms, with or without    | 
    9 // | modification, are permitted provided that the following conditions    | 
    10 // | are met:                                                              | 
    11 // |                                                                       | 
    12 // | o Redistributions of source code must retain the above copyright      | 
    13 // |   notice, this list of conditions and the following disclaimer.       | 
    14 // | o Redistributions in binary form must reproduce the above copyright   | 
    15 // |   notice, this list of conditions and the following disclaimer in the | 
    16 // |   documentation and/or other materials provided with the distribution.| 
    17 // | o The names of the authors may not be used to endorse or promote      | 
    18 // |   products derived from this software without specific prior written  | 
    19 // |   permission.                                                         | 
    20 // |                                                                       | 
    21 // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   | 
    22 // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     | 
    23 // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
    24 // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  | 
    25 // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
    26 // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      | 
    27 // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
    28 // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
    29 // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   | 
    30 // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
    31 // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  | 
    32 // |                                                                       | 
    33 // +-----------------------------------------------------------------------+ 
    34 // | Author: Richard Heyes <richard@phpguru.org>                           | 
    35 // +-----------------------------------------------------------------------+ 
    36  
    37 require_once dirname(__FILE__) . '/../PEAR.php'; 
    38  
    392/** 
    40 *  +----------------------------- IMPORTANT ------------------------------+ 
    41 *  | Usage of this class compared to native php extensions such as        | 
    42 *  | mailparse or imap, is slow and may be feature deficient. If available| 
    43 *  | you are STRONGLY recommended to use the php extensions.              | 
    44 *  +----------------------------------------------------------------------+ 
    45 * 
    46 * Mime Decoding class 
    47 * 
    48 * This class will parse a raw mime email and return 
    49 * the structure. Returned structure is similar to 
    50 * that returned by imap_fetchstructure(). 
    51 * 
    52 * USAGE: (assume $input is your raw email) 
    53 * 
    54 * $decode = new Mail_mimeDecode($input, "\r\n"); 
    55 * $structure = $decode->decode(); 
    56 * print_r($structure); 
    57 * 
    58 * Or statically: 
    59 * 
    60 * $params['input'] = $input; 
    61 * $structure = Mail_mimeDecode::decode($params); 
    62 * print_r($structure); 
    63 * 
    64 * TODO: 
    65 *  o Implement multipart/appledouble 
    66 *  o UTF8: ??? 
    67  
    68         > 4. We have also found a solution for decoding the UTF-8  
    69         > headers. Therefore I made the following function: 
    70         >  
    71         > function decode_utf8($txt) { 
    72         > $trans=array("Å&#8216;"=>"õ","ű"=>"û","Ő"=>"Ã&#8226;","Å°" 
    73         =>"Ã&#8250;"); 
    74         > $txt=strtr($txt,$trans); 
    75         > return(utf8_decode($txt)); 
    76         > } 
    77         >  
    78         > And I have inserted the following line to the class: 
    79         >  
    80         > if (strtolower($charset)=="utf-8") $text=decode_utf8($text); 
    81         >  
    82         > ... before the following one in the "_decodeHeader" function: 
    83         >  
    84         > $input = str_replace($encoded, $text, $input); 
    85         >  
    86         > This way from now on it can easily decode the UTF-8 headers too. 
    87  
    88 * 
    89 * @author  Richard Heyes <richard@phpguru.org> 
    90 * @version $Revision: 1.46 $ 
    91 * @package Mail 
    92 */ 
     3 * The Mail_mimeDecode class is used to decode mail/mime messages 
     4 * 
     5 * This class will parse a raw mime email and return 
     6 * the structure. Returned structure is similar to 
     7 * that returned by imap_fetchstructure(). 
     8 * 
     9 *  +----------------------------- IMPORTANT ------------------------------+ 
     10 *  | Usage of this class compared to native php extensions such as        | 
     11 *  | mailparse or imap, is slow and may be feature deficient. If available| 
     12 *  | you are STRONGLY recommended to use the php extensions.              | 
     13 *  +----------------------------------------------------------------------+ 
     14 * 
     15 * Compatible with PHP versions 4 and 5 
     16 * 
     17 * LICENSE: This LICENSE is in the BSD license style. 
     18 * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org> 
     19 * Copyright (c) 2003-2006, PEAR <pear-group@php.net> 
     20 * All rights reserved. 
     21 * 
     22 * Redistribution and use in source and binary forms, with or 
     23 * without modification, are permitted provided that the following 
     24 * conditions are met: 
     25 * 
     26 * - Redistributions of source code must retain the above copyright 
     27 *   notice, this list of conditions and the following disclaimer. 
     28 * - Redistributions in binary form must reproduce the above copyright 
     29 *   notice, this list of conditions and the following disclaimer in the 
     30 *   documentation and/or other materials provided with the distribution. 
     31 * - Neither the name of the authors, nor the names of its contributors  
     32 *   may be used to endorse or promote products derived from this  
     33 *   software without specific prior written permission. 
     34 * 
     35 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
     36 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
     37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
     38 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
     39 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
     40 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
     41 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
     42 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
     43 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
     44 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
     45 * THE POSSIBILITY OF SUCH DAMAGE. 
     46 * 
     47 * @category   Mail 
     48 * @package    Mail_Mime 
     49 * @author     Richard Heyes  <richard@phpguru.org> 
     50 * @author     George Schlossnagle <george@omniti.com> 
     51 * @author     Cipriano Groenendal <cipri@php.net> 
     52 * @author     Sean Coates <sean@php.net> 
     53 * @copyright  2003-2006 PEAR <pear-group@php.net> 
     54 * @license    http://www.opensource.org/licenses/bsd-license.php BSD License 
     55 * @version    CVS: $Id$ 
     56 * @link       http://pear.php.net/package/Mail_mime 
     57 */ 
     58 
     59 
     60/** 
     61 * require PEAR 
     62 * 
     63 * This package depends on PEAR to raise errors. 
     64 */ 
     65require_once 'PEAR.php'; 
     66 
     67 
     68/** 
     69 * The Mail_mimeDecode class is used to decode mail/mime messages 
     70 * 
     71 * This class will parse a raw mime email and return the structure. 
     72 * Returned structure is similar to that returned by imap_fetchstructure(). 
     73 * 
     74 *  +----------------------------- IMPORTANT ------------------------------+ 
     75 *  | Usage of this class compared to native php extensions such as        | 
     76 *  | mailparse or imap, is slow and may be feature deficient. If available| 
     77 *  | you are STRONGLY recommended to use the php extensions.              | 
     78 *  +----------------------------------------------------------------------+ 
     79 * 
     80 * @category   Mail 
     81 * @package    Mail_Mime 
     82 * @author     Richard Heyes  <richard@phpguru.org> 
     83 * @author     George Schlossnagle <george@omniti.com> 
     84 * @author     Cipriano Groenendal <cipri@php.net> 
     85 * @author     Sean Coates <sean@php.net> 
     86 * @copyright  2003-2006 PEAR <pear-group@php.net> 
     87 * @license    http://www.opensource.org/licenses/bsd-license.php BSD License 
     88 * @version    Release: @package_version@ 
     89 * @link       http://pear.php.net/package/Mail_mime 
     90 */ 
    9391class Mail_mimeDecode extends PEAR 
    9492{ 
    9593    /** 
    9694     * The raw email to decode 
     95     * 
    9796     * @var    string 
     97     * @access private 
    9898     */ 
    9999    var $_input; 
     
    101101    /** 
    102102     * The header part of the input 
     103     * 
    103104     * @var    string 
     105     * @access private 
    104106     */ 
    105107    var $_header; 
     
    107109    /** 
    108110     * The body part of the input 
     111     * 
    109112     * @var    string 
     113     * @access private 
    110114     */ 
    111115    var $_body; 
     
    113117    /** 
    114118     * If an error occurs, this is used to store the message 
     119     * 
    115120     * @var    string 
     121     * @access private 
    116122     */ 
    117123    var $_error; 
     
    120126     * Flag to determine whether to include bodies in the 
    121127     * returned object. 
     128     * 
    122129     * @var    boolean 
     130     * @access private 
    123131     */ 
    124132    var $_include_bodies; 
     
    126134    /** 
    127135     * Flag to determine whether to decode bodies 
     136     * 
    128137     * @var    boolean 
     138     * @access private 
    129139     */ 
    130140    var $_decode_bodies; 
     
    132142    /** 
    133143     * Flag to determine whether to decode headers 
     144     * 
    134145     * @var    boolean 
     146     * @access private 
    135147     */ 
    136148    var $_decode_headers; 
     149 
     150    /** 
     151     * Flag to determine whether to include attached messages 
     152     * as body in the returned object. Depends on $_include_bodies 
     153     * 
     154     * @var    boolean 
     155     * @access private 
     156     */ 
     157    var $_rfc822_bodies; 
    137158 
    138159    /** 
     
    154175        $this->_decode_bodies  = false; 
    155176        $this->_include_bodies = true; 
     177        $this->_rfc822_bodies  = false; 
    156178    } 
    157179 
     
    176198    { 
    177199        // determine if this method has been called statically 
    178         $isStatic = !(isset($this) && get_class($this) == __CLASS__); 
     200        $isStatic = empty($this) || !is_a($this, __CLASS__); 
    179201 
    180202        // Have we been called statically? 
     
    197219            $this->_decode_headers = isset($params['decode_headers']) ? 
    198220                                 $params['decode_headers'] : false; 
     221            $this->_rfc822_bodies  = isset($params['rfc_822bodies']) ? 
     222                                 $params['rfc_822bodies']  : false; 
    199223 
    200224            $structure = $this->_decode($this->_header, $this->_body); 
     
    224248 
    225249        foreach ($headers as $value) { 
     250            $value['value'] = $this->_decode_headers ? $this->_decodeHeader($value['value']) : $value['value']; 
    226251            if (isset($return->headers[strtolower($value['name'])]) AND !is_array($return->headers[strtolower($value['name'])])) { 
    227252                $return->headers[strtolower($value['name'])]   = array($return->headers[strtolower($value['name'])]); 
     
    236261        } 
    237262 
    238         reset($headers); 
    239         while (list($key, $value) = each($headers)) { 
     263 
     264        foreach ($headers as $key => $value) { 
    240265            $headers[$key]['name'] = strtolower($headers[$key]['name']); 
    241266            switch ($headers[$key]['name']) { 
     
    250275 
    251276                    if (isset($content_type['other'])) { 
    252                         while (list($p_name, $p_value) = each($content_type['other'])) { 
     277                        foreach($content_type['other'] as $p_name => $p_value) { 
    253278                            $return->ctype_parameters[$p_name] = $p_value; 
    254279                        } 
     
    260285                    $return->disposition   = $content_disposition['value']; 
    261286                    if (isset($content_disposition['other'])) { 
    262                         while (list($p_name, $p_value) = each($content_disposition['other'])) { 
     287                        foreach($content_disposition['other'] as $p_name => $p_value) { 
    263288                            $return->d_parameters[$p_name] = $p_value; 
    264289                        } 
     
    285310 
    286311                case 'multipart/parallel': 
     312                case 'multipart/appledouble': // Appledouble mail 
    287313                case 'multipart/report': // RFC1892 
    288314                case 'multipart/signed': // PGP 
     
    291317                case 'multipart/related': 
    292318                case 'multipart/mixed': 
     319                case 'application/vnd.wap.multipart.related': 
    293320                    if(!isset($content_type['other']['boundary'])){ 
    294321                        $this->_error = 'No boundary found for ' . $content_type['value'] . ' part'; 
     
    309336 
    310337                case 'message/rfc822': 
    311                     $obj = &new Mail_mimeDecode($body); 
     338                    if ($this->_rfc822_bodies) { 
     339                        $encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit'; 
     340                        $return->body = ($this->_decode_bodies ? $this->_decodeBody($body, $encoding) : $body); 
     341                    } 
     342                    $obj = new Mail_mimeDecode($body); 
    312343                    $return->parts[] = $obj->decode(array('include_bodies' => $this->_include_bodies, 
    313344                                                          'decode_bodies'  => $this->_decode_bodies, 
     
    389420            return array($match[1], $match[2]); 
    390421        } 
     422        // bug #17325 - empty bodies are allowed. - we just check that at least one line  
     423        // of headers exist.. 
     424        if (count(explode("\n",$input))) { 
     425            return array($input, ''); 
     426        } 
    391427        $this->_error = 'Could not split header and body'; 
    392428        return false; 
     
    407443            // Unfold the input 
    408444            $input   = preg_replace("/\r?\n/", "\r\n", $input); 
     445            //#7065 - wrapping.. with encoded stuff.. - probably not needed, 
     446            // wrapping space should only get removed if the trailing item on previous line is a  
     447            // encoded character 
     448            $input   = preg_replace("/=\r\n(\t| )+/", '=', $input); 
    409449            $input   = preg_replace("/\r\n(\t| )+/", ' ', $input); 
     450             
    410451            $headers = explode("\r\n", trim($input)); 
    411452 
     
    418459                $return[] = array( 
    419460                                  'name'  => $hdr_name, 
    420                                   'value' => $this->_decode_headers ? $this->_decodeHeader($hdr_value) : $hdr_value 
     461                                  'value' => $hdr_value 
    421462                                 ); 
    422463            } 
     
    442483    { 
    443484 
    444         if (($pos = strpos($input, ';')) !== false) { 
    445  
    446             $return['value'] = trim(substr($input, 0, $pos)); 
    447             $input = trim(substr($input, $pos+1)); 
    448  
    449             if (strlen($input) > 0) { 
    450  
    451                 // This splits on a semi-colon, if there's no preceeding backslash 
    452                 // Now works with quoted values; had to glue the \; breaks in PHP 
    453                 // the regex is already bordering on incomprehensible 
    454                 $splitRegex = '/([^;\'"]*[\'"]([^\'"]*([^\'"]*)*)[\'"][^;\'"]*|([^;]+))(;|$)/'; 
    455                 preg_match_all($splitRegex, $input, $matches); 
    456                 $parameters = array(); 
    457                 for ($i=0; $i<count($matches[0]); $i++) { 
    458                     $param = $matches[0][$i]; 
    459                     while (substr($param, -2) == '\;') { 
    460                         $param .= $matches[0][++$i]; 
     485        if (($pos = strpos($input, ';')) === false) { 
     486            $input = $this->_decode_headers ? $this->_decodeHeader($input) : $input; 
     487            $return['value'] = trim($input); 
     488            return $return; 
     489        } 
     490 
     491 
     492 
     493        $value = substr($input, 0, $pos); 
     494        $value = $this->_decode_headers ? $this->_decodeHeader($value) : $value; 
     495        $return['value'] = trim($value); 
     496        $input = trim(substr($input, $pos+1)); 
     497 
     498        if (!strlen($input) > 0) { 
     499            return $return; 
     500        } 
     501        // at this point input contains xxxx=".....";zzzz="...." 
     502        // since we are dealing with quoted strings, we need to handle this properly.. 
     503        $i = 0; 
     504        $l = strlen($input); 
     505        $key = ''; 
     506        $val = false; // our string - including quotes.. 
     507        $q = false; // in quote.. 
     508        $lq = ''; // last quote.. 
     509 
     510        while ($i < $l) { 
     511             
     512            $c = $input[$i]; 
     513            //var_dump(array('i'=>$i,'c'=>$c,'q'=>$q, 'lq'=>$lq, 'key'=>$key, 'val' =>$val)); 
     514 
     515            $escaped = false; 
     516            if ($c == '\\') { 
     517                $i++; 
     518                if ($i == $l-1) { // end of string. 
     519                    break; 
     520                } 
     521                $escaped = true; 
     522                $c = $input[$i]; 
     523            }             
     524 
     525 
     526            // state - in key.. 
     527            if ($val === false) { 
     528                if (!$escaped && $c == '=') { 
     529                    $val = ''; 
     530                    $key = trim($key); 
     531                    $i++; 
     532                    continue; 
     533                } 
     534                if (!$escaped && $c == ';') { 
     535                    if ($key) { // a key without a value.. 
     536                        $key= trim($key); 
     537                        $return['other'][$key] = ''; 
     538                        $return['other'][strtolower($key)] = ''; 
    461539                    } 
    462                     $parameters[] = $param; 
    463                 } 
    464  
    465                 for ($i = 0; $i < count($parameters); $i++) { 
    466                     $param_name  = trim(substr($parameters[$i], 0, $pos = strpos($parameters[$i], '=')), "'\";\t\\ "); 
    467                     $param_value = trim(str_replace('\;', ';', substr($parameters[$i], $pos + 1)), "'\";\t\\ "); 
    468                     if ($param_value[0] == '"') { 
    469                         $param_value = substr($param_value, 1, -1); 
     540                    $key = ''; 
     541                } 
     542                $key .= $c; 
     543                $i++; 
     544                continue; 
     545            } 
     546                      
     547            // state - in value.. (as $val is set..) 
     548 
     549            if ($q === false) { 
     550                // not in quote yet. 
     551                if ((!strlen($val) || $lq !== false) && $c == ' ' ||  $c == "\t") { 
     552                    $i++; 
     553                    continue; // skip leading spaces after '=' or after '"' 
     554                } 
     555                if (!$escaped && ($c == '"' || $c == "'")) { 
     556                    // start quoted area.. 
     557                    $q = $c; 
     558                    // in theory should not happen raw text in value part.. 
     559                    // but we will handle it as a merged part of the string.. 
     560                    $val = !strlen(trim($val)) ? '' : trim($val); 
     561                    $i++; 
     562                    continue; 
     563                } 
     564                // got end.... 
     565                if (!$escaped && $c == ';') { 
     566 
     567                    $val = trim($val); 
     568                    $added = false; 
     569                    if (preg_match('/\*[0-9]+$/', $key)) { 
     570                        // this is the extended aaa*0=...;aaa*1=.... code 
     571                        // it assumes the pieces arrive in order, and are valid... 
     572                        $key = preg_replace('/\*[0-9]+$/', '', $key); 
     573                        if (isset($return['other'][$key])) { 
     574                            $return['other'][$key] .= $val; 
     575                            if (strtolower($key) != $key) { 
     576                                $return['other'][strtolower($key)] .= $val; 
     577                            } 
     578                            $added = true; 
     579                        } 
     580                        // continue and use standard setters.. 
    470581                    } 
    471                     $return['other'][$param_name] = $param_value; 
    472                     $return['other'][strtolower($param_name)] = $param_value; 
    473                 } 
    474             } 
    475         } else { 
    476             $return['value'] = trim($input); 
    477         } 
    478  
     582                    if (!$added) { 
     583                        $return['other'][$key] = $val; 
     584                        $return['other'][strtolower($key)] = $val; 
     585                    } 
     586                    $val = false; 
     587                    $key = ''; 
     588                    $lq = false; 
     589                    $i++; 
     590                    continue; 
     591                } 
     592 
     593                $val .= $c; 
     594                $i++; 
     595                continue; 
     596            } 
     597             
     598            // state - in quote.. 
     599            if (!$escaped && $c == $q) {  // potential exit state.. 
     600 
     601                // end of quoted string.. 
     602                $lq = $q; 
     603                $q = false; 
     604                $i++; 
     605                continue; 
     606            } 
     607                 
     608            // normal char inside of quoted string.. 
     609            $val.= $c; 
     610            $i++; 
     611        } 
     612         
     613        // do we have anything left.. 
     614        if (strlen(trim($key)) || $val !== false) { 
     615            
     616            $val = trim($val); 
     617            $added = false; 
     618            if ($val !== false && preg_match('/\*[0-9]+$/', $key)) { 
     619                // no dupes due to our crazy regexp. 
     620                $key = preg_replace('/\*[0-9]+$/', '', $key); 
     621                if (isset($return['other'][$key])) { 
     622                    $return['other'][$key] .= $val; 
     623                    if (strtolower($key) != $key) { 
     624                        $return['other'][strtolower($key)] .= $val; 
     625                    } 
     626                    $added = true; 
     627                } 
     628                // continue and use standard setters.. 
     629            } 
     630            if (!$added) { 
     631                $return['other'][$key] = $val; 
     632                $return['other'][strtolower($key)] = $val; 
     633            } 
     634        } 
     635        // decode values. 
     636        foreach($return['other'] as $key =>$val) { 
     637            $return['other'][$key] = $this->_decode_headers ? $this->_decodeHeader($val) : $val; 
     638        } 
     639       //print_r($return); 
    479640        return $return; 
    480641    } 
     
    498659            $boundary = $bs_possible; 
    499660        } 
    500  
    501         $tmp = explode('--' . $boundary, $input); 
    502  
    503         for ($i = 1; $i < count($tmp) - 1; $i++) { 
    504             $parts[] = $tmp[$i]; 
    505         } 
    506  
     661        $tmp = preg_split("/--".preg_quote($boundary, '/')."((?=\s)|--)/", $input); 
     662 
     663        $len = count($tmp) -1; 
     664        for ($i = 1; $i < $len; $i++) { 
     665            if (strlen(trim($tmp[$i]))) { 
     666                $parts[] = $tmp[$i]; 
     667            } 
     668        } 
     669         
     670        // add the last part on if it does not end with the 'closing indicator' 
     671        if (!empty($tmp[$len]) && strlen(trim($tmp[$len])) && $tmp[$len][0] != '-') { 
     672            $parts[] = $tmp[$len]; 
     673        } 
    507674        return $parts; 
    508675    } 
     
    707874                case "cc": 
    708875                case "bcc": 
    709                     $to = ",".$item['value']; 
     876                    $to .= ",".$item['value']; 
    710877                default: 
    711878                   break; 
     
    8351002 
    8361003} // End of class 
    837 ?> 
  • branches/version-2_5-dev/data/module/Mail/mimePart.php

    r16300 r19941  
    11<?php 
    2 // +-----------------------------------------------------------------------+ 
    3 // | Copyright (c) 2002-2003  Richard Heyes                                | 
    4 // | All rights reserved.                                                  | 
    5 // |                                                                       | 
    6 // | Redistribution and use in source and binary forms, with or without    | 
    7 // | modification, are permitted provided that the following conditions    | 
    8 // | are met:                                                              | 
    9 // |                                                                       | 
    10 // | o Redistributions of source code must retain the above copyright      | 
    11 // |   notice, this list of conditions and the following disclaimer.       | 
    12 // | o Redistributions in binary form must reproduce the above copyright   | 
    13 // |   notice, this list of conditions and the following disclaimer in the | 
    14 // |   documentation and/or other materials provided with the distribution.| 
    15 // | o The names of the authors may not be used to endorse or promote      | 
    16 // |   products derived from this software without specific prior written  | 
    17 // |   permission.                                                         | 
    18 // |                                                                       | 
    19 // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   | 
    20 // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     | 
    21 // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
    22 // | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  | 
    23 // | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
    24 // | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      | 
    25 // | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
    26 // | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
    27 // | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   | 
    28 // | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
    29 // | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  | 
    30 // |                                                                       | 
    31 // +-----------------------------------------------------------------------+ 
    32 // | Author: Richard Heyes <richard@phpguru.org>                           | 
    33 // +-----------------------------------------------------------------------+ 
    34  
    352/** 
    36 * 
    37 *  Raw mime encoding class 
    38 * 
    39 * What is it? 
    40 *   This class enables you to manipulate and build 
    41 *   a mime email from the ground up. 
    42 * 
    43 * Why use this instead of mime.php? 
    44 *   mime.php is a userfriendly api to this class for 
    45 *   people who aren't interested in the internals of 
    46 *   mime mail. This class however allows full control 
    47 *   over the email. 
    48 * 
    49 * Eg. 
    50 * 
    51 * // Since multipart/mixed has no real body, (the body is 
    52 * // the subpart), we set the body argument to blank. 
    53 * 
    54 * $params['content_type'] = 'multipart/mixed'; 
    55 * $email = new Mail_mimePart('', $params); 
    56 * 
    57 * // Here we add a text part to the multipart we have 
    58 * // already. Assume $body contains plain text. 
    59 * 
    60 * $params['content_type'] = 'text/plain'; 
    61 * $params['encoding']     = '7bit'; 
    62 * $text = $email->addSubPart($body, $params); 
    63 * 
    64 * // Now add an attachment. Assume $attach is 
    65 * the contents of the attachment 
    66 * 
    67 * $params['content_type'] = 'application/zip'; 
    68 * $params['encoding']     = 'base64'; 
    69 * $params['disposition']  = 'attachment'; 
    70 * $params['dfilename']    = 'example.zip'; 
    71 * $attach =& $email->addSubPart($body, $params); 
    72 * 
    73 * // Now build the email. Note that the encode 
    74 * // function returns an associative array containing two 
    75 * // elements, body and headers. You will need to add extra 
    76 * // headers, (eg. Mime-Version) before sending. 
    77 * 
    78 * $email = $message->encode(); 
    79 * $email['headers'][] = 'Mime-Version: 1.0'; 
    80 * 
    81 * 
    82 * Further examples are available at http://www.phpguru.org 
    83 * 
    84 * TODO: 
    85 *  - Set encode() to return the $obj->encoded if encode() 
    86 *    has already been run. Unless a flag is passed to specifically 
    87 *    re-build the message. 
    88 * 
    89 * @author  Richard Heyes <richard@phpguru.org> 
    90 * @version $Revision: 1.13 $ 
    91 * @package Mail 
    92 */ 
    93  
    94 class Mail_mimePart { 
    95  
    96    /** 
     3 * The Mail_mimePart class is used to create MIME E-mail messages 
     4 * 
     5 * This class enables you to manipulate and build a mime email 
     6 * from the ground up. The Mail_Mime class is a userfriendly api 
     7 * to this class for people who aren't interested in the internals 
     8 * of mime mail. 
     9 * This class however allows full control over the email. 
     10 * 
     11 * Compatible with PHP versions 4 and 5 
     12 * 
     13 * LICENSE: This LICENSE is in the BSD license style. 
     14 * Copyright (c) 2002-2003, Richard Heyes <richard@phpguru.org> 
     15 * Copyright (c) 2003-2006, PEAR <pear-group@php.net> 
     16 * All rights reserved. 
     17 * 
     18 * Redistribution and use in source and binary forms, with or 
     19 * without modification, are permitted provided that the following 
     20 * conditions are met: 
     21 * 
     22 * - Redistributions of source code must retain the above copyright 
     23 *   notice, this list of conditions and the following disclaimer. 
     24 * - Redistributions in binary form must reproduce the above copyright 
     25 *   notice, this list of conditions and the following disclaimer in the 
     26 *   documentation and/or other materials provided with the distribution. 
     27 * - Neither the name of the authors, nor the names of its contributors  
     28 *   may be used to endorse or promote products derived from this  
     29 *   software without specific prior written permission. 
     30 * 
     31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
     32 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
     33 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
     34 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
     35 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
     36 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
     37 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
     38 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
     39 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
     40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
     41 * THE POSSIBILITY OF SUCH DAMAGE. 
     42 * 
     43 * @category  Mail 
     44 * @package   Mail_Mime 
     45 * @author    Richard Heyes  <richard@phpguru.org> 
     46 * @author    Cipriano Groenendal <cipri@php.net> 
     47 * @author    Sean Coates <sean@php.net> 
     48 * @author    Aleksander Machniak <alec@php.net> 
     49 * @copyright 2003-2006 PEAR <pear-group@php.net> 
     50 * @license   http://www.opensource.org/licenses/bsd-license.php BSD License 
     51 * @version   CVS: $Id$ 
     52 * @link      http://pear.php.net/package/Mail_mime 
     53 */ 
     54 
     55 
     56/** 
     57 * The Mail_mimePart class is used to create MIME E-mail messages 
     58 * 
     59 * This class enables you to manipulate and build a mime email 
     60 * from the ground up. The Mail_Mime class is a userfriendly api 
     61 * to this class for people who aren't interested in the internals 
     62 * of mime mail. 
     63 * This class however allows full control over the email. 
     64 * 
     65 * @category  Mail 
     66 * @package   Mail_Mime 
     67 * @author    Richard Heyes  <richard@phpguru.org> 
     68 * @author    Cipriano Groenendal <cipri@php.net> 
     69 * @author    Sean Coates <sean@php.net> 
     70 * @author    Aleksander Machniak <alec@php.net> 
     71 * @copyright 2003-2006 PEAR <pear-group@php.net> 
     72 * @license   http://www.opensource.org/licenses/bsd-license.php BSD License 
     73 * @version   Release: @package_version@ 
     74 * @link      http://pear.php.net/package/Mail_mime 
     75 */ 
     76class Mail_mimePart 
     77{ 
     78    /** 
    9779    * The encoding type of this part 
     80    * 
    9881    * @var string 
     82    * @access private 
    9983    */ 
    10084    var $_encoding; 
    10185 
    102    /** 
     86    /** 
    10387    * An array of subparts 
     88    * 
    10489    * @var array 
     90    * @access private 
    10591    */ 
    10692    var $_subparts; 
    10793 
    108    /** 
     94    /** 
    10995    * The output of this part after being built 
     96    * 
    11097    * @var string 
     98    * @access private 
    11199    */ 
    112100    var $_encoded; 
    113101 
    114    /** 
     102    /** 
    115103    * Headers for this part 
     104    * 
    116105    * @var array 
     106    * @access private 
    117107    */ 
    118108    var $_headers; 
    119109 
    120    /** 
     110    /** 
    121111    * The body of this part (not encoded) 
     112    * 
    122113    * @var string 
     114    * @access private 
    123115    */ 
    124116    var $_body; 
    125117 
    126118    /** 
    127      * Constructor. 
    128      * 
    129      * Sets up the object. 
    130      * 
    131      * @param $body   - The body of the mime part if any. 
    132      * @param $params - An associative array of parameters: 
    133      *                  content_type - The content type for this part eg multipart/mixed 
    134      *                  encoding     - The encoding to use, 7bit, 8bit, base64, or quoted-printable 
    135      *                  cid          - Content ID to apply 
    136      *                  disposition  - Content disposition, inline or attachment 
    137      *                  dfilename    - Optional filename parameter for content disposition 
    138      *                  description  - Content description 
    139      *                  charset      - Character set to use 
    140      * @access public 
    141      */ 
     119    * The location of file with body of this part (not encoded) 
     120    * 
     121    * @var string 
     122    * @access private 
     123    */ 
     124    var $_body_file; 
     125 
     126    /** 
     127    * The end-of-line sequence 
     128    * 
     129    * @var string 
     130    * @access private 
     131    */ 
     132    var $_eol = "\r\n"; 
     133 
     134    /** 
     135    * Constructor. 
     136    * 
     137    * Sets up the object. 
     138    * 
     139    * @param string $body   The body of the mime part if any. 
     140    * @param array  $params An associative array of optional parameters: 
     141    *     content_type      - The content type for this part eg multipart/mixed 
     142    *     encoding          - The encoding to use, 7bit, 8bit, 
     143    *                         base64, or quoted-printable 
     144    *     charset           - Content character set 
     145    *     cid               - Content ID to apply 
     146    *     disposition       - Content disposition, inline or attachment 
     147    *     dfilename         - Filename parameter for content disposition 
     148    *     description       - Content description 
     149    *     name_encoding     - Encoding of the attachment name (Content-Type) 
     150    *                         By default filenames are encoded using RFC2231 
     151    *                         Here you can set RFC2047 encoding (quoted-printable 
     152    *                         or base64) instead 
     153    *     filename_encoding - Encoding of the attachment filename (Content-Disposition) 
     154    *                         See 'name_encoding' 
     155    *     headers_charset   - Charset of the headers e.g. filename, description. 
     156    *                         If not set, 'charset' will be used 
     157    *     eol               - End of line sequence. Default: "\r\n" 
     158    *     body_file         - Location of file with part's body (instead of $body) 
     159    * 
     160    * @access public 
     161    */ 
    142162    function Mail_mimePart($body = '', $params = array()) 
    143163    { 
    144         if (!defined('MAIL_MIMEPART_CRLF')) { 
    145             define('MAIL_MIMEPART_CRLF', defined('MAIL_MIME_CRLF') ? MAIL_MIME_CRLF : "\r\n", TRUE); 
     164        if (!empty($params['eol'])) { 
     165            $this->_eol = $params['eol']; 
     166        } else if (defined('MAIL_MIMEPART_CRLF')) { // backward-copat. 
     167            $this->_eol = MAIL_MIMEPART_CRLF; 
    146168        } 
    147169 
    148170        foreach ($params as $key => $value) { 
    149171            switch ($key) { 
    150                 case 'content_type': 
    151                     $headers['Content-Type'] = $value . (isset($charset) ? '; charset="' . $charset . '"' : ''); 
    152                     break; 
    153  
    154                 case 'encoding': 
    155                     $this->_encoding = $value; 
    156                     $headers['Content-Transfer-Encoding'] = $value; 
    157                     break; 
    158  
    159                 case 'cid': 
    160                     $headers['Content-ID'] = '<' . $value . '>'; 
    161                     break; 
    162  
    163                 case 'disposition': 
    164                     $headers['Content-Disposition'] = $value . (isset($dfilename) ? '; filename="' . $dfilename . '"' : ''); 
    165                     break; 
    166  
    167                 case 'dfilename': 
    168                     if (isset($headers['Content-Disposition'])) { 
    169                         $headers['Content-Disposition'] .= '; filename="' . $value . '"'; 
    170                     } else { 
    171                         $dfilename = $value; 
    172                     } 
    173                     break; 
    174  
    175                 case 'description': 
    176                     $headers['Content-Description'] = $value; 
    177                     break; 
    178  
    179                 case 'charset': 
    180                     if (isset($headers['Content-Type'])) { 
    181                         $headers['Content-Type'] .= '; charset="' . $value . '"'; 
    182                     } else { 
    183                         $charset = $value; 
    184                     } 
    185                     break; 
     172            case 'encoding': 
     173                $this->_encoding = $value; 
     174                $headers['Content-Transfer-Encoding'] = $value; 
     175                break; 
     176 
     177            case 'cid': 
     178                $headers['Content-ID'] = '<' . $value . '>'; 
     179                break; 
     180 
     181            case 'location': 
     182                $headers['Content-Location'] = $value; 
     183                break; 
     184 
     185            case 'body_file': 
     186                $this->_body_file = $value; 
     187                break; 
    186188            } 
    187189        } 
    188190 
    189191        // Default content-type 
    190         if (!isset($headers['Content-Type'])) { 
    191             $headers['Content-Type'] = 'text/plain'; 
    192         } 
    193  
    194         //Default encoding 
     192        if (empty($params['content_type'])) { 
     193            $params['content_type'] = 'text/plain'; 
     194        } 
     195 
     196        // Content-Type 
     197        $headers['Content-Type'] = $params['content_type']; 
     198        if (!empty($params['charset'])) { 
     199            $charset = "charset={$params['charset']}"; 
     200            // place charset parameter in the same line, if possible 
     201            if ((strlen($headers['Content-Type']) + strlen($charset) + 16) <= 76) { 
     202                $headers['Content-Type'] .= '; '; 
     203            } else { 
     204                $headers['Content-Type'] .= ';' . $this->_eol . ' '; 
     205            } 
     206            $headers['Content-Type'] .= $charset; 
     207 
     208            // Default headers charset 
     209            if (!isset($params['headers_charset'])) { 
     210                $params['headers_charset'] = $params['charset']; 
     211            } 
     212        } 
     213        if (!empty($params['filename'])) { 
     214            $headers['Content-Type'] .= ';' . $this->_eol; 
     215            $headers['Content-Type'] .= $this->_buildHeaderParam( 
     216                'name', $params['filename'], 
     217                !empty($params['headers_charset']) ? $params['headers_charset'] : 'US-ASCII', 
     218                !empty($params['language']) ? $params['language'] : null, 
     219                !empty($params['name_encoding']) ? $params['name_encoding'] : null 
     220            ); 
     221        } 
     222 
     223        // Content-Disposition 
     224        if (!empty($params['disposition'])) { 
     225            $headers['Content-Disposition'] = $params['disposition']; 
     226            if (!empty($params['filename'])) { 
     227                $headers['Content-Disposition'] .= ';' . $this->_eol; 
     228                $headers['Content-Disposition'] .= $this->_buildHeaderParam( 
     229                    'filename', $params['filename'], 
     230                    !empty($params['headers_charset']) ? $params['headers_charset'] : 'US-ASCII', 
     231                    !empty($params['language']) ? $params['language'] : null, 
     232                    !empty($params['filename_encoding']) ? $params['filename_encoding'] : null 
     233                ); 
     234            } 
     235        } 
     236 
     237        if (!empty($params['description'])) { 
     238            $headers['Content-Description'] = $this->encodeHeader( 
     239                'Content-Description', $params['description'], 
     240                !empty($params['headers_charset']) ? $params['headers_charset'] : 'US-ASCII', 
     241                !empty($params['name_encoding']) ? $params['name_encoding'] : 'quoted-printable', 
     242                $this->_eol 
     243            ); 
     244        } 
     245 
     246        // Default encoding 
    195247        if (!isset($this->_encoding)) { 
    196248            $this->_encoding = '7bit'; 
     
    204256 
    205257    /** 
    206      * encode() 
    207      * 
    208258     * Encodes and returns the email. Also stores 
    209259     * it in the encoded member variable 
    210260     * 
     261     * @param string $boundary Pre-defined boundary string 
     262     * 
    211263     * @return An associative array containing two elements, 
    212264     *         body and headers. The headers element is itself 
    213      *         an indexed array. 
     265     *         an indexed array. On error returns PEAR error object. 
    214266     * @access public 
    215267     */ 
    216     function encode() 
     268    function encode($boundary=null) 
    217269    { 
    218270        $encoded =& $this->_encoded; 
    219271 
    220         if (!empty($this->_subparts)) { 
    221             srand((double)microtime()*1000000); 
    222             $boundary = '=_' . md5(rand() . microtime()); 
    223             $this->_headers['Content-Type'] .= ';' . MAIL_MIMEPART_CRLF . "\t" . 'boundary="' . $boundary . '"'; 
    224  
    225             // Add body parts to $subparts 
     272        if (count($this->_subparts)) { 
     273            $boundary = $boundary ? $boundary : '=_' . md5(rand() . microtime()); 
     274            $eol = $this->_eol; 
     275 
     276            $this->_headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; 
     277 
     278            $encoded['body'] = '';  
     279 
    226280            for ($i = 0; $i < count($this->_subparts); $i++) { 
    227                 $headers = array(); 
     281                $encoded['body'] .= '--' . $boundary . $eol; 
    228282                $tmp = $this->_subparts[$i]->encode(); 
     283                if (PEAR::isError($tmp)) { 
     284                    return $tmp; 
     285                } 
    229286                foreach ($tmp['headers'] as $key => $value) { 
    230                     $headers[] = $key . ': ' . $value; 
    231                 } 
    232                 $subparts[] = implode(MAIL_MIMEPART_CRLF, $headers) . MAIL_MIMEPART_CRLF . MAIL_MIMEPART_CRLF . $tmp['body']; 
    233             } 
    234  
    235             $encoded['body'] = '--' . $boundary . MAIL_MIMEPART_CRLF . 
    236                                implode('--' . $boundary . MAIL_MIMEPART_CRLF, $subparts) . 
    237                                '--' . $boundary.'--' . MAIL_MIMEPART_CRLF; 
    238  
     287                    $encoded['body'] .= $key . ': ' . $value . $eol; 
     288                } 
     289                $encoded['body'] .= $eol . $tmp['body'] . $eol; 
     290            } 
     291 
     292            $encoded['body'] .= '--' . $boundary . '--' . $eol; 
     293 
     294        } else if ($this->_body) { 
     295            $encoded['body'] = $this->_getEncodedData($this->_body, $this->_encoding); 
     296        } else if ($this->_body_file) { 
     297            // Temporarily reset magic_quotes_runtime for file reads and writes 
     298            if ($magic_quote_setting = get_magic_quotes_runtime()) { 
     299                @ini_set('magic_quotes_runtime', 0); 
     300            } 
     301            $body = $this->_getEncodedDataFromFile($this->_body_file, $this->_encoding); 
     302            if ($magic_quote_setting) { 
     303                @ini_set('magic_quotes_runtime', $magic_quote_setting); 
     304            } 
     305 
     306            if (PEAR::isError($body)) { 
     307                return $body; 
     308            } 
     309            $encoded['body'] = $body; 
    239310        } else { 
    240             $encoded['body'] = $this->_getEncodedData($this->_body, $this->_encoding) . MAIL_MIMEPART_CRLF; 
     311            $encoded['body'] = ''; 
    241312        } 
    242313 
     
    248319 
    249320    /** 
    250      * &addSubPart() 
    251      * 
     321     * Encodes and saves the email into file. File must exist. 
     322     * Data will be appended to the file. 
     323     * 
     324     * @param string  $filename  Output file location 
     325     * @param string  $boundary  Pre-defined boundary string 
     326     * @param boolean $skip_head True if you don't want to save headers 
     327     * 
     328     * @return array An associative array containing message headers 
     329     *               or PEAR error object 
     330     * @access public 
     331     * @since 1.6.0 
     332     */ 
     333    function encodeToFile($filename, $boundary=null, $skip_head=false) 
     334    { 
     335        if (file_exists($filename) && !is_writable($filename)) { 
     336            $err = PEAR::raiseError('File is not writeable: ' . $filename); 
     337            return $err; 
     338        } 
     339 
     340        if (!($fh = fopen($filename, 'ab'))) { 
     341            $err = PEAR::raiseError('Unable to open file: ' . $filename); 
     342            return $err; 
     343        } 
     344 
     345        // Temporarily reset magic_quotes_runtime for file reads and writes 
     346        if ($magic_quote_setting = get_magic_quotes_runtime()) { 
     347            @ini_set('magic_quotes_runtime', 0); 
     348        } 
     349 
     350        $res = $this->_encodePartToFile($fh, $boundary, $skip_head); 
     351 
     352        fclose($fh); 
     353 
     354        if ($magic_quote_setting) { 
     355            @ini_set('magic_quotes_runtime', $magic_quote_setting); 
     356        } 
     357 
     358        return PEAR::isError($res) ? $res : $this->_headers; 
     359    } 
     360 
     361    /** 
     362     * Encodes given email part into file 
     363     * 
     364     * @param string  $fh        Output file handle 
     365     * @param string  $boundary  Pre-defined boundary string 
     366     * @param boolean $skip_head True if you don't want to save headers 
     367     * 
     368     * @return array True on sucess or PEAR error object 
     369     * @access private 
     370     */ 
     371    function _encodePartToFile($fh, $boundary=null, $skip_head=false) 
     372    { 
     373        $eol = $this->_eol; 
     374 
     375        if (count($this->_subparts)) { 
     376            $boundary = $boundary ? $boundary : '=_' . md5(rand() . microtime()); 
     377            $this->_headers['Content-Type'] .= ";$eol boundary=\"$boundary\""; 
     378        } 
     379 
     380        if (!$skip_head) { 
     381            foreach ($this->_headers as $key => $value) { 
     382                fwrite($fh, $key . ': ' . $value . $eol); 
     383            } 
     384            $f_eol = $eol; 
     385        } else { 
     386            $f_eol = ''; 
     387        } 
     388 
     389        if (count($this->_subparts)) { 
     390            for ($i = 0; $i < count($this->_subparts); $i++) { 
     391                fwrite($fh, $f_eol . '--' . $boundary . $eol); 
     392                $res = $this->_subparts[$i]->_encodePartToFile($fh); 
     393                if (PEAR::isError($res)) { 
     394                    return $res; 
     395                } 
     396                $f_eol = $eol; 
     397            } 
     398 
     399            fwrite($fh, $eol . '--' . $boundary . '--' . $eol); 
     400 
     401        } else if ($this->_body) { 
     402            fwrite($fh, $f_eol . $this->_getEncodedData($this->_body, $this->_encoding)); 
     403        } else if ($this->_body_file) { 
     404            fwrite($fh, $f_eol); 
     405            $res = $this->_getEncodedDataFromFile( 
     406                $this->_body_file, $this->_encoding, $fh 
     407            ); 
     408            if (PEAR::isError($res)) { 
     409                return $res; 
     410            } 
     411        } 
     412 
     413        return true; 
     414    } 
     415 
     416    /** 
    252417     * Adds a subpart to current mime part and returns 
    253418     * a reference to it 
    254419     * 
    255      * @param $body   The body of the subpart, if any. 
    256      * @param $params The parameters for the subpart, same 
    257      *                as the $params argument for constructor. 
    258      * @return A reference to the part you just added. It is 
    259      *         crucial if using multipart/* in your subparts that 
    260      *         you use =& in your script when calling this function, 
    261      *         otherwise you will not be able to add further subparts. 
     420     * @param string $body   The body of the subpart, if any. 
     421     * @param array  $params The parameters for the subpart, same 
     422     *                       as the $params argument for constructor. 
     423     * 
     424     * @return Mail_mimePart A reference to the part you just added. It is 
     425     *                       crucial if using multipart/* in your subparts that 
     426     *                       you use =& in your script when calling this function, 
     427     *                       otherwise you will not be able to add further subparts. 
    262428     * @access public 
    263429     */ 
    264     function &addSubPart($body, $params) 
     430    function &addSubpart($body, $params) 
    265431    { 
    266432        $this->_subparts[] = new Mail_mimePart($body, $params); 
     
    269435 
    270436    /** 
    271      * _getEncodedData() 
    272      * 
    273437     * Returns encoded data based upon encoding passed to it 
    274438     * 
    275      * @param $data     The data to encode. 
    276      * @param $encoding The encoding type to use, 7bit, base64, 
    277      *                  or quoted-printable. 
     439     * @param string $data     The data to encode. 
     440     * @param string $encoding The encoding type to use, 7bit, base64, 
     441     *                         or quoted-printable. 
     442     * 
     443     * @return string 
    278444     * @access private 
    279445     */ 
     
    281447    { 
    282448        switch ($encoding) { 
    283             case '8bit': 
    284             case '7bit': 
    285                 return $data; 
    286                 break; 
    287  
    288             case 'quoted-printable': 
    289                 return $this->_quotedPrintableEncode($data); 
    290                 break; 
    291  
    292             case 'base64': 
    293                 return rtrim(chunk_split(base64_encode($data), 76, MAIL_MIMEPART_CRLF)); 
    294                 break; 
    295  
    296             default: 
    297                 return $data; 
    298         } 
    299     } 
    300  
    301     /** 
    302      * quoteadPrintableEncode() 
    303      * 
     449        case 'quoted-printable': 
     450            return $this->_quotedPrintableEncode($data); 
     451            break; 
     452 
     453        case 'base64': 
     454            return rtrim(chunk_split(base64_encode($data), 76, $this->_eol)); 
     455            break; 
     456 
     457        case '8bit': 
     458        case '7bit': 
     459        default: 
     460            return $data; 
     461        } 
     462    } 
     463 
     464    /** 
     465     * Returns encoded data based upon encoding passed to it 
     466     * 
     467     * @param string   $filename Data file location 
     468     * @param string   $encoding The encoding type to use, 7bit, base64, 
     469     *                           or quoted-printable. 
     470     * @param resource $fh       Output file handle. If set, data will be 
     471     *                           stored into it instead of returning it 
     472     * 
     473     * @return string Encoded data or PEAR error object 
     474     * @access private 
     475     */ 
     476    function _getEncodedDataFromFile($filename, $encoding, $fh=null) 
     477    { 
     478        if (!is_readable($filename)) { 
     479            $err = PEAR::raiseError('Unable to read file: ' . $filename); 
     480            return $err; 
     481        } 
     482 
     483        if (!($fd = fopen($filename, 'rb'))) { 
     484            $err = PEAR::raiseError('Could not open file: ' . $filename); 
     485            return $err; 
     486        } 
     487 
     488        $data = ''; 
     489 
     490        switch ($encoding) { 
     491        case 'quoted-printable': 
     492            while (!feof($fd)) { 
     493                $buffer = $this->_quotedPrintableEncode(fgets($fd)); 
     494                if ($fh) { 
     495                    fwrite($fh, $buffer); 
     496                } else { 
     497                    $data .= $buffer; 
     498                } 
     499            } 
     500            break; 
     501 
     502        case 'base64': 
     503            while (!feof($fd)) { 
     504                // Should read in a multiple of 57 bytes so that 
     505                // the output is 76 bytes per line. Don't use big chunks 
     506                // because base64 encoding is memory expensive 
     507                $buffer = fread($fd, 57 * 9198); // ca. 0.5 MB 
     508                $buffer = base64_encode($buffer); 
     509                $buffer = chunk_split($buffer, 76, $this->_eol); 
     510                if (feof($fd)) { 
     511                    $buffer = rtrim($buffer); 
     512                } 
     513 
     514                if ($fh) { 
     515                    fwrite($fh, $buffer); 
     516                } else { 
     517                    $data .= $buffer; 
     518                } 
     519            } 
     520            break; 
     521 
     522        case '8bit': 
     523        case '7bit': 
     524        default: 
     525            while (!feof($fd)) { 
     526                $buffer = fread($fd, 1048576); // 1 MB 
     527                if ($fh) { 
     528                    fwrite($fh, $buffer); 
     529                } else { 
     530                    $data .= $buffer; 
     531                } 
     532            } 
     533        } 
     534 
     535        fclose($fd); 
     536 
     537        if (!$fh) { 
     538            return $data; 
     539        } 
     540    } 
     541 
     542    /** 
    304543     * Encodes data to quoted-printable standard. 
    305544     * 
    306      * @param $input    The data to encode 
    307      * @param $line_max Optional max line length. Should 
    308      *                  not be more than 76 chars 
     545     * @param string $input    The data to encode 
     546     * @param int    $line_max Optional max line length. Should 
     547     *                         not be more than 76 chars 
     548     * 
     549     * @return string Encoded data 
    309550     * 
    310551     * @access private 
     
    312553    function _quotedPrintableEncode($input , $line_max = 76) 
    313554    { 
     555        $eol = $this->_eol; 
     556        /* 
     557        // imap_8bit() is extremely fast, but doesn't handle properly some characters 
     558        if (function_exists('imap_8bit') && $line_max == 76) { 
     559            $input = preg_replace('/\r?\n/', "\r\n", $input); 
     560            $input = imap_8bit($input); 
     561            if ($eol != "\r\n") { 
     562                $input = str_replace("\r\n", $eol, $input); 
     563            } 
     564            return $input; 
     565        } 
     566        */ 
    314567        $lines  = preg_split("/\r?\n/", $input); 
    315         $eol    = MAIL_MIMEPART_CRLF; 
    316568        $escape = '='; 
    317569        $output = ''; 
    318570 
    319         while(list(, $line) = each($lines)){ 
    320  
    321             $linlen     = strlen($line); 
     571        while (list($idx, $line) = each($lines)) { 
    322572            $newline = ''; 
    323  
    324             for ($i = 0; $i < $linlen; $i++) { 
    325                 $char = substr($line, $i, 1); 
     573            $i = 0; 
     574 
     575            while (isset($line[$i])) { 
     576                $char = $line[$i]; 
    326577                $dec  = ord($char); 
    327  
    328                 if (($dec == 32) AND ($i == ($linlen - 1))){    // convert space at eol only 
     578                $i++; 
     579 
     580                if (($dec == 32) && (!isset($line[$i]))) { 
     581                    // convert space at eol only 
    329582                    $char = '=20'; 
    330  
    331                 } elseif(($dec == 9) AND ($i == ($linlen - 1))) {  // convert tab at eol only 
    332                     $char = '=09'; 
    333                 } elseif($dec == 9) { 
    334                     ; // Do nothing if a tab. 
    335                 } elseif(($dec == 61) OR ($dec < 32 ) OR ($dec > 126)) { 
    336                     $char = $escape . strtoupper(sprintf('%02s', dechex($dec))); 
    337                 } 
    338  
    339                 if ((strlen($newline) + strlen($char)) >= $line_max) {        // MAIL_MIMEPART_CRLF is not counted 
    340                     $output  .= $newline . $escape . $eol;                    // soft line break; " =\r\n" is okay 
     583                } elseif ($dec == 9 && isset($line[$i])) { 
     584                    ; // Do nothing if a TAB is not on eol 
     585                } elseif (($dec == 61) || ($dec < 32) || ($dec > 126)) { 
     586                    $char = $escape . sprintf('%02X', $dec); 
     587                } elseif (($dec == 46) && (($newline == '') 
     588                    || ((strlen($newline) + strlen("=2E")) >= $line_max)) 
     589                ) { 
     590                    // Bug #9722: convert full-stop at bol, 
     591                    // some Windows servers need this, won't break anything (cipri) 
     592                    // Bug #11731: full-stop at bol also needs to be encoded 
     593                    // if this line would push us over the line_max limit. 
     594                    $char = '=2E'; 
     595                } 
     596 
     597                // Note, when changing this line, also change the ($dec == 46) 
     598                // check line, as it mimics this line due to Bug #11731 
     599                // EOL is not counted 
     600                if ((strlen($newline) + strlen($char)) >= $line_max) { 
     601                    // soft line break; " =\r\n" is okay 
     602                    $output  .= $newline . $escape . $eol; 
    341603                    $newline  = ''; 
    342604                } 
     
    344606            } // end of for 
    345607            $output .= $newline . $eol; 
    346         } 
    347         $output = substr($output, 0, -1 * strlen($eol)); // Don't want last crlf 
     608            unset($lines[$idx]); 
     609        } 
     610        // Don't want last crlf 
     611        $output = substr($output, 0, -1 * strlen($eol)); 
    348612        return $output; 
    349613    } 
     614 
     615    /** 
     616     * Encodes the paramater of a header. 
     617     * 
     618     * @param string $name      The name of the header-parameter 
     619     * @param string $value     The value of the paramter 
     620     * @param string $charset   The characterset of $value 
     621     * @param string $language  The language used in $value 
     622     * @param string $encoding  Parameter encoding. If not set, parameter value 
     623     *                          is encoded according to RFC2231 
     624     * @param int    $maxLength The maximum length of a line. Defauls to 75 
     625     * 
     626     * @return string 
     627     * 
     628     * @access private 
     629     */ 
     630    function _buildHeaderParam($name, $value, $charset=null, $language=null, 
     631        $encoding=null, $maxLength=75 
     632    ) { 
     633        // RFC 2045: 
     634        // value needs encoding if contains non-ASCII chars or is longer than 78 chars 
     635        if (!preg_match('#[^\x20-\x7E]#', $value)) { 
     636            $token_regexp = '#([^\x21,\x23-\x27,\x2A,\x2B,\x2D' 
     637                . ',\x2E,\x30-\x39,\x41-\x5A,\x5E-\x7E])#'; 
     638            if (!preg_match($token_regexp, $value)) { 
     639                // token 
     640                if (strlen($name) + strlen($value) + 3 <= $maxLength) { 
     641                    return " {$name}={$value}"; 
     642                } 
     643            } else { 
     644                // quoted-string 
     645                $quoted = addcslashes($value, '\\"'); 
     646                if (strlen($name) + strlen($quoted) + 5 <= $maxLength) { 
     647                    return " {$name}=\"{$quoted}\""; 
     648                } 
     649            } 
     650        } 
     651 
     652        // RFC2047: use quoted-printable/base64 encoding 
     653        if ($encoding == 'quoted-printable' || $encoding == 'base64') { 
     654            return $this->_buildRFC2047Param($name, $value, $charset, $encoding); 
     655        } 
     656 
     657        // RFC2231: 
     658        $encValue = preg_replace_callback( 
     659            '/([^\x21,\x23,\x24,\x26,\x2B,\x2D,\x2E,\x30-\x39,\x41-\x5A,\x5E-\x7E])/', 
     660            array($this, '_encodeReplaceCallback'), $value 
     661        ); 
     662        $value = "$charset'$language'$encValue"; 
     663 
     664        $header = " {$name}*={$value}"; 
     665        if (strlen($header) <= $maxLength) { 
     666            return $header; 
     667        } 
     668 
     669        $preLength = strlen(" {$name}*0*="); 
     670        $maxLength = max(16, $maxLength - $preLength - 3); 
     671        $maxLengthReg = "|(.{0,$maxLength}[^\%][^\%])|"; 
     672 
     673        $headers = array(); 
     674        $headCount = 0; 
     675        while ($value) { 
     676            $matches = array(); 
     677            $found = preg_match($maxLengthReg, $value, $matches); 
     678            if ($found) { 
     679                $headers[] = " {$name}*{$headCount}*={$matches[0]}"; 
     680                $value = substr($value, strlen($matches[0])); 
     681            } else { 
     682                $headers[] = " {$name}*{$headCount}*={$value}"; 
     683                $value = ''; 
     684            } 
     685            $headCount++; 
     686        } 
     687 
     688        $headers = implode(';' . $this->_eol, $headers); 
     689        return $headers; 
     690    } 
     691 
     692    /** 
     693     * Encodes header parameter as per RFC2047 if needed 
     694     * 
     695     * @param string $name      The parameter name 
     696     * @param string $value     The parameter value 
     697     * @param string $charset   The parameter charset 
     698     * @param string $encoding  Encoding type (quoted-printable or base64) 
     699     * @param int    $maxLength Encoded parameter max length. Default: 76 
     700     * 
     701     * @return string Parameter line 
     702     * @access private 
     703     */ 
     704    function _buildRFC2047Param($name, $value, $charset, 
     705        $encoding='quoted-printable', $maxLength=76 
     706    ) { 
     707        // WARNING: RFC 2047 says: "An 'encoded-word' MUST NOT be used in 
     708        // parameter of a MIME Content-Type or Content-Disposition field", 
     709        // but... it's supported by many clients/servers 
     710        $quoted = ''; 
     711 
     712        if ($encoding == 'base64') { 
     713            $value = base64_encode($value); 
     714            $prefix = '=?' . $charset . '?B?'; 
     715            $suffix = '?='; 
     716 
     717            // 2 x SPACE, 2 x '"', '=', ';' 
     718            $add_len = strlen($prefix . $suffix) + strlen($name) + 6; 
     719            $len = $add_len + strlen($value); 
     720 
     721            while ($len > $maxLength) {  
     722                // We can cut base64-encoded string every 4 characters 
     723                $real_len = floor(($maxLength - $add_len) / 4) * 4; 
     724                $_quote = substr($value, 0, $real_len); 
     725                $value = substr($value, $real_len); 
     726 
     727                $quoted .= $prefix . $_quote . $suffix . $this->_eol . ' '; 
     728                $add_len = strlen($prefix . $suffix) + 4; // 2 x SPACE, '"', ';' 
     729                $len = strlen($value) + $add_len; 
     730            } 
     731            $quoted .= $prefix . $value . $suffix; 
     732 
     733        } else { 
     734            // quoted-printable 
     735            $value = $this->encodeQP($value); 
     736            $prefix = '=?' . $charset . '?Q?'; 
     737            $suffix = '?='; 
     738 
     739            // 2 x SPACE, 2 x '"', '=', ';' 
     740            $add_len = strlen($prefix . $suffix) + strlen($name) + 6; 
     741            $len = $add_len + strlen($value); 
     742 
     743            while ($len > $maxLength) { 
     744                $length = $maxLength - $add_len; 
     745                // don't break any encoded letters 
     746                if (preg_match("/^(.{0,$length}[^\=][^\=])/", $value, $matches)) { 
     747                    $_quote = $matches[1]; 
     748                } 
     749 
     750                $quoted .= $prefix . $_quote . $suffix . $this->_eol . ' '; 
     751                $value = substr($value, strlen($_quote)); 
     752                $add_len = strlen($prefix . $suffix) + 4; // 2 x SPACE, '"', ';' 
     753                $len = strlen($value) + $add_len; 
     754            } 
     755 
     756            $quoted .= $prefix . $value . $suffix; 
     757        } 
     758 
     759        return " {$name}=\"{$quoted}\""; 
     760    } 
     761 
     762    /** 
     763     * Encodes a header as per RFC2047 
     764     * 
     765     * @param string $name     The header name 
     766     * @param string $value    The header data to encode 
     767     * @param string $charset  Character set name 
     768     * @param string $encoding Encoding name (base64 or quoted-printable) 
     769     * @param string $eol      End-of-line sequence. Default: "\r\n" 
     770     * 
     771     * @return string          Encoded header data (without a name) 
     772     * @access public 
     773     * @since 1.6.1 
     774     */ 
     775    function encodeHeader($name, $value, $charset='ISO-8859-1', 
     776        $encoding='quoted-printable', $eol="\r\n" 
     777    ) { 
     778        // Structured headers 
     779        $comma_headers = array( 
     780            'from', 'to', 'cc', 'bcc', 'sender', 'reply-to', 
     781            'resent-from', 'resent-to', 'resent-cc', 'resent-bcc', 
     782            'resent-sender', 'resent-reply-to', 
     783            'return-receipt-to', 'disposition-notification-to', 
     784        ); 
     785        $other_headers = array( 
     786            'references', 'in-reply-to', 'message-id', 'resent-message-id', 
     787        ); 
     788 
     789        $name = strtolower($name); 
     790 
     791        if (in_array($name, $comma_headers)) { 
     792            $separator = ','; 
     793        } else if (in_array($name, $other_headers)) { 
     794            $separator = ' '; 
     795        } 
     796 
     797        if (!$charset) { 
     798            $charset = 'ISO-8859-1'; 
     799        } 
     800 
     801        // Structured header (make sure addr-spec inside is not encoded) 
     802        if (!empty($separator)) { 
     803            $parts = Mail_mimePart::_explodeQuotedString($separator, $value); 
     804            $value = ''; 
     805 
     806            foreach ($parts as $part) { 
     807                $part = preg_replace('/\r?\n[\s\t]*/', $eol . ' ', $part); 
     808                $part = trim($part); 
     809 
     810                if (!$part) { 
     811                    continue; 
     812                } 
     813                if ($value) { 
     814                    $value .= $separator==',' ? $separator.' ' : ' '; 
     815                } else { 
     816                    $value = $name . ': '; 
     817                } 
     818 
     819                // let's find phrase (name) and/or addr-spec 
     820                if (preg_match('/^<\S+@\S+>$/', $part)) { 
     821                    $value .= $part; 
     822                } else if (preg_match('/^\S+@\S+$/', $part)) { 
     823                    // address without brackets and without name 
     824                    $value .= $part; 
     825                } else if (preg_match('/<*\S+@\S+>*$/', $part, $matches)) { 
     826                    // address with name (handle name) 
     827                    $address = $matches[0]; 
     828                    $word = str_replace($address, '', $part); 
     829                    $word = trim($word); 
     830                    // check if phrase requires quoting 
     831                    if ($word) { 
     832                        // non-ASCII: require encoding 
     833                        if (preg_match('#([\x80-\xFF]){1}#', $word)) { 
     834                            if ($word[0] == '"' && $word[strlen($word)-1] == '"') { 
     835                                // de-quote quoted-string, encoding changes 
     836                                // string to atom 
     837                                $search = array("\\\"", "\\\\"); 
     838                                $replace = array("\"", "\\"); 
     839                                $word = str_replace($search, $replace, $word); 
     840                                $word = substr($word, 1, -1); 
     841                            } 
     842                            // find length of last line 
     843                            if (($pos = strrpos($value, $eol)) !== false) { 
     844                                $last_len = strlen($value) - $pos; 
     845                            } else { 
     846                                $last_len = strlen($value); 
     847                            } 
     848                            $word = Mail_mimePart::encodeHeaderValue( 
     849                                $word, $charset, $encoding, $last_len, $eol 
     850                            ); 
     851                        } else if (($word[0] != '"' || $word[strlen($word)-1] != '"') 
     852                            && preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $word) 
     853                        ) { 
     854                            // ASCII: quote string if needed 
     855                            $word = '"'.addcslashes($word, '\\"').'"'; 
     856                        } 
     857                    } 
     858                    $value .= $word.' '.$address; 
     859                } else { 
     860                    // addr-spec not found, don't encode (?) 
     861                    $value .= $part; 
     862                } 
     863 
     864                // RFC2822 recommends 78 characters limit, use 76 from RFC2047 
     865                $value = wordwrap($value, 76, $eol . ' '); 
     866            } 
     867 
     868            // remove header name prefix (there could be EOL too) 
     869            $value = preg_replace( 
     870                '/^'.$name.':('.preg_quote($eol, '/').')* /', '', $value 
     871            ); 
     872 
     873        } else { 
     874            // Unstructured header 
     875            // non-ASCII: require encoding 
     876            if (preg_match('#([\x80-\xFF]){1}#', $value)) { 
     877                if ($value[0] == '"' && $value[strlen($value)-1] == '"') { 
     878                    // de-quote quoted-string, encoding changes 
     879                    // string to atom 
     880                    $search = array("\\\"", "\\\\"); 
     881                    $replace = array("\"", "\\"); 
     882                    $value = str_replace($search, $replace, $value); 
     883                    $value = substr($value, 1, -1); 
     884                } 
     885                $value = Mail_mimePart::encodeHeaderValue( 
     886                    $value, $charset, $encoding, strlen($name) + 2, $eol 
     887                ); 
     888            } else if (strlen($name.': '.$value) > 78) { 
     889                // ASCII: check if header line isn't too long and use folding 
     890                $value = preg_replace('/\r?\n[\s\t]*/', $eol . ' ', $value); 
     891                $tmp = wordwrap($name.': '.$value, 78, $eol . ' '); 
     892                $value = preg_replace('/^'.$name.':\s*/', '', $tmp); 
     893                // hard limit 998 (RFC2822) 
     894                $value = wordwrap($value, 998, $eol . ' ', true); 
     895            } 
     896        } 
     897 
     898        return $value; 
     899    } 
     900 
     901    /** 
     902     * Explode quoted string 
     903     * 
     904     * @param string $delimiter Delimiter expression string for preg_match() 
     905     * @param string $string    Input string 
     906     * 
     907     * @return array            String tokens array 
     908     * @access private 
     909     */ 
     910    function _explodeQuotedString($delimiter, $string) 
     911    { 
     912        $result = array(); 
     913        $strlen = strlen($string); 
     914 
     915        for ($q=$p=$i=0; $i < $strlen; $i++) { 
     916            if ($string[$i] == "\"" 
     917                && (empty($string[$i-1]) || $string[$i-1] != "\\") 
     918            ) { 
     919                $q = $q ? false : true; 
     920            } else if (!$q && preg_match("/$delimiter/", $string[$i])) { 
     921                $result[] = substr($string, $p, $i - $p); 
     922                $p = $i + 1; 
     923            } 
     924        } 
     925 
     926        $result[] = substr($string, $p); 
     927        return $result; 
     928    } 
     929 
     930    /** 
     931     * Encodes a header value as per RFC2047 
     932     * 
     933     * @param string $value      The header data to encode 
     934     * @param string $charset    Character set name 
     935     * @param string $encoding   Encoding name (base64 or quoted-printable) 
     936     * @param int    $prefix_len Prefix length. Default: 0 
     937     * @param string $eol        End-of-line sequence. Default: "\r\n" 
     938     * 
     939     * @return string            Encoded header data 
     940     * @access public 
     941     * @since 1.6.1 
     942     */ 
     943    function encodeHeaderValue($value, $charset, $encoding, $prefix_len=0, $eol="\r\n") 
     944    { 
     945        // #17311: Use multibyte aware method (requires mbstring extension) 
     946        if ($result = Mail_mimePart::encodeMB($value, $charset, $encoding, $prefix_len, $eol)) { 
     947            return $result; 
     948        } 
     949 
     950        // Generate the header using the specified params and dynamicly 
     951        // determine the maximum length of such strings. 
     952        // 75 is the value specified in the RFC. 
     953        $encoding = $encoding == 'base64' ? 'B' : 'Q'; 
     954        $prefix = '=?' . $charset . '?' . $encoding .'?'; 
     955        $suffix = '?='; 
     956        $maxLength = 75 - strlen($prefix . $suffix); 
     957        $maxLength1stLine = $maxLength - $prefix_len; 
     958 
     959        if ($encoding == 'B') { 
     960            // Base64 encode the entire string 
     961            $value = base64_encode($value); 
     962 
     963            // We can cut base64 every 4 characters, so the real max 
     964            // we can get must be rounded down. 
     965            $maxLength = $maxLength - ($maxLength % 4); 
     966            $maxLength1stLine = $maxLength1stLine - ($maxLength1stLine % 4); 
     967 
     968            $cutpoint = $maxLength1stLine; 
     969            $output = ''; 
     970 
     971            while ($value) { 
     972                // Split translated string at every $maxLength 
     973                $part = substr($value, 0, $cutpoint); 
     974                $value = substr($value, $cutpoint); 
     975                $cutpoint = $maxLength; 
     976                // RFC 2047 specifies that any split header should 
     977                // be seperated by a CRLF SPACE. 
     978                if ($output) { 
     979                    $output .= $eol . ' '; 
     980                } 
     981                $output .= $prefix . $part . $suffix; 
     982            } 
     983            $value = $output; 
     984        } else { 
     985            // quoted-printable encoding has been selected 
     986            $value = Mail_mimePart::encodeQP($value); 
     987 
     988            // This regexp will break QP-encoded text at every $maxLength 
     989            // but will not break any encoded letters. 
     990            $reg1st = "|(.{0,$maxLength1stLine}[^\=][^\=])|"; 
     991            $reg2nd = "|(.{0,$maxLength}[^\=][^\=])|"; 
     992 
     993            if (strlen($value) > $maxLength1stLine) { 
     994                // Begin with the regexp for the first line. 
     995                $reg = $reg1st; 
     996                $output = ''; 
     997                while ($value) { 
     998                    // Split translated string at every $maxLength 
     999                    // But make sure not to break any translated chars. 
     1000                    $found = preg_match($reg, $value, $matches); 
     1001 
     1002                    // After this first line, we need to use a different 
     1003                    // regexp for the first line. 
     1004                    $reg = $reg2nd; 
     1005 
     1006                    // Save the found part and encapsulate it in the 
     1007                    // prefix & suffix. Then remove the part from the 
     1008                    // $value_out variable. 
     1009                    if ($found) { 
     1010                        $part = $matches[0]; 
     1011                        $len = strlen($matches[0]); 
     1012                        $value = substr($value, $len); 
     1013                    } else { 
     1014                        $part = $value; 
     1015                        $value = ''; 
     1016                    } 
     1017 
     1018                    // RFC 2047 specifies that any split header should 
     1019                    // be seperated by a CRLF SPACE 
     1020                    if ($output) { 
     1021                        $output .= $eol . ' '; 
     1022                    } 
     1023                    $output .= $prefix . $part . $suffix; 
     1024                } 
     1025                $value = $output; 
     1026            } else { 
     1027                $value = $prefix . $value . $suffix; 
     1028            } 
     1029        } 
     1030 
     1031        return $value; 
     1032    } 
     1033 
     1034    /** 
     1035     * Encodes the given string using quoted-printable 
     1036     * 
     1037     * @param string $str String to encode 
     1038     * 
     1039     * @return string     Encoded string 
     1040     * @access public 
     1041     * @since 1.6.0 
     1042     */ 
     1043    function encodeQP($str) 
     1044    { 
     1045        // Bug #17226 RFC 2047 restricts some characters 
     1046        // if the word is inside a phrase, permitted chars are only: 
     1047        // ASCII letters, decimal digits, "!", "*", "+", "-", "/", "=", and "_" 
     1048 
     1049        // "=",  "_",  "?" must be encoded 
     1050        $regexp = '/([\x22-\x29\x2C\x2E\x3A-\x40\x5B-\x60\x7B-\x7E\x80-\xFF])/'; 
     1051        $str = preg_replace_callback( 
     1052            $regexp, array('Mail_mimePart', '_qpReplaceCallback'), $str 
     1053        ); 
     1054 
     1055        return str_replace(' ', '_', $str); 
     1056    } 
     1057 
     1058    /** 
     1059     * Encodes the given string using base64 or quoted-printable. 
     1060     * This method makes sure that encoded-word represents an integral 
     1061     * number of characters as per RFC2047. 
     1062     * 
     1063     * @param string $str        String to encode 
     1064     * @param string $charset    Character set name 
     1065     * @param string $encoding   Encoding name (base64 or quoted-printable) 
     1066     * @param int    $prefix_len Prefix length. Default: 0 
     1067     * @param string $eol        End-of-line sequence. Default: "\r\n" 
     1068     * 
     1069     * @return string     Encoded string 
     1070     * @access public 
     1071     * @since 1.8.0 
     1072     */ 
     1073    function encodeMB($str, $charset, $encoding, $prefix_len=0, $eol="\r\n") 
     1074    { 
     1075        if (!function_exists('mb_substr') || !function_exists('mb_strlen')) { 
     1076            return; 
     1077        } 
     1078 
     1079        $encoding = $encoding == 'base64' ? 'B' : 'Q'; 
     1080        // 75 is the value specified in the RFC 
     1081        $prefix = '=?' . $charset . '?'.$encoding.'?'; 
     1082        $suffix = '?='; 
     1083        $maxLength = 75 - strlen($prefix . $suffix); 
     1084 
     1085        // A multi-octet character may not be split across adjacent encoded-words 
     1086        // So, we'll loop over each character 
     1087        // mb_stlen() with wrong charset will generate a warning here and return null 
     1088        $length      = mb_strlen($str, $charset); 
     1089        $result      = ''; 
     1090        $line_length = $prefix_len; 
     1091 
     1092        if ($encoding == 'B') { 
     1093            // base64 
     1094            $start = 0; 
     1095            $prev  = ''; 
     1096 
     1097            for ($i=1; $i<=$length; $i++) { 
     1098                // See #17311 
     1099                $chunk = mb_substr($str, $start, $i-$start, $charset); 
     1100                $chunk = base64_encode($chunk); 
     1101                $chunk_len = strlen($chunk); 
     1102 
     1103                if ($line_length + $chunk_len == $maxLength || $i == $length) { 
     1104                    if ($result) { 
     1105                        $result .= "\n"; 
     1106                    } 
     1107                    $result .= $chunk; 
     1108                    $line_length = 0; 
     1109                    $start = $i; 
     1110                } else if ($line_length + $chunk_len > $maxLength) { 
     1111                    if ($result) { 
     1112                        $result .= "\n"; 
     1113                    } 
     1114                    if ($prev) { 
     1115                        $result .= $prev; 
     1116                    } 
     1117                    $line_length = 0; 
     1118                    $start = $i - 1; 
     1119                } else { 
     1120                    $prev = $chunk; 
     1121                } 
     1122            } 
     1123        } else { 
     1124            // quoted-printable 
     1125            // see encodeQP() 
     1126            $regexp = '/([\x22-\x29\x2C\x2E\x3A-\x40\x5B-\x60\x7B-\x7E\x80-\xFF])/'; 
     1127 
     1128            for ($i=0; $i<=$length; $i++) { 
     1129                $char = mb_substr($str, $i, 1, $charset); 
     1130                // RFC recommends underline (instead of =20) in place of the space 
     1131                // that's one of the reasons why we're not using iconv_mime_encode() 
     1132                if ($char == ' ') { 
     1133                    $char = '_'; 
     1134                    $char_len = 1; 
     1135                } else { 
     1136                    $char = preg_replace_callback( 
     1137                        $regexp, array('Mail_mimePart', '_qpReplaceCallback'), $char 
     1138                    ); 
     1139                    $char_len = strlen($char); 
     1140                } 
     1141 
     1142                if ($line_length + $char_len > $maxLength) { 
     1143                    if ($result) { 
     1144                        $result .= "\n"; 
     1145                    } 
     1146                    $line_length = 0; 
     1147                } 
     1148 
     1149                $result      .= $char; 
     1150                $line_length += $char_len; 
     1151            } 
     1152        } 
     1153 
     1154        if ($result) { 
     1155            $result = $prefix 
     1156                .str_replace("\n", $suffix.$eol.' '.$prefix, $result).$suffix; 
     1157        } 
     1158 
     1159        return $result; 
     1160    } 
     1161 
     1162    /** 
     1163     * Callback function to replace extended characters (\x80-xFF) with their 
     1164     * ASCII values (RFC2047: quoted-printable) 
     1165     * 
     1166     * @param array $matches Preg_replace's matches array 
     1167     * 
     1168     * @return string        Encoded character string 
     1169     * @access private 
     1170     */ 
     1171    function _qpReplaceCallback($matches) 
     1172    { 
     1173        return sprintf('=%02X', ord($matches[1])); 
     1174    } 
     1175 
     1176    /** 
     1177     * Callback function to replace extended characters (\x80-xFF) with their 
     1178     * ASCII values (RFC2231) 
     1179     * 
     1180     * @param array $matches Preg_replace's matches array 
     1181     * 
     1182     * @return string        Encoded character string 
     1183     * @access private 
     1184     */ 
     1185    function _encodeReplaceCallback($matches) 
     1186    { 
     1187        return sprintf('%%%02X', ord($matches[1])); 
     1188    } 
     1189 
    3501190} // End of class 
    351 ?> 
Note: See TracChangeset for help on using the changeset viewer.