<?php
/**
* Filename.......: imaext.php
* Project........: V-webmail
* Last Modified..: $Date: 2006/03/06 09:48:21 $
* CVS Revision...: $Revision: 1.3 $
* Copyright......: 2001-2004 Richard Heyes
*/
class mailaccess_imapext extends mailaccess
{
/**
* The connection resource
* @var resource
*/
var $conn;
/**
* Hostname of the server we're connected to
* @var string
*/
var $host;
/**
* Port of the server
* @var integer
*/
var $port;
/**
* Username
* @var string
*/
var $user;
/**
* Password
* @var string
*/
var $pass;
/**
* Type of account we're connected to
* @var string
*/
var $type;
/**
* Mailbox/folder we're connected to
* @var string
*/
var $mbox;
/**
* Storage for errors
* @var array
*/
var $errors;
/**
* Returns any errors that have been raised
*
* @return string Errors
*/
function getErrors()
{
$this->errors = array_merge(@$this->errors, imap_errors());
return '(' . implode(', ', $this->errors) . ')';
}
/**
* Returns last error. Will call getErrors() first.
*
* @return string Last error message
*/
function getLastError()
{
$this->getErrors();
return !empty($this->errors) ? $this->errors[count($this->errors) - 1] : '';
}
/**
* Connects. Returns true/false.
*
* @param string $host Hostname
* @param string $user Username
* @param string $pass Password
* @param integer $port Port
* @param string $type Account type
* @param string $mbox Mailbox/folder
*
* @return boolean Success or not
*/
function connect($host, $user, $pass, $port, $type, $mbox = 'INBOX')
{
$options['type'] = 'mysql';
$options['user'] = 'root';
$options['pass'] = '';
$options['dbas'] = 'vwebmail';
$this->db = &database::factory($options);
if ($this->db->isConnected()) {
$this->conn = $imp;
$this->host = $host;
$this->port = $port;
$this->type = $type;
$this->mbox = $mbox;
$this->user = $user;
$this->pass = $pass;
return true;
} else {
return false;
}
}
/**
* Returns whether a we're connected or not
*
* @return boolean Connected or not
*/
function isConnected()
{
return $this->db->isConnected();
}
/**
* Reconnects to server. Used for looking
* at mailboxes other than the current.
*
* @param string $mbox Mailbox/folder
* @return boolean Success or not
*/
function reconnect($mbox = 'INBOX')
{
$info = $this->getFolderInfo($mbox);
$this->mbox = $mbox;
}
/**
* Closes a connection
*
* @return boolean Success or not
*/
function close()
{
return $this->db->disconnect();
}
/**
* Returns capabilities
*
* @param string $attrib Capability to determine
* @return boolean Capable or not
*/
function capabilities($attrib)
{
switch ($attrib) {
case 'folders':
case 'search':
return true;
break;
default:
return false;
}
}
/* Got here */
/**
* Returns structured header information
*/
function headerinfo($msg_id)
{
$info = @imap_headerinfo($this->conn, $msg_id);
return $info ? $info : false;
}
/**
* Returns messages raw headers, as is.
* Includes a blank line at the end.
*/
function getRawHeaders($msg_id, $mime_id = null)
{
$options = $GLOBALS['SESSION']['email']['use_uids'] ? FT_UID : 0;
if (!isset($mime_id)) {
return imap_fetchheader($this->conn, $msg_id, $options);
} else {
$mime_id .= '.0';
return imap_fetchbody($this->conn, $msg_id, $mime_id, $options);
}
}
/**
* Returns parsed headers
*/
function getParsedHeaders($msg_id, $mime_id = null, $decode = false)
{
$headers = $this->getRawHeaders($msg_id, $mime_id);
$headers = preg_split('/\r?\n/', trim($headers));
// Unfold headers
for ($i=count($headers)-1; $i>=0; $i--) {
if ($headers[$i][0] == ' ' OR $headers[$i][0] == "\t") {
$headers[$i-1] .= preg_replace('/^\s+(.*)$/', ' \1', $headers[$i]);
unset($headers[$i]);
}
}
//$headers = array_values($headers);
$headers = array_values($headers);
foreach ($headers as $key => $value) {
$headers[$key] = decode_header($value);
}
// Make associative array out of indexed array
foreach ($headers as $header) {
preg_match('/^(.+):(.*)$/U', $header, $matches);
$header_name = strtolower(trim($matches[1]));
if (!empty($parsed_headers[$header_name])){
if (is_array($parsed_headers[$header_name])) {
$parsed_headers[$header_name][] = trim($matches[2]);
} else {
$parsed_headers[$header_name] = array_merge(array($parsed_headers[$header_name]), array(trim($matches[2])));
}
} else {
$parsed_headers[$header_name] = trim($matches[2]);
}
}
return $parsed_headers;
}
/**
* Fetches body of msg
*/
function getEntireMail($msg_id, $mime_id = null)
{
if (!empty($mime_id)) {
return $this->getPart($msg_id, $mime_id);
} else {
$options = $GLOBALS['SESSION']['email']['use_uids'] ? FT_UID : 0;
return sprintf("%s\r\n\r\n%s", rtrim($this->getRawHeaders($msg_id)), imap_body($this->conn, $msg_id, $options));
}
}
/**
* Fetchs a part
*/
function getPart($msg_id, $part)
{
$options = $GLOBALS['SESSION']['email']['use_uids'] ? FT_UID : 0;
return imap_fetchbody($this->conn, $msg_id, $part, $options);
}
/**
* Returns structure of an email
*/
function fetchStructure($msg_id)
{
$options = $GLOBALS['SESSION']['email']['use_uids'] ? FT_UID : 0;
return imap_fetchstructure($this->conn, $msg_id, $options);
}
/**
* Mime decoding function
*/
function &parseStructure($parts, &$message, $is_related = false)
{
global $msg_id;
$ctypes = array('text', 'multipart', 'message', 'application', 'audio', 'image', 'video', 'other', 'other', 'binary');
$encodings = array('7bit', '8bit', 'binary', 'base64', 'quoted-printable', 'other', 'us-ascii'); // FIXME us-ascii correct ?
$p_ctype = $ctypes[(int)@$parts->type];
$s_ctype = strtolower($parts->subtype);
$content_type = $p_ctype . '/' . $s_ctype;
$encoding = $encodings[(int)@$parts->encoding];
$dparameters = $parts->ifdparameters ? $parts->dparameters : array();
$cparameters = $parts->ifparameters ? $parts->parameters : array();
$description = $parts->ifdescription ? $parts->description : ($p_ctype == 'message' ? 'Attached message' : '');
$cid = $parts->ifid ? preg_replace('/^<(.*)>$/U', '\1', trim($parts->id)) : '';
$size = @$parts->bytes ? $parts->bytes : 0;
$partno = @$parts->mime_id;
switch ($content_type) {
// FIXME Optimise this
case 'multipart/report':
case 'multipart/signed':
case 'multipart/mixed':
case 'multipart/alternative':
case 'multipart/related':
case 'multipart/digest':
case 'multipart/parallel':
if (!empty($parts->parts)) {
for ($i=0; $i<count($parts->parts); $i++) {
$message = &$this->parseStructure($parts->parts[$i], $message, $content_type == 'multipart/related');
}
}
break;
case 'text/plain':
case 'text/html':
// Could be html or text...
$body_var = 'body_' . $s_ctype;
$char_var = $p_ctype . '_charset';
if (!isset($message->$body_var) AND @strtolower($parts->disposition) != 'attachment') {
@$message->$body_var .= decode_body($this->getPart($msg_id, $parts->mime_id), $encoding) . "\r\n";
$message->$char_var = (!empty($cparameters[0]->charset)) ? $cparameters[0]->charset : 'iso-8859-1';
break;
}
case 'message/delivery-status':
case 'message/rfc822':
default:
$member = $is_related ? 'related' : 'attachments';
$filename = null;
$name = null;
foreach ($dparameters as $key => $value) {
if (strtolower($value->attribute) == 'filename') {
$filename = $value->value;
}
}
foreach ($cparameters as $key => $value) {
if (strtolower($value->attribute) == 'name') {
$name = $value->value;
}
}
$message->{$member}[] = array(
'c_type' => $content_type,
'encoding' => $encoding,
'mime_id' => $partno,
'size' => $size,
'c_id' => $cid,
'description' => $description,
'filename' => $filename,
'name' => $name
);
}
return $message;
}
/**
* Assigns mime_ids to the message structure
*/
function assignMimeNumbers(&$structure, $no_refs = false, $mime_number = '', $prepend = '')
{
$return = array();
if (!empty($structure->parts)) {
if ($mime_number != '') {
$structure->mime_id = $prepend . $mime_number;
$return[$prepend . $mime_number] = &$structure;
}
for ($i = 0; $i < count($structure->parts); $i++) {
// 2 == message
if($structure->type == 2){
$prepend = $prepend . $mime_number . '.';
$_mime_number = '';
}else{
$_mime_number = ($mime_number == '' ? $i + 1 : sprintf('%s.%s', $mime_number, $i + 1));
}
$arr = &$this->assignMimeNumbers($structure->parts[$i], $no_refs, $_mime_number, $prepend);
foreach ($arr as $key => $val) {
$no_refs ? $return[$key] = '' : $return[$key] = &$arr[$key];
}
}
} else {
if ($mime_number == '') {
$mime_number = '1';
}
$structure->mime_id = $prepend . $mime_number;
$no_refs ? $return[$prepend . $mime_number] = '' : $return[$prepend . $mime_number] = &$structure;
}
return $return;
}
/**
* Returns number of messages
*/
function numMsg()
{
return (int)imap_num_msg($this->conn);
}
/**
* Marks messages for deletion. $msg_ids
* can be a single msg_id, or a comma
* seperated list of msg_ids.
*/
function delete($msg_ids, &$message, $force = false)
{
global $SESSION;
$deleted_items_folder = $SESSION['userprefs']['settings']['deleted_items_folder'];
// Need to determine which msgs are currently marked as deleted, and unmark
// them since expunging would remove them.
$d_msg_ids = $this->getMsgIds(1, 1, 1, 0, 'DELETED', $num_msgs);
$this->setflag(implode(',', $d_msg_ids), 'undeleted');
if (!$this->capabilities('folders') OR $SESSION['email']['mbox'] == $deleted_items_folder OR $force) {
$options = $GLOBALS['SESSION']['email']['use_uids'] ? FT_UID : 0;
imap_delete($this->conn, is_array($msg_ids) ? implode(',', $msg_ids) : $msg_ids, $options);
$this->expunge();
$message = null;
} else {
// Move to deleted items folder
if (!$this->folderExists($deleted_items_folder)) {
if (!$this->createFolder($deleted_items_folder)) {
$message = lang('Failed to create Trash folder');
}
$this->subscribeFolder($deleted_items_folder);
}
$message = $this->moveMsg($msg_ids, $deleted_items_folder) ? null : lang('Move of message failed');
$this->expunge();
}
$this->setflag(implode(',', $d_msg_ids), 'deleted');
}
/**
* Function to *actually* delete the
* messages currently marked for deletion
*/
function expunge()
{
return imap_expunge($this->conn);
}
/**
* Returns msg_ids based on:
* sorting, asc/desc, start_msg, show_per_page
*/
function getMsgIds($sort, $ascdesc, $start_msg, $per_page = 0, $search = 'ALL', &$num_msgs)
{
global $SESSION, $HOSTINFO;
switch ($sort) {
case 'from':
$sort = SORTFROM;
break;
case 'subject':
$sort = SORTSUBJECT;
break;
case 'to':
$sort = SORTTO;
break;
case 'size':
$sort = SORTSIZE;
break;
case 'date':
$sort = SORTDATE;
break;
case 'arrival':
$sort = SORTARRIVAL;
break;
}
if ($sort == SORTARRIVAL AND $search == 'ALL') {
$msg_ids = array();
$total_num_msgs = imap_num_msg($this->conn);
$num_msgs = $total_num_msgs;
if ($total_num_msgs > 0) {
$msg_ids = $ascdesc ? array_reverse(range(1, $total_num_msgs)) : range(1, $total_num_msgs);
$msg_ids = array_slice($msg_ids, $start_msg-1, $per_page == 0 ? count($msg_ids) : $per_page);
if ($GLOBALS['SESSION']['email']['use_uids']) {
for($i=0; $i<count($msg_ids); $i++){
$msg_ids[$i] = @imap_uid($this->conn, $msg_ids[$i]);
}
}
}
} else {
$options = $GLOBALS['SESSION']['email']['use_uids'] ? SE_UID|SE_NOPREFETCH : SE_NOPREFETCH;
$msg_ids = imap_sort($this->conn, $sort, $ascdesc, $options, $search);
$num_msgs = count($msg_ids);
$msg_ids = array_slice($msg_ids, $start_msg-1, $per_page == 0 ? count($msg_ids) : $per_page);
}
// Clear cache if number of messages has changed?
if (@$HOSTINFO['refresh_email_list'] AND @$SESSION['email']['num_msgs'] != $num_msgs) {
$this->cacheClear();
}
return $msg_ids;
}
/**
* Returns a summary of the message for
* the listing page.
*/
function getMsgSummary($msg_id)
{
global $LANG, $SESSION, $HOSTINFO;
/**
* Get from cache if it's there
*/
if ($data = $this->cacheGet($msg_id . ':' . $this->mbox)) {
return $data;
}
/**
* Not in the cache :(
*/
$headers = $this->headerinfo($msg_id);
/**
* With extended_listing turned Off, this code
* does not run as it requires the whole message
* header
*/
if ($GLOBALS['SESSION']['email']['extended_listing']) {
// Any attachments?
$options = $GLOBALS['SESSION']['email']['use_uids'] ? FT_UID : 0;
$raw_header = imap_fetchheader($this->conn, $msg_id, $options);
$attachments = preg_match('/Content-Type: multipart\/mixed/im', $raw_header);
// Importance header?
$importance = preg_match('/^Importance: High/im', $raw_header);
} else {
$attachments = false;
$importance = false;
}
// Determine Size
if (isset($headers->Size)) {
if ($headers->Size > 1048576) {
$size = round($headers->Size/1048576, 1).' Mb';
} elseif ($headers->Size > 1024) {
$size = round($headers->Size/1024 , 1).' Kb';
} else {
$size = $headers->Size.' b';
}
} else {
$size = ' ';
}
// From address
if (isset($headers->from)) {
$email = $headers->from[0]->mailbox.'@'.$headers->from[0]->host;
$name = isset($headers->from[0]->personal) ? decode_header($headers->from[0]->personal) : $email;
if (strlen($name) > 30) {
$name = substr($name, 0, 30) . '...';
}
} else {
$email = '';
$name = 'Unknown';
}
// To address
if (isset($headers->to)) {
$to_email = $headers->to[0]->mailbox.'@'.@$headers->to[0]->host;
$to_name = isset($headers->to[0]->personal) ? decode_header($headers->to[0]->personal) : $to_email;
if (strlen($to_name) > 30) {
$to_name = substr($to_name, 0, 30) . '...';
}
} else {
$to_email = '';
$to_name = 'Unknown';
}
// Subject
$subject = (isset($headers->subject) AND trim($headers->subject) != '') ? decode_header($headers->subject) : lang('[no subject]');
// FIXME Make this max subject length configurable
if (strlen($subject) > 80) {
$subject = substr($subject, 0, 80).'...';
}
/**
* Why is this here?
* So the lang() parser picks them up. Used
* for the date() operations below
*
* lang('Jan')
* lang('Feb')
* lang('Mar')
* lang('Apr')
* lang('May')
* lang('Jun')
* lang('Jul')
* lang('Aug')
* lang('Sep')
* lang('Oct')
* lang('Nov')
* lang('Dec')
*/
// Return as array of properties
$ret = array(
'msg_id' => $msg_id,
'date' => sprintf('%s %s %s', date('H:i jS', $headers->udate),
lang(date('M', $headers->udate)),
date('Y', $headers->udate)),
'email' => htmlspecialchars($email),
'name' => htmlspecialchars($name),
'email_urlsafe' => urlencode(sprintf('"%s" <%s>', $name, $email)), // Err on caution as imap functions don't always return quoted strings
'to_email' => htmlspecialchars($to_email),
'to_name' => htmlspecialchars($to_name),
'to_urlsafe' => urlencode(sprintf('"%s" <%s>', $to_name, $to_email)), // Err on caution as imap functions don't always return quoted strings
'subject' => htmlspecialchars($subject),
'subject_raw' => $subject,
'size' => $size,
'attachments' => $attachments,
'importance' => $importance,
'deleted' => ($headers->Deleted == 'D'),
'flagged' => ($headers->Flagged == 'F'),
'draft' => ($headers->Draft == 'X'),
'answered' => ($headers->Answered == 'A'),
'unread' => (preg_match('/^imap/i', $this->type) AND ($headers->Unseen == 'U' OR $headers->Recent == 'N'))
);
// Cache the summary information
if (@$HOSTINFO['cache_email_list']) {
$this->cacheAdd($msg_id . ':' . $this->mbox, $ret);
}
return $ret;
}
//////////////////////////////////////////////////// Folder functions ////////////////////////////////////////////////////
/**
* Creates a folder string in the format:
* {host:port/type}folder
*/
function createFolderString($folder = '', $mailbox_only = false)
{
global $SESSION;
if ($mailbox_only == true) {
$return = (strcasecmp($folder, 'INBOX') == 0 ? 'INBOX' : imap_utf7_encode($folder));
} else {
if ($folder == '') {
$return = sprintf('{%s:%s/%s}', $this->host, $this->port, $this->type);
} elseif (strcasecmp($folder, 'INBOX') == 0) {
$return = sprintf('{%s:%s/%s}INBOX', $this->host, $this->port, $this->type);
} else {
$return = sprintf('{%s:%s/%s}%s', $this->host, $this->port, $this->type, imap_utf7_encode($folder));
}
}
return $return;
}
/**
* Determines if the mailbox is valid or not
*/
function isValidMailbox($mbox)
{
if (strcasecmp($mbox, 'INBOX') == 0) {
return true;
}
if (preg_match('/^pop3/', $this->type)) {
return false;
}
$prefix = preg_quote(@$GLOBALS['SESSION']['email']['fold'], '/');
$result = (preg_match(sprintf('/^%s/', $prefix), $mbox) AND (strpos($mbox, '../') === false));
if (!$result) {
$GLOBALS['logger']->log(sprintf('Invalid mailbox specified: %s by user: %s from IP: %s', $mbox, $this->user, common::getClientIP()), LOG_WARNING);
}
return $result;
}
/**
* Gets folders, either subscribed or all.
*/
function getFolders($func_type, $get_sizes = null, $get_unseen = null)
{
global $SESSION, $CONFIG;
$treeMenu = new HTML_TreeMenu();
// Init the "Tree tracking" arrays
$tree['branches'] = array(); // Numeric
$tree['root_nodes'] = array(); // Numeric
$tree['relations'] = array(); // Associative/Numeric
$tree['attributes'] = array(); // Associative
$tree['textnames'] = array(); // Associative
$tree['statistics'] = array(); // Associative/Associative
$flat_list = array(); // Numeric/Associative
$folder_prefix = $SESSION['email']['fold'];
// Get the folders
$function = ($func_type == 'subscribed' ? 'imap_getsubscribed' : 'imap_getmailboxes');
$folders = $function($this->conn, $this->createFolderString(), $folder_prefix . '*');
// Get rid of any prefix only folders and add the
// attributes to the attributes array. Also get
// rid of the identifying string {localhost/imap} etc
$folders = array_merge(imap_getmailboxes($this->conn, $this->createFolderString(), 'INBOX'), $folders);
for ($i=0; $i<count($folders); $i++) {
// Delete empty entries
if (!isset($folders[$i]->name) OR !isset($folders[$i]->attributes) OR !isset($folders[$i]->delimiter)) {
unset($folders[$i]);
continue;
}
// Delete prefix only folders
if ($folders[$i]->name == $this->createFolderString() . $folder_prefix) {
unset($folders[$i]);
continue;
}
$folders[$i]->name = preg_replace('/^' . preg_quote($this->createFolderString(), '/') . '/', '', $folders[$i]->name);
$folders[$i]->name = imap_utf7_decode($folders[$i]->name);
}
$folders = array_values($folders);
foreach ($folders as $folder) {
// See above for test code
$folder->name = preg_replace('/^' . preg_quote($this->createFolderString(), '/') . '/', '', $folder->name);
// Save the attributes
$tree['attributes'][$folder->name] = $folder->attributes;
// Get folderstats
$folderinfo = $this->getFolderInfo($folder->name, $get_sizes, $get_unseen);
if (is_array(@$folderinfo[$folder->name])) {
$tree['statistics'][$folder->name] = $folderinfo[$folder->name];
$tree['statistics'][$folder->name]['delimiter'] = $folder->delimiter;
} else {
$tree['statistics'][$folder->name] = array('noinferiors' => false, 'noselect' => true, 'messages' => '-', 'unseen' => '-', 'size' => '-');
}
// Seperate into path parts
if (!empty($folder->delimiter)) {
$folder_parts = explode($folder->delimiter, $folder->name);
} else {
$folder_parts = array($folder->name);
}
// Save the name
$tree['textnames'][$folder->name] = end($folder_parts);
for ($i=0; $i<count($folder_parts); $i++) {
// Make this folders' id
$folder_id = implode($folder->delimiter, array_slice($folder_parts, 0, $i+1));
// Is the root node for this folder in the root_nodes array?
if ($i == 0 AND !in_array($folder_id, $tree['root_nodes'])) {
$tree['root_nodes'][] = $folder_id;
}
// Need adding to branches array?
if ($i < (count($folder_parts) - 1) AND !in_array($folder_id, $tree['branches'])) {
$tree['branches'][] = $folder_id;
}
// Need to add to relations array?
if ($i != 0) {
$parent_id = implode($folder->delimiter, array_slice($folder_parts, 0, $i));
if (!isset($tree['relations'][$parent_id])) {
$tree['relations'][$parent_id] = array($folder_id);
} elseif (!in_array($folder_id, $tree['relations'][$parent_id])) {
$tree['relations'][$parent_id][] = $folder_id;
}
}
}
}
foreach ($tree['root_nodes'] as $root_node) {
// Handle top level branches
$noselect = isset($tree['statistics'][$root_node]['noselect']) ? $tree['statistics'][$root_node]['noselect'] : true;
$stats_messages = isset($tree['statistics'][$root_node]['messages']) ? $tree['statistics'][$root_node]['messages'] : '-';
$stats_unseen = isset($tree['statistics'][$root_node]['unseen']) ? $tree['statistics'][$root_node]['unseen'] : '-';
$stats_size = isset($tree['statistics'][$root_node]['size']) ? $tree['statistics'][$root_node]['size'] : '-';
$stats_bytes = isset($tree['statistics'][$root_node]['bytes']) ? $tree['statistics'][$root_node]['bytes'] : '-';
$delimiter = isset($tree['statistics'][$root_node]['delimiter']) ? $tree['statistics'][$root_node]['delimiter'] : '';
$rename_prompt = htmlspecialchars(str_replace($GLOBALS['SESSION']['email']['fold'], '', $root_node));
$tree['nodes'][$root_node] = &$treeMenu->addItem(new HTML_TreeNode(array('text' => javascript::escapeString($root_node) . ($stats_unseen != '-' ? ' (' . $stats_unseen . ')': ''), 'link' => $noselect ? null : 'email.list.php?' . VWEBMAILSESSION . '&mbox=' . $root_node, 'icon' => 'folder.gif', 'expandedIcon' => 'folder-expanded.gif'), array('oncontextmenu' => 'return showFolderContext(event, \'' . $root_node . '\', \'' . $rename_prompt . '\')', 'ondragenter' => 'enterDrag()', 'ondrop' => 'drop(\'' . $root_node . '\')', 'ondragover' => 'overDrag()')));
$flat_list[] = array('name' => $root_node,
'name_urlsafe' => $noselect ? '' : urlencode($root_node),
'name_htmlsafe' => htmlspecialchars($root_node),
'text' => htmlspecialchars($root_node),
'depth' => 0,
'space_prefix' => '',
'image_prefix' => sprintf('<img src="%s/%s" align="top">', 'images/TreeMenu', 'folder.gif'),
'noselect' => $noselect,
'messages' => $stats_messages,
'unseen' => $stats_unseen,
'size' => $stats_size,
'bytes' => $stats_bytes,
'delimiter' => $delimiter);
if (in_array($root_node, $tree['branches'])) {
$this->traverse_tree($root_node, 1, $flat_list, $tree, $tree['nodes'][$root_node]);
}
}
return array($flat_list, $treeMenu);
}
/**
* Traverses the tree built in the above function and does various
* things with the data.
*/
function traverse_tree($branch, $depth, &$flat_list, &$tree, &$treeMenu, $prepend = '')
{
if (!empty($tree['relations'][$branch])) {
for ($i=0; $i<count($tree['relations'][$branch]); $i++) {
// Gif modifier
if ($i == 0 AND count($tree['relations'][$branch]) > 1) {
$gif_modifier = '';
} elseif ($i == (count($tree['relations'][$branch]) - 1)) {
$gif_modifier = 'bottom';
} else {
$gif_modifier = '';
}
$folder_id = $tree['relations'][$branch][$i];
/**
* If this folder is not subscribed, it will not have been given in the list.
* This is not a problem unless it's a parent of a folder that *is* subscribed.
* Therefore, if the folder isn't in the textnames array, we need to find its
* details.
*/
if (!isset($tree['textnames'][$folder_id])) {
$folder_info = imap_getmailboxes($this->conn, $this->createFolderString(), $folder_id);
$folder_info[0]->name = preg_replace('/^' . preg_quote($this->createFolderString(), '/') . '/', '', $folder_info[0]->name);
$folder_info[0]->name = imap_utf7_decode($folder_info[0]->name);
$folder_parts = !empty($folder_info[0]->delimiter) ? explode($folder_info[0]->delimiter, $folder_info[0]->name) : array($folder_info[0]->name);
$tree['textnames'][$folder_id] = end($folder_parts);
$tree['attributes'] = $folder_info[0]->attributes;
unset($folder_info);
unset($folder_parts);
}
$noselect = isset($tree['statistics'][$folder_id]['noselect']) ? $tree['statistics'][$folder_id]['noselect'] : true;
$stats_messages = isset($tree['statistics'][$folder_id]['messages']) ? $tree['statistics'][$folder_id]['messages'] : '-';
$stats_unseen = isset($tree['statistics'][$folder_id]['unseen']) ? $tree['statistics'][$folder_id]['unseen'] : '-';
$stats_size = isset($tree['statistics'][$folder_id]['size']) ? $tree['statistics'][$folder_id]['size'] : '-';
$stats_bytes = isset($tree['statistics'][$folder_id]['bytes']) ? $tree['statistics'][$folder_id]['bytes'] : '-';
$delimiter = isset($tree['statistics'][$folder_id]['delimiter']) ? $tree['statistics'][$folder_id]['delimiter'] : '';
$rename_prompt = htmlspecialchars(str_replace($GLOBALS['SESSION']['email']['fold'], '', $folder_id));
$tree['nodes'][$folder_id] = &$treeMenu->addItem(new HTML_TreeNode(array('text' => javascript::escapeString(htmlspecialchars($tree['textnames'][$folder_id])) . ($stats_unseen != '-' ? ' (' . $stats_unseen . ')' : ''), 'link' => $noselect ? null : 'email.list.php?' . VWEBMAILSESSION . '&mbox=' . urlencode($folder_id), 'icon' => 'folder.gif', 'expandedIcon' => 'folder-expanded.gif'), array('oncontextmenu' => 'return showFolderContext(event, \'' . urlencode($folder_id) . '\', \'' . $rename_prompt . '\')', 'ondragenter' => 'enterDrag()', 'ondrop' => 'drop(\'' . $folder_id . '\')', 'ondragover' => 'overDrag()')));
// Add to flat list
$flat_list[] = array('name' => $folder_id,
'name_urlsafe' => $noselect ? '' : urlencode($folder_id),
'name_htmlsafe' => htmlspecialchars($folder_id),
'text' => htmlspecialchars($tree['textnames'][$folder_id]),
'depth' => $depth,
'space_prefix' => str_repeat(' ', $depth),
'image_prefix' => sprintf('%s<img src="%s/%s%s%s" align="top"><img src="%s/%s" align="top">', $prepend, 'images/TreeMenu', 'branch', $gif_modifier, '.gif', 'images/TreeMenu', 'folder.gif'),
'noselect' => $noselect,
'messages' => $stats_messages,
'unseen' => $stats_unseen,
'size' => $stats_size,
'bytes' => $stats_bytes,
'delimiter' => $delimiter);
// Traverse sub branches
if (in_array($folder_id, $tree['branches'])) {
if ($i < (count($tree['relations'][$branch]) - 1)) {
$new_prepend = sprintf('%s<img src="%s/line.gif" align="top">', $prepend, 'images/TreeMenu/');
} else {
$new_prepend = sprintf('%s<img src="%s/linebottom.gif" align="top">', $prepend, 'images/TreeMenu/');
}
$this->traverse_tree($folder_id, $depth + 1, $flat_list, $tree, $tree['nodes'][$folder_id], $new_prepend);
}
}
}
}
/**
* Returns list of subscribed folders
*/
function getSubscribedFolders($get_sizes = null, $nocache = false, $get_unseen = null)
{
global $HOSTINFO;
if (@$HOSTINFO['cache_email_folders'] AND !$nocache) {
if ($folders = $this->cacheGet('subscribed_folders')) {
return $folders;
} else {
$folders = $this->getFolders('subscribed');
$this->cacheAdd('subscribed_folders', $folders);
return $folders;
}
} else {
return $this->getFolders('subscribed', $get_sizes, $get_unseen);
}
}
/**
* Returns a list of all folders
*/
function getAllFolders($get_sizes = null, $get_unseen = null)
{
return $this->getFolders('all', $get_sizes, $get_unseen);
}
/**
* Returns some information about a folder.
*/
function getFolderInfo($folder_name, $get_sizes = null, $get_unseen = null)
{
global $CONFIG;
$mbox = $this->mbox;
$folders = imap_getmailboxes($this->conn, $this->createFolderString(), imap_utf7_encode($folder_name));
if ($folders) {
foreach ($folders as $folder) {
$name = substr(imap_utf7_decode($folder->name), strpos(imap_utf7_decode($folder->name), '}') + 1);
$return[$name]['noinferiors'] = ($folder->attributes & LATT_NOINFERIORS) == LATT_NOINFERIORS;
$return[$name]['noselect'] = ($folder->attributes & LATT_NOSELECT) == LATT_NOSELECT;
$return[$name]['messages'] = 0;
$return[$name]['unseen'] = 0;
$return[$name]['size'] = '';
if ($get_sizes === true) {
$this->reconnect($folder_name);
$mailboxmsginfo = imap_mailboxmsginfo($this->conn);
$this->reconnect($mbox); // Previous mailbox
$return[$name]['messages'] = $mailboxmsginfo->Nmsgs;
$return[$name]['unseen'] = $mailboxmsginfo->Unread;
$return[$name]['size'] = '';
// Format the size
if ($mailboxmsginfo->Size == 0) {
$size = '0';
} elseif($mailboxmsginfo->Size >= 1048576) {
$size = number_format($mailboxmsginfo->Size / 1048576, 1) . 'Mb';
} elseif($mailboxmsginfo->Size >= 1024) {
$size = number_format($mailboxmsginfo->Size / 1024) . 'Kb';
} else {
$size = $mailboxmsginfo->Size . 'b';
}
$return[$name]['size'] = $size;
$return[$name]['bytes'] = $mailboxmsginfo->Size;
} elseif ($get_unseen === true AND $CONFIG['c-client'] >= 2001) {
$status = imap_status($this->conn, $this->createFolderString($name), SA_ALL);
$return[$name]['messages'] = $status->messages;
$return[$name]['unseen'] = $status->unseen;
$return[$name]['size'] = '-';
// No stats
} elseif ($get_sizes === false) {
$return[$name]['messages'] = '-';
$return[$name]['unseen'] = '-';
$return[$name]['size'] = '-';
}
} // End foreach
}
imap_errors();
return @$return;
}
/**
* Moves message from folder to folder
*/
function moveMsg($msg_ids, $to)
{
$d_msg_ids = implode(',', $this->getMsgIds(1,1,1,0, 'DELETED', $num_msgs));
$this->setflag($d_msg_ids, 'undeleted');
$options = $GLOBALS['SESSION']['email']['use_uids'] ? CP_UID : 0;
$result = imap_mail_move($this->conn, $msg_ids, $this->createFolderString($to, true), $options);
$this->expunge();
$this->setflag($d_msg_ids, 'deleted');
return $result;
}
/**
* Copies message from folder to folder
*/
function copyMsg($msg_ids, $to)
{
$options = $GLOBALS['SESSION']['email']['use_uids'] ? CP_UID : 0;
return imap_mail_copy($this->conn, $msg_ids, $this->createFolderString($to, true), $options);
}
/**
* Checks whether a folder exists or not
*/
function folderExists($folder)
{
$folders = imap_listmailbox($this->conn, $this->createFolderString(), $this->createFolderString($folder, true));
return (!empty($folders));
}
/**
* Create a folder
*/
function createFolder($folder)
{
$this->cacheRemove('subscribed_folders');
return imap_createmailbox($this->conn, $this->createFolderString($folder));
}
/**
* Create a subfolder
*/
function createSubFolder($parentFolder, $childFolder)
{
$this->cacheRemove('subscribed_folders');
$parentFolderInfo = imap_getmailboxes($this->conn, $this->createFolderString(), $this->createFolderString($parentFolder, true));
$newFolder = $parentFolder . $parentFolderInfo[0]->delimiter . $childFolder;
$result = $this->createFolder($newFolder);
return $result ? $newFolder : false;
}
/**
* Delete a folder
*/
function deleteFolder($folder)
{
$this->cacheRemove('subscribed_folders');
return imap_deletemailbox($this->conn, $this->createFolderString($folder));
}
/**
* Empties a folders contents
*/
function emptyFolder($folder)
{
if ($this->reconnect($folder)) {
$msg_ids = $this->getMsgIds(1, 1, 1, 0, 'ALL', $num_msgs);
$options = $GLOBALS['SESSION']['email']['use_uids'] ? FT_UID : 0;
if (imap_delete($this->conn, implode(',', $msg_ids), $options) AND imap_expunge($this->conn)) {
return count($msg_ids);
}
}
return false;
}
/**
* Rename a folder
*/
function renameFolder($from, $to)
{
$this->cacheRemove('subscribed_folders');
if ($this->isSubscribed($from)) {
$this->unsubscribeFolder($from);
$result = imap_renamemailbox($this->conn, $this->createFolderString($from), $this->createFolderString($to));
$this->subscribeFolder($result ? $to : $from); // Either subscribe to new, or resubscribe to old.
} else {
$result = imap_renamemailbox($this->conn, $this->createFolderString($from), $this->createFolderString($to));
}
return $result;
}
/**
* Tells whether a folder is subscribed or not
*/
function isSubscribed($folder)
{
global $SESSION;
static $folders;
if (!isset($folders)) {
$folders = imap_getsubscribed($this->conn, $this->createFolderString(), $SESSION['email']['fold'] . '*');
foreach ($folders as $value) {
$tmp[] = substr($value->name, strpos($value->name, '}') + 1);
}
$folders = $tmp;
}
return in_array($folder, $folders);
}
/**
* Function to subscribe to a folder
*/
function subscribeFolder($folder)
{
return imap_subscribe($this->conn, $this->createFolderString($folder));
}
/**
* Function to unsubscribe from a folder
*/
function unsubscribeFolder($folder)
{
$this->cacheRemove('subscribed_folders');
return imap_unsubscribe($this->conn, $this->createFolderString($folder));
}
/**
* Sets flags on messages
*/
function setflag($msg_ids, $flag)
{
switch($flag){
case 'answered':
$function = 'imap_setflag_full';
$flag = '\\Answered';
break;
case 'unanswered':
$function = 'imap_clearflag_full';
$flag = '\\Answered';
break;
case 'deleted':
$function = 'imap_setflag_full';
$flag = '\\Deleted';
break;
case 'undeleted':
$function = 'imap_clearflag_full';
$flag = '\\Deleted';
break;
case 'draft':
$function = 'imap_setflag_full';
$flag = '\\Draft';
break;
case 'undraft': // :-/
$function = 'imap_clearflag_full';
$flag = '\\Draft';
break;
case 'flagged':
$function = 'imap_setflag_full';
$flag = '\\Flagged';
break;
case 'unflagged':
$function = 'imap_clearflag_full';
$flag = '\\Flagged';
break;
case 'read':
$function = 'imap_setflag_full';
$flag = '\\Seen';
break;
case 'unread':
$function = 'imap_clearflag_full';
$flag = '\\Seen';
break;
default:
return false;
}
$options = $GLOBALS['SESSION']['email']['use_uids'] ? ST_UID : 0;
$function($this->conn, $msg_ids, $flag, $options);
return true;
}
/**
* Appends a message to a folder
*/
function append($message, $folder = null, $flags = null)
{
global $SESSION, $USERPREFS;
if (!isset($folder)) {
$folder = !empty($USERPREFS['settings']['sent_items_folder']) ? $USERPREFS['settings']['sent_items_folder'] : $SESSION['email']['fold'] . 'sent-mail';
}
if (!isset($flags) OR !in_array('\\Seen', $flags)) {
$flags[] = '\\Seen';
}
return imap_append($this->conn, $this->createFolderString($folder), $message, implode(' ', $flags));
}
/**
* Function checks for key folders,
* ie Deleted|Sent Items
*/
function checkKeyFolders()
{
global $SESSION, $USERPREFS;
$draft_folder = !empty($USERPREFS['settings']['draft_items_folder']) ? $USERPREFS['settings']['draft_items_folder'] : $SESSION['email']['fold'] . 'drafts';
$deleted_folder = !empty($USERPREFS['settings']['deleted_items_folder']) ? $USERPREFS['settings']['deleted_items_folder'] : $SESSION['email']['fold'] . 'deleted-mail';
$sent_folder = !empty($USERPREFS['settings']['sent_items_folder']) ? $USERPREFS['settings']['sent_items_folder'] : $SESSION['email']['fold'] . 'sent-mail';
foreach (array('INBOX', $draft_folder, $deleted_folder, $sent_folder) as $folder) {
if (!$this->folderExists($folder)) {
$this->createFolder($folder);
}
$this->subscribeFolder($folder);
}
imap_errors();
}
} // End of mailaccess_imapext
?>