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
File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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.