ACC SHELL
<?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 Error
Mail_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 info
if ($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 present
if ($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 any
if (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 accessed
if (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 true
if ($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 retrieved
case 'none':
{
$condition = true;
break;
}
// To gaurantee a top-level part, detect whether a period appears in the pid string
case '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 supplied
if (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 subpart
if (
$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/plain
switch(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 pid
if (!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 part
switch ($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 object
unset($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"... etc
if (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 accessed
if (false !== ($i = array_search((string) $pid, $this->structure[$mid]['pid']))) {
// If this part is message/rfc822 display headers for this part
if ($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 measurement
if (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);
}
}
}
?>
ACC SHELL 2018