yllar
2006-12-16 77c28206a14b5bee3f3091f10cffd531bce5649c
commit | author | age
4e17e6 1 <?php
T 2 // +-----------------------------------------------------------------------+
3 // | Copyright (c) 2002-2003  Richard Heyes                                     |
15fee7 4 // | All rights reserved.                                                  |
4e17e6 5 // |                                                                       |
T 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
35 /**
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$
91 * @package Mail
92 */
93
94 class Mail_mimePart {
95
96    /**
97     * The encoding type of this part
98     * @var string
99     */
100     var $_encoding;
101
102    /**
103     * An array of subparts
104     * @var array
105     */
106     var $_subparts;
107
108    /**
109     * The output of this part after being built
110     * @var string
111     */
112     var $_encoded;
113
114    /**
115     * Headers for this part
116     * @var array
117     */
118     var $_headers;
119
120    /**
121     * The body of this part (not encoded)
122     * @var string
123     */
124     var $_body;
125
126     /**
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      */
142     function Mail_mimePart($body = '', $params = array())
143     {
144         if (!defined('MAIL_MIMEPART_CRLF')) {
145             define('MAIL_MIMEPART_CRLF', defined('MAIL_MIME_CRLF') ? MAIL_MIME_CRLF : "\r\n", TRUE);
146         }
147
148         foreach ($params as $key => $value) {
149             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;
186             }
187         }
188
189         // Default content-type
190         if (!isset($headers['Content-Type'])) {
191             $headers['Content-Type'] = 'text/plain';
192         }
193
194         //Default encoding
195         if (!isset($this->_encoding)) {
196             $this->_encoding = '7bit';
197         }
198
199         // Assign stuff to member variables
200         $this->_encoded  = array();
201         $this->_headers  = $headers;
202         $this->_body     = $body;
203     }
204
205     /**
206      * encode()
207      *
208      * Encodes and returns the email. Also stores
209      * it in the encoded member variable
210      *
211      * @return An associative array containing two elements,
212      *         body and headers. The headers element is itself
213      *         an indexed array.
214      * @access public
215      */
216     function encode()
217     {
218         $encoded =& $this->_encoded;
219
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
226             for ($i = 0; $i < count($this->_subparts); $i++) {
227                 $headers = array();
228                 $tmp = $this->_subparts[$i]->encode();
229                 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
239         } else {
240             $encoded['body'] = $this->_getEncodedData($this->_body, $this->_encoding) . MAIL_MIMEPART_CRLF;
241         }
242
243         // Add headers to $encoded
244         $encoded['headers'] =& $this->_headers;
245
246         return $encoded;
247     }
248
249     /**
250      * &addSubPart()
251      *
252      * Adds a subpart to current mime part and returns
253      * a reference to it
254      *
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.
262      * @access public
263      */
264     function &addSubPart($body, $params)
265     {
266         $this->_subparts[] = new Mail_mimePart($body, $params);
267         return $this->_subparts[count($this->_subparts) - 1];
268     }
269
270     /**
271      * _getEncodedData()
272      *
273      * Returns encoded data based upon encoding passed to it
274      *
275      * @param $data     The data to encode.
276      * @param $encoding The encoding type to use, 7bit, base64,
277      *                  or quoted-printable.
278      * @access private
279      */
280     function _getEncodedData($data, $encoding)
281     {
282         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      *
304      * Encodes data to quoted-printable standard.
305      *
306      * @param $input    The data to encode
307      * @param $line_max Optional max line length. Should
308      *                  not be more than 76 chars
309      *
310      * @access private
311      */
312     function _quotedPrintableEncode($input , $line_max = 76)
313     {
314         $lines  = preg_split("/\r?\n/", $input);
315         $eol    = MAIL_MIMEPART_CRLF;
316         $escape = '=';
317         $output = '';
318
319         while(list(, $line) = each($lines)){
320
321             $linlen     = strlen($line);
322             $newline = '';
323
324             for ($i = 0; $i < $linlen; $i++) {
325                 $char = substr($line, $i, 1);
326                 $dec  = ord($char);
327
328                 if (($dec == 32) AND ($i == ($linlen - 1))){    // convert space at eol only
329                     $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
341                     $newline  = '';
342                 }
343                 $newline .= $char;
344             } // end of for
345             $output .= $newline . $eol;
346         }
347         $output = substr($output, 0, -1 * strlen($eol)); // Don't want last crlf
348         return $output;
349     }
350 } // End of class
351 ?>