Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
4
//\\\       \\\\\\\\|                                                           \\
5
//\\\ @@    @@\\\\\\| Mail_IMAPv2                                               \\
6
//\\ @@@@  @@@@\\\\\|___________________________________________________________\\
7
//\\\@@@@| @@@@\\\\\|                                                           \\
8
//\\\ @@ |\\@@\\\\\\|(c) Copyright 2004-2005 Richard York, All rights Reserved  \\
9
//\\\\  ||   \\\\\\\|___________________________________________________________\\
10
//\\\\  \\_   \\\\\\|Redistribution and use in source and binary forms, with or \\
11
//\\\\\        \\\\\|without modification, are permitted provided that the      \\
12
//\\\\\  ----  \@@@@|following conditions are met:                              \\
13
//@@@@@\       \@@@@|                                                           \\
14
//@@@@@@\     \@@@@@| o Redistributions of source code must retain the above    \\
15
//\\\\\\\\\\\\\\\\\\|   copyright notice, this list of conditions and the       \\
16
//                      following disclaimer.                                   \\
17
//  o Redistributions in binary form must reproduce the above copyright notice, \\
18
//    this list of conditions and the following disclaimer in the documentation \\
19
//    and/or other materials provided with the distribution.                    \\
20
//  o The names of the authors may not be used to endorse or promote products   \\
21
//    derived from this software without specific prior written permission.     \\
22
//                                                                              \\
23
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" \\
24
//  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE   \\
25
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  \\
26
//  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE    \\
27
//  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR      \\
28
//  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF        \\
29
//  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS    \\
30
//  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN     \\
31
//  CONTRACT, STRICT LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE)    \\
32
//  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  \\
33
//  POSSIBILITY OF SUCH DAMAGE.                                                 \\
34
//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
35
 
36
require_once 'PEAR/ErrorStack.php';
37
 
38
define('Mail_IMAPv2_BODY',                                0);
39
define('Mail_IMAPv2_LITERAL',                             1);
40
define('Mail_IMAPv2_LITERAL_DECODE',                      2);
41
 
42
define('Mail_IMAPv2_ERROR',                               1);
43
define('Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY',       2);
44
define('Mail_IMAPv2_ERROR_INVALID_OPTION',                3);
45
define('Mail_IMAPv2_ERROR_INVALID_PID',                   4);
46
define('Mail_IMAPv2_ERROR_INVALID_ACTION',                5);
47
 
48
define('Mail_IMAPv2_NOTICE',                              100);
49
define('Mail_IMAPv2_NOTICE_FALLBACK_PID',                 102);
50
 
51
define('Mail_IMAPv2_FATAL',                               200);
52
 
53
/**
54
* Mail_IMAPv2 provides a flexible API for connecting to and retrieving
55
* mail from mailboxes using the IMAP, POP3 or NNTP mail protocols.
56
* Connection to a mailbox is acheived through the c-client extension
57
* to PHP (http://www.php.net/imap). Meaning installation of the
58
* c-client extension is required to use Mail_IMAPv2.
59
*
60
* Mail_IMAPv2 can be used to retrieve the contents of a mailbox,
61
* whereas it may serve as the backend for a webmail application or
62
* mailing list manager.
63
*
64
* Since Mail_IMAPv2 is an abstracted object, it allows for complete
65
* customization of the UI for any application.
66
*
67
* By default Mail_IMAPv2 parses and retrieves information about
68
* multipart message in a threaded fashion similar to MS Outlook, e.g.
69
* only top level attachments are retrieved initially, then when message
70
* part id and message id are passed to Mail_IMAPv2, it retrieves
71
* attachments and information relevant to that message part.
72
* {@link getParts} can be supplied an argument to turn off threading,
73
* whereas all parts are retrieved at once.
74
*
75
* Mail_IMAPv2 also, by default retrieves the alternative message parts
76
* of multipart messages. This is most useful for debugging
77
* applications that send out multipart mailers where both a text/html
78
* and alterntaive text/plain part are included. This can also be
79
* turned off by supplying an additional argument to {@link getParts}.
80
*
81
* Mail_IMAPv2 always searches for a text/html part to display as the primary
82
* part. This can be reversed so that it always looks for a text/plain part
83
* initially by supplying the necessary arguments to {@link getParts},
84
* and {@link getBody}.
85
*
86
* PLEASE REPORT BUGS FOLLOWING THE GUIDELINES AT:
87
*   http://www.smilingsouls.net/Mail_IMAP
88
*
89
* @author       Richard York <rich_y@php.net>
90
* @category     Mail
91
* @package      Mail_IMAPv2
92
* @license      BSD
93
* @version      0.2.0
94
* @copyright    (c) Copyright 2004, Richard York, All Rights Reserved.
95
* @since        PHP 4.2.0
96
* @since        C-Client 2001
97
* @tutorial     http://www.smilingsouls.net/Mail_IMAP
98
*
99
* @example      docs/examples/IMAP.inbox.php
100
*   Mail_IMAPv2 Inbox
101
*
102
* @example      docs/examples/IMAP.message_viewer.php
103
*   Mail_IMAPv2 Message
104
*
105
* @example      docs/examples/IMAP.part_viewer.php
106
*   Mail_IMAPv2 Message
107
*
108
* @example      docs/examples/IMAP.connection_wizard.php
109
*   Mail_IMAPv2 Connection Wizard
110
*
111
* @example      docs/examples/IMAP.connection_wizard_example.php
112
*   Mail_IMAPv2 Connection Wizard
113
*/
114
class Mail_IMAPv2 {
115
 
116
    /**
117
    * Contains an instance of the PEAR_ErrorStack object.
118
    * @var      object $error
119
    * @access   public
120
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/error
121
    */
122
    var $error;
123
 
124
    /**
125
    * Contains the imap resource stream.
126
    * @var     resource $mailbox
127
    * @access  public
128
    * @see     Mail_IMAPv2
129
    * @see     open
130
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/mailbox
131
    */
132
    var $mailbox;
133
 
134
    /**
135
     * Contains information about the current mailbox.
136
     * @var     array $mailboxInfo
137
     * @access  public
138
     * @see     connect
139
     * @see     getMailboxInfo
140
     * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/mailboxInfo
141
     */
142
    var $mailboxInfo = array();
143
 
144
    /**
145
     * Set flags for various imap_* functions.
146
     *
147
     * Use associative indices to indicate the imap_* function to set flags for,
148
     *  create the indice omitting the 'imap_' portion of the function name.
149
     *  see: {@link setOptions} for more information.
150
     *
151
     * @var     array $option
152
     * @access  public
153
     * @see     setOptions
154
     * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/setOptions
155
     */
156
    var $option = array();
157
 
158
    /**
159
     * Contains various information returned by {@link imap_fetchstructure}.
160
     * The object returned by imap_fetchstructure stored in $this->structure[$mid]['obj'].
161
     *
162
     * @var     array $_structure
163
     * @access  public
164
     * @see     _declareParts
165
     * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/structure
166
     */
167
    var $structure = array();
168
 
169
    /**
170
     * Contains various information about a message, separates inline parts from
171
     * attachments and contains the default part id for each message.
172
     *
173
     * @var     array $msg
174
     * @access  public
175
     * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/msg
176
     */
177
    var $msg = array();
178
 
179
    /**
180
     * (array)(mixed) Associative array containing information
181
     * gathered by {@link imap_headerinfo} or
182
     * {@link imap_rfc822_parse_headers}.
183
     *
184
     * @var    header array $header
185
     * @see     getHeaders
186
     * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/header
187
     */
188
 
189
    var $header = array();
190
 
191
    /**
192
     * (string) contains the various possible data types.
193
     * @var     array $_dataTypes
194
     * @access  private
195
     * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_dataTypes
196
     */
197
    var $_dataTypes = array(
198
    	'text',
199
        'multipart',
200
        'message',
201
        'application',
202
        'audio',
203
        'image',
204
        'video',
205
        'other'
206
    );
207
 
208
    /**
209
     * (string) Contains the various possible encoding types.
210
     * @var     array $_encodingTypes
211
     * @access  private
212
     * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_encodingTypes
213
     */
214
    var $_encodingTypes = array(
215
    	'7bit',
216
        '8bit',
217
        'binary',
218
        'base64',
219
        'quoted-printable',
220
        'other'
221
    );
222
 
223
    /**
224
    * (string) Contains the fields searched for and added to inline and attachment part
225
    * arrays. These are the 'in' and 'at' associative indices of the $msg member variable.
226
    * @var    array $fields
227
    * @access public
228
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/fields
229
    */
230
    var $fields = array(
231
    	'fname',
232
        'pid',
233
        'ftype',
234
        'fsize',
235
        'has_at',
236
        'charset',
237
        'cid'
238
    );
239
 
240
    /**
241
    * Constructor. Optionally set the IMAP resource stream.
242
    *
243
    * If IMAP connection arguments are not supplied, returns null.  Accepts a URI
244
    * abstraction of the standard imap_open connection argument (see {@link connect})
245
    * or the imap resource indicator.
246
    *
247
    * If the optional flags argument of imap_open needs to be set, then {@link connect}
248
    * should be called after either setting the {@link $option} member variable or
249
    * calling {@link setOptions}.
250
    *
251
    * Since Mail_IMAPv2 0.1.0 creates an instance of PEAR_ErrorStack.
252
    *  $options argument became $get_info argument see {@link connect}.
253
    *
254
    * @param     string         $connection  (optional) server URI | imap resource identifier
255
    * @param     int            $action
256
    *
257
    * @tutorial  http://www.smilingsouls.net/?content=Mail_IMAP/Mail_IMAP
258
    * @access    public
259
    * @return    BOOL|null|PEAR_Error
260
    * @see       connect
261
    * @see       imap_open
262
    */
263
    function Mail_IMAPv2($connection = null, $get_info = true)
264
    {
265
        $this->error = new PEAR_ErrorStack('Mail_IMAPv2');
266
 
267
        if (!empty($connection) && is_resource($connection)) {
268
            if (get_resource_type($connection) == 'imap') {
269
                $this->mailbox = $connection;
270
            } else {
271
                $this->error->push(
272
                	Mail_IMAPv2_ERROR,
273
                	'error',
274
                	null,
275
                	'Invalid imap resource passed to constructor.'
276
                );
277
            }
278
        } else if (!empty($connection)) {
279
            $this->connect($connection, $get_info);
280
        }
281
    }
282
 
283
    /**
284
    * @todo Finish writing this method, and test it.
285
    */
286
    function errorTemplate()
287
    {
288
        return array(
289
            // Generic Error
290
            Mail_IMAPv2_ERROR                           => '%message%',
291
            Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY   => 'Argument \'%arg%\' must be an array.',
292
            Mail_IMAPv2_ERROR_INVALID_OPTION            => 'Indice \'%indice%\' for argument \'%arg%\' is not a valid option.',
293
            Mail_IMAPv2_ERROR_INVALID_PID               => 'Supplied part id \'%pid%\' is not valid.',
294
            Mail_IMAPv2_ERROR_INVALID_ACTION            => 'Action \'%action%\' is not a valid action for the \'%arg%\' argument.',
295
            Mail_IMAPv2_NOTICE_FALLBACK_PID             => 'Fallback PID used. A fallback PID is used in the event that Mail_IMAPv2 is not able to find a valid text/plain or text/html message part. The MIME type for the fallback pid is %ftype%'
296
        );
297
    }
298
 
299
    /**
300
    * Wrapper method for {@link imap_open}.  Accepts a URI abstraction in
301
    * the following format: imap://user:pass@mail.example.com:143/INBOX#notls
302
    * instead of the standard connection arguments used in imap_open.
303
    * Replace the protocol with one of pop3|pop3s imap|imaps nntp|nntps.
304
    * Place intial folder in the file path portion, and optionally append
305
    * tls|notls|novalidate-cert in the anchor portion of the URL.  A port
306
    * number is optional, however, leaving it off could lead to a serious
307
    * degradation in preformance.
308
    *
309
    * Since Mail_IMAPv2 0.1.0 the $options argument became the $get_info argument.
310
    * constants for action were removed and the argument is now a BOOL toggle.
311
    *
312
    * @param    string           $uri   server URI
313
    * @param    bool             $get_info
314
    *   (optional) true by default. If true, make a call to {@link getMailboxInfo}
315
    *   if false do not call {@link getMailboxInfo}
316
    * @return   BOOL
317
    * @tutorial http://www.smilingsouls.net/index.php?content=Mail_IMAP/connect
318
    * @access   public
319
    * @see      imap_open
320
    */
321
    function connect($uri, $get_info = true)
322
    {
323
        if (!class_exists('Net_URL') && !@include_once('Net/URL.php')) {
324
			$this->error->push(Mail_IMAPv2_ERROR, 'error', null, 'Inclusion of Net_URL not successful.');
325
            return false;
326
        }
327
 
328
        $opt = (isset($this->option['open']))? $this->option['open'] : null;
329
 
330
        $net_url =& new Net_URL($uri);
331
 
332
        $uri  = '{'.$net_url->host;
333
 
334
        if (!empty($net_url->port)) {
335
            $uri .= ':'.$net_url->port;
336
        }
337
 
338
        $secure   = ('tls' == substr($net_url->anchor, 0, 3))? '' : '/ssl';
339
 
340
        $uri .= ('s' == (substr($net_url->protocol, -1)))? '/'.substr($net_url->protocol, 0, 4).$secure : '/'.$net_url->protocol;
341
 
342
        if (!empty($net_url->anchor)) {
343
            $uri .= '/'.$net_url->anchor;
344
        }
345
 
346
        $uri .= '}';
347
 
348
        $this->mailboxInfo['Mail_IMAPv2']['version'] = 'Mail_IMAPv2 0.2.0 Beta';
349
        $this->mailboxInfo['host'] = $uri;
350
 
351
        // Trim off the leading slash '/'
352
        if (!empty($net_url->path)) {
353
            $this->mailboxInfo['folder'] = substr($net_url->path, 1, (strlen($net_url->path) - 1));
354
            $uri .= $this->mailboxInfo['folder'];
355
        }
356
 
357
        $this->mailboxInfo['user'] = urldecode($net_url->user);
358
 
359
        if (false === ($this->mailbox = @imap_open($uri, urldecode($net_url->user), $net_url->pass, $opt))) {
360
            $this->error->push(
361
            	Mail_IMAPv2_ERROR,
362
            	'error',
363
            	null,
364
            	'Unable to build a connection to the specified mail server.'
365
            );
366
            $ret = false;
367
        } else {
368
            $ret = true;
369
        }
370
 
371
        // get mailbox info
372
        if ($get_info) {
373
            $this->getMailboxInfo(false);
374
        }
375
 
376
        return $ret;
377
    }
378
 
379
    /*
380
    * Adds to the {@link $mailboxInfo} member variable information about the current
381
    * mailbox from {@link imap_mailboxmsginfo}.
382
    *
383
    * Note: This method is automatically called on by default by {@link connect}.
384
    *
385
    * @param    string           $connect   server URL
386
    * @param    bool             $get_info
387
    *   (optional) true by default. If true, make a call to {@link getMailboxInfo}
388
    *   if false do not call {@link getMailboxInfo}
389
    *
390
    * @return   VOID|Array
391
    * @access   public
392
    * @see      imap_open
393
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getMailboxInfo
394
    */
395
    function getMailboxInfo($ret = true)
396
    {
397
        // It's possible that this function has already been called by $this->connect
398
        // If so, the 'Mailbox' indice will already exist and the user just wants
399
        // the contents of the mailboxInfo member variable.
400
        if (!isset($this->mailboxInfo['Mailbox'])) {
401
            $this->mailboxInfo = @array_merge(
402
            	$this->mailboxInfo,
403
            	get_object_vars(
404
            		imap_mailboxmsginfo($this->mailbox)
405
            	)
406
            );
407
        }
408
 
409
        return ($ret)? $this->mailboxInfo : true;
410
    }
411
 
412
    /**
413
    * Set the $option member variable, which is used to specify optional imap_* function
414
    * arguments (labeled in the manual as flags or options e.g. FT_UID, OP_READONLY, etc).
415
    *
416
    * <b>Example:</b>
417
    * <code>
418
    *    $msg->setOptions(array('body', 'fetchbody', 'fetchheader'), 'FT_UID');
419
    * </code>
420
    *
421
    * This results in imap_body, imap_fetchbody and imap_fetchheader being passed the FT_UID
422
    * option in the flags/options argument where ever these are called on by Mail_IMAPv2.
423
    *
424
    * Note: this method only sets optional imap_* arguments labeled as flags/options.
425
    *
426
    * @param    array          $options - function names to pass the arugument to
427
    * @param    string         $constant   - constant name to pass.
428
    * @return   PEAR_Error|true
429
    * @access   public
430
    * @see      $option
431
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/setOptions
432
    */
433
    function setOptions($options, $constant)
434
    {
435
        if (is_array($options) && !empty($options)) {
436
            foreach ($options as $value) {
437
                if (!$this->option[$value] = @constant($constant)) {
438
                    $this->error->push(
439
                    	Mail_IMAPv2_ERROR,
440
                    	'error',
441
                    	null,
442
                    	'The constant: '.$constant.' is not defined!'
443
                    );
444
                }
445
            }
446
        } else {
447
            $this->error->push(
448
            	Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY,
449
            	'error',
450
            	array('arg' => '$options')
451
            );
452
            return false;
453
        }
454
        return true;
455
    }
456
 
457
    /**
458
    * Wrapper method for {@link imap_close}.  Close the IMAP resource stream.
459
    *
460
    * @return   BOOL
461
    * @access   public
462
    * @tutorial http://www.smilingsouls.net/index.php?content=Mail_IMAP/close
463
    * @see      imap_close
464
    */
465
    function close()
466
    {
467
        $opt = (isset($this->option['close']))? $this->option['close'] : null;
468
        return @imap_close($this->mailbox, $opt);
469
    }
470
 
471
    /**
472
    * Wrapper method for {@link imap_num_msg}.
473
    *
474
    * @return   int mailbox message count
475
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/messageCount
476
    * @access   public
477
    * @see      imap_num_msg
478
    */
479
    function messageCount()
480
    {
481
        return @imap_num_msg($this->mailbox);
482
    }
483
 
484
    /**
485
    * Gather message information returned by {@link imap_fetchstructure} and recursively iterate
486
    * through each parts array.  Concatenate part numbers in the following format `1.1`
487
    * each part id is separated by a period, each referring to a part or subpart of a
488
    * multipart message.  Create part numbers as such that they are compatible with
489
    * {@link imap_fetchbody}.
490
    *
491
    * @param    int           &$mid         message id
492
    * @param    array         $sub_part     recursive
493
    * @param    string        $sub_pid      recursive parent part id
494
    * @param    int           $n            recursive counter
495
    * @param    bool          $is_sub_part  recursive
496
    * @param    bool          $skip_part    recursive
497
    * @return   mixed
498
    * @access   protected
499
    * @see      imap_fetchstructure
500
    * @see      imap_fetchbody
501
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_declareParts
502
    */
503
    function _declareParts(&$mid, $sub_part = null, $sub_pid = null, $n = 0, $is_sub_part = false, $skip_part = false, $last_was_signed = false)
504
    {
505
        if (!is_array($sub_part)) {
506
            $opt = (isset($this->option['fetchstructure']))? $this->option['fetchstructure'] : null;
507
            $this->structure[$mid]['obj'] = @imap_fetchstructure($this->mailbox, $mid, $opt);
508
        }
509
 
510
        if (isset($this->structure[$mid]['obj']->parts) || is_array($sub_part)) {
511
            if (!$is_sub_part) {
512
                $parts = $this->structure[$mid]['obj']->parts;
513
            } else {
514
                $parts = $sub_part;
515
                $n++;
516
            }
517
 
518
            for ($p = 0, $i = 1; $p < count($parts); $n++, $p++, $i++) {
519
                // Skip the following...
520
                // multipart/mixed!
521
                // subsequent multipart/alternative if this part is message/rfc822
522
                // multipart/related
523
                //
524
                // Have noticed the existence of several other multipart/* types of messages
525
                // but have yet had the opportunity to test on those.
526
                $ftype = (empty($parts[$p]->type))?
527
                    $this->_dataTypes[0].'/'.strtolower($parts[$p]->subtype)
528
                :
529
                	$this->_dataTypes[$parts[$p]->type].'/'.strtolower($parts[$p]->subtype);
530
 
531
                $this_was_signed	= ($ftype == 'multipart/signed')? true : false;
532
                $skip_next			= ($ftype == 'message/rfc822')?   true : false;
533
 
534
                if (
535
                	$ftype == 'multipart/mixed' && ($last_was_signed || $skip_part) ||
536
                	$ftype == 'multipart/signed' ||
537
                	$skip_part && $ftype == 'multipart/alternative' ||
538
                	$ftype == 'multipart/related' && count($parts) == 1
539
               	) {
540
                    $n--;
541
                    $skipped = true;
542
                } else {
543
                    $skipped = false;
544
 
545
                    $this->structure[$mid]['pid'][$n]       = ($is_sub_part == false)? (string) "$i" : (string) "$sub_pid.$i";
546
                    $this->structure[$mid]['ftype'][$n]     = $ftype;
547
                    $this->structure[$mid]['encoding'][$n]  = (empty($parts[$p]->encoding))? $this->_encodingTypes[0] : $this->_encodingTypes[$parts[$p]->encoding];
548
                    $this->structure[$mid]['fsize'][$n]     = (!isset($parts[$p]->bytes) || empty($parts[$p]->bytes))? 0 : $parts[$p]->bytes;
549
 
550
                    // Get extra parameters.
551
                    if ($parts[$p]->ifparameters) {
552
                        foreach ($parts[$p]->parameters as $param) {
553
                            $this->structure[$mid][strtolower($param->attribute)][$n] = strtolower($param->value);
554
                        }
555
                    }
556
 
557
                    // Force inline disposition if none is present
558
                    if ($parts[$p]->ifdisposition) {
559
                        $this->structure[$mid]['disposition'][$n] = strtolower($parts[$p]->disposition);
560
                        if ($parts[$p]->ifdparameters) {
561
                            foreach ($parts[$p]->dparameters as $param) {
562
                                if (strtolower($param->attribute) == 'filename') {
563
                                    $this->structure[$mid]['fname'][$n] = $param->value;
564
                                    break;
565
                                }
566
                            }
567
                        }
568
                    } else {
569
                        $this->structure[$mid]['disposition'][$n] = 'inline';
570
                    }
571
 
572
                    if ($parts[$p]->ifid) {
573
                        $this->structure[$mid]['cid'][$n] = $parts[$p]->id;
574
                    }
575
                }
576
 
577
                if (isset($parts[$p]->parts) && is_array($parts[$p]->parts)) {
578
                    if (!$skipped) {
579
                        $this->structure[$mid]['has_at'][$n] = true;
580
                    }
581
 
582
                    $n = $this->_declareParts($mid, $parts[$p]->parts, $this->structure[$mid]['pid'][$n], $n, true, $skip_next, $this_was_signed);
583
                }
584
                else if (!$skipped) {
585
                	$this->structure[$mid]['has_at'][$n] = false;
586
                }
587
            }
588
 
589
            if ($is_sub_part) {
590
                return $n;
591
            }
592
         } else {
593
             // $parts is not an array... message is flat
594
            $this->structure[$mid]['pid'][0] = 1;
595
 
596
            if (empty($this->structure[$mid]['obj']->type)) {
597
                $this->structure[$mid]['obj']->type = (int) 0;
598
            }
599
 
600
            if (isset($this->structure[$mid]['obj']->subtype)) {
601
                $this->structure[$mid]['ftype'][0] = $this->_dataTypes[$this->structure[$mid]['obj']->type].'/'.strtolower($this->structure[$mid]['obj']->subtype);
602
            }
603
 
604
            if (empty($this->structure[$mid]['obj']->encoding)) {
605
                $this->structure[$mid]['obj']->encoding = (int) 0;
606
            }
607
 
608
            $this->structure[$mid]['encoding'][0] = $this->_encodingTypes[$this->structure[$mid]['obj']->encoding];
609
 
610
            if (isset($this->structure[$mid]['obj']->bytes)) {
611
                $this->structure[$mid]['fsize'][0] = strtolower($this->structure[$mid]['obj']->bytes);
612
            }
613
 
614
            $this->structure[$mid]['disposition'][0]    = 'inline';
615
            $this->structure[$mid]['has_at'][0] = false;
616
 
617
            // Go through the parameters, if any
618
            if (isset($this->structure[$mid]['obj']->ifparameters) && $this->structure[$mid]['obj']->ifparameters) {
619
                foreach ($this->structure[$mid]['obj']->parameters as $param) {
620
                    $this->structure[$mid][strtolower($param->attribute)][0] = $param->value;
621
                }
622
            }
623
        }
624
 
625
        return;
626
    }
627
 
628
    /**
629
    * Checks if the part has been parsed, if not calls on _declareParts to
630
    * parse the message.
631
    *
632
    * @param    int          &$mid         message id
633
    * @param    bool         $checkPid
634
    * @return   void
635
    * @access   protected
636
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_checkIfParsed
637
    */
638
    function _checkIfParsed(&$mid, $checkPid = true, $get_mime = 'text/html')
639
    {
640
        if (!isset($this->structure[$mid]['pid'])) {
641
           $this->_declareParts($mid);
642
        }
643
 
644
        if ($checkPid == true && !isset($this->msg[$mid]['pid'])) {
645
           $this->_getDefaultPid($mid, $get_mime);
646
        }
647
        return;
648
    }
649
 
650
    /**
651
    * sets up member variables containing inline parts and attachments for a specific
652
    * part in member variable arrays beginning with 'in' and 'attach'. If inline parts
653
    * are present, sets {@link $inPid}, {@link $inFtype}, {@link $inFsize},
654
    * {@link $inHasAttach}, {@link $inInlineId} (if an inline CID is specified). If
655
    * attachments are present, sets, {@link $attachPid}, {@link $attachFsize},
656
    * {@link $attachHasAttach}, {@link $attachFname} (if a filename is present, empty
657
    * string otherwise).
658
    *
659
    * @param    int           &$mid         message id
660
    * @param    int           &$pid         part id
661
    * @param    bool          $ret
662
    *   false by default, if true returns the contents of the $in* and $attach* arrays.
663
    *   If false method returns BOOL.
664
    *
665
    * @param    string        $args         (optional)
666
    *   Associative array containing optional extra arguments. The following are the
667
    *   possible indices.
668
    *
669
    *       $args['get_mime'] STRING
670
    *           Values: text/plain|text/html, text/html by default. The MIME type for
671
    *           the part to be displayed by default for each level of nesting.
672
    *
673
    *       $agrs['get_alternative'] BOOL
674
    *           If true, includes the alternative part of a multipart/alternative
675
    *           message in the $in* array. If veiwing text/html part by default this
676
    *           places the text/plain part in the $in* (inline attachment array).
677
    *
678
    *       $args['retrieve_all'] BOOL
679
    *           If true, gets all the message parts at once, this option will index
680
    *           the entire message in the $in* and $attach* member variables regardless
681
    *           of nesting (method indexes parts relevant to the current level of
682
    *           nesting by default).
683
    *
684
    * @return   BOOL|Array
685
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getParts
686
    * @access   public
687
    * @since    PHP 4.2.0
688
    */
689
    function getParts(&$mid, $pid = '0', $ret = false, $args = array())
690
    {
691
        if (!isset($args['get_mime'])) {
692
            $args['get_mime'] = 'text/html';
693
        }
694
 
695
        if (!isset($args['get_alternative'])) {
696
            $args['get_alternative'] = true;
697
        }
698
 
699
        $this->_checkIfParsed($mid, true, $args['get_mime']);
700
 
701
        if ($pid === '0') {
702
            $pid = $this->msg[$mid]['pid'];
703
        }
704
 
705
        if (count($this->structure[$mid]['pid']) == 1 && !isset($this->structure[$mid]['fallback'][0])) {
706
            return true;
707
        }
708
 
709
        // retrieve key for this part, so that the information may be accessed
710
        if (false !== ($i = array_search((string) $pid, $this->structure[$mid]['pid']))) {
711
            if (isset($args['retrieve_all']) && $args['retrieve_all'] == true) {
712
                $this->_scanMultipart($mid, $pid, $i, $args['get_mime'], 'add', 'none', 2, $args['get_alternative']);
713
            } else {
714
                if ($pid == $this->msg[$mid]['pid']) {
715
                    $this->_scanMultipart($mid, $pid, $i, $args['get_mime'], 'add', 'top', 2, $args['get_alternative']);
716
                } else if ($this->structure[$mid]['ftype'][$i] == 'message/rfc822') {
717
                    $this->_scanMultipart($mid, $pid, $i, $args['get_mime'], 'add', 'all', 1, $args['get_alternative']);
718
                }
719
            }
720
        } else {
721
            $this->error->push(Mail_IMAPv2_ERROR_INVALID_PID, 'error', array('pid' => $pid));
722
            return false;
723
        }
724
 
725
        return ($ret)? $this->msg[$mid] : true;
726
    }
727
 
728
    /**
729
    * Finds message parts relevant to the message part currently being displayed or
730
    * looks through a message and determines which is the best body to display.
731
    *
732
    * @param    int           &$mid         message id
733
    * @param    int           &$pid         part id
734
    * @param    int           $i            offset indice correlating to the pid
735
    * @param    str           $MIME         one of text/plain or text/html the default MIME to retrieve.
736
    * @param    str           $action       one of add|get
737
    * @param    str           $look_for     one of all|multipart|top|none
738
    * @param    int           $pid_add      determines the level of nesting.
739
    * @param    bool          $get_alternative
740
    *   Determines whether the program retrieves the alternative part in a
741
    *   multipart/alternative message.
742
    *
743
    * @return   string|false
744
    * @access   private
745
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_scanMultipart
746
    */
747
    function _scanMultipart(&$mid, &$pid, &$i, $MIME, $action = 'add', $look_for = 'all', $pid_add = 1, $get_alternative = true)
748
    {
749
        // Find subparts, create variables
750
        // Create inline parts first, and attachments second
751
 
752
        // Get all top level parts, with the exception of the part currently being viewed
753
        // If top level part contains multipart/alternative go into that subpart to
754
        // retrieve the other inline message part to display
755
 
756
        // If this part is message/rfc822 get subparts that begin with this part id
757
        // Skip multipart/alternative message part
758
        // Find the displayable message, get text/plain part if $getInline is true
759
        if ($action == 'add') {
760
           $excludeMIME = $MIME;
761
           $MIME        = ($excludeMIME == 'text/plain')? 'text/html' : 'text/plain';
762
           $in          = 0;
763
           $a           = 0;
764
        } else if ($action == 'get') {
765
           $excludeMIME = null;
766
        }
767
 
768
        $pid_len      = strlen($pid);
769
        $this_nesting = count(explode('.', $pid));
770
 
771
        foreach ($this->structure[$mid]['pid'] as $p => $id) {
772
            // To look at the next level of nesting one needs to determine at which level
773
            // of nesting the program currently resides, this needs to be independent of the
774
            // part id length, since part ids can get into double digits (let's hope they
775
            // don't get into triple digits!)
776
 
777
            // To accomplish this we'll explode the part id on the dot to get a count of the
778
            // nesting, then compare the string with the next level in.
779
 
780
            $nesting = count(explode('.', $this->structure[$mid]['pid'][$p]));
781
 
782
            switch ($look_for) {
783
                case 'all':
784
                {
785
                    $condition = (($nesting == ($this_nesting + 1)) && $pid == substr($this->structure[$mid]['pid'][$p], 0, $pid_len));
786
                    break;
787
                }
788
                case 'multipart':
789
                {
790
                    $condition = (($nesting == ($this_nesting + 1)) && ($pid == substr($this->structure[$mid]['pid'][$p], 0)));
791
                    break;
792
                }
793
                // Used if *all* parts are being retrieved
794
                case 'none':
795
                {
796
                    $condition = true;
797
                    break;
798
                }
799
                // To gaurantee a top-level part, detect whether a period appears in the pid string
800
                case 'top':
801
                default:
802
                {
803
                    if ($this->_isMultipart($mid, 'related') || $this->_isMultipart($mid, 'mixed')) {
804
                        $condition = (!stristr($this->structure[$mid]['pid'][$p], '.') || ($nesting == 2) && substr($this->msg[$mid]['pid'], 0, 1) == substr($this->structure[$mid]['pid'][$p], 0, 1));
805
                    } else {
806
                        $condition = (!stristr($this->structure[$mid]['pid'][$p], '.'));
807
                    }
808
                }
809
            }
810
 
811
            if ($condition == true) {
812
                if ($this->structure[$mid]['ftype'][$p] == 'multipart/alternative' || $this->structure[$mid]['ftype'][$p] == 'multipart/mixed') {
813
                    foreach ($this->structure[$mid]['pid'] as $mp => $mpid) {
814
                        // Part must begin with last matching part id and be two levels in
815
                        $sub_nesting = count(explode('.', $this->structure[$mid]['pid'][$p]));
816
 
817
                        if (
818
                        	$this->structure[$mid]['ftype'][$mp] == $MIME &&
819
                            $get_alternative == true &&
820
                            ($sub_nesting == ($this_nesting + $pid_add)) &&
821
                            ($pid == substr($this->structure[$mid]['pid'][$mp], 0, strlen($this->structure[$mid]['pid'][$p])))
822
                        ) {
823
                            if ($action == 'add') {
824
                                 $this->_addPart($in, $mid, $mp, 'in');
825
                                 break;
826
                            } else if ($action == 'get' && !isset($this->structure[$mid]['fname'][$mp]) && empty($this->structure[$mid]['fname'][$mp])) {
827
                                return $this->structure[$mid]['pid'][$mp];
828
                            }
829
                        } else if ($this->structure[$mid]['ftype'][$mp] == 'multipart/alternative' && $action == 'get') {
830
                            // Need to match this PID to next level in
831
                            $pid          = (string) $this->structure[$mid]['pid'][$mp];
832
                            $pid_len      = strlen($pid);
833
                            $this_nesting = count(explode('.', $pid));
834
                            $pid_add       = 2;
835
                            continue;
836
                        }
837
                    }
838
                } else if ($this->structure[$mid]['disposition'][$p] == 'inline' && $this->structure[$mid]['ftype'][$p] != 'multipart/related' && $this->structure[$mid]['ftype'][$p] != 'multipart/mixed') {
839
                    if ((
840
                    	  $action == 'add' &&
841
                          $this->structure[$mid]['ftype'][$p] != $excludeMIME &&
842
                          $pid != $this->structure[$mid]['pid'][$p]
843
                       	) || (
844
                          $action == 'add' &&
845
                          $this->structure[$mid]['ftype'][$p] == $excludeMIME &&
846
                          isset($this->structure[$mid]['fname'][$p]) &&
847
                          $pid != $this->structure[$mid]['pid'][$p]
848
                       	) || (
849
                          $action == 'add' && isset($this->structure[$mid]['fallback'][0])
850
                       )) {
851
                        $this->_addPart($in, $mid, $p, 'in');
852
                    } else if ($action == 'get' && $this->structure[$mid]['ftype'][$p] == $MIME && !isset($this->structure[$mid]['fname'][$p])) {
853
                        return $this->structure[$mid]['pid'][$p];
854
                    }
855
                } else if ($action == 'add' && $this->structure[$mid]['disposition'][$p] == 'attachment') {
856
                    $this->_addPart($a, $mid, $p, 'at');
857
                }
858
            }
859
        }
860
 
861
        return false;
862
    }
863
 
864
    /**
865
    * Determines whether a message contains a multipart/(insert subtype here) part.
866
    * Only called on by $this->_scanMultipart
867
    *
868
    * @return   BOOL
869
    * @access   private
870
    * @see      _scanMultipart
871
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_isMultipart
872
    */
873
    function _isMultipart($mid, $subtype)
874
    {
875
        $ret = $this->extractMIME($mid, array('multipart/'.$subtype));
876
        return (!empty($ret) && is_array($ret) && count($ret) >= 1)? true : false;
877
    }
878
 
879
    /**
880
    * Looks to see if this part has any inline parts associated with it.
881
    * It looks up the message tree for parts with CID entries and
882
    * indexes those entries, whereas an algorithm may be ran to replace
883
    * inline CIDs with a part viewer.
884
    *
885
    * @param   int      &$mid          message id
886
    * @param   string   &$pid          part id
887
    * @param   array    $secureMIME    array of acceptable CID MIME types.
888
    *
889
    * The $secureMIME argument allows you to limit the types of files allowed
890
    * in a multipart/related message, for instance, to prevent a browser from
891
    * automatically initiating download of a part that could contain potentially
892
    * malicious code.
893
    *
894
    * Suggested MIME types:
895
    * text/plain, text/html, text/css, image/jpeg, image/pjpeg, image/gif
896
    * image/png,  image/x-png, application/xml, application/xhtml+xml,
897
    * text/xml
898
    *
899
    * MIME types are not limited by default.
900
    *
901
    * @return  array|false
902
    *    On success returns an array of parts associated with the current message,
903
    *    including the cid of the part, the part id and the MIME type.
904
    *
905
    * @access  public
906
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getRelatedParts
907
    */
908
    function getRelatedParts(&$mid, &$pid, $secureMIME = array())
909
    {
910
        // Check to see if this part has already been parsed
911
        $this->_checkIfParsed($mid);
912
 
913
        // Message has a PID of 1.1.2
914
        // Cid parts are located at the prior level of nesting at 1.x
915
        // From the supplied PID, go back one level of nesting.
916
        // Compare the first number of the supplied PID against the current PID.
917
        // Look for a cid entry in the structure array.
918
        // Index the PID and CID of the part.
919
        //
920
        // Supplied pid must correspond to a text/html part.
921
        if (!empty($secureMIME) && is_array($secureMIME)) {
922
            $this->error->push(
923
            	Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY,
924
            	'error',
925
            	array(
926
            		'arg' => '$secureMIME',
927
            		'actual_value' => $secureMIME
928
            	)
929
            );
930
            return false;
931
        }
932
 
933
        $related = array();
934
 
935
        if (isset($this->structure[$mid]['pid']) && is_array($this->structure[$mid]['pid'])) {
936
            if (strlen($pid) > 1) {
937
                $nesting = count(explode('.', $pid));
938
                $compare = substr($pid, 0, -4);
939
                foreach ($this->structure[$mid]['pid'] as $i => $rpid) {
940
                    // This level of nesting is one above the message part
941
                    // The beginning of the pid string of the related part matches that of the
942
                    // beginning of the pid supplied
943
                    if (count(explode('.', $rpid)) == ($nesting - 1) && substr($rpid, 0, -2) == $compare) {
944
                        $this->_getCIDs($mid, $i, $secureMIME, $related);
945
                    }
946
                }
947
            } else if (strlen($pid) == 1) {
948
                // If the pid is in the first level of nesting, odds are the related parts are in the
949
                // sub level of nesting.
950
                foreach ($this->structure[$mid]['pid'] as $i => $rpid) {
951
                    // The part is one level under and the first number matches that
952
                    // of its parent part.
953
                    if (count(explode('.', $rpid)) == 2 && substr($rpid, 0, 1) == $pid) {
954
                        $this->_getCIDs($mid, $i, $secureMIME, $related);
955
                    }
956
                }
957
            }
958
        } else {
959
            $this->error->push(
960
            	Mail_IMAPv2_ERROR,
961
            	'error',
962
            	null,
963
            	'Message structure does not exist.'
964
            );
965
        }
966
        return (count($related) >= 1)? $related : false;
967
    }
968
 
969
    /**
970
    * Helper function for getRelatedParts
971
    *
972
    * @return void
973
    * @access private
974
    * @see    getRelatedParts
975
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_getCIDs
976
    */
977
    function _getCIDs(&$mid, &$i, &$secureMIME, &$related)
978
    {
979
        if ((isset($this->structure[$mid]['cid'][$i])) && (empty($secureMIME) || is_array($secureMIME) && in_array($this->structure[$mid]['ftype'][$i], $secureMIME))) {
980
            $related['cid'][] = $this->structure[$mid]['cid'][$i];
981
            $related['pid'][] = $this->structure[$mid]['pid'][$i];
982
            $related['ftype'][] = $this->structure[$mid]['ftype'][$i];
983
        }
984
    }
985
 
986
    /**
987
    * Destroys variables set by {@link getParts} and _declareParts.
988
    *
989
    * @param    integer  &$mid   message id
990
    * @return   void
991
    * @access   public
992
    * @see      getParts
993
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/unsetParts
994
    */
995
    function unsetParts(&$mid)
996
    {
997
        unset($this->msg[$mid]);
998
        unset($this->structure[$mid]);
999
        return;
1000
    }
1001
 
1002
    /**
1003
    * Adds information to the member variable inline part 'in' and attachment 'at' arrays.
1004
    *
1005
    * @param    int     &$n   offset part counter
1006
    * @param    int     &$mid  message id
1007
    * @param    int     &$i    offset structure reference counter
1008
    * @return   void
1009
    * @access   private
1010
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_addPart
1011
    */
1012
    function _addPart(&$n, &$mid, &$i, $part)
1013
    {
1014
        foreach ($this->fields as $field) {
1015
            if (isset($this->structure[$mid][$field][$i]) && !empty($this->structure[$mid][$field][$i])) {
1016
                $this->msg[$mid][$part][$field][$n] = $this->structure[$mid][$field][$i];
1017
            }
1018
        }
1019
        $n++;
1020
        return;
1021
    }
1022
 
1023
    /**
1024
    * Returns entire unparsed message body.  See {@link imap_body} for options.
1025
    *
1026
    * @param    int     &$mid      message id
1027
    * @return   string|null
1028
    * @tutorial http://www.smilingsouls.net/index.php?content=Mail_IMAPv2/getRawMessage
1029
    * @access   public
1030
    * @see      imap_body
1031
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getRawMessage
1032
    */
1033
    function getRawMessage(&$mid)
1034
    {
1035
        $opt = (isset($this->option['body']))? $this->option['body'] : null;
1036
        return imap_body($this->mailbox, $mid, $opt);
1037
    }
1038
 
1039
    /**
1040
    * Searches parts array set in $this->_declareParts() for a displayable message.
1041
    * If the part id passed is message/rfc822 looks in subparts for a displayable body.
1042
    * Attempts to return a text/html inline message part by default. And will
1043
    * automatically attempt to find a text/plain part if a text/html part could
1044
    * not be found.
1045
    *
1046
    * Returns an array containing three associative indices; 'ftype', 'fname' and
1047
    * 'message'.  'ftype' contains the MIME type of the message, 'fname', the original
1048
    * file name, if any, empty string otherwise.  And 'message', which contains the
1049
    * message body itself which is returned decoded from base64 or quoted-printable if
1050
    * either of those encoding types are specified, returns untouched otherwise.
1051
    * Returns false on failure.
1052
    *
1053
    * @param    int     &$mid                    message id
1054
    * @param    string  $pid                     part id
1055
    * @param    int     $action
1056
    *      (optional) options for body return.  Set to one of the following:
1057
    *      Mail_IMAPv2_BODY (default), if part is message/rfc822 searches subparts for a
1058
    *      displayable body and returns the body decoded as part of an array.
1059
    *      Mail_IMAPv2_LITERAL, return the message for the specified $pid without searching
1060
    *      subparts or decoding the message (may return unparsed message) body is returned
1061
    *      undecoded as a string.
1062
    *      Mail_IMAPv2_LITERAL_DECODE, same as Mail_IMAPv2_LITERAL, except message decoding is
1063
    *      attempted from base64 or quoted-printable encoding, returns undecoded string
1064
    *      if decoding failed.
1065
    *
1066
    * @param    string  $getPart
1067
    *      (optional) one of text/plain or text/html, allows the specification of the default
1068
    *      part to return from multipart messages, text/html by default.
1069
    *
1070
    * @param    int     $attempt
1071
    *      (optional) used internally by getBody to track attempts at finding the
1072
    *      right part to display for the body of the message.
1073
    *
1074
    * @return   array|string|false
1075
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getBody
1076
    * @access   public
1077
    * @see      imap_fetchbody
1078
    * @see      $this->getParts
1079
    * @since    PHP 4.2.0
1080
    */
1081
    function getBody(&$mid, $pid = '1', $action = 0, $get_mime = 'text/html', $attempt = 1)
1082
    {
1083
        $options = (isset($this->option['fetchbody']))? $this->option['fetchbody'] : null;
1084
 
1085
        if ($action == Mail_IMAPv2_LITERAL) {
1086
            return @imap_fetchbody($this->mailbox, $mid, $pid, $options);
1087
        }
1088
 
1089
        $this->_checkIfParsed($mid, true, $get_mime);
1090
 
1091
        if (false !== ($i = array_search((string) $pid, $this->structure[$mid]['pid']))) {
1092
            if ($action == Mail_IMAPv2_LITERAL_DECODE) {
1093
                $msg_body = @imap_fetchbody($this->mailbox, $mid, $pid, $options);
1094
                return $this->_decodeMessage($msg_body, $this->structure[$mid]['encoding'][$i]);
1095
            }
1096
 
1097
            // If this is an attachment, and the part is message/rfc822 update the pid to the subpart
1098
            // If this is an attachment, and the part is multipart/alternative update the pid to the subpart
1099
            if (
1100
            	$this->structure[$mid]['ftype'][$i] == 'message/rfc822' ||
1101
            	$this->structure[$mid]['ftype'][$i] == 'multipart/related' ||
1102
            	$this->structure[$mid]['ftype'][$i] == 'multipart/alternative'
1103
           	) {
1104
                $new_pid =
1105
                	($this->structure[$mid]['ftype'][$i] == 'message/rfc822' || $this->structure[$mid]['ftype'][$i] == 'multipart/related') ?
1106
                		$this->_scanMultipart($mid, $pid, $i, $get_mime, 'get', 'all', 1)
1107
                	:
1108
                		$this->_scanMultipart($mid, $pid, $i, $get_mime, 'get', 'multipart', 1);
1109
 
1110
                // if a new pid for text/html couldn't be found, try again, this time look for text/plain
1111
                switch(true) {
1112
                    case (!empty($new_pid)):
1113
                    {
1114
                    	$pid = $new_pid;
1115
                    	break;
1116
                    }
1117
                    case (empty($new_pid) && $get_mime == 'text/html'):
1118
                    {
1119
                    	return ($attempt == 1)? $this->getBody($mid, $pid, $action, 'text/plain', 2) : false;
1120
                    }
1121
                    case (empty($new_pid) && $get_mime == 'text/plain'):
1122
                    {
1123
                    	return ($attempt == 1)? $this->getBody($mid, $pid, $action, 'text/html', 2) : false;
1124
                    }
1125
                }
1126
            }
1127
 
1128
            // Update the key for the new pid
1129
            if (!empty($new_pid)) {
1130
                if (false === ($i = array_search((string) $pid, $this->structure[$mid]['pid']))) {
1131
                    // Something's afoot!
1132
                    $this->error->push(
1133
                        Mail_IMAPv2_ERROR,
1134
                        'error',
1135
                        array(
1136
                            'mid' => $mid,
1137
                            'pid' => $pid
1138
                        ),
1139
                        'Unable to find a suitable replacement part ID. Message: may be poorly formed, corrupted, or not supported by the Mail_IMAPv2 parser.'
1140
                    );
1141
                    return false;
1142
                }
1143
            }
1144
 
1145
            $msg_body = imap_fetchbody($this->mailbox, $mid, $pid, $options);
1146
 
1147
            if ($msg_body == null) {
1148
                $this->error->push(
1149
                    Mail_IMAPv2_ERROR,
1150
                    'error',
1151
                    array(
1152
                        'mid' => $mid,
1153
                        'pid' => $pid
1154
                    ),
1155
                    'Message body is null.'
1156
                );
1157
                return false;
1158
            }
1159
 
1160
            // Decode message.
1161
            // Because the body returned may not correspond with the original PID, return
1162
            // an array which also contains the MIME type and original file name, if any.
1163
            $body['message'] = $this->_decodeMessage(
1164
                $msg_body,
1165
                $this->structure[$mid]['encoding'][$i],
1166
                $this->structure[$mid]['charset'][$i]
1167
            );
1168
            $body['ftype']   = $this->structure[$mid]['ftype'][$i];
1169
            $body['fname']   = (isset($this->structure[$mid]['fname'][$i]))? $this->structure[$mid]['fname'][$i] : '';
1170
            $body['charset'] = $this->structure[$mid]['charset'][$i];
1171
 
1172
            return $body;
1173
        }
1174
        else
1175
        {
1176
            $this->error->push(
1177
                Mail_IMAPv2_ERROR_INVALID_PID,
1178
                'error',
1179
                array(
1180
                    'pid' => $pid
1181
                )
1182
            );
1183
            return false;
1184
        }
1185
 
1186
        return false;
1187
    }
1188
 
1189
    /**
1190
    * Decode a string from quoted-printable or base64 encoding.  If
1191
    * neither of those encoding types are specified, returns string
1192
    * untouched.
1193
    *
1194
    * @param    string  &$body           string to decode
1195
    * @param    string  &$encoding       encoding to decode from.
1196
    * @return   string
1197
    * @access   private
1198
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_decodeMessage
1199
    */
1200
    function _decodeMessage(&$body, &$encoding, &$charset)
1201
    {
1202
        switch ($encoding) {
1203
            case 'quoted-printable':
1204
                return ($charset == 'utf-8')? utf8_decode(imap_utf8(imap_qprint($body))) : imap_qprint($body);
1205
            case 'base64':            return imap_base64($body);
1206
            default:                  return $body;
1207
        }
1208
    }
1209
 
1210
    /**
1211
    * Searches structure defined in $this->_declareParts for the top-level default message.
1212
    * Attempts to find a text/html default part, if no text/html part is found,
1213
    * automatically attempts to find a text/plain part. Returns the part id for the default
1214
    * top level message part on success. Returns false on failure.
1215
    *
1216
    * @param    int     &$mid           message id
1217
    * @param    string  $getPart
1218
    *     (optional) default MIME type to look for, one of text/html or text/plain
1219
    *     text/html by default.
1220
    * @param    int     $attempt
1221
    *     (optional) Used internally by _getDefaultPid to track the method's attempt
1222
    *     at retrieving the correct default part to display.
1223
    *
1224
    * @return   string
1225
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_getDefaultPid
1226
    * @access   private
1227
    */
1228
    function _getDefaultPid(&$mid, $get_mime = 'text/html', $attempt = 1)
1229
    {
1230
        // Check to see if this part has already been parsed
1231
        $this->_checkIfParsed($mid, false);
1232
 
1233
        // Look for a text/html message part
1234
        // If no text/html message part was found look for a text/plain message part
1235
        $part =
1236
        	($get_mime == 'text/html') ?
1237
        		array('text/html', 'text/plain')
1238
        	:
1239
        		array('text/plain', 'text/html');
1240
 
1241
        foreach ($part as $mime) {
1242
            if (0 !== count($msg_part = @array_keys($this->structure[$mid]['ftype'], $mime))) {
1243
                foreach ($msg_part as $i) {
1244
                    if ($this->structure[$mid]['disposition'][$i] == 'inline' && !stristr($this->structure[$mid]['pid'][$i], '.')) {
1245
                        $this->msg[$mid]['pid'] = $this->structure[$mid]['pid'][$i];
1246
                        return $this->structure[$mid]['pid'][$i];
1247
                    }
1248
                }
1249
            }
1250
        }
1251
 
1252
        // If no text/plain or text/html part was found
1253
        // Look for a multipart/alternative part
1254
        $mp_nesting = 1;
1255
        $pid_len    = 1;
1256
 
1257
        if (is_array($this->structure[$mid]['pid'])) {
1258
	        foreach ($this->structure[$mid]['pid'] as $p => $id) {
1259
	            $nesting = count(explode('.', $this->structure[$mid]['pid'][$p]));
1260
 
1261
	            if (!isset($mpid)) {
1262
	                if ($nesting == 1 && isset($this->structure[$mid]['ftype'][$p]) && ($this->structure[$mid]['ftype'][$p] == 'multipart/related')) {
1263
	                    $mp_nesting = 2;
1264
	                    $pid_len    = 3;
1265
	                    continue;
1266
	                }
1267
	                if (
1268
	                	$nesting == $mp_nesting &&
1269
	                	isset($this->structure[$mid]['ftype'][$p]) &&
1270
	                	($this->structure[$mid]['ftype'][$p] == 'multipart/alternative'  || $this->structure[$mid]['ftype'][$p]  == 'multipart/mixed')
1271
	               	) {
1272
	                    $mpid = $this->structure[$mid]['pid'][$p];
1273
	                    continue;
1274
	                }
1275
	            }
1276
 
1277
	            if (
1278
	            	isset($mpid) && $nesting == ($mp_nesting + 1) &&
1279
	            	$this->structure[$mid]['ftype'][$p] == $get_mime &&
1280
	            	$mpid == substr($this->structure[$mid]['pid'][$p], 0, $pid_len)
1281
	            ) {
1282
	                $this->msg[$mid]['pid'] = $this->structure[$mid]['pid'][$p];
1283
	                return $this->structure[$mid]['pid'][$p];
1284
	            }
1285
	        }
1286
        } else {
1287
        	$this->error->push(Mail_IMAPv2_ERROR, 'error', null, 'Message structure does not exist.');
1288
        }
1289
 
1290
        // if a text/html part was not found, call on the function again
1291
        // and look for text/plain
1292
        // if the application was unable to find a text/plain part
1293
        switch ($get_mime) {
1294
            case 'text/html':
1295
            {
1296
                $rtn = ($attempt == 1)?
1297
                    $this->_getDefaultPid($mid, 'text/plain', 2)
1298
                :
1299
                    false;
1300
 
1301
                break;
1302
            }
1303
            case 'text/plain':
1304
            {
1305
                $rtn = ($attempt == 1)?
1306
                    $this->_getDefaultPid($mid, 'text/html', 2)
1307
                :
1308
                    false;
1309
 
1310
                break;
1311
            }
1312
            default:
1313
            {
1314
                $rtn = false;
1315
            }
1316
        }
1317
 
1318
        if ($rtn == false && $attempt == 2) {
1319
            if (isset($this->structure[$mid]['ftype'][0])) {
1320
                $this->structure[$mid]['fallback'][0] = true;
1321
            } else {
1322
                $this->error->push(Mail_IMAPv2_ERROR, 'error', null, 'Message contains no MIME types.');
1323
            }
1324
        }
1325
 
1326
        $this->msg[$mid]['pid'] = ($rtn == false)? 1 : $rtn;
1327
 
1328
        return $this->msg[$mid]['pid'];
1329
    }
1330
 
1331
    /**
1332
    * Searches all message parts for the specified MIME type.  Use {@link getBody}
1333
    * with $action option Mail_IMAPv2_LITERAL_DECODE to view MIME type parts retrieved.
1334
    * If you need to access the MIME type with filename use normal {@link getBody}
1335
    * with no action specified.
1336
    *
1337
    * Returns an array of part ids on success.
1338
    * Returns false if MIME couldn't be found, or on failure.
1339
    *
1340
    * @param    int           &$mid           message id
1341
    * @param    string|array  $MIMEs          mime type to extract
1342
    * @return   array|false
1343
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/extractMIME
1344
    * @access   public
1345
    */
1346
    function extractMIME(&$mid, $MIMEs)
1347
    {
1348
        $this->_checkIfParsed($mid);
1349
 
1350
        if (is_array($this->structure[$mid]['ftype'])) {
1351
            if (is_array($MIMEs)) {
1352
                foreach ($MIMEs as $MIME) {
1353
                    if (0 !== count($keys = array_keys($this->structure[$mid]['ftype'], $MIME))) {
1354
                        foreach ($keys as $key) {
1355
                            $rtn[] = $this->structure[$mid]['pid'][$key];
1356
                        }
1357
                    }
1358
                }
1359
            } else {
1360
                $this->error->push(
1361
                    Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY,
1362
                    'error',
1363
                    array(
1364
                        'arg' => '$MIMEs',
1365
                        'actual_value' => $MIMEs
1366
                    )
1367
                );
1368
            }
1369
        } else {
1370
            $this->error->push(
1371
                Mail_IMAPv2_ERROR,
1372
                'error',
1373
                null,
1374
                'Member variable $this->structure[\'ftype\'] is not an array'
1375
            );
1376
        }
1377
 
1378
        return (isset($rtn))? $rtn : false;
1379
    }
1380
 
1381
    /**
1382
    * Set member variable {@link $rawHeaders} to contain Raw Header information
1383
    * for a part.  Returns default header part id on success, returns false on failure.
1384
    *
1385
    * @param    int     &$mid          message_id
1386
    * @param    string  $pid           (optional) part id to retrieve headers for
1387
    * @param    bool    $rtn
1388
    *   Decides what to return. One of true|false|return_pid
1389
    *   If true return the raw headers (returns the headers by default)
1390
    *
1391
    * @return   string|false
1392
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getRawHeaders
1393
    * @access   public
1394
    * @see      imap_fetchbody
1395
    * @see      getHeaders
1396
    */
1397
    function getRawHeaders(&$mid, $pid = '0', $rtn = true, $pid_check = false)
1398
    {
1399
        $this->_checkIfParsed($mid);
1400
 
1401
        if ($pid == $this->msg[$mid]['pid']) {
1402
            $pid = (string) '0';
1403
        }
1404
 
1405
        if ($pid !== '0') {
1406
            if (false === ($pid = $this->_defaultHeaderPid($mid, $pid))) {
1407
                $this->error->push(Mail_IMAPv2_ERROR_INVALID_PID, 'error', array('pid' => $pid));
1408
                return false;
1409
            }
1410
        }
1411
 
1412
        if ($pid === '0' && $pid_check) {
1413
            return true;
1414
        } else if ($pid_check) {
1415
            $rtn = true;
1416
        }
1417
 
1418
        if ($pid === '0') {
1419
            $opt = (isset($this->option['fetchheader']))? $this->option['fetchheader'] : null;
1420
            $raw_headers = @imap_fetchheader($this->mailbox, $mid, $opt);
1421
        } else {
1422
            $opt = (isset($this->option['fetchbody']))? $this->option['fetchbody'] : null;
1423
            $raw_headers = @imap_fetchbody($this->mailbox, $mid, $pid, $opt);
1424
        }
1425
 
1426
        if ($rtn) {
1427
            return $raw_headers;
1428
        } else {
1429
            $this->header[$mid]['raw'] = $raw_headers;
1430
            return true;
1431
        }
1432
    }
1433
 
1434
    /**
1435
    * Set member variable containing header information.  Creates an array containing
1436
    * associative indices referring to various header information.  Use {@link var_dump}
1437
    * or {@link print_r} on the {@link $header} member variable to view information
1438
    * gathered by this function.
1439
    *
1440
    * If $ret is true, returns array containing header information on success and false
1441
    * on failure.
1442
    *
1443
    * If $ret is false, adds the header information to the $header member variable
1444
    * and returns BOOL.
1445
    *
1446
    * @param    int     &$mid           message id
1447
    * @param    string  &$pid           (optional) part id to retrieve headers for.
1448
    * @param    bool    $rtn
1449
    *   (optional) If true return the headers, if false, assign to $header member variable.
1450
    *
1451
    * @param    array   $args
1452
    *   (optional) Associative array containing extra arguments.
1453
    *
1454
    *       $args['from_length'] int
1455
    *           From field length for imap_headerinfo.
1456
    *
1457
    *       $args['subject_length'] int
1458
    *           Subject field length for imap_headerinfo
1459
    *
1460
    *       $args['default_host'] string
1461
    *           Default host for imap_headerinfo & imap_rfc822_parse_headers
1462
    *
1463
    * @return   Array|BOOL
1464
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getHeaders
1465
    * @access   public
1466
    * @see      getParts
1467
    * @see      imap_fetchheader
1468
    * @see      imap_fetchbody
1469
    * @see      imap_headerinfo
1470
    * @see      imap_rfc822_parse_headers
1471
    */
1472
    function getHeaders(&$mid, $pid = '0', $rtn = false, $args = array())
1473
    {
1474
        $this->_checkIfParsed($mid);
1475
 
1476
        if ($pid == $this->msg[$mid]['pid']) {
1477
            $pid = '0';
1478
        }
1479
 
1480
        if ($pid !== '0') {
1481
            if (false === ($raw_headers = $this->getRawHeaders($mid, $pid, true, true))) {
1482
                return false;
1483
            }
1484
 
1485
            if ($raw_headers === true) {
1486
                $pid = '0';
1487
            }
1488
        }
1489
 
1490
        if (!isset($args['from_length'])) {
1491
            $args['from_length'] = 1024;
1492
        }
1493
 
1494
        if (!isset($args['subject_length'])) {
1495
            $args['subject_length'] = 1024;
1496
        }
1497
 
1498
        if (!isset($args['default_host'])) {
1499
            $args['default_host'] = null;
1500
        }
1501
 
1502
        // Parse the headers
1503
        $header_info =
1504
        	($pid === '0')?
1505
                imap_headerinfo($this->mailbox, $mid, $args['from_length'], $args['subject_length'], $args['default_host'])
1506
            :
1507
                imap_rfc822_parse_headers($raw_headers, $args['default_host']);
1508
 
1509
        // Since individual member variable creation might create extra overhead,
1510
        // and having individual variables referencing this data and the original
1511
        // object would be too much as well, we'll just copy the object into an
1512
        // associative array, preform clean-up on those elements that require it,
1513
        // and destroy the original object after copying.
1514
 
1515
        if (!is_object($header_info)) {
1516
            $this->error->push(
1517
                Mail_IMAPv2_ERROR_INVALID_PID,
1518
                'error',
1519
                array(
1520
                    'pid' => $pid
1521
                )
1522
            );
1523
            return false;
1524
        }
1525
 
1526
        $headers = get_object_vars($header_info);
1527
 
1528
        foreach ($headers as $key => $value) {
1529
            if (!is_object($value) && !is_array($value)) {
1530
                // Decode all the headers using utf8_decode(imap_utf8())
1531
                $this->header[$mid][$key] = utf8_decode(imap_utf8($value));
1532
            }
1533
        }
1534
 
1535
        // copy udate or create it from date string.
1536
        $this->header[$mid]['udate'] = (isset($header_info->udate) && !empty($header_info->udate))?
1537
            $header_info->udate
1538
        :
1539
            strtotime($header_info->Date);
1540
 
1541
        // clean up addresses
1542
        $line = array(
1543
        	'from',
1544
        	'reply_to',
1545
       		'sender',
1546
        	'return_path',
1547
        	'to',
1548
        	'cc',
1549
        	'bcc'
1550
        );
1551
 
1552
        for ($i = 0; $i < count($line); $i++) {
1553
            if (isset($header_info->$line[$i])) {
1554
                $this->_parseHeaderLine($mid, $header_info->$line[$i], $line[$i]);
1555
            }
1556
        }
1557
 
1558
        // All possible information has been copied, destroy original object
1559
        unset($header_info);
1560
 
1561
        return ($rtn)? $this->header[$mid] : false;
1562
    }
1563
 
1564
    /**
1565
    * Parse header information from the given line and add it to the {@link $header}
1566
    * array.  This function is only used by {@link getRawHeaders}.
1567
    *
1568
    * @param     string   &$line
1569
    * @param     string   $name
1570
    * @return    array
1571
    * @access    private
1572
    * @tutorial  http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_parseHeaderLine
1573
    */
1574
    function _parseHeaderLine(&$mid, &$line, $name)
1575
    {
1576
        if (isset($line) && count($line) >= 1) {
1577
            $i = 0;
1578
            foreach ($line as $object) {
1579
                if (isset($object->adl)) {
1580
                    $this->header[$mid][$name.'_adl'][$i] = $object->adl;
1581
                }
1582
                if (isset($object->mailbox)) {
1583
                    $this->header[$mid][$name.'_mailbox'][$i] = $object->mailbox;
1584
                }
1585
                if (isset($object->personal)) {
1586
                    $this->header[$mid][$name.'_personal'][$i] = $object->personal;
1587
                }
1588
                if (isset($object->host)) {
1589
                    $this->header[$mid][$name.'_host'][$i] = $object->host;
1590
                }
1591
                if (isset($object->mailbox) && isset($object->host)) {
1592
                    $this->header[$mid][$name][$i] = $object->mailbox.'@'.$object->host;
1593
                }
1594
                $i++;
1595
            }
1596
            // Return the full lines "toaddress", "fromaddress", "ccaddress"... etc
1597
            if (isset(${$name.'address'})) {
1598
                $this->header[$mid][$name.'address'][$i] = ${$name.'address'};
1599
            }
1600
        }
1601
    }
1602
 
1603
    /**
1604
    * Finds and returns a default part id for headers and matches any sub message part to
1605
    * the appropriate headers.  Returns false on failure and may return a value that
1606
    * evaluates to false, use the '===' operator for testing this function's return value.
1607
    *
1608
    * @param    int     &$mid            message id
1609
    * @param    string  $pid             part id
1610
    * @return   string|false
1611
    * @access   private
1612
    * @see      getHeaders
1613
    * @see      getRawHeaders
1614
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_defaultHeaderPid
1615
    */
1616
    function _defaultHeaderPid(&$mid, $pid)
1617
    {
1618
        // pid is modified in this function, so don't pass by reference (will create a logic error)
1619
        $this->_checkIfParsed($mid);
1620
 
1621
        // retrieve key for this part, so that the information may be accessed
1622
        if (false !== ($i = array_search((string) $pid, $this->structure[$mid]['pid']))) {
1623
            // If this part is message/rfc822 display headers for this part
1624
            if ($this->structure[$mid]['ftype'][$i] == 'message/rfc822') {
1625
                $rtn = (string) $pid.'.0';
1626
            } else if ($pid == $this->msg[$mid]['pid']) {
1627
                $rtn = (string) '0';
1628
            } else {
1629
                $pid_len = strlen($pid);
1630
                $this_nesting = count(explode('.', $pid));
1631
 
1632
                // Deeper searching may be required, go back to this part's parent.
1633
                if (!stristr($pid, '.') || ($this_nesting - 1) == 1) {
1634
                    $rtn = (string) '0';
1635
                } else if ($this_nesting > 2) {
1636
                    // Look at previous parts until a message/rfc822 part is found.
1637
                    for ($pos = $this_nesting - 1; $pos > 0; $pos -= 1) {
1638
                        foreach ($this->structure[$mid]['pid'] as $p => $aid) {
1639
                            $nesting = count(explode('.', $this->structure[$mid]['pid'][$p]));
1640
 
1641
                            if (
1642
                            	$nesting == $pos &&
1643
                            	($this->structure[$mid]['ftype'][$p] == 'message/rfc822' || $this->structure[$mid]['ftype'][$p] == 'multipart/related')
1644
                           	) {
1645
                                // Break iteration and return!
1646
                                return (string) $this->structure[$mid]['pid'][$p].'.0';
1647
                            }
1648
                        }
1649
                    }
1650
 
1651
                    $rtn = ($pid_len == 3)? (string) '0' : false;
1652
                } else {
1653
                    $rtn = false;
1654
                }
1655
            }
1656
            return $rtn;
1657
        } else {
1658
            // Something's afoot!
1659
            $this->error->push(
1660
                Mail_IMAPv2_ERROR_INVALID_PID,
1661
                'error',
1662
                array(
1663
                    'pid' => $pid
1664
                )
1665
            );
1666
            return false;
1667
        }
1668
    }
1669
 
1670
    /**
1671
    * Destroys variables set by {@link getHeaders}.
1672
    *
1673
    * @param    int     &$mid            message id
1674
    * @return   void
1675
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/unsetHeaders
1676
    * @access   public
1677
    * @see      getHeaders
1678
    */
1679
    function unsetHeaders(&$mid)
1680
    {
1681
        unset($this->header[$mid]);
1682
        return;
1683
    }
1684
 
1685
    /**
1686
    * Converts an integer containing the number of bytes in a file to one of Bytes, Kilobytes,
1687
    * Megabytes, or Gigabytes, appending the unit of measurement.
1688
    *
1689
    * This method may be called statically.
1690
    *
1691
    * @param    int     $bytes
1692
    * @return   string
1693
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/convertBytes
1694
    * @access   public
1695
    * @static
1696
    */
1697
    function convertBytes($bytes)
1698
    {
1699
        switch (true) {
1700
            case ($bytes < pow(2,10)):
1701
            {
1702
                return $bytes.' Bytes';
1703
            }
1704
            case ($bytes >= pow(2,10) && $bytes < pow(2,20)):
1705
            {
1706
                return round($bytes / pow(2,10), 0).' KB';
1707
            }
1708
            case ($bytes >= pow(2,20) && $bytes < pow(2,30)):
1709
            {
1710
                return round($bytes / pow(2,20), 1).' MB';
1711
            }
1712
            case ($bytes > pow(2,30)):
1713
            {
1714
                return round($bytes / pow(2,30), 2).' GB';
1715
            }
1716
        }
1717
    }
1718
 
1719
    /**
1720
    * Wrapper function for {@link imap_delete}.  Sets the marked for deletion flag.  Note: POP3
1721
    * mailboxes do not remember flag settings between connections, for POP3 mailboxes
1722
    * this function should be used in addtion to {@link expunge}.
1723
    *
1724
    * @param    int     &$mid   message id
1725
    * @return   BOOL
1726
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/delete
1727
    * @access   public
1728
    * @see      imap_delete
1729
    * @see      expunge
1730
    */
1731
    function delete(&$mid, $separator = "<br />\n")
1732
    {
1733
        if (!is_array($mid)) {
1734
            if (!@imap_delete($this->mailbox, $mid)) {
1735
                $this->error->push(
1736
                    Mail_IMAPv2_ERROR,
1737
                    'error',
1738
                    array(
1739
                        'mid' => $mid
1740
                    ),
1741
                    'Unable to mark message for deletion.'
1742
                );
1743
                $rtn = false;
1744
            } else {
1745
                $rtn = true;
1746
            }
1747
        } else {
1748
            foreach ($mid as $id) {
1749
                if (!@imap_delete($this->mailbox, $id)) {
1750
                    $this->error->push(
1751
                        Mail_IMAPv2_ERROR,
1752
                        'error',
1753
                        array(
1754
                            'mid' => $id
1755
                        ),
1756
                        'Unable to mark message for deletion.'
1757
                    );
1758
                    $rtn = false;
1759
                }
1760
            }
1761
            $rtn = true;
1762
        }
1763
 
1764
        return $rtn;
1765
    }
1766
 
1767
    /**
1768
    * Wrapper function for {@link imap_expunge}.  Expunges messages marked for deletion.
1769
    *
1770
    * @return   BOOL
1771
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/expunge
1772
    * @access   public
1773
    * @see      imap_expunge
1774
    * @see      delete
1775
    */
1776
    function expunge()
1777
    {
1778
        if (imap_expunge($this->mailbox)) {
1779
            return true;
1780
        } else {
1781
            $this->error->push(Mail_IMAPv2_ERROR, 'error', null, 'Unable to expunge mailbox.');
1782
            return false;
1783
        }
1784
    }
1785
 
1786
    /**
1787
    * Wrapper function for {@link imap_errors}.  Implodes the array returned by imap_errors,
1788
    * (if any) and returns the error text.
1789
    *
1790
    * @param    bool      $handler
1791
    *   How to handle the imap error stack, true by default. If true adds the errors
1792
    *   to the PEAR_ErrorStack object. If false, returns the imap error stack.
1793
    *
1794
    * @param    string    $seperator
1795
    *   (optional) Characters to seperate each error message. "<br />\n" by default.
1796
    *
1797
    * @return   bool|string
1798
    * @access   public
1799
    * @see      imap_errors
1800
    * @see      alerts
1801
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/errors
1802
    */
1803
    function errors($handler = true, $seperator = "<br />\n")
1804
    {
1805
        $errors = imap_errors();
1806
 
1807
        if (empty($errors)) {
1808
            return false;
1809
        }
1810
 
1811
        if ($handler) {
1812
            foreach ($errors as $error) {
1813
                $this->error->push(
1814
                    Mail_IMAPv2_ERROR,
1815
                    'error',
1816
                    null,
1817
                    $error
1818
                );
1819
            }
1820
            return true;
1821
        }
1822
        return implode($seperator, $errors);
1823
    }
1824
 
1825
    /**
1826
    * Wrapper function for {@link imap_alerts}.  Implodes the array returned by imap_alerts,
1827
    * (if any) and returns the text.
1828
    *
1829
    * @param    bool      $handler
1830
    *   How to handle the imap error stack, true by default. If true adds the alerts
1831
    *   to the PEAR_ErrorStack object. If false, returns the imap alert stack.
1832
    *
1833
    * @param    string    $seperator     Characters to seperate each alert message. '<br />\n' by default.
1834
    * @return   bool|string
1835
    * @access   public
1836
    * @see      imap_alerts
1837
    * @see      errors
1838
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/alerts
1839
    */
1840
    function alerts($handler = true, $seperator = "<br />\n")
1841
    {
1842
        $alerts = imap_alerts();
1843
 
1844
        if (empty($alerts)) {
1845
            return false;
1846
        }
1847
 
1848
        if ($handler) {
1849
            foreach ($alerts as $alert) {
1850
                $this->error->push(
1851
                    Mail_IMAPv2_ERROR,
1852
                    'notice',
1853
                    null,
1854
                    $alert
1855
                );
1856
            }
1857
            return true;
1858
        }
1859
        return implode($seperator, $alerts);
1860
    }
1861
 
1862
    /**
1863
    * Retreives information about the current mailbox's quota.  Rounds up quota sizes and
1864
    * appends the unit of measurment.  Returns information in a multi-dimensional associative
1865
    * array.
1866
    *
1867
    * @param    string   $folder    Folder to retrieve quota for.
1868
    * @param    BOOL     $rtn
1869
    *   (optional) true by default, if true return the quota if false merge quota
1870
    *   information into the $mailboxInfo member variable.
1871
    * @return   array|false
1872
    * @access   public
1873
    * @see      imap_get_quotaroot
1874
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getQuota
1875
    */
1876
    function getQuota($folder = null, $rtn = true)
1877
    {
1878
        if (empty($folder) && !isset($this->mailboxInfo['folder'])) {
1879
            $folder = 'INBOX';
1880
        } else if (empty($folder) && isset($this->mailboxInfo['folder'])) {
1881
            $folder = $this->mailboxInfo['folder'];
1882
        }
1883
 
1884
        $q = @imap_get_quotaroot($this->mailbox, $folder);
1885
 
1886
        // STORAGE Values are returned in KB
1887
        // Convert back to bytes first
1888
        // Then round these to the simpliest unit of measurement
1889
        if (isset($q['STORAGE']['usage']) && isset($q['STORAGE']['limit'])) {
1890
            $q['STORAGE']['usage'] = $this->convertBytes($q['STORAGE']['usage'] * 1024);
1891
            $q['STORAGE']['limit'] = $this->convertBytes($q['STORAGE']['limit'] * 1024);
1892
        }
1893
 
1894
        if (isset($q['MESSAGE']['usage']) && isset($q['MESSAGE']['limit'])) {
1895
            $q['MESSAGE']['usage'] = $this->convertBytes($q['MESSAGE']['usage']);
1896
            $q['MESSAGE']['limit'] = $this->convertBytes($q['MESSAGE']['limit']);
1897
        }
1898
 
1899
        if (empty($q['STORAGE']['usage']) && empty($q['STORAGE']['limit'])) {
1900
            $this->error->push(
1901
                Mail_IMAPv2_ERROR,
1902
                'error',
1903
                null,
1904
                'Quota not available for this server.'
1905
            );
1906
            return false;
1907
        } else if ($rtn) {
1908
            return $q;
1909
        } else {
1910
            $this->mailboxInfo = array_merge($this->mailboxInfo, $q);
1911
            return true;
1912
        }
1913
    }
1914
 
1915
    /**
1916
    * Wrapper function for {@link imap_setflag_full}.  Sets various message flags.
1917
    * Accepts an array of message ids and an array of flags to be set.
1918
    *
1919
    * The flags which you can set are "\\Seen", "\\Answered", "\\Flagged",
1920
    * "\\Deleted", and "\\Draft" (as defined by RFC2060).
1921
    *
1922
    * Warning: POP3 mailboxes do not remember flag settings from connection to connection.
1923
    *
1924
    * @param    array  $mids        Array of message ids to set flags on.
1925
    * @param    array  $flags       Array of flags to set on messages.
1926
    * @param    int    $action      Flag operation toggle one of set|clear
1927
    * @param    int    $options
1928
    *   (optional) sets the forth argument of {@link imap_setflag_full} or {@imap_clearflag_full}.
1929
    *
1930
    * @return   BOOL
1931
    * @throws   Message IDs and Flags are to be supplied as arrays.  Remedy: place message ids
1932
    *           and flags in arrays.
1933
    * @access   public
1934
    * @see      imap_setflag_full
1935
    * @see      imap_clearflag_full
1936
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/setFlags
1937
    */
1938
    function setFlags($mids, $flags, $action = 'set')
1939
    {
1940
        if (!is_array($mids)) {
1941
            $this->error->push(
1942
                Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY,
1943
                'error',
1944
                array(
1945
                    'arg' => '$mids'
1946
                )
1947
            );
1948
            return false;
1949
        }
1950
 
1951
        if (!is_array($flags)) {
1952
            $this->error->push(
1953
                Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY,
1954
                'error',
1955
                array(
1956
                    'arg' => '$flags'
1957
                )
1958
            );
1959
            return false;
1960
        }
1961
 
1962
        switch ($action) {
1963
            case 'set':
1964
            {
1965
                $func = 'imap_setflag_full';
1966
                break;
1967
            }
1968
            case 'clear':
1969
            {
1970
                $func = 'imap_clearflag_full';
1971
                break;
1972
            }
1973
            default:
1974
            {
1975
                $this->error->push(
1976
                    Mail_IMAPv2_ERROR_INVALID_ACTION,
1977
                    'error',
1978
                    array(
1979
                        'action' => $action,
1980
                        'arg' => '$action'
1981
                    )
1982
                );
1983
                return false;
1984
            }
1985
        }
1986
 
1987
        $opt =
1988
        	(isset($this->option[$action.'flag_full']))?
1989
            	$this->option[$action.'flag_full']
1990
        	:
1991
            	null;
1992
 
1993
        return @$func($this->mailbox, implode(',', $mids), implode(' ', $flags), $opt);
1994
    }
1995
 
1996
    /**
1997
    * Wrapper method for imap_list.  Calling on this function will return a list of mailboxes.
1998
    * This method receives the host argument automatically via $this->connect in the
1999
    * $this->mailboxInfo['host'] variable if a connection URI is used.
2000
    *
2001
    * @param    string  (optional) host name.
2002
    * @return   array|false   list of mailboxes on the current server.
2003
    * @access   public
2004
    * @see      imap_list
2005
    * @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getMailboxes
2006
    */
2007
    function getMailboxes($host = null, $pattern = '*', $rtn = true)
2008
    {
2009
        if (empty($host) && !isset($this->mailboxInfo['host'])) {
2010
            $this->error->push(
2011
                Mail_IMAPv2_ERROR,
2012
                'error',
2013
                null,
2014
                'Supplied host is not valid!'
2015
            );
2016
            return false;
2017
        } else if (empty($host) && isset($this->mailboxInfo['host'])) {
2018
            $host = $this->mailboxInfo['host'];
2019
        }
2020
 
2021
        if ($list = @imap_list($this->mailbox, $host, $pattern)) {
2022
            if (is_array($list)) {
2023
                foreach ($list as $key => $val) {
2024
                   $mb[$key] = str_replace($host, '', imap_utf7_decode($val));
2025
                }
2026
            }
2027
        } else {
2028
            $this->error->push(Mail_IMAPv2_ERROR, 'error', null, 'Cannot fetch mailbox names.');
2029
            return false;
2030
        }
2031
 
2032
        if ($rtn) {
2033
           return $mb;
2034
        } else {
2035
            $this->mailboxInfo = array_merge($this->mailboxInfo, $mb);
2036
        }
2037
    }
2038
}
2039
?>