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

Source for file IMAP.php

Documentation is available at IMAP.php

  1. <?php
  2.  
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 3.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available at through the world-wide-web at                           |
  11. // | http://www.php.net/license/3_0.txt                                   |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Richard York <richy@smilingsouls.net>                        |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id
  20.  
  21. require_once 'PEAR.php';
  22.  
  23. // {{{ constants
  24. // {{{ Mail_IMAP::getBody action options
  25. define('MAIL_IMAP_BODY'0);
  26. define('MAIL_IMAP_LITERAL'1);
  27. define('MAIL_IMAP_LITERAL_DECODE'2);
  28. define('MAIL_IMAP_SET_FLAGS'3);
  29. define('MAIL_IMAP_CLEAR_FLAGS'4);
  30. // }}}
  31. // }}}
  32.  
  33. /**
  34. * Mail_IMAP provides a simplified backend for working with the c-client (IMAP) extension.
  35. * It serves as an OO wrapper for commonly used c-client functions.
  36. * It provides structure and header parsing as well as body retrieval.
  37. *
  38. * This package requires the c-client extension.  To download the latest version of
  39. * the c-client extension goto: http://www.php.net/imap.
  40. *
  41. * PEAR PREREQUISITES:
  42. *   Net_URL (Required if you will be using a URI abstraction to connect.)
  43. *
  44. * Known Bugs:
  45. *   Potential bugs may arise from the detection of certain multipart/* messages.
  46. *   Application parses and adjusts for anomalies in multipart/mixed,
  47. *   multipart/related and multipart/alternative messages.  Bugs may arise from
  48. *   untested and unincluded multipart message types, multipart/parallel,
  49. *   multipart/report, multipart/signed and multipart/digest.  If you happen
  50. *   across a problem related to one of the untested message types, please mail a
  51. *   copy of the message (if possible) to demo@smilingsouls.net where I may
  52. *   analyze the message structure and include it in the class.  Be sure to include
  53. *   Mail_IMAP (multipart/*) in the subject line so that I know the message isn't
  54. *   SPAM and don't delete it.
  55. *
  56. * Having trouble finding the example files?
  57. *   Examples are located in the PEAR install directory under /docs/Mail_IMAP/examples
  58. *
  59. * Extended documentation available at:
  60. *   http://www.spicypeanut.net
  61. *
  62. @author       Richard York <richy@smilingsouls.net>
  63. @category     Mail
  64. @package      Mail_IMAP
  65. @license      PHP
  66. @version      1.0.0RC3
  67. @copyright    (c) Copyright 2004, Richard York, All Rights Reserved.
  68. @since        PHP 4.2.0
  69. *
  70. @example      examples/IMAP.inbox.php
  71. *    Mail_IMAP Inbox
  72. *
  73. @example      examples/IMAP.message.php
  74. *    Mail_IMAP Message
  75. *
  76. @example      examples/IMAP.connection_wizard.php
  77. *    Mail_IMAP Connection Wizard
  78. *
  79. @example      examples/IMAP.connection_wizard_example.php
  80. *    Mail_IMAP Connection Wizard
  81. *
  82. @todo imap_list
  83. @todo imap_mail_copy
  84. @todo imap_mail_move
  85. */
  86.  
  87. // {{{ class Mail_IMAP
  88. class Mail_IMAP {
  89.  
  90.     // {{{ properties
  91.     /**
  92.      * Contains the imap resource stream.
  93.      * @var     resource $mailbox 
  94.      * @access  public
  95.      */
  96.     var $mailbox;
  97.  
  98.     /**
  99.      * Contains information about the current mailbox.
  100.      * @var     array $mailboxInfo 
  101.      * @access  public
  102.      */
  103.     var $mailboxInfo;
  104.  
  105.     /**
  106.      * (string) contains the various possible data types.
  107.      * @var     array $_dataTypes 
  108.      * @access  private
  109.      */
  110.     var $_dataTypes = array('text''multipart''message''application''audio''image''video''other');
  111.  
  112.     /**
  113.      * (string) Contains the various possible encoding types.
  114.      * @var     array $_encodingTypes 
  115.      * @access  private
  116.      */
  117.     var $_encodingTypes = array('7bit''8bit''binary''base64''quoted-printable''other');
  118.  
  119.     // --------------------------ALL MESSAGE PARTS-----------------------------
  120.     /**
  121.      * (object) Contains the object returned by {@link imap_fetchstructure}.
  122.      * @var     array $_structure 
  123.      * @access  private
  124.      */
  125.     var $_structure;
  126.  
  127.     /**
  128.      * (str) Contains all of the part ids for a given message.
  129.      * @var     array $_pid 
  130.      * @access  private
  131.      */
  132.     var $_pid;
  133.  
  134.     /**
  135.      * (string) Contains all of the part mime types for a given message.
  136.      * @var     array $_ftype 
  137.      * @access  private
  138.      */
  139.     var $_ftype;
  140.  
  141.     /**
  142.      * (int) Contains the file size in bytes for message parts.
  143.      * @var     array $_fsize 
  144.      * @access  private
  145.      */
  146.     var $_fsize;
  147.  
  148.     /**
  149.      * (str) Contains the original file name for a message part (if any).
  150.      * @var     array $_fname 
  151.      * @access  private
  152.      */
  153.     var $_fname;
  154.  
  155.     /**
  156.      * (string) Contains the part disposition inline | attachment.
  157.      * @var     array $_disposition 
  158.      * @access  private
  159.      */
  160.     var $_disposition;
  161.  
  162.     /**
  163.      * (str) Contains the part encoding.
  164.      * @var     array $_encoding 
  165.      * @access  private
  166.      */
  167.     var $_encoding;
  168.  
  169.     /**
  170.      * (str) Contains the part id for multipart/related (if any).
  171.      * @var     array $_inlineId 
  172.      * @access  private
  173.      */
  174.     var $_inlineId;
  175.  
  176.     /**
  177.      * (bool) Determines whether the current part has attachments.
  178.      * @var     array $_hasAttachments 
  179.      * @access  private
  180.      */
  181.     var $_hasAttachments;
  182.  
  183.     // --------------------------INLINE MESSAGE PARTS-----------------------------
  184.     /**
  185.      * (str) Inline part id.
  186.      * @var     array $inPid 
  187.      * @access  public
  188.      */
  189.     var $inPid;
  190.  
  191.     /**
  192.      * (str) Inline part MIME type.
  193.      * @var     array $inFtype 
  194.      * @access  public
  195.      */
  196.     var $inFtype;
  197.  
  198.     /**
  199.      * (int) Inline file size of the part in bytes.
  200.      * @var     array $inFsize 
  201.      * @access  public
  202.      */
  203.     var $inFsize;
  204.  
  205.     /**
  206.      * (int) Inline file name of the part, if any.
  207.      * @var     array $inFname 
  208.      * @access  public
  209.      */
  210.     var $inFname;
  211.  
  212.     /**
  213.      * (bool) Inline part has attachments?
  214.      * @var     array $inHasAttach 
  215.      * @access  public
  216.      */
  217.     var $inHasAttach;
  218.  
  219.     /**
  220.      * (str) Inline CID for multipart/related.
  221.      * @var     array $inInlineId 
  222.      * @access  public
  223.      */
  224.     var $inInlineId;
  225.  
  226.     // --------------------------ATTACHMENT MESSAGE PARTS-----------------------------
  227.     /**
  228.      * (str) Attachment part id.
  229.      * @var     array $attachPid 
  230.      * @access  public
  231.      */
  232.     var $attachPid;
  233.  
  234.     /**
  235.      * (str) Attachment MIME type.
  236.      * @var     array $attachFtype 
  237.      * @access  public
  238.      */
  239.     var $attachFtype;
  240.  
  241.     /**
  242.      * (int) Attachment file size in bytes.
  243.      * @var     array $attachFsize 
  244.      * @access  public
  245.      */
  246.     var $attachFsize;
  247.  
  248.     /**
  249.      * (str) Attachment original file name (if any, if not file name is empty string).
  250.      * @var     array $attachFname 
  251.      * @access  public
  252.      */
  253.     var $attachFname;
  254.  
  255.     /**
  256.      * (bool) Attachment has attachments?
  257.      * @var     array $attachHasAttach 
  258.      * @access  public
  259.      */
  260.     var $attachHasAttach;
  261.  
  262.     // -------------------------- MESSAGE HEADERS -----------------------------
  263.     /**
  264.      * (str) Contains raw message headers fetched from {@link imap_fetchbody}
  265.      * or {@link imap_fetchheader} depending on which message part is being retrieved.
  266.      * @var     array $rawHeaders 
  267.      * @access  public
  268.      */
  269.     var $rawHeaders;
  270.  
  271.     /**
  272.      * (array)(mixed) Associative array containing information gathered by {@link imap_headerinfo}
  273.      * or {@link imap_rfc822_parse_headers}.
  274.      * @var    header array $header
  275.      */
  276.     var $header;
  277.     // }}}
  278.  
  279.     // {{{ constructor
  280.     /**
  281.     * Constructor. Optionally set the IMAP resource stream.
  282.     *
  283.     * If IMAP connection arguments are not supplied, returns NULL.  Accepts
  284.     * a URI abstraction of the standard imap_open connection argument
  285.     * (see {@link connect}) or the imap resource indicator.
  286.     *
  287.     * @param     string         $connect  (optional) server URL to connect to
  288.     * @param     int            $options  (optional) options see imap_open
  289.     * @access    public
  290.     * @return    BOOL|NULL|PEAR_Error
  291.     * @see       connect
  292.     * @see       imap_open
  293.     */
  294.     function Mail_IMAP($connection = NULL$options = NULL)
  295.     {
  296.         if (is_resource($connection)) {
  297.             if (get_resource_type($connection== 'imap'{
  298.                 $this->mailbox = $connection;
  299.                 return TRUE;
  300.             else {
  301.                 return PEAR::raiseError('Error: Supplied resource is not a valid IMAP stream.');
  302.             }
  303.         else {
  304.             return ($connection == NULL)? NULL : Mail_IMAP::connect($connection$options);
  305.         }
  306.     }
  307.     // }}}
  308.  
  309.     // {{{ connect()
  310.     /**
  311.     * Wrapper method for {@link imap_open}.  Accepts a URI abstraction in
  312.     * the following format: imap://user:pass@mail.example.com:143/INBOX#notls
  313.     * instead of the standard connection arguments used in imap_open.
  314.     * Replace the protocol with one of pop3|pop3s imap|imaps nntp|nntps.
  315.     * Place intial folder in the file path portion, and optionally append
  316.     * tls|notls|novalidate-cert in the anchor portion of the URL.  A port
  317.     * number is optional, however, leaving it off could lead to a serious
  318.     * degradation in preformance.
  319.     *
  320.     * Examples of a well-formed connection argument:
  321.     *
  322.     * For IMAP:        imap://user:pass@mail.example.com:143/INBOX
  323.     *
  324.     * For IMAP SSL:    imaps://user:pass@example.com:993/INBOX
  325.     *
  326.     * For POP3:        pop3://user:pass@mail.example.com:110/INBOX
  327.     *
  328.     * For POP3 SSL:    pop3s://user:pass@mail.example.com:993/INBOX
  329.     *
  330.     * For NNTP:        nntp://user:pass@mail.example.com:119/comp.test
  331.     *
  332.     * For 'notls' OR 'novalidate-cert' append to the URL as an anchor.
  333.     * For 'tls' use secure protocol and add the 'tls' option to the anchor.
  334.     *
  335.     * Examples:
  336.     *
  337.     * For notls:       imap://user:pass@mail.example.com:143/INBOX#notls
  338.     *
  339.     * For tls:         imaps://user:pass@mail.example.com:143/INBOX#tls
  340.     *
  341.     * tls no-validate: imaps://user:pass@mail.example.com:143/INBOX#tls/novalidate-cert
  342.     *
  343.     * ssl no-validate: imaps://user:pass@mail.example.com:143/INBOX#novalidate-cert
  344.     *
  345.     * If the username is an email address or contains invalid URL characters,
  346.     * urlencode the username portion of the string before passing it.
  347.     *
  348.     * Use the IMAP.connection_wizard_example.php file to automatically detect
  349.     * the correct URI to pass to this function.  This file is located in the
  350.     * examples directory.
  351.     *
  352.     * @param    string           $connect   server URL
  353.     * @param    int              (optional) options
  354.     * @return   PEAR_Error|TRUE
  355.     * @access   public
  356.     * @see      imap_open
  357.     */
  358.     function connect($connect$options = NULL)
  359.     {
  360.         if (!class_exists('Net_URL')) {
  361.             require_once 'Net/URL.php';
  362.         }
  363.  
  364.         $url =new Net_URL($connect);
  365.  
  366.         $connect  '{'.$url->host;
  367.  
  368.         if (!empty($url->port)) {
  369.             $connect .= ':'.$url->port;
  370.         }
  371.  
  372.         $secure   ('tls' == substr($url->anchor03))'' '/ssl';
  373.         $connect .= ('s' == (substr($url->protocol-1)))'/'.substr($url->protocol04).$secure '/'.$url->protocol;
  374.  
  375.         if (!empty($url->anchor)) {
  376.             $connect .= '/'.$url->anchor;
  377.         }
  378.  
  379.         $connect .= '}';
  380.  
  381.         // Trim off the leading slash '/'
  382.         if (!empty($url->path)) {
  383.             $connect .= substr($url->path1(strlen($url->path- 1));
  384.         }
  385.  
  386.         $this->mailboxInfo['user']   urldecode($url->user);
  387.         $this->mailboxInfo['folder'$url->path;
  388.  
  389.         if ($options == NULL{
  390.             return (FALSE === ($this->mailbox = @imap_open($connecturldecode($url->user)$url->pass)))? PEAR::raiseError('Mail_IMAP::connect, unable to build a connection to the specified mail server.': TRUE;
  391.         else {
  392.             return (FALSE === ($this->mailbox = @imap_open($connecturldecode($url->user)$url->pass$options)))? PEAR::raiseError('Mail_IMAP::connect, unable to build a connection to the specified mail server.': TRUE;
  393.         }
  394.     }
  395.     // }}}
  396.  
  397.     // {{{ close()
  398.     /**
  399.     * Wrapper method for {@link imap_close}.  Close the IMAP resource stream.
  400.     *
  401.     * @param    int           $options    (optional) sets the second argument of imap_close
  402.     * @return   PEAR_Error|TRUE
  403.     * @access   public
  404.     * @see      imap_close
  405.     */
  406.     function close($options = NULL)
  407.     {
  408.         if ($options == NULL{
  409.              return (@imap_close($this->mailbox))? TRUE : PEAR::raiseError('Mail_IMAP::close, unable to close the connection to the mail server.');
  410.         else {
  411.              return (@imap_close($this->mailbox$options))? TRUE : PEAR::raiseError('Mail_IMAP::close, unable to close the connection to the mail server.');
  412.         }
  413.     }
  414.     // }}}
  415.  
  416.     // {{{ messageCount()
  417.     /**
  418.     * Wrapper method for {@link imap_num_msg}.  Calling on this function will reset the IMAP error
  419.     * stack (if mailbox is empty). {@link imap_errors} is called to supress a NOTICE level error,
  420.     * mailbox is empty, which isn't an error state.  For debugging, comment out the call to
  421.     * imap_errors.
  422.     *
  423.     * @return   int mailbox message count
  424.     * @access   public
  425.     * @see      imap_num_msg
  426.     */
  427.     function messageCount()
  428.     {
  429.         if (!$message_count @imap_num_msg($this->mailbox)) {
  430.             $errors = imap_errors();  // Gets rid of the annoying mailbox is empty error!
  431.             $message_count "0";
  432.         }
  433.  
  434.         return $message_count;
  435.     }
  436.     // }}}
  437.  
  438.     // {{{ _declareParts()
  439.     /**
  440.     * Gather message information returned by {@link imap_fetchstructure} and recursively iterate
  441.     * through each parts array.  Concatenate part numbers in the following format `1.1`
  442.     * each part id is separated by a period, each referring to a part or subpart of a
  443.     * multipart message.  Create part numbers as such that they are compatible with
  444.     * {@link imap_fetchbody}.
  445.     *
  446.     * @param    int           &$mid         message id
  447.     * @param    array         $sub_part     recursive
  448.     * @param    string        $sub_pid      recursive parent part id
  449.     * @param    int           $n            recursive counter
  450.     * @param    bool          $is_sub_part  recursive
  451.     * @param    bool          $skip_part    recursive
  452.     * @return   mixed 
  453.     * @access   private
  454.     * @see      imap_fetchstructure
  455.     * @see      imap_fetchbody
  456.     */
  457.     function _declareParts(&$mid$sub_part = NULL$sub_pid = NULL$n = 0$is_sub_part = FALSE$skip_part = FALSE)
  458.     {
  459.         if (!is_array($sub_part)) {
  460.             $this->_structure[$mid= imap_fetchstructure($this->mailbox$mid);
  461.         }
  462.  
  463.         if (isset($this->_structure[$mid]->parts|| is_array($sub_part)) {
  464.  
  465.             if ($is_sub_part == FALSE{
  466.                 $parts $this->_structure[$mid]->parts;
  467.             else {
  468.                 $parts $sub_part;
  469.                 $n++;
  470.             }
  471.  
  472.             for ($p = 0$i = 1; $p count($parts)$n++$p++$i++{
  473.                 // Skip the following...
  474.                 // multipart/mixed!
  475.                 // subsequent multipart/alternative if this part is message/rfc822
  476.                 // multipart/related
  477.                 //
  478.                 // Have noticed the existence of serveral other multipart/* types of messages
  479.                 // but have yet had the opportunity to test on those.
  480.                 $ftype        (empty($parts[$p]->type))?    $this->_dataTypes[0].'/'.strtolower($parts[$p]->subtype$this->_dataTypes[$parts[$p]->type].'/'.strtolower($parts[$p]->subtype);
  481.                 $skip_next    ($ftype == 'message/rfc822')? TRUE : FALSE;
  482.  
  483.                 if ($ftype == 'multipart/mixed' || $skip_part == TRUE && $ftype == 'multipart/alternative' || $ftype == 'multipart/related'{
  484.                     $n--;
  485.                     $skipped = TRUE;
  486.                 else {
  487.  
  488.                     $skipped = FALSE;
  489.  
  490.                     $this->_pid[$mid][$n($is_sub_part == FALSE)? (string) "$i" : (string) "$sub_pid.$i";
  491.  
  492.                     $this->_ftype[$mid][$n]     $ftype;
  493.                     $this->_encoding[$mid][$n]  (empty($parts[$p]->encoding))$this->_encodingTypes[0$this->_encodingTypes[$parts[$p]->encoding];
  494.                     $this->_fsize[$mid][$n]     (!isset($parts[$p]->bytes|| empty($parts[$p]->bytes))? 0 : $parts[$p]->bytes;
  495.  
  496.                     // Force inline disposition if none is present
  497.                     if ($parts[$p]->ifdisposition == TRUE{
  498.  
  499.                         $this->_disposition[$mid][$nstrtolower($parts[$p]->disposition);
  500.  
  501.                         if ($parts[$p]->ifdparameters == TRUE{
  502.  
  503.                             $params $parts[$p]->dparameters;
  504.  
  505.                             foreach ($params as $param{
  506.  
  507.                                 if (strtolower($param->attribute== 'filename'{
  508.  
  509.                                     $this->_fname[$mid][$n$param->value;
  510.                                     break;
  511.  
  512.                                 }
  513.                             }
  514.                         }
  515.  
  516.                     else {
  517.                         $this->_disposition[$mid][$n'inline';
  518.                     }
  519.  
  520.                     if ($parts[$p]->ifid == TRUE{
  521.                         $this->inline_id[$mid][$n$parts[$p]->id;
  522.                     }
  523.                 }
  524.  
  525.                 if (isset($parts[$p]->parts&& is_array($parts[$p]->parts)) {
  526.                     if ($skipped == FALSE{
  527.                         $this->_hasAttachments[$mid][$n= TRUE;
  528.                     }
  529.  
  530.                     $n Mail_IMAP::_declareParts($mid$parts[$p]->parts$this->_pid[$mid][$n]$nTRUE$skip_next);
  531.  
  532.                 else if ($skipped == FALSE{
  533.                     $this->_hasAttachments[$mid][$n= FALSE;
  534.                 }
  535.             }
  536.  
  537.             if ($is_sub_part == TRUE{
  538.                 return $n;
  539.             }
  540.  
  541.          else {
  542.  
  543.              // $parts is not an array... message is flat
  544.             $this->_pid[$mid][0= 1;
  545.  
  546.             if (empty($this->_structure[$mid]->type)) {
  547.                 $this->_structure[$mid]->type        = (int) 0;
  548.             }
  549.  
  550.             if (isset($this->_structure[$mid]->subtype)) {
  551.                 $this->_ftype[$mid][0]               $this->_dataTypes[$this->_structure[$mid]->type].'/'.strtolower($this->_structure[$mid]->subtype);
  552.             }
  553.  
  554.             if (empty($this->_structure[$mid]->encoding)) {
  555.                 $this->_structure[$mid]->encoding    = (int) 0;
  556.             }
  557.  
  558.             $this->_encoding[$mid][0]                $this->_encodingTypes[$this->_structure[$mid]->encoding];
  559.  
  560.             if (isset($this->_structure[$mid]->bytes)) {
  561.                 $this->_fsize[$mid][0]               strtolower($this->_structure[$mid]->bytes);
  562.             }
  563.  
  564.             $this->_disposition[$mid][0]             'inline';
  565.             $this->_hasAttachments[$mid][0]          = FALSE;
  566.         }
  567.  
  568.         return;
  569.     }
  570.     // }}}
  571.  
  572.     // {{{ _checkIfParsed()
  573.     /**
  574.     * Checks if the part has been parsed, if not calls on _declareParts to
  575.     * parse the message.
  576.     *
  577.     * @param    int          &$mid         message id
  578.     * @return   void 
  579.     * @access   private
  580.     */
  581.     function _checkIfParsed(&$mid)
  582.     {
  583.         if (!isset($this->_pid[$mid])) {
  584.            Mail_IMAP::_declareParts($mid);
  585.         }
  586.         return;
  587.     }
  588.     // }}}
  589.  
  590.     // {{{ getParts()
  591.     /**
  592.     * sets up member variables containing inline parts and attachments for a specific part
  593.     * in member variable arrays beginning with 'in' and 'attach'.
  594.     * If inline parts are present, sets {@link $inPid}{@link $inFtype}{@link $inFsize},
  595.     * {@link $inHasAttach}{@link $inInlineId} (if an inline CID is specified).
  596.     * If attachments are present, sets, {@link $attachPid}{@link $attachFsize}{@link $attachHasAttach},
  597.     * {@link $attachFname} (if a filename is present, empty string otherwise).
  598.     *
  599.     * Typically the text/html part is displayed by default by a message viewer, this part is
  600.     * excluded from the inline member variable arrays thourgh $excludeMime by default.  If
  601.     * $getInline is TRUE the text/plain alternative part will be returned in the inline array
  602.     * and may be included as an attachment.  Useful for mail developement/debugging of multipart
  603.     * messages.
  604.     *
  605.     * @param    int           &$mid         message id
  606.     * @param    int           &$pid         part id
  607.     * @param    string        $MIME 
  608.     *        (optional) values: text/plain|text/html, the part MIME type that will be
  609.     *        retrieved by default.
  610.     *
  611.     * @param    bool          $getAlternative 
  612.     *        (optional) include the plain text alternative part in the created inline parts
  613.     *        array.
  614.     *
  615.     * @return   bool 
  616.     * @access   public
  617.     * @since    PHP 4.2.0
  618.     */
  619.     function getParts(&$mid&$pid$MIME 'text/html'$getAlternative = TRUE)
  620.     {
  621.         Mail_IMAP::_checkIfParsed($mid);
  622.  
  623.         if (count($this->_pid[$mid]== 1{
  624.             return TRUE;
  625.         }
  626.  
  627.         // retrieve key for this part, so that the information may be accessed
  628.         if (FALSE !== ($i array_search((string) $pid$this->_pid[$mid]))) {
  629.  
  630.             if ($pid == Mail_IMAP::getDefaultPid($mid)) {
  631.  
  632.                 Mail_IMAP::_scanMultipart($mid$pid$i$MIME'add''top'2$getAlternative);
  633.  
  634.             else if ($this->_ftype[$mid][$i== 'message/rfc822'{
  635.  
  636.                 Mail_IMAP::_scanMultipart($mid$pid$i$MIME'add''all'1$getAlternative);
  637.  
  638.             }
  639.  
  640.         else {
  641.             PEAR::raiseError('Mail_IMAP::getParts was unable to retrieve a valid part id from the pid passed.'nullPEAR_ERROR_TRIGGERE_USER_WARNING'mid: '.$mid.' pid: '.$pid);
  642.             return FALSE;
  643.         }
  644.  
  645.         return TRUE;
  646.     }
  647.     // }}}
  648.  
  649.     function _scanMultipart(&$mid&$pid&$i$MIME$action 'add'$lookAt 'all'$pidAdd = 1$getAlternative = TRUE)
  650.     {
  651.         // Find subparts, create variables
  652.         // Create inline parts first, and attachments second
  653.  
  654.         // Get all top level parts, with the exception of the part currently being viewed
  655.         // If top level part contains multipart/alternative go into that subpart to
  656.         // retrieve the other inline message part to display
  657.  
  658.         // If this part is message/rfc822 get subparts that begin with this part id
  659.         // Skip multipart/alternative message part
  660.         // Find the displayable message, get plain/text part if $getInline is TRUE
  661.  
  662.         if ($action == 'add'{
  663.  
  664.            $excludeMIME $MIME;
  665.            $MIME        ($excludeMIME == 'text/plain')'text/html' 'text/plain';
  666.            $in          = 0;
  667.            $a           = 0;
  668.  
  669.         else if ($action == 'get'{
  670.  
  671.            $excludeMIME = NULL;
  672.  
  673.         }
  674.  
  675.         $pid_len strlen($pid);
  676.         $this_nesting count(explode('.'$pid));
  677.  
  678.         foreach ($this->_pid[$midas $p => $id{
  679.  
  680.             // To look at the next level of nesting one needs to determine at which level
  681.             // of nesting the program currently resides, this needs to be independent of the
  682.             // part id length, since part ids can get into double digits (let's hope they
  683.             // don't get into triple digits!)
  684.  
  685.             // To accomplish this we'll explode the part id on the dot to get a count of the
  686.             // nesting, then compare the string with the next level in.
  687.  
  688.             $nesting count(explode('.'$this->_pid[$mid][$p]));
  689.  
  690.             switch ($lookAt{
  691.  
  692.                 case 'all':       $condition (($nesting == ($this_nesting + 1)) && $pid == substr($this->_pid[$mid][$p]0$pid_len)); break;
  693.                 case 'multipart'$condition (($nesting == ($this_nesting + 1)) && ($pid == substr($this->_pid[$mid][$p]0)));         break;
  694.  
  695.                 // To gaurantee a top-level part, detect whether a period appears in the pid string
  696.                 default:          $condition (!stristr($this->_pid[$mid][$p]'.'));
  697.             }
  698.  
  699.             if ($condition == TRUE{
  700.  
  701.                 if ($this->_ftype[$mid][$p== 'multipart/alternative'{
  702.  
  703.                     foreach ($this->_pid[$midas $mp => $mpid{
  704.  
  705.                         // Part must begin with last matching part id and be two levels in
  706.  
  707.                         $sub_nesting count(explode('.'$this->_pid[$mid][$p]));
  708.  
  709.                         if (($this->_ftype[$mid][$mp== $MIME && $getAlternative == TRUE && ($sub_nesting == ($this_nesting $pidAdd)) && ($pid == substr($this->_pid[$mid][$mp]0strlen($this->_pid[$mid][$p]))))) {
  710.  
  711.                             if ($action == 'add'{
  712.  
  713.                                  Mail_IMAP::_addInlinePart($in$mid$mp);
  714.                                  break;
  715.  
  716.                             else if ($action == 'get' && !isset($this->_fname[$mid][$mp]&& empty($this->_fname[$mid][$mp])) {
  717.  
  718.                                 return $this->_pid[$mid][$mp];
  719.  
  720.                             }
  721.  
  722.                         else if ($this->_ftype[$mid][$mp== 'multipart/alternative' && $action == 'get'{
  723.  
  724.                             // Need to match this PID to next level in
  725.                             $pid          = (string) $this->_pid[$mid][$mp];
  726.                             $pid_len      strlen($pid);
  727.                             $this_nesting count(explode('.'$pid));
  728.                             $pidAdd       = 2;
  729.                             continue;
  730.  
  731.                         }
  732.                     }
  733.  
  734.                 else if ($this->_disposition[$mid][$p== 'inline'{
  735.  
  736.                     if (( $action == 'add' &&
  737.                           $this->_ftype[$mid][$p!= $excludeMIME &&
  738.                           $pid != $this->_pid[$mid][$p]||
  739.                         $action == 'add' &&
  740.                           $this->_ftype[$mid][$p== $excludeMIME &&
  741.                           isset($this->_fname[$mid][$p]&&
  742.                           $pid != $this->_pid[$mid][$p])
  743.                        {
  744.  
  745.                         Mail_IMAP::_addInlinePart($in$mid$p);
  746.  
  747.                     else if ($action == 'get' && $this->_ftype[$mid][$p== $MIME && !isset($this->_fname[$mid][$p])) {
  748.  
  749.                         return $this->_pid[$mid][$p];
  750.                     }
  751.  
  752.                 else if ($action == 'add' && $this->_disposition[$mid][$p== 'attachment'{
  753.  
  754.                     Mail_IMAP::_addAttachment($a$mid$p);
  755.  
  756.                 }
  757.  
  758.             }
  759.  
  760.         }
  761.  
  762.         return FALSE;
  763.     }
  764.  
  765.     // {{{ unsetParts()
  766.     /**
  767.     * Destroys variables set by {@link getParts} and _declareParts.
  768.     *
  769.     * @param    integer  &$mid   message id
  770.     * @return   void 
  771.     * @access   public
  772.     * @see      getParts
  773.     */
  774.     function unsetParts(&$mid)
  775.     {
  776.         unset($this->inPid[$mid]);
  777.         unset($this->inFtype[$mid]);
  778.         unset($this->inFsize[$mid]);
  779.         unset($this->inHasAttach[$mid]);
  780.         unset($this->inInlineId[$mid]);
  781.  
  782.         unset($this->attachPid[$mid]);
  783.         unset($this->attachFtype[$mid]);
  784.         unset($this->attachFsize[$mid]);
  785.         unset($this->attachFname[$mid]);
  786.         unset($this->attachHasAttach[$mid]);
  787.  
  788.         unset($this->_structure[$mid]);
  789.         unset($this->_pid[$mid]);
  790.         unset($this->_disposition[$mid]);
  791.         unset($this->_encoding[$mid]);
  792.         unset($this->_ftype[$mid]);
  793.         unset($this->_fsize[$mid]);
  794.         unset($this->_fname[$mid]);
  795.         unset($this->_inlineId[$mid]);
  796.         unset($this->_hasAttachments[$mid]);
  797.  
  798.         return;
  799.     }
  800.     // }}}
  801.  
  802.     // {{{ _addInlinePart()
  803.     /**
  804.     * Adds information to the member variable inline parts arrays.
  805.     *
  806.     * @param    int     &$in   offset inline counter
  807.     * @param    int     &$mid  message id
  808.     * @param    int     &$i    offset structure reference counter
  809.     * @return   void 
  810.     * @access   private
  811.     */
  812.     function _addInlinePart(&$in&$mid&$i)
  813.     {
  814.         $this->inFname[$mid][$in(isset($this->_fname[$mid][$i]&& !empty($this->_fname[$mid][$i]))$this->_fname[$mid][$i'';
  815.  
  816.         $this->inPid[$mid][$in]            $this->_pid[$mid][$i];
  817.         $this->inFtype[$mid][$in]          $this->_ftype[$mid][$i];
  818.         $this->inFsize[$mid][$in]          $this->_fsize[$mid][$i];
  819.         $this->inHasAttach[$mid][$in]      $this->_hasAttachments[$mid][$i];
  820.  
  821.         if (isset($this->_inlineId[$mid][$i])) {
  822.             $this->inInlineId[$mid][$in]   $this->_inlineId[$mid][$i];
  823.         }
  824.  
  825.         $in++;
  826.  
  827.         return;
  828.     }
  829.     // }}}
  830.  
  831.     // {{{ _addAttachment()
  832.     /**
  833.     * Adds information to the member variable attachment parts arrays.
  834.     *
  835.     * @param    int     &$a    offset attachment counter
  836.     * @param    int     &$mid  message id
  837.     * @param    int     &$i    offset structure reference counter
  838.     * @return   void 
  839.     * @access   private
  840.     */
  841.     function _addAttachment(&$a&$mid&$i)
  842.     {
  843.         if (!isset($this->_fname[$mid][$i])) {
  844.             $this->_fname[$mid][$i'';
  845.         }
  846.  
  847.         $this->attachPid[$mid][$a]         $this->_pid[$mid][$i];
  848.         $this->attachFtype[$mid][$a]       $this->_ftype[$mid][$i];
  849.         $this->attachFsize[$mid][$a]       $this->_fsize[$mid][$i];
  850.         $this->attachFname[$mid][$a]       $this->_fname[$mid][$i];
  851.         $this->attachHasAttach[$mid][$a]   $this->_hasAttachments[$mid][$i];
  852.  
  853.         $a++;
  854.  
  855.         return;
  856.     }
  857.     // }}}
  858.  
  859.     // {{{ getRawMessage()
  860.     /**
  861.     * Returns entire unparsed message body.  See {@link imap_body} for options.
  862.     *
  863.     * @param    int     &$mid      message id
  864.     * @param    int     $options   flags
  865.     * @return   void 
  866.     * @access   public
  867.     * @see      imap_body
  868.     */
  869.     function getRawMessage(&$mid$options = NULL)
  870.     {
  871.         return ($options == NULL)? imap_body($this->mailbox$mid: imap_body($this->mailbox$mid$options);
  872.     }
  873.     // }}}
  874.  
  875.     // {{{ getBody()
  876.     /**
  877.     * Searches parts array set in Mail_IMAP::_declareParts() for a displayable message.
  878.     * If the part id passed is message/rfc822 looks in subparts for a displayable body.
  879.     * Attempts to return a text/html inline message part by default. And will
  880.     * automatically attempt to find a text/plain part if a text/html part could
  881.     * not be found.
  882.     *
  883.     * Returns an array containing three associative indices; 'ftype', 'fname' and
  884.     * 'message'.  'ftype' contains the MIME type of the message, 'fname', the original
  885.     * file name, if any, empty string otherwise.  And 'message', which contains the
  886.     * message body itself which is returned decoded from base64 or quoted-printable if
  887.     * either of those encoding types are specified, returns untouched otherwise.
  888.     * Returns FALSE on failure.
  889.     *
  890.     * @param    int     &$mid                    message id
  891.     * @param    string  $pid                     part id
  892.     * @param    int     $action 
  893.     *       (optional) options for body return.  Set to one of the following:
  894.     *       MAIL_IMAP_BODY (default), if part is message/rfc822 searches subparts for a
  895.     *       displayable body and returns the body decoded as part of an array.
  896.     *       MAIL_IMAP_LITERAL, return the message for the specified $pid without searching
  897.     *       subparts or decoding the message (may return unparsed message) body is returned
  898.     *       undecoded as a string.
  899.     *       MAIL_IMAP_LITERAL_DECODE, same as MAIL_IMAP_LITERAL, except message decoding is
  900.     *       attempted from base64 or quoted-printable encoding, returns undecoded string
  901.     *       if decoding failed.
  902.     *
  903.     * @param    string  $getPart 
  904.     *       (optional) one of text/plain or text/html, allows the specification of the default
  905.     *       part to return from multipart messages, text/html by default.
  906.     *
  907.     * @param    int     $options 
  908.     *       (optional) allows the specification of the forth argument of imap_fetchbody
  909.     *
  910.     * @return   array|string|FALSE
  911.     * @access   public
  912.     * @see      imap_fetchbody
  913.     * @see      Mail_IMAP::getParts
  914.     * @since    PHP 4.2.0
  915.     */
  916.     function getBody(&$mid$pid$action = 0$getPart 'text/html'$options = NULL$attempt = 1)
  917.     {
  918.         if ($action == MAIL_IMAP_LITERAL{
  919.             return ($options == NULL)? imap_fetchbody($this->mailbox$mid$pid: imap_fetchbody($this->mailbox$mid$pid$options);
  920.         }
  921.  
  922.         Mail_IMAP::_checkIfParsed($mid);
  923.  
  924.         if (FALSE !== ($i array_search((string) $pid$this->_pid[$mid]))) {
  925.             if ($action == MAIL_IMAP_LITERAL_DECODE{
  926.                 $msg_body ($options == NULL)? imap_fetchbody($this->mailbox$mid$pid: imap_fetchbody($this->mailbox$mid$pid$options);
  927.                 return Mail_IMAP::_decodeMessage($msg_body$this->_encoding[$mid][$i]);
  928.             }
  929.  
  930.             // If this is an attachment, and the part is message/rfc822 update the pid to the subpart
  931.             // If this is an attachment, and the part is multipart/alternative update the pid to the subpart
  932.             if ($this->_ftype[$mid][$i== 'message/rfc822' || $this->_ftype[$mid][$i== 'multipart/alternative'{
  933.                 $new_pid ($this->_ftype[$mid][$i== 'message/rfc822')Mail_IMAP::_scanMultipart($mid$pid$i$getPart'get''all'1Mail_IMAP::_scanMultipart($mid$pid$i$getPart'get''multipart'1);
  934.  
  935.                 // if a new pid for text/html couldn't be found, try again, this time look for text/plain
  936.                 switch(TRUE{
  937.                     case (!empty($new_pid)):                             $pid $new_pid; break;
  938.                     case (empty($new_pid&& $getPart == 'text/html'):   return ($attempt == 1)Mail_IMAP::getBody($mid$pid$action'text/plain'$options2: FALSE;
  939.                     case (empty($new_pid&& $getPart == 'text/plain'):  return ($attempt == 1)Mail_IMAP::getBody($mid$pid$action'text/html'$options2: FALSE;
  940.                 }
  941.             }
  942.  
  943.             // Update the key for the new pid
  944.             if (!empty($new_pid)) {
  945.                 if (FALSE === ($i array_search((string) $pid$this->_pid[$mid]))) {
  946.                     // Something's afoot!
  947.                     PEAR::raiseError('Mail_IMAP::getBody was unable to find a suitable replacement part ID for: '.$pid.'.  Message: '.$mid.' may be poorly formed or corrupted.'NULLPEAR_ERROR_TRIGGERE_USER_WARNING);
  948.                     return FALSE;
  949.                 }
  950.             }
  951.  
  952.             $msg_body ($options == NULL)? imap_fetchbody($this->mailbox$mid$pid: imap_fetchbody($this->mailbox$mid$pid$options);
  953.  
  954.             if ($msg_body == NULL{
  955.                 PEAR::raiseError('Mail_IMAP::getBody message body was NULL for pid: '.$pid.', is not a valid part number.'NULLPEAR_ERROR_TRIGGERE_USER_NOTICE);
  956.                 return FALSE;
  957.             }
  958.  
  959.             // Decode message.
  960.             // Because the body returned may not correspond with the original PID, return
  961.             // an array which also contains the MIME type and original file name, if any.
  962.             $body['message'Mail_IMAP::_decodeMessage($msg_body$this->_encoding[$mid][$i]);
  963.             $body['ftype']   $this->_ftype[$mid][$i];
  964.             $body['fname']   (isset($this->_fname[$mid][$i]))$this->_fname[$mid][$i'';
  965.  
  966.             return $body;
  967.         else {
  968.             PEAR::raiseError('Mail_IMAP::getBody was unable to retrieve message body, invalid part id: '.$pidNULLPEAR_ERROR_TRIGGERE_USER_WARNING);
  969.             return FALSE;
  970.         }
  971.  
  972.         return FALSE;
  973.     }
  974.     // }}}
  975.  
  976.     // {{{ _decodeMessage()
  977.     /**
  978.     * Decode a string from quoted-printable or base64 encoding.  If
  979.     * neither of those encoding types are specified, returns string
  980.     * untouched.
  981.     *
  982.     * @param    string  &$body           string to decode
  983.     * @param    string  &$encoding       encoding to decode from.
  984.     * @return   string 
  985.     * @access   private
  986.     */
  987.     function _decodeMessage(&$body&$encoding)
  988.     {
  989.         switch ($encoding{
  990.             case 'quoted-printable':  return imap_qprint($body);
  991.             case 'base64':            return imap_base64($body);
  992.             default:                  return $body;
  993.         }
  994.     }
  995.     // }}}
  996.  
  997.     // {{{ getDefaultPid()
  998.     /**
  999.     * Searches structure defined in Mail_IMAP::_declareParts for the top-level default message.
  1000.     * Attempts to find a text/html default part, if no text/html part is found,
  1001.     * automatically attempts to find a text/plain part. Returns the part id for the default
  1002.     * top level message part on success. Returns FALSE on failure.
  1003.     *
  1004.     * @param    int     &$mid           message id
  1005.     * @param    string  $getPart 
  1006.     *      (optional) default MIME type to look for, one of text/html or text/plain
  1007.     *      text/html by default.
  1008.     * @return   string 
  1009.     * @access   public
  1010.     */
  1011.     function getDefaultPid(&$mid$getPart 'text/html'$attempt = 1)
  1012.     {
  1013.         // Check to see if this part has already been parsed
  1014.         Mail_IMAP::_checkIfParsed($mid);
  1015.  
  1016.         // Look for a text/html message part
  1017.         // If no text/html message part was found look for a text/plain message part
  1018.  
  1019.         $part ($getPart == 'text/html')? array('text/html''text/plain': array('text/plain''text/html');
  1020.  
  1021.         foreach ($part as $mime{
  1022.  
  1023.             if (0 !== count($msg_part array_keys($this->_ftype[$mid]$mime))) {
  1024.  
  1025.                 foreach ($msg_part as $i{
  1026.  
  1027.                     if ($this->_disposition[$mid][$i== 'inline' && !stristr($this->_pid[$mid][$i]'.')) {
  1028.                         $ret $this->_pid[$mid][$i];
  1029.                         break;
  1030.                         break;
  1031.                     }
  1032.                 }
  1033.             }
  1034.         }
  1035.  
  1036.         // If no text/plain or text/html part was found
  1037.         // Look for a multipart/alternative part
  1038.  
  1039.         if (!isset($ret))
  1040.         {
  1041.             foreach ($this->_pid[$midas $p => $id{
  1042.  
  1043.                 if (!stristr($this->_pid[$mid][$p]'.'&& $this->_ftype[$mid][$p== 'multipart/alternative'{
  1044.  
  1045.                     foreach ($this->_pid[$midas $mp => $mpid{
  1046.  
  1047.                         $nesting count(explode('.'$this->_pid[$mid][$p]));
  1048.  
  1049.                         if ($this->_ftype[$mid][$mp== $getPart && $nesting == 2 && $this->_pid[$mid][$p== substr($this->_pid[$mid][$mp]01)) {
  1050.                             $ret $this->_pid[$mid][$mp];
  1051.                             break;
  1052.                             break;
  1053.                         }
  1054.                     }
  1055.                 }
  1056.             }
  1057.         }
  1058.  
  1059.         // if a text/html part was not found, call on the function again
  1060.         // and look for text/plain
  1061.         // if the application was unable to find a text/plain part
  1062.  
  1063.         switch ($getPart{
  1064.             case 'text/html':  $ret ($attempt == 1)Mail_IMAP::getDefaultPid($mid'text/plain'2: FALSE;
  1065.             case 'text/plain'$ret ($attempt == 1)Mail_IMAP::getDefaultPid($mid'text/html'2: FALSE;
  1066.             default:           $ret = FALSE;
  1067.         }
  1068.  
  1069.         return ($ret == FALSE)? 1 : $ret;
  1070.     }
  1071.     // }}}
  1072.  
  1073.     // {{{ extractMIME()
  1074.     /**
  1075.     * Searches all message parts for the specified MIME type.  Use {@link getBody}
  1076.     * with $action option MAIL_IMAP_LITERAL_DECODE to view MIME type parts retrieved.
  1077.     * If you need to access the MIME type with filename use normal {@link getBody}
  1078.     * with no action specified.
  1079.     *
  1080.     * Returns an array of part ids on success.
  1081.     * Returns FALSE if MIME couldn't be found, or on failure.
  1082.     *
  1083.     * @param    int     &$mid           message id
  1084.     * @param    string  $MIME           mime type to extract
  1085.     * @return   array|FALSE
  1086.     * @access   public
  1087.     */
  1088.     function extractMIME(&$mid$MIME)
  1089.     {
  1090.         Mail_IMAP::_checkIfParsed($mid);
  1091.  
  1092.         if (is_array($this->_ftype[$mid])) {
  1093.             if (!is_array($MIME)) {
  1094.                 if (0 !== count($pids array_keys($this->_ftype[$mid]$MIME))) {
  1095.                     foreach ($pids as $i{
  1096.                         $rtn[$this->_pid[$mid][$i];
  1097.                     }
  1098.                 else {
  1099.                     $rtn = FALSE;
  1100.                 }
  1101.             else {
  1102.                 foreach ($MIME as $mtype{
  1103.                     if (0 !== count($pids array_keys($this->_ftype[$mid]$mtype))) {
  1104.                         foreach ($pids as $i{
  1105.                             $rtn[$this->_pid[$mid][$i];
  1106.                         }
  1107.                     else {
  1108.                         $rtn = FALSE;
  1109.                     }
  1110.                 }
  1111.             }
  1112.         else {
  1113.             $rtn = FALSE;
  1114.         }
  1115.  
  1116.         return $rtn;
  1117.     }
  1118.     // }}}
  1119.  
  1120.     // {{{ getRawHeaders()
  1121.     /**
  1122.     * Set member variable {@link $rawHeaders} to contain Raw Header information
  1123.     * for a part.  Returns default header part id on success, returns FALSE on failure.
  1124.     *
  1125.     * @param    int     &$mid          message_id
  1126.     * @param    string  $pid           part id
  1127.     * @param    int     $options       flags/options for imap_fetchbody
  1128.     * @return   string|FALSE
  1129.     * @access   public
  1130.     * @see      imap_fetchbody
  1131.     * @see      getHeaders
  1132.     */
  1133.     function getRawHeaders(&$mid$pid$options = NULL)
  1134.     {
  1135.         // pid is modified in this function, so don't pass by reference (will create a logic error)
  1136.         if (FALSE !== ($pid Mail_IMAP::_defaultHeaderPid($mid$pid))) {
  1137.             if ($options == NULL{
  1138.                 $this->rawHeaders[$mid($pid == '0')? imap_fetchheader($this->mailbox$mid: imap_fetchbody($this->mailbox$mid$pid);
  1139.             else {
  1140.                 $this->rawHeaders[$mid($pid == '0')? imap_fetchheader($this->mailbox$mid: imap_fetchbody($this->mailbox$mid$pid$options);
  1141.             }
  1142.  
  1143.             return $pid;
  1144.         else {
  1145.             PEAR::raiseError('Mail_IMAP::getRawHeaders unable to retrieve headers, invalid part id: '.$pidNULLPEAR_ERROR_TRIGGERE_USER_WARNING);
  1146.             return FALSE;
  1147.         }
  1148.     }
  1149.     // }}}
  1150.  
  1151.     // {{{ getHeaders()
  1152.     /**
  1153.     * Set member variable containing header information.  Creates an array containing associative indices
  1154.     * referring to various header information.  Use {@link var_dump} or {@link print_r} on the {@link $header}
  1155.     * member variable to view information gathered by this function.
  1156.     *
  1157.     * @param    int     &$mid           message id
  1158.     * @param    string  &$pid           part id
  1159.     * @param    int     $from_length    (optional) from length for imap_headerinfo
  1160.     * @param    int     $subject_length (optional) subject length for imap_headerinfo
  1161.     * @param    string  $default_host   (optional) default host for imap_headerinfo & imap_rfc822_parse_headers
  1162.     * @param    int     $options        (optional) flags/options for imap_fetchbody
  1163.     * @return   BOOL 
  1164.     * @access   public
  1165.     * @see      getParts
  1166.     * @see      imap_fetchheader
  1167.     * @see      imap_fetchbody
  1168.     * @see      imap_headerinfo
  1169.     * @see      imap_rfc822_parse_headers
  1170.     */
  1171.     function getHeaders(&$mid&$pid$from_length = 1024$subject_length = 1024$default_host = NULL$options = NULL)
  1172.     {
  1173.         if (FALSE === ($hpid Mail_IMAP::getRawHeaders($mid$pid$options))) {
  1174.             return FALSE;
  1175.         }
  1176.  
  1177.         // $default_host contains the host information for addresses where it is not
  1178.         // present.  Specify it or attempt to use SERVER_NAME
  1179.         if ($default_host == NULL && isset($_SERVER['SERVER_NAME']&& !empty($_SERVER['SERVER_NAME'])) {
  1180.             $default_host $_SERVER['SERVER_NAME'];
  1181.         else if ($default_host == NULL{
  1182.             $default_host 'UNSPECIFIED-HOST-NAME';
  1183.         }
  1184.  
  1185.         // Parse the headers
  1186.         $header_info ($hpid == '0')? imap_headerinfo($this->mailbox$mid$from_length$subject_length$default_host: imap_rfc822_parse_headers($this->rawHeaders[$mid]$default_host);
  1187.  
  1188.         // Since individual member variable creation might create extra overhead,
  1189.         // and having individual variables referencing this data and the original
  1190.         // object would be too much as well, we'll just copy the object into an
  1191.         // associative array, preform clean-up on those elements that require it,
  1192.         // and destroy the original object after copying.
  1193.  
  1194.         if (!is_object($header_info)) {
  1195.             PEAR::raiseError('Mail_IMAP::getHeaders unable to retrieve header object, invalid part id: '.$pidNULLPEAR_ERROR_TRIGGERE_USER_WARNING);
  1196.             return FALSE;
  1197.         }
  1198.  
  1199.         $headers get_object_vars($header_info);
  1200.  
  1201.         foreach ($headers as $key => $value{
  1202.             if (!is_object($value&& !is_array($value)) {
  1203.                 $this->header[$mid][$key$value;
  1204.             }
  1205.         }
  1206.  
  1207.         // copy udate or create it from date string.
  1208.         $this->header[$mid]['udate'(isset($header_info->udate&& !empty($header_info->udate))$header_info->udate : strtotime($header_info->Date);
  1209.  
  1210.         // clean up addresses
  1211.         $line['from';
  1212.         $line['reply_to';
  1213.         $line['sender';
  1214.         $line['return_path';
  1215.         $line['to';
  1216.         $line['cc';
  1217.         $line['bcc';
  1218.  
  1219.         for ($i = 0; $i count($line)$i++{
  1220.             if (isset($header_info->$line[$i])) {
  1221.                 Mail_IMAP::_parseHeaderLine($mid$header_info->$line[$i]$line[$i]);
  1222.             }
  1223.         }
  1224.  
  1225.         // All possible information has been copied, destroy original object
  1226.         unset($header_info);
  1227.  
  1228.         return TRUE;
  1229.     }
  1230.     // }}}
  1231.  
  1232.     // {{{ _parseHeaderLine()
  1233.     /**
  1234.     * Parse header information from the given line and add it to the {@link $header}
  1235.     * array.  This function is only used by {@link getRawHeaders}.
  1236.     *
  1237.     * @param     string   &$line 
  1238.     * @param     string   $name 
  1239.     * @return    void 
  1240.     * @access    private
  1241.     */
  1242.     function _parseHeaderLine(&$mid&$line$name{
  1243.         if (isset($line&& count($line>= 1{
  1244.             $i = 0;
  1245.             foreach ($line as $object{
  1246.                 if (isset($object->personal)) {
  1247.                     $this->header[$mid][$name.'_personal'][$i$object->personal;
  1248.                 }
  1249.  
  1250.                 if (isset($object->mailbox&& isset($object->host)) {
  1251.                     $this->header[$mid][$name][$i$object->mailbox.'@'.$object->host;
  1252.                 }
  1253.                 $i++;
  1254.             }
  1255.         }
  1256.         return;
  1257.     }
  1258.     // }}}
  1259.  
  1260.     // {{{ _defaultHeaderPid()
  1261.     /**
  1262.     * Finds and returns a default part id for headers and matches any sub message part to
  1263.     * the appropriate headers.  Returns FALSE on failure and may return a value that
  1264.     * evaluates to false, use the '===' operator for testing this function's return value.
  1265.     *
  1266.     * @param    int     &$mid            message id
  1267.     * @param    string  $pid             part id
  1268.     * @return   string|FALSE
  1269.     * @access   private
  1270.     * @see      getHeaders
  1271.     * @see      getRawHeaders
  1272.     */
  1273.     function _defaultHeaderPid(&$mid$pid)
  1274.     {
  1275.         // pid is modified in this function, so don't pass by reference (will create a logic error)
  1276.         Mail_IMAP::_checkIfParsed($mid);
  1277.  
  1278.         // retrieve key for this part, so that the information may be accessed
  1279.         if (FALSE !== ($i array_search((string) $pid$this->_pid[$mid]))) {
  1280.  
  1281.             // If this part is message/rfc822 display headers for this part
  1282.             if ($this->_ftype[$mid][$i== 'message/rfc822'{
  1283.  
  1284.                 $ret = (string) $pid.'.0';
  1285.  
  1286.             else if ($pid == Mail_IMAP::getDefaultPid($mid)) {
  1287.  
  1288.                 $ret = (string) '0';
  1289.  
  1290.             else {
  1291.  
  1292.                 $pid_len strlen($pid);
  1293.                 $this_nesting count(explode('.'$pid));
  1294.  
  1295.                 // Deeper searching may be required, go back to this part's parent.
  1296.                 if (!stristr($pid'.'|| ($this_nesting - 1== 1{
  1297.  
  1298.                     $ret = (string) '0';
  1299.  
  1300.                 else if ($this_nesting > 2{
  1301.  
  1302.                     // Look at previous parts until a message/rfc822 part is found.
  1303.                     for ($pos $this_nesting - 1; $pos > 0; $pos -= 1{
  1304.  
  1305.                         foreach ($this->_pid[$midas $p => $aid{
  1306.  
  1307.                             $nesting count(explode('.'$this->_pid[$mid][$p]));
  1308.  
  1309.                             if ($nesting == $pos && $this->_ftype[$mid][$p== 'message/rfc822'{
  1310.                                 // Break iteration and return!
  1311.                                 return (string) $this->_pid[$mid][$p].'.0';
  1312.                             }
  1313.                         }
  1314.                     }
  1315.  
  1316.                     $ret ($pid_len == 3)? (string) '0' : FALSE;
  1317.  
  1318.                 else {
  1319.                     $ret = FALSE;
  1320.                 }
  1321.             }
  1322.  
  1323.             return $ret;
  1324.  
  1325.         else {
  1326.             // Something's afoot!
  1327.             PEAR::raiseError('Mail_IMAP::_defaultHeaderPid unable to retrieve headers, invalid part id: '.$pidNULLPEAR_ERROR_TRIGGERE_USER_WARNING);
  1328.             return FALSE;
  1329.         }
  1330.     }
  1331.     // }}}
  1332.  
  1333.     // {{{ unsetHeaders()
  1334.     /**
  1335.     * Destroys variables set by {@link getHeaders}.
  1336.     *
  1337.     * @param    int     &$mid            message id
  1338.     * @return   void 
  1339.     * @access   public
  1340.     * @see      getHeaders
  1341.     */
  1342.     function unsetHeaders(&$mid)
  1343.     {
  1344.         unset($this->rawHeaders[$mid]);
  1345.         unset($this->header[$mid]);
  1346.         return;
  1347.     }
  1348.     // }}}
  1349.  
  1350.     // {{{ convertBytes()
  1351.     /**
  1352.     * Converts an integer containing the number of bytes in a file to one of Bytes, Kilobytes,
  1353.     * Megabytes, or Gigabytes, appending the unit of measurement.
  1354.     *
  1355.     * @param    int     $bytes 
  1356.     * @return   string 
  1357.     * @access   public
  1358.     */
  1359.     function convertBytes($bytes)
  1360.     {
  1361.         switch (TRUE{
  1362.             case ($bytes pow(2,10)):                             return $bytes.' Bytes';
  1363.             case ($bytes >= pow(2,10&& $bytes pow(2,20)):      return round($bytes pow(2,10)0).' KB';
  1364.             case ($bytes >= pow(2,20&& $bytes pow(2,30)):      return round($bytes pow(2,20)1).' MB';
  1365.             case ($bytes pow(2,30)):                             return round($bytes pow(2,30)2).' GB';
  1366.         }
  1367.     }
  1368.     // }}}
  1369.  
  1370.     // {{{ delete()
  1371.     /**
  1372.     * Wrapper function for {@link imap_delete}.  Sets the marked for deletion flag.  Note: POP3
  1373.     * mailboxes do not remember flag settings between connections, for POP3 mailboxes
  1374.     * this function should be used in addtion to {@link expunge}.
  1375.     *
  1376.     * @param    int     &$mid   message id
  1377.     * @return   TRUE|PEAR_Error
  1378.     * @access   public
  1379.     * @see      imap_delete
  1380.     * @see      expunge
  1381.     */
  1382.     function delete(&$mid$separator "<br />\n")
  1383.     {
  1384.         if (!is_array($mid)) {
  1385.             return (imap_delete($this->mailbox$mid))? TRUE : PEAR::raiseError('Unable to mark message: '.$mid.' for deletion.');
  1386.         else {
  1387.             foreach ($mid as $id{
  1388.                 if (!imap_delete($this->mailbox$id)) {
  1389.                     $stack['Unable to mark message: '.$id."for deletion.";
  1390.                 }
  1391.             }
  1392.             return (isset($stack&& is_array($stack))? PEAR::raiseError(implode($separator$stack)) : TRUE;
  1393.         }
  1394.     }
  1395.     // }}}
  1396.  
  1397.     // {{{ expunge()
  1398.     /**
  1399.     * Wrapper function for {@link imap_expunge}.  Expunges messages marked for deletion.
  1400.     *
  1401.     * @return   TRUE|PEAR_Error
  1402.     * @access   public
  1403.     * @see      imap_expunge
  1404.     * @see      delete
  1405.     */
  1406.     function expunge()
  1407.     {
  1408.         return (imap_expunge($this->mailbox))? TRUE : PEAR::raiseError('Unable to expunge mailbox.');
  1409.     }
  1410.     // }}}
  1411.  
  1412.     // {{{ errors()
  1413.     /**
  1414.     * Wrapper function for {@link imap_errors}.  Implodes the array returned by imap_errors,
  1415.     * (if any) and returns the error text.
  1416.     *
  1417.     * @param    string    $seperator     Characters to seperate each error message. '<br />\n' by default.
  1418.     * @return   string|FALSE
  1419.     * @access   public
  1420.     * @see      imap_errors
  1421.     * @see      alerts
  1422.     */
  1423.     function errors($seperator "<br />\n")
  1424.     {
  1425.         $errors = imap_errors();
  1426.         return (is_array($errors&& !empty($errors))implode($seperator$errors: FALSE;
  1427.     }
  1428.     // }}}
  1429.  
  1430.     // {{{ alerts()
  1431.     /**
  1432.     * Wrapper function for {@link imap_alerts}.  Implodes the array returned by imap_alerts,
  1433.     * (if any) and returns the text.
  1434.     *
  1435.     * @param    string    $seperator     Characters to seperate each alert message. '<br />\n' by default.
  1436.     * @return   string|FALSE
  1437.     * @access   public
  1438.     * @see      imap_alerts
  1439.     * @see      errors
  1440.     */
  1441.     function alerts($seperator "<br />\n")
  1442.     {
  1443.         $alerts = imap_alerts();
  1444.         return (is_array($alerts&& !empty($alerts))implode($seperator$alerts: FALSE;
  1445.     }
  1446.     // }}}
  1447.  
  1448.     // {{{ getQuota()
  1449.     /**
  1450.     * Retreives information about the current mailbox's quota.  Rounds up quota sizes and
  1451.     * appends the unit of measurment.  Returns information in a multi-dimensional associative
  1452.     * array.
  1453.     *
  1454.     * @param    string   $folder    Folder to retrieve quota for.
  1455.     * @return   array|PEAR_Error
  1456.     * @throws   Quota not available on this server.  Remedy: none.
  1457.     * @access   public
  1458.     * @see      imap_get_quotaroot
  1459.     */
  1460.     function getQuota($folder 'INBOX')
  1461.     {
  1462.         $quota @imap_get_quotaroot($this->mailbox$folder);
  1463.  
  1464.         // STORAGE Values are returned in KB
  1465.         // Convert back to bytes first
  1466.         // Then round these to the simpliest unit of measurement
  1467.         $rtn['STORAGE']['usage'Mail_IMAP::convertBytes($quota['STORAGE']['usage'* 1024);
  1468.         $rtn['STORAGE']['limit'Mail_IMAP::convertBytes($quota['STORAGE']['limit'* 1024);
  1469.  
  1470.         $rtn['MESSAGE']['usage'Mail_IMAP::convertBytes($quota['MESSAGE']['usage']);
  1471.         $rtn['MESSAGE']['limit'Mail_IMAP::convertBytes($quota['MESSAGE']['limit']);
  1472.  
  1473.         return (empty($quota['STORAGE']['usage']&& empty($quota['STORAGE']['limit']))? PEAR::raiseError('Error: Quota not available for this server.'$rtn;
  1474.     }
  1475.     // }}}
  1476.  
  1477.     // {{{ setFlags()
  1478.     /**
  1479.     * Wrapper function for {@link imap_setflag_full}.  Sets various message flags.
  1480.     * Accepts an array of message ids and an array of flags to be set.
  1481.     *
  1482.     * The flags which you can set are "\\Seen", "\\Answered", "\\Flagged",
  1483.     * "\\Deleted", and "\\Draft" (as defined by RFC2060).
  1484.     *
  1485.     * Warning: POP3 mailboxes do not remember flag settings from connection to connection.
  1486.     *
  1487.     * @param    array  $mids        Array of message ids to set flags on.
  1488.     * @param    array  $flags       Array of flags to set on messages.
  1489.     * @param    int    $action      Flag operation toggle one of MAIL_IMAP_SET_FLAGS (default) or
  1490.     *                                MAIL_IMAP_CLEAR_FLAGS.
  1491.     * @param    int    $options 
  1492.     *    (optional) sets the forth argument of {@link imap_setflag_full} or {@imap_clearflag_full}. 
  1493.     *
  1494.     * @return   BOOL|PEAR_Error
  1495.     * @throws   Message IDs and Flags are to be supplied as arrays.  Remedy: place message ids
  1496.     *            and flags in arrays.
  1497.     * @access   public
  1498.     * @see      imap_setflag_full
  1499.     * @see      imap_clearflag_full
  1500.     */
  1501.     function setFlags($mids$flags$action = 3$options = NULL)
  1502.     {
  1503.         if (is_array($mids&& is_array($flags)) {
  1504.             if ($action == MAIL_IMAP_SET_FLAGS{
  1505.                 return ($options == NULL)@imap_setflag_full($this->mailboximplode(','$mids)implode(' '$flags)) @imap_setflag_full($this->mailboximplode(','$mids)implode(' '$flags)$options);
  1506.             else {
  1507.                 return ($options == NULL)@imap_clearflag_full($this->mailboximplode(','$mids)implode(' '$flags)) @imap_clearflag_full($this->mailboximplode(','$mids)implode(' '$flags)$options);
  1508.             }
  1509.         else {
  1510.             return PEAR::raiseError('Message IDs and Flags are to be supplied as arrays.');
  1511.         }
  1512.     }
  1513.     // }}}
  1514. }
  1515. // }}}
  1516. ?>

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