Mail_IMAPv2
[ class tree: Mail_IMAPv2 ] [ index: Mail_IMAPv2 ] [ all elements ]

Source for file IMAPv2.php

Documentation is available at IMAPv2.php

  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->anchor03))'' '/ssl';
  339.  
  340.         $uri .= ('s' == (substr($net_url->protocol-1)))'/'.substr($net_url->protocol04).$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->path1(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($uriurldecode($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)][$nstrtolower($param->value);
  554.                         }
  555.                     }
  556.  
  557.                     // Force inline disposition if none is present
  558.                     if ($parts[$p]->ifdisposition{
  559.                         $this->structure[$mid]['disposition'][$nstrtolower($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]$ntrue$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'][0strtolower($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($midtrue$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']01== substr($this->structure[$mid]['pid'][$p]01));
  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]0strlen($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($midarray('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($pid0-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($rpid0-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($rpid01== $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($midtrue$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($midfalse);
  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$pidtruetrue))) {
  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][$keyutf8_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->mailboximplode(','$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[$keystr_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. ?>

Documentation generated on Mon, 11 Mar 2019 15:40:08 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.