source: branches/comu-ver2/data/module/Mail/mime.php @ 15532

Revision 15532, 25.2 KB checked in by nanasess, 17 years ago (diff)

svn:mime-type 修正

  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
1<?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
40require_once(dirname(__FILE__) . '/../PEAR.php');
41require_once(dirname(__FILE__) . '/mimePart.php');
42
43/**
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/
48 *
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>
53 *
54 * @author   Richard Heyes <richard.heyes@heyes-computing.net>
55 * @author   Tomas V.V.Cox <cox@idecnet.com>
56 * @package  Mail
57 * @access   public
58 */
59class Mail_mime
60{
61    /**
62     * Contains the plain text part of the email
63     * @var string
64     */
65    var $_txtbody;
66    /**
67     * Contains the html part of the email
68     * @var string
69     */
70    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;
81    /**
82     * list of the attached images
83     * @var array
84     */
85    var $_html_images = array();
86    /**
87     * list of the attachements
88     * @var array
89     */
90    var $_parts = array();
91    /**
92     * Build parameters
93     * @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
107
108    /**
109     * Constructor function
110     *
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);
134    }
135
136    /**
137     * Accessor function to set the body text. Body text is used if
138     * it's not an html mail being sent or else is used to fill the
139     * text/plain part that emails clients who don't support
140     * html should show.
141     *
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
150     * @access public
151     */
152    function setTXTBody($data, $isfile = false, $append = false)
153    {
154        if (!$isfile) {
155            if (!$append) {
156                $this->_txtbody = $data;
157            } else {
158                $this->_txtbody .= $data;
159            }
160        } else {
161            $cont = $this->_file2str($data);
162            if (PEAR::isError($cont)) {
163                return $cont;
164            }
165            if (!$append) {
166                $this->_txtbody = $cont;
167            } else {
168                $this->_txtbody .= $cont;
169            }
170        }
171        return true;
172    }
173
174    /**
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
182     * @access public
183     */
184    function setHTMLBody($data, $isfile = false)
185    {
186        if (!$isfile) {
187            $this->_htmlbody = $data;
188        } else {
189            $cont = $this->_file2str($data);
190            if (PEAR::isError($cont)) {
191                return $cont;
192            }
193            $this->_htmlbody = $cont;
194        }
195
196        return true;
197    }
198
199    /**
200     * Adds an image to the list of embedded images.
201     *
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        }
224        $this->_html_images[] = array(
225                                      'body'   => $filedata,
226                                      'name'   => $filename,
227                                      'c_type' => $c_type,
228                                      'cid'    => md5(uniqid(time()))
229                                     );
230        return true;
231    }
232
233    /**
234     * Adds a file to the list of attachments.
235     *
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) {
253            // Force the name the user supplied, otherwise use $file
254            $filename = (!empty($name)) ? $name : $file;
255        } else {
256            $filename = $name;
257        }
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        }
267
268        $this->_parts[] = array(
269                                'body'     => $filedata,
270                                'name'     => $filename,
271                                'c_type'   => $c_type,
272                                'encoding' => $encoding
273                               );
274        return true;
275    }
276
277    /**
278     * Get the contents of the given file name as string
279     *
280     * @param  string  $file_name  path of file to process
281     * @return string  contents of $file_name
282     * @access private
283     */
284    function &_file2str($file_name)
285    {
286        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);
299        return $cont;
300    }
301
302    /**
303     * Adds a text subpart to the mimePart object and
304     * returns it during the build process.
305     *
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
310     * @access private
311     */
312    function &_addTextPart(&$obj, $text)
313    {
314        $params['content_type'] = 'text/plain';
315        $params['encoding']     = $this->_build_params['text_encoding'];
316        $params['charset']      = $this->_build_params['text_charset'];
317        if (is_object($obj)) {
318            return $obj->addSubpart($text, $params);
319        } else {
320            return new Mail_mimePart($text, $params);
321        }
322    }
323
324    /**
325     * Adds a html subpart to the mimePart object and
326     * returns it during the build process.
327     *
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
331     * @access private
332     */
333    function &_addHtmlPart(&$obj)
334    {
335        $params['content_type'] = 'text/html';
336        $params['encoding']     = $this->_build_params['html_encoding'];
337        $params['charset']      = $this->_build_params['html_charset'];
338        if (is_object($obj)) {
339            return $obj->addSubpart($this->_htmlbody, $params);
340        } else {
341            return new Mail_mimePart($this->_htmlbody, $params);
342        }
343    }
344
345    /**
346     * Creates a new mimePart object, using multipart/mixed as
347     * the initial content-type and returns it during the
348     * build process.
349     *
350     * @return object  The multipart/mixed mimePart object
351     * @access private
352     */
353    function &_addMixedPart()
354    {
355        $params['content_type'] = 'multipart/mixed';
356        return new Mail_mimePart('', $params);
357    }
358
359    /**
360     * Adds a multipart/alternative part to a mimePart
361     * object (or creates one), and returns it during
362     * the build process.
363     *
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
367     * @access private
368     */
369    function &_addAlternativePart(&$obj)
370    {
371        $params['content_type'] = 'multipart/alternative';
372        if (is_object($obj)) {
373            return $obj->addSubpart('', $params);
374        } else {
375            return new Mail_mimePart('', $params);
376        }
377    }
378
379    /**
380     * Adds a multipart/related part to a mimePart
381     * object (or creates one), and returns it during
382     * the build process.
383     *
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
387     * @access private
388     */
389    function &_addRelatedPart(&$obj)
390    {
391        $params['content_type'] = 'multipart/related';
392        if (is_object($obj)) {
393            return $obj->addSubpart('', $params);
394        } else {
395            return new Mail_mimePart('', $params);
396        }
397    }
398
399    /**
400     * Adds an html image subpart to a mimePart object
401     * and returns it during the build process.
402     *
403     * @param  object  The mimePart to add the image to
404     * @param  array   The image information
405     * @return object  The image mimePart object
406     * @access private
407     */
408    function &_addHtmlImagePart(&$obj, $value)
409    {
410        $params['content_type'] = $value['c_type'];
411        $params['encoding']     = 'base64';
412        $params['disposition']  = 'inline';
413        $params['dfilename']    = $value['name'];
414        $params['cid']          = $value['cid'];
415        $obj->addSubpart($value['body'], $params);
416    }
417
418    /**
419     * Adds an attachment subpart to a mimePart object
420     * and returns it during the build process.
421     *
422     * @param  object  The mimePart to add the image to
423     * @param  array   The attachment information
424     * @return object  The image mimePart object
425     * @access private
426     */
427    function &_addAttachmentPart(&$obj, $value)
428    {
429        $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);
434    }
435
436    /**
437     * Builds the multipart message from the list ($this->_parts) and
438     * returns the mime content.
439     *
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)) {
462                $this->_build_params[$key] = $value;
463            }
464        }
465
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        }
476
477        $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;
482
483        switch (true) {
484        case $text AND !$attachments:
485            $message =& $this->_addTextPart($null, $this->_txtbody);
486            break;
487
488        case !$text AND !$html AND $attachments:
489            $message =& $this->_addMixedPart();
490            for ($i = 0; $i < count($this->_parts); $i++) {
491                $this->_addAttachmentPart($message, $this->_parts[$i]);
492            }
493            break;
494
495        case $text AND $attachments:
496            $message =& $this->_addMixedPart();
497            $this->_addTextPart($message, $this->_txtbody);
498            for ($i = 0; $i < count($this->_parts); $i++) {
499                $this->_addAttachmentPart($message, $this->_parts[$i]);
500            }
501            break;
502
503        case $html AND !$attachments AND !$html_images:
504            if (isset($this->_txtbody)) {
505                $message =& $this->_addAlternativePart($null);
506                $this->_addTextPart($message, $this->_txtbody);
507                $this->_addHtmlPart($message);
508            } else {
509                $message =& $this->_addHtmlPart($null);
510            }
511            break;
512
513        case $html AND !$attachments AND $html_images:
514            if (isset($this->_txtbody)) {
515                $message =& $this->_addAlternativePart($null);
516                $this->_addTextPart($message, $this->_txtbody);
517                $related =& $this->_addRelatedPart($message);
518            } else {
519                $message =& $this->_addRelatedPart($null);
520                $related =& $message;
521            }
522            $this->_addHtmlPart($related);
523            for ($i = 0; $i < count($this->_html_images); $i++) {
524                $this->_addHtmlImagePart($related, $this->_html_images[$i]);
525            }
526            break;
527
528        case $html AND $attachments AND !$html_images:
529            $message =& $this->_addMixedPart();
530            if (isset($this->_txtbody)) {
531                $alt =& $this->_addAlternativePart($message);
532                $this->_addTextPart($alt, $this->_txtbody);
533                $this->_addHtmlPart($alt);
534            } else {
535                $this->_addHtmlPart($message);
536            }
537            for ($i = 0; $i < count($this->_parts); $i++) {
538                $this->_addAttachmentPart($message, $this->_parts[$i]);
539            }
540            break;
541
542        case $html AND $attachments AND $html_images:
543            $message =& $this->_addMixedPart();
544            if (isset($this->_txtbody)) {
545                $alt =& $this->_addAlternativePart($message);
546                $this->_addTextPart($alt, $this->_txtbody);
547                $rel =& $this->_addRelatedPart($alt);
548            } else {
549                $rel =& $this->_addRelatedPart($message);
550            }
551            $this->_addHtmlPart($rel);
552            for ($i = 0; $i < count($this->_html_images); $i++) {
553                $this->_addHtmlImagePart($rel, $this->_html_images[$i]);
554            }
555            for ($i = 0; $i < count($this->_parts); $i++) {
556                $this->_addAttachmentPart($message, $this->_parts[$i]);
557            }
558            break;
559
560        }
561
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;
570        }
571    }
572
573    /**
574     * Returns an array with the headers needed to prepend to the email
575     * (MIME-Version and Content-Type). Format of argument is:
576     * $array['header-name'] = 'header-value';
577     *
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
587        $headers['MIME-Version'] = '1.0';
588        if (isset($xtra_headers)) {
589            $headers = array_merge($headers, $xtra_headers);
590        }
591        $this->_headers = array_merge($headers, $this->_headers);
592
593        return $this->_encodeHeaders($this->_headers);
594    }
595
596    /**
597     * Get the text version of the headers
598     * (usefull if you want to use the PHP mail() function)
599     *
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);
608        $ret = '';
609        foreach ($headers as $key => $val) {
610            $ret .= "$key: $val" . MAIL_MIME_CRLF;
611        }
612        return $ret;
613    }
614
615    /**
616     * Sets the Subject header
617     *
618     * @param  string $subject String to set the subject to
619     * access  public
620     */
621    function setSubject($subject)
622    {
623        $this->_headers['Subject'] = $subject;
624    }
625
626    /**
627     * Set an email to the From (the sender) header
628     *
629     * @param  string $email The email direction to add
630     * @access public
631     */
632    function setFrom($email)
633    {
634        $this->_headers['From'] = $email;
635    }
636
637    /**
638     * Add an email to the Cc (carbon copy) header
639     * (multiple calls to this method are allowed)
640     *
641     * @param  string $email The email direction to add
642     * @access public
643     */
644    function addCc($email)
645    {
646        if (isset($this->_headers['Cc'])) {
647            $this->_headers['Cc'] .= ", $email";
648        } else {
649            $this->_headers['Cc'] = $email;
650        }
651    }
652
653    /**
654     * Add an email to the Bcc (blank carbon copy) header
655     * (multiple calls to this method are allowed)
656     *
657     * @param  string $email The email direction to add
658     * @access public
659     */
660    function addBcc($email)
661    {
662        if (isset($this->_headers['Bcc'])) {
663            $this->_headers['Bcc'] .= ", $email";
664        } else {
665            $this->_headers['Bcc'] = $email;
666        }
667    }
668
669    /**
670     * Encodes a header as per RFC2047
671     *
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   
711
712} // End of class
713?>
Note: See TracBrowser for help on using the repository browser.