Subversion Repositories Applications.papyrus

Rev

Rev 1700 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
929 alexandre_ 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: mime.php,v 1.1 2006-09-13 08:49:41 alexandre_tb Exp $
39
 
40
require_once('PEAR.php');
41
require_once('Mail/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
 */
59
class 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
?>