Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\//\\\ \\\\\\\\| \\//\\\ @@ @@\\\\\\| Mail_IMAPv2 \\//\\ @@@@ @@@@\\\\\|___________________________________________________________\\//\\\@@@@| @@@@\\\\\| \\//\\\ @@ |\\@@\\\\\\|(c) Copyright 2004-2005 Richard York, All rights Reserved \\//\\\\ || \\\\\\\|___________________________________________________________\\//\\\\ \\_ \\\\\\|Redistribution and use in source and binary forms, with or \\//\\\\\ \\\\\|without modification, are permitted provided that the \\//\\\\\ ---- \@@@@|following conditions are met: \\//@@@@@\ \@@@@| \\//@@@@@@\ \@@@@@| o Redistributions of source code must retain the above \\//\\\\\\\\\\\\\\\\\\| copyright notice, this list of conditions and the \\// following disclaimer. \\// o Redistributions in binary form must reproduce the above copyright notice, \\// this list of conditions and the following disclaimer in the documentation \\// and/or other materials provided with the distribution. \\// o The names of the authors may not be used to endorse or promote products \\// derived from this software without specific prior written permission. \\// \\// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" \\// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE \\// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE \\// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE \\// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR \\// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF \\// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS \\// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN \\// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) \\// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE \\// POSSIBILITY OF SUCH DAMAGE. \\//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\require_once 'PEAR/ErrorStack.php';define('Mail_IMAPv2_BODY', 0);define('Mail_IMAPv2_LITERAL', 1);define('Mail_IMAPv2_LITERAL_DECODE', 2);define('Mail_IMAPv2_ERROR', 1);define('Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY', 2);define('Mail_IMAPv2_ERROR_INVALID_OPTION', 3);define('Mail_IMAPv2_ERROR_INVALID_PID', 4);define('Mail_IMAPv2_ERROR_INVALID_ACTION', 5);define('Mail_IMAPv2_NOTICE', 100);define('Mail_IMAPv2_NOTICE_FALLBACK_PID', 102);define('Mail_IMAPv2_FATAL', 200);/*** Mail_IMAPv2 provides a flexible API for connecting to and retrieving* mail from mailboxes using the IMAP, POP3 or NNTP mail protocols.* Connection to a mailbox is acheived through the c-client extension* to PHP (http://www.php.net/imap). Meaning installation of the* c-client extension is required to use Mail_IMAPv2.** Mail_IMAPv2 can be used to retrieve the contents of a mailbox,* whereas it may serve as the backend for a webmail application or* mailing list manager.** Since Mail_IMAPv2 is an abstracted object, it allows for complete* customization of the UI for any application.** By default Mail_IMAPv2 parses and retrieves information about* multipart message in a threaded fashion similar to MS Outlook, e.g.* only top level attachments are retrieved initially, then when message* part id and message id are passed to Mail_IMAPv2, it retrieves* attachments and information relevant to that message part.* {@link getParts} can be supplied an argument to turn off threading,* whereas all parts are retrieved at once.** Mail_IMAPv2 also, by default retrieves the alternative message parts* of multipart messages. This is most useful for debugging* applications that send out multipart mailers where both a text/html* and alterntaive text/plain part are included. This can also be* turned off by supplying an additional argument to {@link getParts}.** Mail_IMAPv2 always searches for a text/html part to display as the primary* part. This can be reversed so that it always looks for a text/plain part* initially by supplying the necessary arguments to {@link getParts},* and {@link getBody}.** PLEASE REPORT BUGS FOLLOWING THE GUIDELINES AT:* http://www.smilingsouls.net/Mail_IMAP** @author Richard York <rich_y@php.net>* @category Mail* @package Mail_IMAPv2* @license BSD* @version 0.2.0* @copyright (c) Copyright 2004, Richard York, All Rights Reserved.* @since PHP 4.2.0* @since C-Client 2001* @tutorial http://www.smilingsouls.net/Mail_IMAP** @example docs/examples/IMAP.inbox.php* Mail_IMAPv2 Inbox** @example docs/examples/IMAP.message_viewer.php* Mail_IMAPv2 Message** @example docs/examples/IMAP.part_viewer.php* Mail_IMAPv2 Message** @example docs/examples/IMAP.connection_wizard.php* Mail_IMAPv2 Connection Wizard** @example docs/examples/IMAP.connection_wizard_example.php* Mail_IMAPv2 Connection Wizard*/class Mail_IMAPv2 {/*** Contains an instance of the PEAR_ErrorStack object.* @var object $error* @access public* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/error*/var $error;/*** Contains the imap resource stream.* @var resource $mailbox* @access public* @see Mail_IMAPv2* @see open* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/mailbox*/var $mailbox;/*** Contains information about the current mailbox.* @var array $mailboxInfo* @access public* @see connect* @see getMailboxInfo* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/mailboxInfo*/var $mailboxInfo = array();/*** Set flags for various imap_* functions.** Use associative indices to indicate the imap_* function to set flags for,* create the indice omitting the 'imap_' portion of the function name.* see: {@link setOptions} for more information.** @var array $option* @access public* @see setOptions* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/setOptions*/var $option = array();/*** Contains various information returned by {@link imap_fetchstructure}.* The object returned by imap_fetchstructure stored in $this->structure[$mid]['obj'].** @var array $_structure* @access public* @see _declareParts* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/structure*/var $structure = array();/*** Contains various information about a message, separates inline parts from* attachments and contains the default part id for each message.** @var array $msg* @access public* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/msg*/var $msg = array();/*** (array)(mixed) Associative array containing information* gathered by {@link imap_headerinfo} or* {@link imap_rfc822_parse_headers}.** @var header array $header* @see getHeaders* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/header*/var $header = array();/*** (string) contains the various possible data types.* @var array $_dataTypes* @access private* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_dataTypes*/var $_dataTypes = array('text','multipart','message','application','audio','image','video','other');/*** (string) Contains the various possible encoding types.* @var array $_encodingTypes* @access private* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_encodingTypes*/var $_encodingTypes = array('7bit','8bit','binary','base64','quoted-printable','other');/*** (string) Contains the fields searched for and added to inline and attachment part* arrays. These are the 'in' and 'at' associative indices of the $msg member variable.* @var array $fields* @access public* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/fields*/var $fields = array('fname','pid','ftype','fsize','has_at','charset','cid');/*** Constructor. Optionally set the IMAP resource stream.** If IMAP connection arguments are not supplied, returns null. Accepts a URI* abstraction of the standard imap_open connection argument (see {@link connect})* or the imap resource indicator.** If the optional flags argument of imap_open needs to be set, then {@link connect}* should be called after either setting the {@link $option} member variable or* calling {@link setOptions}.** Since Mail_IMAPv2 0.1.0 creates an instance of PEAR_ErrorStack.* $options argument became $get_info argument see {@link connect}.** @param string $connection (optional) server URI | imap resource identifier* @param int $action** @tutorial http://www.smilingsouls.net/?content=Mail_IMAP/Mail_IMAP* @access public* @return BOOL|null|PEAR_Error* @see connect* @see imap_open*/function Mail_IMAPv2($connection = null, $get_info = true){$this->error = new PEAR_ErrorStack('Mail_IMAPv2');if (!empty($connection) && is_resource($connection)) {if (get_resource_type($connection) == 'imap') {$this->mailbox = $connection;} else {$this->error->push(Mail_IMAPv2_ERROR,'error',null,'Invalid imap resource passed to constructor.');}} else if (!empty($connection)) {$this->connect($connection, $get_info);}}/*** @todo Finish writing this method, and test it.*/function errorTemplate(){return array(// Generic ErrorMail_IMAPv2_ERROR => '%message%',Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY => 'Argument \'%arg%\' must be an array.',Mail_IMAPv2_ERROR_INVALID_OPTION => 'Indice \'%indice%\' for argument \'%arg%\' is not a valid option.',Mail_IMAPv2_ERROR_INVALID_PID => 'Supplied part id \'%pid%\' is not valid.',Mail_IMAPv2_ERROR_INVALID_ACTION => 'Action \'%action%\' is not a valid action for the \'%arg%\' argument.',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%');}/*** Wrapper method for {@link imap_open}. Accepts a URI abstraction in* the following format: imap://user:pass@mail.example.com:143/INBOX#notls* instead of the standard connection arguments used in imap_open.* Replace the protocol with one of pop3|pop3s imap|imaps nntp|nntps.* Place intial folder in the file path portion, and optionally append* tls|notls|novalidate-cert in the anchor portion of the URL. A port* number is optional, however, leaving it off could lead to a serious* degradation in preformance.** Since Mail_IMAPv2 0.1.0 the $options argument became the $get_info argument.* constants for action were removed and the argument is now a BOOL toggle.** @param string $uri server URI* @param bool $get_info* (optional) true by default. If true, make a call to {@link getMailboxInfo}* if false do not call {@link getMailboxInfo}* @return BOOL* @tutorial http://www.smilingsouls.net/index.php?content=Mail_IMAP/connect* @access public* @see imap_open*/function connect($uri, $get_info = true){if (!class_exists('Net_URL') && !@include_once('Net/URL.php')) {$this->error->push(Mail_IMAPv2_ERROR, 'error', null, 'Inclusion of Net_URL not successful.');return false;}$opt = (isset($this->option['open']))? $this->option['open'] : null;$net_url =& new Net_URL($uri);$uri = '{'.$net_url->host;if (!empty($net_url->port)) {$uri .= ':'.$net_url->port;}$secure = ('tls' == substr($net_url->anchor, 0, 3))? '' : '/ssl';$uri .= ('s' == (substr($net_url->protocol, -1)))? '/'.substr($net_url->protocol, 0, 4).$secure : '/'.$net_url->protocol;if (!empty($net_url->anchor)) {$uri .= '/'.$net_url->anchor;}$uri .= '}';$this->mailboxInfo['Mail_IMAPv2']['version'] = 'Mail_IMAPv2 0.2.0 Beta';$this->mailboxInfo['host'] = $uri;// Trim off the leading slash '/'if (!empty($net_url->path)) {$this->mailboxInfo['folder'] = substr($net_url->path, 1, (strlen($net_url->path) - 1));$uri .= $this->mailboxInfo['folder'];}$this->mailboxInfo['user'] = urldecode($net_url->user);if (false === ($this->mailbox = @imap_open($uri, urldecode($net_url->user), $net_url->pass, $opt))) {$this->error->push(Mail_IMAPv2_ERROR,'error',null,'Unable to build a connection to the specified mail server.');$ret = false;} else {$ret = true;}// get mailbox infoif ($get_info) {$this->getMailboxInfo(false);}return $ret;}/** Adds to the {@link $mailboxInfo} member variable information about the current* mailbox from {@link imap_mailboxmsginfo}.** Note: This method is automatically called on by default by {@link connect}.** @param string $connect server URL* @param bool $get_info* (optional) true by default. If true, make a call to {@link getMailboxInfo}* if false do not call {@link getMailboxInfo}** @return VOID|Array* @access public* @see imap_open* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getMailboxInfo*/function getMailboxInfo($ret = true){// It's possible that this function has already been called by $this->connect// If so, the 'Mailbox' indice will already exist and the user just wants// the contents of the mailboxInfo member variable.if (!isset($this->mailboxInfo['Mailbox'])) {$this->mailboxInfo = @array_merge($this->mailboxInfo,get_object_vars(imap_mailboxmsginfo($this->mailbox)));}return ($ret)? $this->mailboxInfo : true;}/*** Set the $option member variable, which is used to specify optional imap_* function* arguments (labeled in the manual as flags or options e.g. FT_UID, OP_READONLY, etc).** <b>Example:</b>* <code>* $msg->setOptions(array('body', 'fetchbody', 'fetchheader'), 'FT_UID');* </code>** This results in imap_body, imap_fetchbody and imap_fetchheader being passed the FT_UID* option in the flags/options argument where ever these are called on by Mail_IMAPv2.** Note: this method only sets optional imap_* arguments labeled as flags/options.** @param array $options - function names to pass the arugument to* @param string $constant - constant name to pass.* @return PEAR_Error|true* @access public* @see $option* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/setOptions*/function setOptions($options, $constant){if (is_array($options) && !empty($options)) {foreach ($options as $value) {if (!$this->option[$value] = @constant($constant)) {$this->error->push(Mail_IMAPv2_ERROR,'error',null,'The constant: '.$constant.' is not defined!');}}} else {$this->error->push(Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY,'error',array('arg' => '$options'));return false;}return true;}/*** Wrapper method for {@link imap_close}. Close the IMAP resource stream.** @return BOOL* @access public* @tutorial http://www.smilingsouls.net/index.php?content=Mail_IMAP/close* @see imap_close*/function close(){$opt = (isset($this->option['close']))? $this->option['close'] : null;return @imap_close($this->mailbox, $opt);}/*** Wrapper method for {@link imap_num_msg}.** @return int mailbox message count* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/messageCount* @access public* @see imap_num_msg*/function messageCount(){return @imap_num_msg($this->mailbox);}/*** Gather message information returned by {@link imap_fetchstructure} and recursively iterate* through each parts array. Concatenate part numbers in the following format `1.1`* each part id is separated by a period, each referring to a part or subpart of a* multipart message. Create part numbers as such that they are compatible with* {@link imap_fetchbody}.** @param int &$mid message id* @param array $sub_part recursive* @param string $sub_pid recursive parent part id* @param int $n recursive counter* @param bool $is_sub_part recursive* @param bool $skip_part recursive* @return mixed* @access protected* @see imap_fetchstructure* @see imap_fetchbody* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_declareParts*/function _declareParts(&$mid, $sub_part = null, $sub_pid = null, $n = 0, $is_sub_part = false, $skip_part = false, $last_was_signed = false){if (!is_array($sub_part)) {$opt = (isset($this->option['fetchstructure']))? $this->option['fetchstructure'] : null;$this->structure[$mid]['obj'] = @imap_fetchstructure($this->mailbox, $mid, $opt);}if (isset($this->structure[$mid]['obj']->parts) || is_array($sub_part)) {if (!$is_sub_part) {$parts = $this->structure[$mid]['obj']->parts;} else {$parts = $sub_part;$n++;}for ($p = 0, $i = 1; $p < count($parts); $n++, $p++, $i++) {// Skip the following...// multipart/mixed!// subsequent multipart/alternative if this part is message/rfc822// multipart/related//// Have noticed the existence of several other multipart/* types of messages// but have yet had the opportunity to test on those.$ftype = (empty($parts[$p]->type))?$this->_dataTypes[0].'/'.strtolower($parts[$p]->subtype):$this->_dataTypes[$parts[$p]->type].'/'.strtolower($parts[$p]->subtype);$this_was_signed = ($ftype == 'multipart/signed')? true : false;$skip_next = ($ftype == 'message/rfc822')? true : false;if ($ftype == 'multipart/mixed' && ($last_was_signed || $skip_part) ||$ftype == 'multipart/signed' ||$skip_part && $ftype == 'multipart/alternative' ||$ftype == 'multipart/related' && count($parts) == 1) {$n--;$skipped = true;} else {$skipped = false;$this->structure[$mid]['pid'][$n] = ($is_sub_part == false)? (string) "$i" : (string) "$sub_pid.$i";$this->structure[$mid]['ftype'][$n] = $ftype;$this->structure[$mid]['encoding'][$n] = (empty($parts[$p]->encoding))? $this->_encodingTypes[0] : $this->_encodingTypes[$parts[$p]->encoding];$this->structure[$mid]['fsize'][$n] = (!isset($parts[$p]->bytes) || empty($parts[$p]->bytes))? 0 : $parts[$p]->bytes;// Get extra parameters.if ($parts[$p]->ifparameters) {foreach ($parts[$p]->parameters as $param) {$this->structure[$mid][strtolower($param->attribute)][$n] = strtolower($param->value);}}// Force inline disposition if none is presentif ($parts[$p]->ifdisposition) {$this->structure[$mid]['disposition'][$n] = strtolower($parts[$p]->disposition);if ($parts[$p]->ifdparameters) {foreach ($parts[$p]->dparameters as $param) {if (strtolower($param->attribute) == 'filename') {$this->structure[$mid]['fname'][$n] = $param->value;break;}}}} else {$this->structure[$mid]['disposition'][$n] = 'inline';}if ($parts[$p]->ifid) {$this->structure[$mid]['cid'][$n] = $parts[$p]->id;}}if (isset($parts[$p]->parts) && is_array($parts[$p]->parts)) {if (!$skipped) {$this->structure[$mid]['has_at'][$n] = true;}$n = $this->_declareParts($mid, $parts[$p]->parts, $this->structure[$mid]['pid'][$n], $n, true, $skip_next, $this_was_signed);}else if (!$skipped) {$this->structure[$mid]['has_at'][$n] = false;}}if ($is_sub_part) {return $n;}} else {// $parts is not an array... message is flat$this->structure[$mid]['pid'][0] = 1;if (empty($this->structure[$mid]['obj']->type)) {$this->structure[$mid]['obj']->type = (int) 0;}if (isset($this->structure[$mid]['obj']->subtype)) {$this->structure[$mid]['ftype'][0] = $this->_dataTypes[$this->structure[$mid]['obj']->type].'/'.strtolower($this->structure[$mid]['obj']->subtype);}if (empty($this->structure[$mid]['obj']->encoding)) {$this->structure[$mid]['obj']->encoding = (int) 0;}$this->structure[$mid]['encoding'][0] = $this->_encodingTypes[$this->structure[$mid]['obj']->encoding];if (isset($this->structure[$mid]['obj']->bytes)) {$this->structure[$mid]['fsize'][0] = strtolower($this->structure[$mid]['obj']->bytes);}$this->structure[$mid]['disposition'][0] = 'inline';$this->structure[$mid]['has_at'][0] = false;// Go through the parameters, if anyif (isset($this->structure[$mid]['obj']->ifparameters) && $this->structure[$mid]['obj']->ifparameters) {foreach ($this->structure[$mid]['obj']->parameters as $param) {$this->structure[$mid][strtolower($param->attribute)][0] = $param->value;}}}return;}/*** Checks if the part has been parsed, if not calls on _declareParts to* parse the message.** @param int &$mid message id* @param bool $checkPid* @return void* @access protected* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_checkIfParsed*/function _checkIfParsed(&$mid, $checkPid = true, $get_mime = 'text/html'){if (!isset($this->structure[$mid]['pid'])) {$this->_declareParts($mid);}if ($checkPid == true && !isset($this->msg[$mid]['pid'])) {$this->_getDefaultPid($mid, $get_mime);}return;}/*** sets up member variables containing inline parts and attachments for a specific* part in member variable arrays beginning with 'in' and 'attach'. If inline parts* are present, sets {@link $inPid}, {@link $inFtype}, {@link $inFsize},* {@link $inHasAttach}, {@link $inInlineId} (if an inline CID is specified). If* attachments are present, sets, {@link $attachPid}, {@link $attachFsize},* {@link $attachHasAttach}, {@link $attachFname} (if a filename is present, empty* string otherwise).** @param int &$mid message id* @param int &$pid part id* @param bool $ret* false by default, if true returns the contents of the $in* and $attach* arrays.* If false method returns BOOL.** @param string $args (optional)* Associative array containing optional extra arguments. The following are the* possible indices.** $args['get_mime'] STRING* Values: text/plain|text/html, text/html by default. The MIME type for* the part to be displayed by default for each level of nesting.** $agrs['get_alternative'] BOOL* If true, includes the alternative part of a multipart/alternative* message in the $in* array. If veiwing text/html part by default this* places the text/plain part in the $in* (inline attachment array).** $args['retrieve_all'] BOOL* If true, gets all the message parts at once, this option will index* the entire message in the $in* and $attach* member variables regardless* of nesting (method indexes parts relevant to the current level of* nesting by default).** @return BOOL|Array* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getParts* @access public* @since PHP 4.2.0*/function getParts(&$mid, $pid = '0', $ret = false, $args = array()){if (!isset($args['get_mime'])) {$args['get_mime'] = 'text/html';}if (!isset($args['get_alternative'])) {$args['get_alternative'] = true;}$this->_checkIfParsed($mid, true, $args['get_mime']);if ($pid === '0') {$pid = $this->msg[$mid]['pid'];}if (count($this->structure[$mid]['pid']) == 1 && !isset($this->structure[$mid]['fallback'][0])) {return true;}// retrieve key for this part, so that the information may be accessedif (false !== ($i = array_search((string) $pid, $this->structure[$mid]['pid']))) {if (isset($args['retrieve_all']) && $args['retrieve_all'] == true) {$this->_scanMultipart($mid, $pid, $i, $args['get_mime'], 'add', 'none', 2, $args['get_alternative']);} else {if ($pid == $this->msg[$mid]['pid']) {$this->_scanMultipart($mid, $pid, $i, $args['get_mime'], 'add', 'top', 2, $args['get_alternative']);} else if ($this->structure[$mid]['ftype'][$i] == 'message/rfc822') {$this->_scanMultipart($mid, $pid, $i, $args['get_mime'], 'add', 'all', 1, $args['get_alternative']);}}} else {$this->error->push(Mail_IMAPv2_ERROR_INVALID_PID, 'error', array('pid' => $pid));return false;}return ($ret)? $this->msg[$mid] : true;}/*** Finds message parts relevant to the message part currently being displayed or* looks through a message and determines which is the best body to display.** @param int &$mid message id* @param int &$pid part id* @param int $i offset indice correlating to the pid* @param str $MIME one of text/plain or text/html the default MIME to retrieve.* @param str $action one of add|get* @param str $look_for one of all|multipart|top|none* @param int $pid_add determines the level of nesting.* @param bool $get_alternative* Determines whether the program retrieves the alternative part in a* multipart/alternative message.** @return string|false* @access private* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_scanMultipart*/function _scanMultipart(&$mid, &$pid, &$i, $MIME, $action = 'add', $look_for = 'all', $pid_add = 1, $get_alternative = true){// Find subparts, create variables// Create inline parts first, and attachments second// Get all top level parts, with the exception of the part currently being viewed// If top level part contains multipart/alternative go into that subpart to// retrieve the other inline message part to display// If this part is message/rfc822 get subparts that begin with this part id// Skip multipart/alternative message part// Find the displayable message, get text/plain part if $getInline is trueif ($action == 'add') {$excludeMIME = $MIME;$MIME = ($excludeMIME == 'text/plain')? 'text/html' : 'text/plain';$in = 0;$a = 0;} else if ($action == 'get') {$excludeMIME = null;}$pid_len = strlen($pid);$this_nesting = count(explode('.', $pid));foreach ($this->structure[$mid]['pid'] as $p => $id) {// To look at the next level of nesting one needs to determine at which level// of nesting the program currently resides, this needs to be independent of the// part id length, since part ids can get into double digits (let's hope they// don't get into triple digits!)// To accomplish this we'll explode the part id on the dot to get a count of the// nesting, then compare the string with the next level in.$nesting = count(explode('.', $this->structure[$mid]['pid'][$p]));switch ($look_for) {case 'all':{$condition = (($nesting == ($this_nesting + 1)) && $pid == substr($this->structure[$mid]['pid'][$p], 0, $pid_len));break;}case 'multipart':{$condition = (($nesting == ($this_nesting + 1)) && ($pid == substr($this->structure[$mid]['pid'][$p], 0)));break;}// Used if *all* parts are being retrievedcase 'none':{$condition = true;break;}// To gaurantee a top-level part, detect whether a period appears in the pid stringcase 'top':default:{if ($this->_isMultipart($mid, 'related') || $this->_isMultipart($mid, 'mixed')) {$condition = (!stristr($this->structure[$mid]['pid'][$p], '.') || ($nesting == 2) && substr($this->msg[$mid]['pid'], 0, 1) == substr($this->structure[$mid]['pid'][$p], 0, 1));} else {$condition = (!stristr($this->structure[$mid]['pid'][$p], '.'));}}}if ($condition == true) {if ($this->structure[$mid]['ftype'][$p] == 'multipart/alternative' || $this->structure[$mid]['ftype'][$p] == 'multipart/mixed') {foreach ($this->structure[$mid]['pid'] as $mp => $mpid) {// Part must begin with last matching part id and be two levels in$sub_nesting = count(explode('.', $this->structure[$mid]['pid'][$p]));if ($this->structure[$mid]['ftype'][$mp] == $MIME &&$get_alternative == true &&($sub_nesting == ($this_nesting + $pid_add)) &&($pid == substr($this->structure[$mid]['pid'][$mp], 0, strlen($this->structure[$mid]['pid'][$p])))) {if ($action == 'add') {$this->_addPart($in, $mid, $mp, 'in');break;} else if ($action == 'get' && !isset($this->structure[$mid]['fname'][$mp]) && empty($this->structure[$mid]['fname'][$mp])) {return $this->structure[$mid]['pid'][$mp];}} else if ($this->structure[$mid]['ftype'][$mp] == 'multipart/alternative' && $action == 'get') {// Need to match this PID to next level in$pid = (string) $this->structure[$mid]['pid'][$mp];$pid_len = strlen($pid);$this_nesting = count(explode('.', $pid));$pid_add = 2;continue;}}} else if ($this->structure[$mid]['disposition'][$p] == 'inline' && $this->structure[$mid]['ftype'][$p] != 'multipart/related' && $this->structure[$mid]['ftype'][$p] != 'multipart/mixed') {if (($action == 'add' &&$this->structure[$mid]['ftype'][$p] != $excludeMIME &&$pid != $this->structure[$mid]['pid'][$p]) || ($action == 'add' &&$this->structure[$mid]['ftype'][$p] == $excludeMIME &&isset($this->structure[$mid]['fname'][$p]) &&$pid != $this->structure[$mid]['pid'][$p]) || ($action == 'add' && isset($this->structure[$mid]['fallback'][0]))) {$this->_addPart($in, $mid, $p, 'in');} else if ($action == 'get' && $this->structure[$mid]['ftype'][$p] == $MIME && !isset($this->structure[$mid]['fname'][$p])) {return $this->structure[$mid]['pid'][$p];}} else if ($action == 'add' && $this->structure[$mid]['disposition'][$p] == 'attachment') {$this->_addPart($a, $mid, $p, 'at');}}}return false;}/*** Determines whether a message contains a multipart/(insert subtype here) part.* Only called on by $this->_scanMultipart** @return BOOL* @access private* @see _scanMultipart* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_isMultipart*/function _isMultipart($mid, $subtype){$ret = $this->extractMIME($mid, array('multipart/'.$subtype));return (!empty($ret) && is_array($ret) && count($ret) >= 1)? true : false;}/*** Looks to see if this part has any inline parts associated with it.* It looks up the message tree for parts with CID entries and* indexes those entries, whereas an algorithm may be ran to replace* inline CIDs with a part viewer.** @param int &$mid message id* @param string &$pid part id* @param array $secureMIME array of acceptable CID MIME types.** The $secureMIME argument allows you to limit the types of files allowed* in a multipart/related message, for instance, to prevent a browser from* automatically initiating download of a part that could contain potentially* malicious code.** Suggested MIME types:* text/plain, text/html, text/css, image/jpeg, image/pjpeg, image/gif* image/png, image/x-png, application/xml, application/xhtml+xml,* text/xml** MIME types are not limited by default.** @return array|false* On success returns an array of parts associated with the current message,* including the cid of the part, the part id and the MIME type.** @access public* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getRelatedParts*/function getRelatedParts(&$mid, &$pid, $secureMIME = array()){// Check to see if this part has already been parsed$this->_checkIfParsed($mid);// Message has a PID of 1.1.2// Cid parts are located at the prior level of nesting at 1.x// From the supplied PID, go back one level of nesting.// Compare the first number of the supplied PID against the current PID.// Look for a cid entry in the structure array.// Index the PID and CID of the part.//// Supplied pid must correspond to a text/html part.if (!empty($secureMIME) && is_array($secureMIME)) {$this->error->push(Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY,'error',array('arg' => '$secureMIME','actual_value' => $secureMIME));return false;}$related = array();if (isset($this->structure[$mid]['pid']) && is_array($this->structure[$mid]['pid'])) {if (strlen($pid) > 1) {$nesting = count(explode('.', $pid));$compare = substr($pid, 0, -4);foreach ($this->structure[$mid]['pid'] as $i => $rpid) {// This level of nesting is one above the message part// The beginning of the pid string of the related part matches that of the// beginning of the pid suppliedif (count(explode('.', $rpid)) == ($nesting - 1) && substr($rpid, 0, -2) == $compare) {$this->_getCIDs($mid, $i, $secureMIME, $related);}}} else if (strlen($pid) == 1) {// If the pid is in the first level of nesting, odds are the related parts are in the// sub level of nesting.foreach ($this->structure[$mid]['pid'] as $i => $rpid) {// The part is one level under and the first number matches that// of its parent part.if (count(explode('.', $rpid)) == 2 && substr($rpid, 0, 1) == $pid) {$this->_getCIDs($mid, $i, $secureMIME, $related);}}}} else {$this->error->push(Mail_IMAPv2_ERROR,'error',null,'Message structure does not exist.');}return (count($related) >= 1)? $related : false;}/*** Helper function for getRelatedParts** @return void* @access private* @see getRelatedParts* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_getCIDs*/function _getCIDs(&$mid, &$i, &$secureMIME, &$related){if ((isset($this->structure[$mid]['cid'][$i])) && (empty($secureMIME) || is_array($secureMIME) && in_array($this->structure[$mid]['ftype'][$i], $secureMIME))) {$related['cid'][] = $this->structure[$mid]['cid'][$i];$related['pid'][] = $this->structure[$mid]['pid'][$i];$related['ftype'][] = $this->structure[$mid]['ftype'][$i];}}/*** Destroys variables set by {@link getParts} and _declareParts.** @param integer &$mid message id* @return void* @access public* @see getParts* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/unsetParts*/function unsetParts(&$mid){unset($this->msg[$mid]);unset($this->structure[$mid]);return;}/*** Adds information to the member variable inline part 'in' and attachment 'at' arrays.** @param int &$n offset part counter* @param int &$mid message id* @param int &$i offset structure reference counter* @return void* @access private* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_addPart*/function _addPart(&$n, &$mid, &$i, $part){foreach ($this->fields as $field) {if (isset($this->structure[$mid][$field][$i]) && !empty($this->structure[$mid][$field][$i])) {$this->msg[$mid][$part][$field][$n] = $this->structure[$mid][$field][$i];}}$n++;return;}/*** Returns entire unparsed message body. See {@link imap_body} for options.** @param int &$mid message id* @return string|null* @tutorial http://www.smilingsouls.net/index.php?content=Mail_IMAPv2/getRawMessage* @access public* @see imap_body* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getRawMessage*/function getRawMessage(&$mid){$opt = (isset($this->option['body']))? $this->option['body'] : null;return imap_body($this->mailbox, $mid, $opt);}/*** Searches parts array set in $this->_declareParts() for a displayable message.* If the part id passed is message/rfc822 looks in subparts for a displayable body.* Attempts to return a text/html inline message part by default. And will* automatically attempt to find a text/plain part if a text/html part could* not be found.** Returns an array containing three associative indices; 'ftype', 'fname' and* 'message'. 'ftype' contains the MIME type of the message, 'fname', the original* file name, if any, empty string otherwise. And 'message', which contains the* message body itself which is returned decoded from base64 or quoted-printable if* either of those encoding types are specified, returns untouched otherwise.* Returns false on failure.** @param int &$mid message id* @param string $pid part id* @param int $action* (optional) options for body return. Set to one of the following:* Mail_IMAPv2_BODY (default), if part is message/rfc822 searches subparts for a* displayable body and returns the body decoded as part of an array.* Mail_IMAPv2_LITERAL, return the message for the specified $pid without searching* subparts or decoding the message (may return unparsed message) body is returned* undecoded as a string.* Mail_IMAPv2_LITERAL_DECODE, same as Mail_IMAPv2_LITERAL, except message decoding is* attempted from base64 or quoted-printable encoding, returns undecoded string* if decoding failed.** @param string $getPart* (optional) one of text/plain or text/html, allows the specification of the default* part to return from multipart messages, text/html by default.** @param int $attempt* (optional) used internally by getBody to track attempts at finding the* right part to display for the body of the message.** @return array|string|false* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getBody* @access public* @see imap_fetchbody* @see $this->getParts* @since PHP 4.2.0*/function getBody(&$mid, $pid = '1', $action = 0, $get_mime = 'text/html', $attempt = 1){$options = (isset($this->option['fetchbody']))? $this->option['fetchbody'] : null;if ($action == Mail_IMAPv2_LITERAL) {return @imap_fetchbody($this->mailbox, $mid, $pid, $options);}$this->_checkIfParsed($mid, true, $get_mime);if (false !== ($i = array_search((string) $pid, $this->structure[$mid]['pid']))) {if ($action == Mail_IMAPv2_LITERAL_DECODE) {$msg_body = @imap_fetchbody($this->mailbox, $mid, $pid, $options);return $this->_decodeMessage($msg_body, $this->structure[$mid]['encoding'][$i]);}// If this is an attachment, and the part is message/rfc822 update the pid to the subpart// If this is an attachment, and the part is multipart/alternative update the pid to the subpartif ($this->structure[$mid]['ftype'][$i] == 'message/rfc822' ||$this->structure[$mid]['ftype'][$i] == 'multipart/related' ||$this->structure[$mid]['ftype'][$i] == 'multipart/alternative') {$new_pid =($this->structure[$mid]['ftype'][$i] == 'message/rfc822' || $this->structure[$mid]['ftype'][$i] == 'multipart/related') ?$this->_scanMultipart($mid, $pid, $i, $get_mime, 'get', 'all', 1):$this->_scanMultipart($mid, $pid, $i, $get_mime, 'get', 'multipart', 1);// if a new pid for text/html couldn't be found, try again, this time look for text/plainswitch(true) {case (!empty($new_pid)):{$pid = $new_pid;break;}case (empty($new_pid) && $get_mime == 'text/html'):{return ($attempt == 1)? $this->getBody($mid, $pid, $action, 'text/plain', 2) : false;}case (empty($new_pid) && $get_mime == 'text/plain'):{return ($attempt == 1)? $this->getBody($mid, $pid, $action, 'text/html', 2) : false;}}}// Update the key for the new pidif (!empty($new_pid)) {if (false === ($i = array_search((string) $pid, $this->structure[$mid]['pid']))) {// Something's afoot!$this->error->push(Mail_IMAPv2_ERROR,'error',array('mid' => $mid,'pid' => $pid),'Unable to find a suitable replacement part ID. Message: may be poorly formed, corrupted, or not supported by the Mail_IMAPv2 parser.');return false;}}$msg_body = imap_fetchbody($this->mailbox, $mid, $pid, $options);if ($msg_body == null) {$this->error->push(Mail_IMAPv2_ERROR,'error',array('mid' => $mid,'pid' => $pid),'Message body is null.');return false;}// Decode message.// Because the body returned may not correspond with the original PID, return// an array which also contains the MIME type and original file name, if any.$body['message'] = $this->_decodeMessage($msg_body,$this->structure[$mid]['encoding'][$i],$this->structure[$mid]['charset'][$i]);$body['ftype'] = $this->structure[$mid]['ftype'][$i];$body['fname'] = (isset($this->structure[$mid]['fname'][$i]))? $this->structure[$mid]['fname'][$i] : '';$body['charset'] = $this->structure[$mid]['charset'][$i];return $body;}else{$this->error->push(Mail_IMAPv2_ERROR_INVALID_PID,'error',array('pid' => $pid));return false;}return false;}/*** Decode a string from quoted-printable or base64 encoding. If* neither of those encoding types are specified, returns string* untouched.** @param string &$body string to decode* @param string &$encoding encoding to decode from.* @return string* @access private* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_decodeMessage*/function _decodeMessage(&$body, &$encoding, &$charset){switch ($encoding) {case 'quoted-printable':return ($charset == 'utf-8')? utf8_decode(imap_utf8(imap_qprint($body))) : imap_qprint($body);case 'base64': return imap_base64($body);default: return $body;}}/*** Searches structure defined in $this->_declareParts for the top-level default message.* Attempts to find a text/html default part, if no text/html part is found,* automatically attempts to find a text/plain part. Returns the part id for the default* top level message part on success. Returns false on failure.** @param int &$mid message id* @param string $getPart* (optional) default MIME type to look for, one of text/html or text/plain* text/html by default.* @param int $attempt* (optional) Used internally by _getDefaultPid to track the method's attempt* at retrieving the correct default part to display.** @return string* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_getDefaultPid* @access private*/function _getDefaultPid(&$mid, $get_mime = 'text/html', $attempt = 1){// Check to see if this part has already been parsed$this->_checkIfParsed($mid, false);// Look for a text/html message part// If no text/html message part was found look for a text/plain message part$part =($get_mime == 'text/html') ?array('text/html', 'text/plain'):array('text/plain', 'text/html');foreach ($part as $mime) {if (0 !== count($msg_part = @array_keys($this->structure[$mid]['ftype'], $mime))) {foreach ($msg_part as $i) {if ($this->structure[$mid]['disposition'][$i] == 'inline' && !stristr($this->structure[$mid]['pid'][$i], '.')) {$this->msg[$mid]['pid'] = $this->structure[$mid]['pid'][$i];return $this->structure[$mid]['pid'][$i];}}}}// If no text/plain or text/html part was found// Look for a multipart/alternative part$mp_nesting = 1;$pid_len = 1;if (is_array($this->structure[$mid]['pid'])) {foreach ($this->structure[$mid]['pid'] as $p => $id) {$nesting = count(explode('.', $this->structure[$mid]['pid'][$p]));if (!isset($mpid)) {if ($nesting == 1 && isset($this->structure[$mid]['ftype'][$p]) && ($this->structure[$mid]['ftype'][$p] == 'multipart/related')) {$mp_nesting = 2;$pid_len = 3;continue;}if ($nesting == $mp_nesting &&isset($this->structure[$mid]['ftype'][$p]) &&($this->structure[$mid]['ftype'][$p] == 'multipart/alternative' || $this->structure[$mid]['ftype'][$p] == 'multipart/mixed')) {$mpid = $this->structure[$mid]['pid'][$p];continue;}}if (isset($mpid) && $nesting == ($mp_nesting + 1) &&$this->structure[$mid]['ftype'][$p] == $get_mime &&$mpid == substr($this->structure[$mid]['pid'][$p], 0, $pid_len)) {$this->msg[$mid]['pid'] = $this->structure[$mid]['pid'][$p];return $this->structure[$mid]['pid'][$p];}}} else {$this->error->push(Mail_IMAPv2_ERROR, 'error', null, 'Message structure does not exist.');}// if a text/html part was not found, call on the function again// and look for text/plain// if the application was unable to find a text/plain partswitch ($get_mime) {case 'text/html':{$rtn = ($attempt == 1)?$this->_getDefaultPid($mid, 'text/plain', 2):false;break;}case 'text/plain':{$rtn = ($attempt == 1)?$this->_getDefaultPid($mid, 'text/html', 2):false;break;}default:{$rtn = false;}}if ($rtn == false && $attempt == 2) {if (isset($this->structure[$mid]['ftype'][0])) {$this->structure[$mid]['fallback'][0] = true;} else {$this->error->push(Mail_IMAPv2_ERROR, 'error', null, 'Message contains no MIME types.');}}$this->msg[$mid]['pid'] = ($rtn == false)? 1 : $rtn;return $this->msg[$mid]['pid'];}/*** Searches all message parts for the specified MIME type. Use {@link getBody}* with $action option Mail_IMAPv2_LITERAL_DECODE to view MIME type parts retrieved.* If you need to access the MIME type with filename use normal {@link getBody}* with no action specified.** Returns an array of part ids on success.* Returns false if MIME couldn't be found, or on failure.** @param int &$mid message id* @param string|array $MIMEs mime type to extract* @return array|false* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/extractMIME* @access public*/function extractMIME(&$mid, $MIMEs){$this->_checkIfParsed($mid);if (is_array($this->structure[$mid]['ftype'])) {if (is_array($MIMEs)) {foreach ($MIMEs as $MIME) {if (0 !== count($keys = array_keys($this->structure[$mid]['ftype'], $MIME))) {foreach ($keys as $key) {$rtn[] = $this->structure[$mid]['pid'][$key];}}}} else {$this->error->push(Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY,'error',array('arg' => '$MIMEs','actual_value' => $MIMEs));}} else {$this->error->push(Mail_IMAPv2_ERROR,'error',null,'Member variable $this->structure[\'ftype\'] is not an array');}return (isset($rtn))? $rtn : false;}/*** Set member variable {@link $rawHeaders} to contain Raw Header information* for a part. Returns default header part id on success, returns false on failure.** @param int &$mid message_id* @param string $pid (optional) part id to retrieve headers for* @param bool $rtn* Decides what to return. One of true|false|return_pid* If true return the raw headers (returns the headers by default)** @return string|false* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getRawHeaders* @access public* @see imap_fetchbody* @see getHeaders*/function getRawHeaders(&$mid, $pid = '0', $rtn = true, $pid_check = false){$this->_checkIfParsed($mid);if ($pid == $this->msg[$mid]['pid']) {$pid = (string) '0';}if ($pid !== '0') {if (false === ($pid = $this->_defaultHeaderPid($mid, $pid))) {$this->error->push(Mail_IMAPv2_ERROR_INVALID_PID, 'error', array('pid' => $pid));return false;}}if ($pid === '0' && $pid_check) {return true;} else if ($pid_check) {$rtn = true;}if ($pid === '0') {$opt = (isset($this->option['fetchheader']))? $this->option['fetchheader'] : null;$raw_headers = @imap_fetchheader($this->mailbox, $mid, $opt);} else {$opt = (isset($this->option['fetchbody']))? $this->option['fetchbody'] : null;$raw_headers = @imap_fetchbody($this->mailbox, $mid, $pid, $opt);}if ($rtn) {return $raw_headers;} else {$this->header[$mid]['raw'] = $raw_headers;return true;}}/*** Set member variable containing header information. Creates an array containing* associative indices referring to various header information. Use {@link var_dump}* or {@link print_r} on the {@link $header} member variable to view information* gathered by this function.** If $ret is true, returns array containing header information on success and false* on failure.** If $ret is false, adds the header information to the $header member variable* and returns BOOL.** @param int &$mid message id* @param string &$pid (optional) part id to retrieve headers for.* @param bool $rtn* (optional) If true return the headers, if false, assign to $header member variable.** @param array $args* (optional) Associative array containing extra arguments.** $args['from_length'] int* From field length for imap_headerinfo.** $args['subject_length'] int* Subject field length for imap_headerinfo** $args['default_host'] string* Default host for imap_headerinfo & imap_rfc822_parse_headers** @return Array|BOOL* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getHeaders* @access public* @see getParts* @see imap_fetchheader* @see imap_fetchbody* @see imap_headerinfo* @see imap_rfc822_parse_headers*/function getHeaders(&$mid, $pid = '0', $rtn = false, $args = array()){$this->_checkIfParsed($mid);if ($pid == $this->msg[$mid]['pid']) {$pid = '0';}if ($pid !== '0') {if (false === ($raw_headers = $this->getRawHeaders($mid, $pid, true, true))) {return false;}if ($raw_headers === true) {$pid = '0';}}if (!isset($args['from_length'])) {$args['from_length'] = 1024;}if (!isset($args['subject_length'])) {$args['subject_length'] = 1024;}if (!isset($args['default_host'])) {$args['default_host'] = null;}// Parse the headers$header_info =($pid === '0')?imap_headerinfo($this->mailbox, $mid, $args['from_length'], $args['subject_length'], $args['default_host']):imap_rfc822_parse_headers($raw_headers, $args['default_host']);// Since individual member variable creation might create extra overhead,// and having individual variables referencing this data and the original// object would be too much as well, we'll just copy the object into an// associative array, preform clean-up on those elements that require it,// and destroy the original object after copying.if (!is_object($header_info)) {$this->error->push(Mail_IMAPv2_ERROR_INVALID_PID,'error',array('pid' => $pid));return false;}$headers = get_object_vars($header_info);foreach ($headers as $key => $value) {if (!is_object($value) && !is_array($value)) {// Decode all the headers using utf8_decode(imap_utf8())$this->header[$mid][$key] = utf8_decode(imap_utf8($value));}}// copy udate or create it from date string.$this->header[$mid]['udate'] = (isset($header_info->udate) && !empty($header_info->udate))?$header_info->udate:strtotime($header_info->Date);// clean up addresses$line = array('from','reply_to','sender','return_path','to','cc','bcc');for ($i = 0; $i < count($line); $i++) {if (isset($header_info->$line[$i])) {$this->_parseHeaderLine($mid, $header_info->$line[$i], $line[$i]);}}// All possible information has been copied, destroy original objectunset($header_info);return ($rtn)? $this->header[$mid] : false;}/*** Parse header information from the given line and add it to the {@link $header}* array. This function is only used by {@link getRawHeaders}.** @param string &$line* @param string $name* @return array* @access private* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_parseHeaderLine*/function _parseHeaderLine(&$mid, &$line, $name){if (isset($line) && count($line) >= 1) {$i = 0;foreach ($line as $object) {if (isset($object->adl)) {$this->header[$mid][$name.'_adl'][$i] = $object->adl;}if (isset($object->mailbox)) {$this->header[$mid][$name.'_mailbox'][$i] = $object->mailbox;}if (isset($object->personal)) {$this->header[$mid][$name.'_personal'][$i] = $object->personal;}if (isset($object->host)) {$this->header[$mid][$name.'_host'][$i] = $object->host;}if (isset($object->mailbox) && isset($object->host)) {$this->header[$mid][$name][$i] = $object->mailbox.'@'.$object->host;}$i++;}// Return the full lines "toaddress", "fromaddress", "ccaddress"... etcif (isset(${$name.'address'})) {$this->header[$mid][$name.'address'][$i] = ${$name.'address'};}}}/*** Finds and returns a default part id for headers and matches any sub message part to* the appropriate headers. Returns false on failure and may return a value that* evaluates to false, use the '===' operator for testing this function's return value.** @param int &$mid message id* @param string $pid part id* @return string|false* @access private* @see getHeaders* @see getRawHeaders* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/_defaultHeaderPid*/function _defaultHeaderPid(&$mid, $pid){// pid is modified in this function, so don't pass by reference (will create a logic error)$this->_checkIfParsed($mid);// retrieve key for this part, so that the information may be accessedif (false !== ($i = array_search((string) $pid, $this->structure[$mid]['pid']))) {// If this part is message/rfc822 display headers for this partif ($this->structure[$mid]['ftype'][$i] == 'message/rfc822') {$rtn = (string) $pid.'.0';} else if ($pid == $this->msg[$mid]['pid']) {$rtn = (string) '0';} else {$pid_len = strlen($pid);$this_nesting = count(explode('.', $pid));// Deeper searching may be required, go back to this part's parent.if (!stristr($pid, '.') || ($this_nesting - 1) == 1) {$rtn = (string) '0';} else if ($this_nesting > 2) {// Look at previous parts until a message/rfc822 part is found.for ($pos = $this_nesting - 1; $pos > 0; $pos -= 1) {foreach ($this->structure[$mid]['pid'] as $p => $aid) {$nesting = count(explode('.', $this->structure[$mid]['pid'][$p]));if ($nesting == $pos &&($this->structure[$mid]['ftype'][$p] == 'message/rfc822' || $this->structure[$mid]['ftype'][$p] == 'multipart/related')) {// Break iteration and return!return (string) $this->structure[$mid]['pid'][$p].'.0';}}}$rtn = ($pid_len == 3)? (string) '0' : false;} else {$rtn = false;}}return $rtn;} else {// Something's afoot!$this->error->push(Mail_IMAPv2_ERROR_INVALID_PID,'error',array('pid' => $pid));return false;}}/*** Destroys variables set by {@link getHeaders}.** @param int &$mid message id* @return void* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/unsetHeaders* @access public* @see getHeaders*/function unsetHeaders(&$mid){unset($this->header[$mid]);return;}/*** Converts an integer containing the number of bytes in a file to one of Bytes, Kilobytes,* Megabytes, or Gigabytes, appending the unit of measurement.** This method may be called statically.** @param int $bytes* @return string* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/convertBytes* @access public* @static*/function convertBytes($bytes){switch (true) {case ($bytes < pow(2,10)):{return $bytes.' Bytes';}case ($bytes >= pow(2,10) && $bytes < pow(2,20)):{return round($bytes / pow(2,10), 0).' KB';}case ($bytes >= pow(2,20) && $bytes < pow(2,30)):{return round($bytes / pow(2,20), 1).' MB';}case ($bytes > pow(2,30)):{return round($bytes / pow(2,30), 2).' GB';}}}/*** Wrapper function for {@link imap_delete}. Sets the marked for deletion flag. Note: POP3* mailboxes do not remember flag settings between connections, for POP3 mailboxes* this function should be used in addtion to {@link expunge}.** @param int &$mid message id* @return BOOL* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/delete* @access public* @see imap_delete* @see expunge*/function delete(&$mid, $separator = "<br />\n"){if (!is_array($mid)) {if (!@imap_delete($this->mailbox, $mid)) {$this->error->push(Mail_IMAPv2_ERROR,'error',array('mid' => $mid),'Unable to mark message for deletion.');$rtn = false;} else {$rtn = true;}} else {foreach ($mid as $id) {if (!@imap_delete($this->mailbox, $id)) {$this->error->push(Mail_IMAPv2_ERROR,'error',array('mid' => $id),'Unable to mark message for deletion.');$rtn = false;}}$rtn = true;}return $rtn;}/*** Wrapper function for {@link imap_expunge}. Expunges messages marked for deletion.** @return BOOL* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/expunge* @access public* @see imap_expunge* @see delete*/function expunge(){if (imap_expunge($this->mailbox)) {return true;} else {$this->error->push(Mail_IMAPv2_ERROR, 'error', null, 'Unable to expunge mailbox.');return false;}}/*** Wrapper function for {@link imap_errors}. Implodes the array returned by imap_errors,* (if any) and returns the error text.** @param bool $handler* How to handle the imap error stack, true by default. If true adds the errors* to the PEAR_ErrorStack object. If false, returns the imap error stack.** @param string $seperator* (optional) Characters to seperate each error message. "<br />\n" by default.** @return bool|string* @access public* @see imap_errors* @see alerts* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/errors*/function errors($handler = true, $seperator = "<br />\n"){$errors = imap_errors();if (empty($errors)) {return false;}if ($handler) {foreach ($errors as $error) {$this->error->push(Mail_IMAPv2_ERROR,'error',null,$error);}return true;}return implode($seperator, $errors);}/*** Wrapper function for {@link imap_alerts}. Implodes the array returned by imap_alerts,* (if any) and returns the text.** @param bool $handler* How to handle the imap error stack, true by default. If true adds the alerts* to the PEAR_ErrorStack object. If false, returns the imap alert stack.** @param string $seperator Characters to seperate each alert message. '<br />\n' by default.* @return bool|string* @access public* @see imap_alerts* @see errors* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/alerts*/function alerts($handler = true, $seperator = "<br />\n"){$alerts = imap_alerts();if (empty($alerts)) {return false;}if ($handler) {foreach ($alerts as $alert) {$this->error->push(Mail_IMAPv2_ERROR,'notice',null,$alert);}return true;}return implode($seperator, $alerts);}/*** Retreives information about the current mailbox's quota. Rounds up quota sizes and* appends the unit of measurment. Returns information in a multi-dimensional associative* array.** @param string $folder Folder to retrieve quota for.* @param BOOL $rtn* (optional) true by default, if true return the quota if false merge quota* information into the $mailboxInfo member variable.* @return array|false* @access public* @see imap_get_quotaroot* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getQuota*/function getQuota($folder = null, $rtn = true){if (empty($folder) && !isset($this->mailboxInfo['folder'])) {$folder = 'INBOX';} else if (empty($folder) && isset($this->mailboxInfo['folder'])) {$folder = $this->mailboxInfo['folder'];}$q = @imap_get_quotaroot($this->mailbox, $folder);// STORAGE Values are returned in KB// Convert back to bytes first// Then round these to the simpliest unit of measurementif (isset($q['STORAGE']['usage']) && isset($q['STORAGE']['limit'])) {$q['STORAGE']['usage'] = $this->convertBytes($q['STORAGE']['usage'] * 1024);$q['STORAGE']['limit'] = $this->convertBytes($q['STORAGE']['limit'] * 1024);}if (isset($q['MESSAGE']['usage']) && isset($q['MESSAGE']['limit'])) {$q['MESSAGE']['usage'] = $this->convertBytes($q['MESSAGE']['usage']);$q['MESSAGE']['limit'] = $this->convertBytes($q['MESSAGE']['limit']);}if (empty($q['STORAGE']['usage']) && empty($q['STORAGE']['limit'])) {$this->error->push(Mail_IMAPv2_ERROR,'error',null,'Quota not available for this server.');return false;} else if ($rtn) {return $q;} else {$this->mailboxInfo = array_merge($this->mailboxInfo, $q);return true;}}/*** Wrapper function for {@link imap_setflag_full}. Sets various message flags.* Accepts an array of message ids and an array of flags to be set.** The flags which you can set are "\\Seen", "\\Answered", "\\Flagged",* "\\Deleted", and "\\Draft" (as defined by RFC2060).** Warning: POP3 mailboxes do not remember flag settings from connection to connection.** @param array $mids Array of message ids to set flags on.* @param array $flags Array of flags to set on messages.* @param int $action Flag operation toggle one of set|clear* @param int $options* (optional) sets the forth argument of {@link imap_setflag_full} or {@imap_clearflag_full}.** @return BOOL* @throws Message IDs and Flags are to be supplied as arrays. Remedy: place message ids* and flags in arrays.* @access public* @see imap_setflag_full* @see imap_clearflag_full* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/setFlags*/function setFlags($mids, $flags, $action = 'set'){if (!is_array($mids)) {$this->error->push(Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY,'error',array('arg' => '$mids'));return false;}if (!is_array($flags)) {$this->error->push(Mail_IMAPv2_ERROR_ARGUMENT_REQUIRES_ARRAY,'error',array('arg' => '$flags'));return false;}switch ($action) {case 'set':{$func = 'imap_setflag_full';break;}case 'clear':{$func = 'imap_clearflag_full';break;}default:{$this->error->push(Mail_IMAPv2_ERROR_INVALID_ACTION,'error',array('action' => $action,'arg' => '$action'));return false;}}$opt =(isset($this->option[$action.'flag_full']))?$this->option[$action.'flag_full']:null;return @$func($this->mailbox, implode(',', $mids), implode(' ', $flags), $opt);}/*** Wrapper method for imap_list. Calling on this function will return a list of mailboxes.* This method receives the host argument automatically via $this->connect in the* $this->mailboxInfo['host'] variable if a connection URI is used.** @param string (optional) host name.* @return array|false list of mailboxes on the current server.* @access public* @see imap_list* @tutorial http://www.smilingsouls.net/Mail_IMAP?content=Mail_IMAP/getMailboxes*/function getMailboxes($host = null, $pattern = '*', $rtn = true){if (empty($host) && !isset($this->mailboxInfo['host'])) {$this->error->push(Mail_IMAPv2_ERROR,'error',null,'Supplied host is not valid!');return false;} else if (empty($host) && isset($this->mailboxInfo['host'])) {$host = $this->mailboxInfo['host'];}if ($list = @imap_list($this->mailbox, $host, $pattern)) {if (is_array($list)) {foreach ($list as $key => $val) {$mb[$key] = str_replace($host, '', imap_utf7_decode($val));}}} else {$this->error->push(Mail_IMAPv2_ERROR, 'error', null, 'Cannot fetch mailbox names.');return false;}if ($rtn) {return $mb;} else {$this->mailboxInfo = array_merge($this->mailboxInfo, $mb);}}}?>