Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F224234
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
153 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index 922b15ec1..0323c530b 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -1,2930 +1,2896 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/include/rcube_imap.php |
| |
| This file is part of the RoundCube Webmail client |
| Copyright (C) 2005-2008, RoundCube Dev. - Switzerland |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| IMAP wrapper that implements the Iloha IMAP Library (IIL) |
| See http://ilohamail.org/ for details |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
$Id$
*/
/*
* Obtain classes from the Iloha IMAP library
*/
require_once('lib/imap.inc');
require_once('lib/mime.inc');
/**
* Interface class for accessing an IMAP server
*
* This is a wrapper that implements the Iloha IMAP Library (IIL)
*
* @package Mail
* @author Thomas Bruederli <roundcube@gmail.com>
* @version 1.40
* @link http://ilohamail.org
*/
class rcube_imap
{
var $db;
var $conn;
var $root_ns = '';
var $root_dir = '';
var $mailbox = 'INBOX';
var $list_page = 1;
var $page_size = 10;
var $sort_field = 'date';
var $sort_order = 'DESC';
var $delimiter = NULL;
var $caching_enabled = FALSE;
var $default_charset = 'ISO-8859-1';
var $default_folders = array('INBOX');
var $default_folders_lc = array('inbox');
var $cache = array();
var $cache_keys = array();
var $cache_changes = array();
var $uid_id_map = array();
var $msg_headers = array();
- var $capabilities = array();
var $skip_deleted = FALSE;
var $search_set = NULL;
var $search_subject = '';
var $search_string = '';
var $search_charset = '';
var $debug_level = 1;
var $error_code = 0;
/**
* Object constructor
*
* @param object DB Database connection
*/
function __construct($db_conn)
{
$this->db = $db_conn;
}
/**
* Connect to an IMAP server
*
* @param string Host to connect
* @param string Username for IMAP account
* @param string Password for IMAP account
* @param number Port to connect to
* @param string SSL schema (either ssl or tls) or null if plain connection
* @return boolean TRUE on success, FALSE on failure
* @access public
*/
function connect($host, $user, $pass, $port=143, $use_ssl=null, $auth_type=null)
{
global $ICL_SSL, $ICL_PORT, $IMAP_USE_INTERNAL_DATE;
// check for Open-SSL support in PHP build
if ($use_ssl && in_array('openssl', get_loaded_extensions()))
$ICL_SSL = $use_ssl == 'imaps' ? 'ssl' : $use_ssl;
else if ($use_ssl)
{
raise_error(array('code' => 403, 'type' => 'imap', 'file' => __FILE__,
'message' => 'Open SSL not available;'), TRUE, FALSE);
$port = 143;
}
$ICL_PORT = $port;
$IMAP_USE_INTERNAL_DATE = false;
$this->conn = iil_Connect($host, $user, $pass, array('imap' => $auth_type ? $auth_type : 'check'));
$this->host = $host;
$this->user = $user;
$this->pass = $pass;
$this->port = $port;
$this->ssl = $use_ssl;
// print trace mesages
if ($this->conn && ($this->debug_level & 8))
console($this->conn->message);
// write error log
else if (!$this->conn && $GLOBALS['iil_error'])
{
$this->error_code = $GLOBALS['iil_errornum'];
raise_error(array('code' => 403,
'type' => 'imap',
'message' => $GLOBALS['iil_error']), TRUE, FALSE);
}
// get server properties
if ($this->conn)
{
- $this->_parse_capability($this->conn->capability);
-
if (!empty($this->conn->delimiter))
$this->delimiter = $this->conn->delimiter;
if (!empty($this->conn->rootdir))
{
$this->set_rootdir($this->conn->rootdir);
$this->root_ns = ereg_replace('[\.\/]$', '', $this->conn->rootdir);
}
}
return $this->conn ? TRUE : FALSE;
}
/**
* Close IMAP connection
* Usually done on script shutdown
*
* @access public
*/
function close()
{
if ($this->conn)
iil_Close($this->conn);
}
/**
* Close IMAP connection and re-connect
* This is used to avoid some strange socket errors when talking to Courier IMAP
*
* @access public
*/
function reconnect()
{
$this->close();
$this->connect($this->host, $this->user, $this->pass, $this->port, $this->ssl);
}
/**
* Set a root folder for the IMAP connection.
*
* Only folders within this root folder will be displayed
* and all folder paths will be translated using this folder name
*
* @param string Root folder
* @access public
*/
function set_rootdir($root)
{
if (ereg('[\.\/]$', $root)) //(substr($root, -1, 1)==='/')
$root = substr($root, 0, -1);
$this->root_dir = $root;
if (empty($this->delimiter))
$this->get_hierarchy_delimiter();
}
/**
* Set default message charset
*
* This will be used for message decoding if a charset specification is not available
*
* @param string Charset string
* @access public
*/
function set_charset($cs)
{
$this->default_charset = $cs;
}
/**
* This list of folders will be listed above all other folders
*
* @param array Indexed list of folder names
* @access public
*/
function set_default_mailboxes($arr)
{
if (is_array($arr))
{
$this->default_folders = $arr;
$this->default_folders_lc = array();
// add inbox if not included
if (!in_array_nocase('INBOX', $this->default_folders))
array_unshift($this->default_folders, 'INBOX');
// create a second list with lower cased names
foreach ($this->default_folders as $mbox)
$this->default_folders_lc[] = strtolower($mbox);
}
}
/**
* Set internal mailbox reference.
*
* All operations will be perfomed on this mailbox/folder
*
* @param string Mailbox/Folder name
* @access public
*/
function set_mailbox($new_mbox)
{
$mailbox = $this->_mod_mailbox($new_mbox);
if ($this->mailbox == $mailbox)
return;
$this->mailbox = $mailbox;
// clear messagecount cache for this mailbox
$this->_clear_messagecount($mailbox);
}
/**
* Set internal list page
*
* @param number Page number to list
* @access public
*/
function set_page($page)
{
$this->list_page = (int)$page;
}
/**
* Set internal page size
*
* @param number Number of messages to display on one page
* @access public
*/
function set_pagesize($size)
{
$this->page_size = (int)$size;
}
/**
* Save a set of message ids for future message listing methods
*
* @param array List of IMAP fields to search in
* @param string Search string
* @param array List of message ids or NULL if empty
*/
function set_search_set($subject, $str=null, $msgs=null, $charset=null)
{
if (is_array($subject) && $str == null && $msgs == null)
list($subject, $str, $msgs, $charset) = $subject;
if ($msgs != null && !is_array($msgs))
$msgs = split(',', $msgs);
$this->search_subject = $subject;
$this->search_string = $str;
$this->search_set = (array)$msgs;
$this->search_charset = $charset;
}
/**
* Return the saved search set as hash array
* @return array Search set
*/
function get_search_set()
{
return array($this->search_subject, $this->search_string, $this->search_set, $this->search_charset);
}
/**
* Returns the currently used mailbox name
*
* @return string Name of the mailbox/folder
* @access public
*/
function get_mailbox_name()
{
return $this->conn ? $this->_mod_mailbox($this->mailbox, 'out') : '';
}
/**
* Returns the IMAP server's capability
*
* @param string Capability name
* @return mixed Capability value or TRUE if supported, FALSE if not
* @access public
*/
function get_capability($cap)
{
- $cap = strtoupper($cap);
- return $this->capabilities[$cap];
+ return iil_C_GetCapability($this->conn, strtoupper($cap));
}
/**
* Checks the PERMANENTFLAGS capability of the current mailbox
* and returns true if the given flag is supported by the IMAP server
*
* @param string Permanentflag name
* @return mixed True if this flag is supported
* @access public
*/
function check_permflag($flag)
{
$flagsmap = $GLOBALS['IMAP_FLAGS'];
return (($imap_flag = $flagsmap[strtoupper($flag)]) && in_array_nocase($imap_flag, $this->conn->permanentflags));
}
/**
* Returns the delimiter that is used by the IMAP server for folder separation
*
* @return string Delimiter string
* @access public
*/
function get_hierarchy_delimiter()
{
if ($this->conn && empty($this->delimiter))
$this->delimiter = iil_C_GetHierarchyDelimiter($this->conn);
if (empty($this->delimiter))
$this->delimiter = '/';
return $this->delimiter;
}
/**
* Public method for mailbox listing.
*
* Converts mailbox name with root dir first
*
* @param string Optional root folder
* @param string Optional filter for mailbox listing
* @return array List of mailboxes/folders
* @access public
*/
function list_mailboxes($root='', $filter='*')
{
$a_out = array();
$a_mboxes = $this->_list_mailboxes($root, $filter);
foreach ($a_mboxes as $mbox_row)
{
$name = $this->_mod_mailbox($mbox_row, 'out');
if (strlen($name))
$a_out[] = $name;
}
// INBOX should always be available
if (!in_array_nocase('INBOX', $a_out))
array_unshift($a_out, 'INBOX');
// sort mailboxes
$a_out = $this->_sort_mailbox_list($a_out);
return $a_out;
}
/**
* Private method for mailbox listing
*
* @return array List of mailboxes/folders
* @see rcube_imap::list_mailboxes()
* @access private
*/
function _list_mailboxes($root='', $filter='*')
{
$a_defaults = $a_out = array();
// get cached folder list
$a_mboxes = $this->get_cache('mailboxes');
if (is_array($a_mboxes))
return $a_mboxes;
// retrieve list of folders from IMAP server
$a_folders = iil_C_ListSubscribed($this->conn, $this->_mod_mailbox($root), $filter);
if (!is_array($a_folders) || !sizeof($a_folders))
$a_folders = array();
// write mailboxlist to cache
$this->update_cache('mailboxes', $a_folders);
return $a_folders;
}
/**
* Get message count for a specific mailbox
*
* @param string Mailbox/folder name
* @param string Mode for count [ALL|UNSEEN|RECENT]
* @param boolean Force reading from server and update cache
* @return int Number of messages
* @access public
*/
function messagecount($mbox_name='', $mode='ALL', $force=FALSE)
{
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
return $this->_messagecount($mailbox, $mode, $force);
}
/**
* Private method for getting nr of messages
*
* @access private
* @see rcube_imap::messagecount()
*/
function _messagecount($mailbox='', $mode='ALL', $force=FALSE)
{
$a_mailbox_cache = FALSE;
$mode = strtoupper($mode);
if (empty($mailbox))
$mailbox = $this->mailbox;
// count search set
if ($this->search_string && $mailbox == $this->mailbox && $mode == 'ALL' && !$force)
return count((array)$this->search_set);
$a_mailbox_cache = $this->get_cache('messagecount');
// return cached value
if (!$force && is_array($a_mailbox_cache[$mailbox]) && isset($a_mailbox_cache[$mailbox][$mode]))
return $a_mailbox_cache[$mailbox][$mode];
// RECENT count is fetched a bit different
if ($mode == 'RECENT')
$count = iil_C_CheckForRecent($this->conn, $mailbox);
// use SEARCH for message counting
else if ($this->skip_deleted)
{
$search_str = "ALL UNDELETED";
// get message count and store in cache
if ($mode == 'UNSEEN')
$search_str .= " UNSEEN";
// get message count using SEARCH
// not very performant but more precise (using UNDELETED)
$count = 0;
$index = $this->_search_index($mailbox, $search_str);
if (is_array($index))
{
$str = implode(",", $index);
if (!empty($str))
$count = count($index);
}
}
else
{
if ($mode == 'UNSEEN')
$count = iil_C_CountUnseen($this->conn, $mailbox);
else
$count = iil_C_CountMessages($this->conn, $mailbox);
}
if (!is_array($a_mailbox_cache[$mailbox]))
$a_mailbox_cache[$mailbox] = array();
$a_mailbox_cache[$mailbox][$mode] = (int)$count;
// write back to cache
$this->update_cache('messagecount', $a_mailbox_cache);
return (int)$count;
}
/**
* Public method for listing headers
* convert mailbox name with root dir first
*
* @param string Mailbox/folder name
* @param int Current page to list
* @param string Header field to sort by
* @param string Sort order [ASC|DESC]
* @return array Indexed array with message header objects
* @access public
*/
function list_headers($mbox_name='', $page=NULL, $sort_field=NULL, $sort_order=NULL)
{
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
return $this->_list_headers($mailbox, $page, $sort_field, $sort_order);
}
/**
* Private method for listing message headers
*
* @access private
* @see rcube_imap::list_headers
*/
function _list_headers($mailbox='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $recursive=FALSE)
{
if (!strlen($mailbox))
return array();
// use saved message set
if ($this->search_string && $mailbox == $this->mailbox)
return $this->_list_header_set($mailbox, $this->search_set, $page, $sort_field, $sort_order);
$this->_set_sort_order($sort_field, $sort_order);
$max = $this->_messagecount($mailbox);
$start_msg = ($this->list_page-1) * $this->page_size;
list($begin, $end) = $this->_get_message_range($max, $page);
// mailbox is empty
if ($begin >= $end)
return array();
$headers_sorted = FALSE;
$cache_key = $mailbox.'.msg';
$cache_status = $this->check_cache_status($mailbox, $cache_key);
// cache is OK, we can get all messages from local cache
if ($cache_status>0)
{
$a_msg_headers = $this->get_message_cache($cache_key, $start_msg, $start_msg+$this->page_size, $this->sort_field, $this->sort_order);
$headers_sorted = TRUE;
}
// cache is dirty, sync it
else if ($this->caching_enabled && $cache_status==-1 && !$recursive)
{
$this->sync_header_index($mailbox);
return $this->_list_headers($mailbox, $page, $this->sort_field, $this->sort_order, TRUE);
}
else
{
// retrieve headers from IMAP
if ($this->get_capability('sort') && ($msg_index = iil_C_Sort($this->conn, $mailbox, $this->sort_field, $this->skip_deleted ? 'UNDELETED' : '')))
{
$mymsgidx = array_slice ($msg_index, $begin, $end-$begin);
$msgs = join(",", $mymsgidx);
}
else
{
$msgs = sprintf("%d:%d", $begin+1, $end);
$msg_index = range($begin, $end);
}
// fetch reuested headers from server
$a_msg_headers = array();
$deleted_count = $this->_fetch_headers($mailbox, $msgs, $a_msg_headers, $cache_key);
if ($this->sort_order == 'DESC' && $headers_sorted) {
//since the sort order is not used in the iil_c_sort function we have to do it here
$a_msg_headers = array_reverse($a_msg_headers);
}
// delete cached messages with a higher index than $max+1
// Changed $max to $max+1 to fix this bug : #1484295
$this->clear_message_cache($cache_key, $max + 1);
// kick child process to sync cache
// ...
}
// return empty array if no messages found
if (!is_array($a_msg_headers) || empty($a_msg_headers)) {
return array();
}
// if not already sorted
if (!$headers_sorted)
{
// use this class for message sorting
$sorter = new rcube_header_sorter();
$sorter->set_sequence_numbers($msg_index);
$sorter->sort_headers($a_msg_headers);
if ($this->sort_order == 'DESC')
$a_msg_headers = array_reverse($a_msg_headers);
}
return array_values($a_msg_headers);
}
/**
* Public method for listing a specific set of headers
* convert mailbox name with root dir first
*
* @param string Mailbox/folder name
* @param array List of message ids to list
* @param int Current page to list
* @param string Header field to sort by
* @param string Sort order [ASC|DESC]
* @return array Indexed array with message header objects
* @access public
*/
function list_header_set($mbox_name='', $msgs, $page=NULL, $sort_field=NULL, $sort_order=NULL)
{
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
return $this->_list_header_set($mailbox, $msgs, $page, $sort_field, $sort_order);
}
/**
* Private method for listing a set of message headers
*
* @access private
* @see rcube_imap::list_header_set()
*/
function _list_header_set($mailbox, $msgs, $page=NULL, $sort_field=NULL, $sort_order=NULL)
{
if (!strlen($mailbox) || empty($msgs))
return array();
// also accept a comma-separated list of message ids
if (is_array ($msgs)) {
$max = count ($msgs);
$msgs = join (',', $msgs);
} else {
$max = count(split(',', $msgs));
}
$this->_set_sort_order($sort_field, $sort_order);
$start_msg = ($this->list_page-1) * $this->page_size;
// fetch reuested headers from server
$a_msg_headers = array();
$this->_fetch_headers($mailbox, $msgs, $a_msg_headers, NULL);
// return empty array if no messages found
if (!is_array($a_msg_headers) || empty($a_msg_headers))
return array();
// if not already sorted
$a_msg_headers = iil_SortHeaders($a_msg_headers, $this->sort_field, $this->sort_order);
// only return the requested part of the set
return array_slice(array_values($a_msg_headers), $start_msg, min($max-$start_msg, $this->page_size));
}
/**
* Helper function to get first and last index of the requested set
*
* @param int message count
* @param mixed page number to show, or string 'all'
* @return array array with two values: first index, last index
* @access private
*/
function _get_message_range($max, $page)
{
$start_msg = ($this->list_page-1) * $this->page_size;
if ($page=='all')
{
$begin = 0;
$end = $max;
}
else if ($this->sort_order=='DESC')
{
$begin = $max - $this->page_size - $start_msg;
$end = $max - $start_msg;
}
else
{
$begin = $start_msg;
$end = $start_msg + $this->page_size;
}
if ($begin < 0) $begin = 0;
if ($end < 0) $end = $max;
if ($end > $max) $end = $max;
return array($begin, $end);
}
/**
* Fetches message headers
* Used for loop
*
* @param string Mailbox name
* @param string Message index to fetch
* @param array Reference to message headers array
* @param array Array with cache index
* @return int Number of deleted messages
* @access private
*/
function _fetch_headers($mailbox, $msgs, &$a_msg_headers, $cache_key)
{
// cache is incomplete
$cache_index = $this->get_message_cache_index($cache_key);
// fetch reuested headers from server
$a_header_index = iil_C_FetchHeaders($this->conn, $mailbox, $msgs);
$deleted_count = 0;
if (!empty($a_header_index))
{
foreach ($a_header_index as $i => $headers)
{
if ($headers->deleted && $this->skip_deleted)
{
// delete from cache
if ($cache_index[$headers->id] && $cache_index[$headers->id] == $headers->uid)
$this->remove_message_cache($cache_key, $headers->id);
$deleted_count++;
continue;
}
// add message to cache
if ($this->caching_enabled && $cache_index[$headers->id] != $headers->uid)
$this->add_message_cache($cache_key, $headers->id, $headers);
$a_msg_headers[$headers->uid] = $headers;
}
}
return $deleted_count;
}
/**
* Return sorted array of message UIDs
*
* @param string Mailbox to get index from
* @param string Sort column
* @param string Sort order [ASC, DESC]
* @return array Indexed array with message ids
*/
function message_index($mbox_name='', $sort_field=NULL, $sort_order=NULL)
{
$this->_set_sort_order($sort_field, $sort_order);
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
$key = "{$mailbox}:{$this->sort_field}:{$this->sort_order}:{$this->search_string}.msgi";
// we have a saved search result. get index from there
if (!isset($this->cache[$key]) && $this->search_string && $mailbox == $this->mailbox)
{
$this->cache[$key] = $a_msg_headers = array();
$this->_fetch_headers($mailbox, join(',', $this->search_set), $a_msg_headers, NULL);
foreach (iil_SortHeaders($a_msg_headers, $this->sort_field, $this->sort_order) as $i => $msg)
$this->cache[$key][] = $msg->uid;
}
// have stored it in RAM
if (isset($this->cache[$key]))
return $this->cache[$key];
// check local cache
$cache_key = $mailbox.'.msg';
$cache_status = $this->check_cache_status($mailbox, $cache_key);
// cache is OK
if ($cache_status>0)
{
$a_index = $this->get_message_cache_index($cache_key, TRUE, $this->sort_field, $this->sort_order);
return array_values($a_index);
}
// fetch complete message index
$msg_count = $this->_messagecount($mailbox);
if ($this->get_capability('sort') && ($a_index = iil_C_Sort($this->conn, $mailbox, $this->sort_field, '', TRUE)))
{
if ($this->sort_order == 'DESC')
$a_index = array_reverse($a_index);
$this->cache[$key] = $a_index;
}
else
{
$a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, "1:$msg_count", $this->sort_field);
$a_uids = iil_C_FetchUIDs($this->conn, $mailbox);
if ($this->sort_order=="ASC")
asort($a_index);
else if ($this->sort_order=="DESC")
arsort($a_index);
$i = 0;
$this->cache[$key] = array();
foreach ($a_index as $index => $value)
$this->cache[$key][$i++] = $a_uids[$index];
}
return $this->cache[$key];
}
/**
* @access private
*/
function sync_header_index($mailbox)
{
$cache_key = $mailbox.'.msg';
$cache_index = $this->get_message_cache_index($cache_key);
$msg_count = $this->_messagecount($mailbox);
// fetch complete message index
$a_message_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, "1:$msg_count", 'UID');
foreach ($a_message_index as $id => $uid)
{
// message in cache at correct position
if ($cache_index[$id] == $uid)
{
unset($cache_index[$id]);
continue;
}
// message in cache but in wrong position
if (in_array((string)$uid, $cache_index, TRUE))
{
unset($cache_index[$id]);
}
// other message at this position
if (isset($cache_index[$id]))
{
$this->remove_message_cache($cache_key, $id);
unset($cache_index[$id]);
}
// fetch complete headers and add to cache
$headers = iil_C_FetchHeader($this->conn, $mailbox, $id);
$this->add_message_cache($cache_key, $headers->id, $headers);
}
// those ids that are still in cache_index have been deleted
if (!empty($cache_index))
{
foreach ($cache_index as $id => $uid)
$this->remove_message_cache($cache_key, $id);
}
}
/**
* Invoke search request to IMAP server
*
* @param string mailbox name to search in
* @param string search criteria (ALL, TO, FROM, SUBJECT, etc)
* @param string search string
* @return array search results as list of message ids
* @access public
*/
function search($mbox_name='', $criteria='ALL', $str=NULL, $charset=NULL)
{
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
// have an array of criterias => execute multiple searches
if (is_array($criteria) && $str)
{
$results = array();
foreach ($criteria as $crit)
if ($search_result = $this->search($mbox_name, $crit, $str, $charset))
$results = array_merge($results, $search_result);
$results = array_unique($results);
$this->set_search_set($criteria, $str, $results, $charset);
return $results;
}
else if ($str && $criteria)
{
$search = (!empty($charset) ? "CHARSET $charset " : '') . sprintf("%s {%d}\r\n%s", $criteria, strlen($str), $str);
$results = $this->_search_index($mailbox, $search);
// try search with ISO charset (should be supported by server)
if (empty($results) && !empty($charset) && $charset!='ISO-8859-1')
$results = $this->search($mbox_name, $criteria, rcube_charset_convert($str, $charset, 'ISO-8859-1'), 'ISO-8859-1');
$this->set_search_set($criteria, $str, $results, $charset);
return $results;
}
else
return $this->_search_index($mailbox, $criteria);
}
/**
* Private search method
*
* @return array search results as list of message ids
* @access private
* @see rcube_imap::search()
*/
function _search_index($mailbox, $criteria='ALL')
{
$a_messages = iil_C_Search($this->conn, $mailbox, $criteria);
// clean message list (there might be some empty entries)
if (is_array($a_messages))
{
foreach ($a_messages as $i => $val)
if (empty($val))
unset($a_messages[$i]);
}
return $a_messages;
}
/**
* Refresh saved search set
*
* @return array Current search set
*/
function refresh_search()
{
if (!empty($this->search_subject) && !empty($this->search_string))
$this->search_set = $this->search('', $this->search_subject, $this->search_string, $this->search_charset);
return $this->get_search_set();
}
/**
* Check if the given message ID is part of the current search set
*
* @return boolean True on match or if no search request is stored
*/
function in_searchset($msgid)
{
if (!empty($this->search_string))
return in_array("$msgid", (array)$this->search_set, true);
else
return true;
}
/**
* Return message headers object of a specific message
*
* @param int Message ID
* @param string Mailbox to read from
* @param boolean True if $id is the message UID
* @return object Message headers representation
*/
function get_headers($id, $mbox_name=NULL, $is_uid=TRUE)
{
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
$uid = $is_uid ? $id : $this->_id2uid($id);
// get cached headers
if ($uid && ($headers = &$this->get_cached_message($mailbox.'.msg', $uid)))
return $headers;
$headers = iil_C_FetchHeader($this->conn, $mailbox, $id, $is_uid);
// write headers cache
if ($headers)
{
if ($headers->uid && $headers->id)
$this->uid_id_map[$mailbox][$headers->uid] = $headers->id;
$this->add_message_cache($mailbox.'.msg', $headers->id, $headers);
}
return $headers;
}
/**
* Fetch body structure from the IMAP server and build
* an object structure similar to the one generated by PEAR::Mail_mimeDecode
*
* @param int Message UID to fetch
* @return object stdClass Message part tree or False on failure
*/
function &get_structure($uid)
{
$cache_key = $this->mailbox.'.msg';
$headers = &$this->get_cached_message($cache_key, $uid, true);
// return cached message structure
if (is_object($headers) && is_object($headers->structure))
return $headers->structure;
// resolve message sequence number
if (!($msg_id = $this->_uid2id($uid)))
return FALSE;
$structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id);
$structure = iml_GetRawStructureArray($structure_str);
$struct = false;
// parse structure and add headers
if (!empty($structure))
{
$this->_msg_id = $msg_id;
$headers = $this->get_headers($uid);
$struct = &$this->_structure_part($structure);
$struct->headers = get_object_vars($headers);
// don't trust given content-type
if (empty($struct->parts) && !empty($struct->headers['ctype']))
{
$struct->mime_id = '1';
$struct->mimetype = strtolower($struct->headers['ctype']);
list($struct->ctype_primary, $struct->ctype_secondary) = explode('/', $struct->mimetype);
}
// write structure to cache
if ($this->caching_enabled)
$this->add_message_cache($cache_key, $msg_id, $headers, $struct);
}
return $struct;
}
/**
* Build message part object
*
* @access private
*/
function &_structure_part($part, $count=0, $parent='')
{
$struct = new rcube_message_part;
$struct->mime_id = empty($parent) ? (string)$count : "$parent.$count";
// multipart
if (is_array($part[0]))
{
$struct->ctype_primary = 'multipart';
// find first non-array entry
for ($i=1; $i<count($part); $i++)
if (!is_array($part[$i]))
{
$struct->ctype_secondary = strtolower($part[$i]);
break;
}
$struct->mimetype = 'multipart/'.$struct->ctype_secondary;
$struct->parts = array();
for ($i=0, $count=0; $i<count($part); $i++)
if (is_array($part[$i]) && count($part[$i]) > 3)
$struct->parts[] = $this->_structure_part($part[$i], ++$count, $struct->mime_id);
return $struct;
}
// regular part
$struct->ctype_primary = strtolower($part[0]);
$struct->ctype_secondary = strtolower($part[1]);
$struct->mimetype = $struct->ctype_primary.'/'.$struct->ctype_secondary;
// read content type parameters
if (is_array($part[2]))
{
$struct->ctype_parameters = array();
for ($i=0; $i<count($part[2]); $i+=2)
$struct->ctype_parameters[strtolower($part[2][$i])] = $part[2][$i+1];
if (isset($struct->ctype_parameters['charset']))
$struct->charset = $struct->ctype_parameters['charset'];
}
// read content encoding
if (!empty($part[5]) && $part[5]!='NIL')
{
$struct->encoding = strtolower($part[5]);
$struct->headers['content-transfer-encoding'] = $struct->encoding;
}
// get part size
if (!empty($part[6]) && $part[6]!='NIL')
$struct->size = intval($part[6]);
// read part disposition
$di = count($part) - 2;
if ((is_array($part[$di]) && count($part[$di]) == 2 && is_array($part[$di][1])) ||
(is_array($part[--$di]) && count($part[$di]) == 2))
{
$struct->disposition = strtolower($part[$di][0]);
if (is_array($part[$di][1]))
for ($n=0; $n<count($part[$di][1]); $n+=2)
$struct->d_parameters[strtolower($part[$di][1][$n])] = $part[$di][1][$n+1];
}
// get child parts
if (is_array($part[8]) && $di != 8)
{
$struct->parts = array();
for ($i=0, $count=0; $i<count($part[8]); $i++)
if (is_array($part[8][$i]) && count($part[8][$i]) > 5)
$struct->parts[] = $this->_structure_part($part[8][$i], ++$count, $struct->mime_id);
}
// get part ID
if (!empty($part[3]) && $part[3]!='NIL')
{
$struct->content_id = $part[3];
$struct->headers['content-id'] = $part[3];
if (empty($struct->disposition))
$struct->disposition = 'inline';
}
// fetch message headers if message/rfc822
if ($struct->ctype_primary=='message')
{
$headers = iil_C_FetchPartBody($this->conn, $this->mailbox, $this->_msg_id, $struct->mime_id.'.HEADER');
$struct->headers = $this->_parse_headers($headers);
if (is_array($part[8]) && empty($struct->parts))
$struct->parts[] = $this->_structure_part($part[8], ++$count, $struct->mime_id);
}
// normalize filename property
if ($filename_mime = $struct->d_parameters['filename'] ? $struct->d_parameters['filename'] : $struct->ctype_parameters['name'])
{
$struct->filename = rcube_imap::decode_mime_string($filename_mime,
$struct->charset ? $struct->charset : rc_detect_encoding($filename_mime, $this->default_charset));
}
else if ($filename_encoded = $struct->d_parameters['filename*'] ? $struct->d_parameters['filename*'] : $struct->ctype_parameters['name*'])
{
// decode filename according to RFC 2231, Section 4
list($filename_charset,, $filename_urlencoded) = split('\'', $filename_encoded);
$struct->filename = rcube_charset_convert(urldecode($filename_urlencoded), $filename_charset);
}
else if (!empty($struct->headers['content-description']))
$struct->filename = rcube_imap::decode_mime_string($struct->headers['content-description'],
$struct->charset ? $struct->charset : rc_detect_encoding($struct->headers['content-description'],$this->default_charset));
return $struct;
}
/**
* Fetch message body of a specific message from the server
*
* @param int Message UID
* @param string Part number
* @param object rcube_message_part Part object created by get_structure()
* @param mixed True to print part, ressource to write part contents in
* @return string Message/part body if not printed
*/
function &get_message_part($uid, $part=1, $o_part=NULL, $print=NULL)
{
if (!($msg_id = $this->_uid2id($uid)))
return FALSE;
// get part encoding if not provided
if (!is_object($o_part))
{
$structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id);
$structure = iml_GetRawStructureArray($structure_str);
$part_type = iml_GetPartTypeCode($structure, $part);
$o_part = new rcube_message_part;
$o_part->ctype_primary = $part_type==0 ? 'text' : ($part_type==2 ? 'message' : 'other');
$o_part->encoding = strtolower(iml_GetPartEncodingString($structure, $part));
$o_part->charset = iml_GetPartCharset($structure, $part);
}
// TODO: Add caching for message parts
if ($print)
{
$mode = $o_part->encoding == 'base64' ? 3 : ($o_part->encoding == 'quoted-printable' ? 1 : 2);
$body = iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, $part, $mode);
// we have to decode the part manually before printing
if ($mode == 1)
{
echo $this->mime_decode($body, $o_part->encoding);
$body = true;
}
}
else
{
$body = iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, $part, 1);
// decode part body
if ($o_part->encoding)
$body = $this->mime_decode($body, $o_part->encoding);
// convert charset (if text or message part)
if ($o_part->ctype_primary=='text' || $o_part->ctype_primary=='message')
{
// assume default if no charset specified
if (empty($o_part->charset))
$o_part->charset = $this->default_charset;
$body = rcube_charset_convert($body, $o_part->charset);
}
}
return $body;
}
/**
* Fetch message body of a specific message from the server
*
* @param int Message UID
* @return string Message/part body
* @see rcube_imap::get_message_part()
*/
function &get_body($uid, $part=1)
{
$headers = $this->get_headers($uid);
return rcube_charset_convert(
$this->mime_decode($this->get_message_part($uid, $part), 'quoted-printable'),
$headers->charset ? $headers->charset : $this->default_charset);
}
/**
* Returns the whole message source as string
*
* @param int Message UID
* @return string Message source string
*/
function &get_raw_body($uid)
{
if (!($msg_id = $this->_uid2id($uid)))
return FALSE;
$body = iil_C_FetchPartHeader($this->conn, $this->mailbox, $msg_id, NULL);
$body .= iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, NULL, 1);
return $body;
}
/**
* Sends the whole message source to stdout
*
* @param int Message UID
*/
function print_raw_body($uid)
{
if (!($msg_id = $this->_uid2id($uid)))
return FALSE;
print iil_C_FetchPartHeader($this->conn, $this->mailbox, $msg_id, NULL);
flush();
iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, NULL, 2);
}
/**
* Set message flag to one or several messages
*
* @param mixed Message UIDs as array or as comma-separated string
* @param string Flag to set: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT, MDNSENT
* @return boolean True on success, False on failure
*/
function set_flag($uids, $flag)
{
$flag = strtoupper($flag);
$msg_ids = array();
if (!is_array($uids))
$uids = explode(',',$uids);
foreach ($uids as $uid) {
$msg_ids[$uid] = $this->_uid2id($uid);
}
if ($flag=='UNDELETED')
$result = iil_C_Undelete($this->conn, $this->mailbox, join(',', array_values($msg_ids)));
else if ($flag=='UNSEEN')
$result = iil_C_Unseen($this->conn, $this->mailbox, join(',', array_values($msg_ids)));
else if ($flag=='UNFLAGGED')
$result = iil_C_UnFlag($this->conn, $this->mailbox, join(',', array_values($msg_ids)), 'FLAGGED');
else
$result = iil_C_Flag($this->conn, $this->mailbox, join(',', array_values($msg_ids)), $flag);
// reload message headers if cached
$cache_key = $this->mailbox.'.msg';
if ($this->caching_enabled)
{
foreach ($msg_ids as $uid => $id)
{
if ($cached_headers = $this->get_cached_message($cache_key, $uid))
{
$this->remove_message_cache($cache_key, $id);
//$this->get_headers($uid);
}
}
// close and re-open connection
// this prevents connection problems with Courier
$this->reconnect();
}
// set nr of messages that were flaged
$count = count($msg_ids);
// clear message count cache
if ($result && $flag=='SEEN')
$this->_set_messagecount($this->mailbox, 'UNSEEN', $count*(-1));
else if ($result && $flag=='UNSEEN')
$this->_set_messagecount($this->mailbox, 'UNSEEN', $count);
else if ($result && $flag=='DELETED')
$this->_set_messagecount($this->mailbox, 'ALL', $count*(-1));
return $result;
}
/**
* Append a mail message (source) to a specific mailbox
*
* @param string Target mailbox
* @param string Message source
* @return boolean True on success, False on error
*/
function save_message($mbox_name, &$message)
{
$mbox_name = stripslashes($mbox_name);
$mailbox = $this->_mod_mailbox($mbox_name);
// make sure mailbox exists
if (in_array($mailbox, $this->_list_mailboxes()))
$saved = iil_C_Append($this->conn, $mailbox, $message);
if ($saved)
{
// increase messagecount of the target mailbox
$this->_set_messagecount($mailbox, 'ALL', 1);
}
return $saved;
}
/**
* Move a message from one mailbox to another
*
* @param string List of UIDs to move, separated by comma
* @param string Target mailbox
* @param string Source mailbox
* @return boolean True on success, False on error
*/
function move_message($uids, $to_mbox, $from_mbox='')
{
$to_mbox_in = stripslashes($to_mbox);
$from_mbox = stripslashes($from_mbox);
$to_mbox = $this->_mod_mailbox($to_mbox_in);
$from_mbox = $from_mbox ? $this->_mod_mailbox($from_mbox) : $this->mailbox;
// make sure mailbox exists
if (!in_array($to_mbox, $this->_list_mailboxes()))
{
if (in_array($to_mbox_in, $this->default_folders))
$this->create_mailbox($to_mbox_in, TRUE);
else
return FALSE;
}
// convert the list of uids to array
$a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL);
// exit if no message uids are specified
if (!is_array($a_uids))
return false;
// convert uids to message ids
$a_mids = array();
foreach ($a_uids as $uid)
$a_mids[] = $this->_uid2id($uid, $from_mbox);
$iil_move = iil_C_Move($this->conn, join(',', $a_mids), $from_mbox, $to_mbox);
$moved = !($iil_move === false || $iil_move < 0);
// send expunge command in order to have the moved message
// really deleted from the source mailbox
if ($moved) {
// but only when flag_for_deletion is set to false
if (!rcmail::get_instance()->config->get('flag_for_deletion', false))
{
$this->_expunge($from_mbox, FALSE);
$this->_clear_messagecount($from_mbox);
$this->_clear_messagecount($to_mbox);
}
}
// moving failed
else if (rcmail::get_instance()->config->get('delete_always', false)) {
return iil_C_Delete($this->conn, $from_mbox, join(',', $a_mids));
}
// remove message ids from search set
if ($moved && $this->search_set && $from_mbox == $this->mailbox)
$this->search_set = array_diff($this->search_set, $a_mids);
// update cached message headers
$cache_key = $from_mbox.'.msg';
if ($moved && ($a_cache_index = $this->get_message_cache_index($cache_key)))
{
$start_index = 100000;
foreach ($a_uids as $uid)
{
if (($index = array_search($uid, $a_cache_index)) !== FALSE)
$start_index = min($index, $start_index);
}
// clear cache from the lowest index on
$this->clear_message_cache($cache_key, $start_index);
}
return $moved;
}
/**
* Mark messages as deleted and expunge mailbox
*
* @param string List of UIDs to move, separated by comma
* @param string Source mailbox
* @return boolean True on success, False on error
*/
function delete_message($uids, $mbox_name='')
{
$mbox_name = stripslashes($mbox_name);
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
// convert the list of uids to array
$a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL);
// exit if no message uids are specified
if (!is_array($a_uids))
return false;
// convert uids to message ids
$a_mids = array();
foreach ($a_uids as $uid)
$a_mids[] = $this->_uid2id($uid, $mailbox);
$deleted = iil_C_Delete($this->conn, $mailbox, join(',', $a_mids));
// send expunge command in order to have the deleted message
// really deleted from the mailbox
if ($deleted)
{
$this->_expunge($mailbox, FALSE);
$this->_clear_messagecount($mailbox);
unset($this->uid_id_map[$mailbox]);
}
// remove message ids from search set
if ($deleted && $this->search_set && $mailbox == $this->mailbox)
$this->search_set = array_diff($this->search_set, $a_mids);
// remove deleted messages from cache
$cache_key = $mailbox.'.msg';
if ($deleted && ($a_cache_index = $this->get_message_cache_index($cache_key)))
{
$start_index = 100000;
foreach ($a_uids as $uid)
{
if (($index = array_search($uid, $a_cache_index)) !== FALSE)
$start_index = min($index, $start_index);
}
// clear cache from the lowest index on
$this->clear_message_cache($cache_key, $start_index);
}
return $deleted;
}
/**
* Clear all messages in a specific mailbox
*
* @param string Mailbox name
* @return int Above 0 on success
*/
function clear_mailbox($mbox_name=NULL)
{
$mbox_name = stripslashes($mbox_name);
$mailbox = !empty($mbox_name) ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
$msg_count = $this->_messagecount($mailbox, 'ALL');
if ($msg_count>0)
{
$cleared = iil_C_ClearFolder($this->conn, $mailbox);
// make sure the message count cache is cleared as well
if ($cleared)
{
$this->clear_message_cache($mailbox.'.msg');
$a_mailbox_cache = $this->get_cache('messagecount');
unset($a_mailbox_cache[$mailbox]);
$this->update_cache('messagecount', $a_mailbox_cache);
}
return $cleared;
}
else
return 0;
}
/**
* Send IMAP expunge command and clear cache
*
* @param string Mailbox name
* @param boolean False if cache should not be cleared
* @return boolean True on success
*/
function expunge($mbox_name='', $clear_cache=TRUE)
{
$mbox_name = stripslashes($mbox_name);
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
return $this->_expunge($mailbox, $clear_cache);
}
/**
* Send IMAP expunge command and clear cache
*
* @see rcube_imap::expunge()
* @access private
*/
function _expunge($mailbox, $clear_cache=TRUE)
{
$result = iil_C_Expunge($this->conn, $mailbox);
if ($result>=0 && $clear_cache)
{
$this->clear_message_cache($mailbox.'.msg');
$this->_clear_messagecount($mailbox);
}
return $result;
}
/* --------------------------------
* folder managment
* --------------------------------*/
/**
* Get a list of all folders available on the IMAP server
*
* @param string IMAP root dir
* @return array Indexed array with folder names
*/
function list_unsubscribed($root='')
{
static $sa_unsubscribed;
if (is_array($sa_unsubscribed))
return $sa_unsubscribed;
// retrieve list of folders from IMAP server
$a_mboxes = iil_C_ListMailboxes($this->conn, $this->_mod_mailbox($root), '*');
// modify names with root dir
foreach ($a_mboxes as $mbox_name)
{
$name = $this->_mod_mailbox($mbox_name, 'out');
if (strlen($name))
$a_folders[] = $name;
}
// filter folders and sort them
$sa_unsubscribed = $this->_sort_mailbox_list($a_folders);
return $sa_unsubscribed;
}
/**
* Get mailbox quota information
* added by Nuny
*
* @return mixed Quota info or False if not supported
*/
function get_quota()
{
if ($this->get_capability('QUOTA'))
return iil_C_GetQuota($this->conn);
return FALSE;
}
/**
* Subscribe to a specific mailbox(es)
*
* @param array Mailbox name(s)
* @return boolean True on success
*/
function subscribe($a_mboxes)
{
if (!is_array($a_mboxes))
$a_mboxes = array($a_mboxes);
// let this common function do the main work
return $this->_change_subscription($a_mboxes, 'subscribe');
}
/**
* Unsubscribe mailboxes
*
* @param array Mailbox name(s)
* @return boolean True on success
*/
function unsubscribe($a_mboxes)
{
if (!is_array($a_mboxes))
$a_mboxes = array($a_mboxes);
// let this common function do the main work
return $this->_change_subscription($a_mboxes, 'unsubscribe');
}
/**
* Create a new mailbox on the server and register it in local cache
*
* @param string New mailbox name (as utf-7 string)
* @param boolean True if the new mailbox should be subscribed
* @param string Name of the created mailbox, false on error
*/
function create_mailbox($name, $subscribe=FALSE)
{
$result = FALSE;
// replace backslashes
$name = preg_replace('/[\\\]+/', '-', $name);
// reduce mailbox name to 100 chars
$name = substr($name, 0, 100);
$abs_name = $this->_mod_mailbox($name);
$a_mailbox_cache = $this->get_cache('mailboxes');
if (strlen($abs_name) && (!is_array($a_mailbox_cache) || !in_array($abs_name, $a_mailbox_cache)))
$result = iil_C_CreateFolder($this->conn, $abs_name);
// try to subscribe it
if ($result && $subscribe)
$this->subscribe($name);
return $result ? $name : FALSE;
}
/**
* Set a new name to an existing mailbox
*
* @param string Mailbox to rename (as utf-7 string)
* @param string New mailbox name (as utf-7 string)
* @return string Name of the renames mailbox, False on error
*/
function rename_mailbox($mbox_name, $new_name)
{
$result = FALSE;
// replace backslashes
$name = preg_replace('/[\\\]+/', '-', $new_name);
// encode mailbox name and reduce it to 100 chars
$name = substr($new_name, 0, 100);
// make absolute path
$mailbox = $this->_mod_mailbox($mbox_name);
$abs_name = $this->_mod_mailbox($name);
// check if mailbox is subscribed
$a_subscribed = $this->_list_mailboxes();
$subscribed = in_array($mailbox, $a_subscribed);
// unsubscribe folder
if ($subscribed)
iil_C_UnSubscribe($this->conn, $mailbox);
if (strlen($abs_name))
$result = iil_C_RenameFolder($this->conn, $mailbox, $abs_name);
if ($result)
{
$delm = $this->get_hierarchy_delimiter();
// check if mailbox children are subscribed
foreach ($a_subscribed as $c_subscribed)
if (preg_match('/^'.preg_quote($mailbox.$delm, '/').'/', $c_subscribed))
{
iil_C_UnSubscribe($this->conn, $c_subscribed);
iil_C_Subscribe($this->conn, preg_replace('/^'.preg_quote($mailbox, '/').'/', $abs_name, $c_subscribed));
}
// clear cache
$this->clear_message_cache($mailbox.'.msg');
$this->clear_cache('mailboxes');
}
// try to subscribe it
if ($result && $subscribed)
iil_C_Subscribe($this->conn, $abs_name);
return $result ? $name : FALSE;
}
/**
* Remove mailboxes from server
*
* @param string Mailbox name
* @return boolean True on success
*/
function delete_mailbox($mbox_name)
{
$deleted = FALSE;
if (is_array($mbox_name))
$a_mboxes = $mbox_name;
else if (is_string($mbox_name) && strlen($mbox_name))
$a_mboxes = explode(',', $mbox_name);
$all_mboxes = iil_C_ListMailboxes($this->conn, $this->_mod_mailbox($root), '*');
if (is_array($a_mboxes))
foreach ($a_mboxes as $mbox_name)
{
$mailbox = $this->_mod_mailbox($mbox_name);
// unsubscribe mailbox before deleting
iil_C_UnSubscribe($this->conn, $mailbox);
// send delete command to server
$result = iil_C_DeleteFolder($this->conn, $mailbox);
if ($result>=0)
$deleted = TRUE;
foreach ($all_mboxes as $c_mbox)
{
$regex = preg_quote($mailbox . $this->delimiter, '/');
$regex = '/^' . $regex . '/';
if (preg_match($regex, $c_mbox))
{
iil_C_UnSubscribe($this->conn, $c_mbox);
$result = iil_C_DeleteFolder($this->conn, $c_mbox);
if ($result>=0)
$deleted = TRUE;
}
}
}
// clear mailboxlist cache
if ($deleted)
{
$this->clear_message_cache($mailbox.'.msg');
$this->clear_cache('mailboxes');
}
return $deleted;
}
/**
* Create all folders specified as default
*/
function create_default_folders()
{
$a_folders = iil_C_ListMailboxes($this->conn, $this->_mod_mailbox(''), '*');
$a_subscribed = iil_C_ListSubscribed($this->conn, $this->_mod_mailbox(''), '*');
// create default folders if they do not exist
foreach ($this->default_folders as $folder)
{
$abs_name = $this->_mod_mailbox($folder);
if (!in_array_nocase($abs_name, $a_folders))
$this->create_mailbox($folder, TRUE);
else if (!in_array_nocase($abs_name, $a_subscribed))
$this->subscribe($folder);
}
}
/* --------------------------------
* internal caching methods
* --------------------------------*/
/**
* @access private
*/
function set_caching($set)
{
if ($set && is_object($this->db))
$this->caching_enabled = TRUE;
else
$this->caching_enabled = FALSE;
}
/**
* @access private
*/
function get_cache($key)
{
// read cache
if (!isset($this->cache[$key]) && $this->caching_enabled)
{
$cache_data = $this->_read_cache_record('IMAP.'.$key);
$this->cache[$key] = strlen($cache_data) ? unserialize($cache_data) : FALSE;
}
return $this->cache[$key];
}
/**
* @access private
*/
function update_cache($key, $data)
{
$this->cache[$key] = $data;
$this->cache_changed = TRUE;
$this->cache_changes[$key] = TRUE;
}
/**
* @access private
*/
function write_cache()
{
if ($this->caching_enabled && $this->cache_changed)
{
foreach ($this->cache as $key => $data)
{
if ($this->cache_changes[$key])
$this->_write_cache_record('IMAP.'.$key, serialize($data));
}
}
}
/**
* @access private
*/
function clear_cache($key=NULL)
{
if (!$this->caching_enabled)
return;
if ($key===NULL)
{
foreach ($this->cache as $key => $data)
$this->_clear_cache_record('IMAP.'.$key);
$this->cache = array();
$this->cache_changed = FALSE;
$this->cache_changes = array();
}
else
{
$this->_clear_cache_record('IMAP.'.$key);
$this->cache_changes[$key] = FALSE;
unset($this->cache[$key]);
}
}
/**
* @access private
*/
function _read_cache_record($key)
{
$cache_data = FALSE;
if ($this->db)
{
// get cached data from DB
$sql_result = $this->db->query(
"SELECT cache_id, data
FROM ".get_table_name('cache')."
WHERE user_id=?
AND cache_key=?",
$_SESSION['user_id'],
$key);
if ($sql_arr = $this->db->fetch_assoc($sql_result))
{
$cache_data = $sql_arr['data'];
$this->cache_keys[$key] = $sql_arr['cache_id'];
}
}
return $cache_data;
}
/**
* @access private
*/
function _write_cache_record($key, $data)
{
if (!$this->db)
return FALSE;
// check if we already have a cache entry for this key
if (!isset($this->cache_keys[$key]))
{
$sql_result = $this->db->query(
"SELECT cache_id
FROM ".get_table_name('cache')."
WHERE user_id=?
AND cache_key=?",
$_SESSION['user_id'],
$key);
if ($sql_arr = $this->db->fetch_assoc($sql_result))
$this->cache_keys[$key] = $sql_arr['cache_id'];
else
$this->cache_keys[$key] = FALSE;
}
// update existing cache record
if ($this->cache_keys[$key])
{
$this->db->query(
"UPDATE ".get_table_name('cache')."
SET created=".$this->db->now().",
data=?
WHERE user_id=?
AND cache_key=?",
$data,
$_SESSION['user_id'],
$key);
}
// add new cache record
else
{
$this->db->query(
"INSERT INTO ".get_table_name('cache')."
(created, user_id, cache_key, data)
VALUES (".$this->db->now().", ?, ?, ?)",
$_SESSION['user_id'],
$key,
$data);
}
}
/**
* @access private
*/
function _clear_cache_record($key)
{
$this->db->query(
"DELETE FROM ".get_table_name('cache')."
WHERE user_id=?
AND cache_key=?",
$_SESSION['user_id'],
$key);
}
/* --------------------------------
* message caching methods
* --------------------------------*/
/**
* Checks if the cache is up-to-date
*
* @param string Mailbox name
* @param string Internal cache key
* @return int -3 = off, -2 = incomplete, -1 = dirty
*/
function check_cache_status($mailbox, $cache_key)
{
if (!$this->caching_enabled)
return -3;
$cache_index = $this->get_message_cache_index($cache_key, TRUE);
$msg_count = $this->_messagecount($mailbox);
$cache_count = count($cache_index);
// console("Cache check: $msg_count !== ".count($cache_index));
if ($cache_count==$msg_count)
{
// get highest index
$header = iil_C_FetchHeader($this->conn, $mailbox, "$msg_count");
$cache_uid = array_pop($cache_index);
// uids of highest message matches -> cache seems OK
if ($cache_uid == $header->uid)
return 1;
// cache is dirty
return -1;
}
// if cache count differs less than 10% report as dirty
else if (abs($msg_count - $cache_count) < $msg_count/10)
return -1;
else
return -2;
}
/**
* @access private
*/
function get_message_cache($key, $from, $to, $sort_field, $sort_order)
{
$cache_key = "$key:$from:$to:$sort_field:$sort_order";
$db_header_fields = array('idx', 'uid', 'subject', 'from', 'to', 'cc', 'date', 'size');
if (!in_array($sort_field, $db_header_fields))
$sort_field = 'idx';
if ($this->caching_enabled && !isset($this->cache[$cache_key]))
{
$this->cache[$cache_key] = array();
$sql_result = $this->db->limitquery(
"SELECT idx, uid, headers
FROM ".get_table_name('messages')."
WHERE user_id=?
AND cache_key=?
ORDER BY ".$this->db->quoteIdentifier($sort_field)." ".
strtoupper($sort_order),
$from,
$to-$from,
$_SESSION['user_id'],
$key);
while ($sql_arr = $this->db->fetch_assoc($sql_result))
{
$uid = $sql_arr['uid'];
$this->cache[$cache_key][$uid] = unserialize($sql_arr['headers']);
// featch headers if unserialize failed
if (empty($this->cache[$cache_key][$uid]))
$this->cache[$cache_key][$uid] = iil_C_FetchHeader($this->conn, preg_replace('/.msg$/', '', $key), $uid, true);
}
}
return $this->cache[$cache_key];
}
/**
* @access private
*/
function &get_cached_message($key, $uid, $struct=false)
{
$internal_key = '__single_msg';
if ($this->caching_enabled && (!isset($this->cache[$internal_key][$uid]) ||
($struct && empty($this->cache[$internal_key][$uid]->structure))))
{
$sql_select = "idx, uid, headers" . ($struct ? ", structure" : '');
$sql_result = $this->db->query(
"SELECT $sql_select
FROM ".get_table_name('messages')."
WHERE user_id=?
AND cache_key=?
AND uid=?",
$_SESSION['user_id'],
$key,
$uid);
if ($sql_arr = $this->db->fetch_assoc($sql_result))
{
$this->cache[$internal_key][$uid] = unserialize($sql_arr['headers']);
if (is_object($this->cache[$internal_key][$uid]) && !empty($sql_arr['structure']))
$this->cache[$internal_key][$uid]->structure = unserialize($sql_arr['structure']);
}
}
return $this->cache[$internal_key][$uid];
}
/**
* @access private
*/
function get_message_cache_index($key, $force=FALSE, $sort_col='idx', $sort_order='ASC')
{
static $sa_message_index = array();
// empty key -> empty array
if (!$this->caching_enabled || empty($key))
return array();
if (!empty($sa_message_index[$key]) && !$force)
return $sa_message_index[$key];
$sa_message_index[$key] = array();
$sql_result = $this->db->query(
"SELECT idx, uid
FROM ".get_table_name('messages')."
WHERE user_id=?
AND cache_key=?
ORDER BY ".$this->db->quote_identifier($sort_col)." ".$sort_order,
$_SESSION['user_id'],
$key);
while ($sql_arr = $this->db->fetch_assoc($sql_result))
$sa_message_index[$key][$sql_arr['idx']] = $sql_arr['uid'];
return $sa_message_index[$key];
}
/**
* @access private
*/
function add_message_cache($key, $index, $headers, $struct=null)
{
if (empty($key) || !is_object($headers) || empty($headers->uid))
return;
// add to internal (fast) cache
$this->cache['__single_msg'][$headers->uid] = $headers;
$this->cache['__single_msg'][$headers->uid]->structure = $struct;
// no further caching
if (!$this->caching_enabled)
return;
// check for an existing record (probly headers are cached but structure not)
$sql_result = $this->db->query(
"SELECT message_id
FROM ".get_table_name('messages')."
WHERE user_id=?
AND cache_key=?
AND uid=?
AND del<>1",
$_SESSION['user_id'],
$key,
$headers->uid);
// update cache record
if ($sql_arr = $this->db->fetch_assoc($sql_result))
{
$this->db->query(
"UPDATE ".get_table_name('messages')."
SET idx=?, headers=?, structure=?
WHERE message_id=?",
$index,
serialize($headers),
is_object($struct) ? serialize($struct) : NULL,
$sql_arr['message_id']
);
}
else // insert new record
{
$this->db->query(
"INSERT INTO ".get_table_name('messages')."
(user_id, del, cache_key, created, idx, uid, subject, ".$this->db->quoteIdentifier('from').", ".$this->db->quoteIdentifier('to').", cc, date, size, headers, structure)
VALUES (?, 0, ?, ".$this->db->now().", ?, ?, ?, ?, ?, ?, ".$this->db->fromunixtime($headers->timestamp).", ?, ?, ?)",
$_SESSION['user_id'],
$key,
$index,
$headers->uid,
(string)substr($this->decode_header($headers->subject, TRUE), 0, 128),
(string)substr($this->decode_header($headers->from, TRUE), 0, 128),
(string)substr($this->decode_header($headers->to, TRUE), 0, 128),
(string)substr($this->decode_header($headers->cc, TRUE), 0, 128),
(int)$headers->size,
serialize($headers),
is_object($struct) ? serialize($struct) : NULL
);
}
}
/**
* @access private
*/
function remove_message_cache($key, $index)
{
if (!$this->caching_enabled)
return;
$this->db->query(
"DELETE FROM ".get_table_name('messages')."
WHERE user_id=?
AND cache_key=?
AND idx=?",
$_SESSION['user_id'],
$key,
$index);
}
/**
* @access private
*/
function clear_message_cache($key, $start_index=1)
{
if (!$this->caching_enabled)
return;
$this->db->query(
"DELETE FROM ".get_table_name('messages')."
WHERE user_id=?
AND cache_key=?
AND idx>=?",
$_SESSION['user_id'],
$key,
$start_index);
}
/* --------------------------------
* encoding/decoding methods
* --------------------------------*/
/**
* Split an address list into a structured array list
*
* @param string Input string
* @param int List only this number of addresses
* @param boolean Decode address strings
* @return array Indexed list of addresses
*/
function decode_address_list($input, $max=null, $decode=true)
{
$a = $this->_parse_address_list($input, $decode);
$out = array();
// Special chars as defined by RFC 822 need to in quoted string (or escaped).
$special_chars = '[\(\)\<\>\\\.\[\]@,;:"]';
if (!is_array($a))
return $out;
$c = count($a);
$j = 0;
foreach ($a as $val)
{
$j++;
$address = $val['address'];
$name = preg_replace(array('/^[\'"]/', '/[\'"]$/'), '', trim($val['name']));
if ($name && $address && $name != $address)
$string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address);
else if ($address)
$string = $address;
else if ($name)
$string = $name;
$out[$j] = array('name' => $name,
'mailto' => $address,
'string' => $string);
if ($max && $j==$max)
break;
}
return $out;
}
/**
* Decode a message header value
*
* @param string Header value
* @param boolean Remove quotes if necessary
* @return string Decoded string
*/
function decode_header($input, $remove_quotes=FALSE)
{
$str = rcube_imap::decode_mime_string((string)$input, $this->default_charset);
if ($str{0}=='"' && $remove_quotes)
$str = str_replace('"', '', $str);
return $str;
}
/**
* Decode a mime-encoded string to internal charset
*
* @param string Header value
* @param string Fallback charset if none specified
* @return string Decoded string
* @static
*/
function decode_mime_string($input, $fallback=null)
{
$out = '';
$pos = strpos($input, '=?');
if ($pos !== false)
{
// rfc: all line breaks or other characters not found
// in the Base64 Alphabet must be ignored by decoding software
// delete all blanks between MIME-lines, differently we can
// receive unnecessary blanks and broken utf-8 symbols
$input = preg_replace("/\?=\s+=\?/", '?==?', $input);
$out = substr($input, 0, $pos);
$end_cs_pos = strpos($input, "?", $pos+2);
$end_en_pos = strpos($input, "?", $end_cs_pos+1);
$end_pos = strpos($input, "?=", $end_en_pos+1);
$encstr = substr($input, $pos+2, ($end_pos-$pos-2));
$rest = substr($input, $end_pos+2);
$out .= rcube_imap::_decode_mime_string_part($encstr);
$out .= rcube_imap::decode_mime_string($rest, $fallback);
return $out;
}
// no encoding information, use fallback
return rcube_charset_convert($input,
!empty($fallback) ? $fallback : rcmail::get_instance()->config->get('default_charset', 'ISO-8859-1'));
}
/**
* Decode a part of a mime-encoded string
*
* @access private
*/
function _decode_mime_string_part($str)
{
$a = explode('?', $str);
$count = count($a);
// should be in format "charset?encoding?base64_string"
if ($count >= 3)
{
for ($i=2; $i<$count; $i++)
$rest.=$a[$i];
if (($a[1]=="B")||($a[1]=="b"))
$rest = base64_decode($rest);
else if (($a[1]=="Q")||($a[1]=="q"))
{
$rest = str_replace("_", " ", $rest);
$rest = quoted_printable_decode($rest);
}
return rcube_charset_convert($rest, $a[0]);
}
else
return $str; // we dont' know what to do with this
}
/**
* Decode a mime part
*
* @param string Input string
* @param string Part encoding
* @return string Decoded string
* @access private
*/
function mime_decode($input, $encoding='7bit')
{
switch (strtolower($encoding))
{
case '7bit':
return $input;
break;
case 'quoted-printable':
return quoted_printable_decode($input);
break;
case 'base64':
return base64_decode($input);
break;
default:
return $input;
}
}
/**
* Convert body charset to UTF-8 according to the ctype_parameters
*
* @param string Part body to decode
* @param string Charset to convert from
* @return string Content converted to internal charset
*/
function charset_decode($body, $ctype_param)
{
if (is_array($ctype_param) && !empty($ctype_param['charset']))
return rcube_charset_convert($body, $ctype_param['charset']);
// defaults to what is specified in the class header
return rcube_charset_convert($body, $this->default_charset);
}
/**
* Translate UID to message ID
*
* @param int Message UID
* @param string Mailbox name
* @return int Message ID
*/
function get_id($uid, $mbox_name=NULL)
{
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
return $this->_uid2id($uid, $mailbox);
}
/**
* Translate message number to UID
*
* @param int Message ID
* @param string Mailbox name
* @return int Message UID
*/
function get_uid($id,$mbox_name=NULL)
{
$mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox;
return $this->_id2uid($id, $mailbox);
}
/* --------------------------------
* private methods
* --------------------------------*/
/**
* @access private
*/
function _mod_mailbox($mbox_name, $mode='in')
{
if ((!empty($this->root_ns) && $this->root_ns == $mbox_name) || $mbox_name == 'INBOX')
return $mbox_name;
if (!empty($this->root_dir) && $mode=='in')
$mbox_name = $this->root_dir.$this->delimiter.$mbox_name;
else if (strlen($this->root_dir) && $mode=='out')
$mbox_name = substr($mbox_name, strlen($this->root_dir)+1);
return $mbox_name;
}
/**
* Validate the given input and save to local properties
* @access private
*/
function _set_sort_order($sort_field, $sort_order)
{
if ($sort_field != null)
$this->sort_field = asciiwords($sort_field);
if ($sort_order != null)
$this->sort_order = strtoupper($sort_order) == 'DESC' ? 'DESC' : 'ASC';
}
/**
* Sort mailboxes first by default folders and then in alphabethical order
* @access private
*/
function _sort_mailbox_list($a_folders)
{
$a_out = $a_defaults = $folders = array();
$delimiter = $this->get_hierarchy_delimiter();
// find default folders and skip folders starting with '.'
foreach ($a_folders as $i => $folder)
{
if ($folder{0}=='.')
continue;
if (($p = array_search(strtolower($folder), $this->default_folders_lc)) !== false && !$a_defaults[$p])
$a_defaults[$p] = $folder;
else
$folders[$folder] = rc_strtolower(rcube_charset_convert($folder, 'UTF-7'));
}
asort($folders, SORT_LOCALE_STRING);
ksort($a_defaults);
$folders = array_merge($a_defaults, array_keys($folders));
// finally we must rebuild the list to move
// subfolders of default folders to their place...
// ...also do this for the rest of folders because
// asort() is not properly sorting case sensitive names
while (list($key, $folder) = each($folders)) {
$a_out[] = $folder;
unset($folders[$key]);
foreach ($folders as $idx => $f) {
if (strpos($f, $folder.$delimiter) === 0) {
$a_out[] = $f;
unset($folders[$idx]);
}
}
reset($folders);
}
return $a_out;
}
/**
* @access private
*/
function _uid2id($uid, $mbox_name=NULL)
{
if (!$mbox_name)
$mbox_name = $this->mailbox;
if (!isset($this->uid_id_map[$mbox_name][$uid]))
$this->uid_id_map[$mbox_name][$uid] = iil_C_UID2ID($this->conn, $mbox_name, $uid);
return $this->uid_id_map[$mbox_name][$uid];
}
/**
* @access private
*/
function _id2uid($id, $mbox_name=NULL)
{
if (!$mbox_name)
$mbox_name = $this->mailbox;
$index = array_flip((array)$this->uid_id_map[$mbox_name]);
if (isset($index[$id]))
$uid = $index[$id];
else
{
$uid = iil_C_ID2UID($this->conn, $mbox_name, $id);
$this->uid_id_map[$mbox_name][$uid] = $id;
}
return $uid;
}
- /**
- * Parse string or array of server capabilities and put them in internal array
- * @access private
- */
- function _parse_capability($caps)
- {
- if (!is_array($caps))
- $cap_arr = explode(' ', $caps);
- else
- $cap_arr = $caps;
-
- foreach ($cap_arr as $cap)
- {
- if ($cap=='CAPABILITY')
- continue;
-
- if (strpos($cap, '=')>0)
- {
- list($key, $value) = explode('=', $cap);
- if (!is_array($this->capabilities[$key]))
- $this->capabilities[$key] = array();
-
- $this->capabilities[$key][] = $value;
- }
- else
- $this->capabilities[$cap] = TRUE;
- }
- }
-
-
/**
* Subscribe/unsubscribe a list of mailboxes and update local cache
* @access private
*/
function _change_subscription($a_mboxes, $mode)
{
$updated = FALSE;
if (is_array($a_mboxes))
foreach ($a_mboxes as $i => $mbox_name)
{
$mailbox = $this->_mod_mailbox($mbox_name);
$a_mboxes[$i] = $mailbox;
if ($mode=='subscribe')
$result = iil_C_Subscribe($this->conn, $mailbox);
else if ($mode=='unsubscribe')
$result = iil_C_UnSubscribe($this->conn, $mailbox);
if ($result>=0)
$updated = TRUE;
}
// get cached mailbox list
if ($updated)
{
$a_mailbox_cache = $this->get_cache('mailboxes');
if (!is_array($a_mailbox_cache))
return $updated;
// modify cached list
if ($mode=='subscribe')
$a_mailbox_cache = array_merge($a_mailbox_cache, $a_mboxes);
else if ($mode=='unsubscribe')
$a_mailbox_cache = array_diff($a_mailbox_cache, $a_mboxes);
// write mailboxlist to cache
$this->update_cache('mailboxes', $this->_sort_mailbox_list($a_mailbox_cache));
}
return $updated;
}
/**
* Increde/decrese messagecount for a specific mailbox
* @access private
*/
function _set_messagecount($mbox_name, $mode, $increment)
{
$a_mailbox_cache = FALSE;
$mailbox = $mbox_name ? $mbox_name : $this->mailbox;
$mode = strtoupper($mode);
$a_mailbox_cache = $this->get_cache('messagecount');
if (!is_array($a_mailbox_cache[$mailbox]) || !isset($a_mailbox_cache[$mailbox][$mode]) || !is_numeric($increment))
return FALSE;
// add incremental value to messagecount
$a_mailbox_cache[$mailbox][$mode] += $increment;
// there's something wrong, delete from cache
if ($a_mailbox_cache[$mailbox][$mode] < 0)
unset($a_mailbox_cache[$mailbox][$mode]);
// write back to cache
$this->update_cache('messagecount', $a_mailbox_cache);
return TRUE;
}
/**
* Remove messagecount of a specific mailbox from cache
* @access private
*/
function _clear_messagecount($mbox_name='')
{
$a_mailbox_cache = FALSE;
$mailbox = $mbox_name ? $mbox_name : $this->mailbox;
$a_mailbox_cache = $this->get_cache('messagecount');
if (is_array($a_mailbox_cache[$mailbox]))
{
unset($a_mailbox_cache[$mailbox]);
$this->update_cache('messagecount', $a_mailbox_cache);
}
}
/**
* Split RFC822 header string into an associative array
* @access private
*/
function _parse_headers($headers)
{
$a_headers = array();
$lines = explode("\n", $headers);
$c = count($lines);
for ($i=0; $i<$c; $i++)
{
if ($p = strpos($lines[$i], ': '))
{
$field = strtolower(substr($lines[$i], 0, $p));
$value = trim(substr($lines[$i], $p+1));
if (!empty($value))
$a_headers[$field] = $value;
}
}
return $a_headers;
}
/**
* @access private
*/
function _parse_address_list($str, $decode=true)
{
// remove any newlines and carriage returns before
$a = $this->_explode_quoted_string('[,;]', preg_replace( "/[\r\n]/", " ", $str));
$result = array();
foreach ($a as $key => $val)
{
$val = preg_replace("/([\"\w])</", "$1 <", $val);
$sub_a = $this->_explode_quoted_string(' ', $decode ? $this->decode_header($val) : $val);
$result[$key]['name'] = '';
foreach ($sub_a as $k => $v)
{
if (strpos($v, '@') > 0)
$result[$key]['address'] = str_replace('<', '', str_replace('>', '', $v));
else
$result[$key]['name'] .= (empty($result[$key]['name'])?'':' ').str_replace("\"",'',stripslashes($v));
}
if (empty($result[$key]['name']))
$result[$key]['name'] = $result[$key]['address'];
}
return $result;
}
/**
* @access private
*/
function _explode_quoted_string($delimiter, $string)
{
$result = array();
$strlen = strlen($string);
for ($q=$p=$i=0; $i < $strlen; $i++)
{
if ($string{$i} == "\"" && $string{$i-1} != "\\")
$q = $q ? false : true;
else if (!$q && preg_match("/$delimiter/", $string{$i}))
{
$result[] = substr($string, $p, $i - $p);
$p = $i + 1;
}
}
$result[] = substr($string, $p);
return $result;
}
} // end class rcube_imap
/**
* Class representing a message part
*
* @package Mail
*/
class rcube_message_part
{
var $mime_id = '';
var $ctype_primary = 'text';
var $ctype_secondary = 'plain';
var $mimetype = 'text/plain';
var $disposition = '';
var $filename = '';
var $encoding = '8bit';
var $charset = '';
var $size = 0;
var $headers = array();
var $d_parameters = array();
var $ctype_parameters = array();
}
/**
* Class for sorting an array of iilBasicHeader objects in a predetermined order.
*
* @package Mail
* @author Eric Stadtherr
*/
class rcube_header_sorter
{
var $sequence_numbers = array();
/**
* Set the predetermined sort order.
*
* @param array Numerically indexed array of IMAP message sequence numbers
*/
function set_sequence_numbers($seqnums)
{
$this->sequence_numbers = array_flip($seqnums);
}
/**
* Sort the array of header objects
*
* @param array Array of iilBasicHeader objects indexed by UID
*/
function sort_headers(&$headers)
{
/*
* uksort would work if the keys were the sequence number, but unfortunately
* the keys are the UIDs. We'll use uasort instead and dereference the value
* to get the sequence number (in the "id" field).
*
* uksort($headers, array($this, "compare_seqnums"));
*/
uasort($headers, array($this, "compare_seqnums"));
}
/**
* Sort method called by uasort()
*/
function compare_seqnums($a, $b)
{
// First get the sequence number from the header object (the 'id' field).
$seqa = $a->id;
$seqb = $b->id;
// then find each sequence number in my ordered list
$posa = isset($this->sequence_numbers[$seqa]) ? intval($this->sequence_numbers[$seqa]) : -1;
$posb = isset($this->sequence_numbers[$seqb]) ? intval($this->sequence_numbers[$seqb]) : -1;
// return the relative position as the comparison value
return $posa - $posb;
}
}
/**
* Add quoted-printable encoding to a given string
*
* @param string String to encode
* @param int Add new line after this number of characters
* @param boolean True if spaces should be converted into =20
* @return string Encoded string
*/
function quoted_printable_encode($input, $line_max=76, $space_conv=false)
{
$hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
$lines = preg_split("/(?:\r\n|\r|\n)/", $input);
$eol = "\r\n";
$escape = "=";
$output = "";
while( list(, $line) = each($lines))
{
//$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary
$linlen = strlen($line);
$newline = "";
for($i = 0; $i < $linlen; $i++)
{
$c = substr( $line, $i, 1 );
$dec = ord( $c );
if ( ( $i == 0 ) && ( $dec == 46 ) ) // convert first point in the line into =2E
{
$c = "=2E";
}
if ( $dec == 32 )
{
if ( $i == ( $linlen - 1 ) ) // convert space at eol only
{
$c = "=20";
}
else if ( $space_conv )
{
$c = "=20";
}
}
else if ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) ) // always encode "\t", which is *not* required
{
$h2 = floor($dec/16);
$h1 = floor($dec%16);
$c = $escape.$hex["$h2"].$hex["$h1"];
}
if ( (strlen($newline) + strlen($c)) >= $line_max ) // CRLF is not counted
{
$output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay
$newline = "";
// check if newline first character will be point or not
if ( $dec == 46 )
{
$c = "=2E";
}
}
$newline .= $c;
} // end of for
$output .= $newline.$eol;
} // end of while
return trim($output);
}
diff --git a/program/lib/imap.inc b/program/lib/imap.inc
index 7f2315671..9402dfb4f 100644
--- a/program/lib/imap.inc
+++ b/program/lib/imap.inc
@@ -1,2642 +1,2657 @@
<?php
/////////////////////////////////////////////////////////
//
// Iloha IMAP Library (IIL)
//
// (C)Copyright 2002 Ryo Chijiiwa <Ryo@IlohaMail.org>
//
// This file is part of IlohaMail. IlohaMail is free software released
// under the GPL license. See enclosed file COPYING for details, or
// see http://www.fsf.org/copyleft/gpl.html
//
/////////////////////////////////////////////////////////
/********************************************************
FILE: include/imap.inc
PURPOSE:
Provide alternative IMAP library that doesn't rely on the standard
C-Client based version. This allows IlohaMail to function regardless
of whether or not the PHP build it's running on has IMAP functionality
built-in.
USEAGE:
Function containing "_C_" in name require connection handler to be
passed as one of the parameters. To obtain connection handler, use
iil_Connect()
VERSION:
IlohaMail-0.9-20050415
CHANGES:
File altered by Thomas Bruederli <roundcube@gmail.com>
to fit enhanced equirements by the RoundCube Webmail:
- Added list of server capabilites and check these before invoking commands
- Added junk flag to iilBasicHeader
- Enhanced error reporting on fsockopen()
- Additional parameter for SORT command
- Removed Call-time pass-by-reference because deprecated
- Parse charset from content-type in iil_C_FetchHeaders()
- Enhanced heaer sorting
- Pass message as reference in iil_C_Append (to save memory)
- Added BCC and REFERENCE to the list of headers to fetch in iil_C_FetchHeaders()
- Leave messageID unchanged in iil_C_FetchHeaders()
- Avoid stripslahes in iil_Connect()
- Escape quotes and backslashes in iil_C_Login()
- Added patch to iil_SortHeaders() by Richard Green
- Removed <br> from error messages (better for logging)
- Added patch to iil_C_Sort() enabling UID SORT commands
- Added function iil_C_ID2UID()
- Casting date parts in iil_StrToTime() to avoid mktime() warnings
- Also acceppt LIST responses in iil_C_ListSubscribed()
- Sanity check of $message_set in iil_C_FetchHeaders(), iil_C_FetchHeaderIndex(), iil_C_FetchThreadHeaders()
- Implemented UID FETCH in iil_C_FetchHeaders()
- Abort do-loop on socket errors (fgets returns false)
- $ICL_SSL is not boolean anymore but contains the connection schema (ssl or tls)
- Removed some debuggers (echo ...)
File altered by Aleksander Machniak <alec@alec.pl>
- - RFC3501 [7.1] don't call CAPABILITY if was returned in server
- optional resposne in iil_Connect()
- trim(chop()) replaced by trim()
- added iil_Escape() with support for " and \ in folder names
- support \ character in username in iil_C_Login()
- fixed iil_MultLine(): use iil_ReadBytes() instead of iil_ReadLine()
- fixed iil_C_FetchStructureString() to handle many literal strings in response
- removed hardcoded data size in iil_ReadLine()
- added iil_PutLine() wrapper for fputs()
- code cleanup and identation fixes
- removed flush() calls in iil_C_HandlePartBody() to prevent from memory leak (#1485187)
- don't return "??" from iil_C_GetQuota()
+ - RFC3501 [7.1] don't call CAPABILITY if was returned in server
+ optional resposne in iil_Connect(), added iil_C_GetCapability()
********************************************************/
/**
* @todo Possibly clean up more CS.
* @todo Try to replace most double-quotes with single-quotes.
* @todo Split this file into smaller files.
* @todo Refactor code.
* @todo Replace echo-debugging (make it adhere to config setting and log)
*/
// changed path to work within roundcube webmail
include_once 'lib/icl_commons.inc';
if (!isset($IMAP_USE_HEADER_DATE) || !$IMAP_USE_HEADER_DATE) {
$IMAP_USE_INTERNAL_DATE = true;
}
/**
* @todo Maybe use date() to generate this.
*/
$GLOBALS['IMAP_MONTHS'] = array("Jan" => 1, "Feb" => 2, "Mar" => 3, "Apr" => 4,
"May" => 5, "Jun" => 6, "Jul" => 7, "Aug" => 8, "Sep" => 9, "Oct" => 10,
"Nov" => 11, "Dec" => 12);
$GLOBALS['IMAP_SERVER_TZ'] = date('Z');
$GLOBALS['IMAP_FLAGS'] = array(
'SEEN' => '\\Seen',
'DELETED' => '\\Deleted',
'RECENT' => '\\Recent',
'ANSWERED' => '\\Answered',
'DRAFT' => '\\Draft',
'FLAGGED' => '\\Flagged',
'FORWARDED' => '$Forwarded',
'MDNSENT' => '$MDNSent');
$iil_error;
$iil_errornum;
$iil_selected;
/**
* @todo Change class vars to public/private
*/
class iilConnection
{
var $fp;
var $error;
var $errorNum;
var $selected;
var $message;
var $host;
var $cache;
var $uid_cache;
var $do_cache;
var $exists;
var $recent;
var $rootdir;
var $delimiter;
var $capability = array();
var $permanentflags = array();
+ var $capability_readed = false;
}
/**
* @todo Change class vars to public/private
*/
class iilBasicHeader
{
var $id;
var $uid;
var $subject;
var $from;
var $to;
var $cc;
var $replyto;
var $in_reply_to;
var $date;
var $messageID;
var $size;
var $encoding;
var $charset;
var $ctype;
var $flags;
var $timestamp;
var $f;
var $internaldate;
var $references;
var $priority;
var $mdn_to;
var $mdn_sent = false;
var $is_draft = false;
var $seen = false;
var $deleted = false;
var $recent = false;
var $answered = false;
var $forwarded = false;
var $junk = false;
var $flagged = false;
}
/**
* @todo Change class vars to public/private
*/
class iilThreadHeader
{
var $id;
var $sbj;
var $irt;
var $mid;
}
function iil_xor($string, $string2) {
$result = '';
$size = strlen($string);
for ($i=0; $i<$size; $i++) {
$result .= chr(ord($string[$i]) ^ ord($string2[$i]));
}
return $result;
}
function iil_PutLine($fp, $string, $endln=true) {
// console('C: '. $string);
return fputs($fp, $string . ($endln ? "\r\n" : ''));
}
function iil_ReadLine($fp, $size) {
$line = '';
if (!$fp) {
return $line;
}
if (!$size) {
$size = 1024;
}
do {
$buffer = fgets($fp, $size);
if ($buffer === false) {
break;
}
// console('S: '. chop($buffer));
$line .= $buffer;
} while ($buffer[strlen($buffer)-1] != "\n");
return $line;
}
function iil_MultLine($fp, $line) {
$line = chop($line);
if (ereg('\{[0-9]+\}$', $line)) {
$out = '';
preg_match_all('/(.*)\{([0-9]+)\}$/', $line, $a);
$bytes = $a[2][0];
while (strlen($out) < $bytes) {
$line = iil_ReadBytes($fp, $bytes);
$out .= $line;
}
$line = $a[1][0] . "\"$out\"";
// console('[...] '. $out);
}
return $line;
}
function iil_ReadBytes($fp, $bytes) {
$data = '';
$len = 0;
do {
$data .= fread($fp, $bytes-$len);
if ($len == strlen($data)) {
break; //nothing was read -> exit to avoid apache lockups
}
$len = strlen($data);
} while ($len < $bytes);
return $data;
}
function iil_ReadReply($fp) {
do {
$line = trim(iil_ReadLine($fp, 1024));
} while ($line[0] == '*');
return $line;
}
function iil_ParseResult($string) {
$a=explode(' ', $string);
if (count($a) > 2) {
if (strcasecmp($a[1], 'OK') == 0) {
return 0;
} else if (strcasecmp($a[1], 'NO') == 0) {
return -1;
} else if (strcasecmp($a[1], 'BAD') == 0) {
return -2;
}
}
return -3;
}
// check if $string starts with $match
function iil_StartsWith($string, $match) {
$len = strlen($match);
if ($len == 0) {
return false;
}
if (strncmp($string, $match, $len) == 0) {
return true;
}
return false;
}
function iil_StartsWithI($string, $match) {
$len = strlen($match);
if ($len == 0) {
return false;
}
if (strncasecmp($string, $match, $len) == 0) {
return true;
}
return false;
}
function iil_Escape($string)
{
return strtr($string, array('"'=>'\\"', '\\' => '\\\\'));
}
+function iil_C_GetCapability(&$conn, $name)
+{
+ if (in_array($name, $conn->capability)) {
+ return $conn->capability[$name];
+ }
+ else if ($conn->capability_readed) {
+ return false;
+ }
+
+ // get capabilities (only once) because initial
+ // optional CAPABILITY response may differ
+ $conn->capability = array();
+
+ iil_PutLine($conn->fp, "cp01 CAPABILITY");
+ do {
+ $line = trim(iil_ReadLine($conn->fp, 1024));
+ $a = explode(' ', $line);
+ if ($line[0] == '*') {
+ while (list($k, $w) = each($a)) {
+ if ($w != '*' && $w != 'CAPABILITY')
+ $conn->capability[] = strtoupper($w);
+ }
+ }
+ } while ($a[0] != 'cp01');
+
+ $conn->capability_readed = true;
+
+ if (in_array($name, $conn->capability)) {
+ return $conn->capability[$name];
+ }
+
+ return false;
+}
+
function iil_C_Authenticate(&$conn, $user, $pass, $encChallenge) {
$ipad = '';
$opad = '';
// initialize ipad, opad
for ($i=0;$i<64;$i++) {
$ipad .= chr(0x36);
$opad .= chr(0x5C);
}
// pad $pass so it's 64 bytes
$padLen = 64 - strlen($pass);
for ($i=0;$i<$padLen;$i++) {
$pass .= chr(0);
}
// generate hash
$hash = md5(iil_xor($pass,$opad) . pack("H*", md5(iil_xor($pass, $ipad) . base64_decode($encChallenge))));
// generate reply
$reply = base64_encode($user . ' ' . $hash);
// send result, get reply
iil_PutLine($conn->fp, $reply);
$line = iil_ReadLine($conn->fp, 1024);
// process result
if (iil_ParseResult($line) == 0) {
$conn->error .= '';
$conn->errorNum = 0;
return $conn->fp;
}
$conn->error .= 'Authentication for ' . $user . ' failed (AUTH): "';
$conn->error .= htmlspecialchars($line) . '"';
$conn->errorNum = -2;
return false;
}
function iil_C_Login(&$conn, $user, $password) {
iil_PutLine($conn->fp, 'a001 LOGIN "'.iil_Escape($user).'" "'.iil_Escape($password).'"');
do {
$line = iil_ReadReply($conn->fp);
if ($line === false) {
break;
}
} while (!iil_StartsWith($line, "a001 "));
$a = explode(' ', $line);
if (strcmp($a[1], 'OK') == 0) {
$result = $conn->fp;
$conn->error .= '';
$conn->errorNum = 0;
return $result;
}
$result = false;
fclose($conn->fp);
$conn->error .= 'Authentication for ' . $user . ' failed (LOGIN): "';
$conn->error .= htmlspecialchars($line)."\"";
$conn->errorNum = -2;
return $result;
}
function iil_ParseNamespace2($str, &$i, $len=0, $l) {
if (!$l) {
$str = str_replace('NIL', '()', $str);
}
if (!$len) {
$len = strlen($str);
}
$data = array();
$in_quotes = false;
$elem = 0;
for ($i;$i<$len;$i++) {
$c = (string)$str[$i];
if ($c == '(' && !$in_quotes) {
$i++;
$data[$elem] = iil_ParseNamespace2($str, $i, $len, $l++);
$elem++;
} else if ($c == ')' && !$in_quotes) {
return $data;
} else if ($c == '\\') {
$i++;
if ($in_quotes) {
$data[$elem] .= $c.$str[$i];
}
} else if ($c == '"') {
$in_quotes = !$in_quotes;
if (!$in_quotes) {
$elem++;
}
} else if ($in_quotes) {
$data[$elem].=$c;
}
}
return $data;
}
function iil_C_NameSpace(&$conn) {
global $my_prefs;
- if (!in_array('NAMESPACE', $conn->capability)) {
+ if (!iil_C_GetCapability($conn, 'NAMESPACE')) {
return false;
}
if ($my_prefs["rootdir"]) {
return true;
}
iil_PutLine($conn->fp, "ns1 NAMESPACE");
do {
$line = iil_ReadLine($conn->fp, 1024);
if (iil_StartsWith($line, '* NAMESPACE')) {
$i = 0;
$data = iil_ParseNamespace2(substr($line,11), $i, 0, 0);
}
} while (!iil_StartsWith($line, "ns1"));
if (!is_array($data)) {
return false;
}
$user_space_data = $data[0];
if (!is_array($user_space_data)) {
return false;
}
$first_userspace = $user_space_data[0];
if (count($first_userspace)!=2) {
return false;
}
$conn->rootdir = $first_userspace[0];
$conn->delimiter = $first_userspace[1];
$my_prefs["rootdir"] = substr($conn->rootdir, 0, -1);
return true;
}
function iil_Connect($host, $user, $password) {
global $iil_error, $iil_errornum;
global $ICL_SSL, $ICL_PORT;
global $IMAP_NO_CACHE;
global $my_prefs, $IMAP_USE_INTERNAL_DATE;
$iil_error = '';
$iil_errornum = 0;
//strip slashes
// $user = stripslashes($user);
// $password = stripslashes($password);
//set auth method
$auth_method = 'plain';
if (func_num_args() >= 4) {
$auth_array = func_get_arg(3);
if (is_array($auth_array)) {
$auth_method = $auth_array['imap'];
}
if (empty($auth_method)) {
$auth_method = "plain";
}
}
$message = "INITIAL: $auth_method\n";
$result = false;
//initialize connection
$conn = new iilConnection;
$conn->error = '';
$conn->errorNum = 0;
$conn->selected = '';
$conn->user = $user;
$conn->host = $host;
$conn->cache = array();
$conn->do_cache = (function_exists("cache_write")&&!$IMAP_NO_CACHE);
$conn->cache_dirty = array();
if ($my_prefs['sort_field'] == 'INTERNALDATE') {
$IMAP_USE_INTERNAL_DATE = true;
} else if ($my_prefs['sort_field'] == 'DATE') {
$IMAP_USE_INTERNAL_DATE = false;
}
//echo '<!-- conn sort_field: '.$my_prefs['sort_field'].' //-->';
//check input
if (empty($host)) {
$iil_error .= "Invalid host\n";
}
if (empty($user)) {
$iil_error .= "Invalid user\n";
}
if (empty($password)) {
$iil_error .= "Invalid password\n";
}
if (!empty($iil_error)) {
return false;
}
if (!$ICL_PORT) {
$ICL_PORT = 143;
}
//check for SSL
if ($ICL_SSL) {
$host = $ICL_SSL . '://' . $host;
}
//open socket connection
$conn->fp = fsockopen($host, $ICL_PORT, $errno, $errstr, 10);
if (!$conn->fp) {
$iil_error = "Could not connect to $host at port $ICL_PORT: $errstr";
$iil_errornum = -1;
return false;
}
$iil_error .= "Socket connection established\r\n";
$line = iil_ReadLine($conn->fp, 1024);
// RFC3501 [7.1] optional CAPABILITY response
- // commented out, because it's not working always as should
-// if (preg_match('/\[CAPABILITY ([^]]+)\]/i', $line, $matches)) {
-// $conn->capability = explode(' ', $matches[1]);
-// } else {
- iil_PutLine($conn->fp, "cp01 CAPABILITY");
- do {
- $line = trim(iil_ReadLine($conn->fp, 1024));
-
- $conn->message .= "$line\n";
-
- $a = explode(' ', $line);
- if ($line[0] == '*') {
- while (list($k, $w) = each($a)) {
- if ($w != '*' && $w != 'CAPABILITY')
- $conn->capability[] = $w;
- }
- }
- } while ($a[0] != 'cp01');
-// }
+ if (preg_match('/\[CAPABILITY ([^]]+)\]/i', $line, $matches)) {
+ $conn->capability = explode(' ', strtoupper($matches[1]));
+ }
if (strcasecmp($auth_method, "check") == 0) {
//check for supported auth methods
-
- //default to plain text auth
- $auth_method = 'plain';
-
- //check for CRAM-MD5
- foreach ($conn->capability as $c)
- if (strcasecmp($c, 'AUTH=CRAM_MD5') == 0 ||
- strcasecmp($c, 'AUTH=CRAM-MD5') == 0) {
- $auth_method = 'auth';
- break;
- }
+ if (iil_C_GetCapability($conn, 'AUTH=CRAM-MD5') || iil_C_GetCapability($conn, 'AUTH=CRAM_MD5')) {
+ $auth_method = 'auth';
+ }
+ else {
+ //default to plain text auth
+ $auth_method = 'plain';
+ }
}
if (strcasecmp($auth_method, 'auth') == 0) {
$conn->message .= "Trying CRAM-MD5\n";
//do CRAM-MD5 authentication
iil_PutLine($conn->fp, "a000 AUTHENTICATE CRAM-MD5");
$line = trim(iil_ReadLine($conn->fp, 1024));
$conn->message .= "$line\n";
if ($line[0] == '+') {
$conn->message .= 'Got challenge: ' . htmlspecialchars($line) . "\n";
//got a challenge string, try CRAM-5
$result = iil_C_Authenticate($conn, $user, $password, substr($line,2));
$conn->message .= "Tried CRAM-MD5: $result \n";
} else {
$conn->message .='No challenge ('.htmlspecialchars($line)."), try plain\n";
$auth = 'plain';
}
}
if ((!$result)||(strcasecmp($auth, "plain") == 0)) {
//do plain text auth
$result = iil_C_Login($conn, $user, $password);
$conn->message.="Tried PLAIN: $result \n";
}
$conn->message .= $auth;
if ($result) {
iil_C_Namespace($conn);
return $conn;
} else {
$iil_error = $conn->error;
$iil_errornum = $conn->errorNum;
return false;
}
}
function iil_Close(&$conn) {
iil_C_WriteCache($conn);
if (iil_PutLine($conn->fp, "I LOGOUT")) {
fgets($conn->fp, 1024);
fclose($conn->fp);
$conn->fp = false;
}
}
function iil_ClearCache($user, $host) {
}
function iil_C_WriteCache(&$conn) {
//echo "<!-- doing iil_C_WriteCache //-->\n";
if (!$conn->do_cache) return false;
if (is_array($conn->cache)) {
while (list($folder,$data)=each($conn->cache)) {
if ($folder && is_array($data) && $conn->cache_dirty[$folder]) {
$key = $folder.".imap";
$result = cache_write($conn->user, $conn->host, $key, $data, true);
//echo "<!-- writing $key $data: $result //-->\n";
}
}
}
}
function iil_C_EnableCache(&$conn) {
$conn->do_cache = true;
}
function iil_C_DisableCache(&$conn) {
$conn->do_cache = false;
}
function iil_C_LoadCache(&$conn, $folder) {
if (!$conn->do_cache) {
return false;
}
$key = $folder.'.imap';
if (!is_array($conn->cache[$folder])) {
$conn->cache[$folder] = cache_read($conn->user, $conn->host, $key);
$conn->cache_dirty[$folder] = false;
}
}
function iil_C_ExpireCachedItems(&$conn, $folder, $message_set) {
if (!$conn->do_cache) {
return; //caching disabled
}
if (!is_array($conn->cache[$folder])) {
return; //cache not initialized|empty
}
if (count($conn->cache[$folder]) == 0) {
return; //cache not initialized|empty
}
$uids = iil_C_FetchHeaderIndex($conn, $folder, $message_set, 'UID');
$num_removed = 0;
if (is_array($uids)) {
//echo "<!-- unsetting: ".implode(",",$uids)." //-->\n";
while (list($n,$uid)=each($uids)) {
unset($conn->cache[$folder][$uid]);
//$conn->cache[$folder][$uid] = false;
//$num_removed++;
}
$conn->cache_dirty[$folder] = true;
//echo '<!--'."\n";
//print_r($conn->cache);
//echo "\n".'//-->'."\n";
} else {
echo "<!-- failed to get uids: $message_set //-->\n";
}
/*
if ($num_removed>0) {
$new_cache;
reset($conn->cache[$folder]);
while (list($uid,$item)=each($conn->cache[$folder])) {
if ($item) $new_cache[$uid] = $conn->cache[$folder][$uid];
}
$conn->cache[$folder] = $new_cache;
}
*/
}
function iil_ExplodeQuotedString($delimiter, $string) {
$quotes=explode('"', $string);
while ( list($key, $val) = each($quotes)) {
if (($key % 2) == 1) {
$quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]);
}
}
$string=implode('"', $quotes);
$result=explode($delimiter, $string);
while ( list($key, $val) = each($result) ) {
$result[$key] = str_replace('_!@!_', $delimiter, $result[$key]);
}
return $result;
}
function iil_CheckForRecent($host, $user, $password, $mailbox) {
if (empty($mailbox)) {
$mailbox = 'INBOX';
}
$conn = iil_Connect($host, $user, $password, 'plain');
$fp = $conn->fp;
if ($fp) {
iil_PutLine($fp, "a002 EXAMINE \"".iil_Escape($mailbox)."\"");
do {
$line=chop(iil_ReadLine($fp, 300));
$a=explode(' ', $line);
if (($a[0] == '*') && (strcasecmp($a[2], 'RECENT') == 0)) {
$result = (int) $a[1];
}
} while (!iil_StartsWith($a[0], 'a002'));
iil_PutLine($fp, "a003 LOGOUT");
fclose($fp);
} else {
$result = -2;
}
return $result;
}
function iil_C_Select(&$conn, $mailbox) {
if (empty($mailbox)) {
return false;
}
if (strcmp($conn->selected, $mailbox) == 0) {
return true;
}
iil_C_LoadCache($conn, $mailbox);
if (iil_PutLine($conn->fp, "sel1 SELECT \"".iil_Escape($mailbox).'"')) {
do {
$line = chop(iil_ReadLine($conn->fp, 300));
$a = explode(' ', $line);
if (count($a) == 3) {
if (strcasecmp($a[2], 'EXISTS') == 0) {
$conn->exists = (int) $a[1];
}
if (strcasecmp($a[2], 'RECENT') == 0) {
$conn->recent = (int) $a[1];
}
}
else if (preg_match('/\[?PERMANENTFLAGS\s+\(([^\)]+)\)\]/U', $line, $match)) {
$conn->permanentflags = explode(' ', $match[1]);
}
} while (!iil_StartsWith($line, 'sel1'));
$a = explode(' ', $line);
if (strcasecmp($a[1], 'OK') == 0) {
$conn->selected = $mailbox;
return true;
}
}
return false;
}
function iil_C_CheckForRecent(&$conn, $mailbox) {
if (empty($mailbox)) {
$mailbox = 'INBOX';
}
iil_C_Select($conn, $mailbox);
if ($conn->selected == $mailbox) {
return $conn->recent;
}
return false;
}
function iil_C_CountMessages(&$conn, $mailbox, $refresh = false) {
if ($refresh) {
$conn->selected= '';
}
iil_C_Select($conn, $mailbox);
if ($conn->selected == $mailbox) {
return $conn->exists;
}
return false;
}
function iil_SplitHeaderLine($string) {
$pos=strpos($string, ':');
if ($pos>0) {
$res[0] = substr($string, 0, $pos);
$res[1] = trim(substr($string, $pos+1));
return $res;
}
return $string;
}
function iil_StrToTime($str) {
$IMAP_MONTHS = $GLOBALS['IMAP_MONTHS'];
$IMAP_SERVER_TZ = $GLOBALS['IMAP_SERVER_TR'];
if ($str) {
$time1 = strtotime($str);
}
if ($time1 && $time1 != -1) {
return $time1-$IMAP_SERVER_TZ;
}
//echo '<!--'.$str.'//-->';
//replace double spaces with single space
$str = trim($str);
$str = str_replace(' ', ' ', $str);
//strip off day of week
$pos = strpos($str, ' ');
if (!is_numeric(substr($str, 0, $pos))) {
$str = substr($str, $pos+1);
}
//explode, take good parts
$a = explode(' ', $str);
$month_str = $a[1];
$month = $IMAP_MONTHS[$month_str];
$day = (int)$a[0];
$year = (int)$a[2];
$time = $a[3];
$tz_str = $a[4];
$tz = substr($tz_str, 0, 3);
$ta = explode(':', $time);
$hour = (int)$ta[0]-(int)$tz;
$minute = (int)$ta[1];
$second = (int)$ta[2];
//make UNIX timestamp
$time2 = mktime($hour, $minute, $second, $month, $day, $year);
//echo '<!--'.$time1.' '.$time2.' //-->'."\n";
return $time2;
}
function iil_C_Sort(&$conn, $mailbox, $field, $add='', $is_uid=FALSE,
$encoding = 'US-ASCII') {
/* Do "SELECT" command */
if (!iil_C_Select($conn, $mailbox)) {
return false;
}
$field = strtoupper($field);
if ($field == 'INTERNALDATE') {
$field = 'ARRIVAL';
}
$fields = array('ARRIVAL' => 1,'CC' => 1,'DATE' => 1,
'FROM' => 1, 'SIZE' => 1, 'SUBJECT' => 1, 'TO' => 1);
if (!$fields[$field]) {
return false;
}
$is_uid = $is_uid ? 'UID ' : '';
if (!empty($add)) {
$add = " $add";
}
$fp = $conn->fp;
$command = 's ' . $is_uid . 'SORT (' . $field . ') ';
$command .= $encoding . ' ALL' . $add;
$line = $data = '';
if (!iil_PutLine($fp, $command)) {
return false;
}
do {
$line = chop(iil_ReadLine($fp, 1024));
if (iil_StartsWith($line, '* SORT')) {
$data .= ($data?' ':'') . substr($line, 7);
}
} while ($line[0]!='s');
if (empty($data)) {
$conn->error = $line;
return false;
}
$out = explode(' ',$data);
return $out;
}
function iil_C_FetchHeaderIndex(&$conn, $mailbox, $message_set, $index_field,
$normalize=true) {
global $IMAP_USE_INTERNAL_DATE;
$c=0;
$result=array();
$fp = $conn->fp;
if (empty($index_field)) {
$index_field = 'DATE';
}
$index_field = strtoupper($index_field);
list($from_idx, $to_idx) = explode(':', $message_set);
if (empty($message_set) || (isset($to_idx)
&& (int)$from_idx > (int)$to_idx)) {
return false;
}
//$fields_a['DATE'] = ($IMAP_USE_INTERNAL_DATE?6:1);
$fields_a['DATE'] = 1;
$fields_a['INTERNALDATE'] = 6;
$fields_a['FROM'] = 1;
$fields_a['REPLY-TO'] = 1;
$fields_a['SENDER'] = 1;
$fields_a['TO'] = 1;
$fields_a['SUBJECT'] = 1;
$fields_a['UID'] = 2;
$fields_a['SIZE'] = 2;
$fields_a['SEEN'] = 3;
$fields_a['RECENT'] = 4;
$fields_a['DELETED'] = 5;
$mode=$fields_a[$index_field];
if (!($mode > 0)) {
return false;
}
/* Do "SELECT" command */
if (!iil_C_Select($conn, $mailbox)) {
return false;
}
/* FETCH date,from,subject headers */
if ($mode == 1) {
$key = 'fhi' . ($c++);
$request = $key . " FETCH $message_set (BODY.PEEK[HEADER.FIELDS ($index_field)])";
if (!iil_PutLine($fp, $request)) {
return false;
}
do {
$line=chop(iil_ReadLine($fp, 200));
$a=explode(' ', $line);
if (($line[0] == '*') && ($a[2] == 'FETCH')
&& ($line[strlen($line)-1] != ')')) {
$id=$a[1];
$str=$line=chop(iil_ReadLine($fp, 300));
while ($line[0] != ')') { //caution, this line works only in this particular case
$line=chop(iil_ReadLine($fp, 300));
if ($line[0] != ')') {
if (ord($line[0]) <= 32) { //continuation from previous header line
$str.= ' ' . trim($line);
}
if ((ord($line[0]) > 32) || (strlen($line[0]) == 0)) {
list($field, $string) = iil_SplitHeaderLine($str);
if (strcasecmp($field, 'date') == 0) {
$result[$id] = iil_StrToTime($string);
} else {
$result[$id] = str_replace('"', '', $string);
if ($normalize) {
$result[$id] = strtoupper($result[$id]);
}
}
$str=$line;
}
}
}
}
/*
$end_pos = strlen($line)-1;
if (($line[0]=="*") && ($a[2]=="FETCH") && ($line[$end_pos]=="}")) {
$id = $a[1];
$pos = strrpos($line, "{")+1;
$bytes = (int)substr($line, $pos, $end_pos-$pos);
$received = 0;
do {
$line = iil_ReadLine($fp, 0);
$received += strlen($line);
$line = chop($line);
if ($received>$bytes) {
break;
} else if (!$line) {
continue;
}
list($field, $string) = explode(': ', $line);
if (strcasecmp($field, 'date') == 0) {
$result[$id] = iil_StrToTime($string);
} else if ($index_field != 'DATE') {
$result[$id]=strtoupper(str_replace('"', '', $string));
}
} while ($line[0] != ')');
} else {
//one line response, not expected so ignore
}
*/
} while (!iil_StartsWith($line, $key));
}else if ($mode == 6) {
$key = 'fhi' . ($c++);
$request = $key . " FETCH $message_set (INTERNALDATE)";
if (!iil_PutLine($fp, $request)) {
return false;
}
do {
$line=chop(iil_ReadLine($fp, 200));
if ($line[0] == '*') {
/*
* original:
* "* 10 FETCH (INTERNALDATE "31-Jul-2002 09:18:02 -0500")"
*/
$paren_pos = strpos($line, '(');
$foo = substr($line, 0, $paren_pos);
$a = explode(' ', $foo);
$id = $a[1];
$open_pos = strpos($line, '"') + 1;
$close_pos = strrpos($line, '"');
if ($open_pos && $close_pos) {
$len = $close_pos - $open_pos;
$time_str = substr($line, $open_pos, $len);
$result[$id] = strtotime($time_str);
}
} else {
$a = explode(' ', $line);
}
} while (!iil_StartsWith($a[0], $key));
} else {
if ($mode >= 3) {
$field_name = 'FLAGS';
} else if ($index_field == 'SIZE') {
$field_name = 'RFC822.SIZE';
} else {
$field_name = $index_field;
}
/* FETCH uid, size, flags */
$key = 'fhi' .($c++);
$request = $key . " FETCH $message_set ($field_name)";
if (!iil_PutLine($fp, $request)) {
return false;
}
do {
$line=chop(iil_ReadLine($fp, 200));
$a = explode(' ', $line);
if (($line[0] == '*') && ($a[2] == 'FETCH')) {
$line = str_replace('(', '', $line);
$line = str_replace(')', '', $line);
$a = explode(' ', $line);
$id = $a[1];
if (isset($result[$id])) {
continue; //if we already got the data, skip forward
}
if ($a[3]!=$field_name) {
continue; //make sure it's returning what we requested
}
/* Caution, bad assumptions, next several lines */
if ($mode == 2) {
$result[$id] = $a[4];
} else {
$haystack = strtoupper($line);
$result[$id] = (strpos($haystack, $index_field) > 0 ? "F" : "N");
}
}
} while (!iil_StartsWith($line, $key));
}
//check number of elements...
list($start_mid, $end_mid) = explode(':', $message_set);
if (is_numeric($start_mid) && is_numeric($end_mid)) {
//count how many we should have
$should_have = $end_mid - $start_mid +1;
//if we have less, try and fill in the "gaps"
if (count($result) < $should_have) {
for ($i=$start_mid; $i<=$end_mid; $i++) {
if (!isset($result[$i])) {
$result[$i] = '';
}
}
}
}
return $result;
}
function iil_CompressMessageSet($message_set) {
//given a comma delimited list of independent mid's,
//compresses by grouping sequences together
//if less than 255 bytes long, let's not bother
if (strlen($message_set)<255) {
return $message_set;
}
//see if it's already been compress
if (strpos($message_set, ':') !== false) {
return $message_set;
}
//separate, then sort
$ids = explode(',', $message_set);
sort($ids);
$result = array();
$start = $prev = $ids[0];
foreach ($ids as $id) {
$incr = $id - $prev;
if ($incr > 1) { //found a gap
if ($start == $prev) {
$result[] = $prev; //push single id
} else {
$result[] = $start . ':' . $prev; //push sequence as start_id:end_id
}
$start = $id; //start of new sequence
}
$prev = $id;
}
//handle the last sequence/id
if ($start==$prev) {
$result[] = $prev;
} else {
$result[] = $start.':'.$prev;
}
//return as comma separated string
return implode(',', $result);
}
function iil_C_UIDsToMIDs(&$conn, $mailbox, $uids) {
if (!is_array($uids) || count($uids) == 0) {
return array();
}
return iil_C_Search($conn, $mailbox, 'UID ' . implode(',', $uids));
}
function iil_C_UIDToMID(&$conn, $mailbox, $uid) {
$result = iil_C_UIDsToMIDs($conn, $mailbox, array($uid));
if (count($result) == 1) {
return $result[0];
}
return false;
}
function iil_C_FetchUIDs(&$conn,$mailbox) {
global $clock;
$num = iil_C_CountMessages($conn, $mailbox);
if ($num == 0) {
return array();
}
$message_set = '1' . ($num>1?':' . $num:'');
//if cache not enabled, just call iil_C_FetchHeaderIndex on 'UID' field
if (!$conn->do_cache)
return iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID');
//otherwise, let's check cache first
$key = $mailbox.'.uids';
$cache_good = true;
if ($conn->uid_cache) {
$data = $conn->uid_cache;
} else {
$data = cache_read($conn->user, $conn->host, $key);
}
//was anything cached at all?
if ($data === false) {
$cache_good = -1;
}
//make sure number of messages were the same
if ($cache_good > 0 && $data['n'] != $num) {
$cache_good = -2;
}
//if everything's okay so far...
if ($cache_good > 0) {
//check UIDs of highest mid with current and cached
$temp = iil_C_Search($conn, $mailbox, 'UID ' . $data['d'][$num]);
if (!$temp || !is_array($temp) || $temp[0] != $num) {
$cache_good = -3;
}
}
//if cached data's good, return it
if ($cache_good > 0) {
return $data['d'];
}
//otherwise, we need to fetch it
$data = array('n' => $num, 'd' => array());
$data['d'] = iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID');
cache_write($conn->user, $conn->host, $key, $data);
$conn->uid_cache = $data;
return $data['d'];
}
function iil_SortThreadHeaders($headers, $index_a, $uids) {
asort($index_a);
$result = array();
foreach ($index_a as $mid=>$foobar) {
$uid = $uids[$mid];
$result[$uid] = $headers[$uid];
}
return $result;
}
function iil_C_FetchThreadHeaders(&$conn, $mailbox, $message_set) {
global $clock;
global $index_a;
list($from_idx, $to_idx) = explode(':', $message_set);
if (empty($message_set) || (isset($to_idx)
&& (int)$from_idx > (int)$to_idx)) {
return false;
}
$result = array();
$uids = iil_C_FetchUIDs($conn, $mailbox);
$debug = false;
/* Get cached records where possible */
if ($conn->do_cache) {
$cached = cache_read($conn->user, $conn->host, $mailbox.'.thhd');
if ($cached && is_array($uids) && count($uids)>0) {
$needed_set = '';
foreach ($uids as $id=>$uid) {
if ($cached[$uid]) {
$result[$uid] = $cached[$uid];
$result[$uid]->id = $id;
} else {
$needed_set .= ($needed_set ? ',' : '') . $id;
}
}
if ($needed_set) {
$message_set = $needed_set;
} else {
$message_set = '';
}
}
}
$message_set = iil_CompressMessageSet($message_set);
if ($debug) {
echo "Still need: ".$message_set;
}
/* if we're missing any, get them */
if ($message_set) {
/* FETCH date,from,subject headers */
$key = 'fh';
$fp = $conn->fp;
$request = $key . " FETCH $message_set ";
$request .= "(BODY.PEEK[HEADER.FIELDS (SUBJECT MESSAGE-ID IN-REPLY-TO)])";
$mid_to_id = array();
if (!iil_PutLine($fp, $request)) {
return false;
}
do {
$line = chop(iil_ReadLine($fp, 1024));
if ($debug) {
echo $line . "\n";
}
if (ereg('\{[0-9]+\}$', $line)) {
$a = explode(' ', $line);
$new = array();
$new_thhd = new iilThreadHeader;
$new_thhd->id = $a[1];
do {
$line = chop(iil_ReadLine($fp, 1024), "\r\n");
if (iil_StartsWithI($line, 'Message-ID:')
|| (iil_StartsWithI($line,'In-Reply-To:'))
|| (iil_StartsWithI($line,'SUBJECT:'))) {
$pos = strpos($line, ':');
$field_name = substr($line, 0, $pos);
$field_val = substr($line, $pos+1);
$new[strtoupper($field_name)] = trim($field_val);
} else if (ereg('^[[:space:]]', $line)) {
$new[strtoupper($field_name)] .= trim($line);
}
} while ($line[0] != ')');
$new_thhd->sbj = $new['SUBJECT'];
$new_thhd->mid = substr($new['MESSAGE-ID'], 1, -1);
$new_thhd->irt = substr($new['IN-REPLY-TO'], 1, -1);
$result[$uids[$new_thhd->id]] = $new_thhd;
}
} while (!iil_StartsWith($line, 'fh'));
}
/* sort headers */
if (is_array($index_a)) {
$result = iil_SortThreadHeaders($result, $index_a, $uids);
}
/* write new set to cache */
if ($conn->do_cache) {
if (count($result)!=count($cached)) {
cache_write($conn->user, $conn->host, $mailbox . '.thhd', $result);
}
}
//echo 'iil_FetchThreadHeaders:'."\n";
//print_r($result);
return $result;
}
function iil_C_BuildThreads2(&$conn, $mailbox, $message_set, &$clock) {
global $index_a;
list($from_idx, $to_idx) = explode(':', $message_set);
if (empty($message_set) || (isset($to_idx)
&& (int)$from_idx > (int)$to_idx)) {
return false;
}
$result = array();
$roots = array();
$root_mids = array();
$sub_mids = array();
$strays = array();
$messages = array();
$fp = $conn->fp;
$debug = false;
$sbj_filter_pat = '[a-zA-Z]{2,3}(\[[0-9]*\])?:([[:space:]]*)';
/* Do "SELECT" command */
if (!iil_C_Select($conn, $mailbox)) {
return false;
}
/* FETCH date,from,subject headers */
$mid_to_id = array();
$messages = array();
$headers = iil_C_FetchThreadHeaders($conn, $mailbox, $message_set);
if ($clock) {
$clock->register('fetched headers');
}
if ($debug) {
print_r($headers);
}
/* go through header records */
foreach ($headers as $header) {
//$id = $header['i'];
//$new = array('id'=>$id, 'MESSAGE-ID'=>$header['m'],
// 'IN-REPLY-TO'=>$header['r'], 'SUBJECT'=>$header['s']);
$id = $header->id;
$new = array('id' => $id, 'MESSAGE-ID' => $header->mid,
'IN-REPLY-TO' => $header->irt, 'SUBJECT' => $header->sbj);
/* add to message-id -> mid lookup table */
$mid_to_id[$new['MESSAGE-ID']] = $id;
/* if no subject, use message-id */
if (empty($new['SUBJECT'])) {
$new['SUBJECT'] = $new['MESSAGE-ID'];
}
/* if subject contains 'RE:' or has in-reply-to header, it's a reply */
$sbj_pre ='';
$has_re = false;
if (eregi($sbj_filter_pat, $new['SUBJECT'])) {
$has_re = true;
}
if ($has_re||$new['IN-REPLY-TO']) {
$sbj_pre = 'RE:';
}
/* strip out 're:', 'fw:' etc */
if ($has_re) {
$sbj = ereg_replace($sbj_filter_pat, '', $new['SUBJECT']);
} else {
$sbj = $new['SUBJECT'];
}
$new['SUBJECT'] = $sbj_pre.$sbj;
/* if subject not a known thread-root, add to list */
if ($debug) {
echo $id . ' ' . $new['SUBJECT'] . "\t" . $new['MESSAGE-ID'] . "\n";
}
$root_id = $roots[$sbj];
if ($root_id && ($has_re || !$root_in_root[$root_id])) {
if ($debug) {
echo "\tfound root: $root_id\n";
}
$sub_mids[$new['MESSAGE-ID']] = $root_id;
$result[$root_id][] = $id;
} else if (!isset($roots[$sbj]) || (!$has_re && $root_in_root[$root_id])) {
/* try to use In-Reply-To header to find root
unless subject contains 'Re:' */
if ($has_re&&$new['IN-REPLY-TO']) {
if ($debug) {
echo "\tlooking: ".$new['IN-REPLY-TO']."\n";
}
//reply to known message?
$temp = $sub_mids[$new['IN-REPLY-TO']];
if ($temp) {
//found it, root:=parent's root
if ($debug) {
echo "\tfound parent: ".$new['SUBJECT']."\n";
}
$result[$temp][] = $id;
$sub_mids[$new['MESSAGE-ID']] = $temp;
$sbj = '';
} else {
//if we can't find referenced parent, it's a "stray"
$strays[$id] = $new['IN-REPLY-TO'];
}
}
//add subject as root
if ($sbj) {
if ($debug) {
echo "\t added to root\n";
}
$roots[$sbj] = $id;
$root_in_root[$id] = !$has_re;
$sub_mids[$new['MESSAGE-ID']] = $id;
$result[$id] = array($id);
}
if ($debug) {
echo $new['MESSAGE-ID'] . "\t" . $sbj . "\n";
}
}
}
//now that we've gone through all the messages,
//go back and try and link up the stray threads
if (count($strays) > 0) {
foreach ($strays as $id=>$irt) {
$root_id = $sub_mids[$irt];
if (!$root_id || $root_id==$id) {
continue;
}
$result[$root_id] = array_merge($result[$root_id],$result[$id]);
unset($result[$id]);
}
}
if ($clock) {
$clock->register('data prepped');
}
if ($debug) {
print_r($roots);
}
return $result;
}
function iil_SortThreads(&$tree, $index, $sort_order = 'ASC') {
if (!is_array($tree) || !is_array($index)) {
return false;
}
//create an id to position lookup table
$i = 0;
foreach ($index as $id=>$val) {
$i++;
$index[$id] = $i;
}
$max = $i+1;
//for each tree, set array key to position
$itree = array();
foreach ($tree as $id=>$node) {
if (count($tree[$id])<=1) {
//for "threads" with only one message, key is position of that message
$n = $index[$id];
$itree[$n] = array($n=>$id);
} else {
//for "threads" with multiple messages,
$min = $max;
$new_a = array();
foreach ($tree[$id] as $mid) {
$new_a[$index[$mid]] = $mid; //create new sub-array mapping position to id
$pos = $index[$mid];
if ($pos&&$pos<$min) {
$min = $index[$mid]; //find smallest position
}
}
$n = $min; //smallest position of child is thread position
//assign smallest position to root level key
//set children array to one created above
ksort($new_a);
$itree[$n] = $new_a;
}
}
//sort by key, this basically sorts all threads
ksort($itree);
$i = 0;
$out = array();
foreach ($itree as $k=>$node) {
$out[$i] = $itree[$k];
$i++;
}
return $out;
}
function iil_IndexThreads(&$tree) {
/* creates array mapping mid to thread id */
if (!is_array($tree)) {
return false;
}
$t_index = array();
foreach ($tree as $pos=>$kids) {
foreach ($kids as $kid) $t_index[$kid] = $pos;
}
return $t_index;
}
function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false)
{
global $IMAP_USE_INTERNAL_DATE;
$c = 0;
$result = array();
$fp = $conn->fp;
list($from_idx, $to_idx) = explode(':', $message_set);
if (empty($message_set) || (isset($to_idx)
&& (int)$from_idx > (int)$to_idx)) {
return false;
}
/* Do "SELECT" command */
if (!iil_C_Select($conn, $mailbox)) {
$conn->error = "Couldn't select $mailbox";
return false;
}
/* Get cached records where possible */
if ($conn->do_cache) {
$uids = iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, "UID");
if (is_array($uids) && count($conn->cache[$mailbox]>0)) {
$needed_set = '';
while (list($id,$uid)=each($uids)) {
if ($conn->cache[$mailbox][$uid]) {
$result[$id] = $conn->cache[$mailbox][$uid];
$result[$id]->id = $id;
} else {
$needed_set.=($needed_set ? ',': '') . $id;
}
}
//echo "<!-- iil_C_FetchHeader\nMessage Set: $message_set\nNeeded Set:$needed_set\n//-->\n";
if ($needed_set) {
$message_set = iil_CompressMessageSet($needed_set);
} else {
return $result;
}
}
}
/* FETCH date,from,subject headers */
$key = 'fh' . ($c++);
$prefix = $uidfetch?' UID':'';
$request = $key . $prefix;
$request .= " FETCH $message_set (BODY.PEEK[HEADER.FIELDS ";
$request .= "(DATE FROM TO SUBJECT REPLY-TO IN-REPLY-TO CC BCC ";
$request .= "CONTENT-TRANSFER-ENCODING CONTENT-TYPE MESSAGE-ID ";
$request .= "REFERENCES DISPOSITION-NOTIFICATION-TO X-PRIORITY)])";
if (!iil_PutLine($fp, $request)) {
return false;
}
do {
$line = chop(iil_ReadLine($fp, 200));
$a = explode(' ', $line);
if (($line[0] == '*') && ($a[2] == 'FETCH')) {
$id = $a[1];
$result[$id] = new iilBasicHeader;
$result[$id]->id = $id;
$result[$id]->subject = '';
$result[$id]->messageID = 'mid:' . $id;
/*
Start parsing headers. The problem is, some header "lines" take up multiple lines.
So, we'll read ahead, and if the one we're reading now is a valid header, we'll
process the previous line. Otherwise, we'll keep adding the strings until we come
to the next valid header line.
*/
$i = 0;
$lines = array();
do {
$line = chop(iil_ReadLine($fp, 300), "\r\n");
if (ord($line[0])<=32) {
$lines[$i] .= (empty($lines[$i])?'':"\n").trim($line);
} else {
$i++;
$lines[$i] = trim($line);
}
/*
The preg_match below works around communigate imap, which outputs " UID <number>)".
Without this, the while statement continues on and gets the "fh0 OK completed" message.
If this loop gets the ending message, then the outer loop does not receive it from radline on line 1249.
This in causes the if statement on line 1278 to never be true, which causes the headers to end up missing
If the if statement was changed to pick up the fh0 from this loop, then it causes the outer loop to spin
An alternative might be:
if (!preg_match("/:/",$line) && preg_match("/\)$/",$line)) break;
however, unsure how well this would work with all imap clients.
*/
if (preg_match("/^\s*UID [0-9]+\)$/", $line)) {
break;
}
// patch from "Maksim Rubis" <siburny@hotmail.com>
} while (trim($line[0]) != ')' && strncmp($line, $key, strlen($key)));
if (strncmp($line, $key, strlen($key))) {
//process header, fill iilBasicHeader obj.
// initialize
if (is_array($headers)) {
reset($headers);
while (list($k, $bar) = each($headers)) {
$headers[$k] = '';
}
}
// create array with header field:data
while ( list($lines_key, $str) = each($lines) ) {
list($field, $string) = iil_SplitHeaderLine($str);
$field = strtolower($field);
$string = ereg_replace("\n[[:space:]]*"," ",$string);
switch ($field) {
case 'date';
$result[$id]->date = $string;
$result[$id]->timestamp = iil_StrToTime($string);
break;
case 'from':
$result[$id]->from = $string;
break;
case 'to':
$result[$id]->to = $string;
break;
case 'subject':
$result[$id]->subject = $string;
break;
case 'reply-to':
$result[$id]->replyto = $string;
break;
case 'cc':
$result[$id]->cc = $string;
break;
case 'bcc':
$result[$id]->bcc = $string;
break;
case 'content-transfer-encoding':
$result[$id]->encoding = $string;
break;
case 'content-type':
$ctype_parts = explode(";", $string);
$result[$id]->ctype = array_shift($ctype_parts);
foreach ($ctype_parts as $ctype_add) {
if (preg_match('/charset="?([a-z0-9\-\.\_]+)"?/i',
$ctype_add, $regs)) {
$result[$id]->charset = $regs[1];
}
}
break;
case 'in-reply-to':
$result[$id]->in_reply_to = ereg_replace("[\n<>]", '', $string);
break;
case 'references':
$result[$id]->references = $string;
break;
case 'return-receipt-to':
case 'disposition-notification-to':
case 'x-confirm-reading-to':
$result[$id]->mdn_to = $string;
break;
case 'message-id':
$result[$id]->messageID = $string;
break;
case 'x-priority':
if (preg_match('/^(\d+)/', $string, $matches))
$result[$id]->priority = intval($matches[1]);
break;
} // end switch ()
} // end while ()
} else {
$a = explode(' ', $line);
}
}
} while (strcmp($a[0], $key) != 0);
/*
FETCH uid, size, flags
Sample reply line: "* 3 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen \Deleted))"
*/
$command_key = 'fh' . ($c++);
$request = $command_key . $prefix;
$request .= " FETCH $message_set (UID RFC822.SIZE FLAGS INTERNALDATE)";
if (!iil_PutLine($fp, $request)) {
return false;
}
do {
$line = chop(iil_ReadLine($fp, 200));
//$a = explode(' ', $line);
//if (($line[0]=="*") && ($a[2]=="FETCH")) {
if ($line[0] == '*') {
//echo "<!-- $line //-->\n";
//get outter most parens
$open_pos = strpos($line, "(") + 1;
$close_pos = strrpos($line, ")");
if ($open_pos && $close_pos) {
//extract ID from pre-paren
$pre_str = substr($line, 0, $open_pos);
$pre_a = explode(' ', $line);
$id = $pre_a[1];
//get data
$len = $close_pos - $open_pos;
$str = substr($line, $open_pos, $len);
//swap parents with quotes, then explode
$str = eregi_replace("[()]", "\"", $str);
$a = iil_ExplodeQuotedString(' ', $str);
//did we get the right number of replies?
$parts_count = count($a);
if ($parts_count>=8) {
for ($i=0;$i<$parts_count;$i=$i+2) {
if (strcasecmp($a[$i],"UID") == 0) $result[$id]->uid=$a[$i+1];
else if (strcasecmp($a[$i],"RFC822.SIZE") == 0) $result[$id]->size=$a[$i+1];
else if (strcasecmp($a[$i],"INTERNALDATE") == 0) $time_str = $a[$i+1];
else if (strcasecmp($a[$i],"FLAGS") == 0) $flags_str = $a[$i+1];
}
// process flags
$flags_str = eregi_replace('[\\\"]', '', $flags_str);
$flags_a = explode(' ', $flags_str);
if (is_array($flags_a)) {
reset($flags_a);
while (list($key,$val)=each($flags_a)) {
if (strcasecmp($val,'Seen') == 0) {
$result[$id]->seen = true;
} else if (strcasecmp($val, 'Deleted') == 0) {
$result[$id]->deleted=true;
} else if (strcasecmp($val, 'Recent') == 0) {
$result[$id]->recent = true;
} else if (strcasecmp($val, 'Answered') == 0) {
$result[$id]->answered = true;
} else if (strcasecmp($val, '$Forwarded') == 0) {
$result[$id]->forwarded = true;
} else if (strcasecmp($val, 'Draft') == 0) {
$result[$id]->is_draft = true;
} else if (strcasecmp($val, '$MDNSent') == 0) {
$result[$id]->mdn_sent = true;
} else if (strcasecmp($val, 'Flagged') == 0) {
$result[$id]->flagged = true;
}
}
$result[$id]->flags = $flags_a;
}
// if time is gmt...
$time_str = str_replace('GMT','+0000',$time_str);
//get timezone
$time_str = substr($time_str, 0, -1);
$time_zone_str = substr($time_str, -5); //extract timezone
$time_str = substr($time_str, 1, -6); //remove quotes
$time_zone = (float)substr($time_zone_str, 1, 2); //get first two digits
if ($time_zone_str[3] != '0') {
$time_zone += 0.5; //handle half hour offset
}
if ($time_zone_str[0] == '-') {
$time_zone = $time_zone * -1.0; //minus?
}
$result[$id]->internaldate = $time_str;
if ($IMAP_USE_INTERNAL_DATE || empty($result[$id]->date)) {
//calculate timestamp
$timestamp = strtotime($time_str); //return's server's time
$na_timestamp = $timestamp;
$timestamp -= $time_zone * 3600; //compensate for tz, get GMT
$result[$id]->timestamp = $timestamp;
$result[$id]->date = $time_str;
}
if ($conn->do_cache) {
$uid = $result[$id]->uid;
$conn->cache[$mailbox][$uid] = $result[$id];
$conn->cache_dirty[$mailbox] = true;
}
//echo "<!-- ID: $id : $time_str -- local: $na_timestamp (".date("F j, Y, g:i a", $na_timestamp).") tz: $time_zone -- GMT: ".$timestamp." (".date("F j, Y, g:i a", $timestamp).") //-->\n";
} else {
//echo "<!-- ERROR: $id : $str //-->\n";
}
}
}
} while (strpos($line, $command_key) === false);
return $result;
}
function iil_C_FetchHeader(&$conn, $mailbox, $id, $uidfetch=false) {
$fp = $conn->fp;
$a = iil_C_FetchHeaders($conn, $mailbox, $id, $uidfetch);
if (is_array($a)) {
return array_shift($a);
}
return false;
}
function iil_SortHeaders($a, $field, $flag) {
if (empty($field)) {
$field = 'uid';
}
$field = strtolower($field);
if ($field == 'date' || $field == 'internaldate') {
$field = 'timestamp';
}
if (empty($flag)) {
$flag = 'ASC';
}
$flag = strtoupper($flag);
$stripArr = ($field=='subject') ? array('Re: ','Fwd: ','Fw: ','"') : array('"');
$c=count($a);
if ($c > 0) {
/*
Strategy:
First, we'll create an "index" array.
Then, we'll use sort() on that array,
and use that to sort the main array.
*/
// create "index" array
$index = array();
reset($a);
while (list($key, $val)=each($a)) {
if ($field == 'timestamp') {
$data = @strtotime($val->date);
if ($data == false) {
$data = $val->timestamp;
}
} else {
$data = $val->$field;
if (is_string($data)) {
$data=strtoupper(str_replace($stripArr, '', $data));
}
}
$index[$key]=$data;
}
// sort index
$i = 0;
if ($flag == 'ASC') {
asort($index);
} else {
arsort($index);
}
// form new array based on index
$result = array();
reset($index);
while (list($key, $val)=each($index)) {
$result[$key]=$a[$key];
$i++;
}
}
return $result;
}
function iil_C_Expunge(&$conn, $mailbox) {
if (iil_C_Select($conn, $mailbox)) {
$c = 0;
iil_PutLine($conn->fp, "exp1 EXPUNGE");
do {
$line=chop(iil_ReadLine($conn->fp, 100));
if ($line[0] == '*') {
$c++;
}
} while (!iil_StartsWith($line, 'exp1'));
if (iil_ParseResult($line) == 0) {
$conn->selected = ''; //state has changed, need to reselect
//$conn->exists-=$c;
return $c;
}
$conn->error = $line;
}
return -1;
}
function iil_C_ModFlag(&$conn, $mailbox, $messages, $flag, $mod) {
if ($mod != '+' && $mod != '-') {
return -1;
}
$fp = $conn->fp;
$flags = $GLOBALS['IMAP_FLAGS'];
$flag = strtoupper($flag);
$flag = $flags[$flag];
if (iil_C_Select($conn, $mailbox)) {
$c = 0;
iil_PutLine($fp, "flg STORE $messages " . $mod . "FLAGS (" . $flag . ")");
do {
$line=chop(iil_ReadLine($fp, 100));
if ($line[0] == '*') {
$c++;
}
} while (!iil_StartsWith($line, 'flg'));
if (iil_ParseResult($line) == 0) {
iil_C_ExpireCachedItems($conn, $mailbox, $messages);
return $c;
}
$conn->error = $line;
return -1;
}
$conn->error = 'Select failed';
return -1;
}
function iil_C_Flag(&$conn, $mailbox, $messages, $flag) {
return iil_C_ModFlag($conn, $mailbox, $messages, $flag, '+');
}
function iil_C_Unflag(&$conn, $mailbox, $messages, $flag) {
return iil_C_ModFlag($conn, $mailbox, $messages, $flag, '-');
}
function iil_C_Delete(&$conn, $mailbox, $messages) {
return iil_C_ModFlag($conn, $mailbox, $messages, 'DELETED', '+');
}
function iil_C_Undelete(&$conn, $mailbox, $messages) {
return iil_C_ModFlag($conn, $mailbox, $messages, 'DELETED', '-');
}
function iil_C_Unseen(&$conn, $mailbox, $messages) {
return iil_C_ModFlag($conn, $mailbox, $messages, 'SEEN', '-');
}
function iil_C_Copy(&$conn, $messages, $from, $to) {
$fp = $conn->fp;
if (empty($from) || empty($to)) {
return -1;
}
if (iil_C_Select($conn, $from)) {
$c=0;
iil_PutLine($fp, "cpy1 COPY $messages \"".iil_Escape($to)."\"");
$line=iil_ReadReply($fp);
return iil_ParseResult($line);
} else {
return -1;
}
}
function iil_FormatSearchDate($month, $day, $year) {
$month = (int) $month;
$months = $GLOBALS['IMAP_MONTHS'];
return $day . '-' . $months[$month] . '-' . $year;
}
function iil_C_CountUnseen(&$conn, $folder) {
$index = iil_C_Search($conn, $folder, 'ALL UNSEEN');
if (is_array($index)) {
$str = implode(',', $index);
if (empty($str)) {
return false;
}
return count($index);
}
return false;
}
function iil_C_UID2ID(&$conn, $folder, $uid) {
if ($uid > 0) {
$id_a = iil_C_Search($conn, $folder, "UID $uid");
if (is_array($id_a)) {
$count = count($id_a);
if ($count > 1) {
return false;
}
return $id_a[0];
}
}
return false;
}
function iil_C_ID2UID(&$conn, $folder, $id) {
$fp = $conn->fp;
if ($id == 0) {
return -1;
}
$result = -1;
if (iil_C_Select($conn, $folder)) {
$key = 'FUID';
if (iil_PutLine($fp, "$key FETCH $id (UID)")) {
do {
$line=chop(iil_ReadLine($fp, 1024));
if (eregi("^\* $id FETCH \(UID (.*)\)", $line, $r)) {
$result = $r[1];
}
} while (!preg_match("/^$key/", $line));
}
}
return $result;
}
function iil_C_Search(&$conn, $folder, $criteria) {
$fp = $conn->fp;
if (iil_C_Select($conn, $folder)) {
$c = 0;
$query = 'srch1 SEARCH ' . chop($criteria);
iil_PutLine($fp, $query);
do {
$line=trim(iil_ReadLine($fp, 10000));
if (eregi("^\* SEARCH", $line)) {
$str = trim(substr($line, 8));
$messages = explode(' ', $str);
}
} while (!iil_StartsWith($line, 'srch1'));
$result_code = iil_ParseResult($line);
if ($result_code == 0) {
return $messages;
}
$conn->error = 'iil_C_Search: ' . $line . "\n";
return false;
}
$conn->error = "iil_C_Search: Couldn't select \"$folder\"\n";
return false;
}
function iil_C_Move(&$conn, $messages, $from, $to) {
$fp = $conn->fp;
if (!$from || !$to) {
return -1;
}
$r = iil_C_Copy($conn, $messages, $from,$to);
if ($r==0) {
return iil_C_Delete($conn, $from, $messages);
}
return $r;
}
/**
* Gets the delimiter, for example:
* INBOX.foo -> .
* INBOX/foo -> /
* INBOX\foo -> \
*
* @return mixed A delimiter (string), or false.
* @param object $conn The current connection.
* @see iil_Connect()
*/
function iil_C_GetHierarchyDelimiter(&$conn) {
if ($conn->delimiter) {
return $conn->delimiter;
}
$fp = $conn->fp;
$delimiter = false;
//try (LIST "" ""), should return delimiter (RFC2060 Sec 6.3.8)
if (!iil_PutLine($fp, 'ghd LIST "" ""')) {
return false;
}
do {
$line=iil_ReadLine($fp, 500);
if ($line[0] == '*') {
$line = rtrim($line);
$a=iil_ExplodeQuotedString(' ', $line);
if ($a[0] == '*') {
$delimiter = str_replace('"', '', $a[count($a)-2]);
}
}
} while (!iil_StartsWith($line, 'ghd'));
if (strlen($delimiter)>0) {
return $delimiter;
}
//if that fails, try namespace extension
//try to fetch namespace data
iil_PutLine($conn->fp, "ns1 NAMESPACE");
do {
$line = iil_ReadLine($conn->fp, 1024);
if (iil_StartsWith($line, '* NAMESPACE')) {
$i = 0;
$data = iil_ParseNamespace2(substr($line,11), $i, 0, 0);
}
} while (!iil_StartsWith($line, 'ns1'));
if (!is_array($data)) {
return false;
}
//extract user space data (opposed to global/shared space)
$user_space_data = $data[0];
if (!is_array($user_space_data)) {
return false;
}
//get first element
$first_userspace = $user_space_data[0];
if (!is_array($first_userspace)) {
return false;
}
//extract delimiter
$delimiter = $first_userspace[1];
return $delimiter;
}
function iil_C_ListMailboxes(&$conn, $ref, $mailbox) {
global $IGNORE_FOLDERS;
$ignore = $IGNORE_FOLDERS[strtolower($conn->host)];
$fp = $conn->fp;
if (empty($mailbox)) {
$mailbox = '*';
}
if (empty($ref) && $conn->rootdir) {
$ref = $conn->rootdir;
}
// send command
if (!iil_PutLine($fp, "lmb LIST \"".$ref."\" \"".iil_Escape($mailbox)."\"")) {
return false;
}
$i = 0;
// get folder list
do {
$line = iil_ReadLine($fp, 500);
$line = iil_MultLine($fp, $line);
$a = explode(' ', $line);
if (($line[0] == '*') && ($a[1] == 'LIST')) {
$line = rtrim($line);
// split one line
$a = iil_ExplodeQuotedString(' ', $line);
// last string is folder name
$folder = trim($a[count($a)-1], '"');
if (empty($ignore) || (!empty($ignore)
&& !eregi($ignore, $folder))) {
$folders[$i] = $folder;
}
// second from last is delimiter
$delim = trim($a[count($a)-2], '"');
// is it a container?
$i++;
}
} while (!iil_StartsWith($line, 'lmb'));
if (is_array($folders)) {
if (!empty($ref)) {
// if rootdir was specified, make sure it's the first element
// some IMAP servers (i.e. Courier) won't return it
if ($ref[strlen($ref)-1]==$delim)
$ref = substr($ref, 0, strlen($ref)-1);
if ($folders[0]!=$ref)
array_unshift($folders, $ref);
}
return $folders;
} else if (iil_ParseResult($line) == 0) {
return array('INBOX');
} else {
$conn->error = $line;
return false;
}
}
function iil_C_ListSubscribed(&$conn, $ref, $mailbox) {
global $IGNORE_FOLDERS;
$ignore = $IGNORE_FOLDERS[strtolower($conn->host)];
$fp = $conn->fp;
if (empty($mailbox)) {
$mailbox = '*';
}
if (empty($ref) && $conn->rootdir) {
$ref = $conn->rootdir;
}
$folders = array();
// send command
if (!iil_PutLine($fp, 'lsb LSUB "' . $ref . '" "' . iil_Escape($mailbox).'"')) {
$conn->error = "Couldn't send LSUB command\n";
return false;
}
$i = 0;
// get folder list
do {
$line = iil_ReadLine($fp, 500);
$line = iil_MultLine($fp, $line);
$a = explode(' ', $line);
if (($line[0] == '*') && ($a[1] == 'LSUB' || $a[1] == 'LIST')) {
$line = rtrim($line);
// split one line
$a = iil_ExplodeQuotedString(' ', $line);
// last string is folder name
//$folder = UTF7DecodeString(str_replace('"', '', $a[count($a)-1]));
$folder = trim($a[count($a)-1], '"');
if ((!in_array($folder, $folders)) && (empty($ignore)
|| (!empty($ignore) && !eregi($ignore, $folder)))) {
$folders[$i] = $folder;
}
// second from last is delimiter
$delim = trim($a[count($a)-2], '"');
// is it a container?
$i++;
}
} while (!iil_StartsWith($line, 'lsb'));
if (is_array($folders)) {
if (!empty($ref)) {
// if rootdir was specified, make sure it's the first element
// some IMAP servers (i.e. Courier) won't return it
if ($ref[strlen($ref)-1]==$delim) {
$ref = substr($ref, 0, strlen($ref)-1);
}
if ($folders[0]!=$ref) {
array_unshift($folders, $ref);
}
}
return $folders;
}
$conn->error = $line;
return false;
}
function iil_C_Subscribe(&$conn, $folder) {
$fp = $conn->fp;
$query = 'sub1 SUBSCRIBE "' . iil_Escape($folder). '"';
iil_PutLine($fp, $query);
$line = trim(iil_ReadLine($fp, 10000));
return iil_ParseResult($line);
}
function iil_C_UnSubscribe(&$conn, $folder) {
$fp = $conn->fp;
$query = 'usub1 UNSUBSCRIBE "' . iil_Escape($folder) . '"';
iil_PutLine($fp, $query);
$line = trim(iil_ReadLine($fp, 10000));
return iil_ParseResult($line);
}
function iil_C_FetchPartHeader(&$conn, $mailbox, $id, $part) {
$fp = $conn->fp;
$result = false;
if (($part == 0) || (empty($part))) {
$part = 'HEADER';
} else {
$part .= '.MIME';
}
if (iil_C_Select($conn, $mailbox)) {
$key = 'fh' . ($c++);
$request = $key . " FETCH $id (BODY.PEEK[$part])";
if (!iil_PutLine($fp, $request)) return false;
do {
$line = chop(iil_ReadLine($fp, 200));
$a = explode(' ', $line);
if (($line[0] == '*') && ($a[2] == 'FETCH')
&& ($line[strlen($line)-1] != ')')) {
$line=iil_ReadLine($fp, 300);
while (trim($line) != ')') {
$result .= $line;
$line=iil_ReadLine($fp, 300);
}
}
} while (strcmp($a[0], $key) != 0);
}
return $result;
}
function iil_C_HandlePartBody(&$conn, $mailbox, $id, $part, $mode) {
/* modes:
1: return string
2: print
3: base64 and print
*/
$fp = $conn->fp;
$result = false;
if (($part == 0) || empty($part)) {
$part = 'TEXT';
}
if (iil_C_Select($conn, $mailbox)) {
$reply_key = '* ' . $id;
// format request
$key = 'ftch' . ($c++) . ' ';
$request = $key . "FETCH $id (BODY.PEEK[$part])";
// send request
if (!iil_PutLine($fp, $request)) {
return false;
}
// receive reply line
do {
$line = chop(iil_ReadLine($fp, 1000));
$a = explode(' ', $line);
} while ($a[2] != 'FETCH');
$len = strlen($line);
if ($line[$len-1] == ')') {
// one line response, get everything between first and last quotes
if (substr($line, -4, 3) == 'NIL') {
// NIL response
$result = '';
} else {
$from = strpos($line, '"') + 1;
$to = strrpos($line, '"');
$len = $to - $from;
$result = substr($line, $from, $len);
}
if ($mode == 2) {
echo $result;
} else if ($mode == 3) {
echo base64_decode($result);
}
} else if ($line[$len-1] == '}') {
//multi-line request, find sizes of content and receive that many bytes
$from = strpos($line, '{') + 1;
$to = strrpos($line, '}');
$len = $to - $from;
$sizeStr = substr($line, $from, $len);
$bytes = (int)$sizeStr;
$received = 0;
while ($received < $bytes) {
$remaining = $bytes - $received;
$line = iil_ReadLine($fp, 1024);
$len = strlen($line);
if ($len > $remaining) {
$line = substr($line, 0, $remaining);
}
$received += strlen($line);
if ($mode == 1) {
$result .= rtrim($line, "\t\r\n\0\x0B") . "\n";
} else if ($mode == 2) {
echo rtrim($line, "\t\r\n\0\x0B") . "\n";
} else if ($mode == 3) {
echo base64_decode($line);
}
}
}
// read in anything up until 'til last line
do {
$line = iil_ReadLine($fp, 1024);
} while (!iil_StartsWith($line, $key));
if ($result) {
$result = rtrim($result, "\t\r\n\0\x0B");
return $result; // substr($result, 0, strlen($result)-1);
}
return false;
} else {
echo 'Select failed.';
}
if ($mode==1) {
return $result;
}
return $received;
}
function iil_C_FetchPartBody(&$conn, $mailbox, $id, $part) {
return iil_C_HandlePartBody($conn, $mailbox, $id, $part, 1);
}
function iil_C_PrintPartBody(&$conn, $mailbox, $id, $part) {
iil_C_HandlePartBody($conn, $mailbox, $id, $part, 2);
}
function iil_C_PrintBase64Body(&$conn, $mailbox, $id, $part) {
iil_C_HandlePartBody($conn, $mailbox, $id, $part, 3);
}
function iil_C_CreateFolder(&$conn, $folder) {
$fp = $conn->fp;
if (iil_PutLine($fp, 'c CREATE "' . iil_Escape($folder) . '"')) {
do {
$line=iil_ReadLine($fp, 300);
} while ($line[0] != 'c');
$conn->error = $line;
return (iil_ParseResult($line) == 0);
}
return false;
}
function iil_C_RenameFolder(&$conn, $from, $to) {
$fp = $conn->fp;
if (iil_PutLine($fp, 'r RENAME "' . iil_Escape($from) . '" "' . iil_Escape($to) . '"')) {
do {
$line = iil_ReadLine($fp, 300);
} while ($line[0] != 'r');
return (iil_ParseResult($line) == 0);
}
return false;
}
function iil_C_DeleteFolder(&$conn, $folder) {
$fp = $conn->fp;
if (iil_PutLine($fp, 'd DELETE "' . iil_Escape($folder). '"')) {
do {
$line=iil_ReadLine($fp, 300);
} while ($line[0] != 'd');
return (iil_ParseResult($line) == 0);
}
$conn->error = "Couldn't send command\n";
return false;
}
function iil_C_Append(&$conn, $folder, &$message) {
if (!$folder) {
return false;
}
$fp = $conn->fp;
$message = str_replace("\r", '', $message);
$message = str_replace("\n", "\r\n", $message);
$len = strlen($message);
if (!$len) {
return false;
}
$request = 'A APPEND "' . iil_Escape($folder) .'" (\\Seen) {' . $len . '}';
if (iil_PutLine($fp, $request)) {
$line=iil_ReadLine($fp, 100);
$sent = fwrite($fp, $message."\r\n");
do {
$line=iil_ReadLine($fp, 1000);
} while ($line[0] != 'A');
$result = (iil_ParseResult($line) == 0);
if (!$result) {
$conn->error .= $line . "\n";
}
return $result;
}
$conn->error .= "Couldn't send command \"$request\"\n";
return false;
}
function iil_C_AppendFromFile(&$conn, $folder, $path) {
if (!$folder) {
return false;
}
//open message file
$in_fp = false;
if (file_exists(realpath($path))) {
$in_fp = fopen($path, 'r');
}
if (!$in_fp) {
$conn->error .= "Couldn't open $path for reading\n";
return false;
}
$fp = $conn->fp;
$len = filesize($path);
if (!$len) {
return false;
}
//send APPEND command
$request = 'A APPEND "' . iil_Escape($folder) . '" (\\Seen) {' . $len . '}';
$bytes_sent = 0;
if (iil_PutLine($fp, $request)) {
$line = iil_ReadLine($fp, 100);
//send file
while (!feof($in_fp)) {
$buffer = fgets($in_fp, 4096);
$bytes_sent += strlen($buffer);
iil_PutLine($fp, $buffer, false);
}
fclose($in_fp);
iil_PutLine($fp, '');
//read response
do {
$line = iil_ReadLine($fp, 1000);
} while ($line[0] != 'A');
$result = (iil_ParseResult($line) == 0);
if (!$result) {
$conn->error .= $line . "\n";
}
return $result;
}
$conn->error .= "Couldn't send command \"$request\"\n";
return false;
}
function iil_C_FetchStructureString(&$conn, $folder, $id) {
$fp = $conn->fp;
$result = false;
if (iil_C_Select($conn, $folder)) {
$key = 'F1247';
if (iil_PutLine($fp, "$key FETCH $id (BODYSTRUCTURE)")) {
do {
$line = iil_ReadLine($fp, 5000);
$line = iil_MultLine($fp, $line);
list(, $index, $cmd, $rest) = explode(' ', $line);
if ($cmd != 'FETCH' || $index == $id || preg_match("/^$key/", $line))
$result .= $line;
} while (!preg_match("/^$key/", $line));
$result = trim(substr($result, strpos($result, 'BODYSTRUCTURE')+13, -(strlen($result)-strrpos($result, $key)+1)));
}
}
return $result;
}
function iil_C_PrintSource(&$conn, $folder, $id, $part) {
$header = iil_C_FetchPartHeader($conn, $folder, $id, $part);
//echo str_replace("\r", '', $header);
echo $header;
echo iil_C_PrintPartBody($conn, $folder, $id, $part);
}
function iil_C_GetQuota(&$conn) {
/*
* GETQUOTAROOT "INBOX"
* QUOTAROOT INBOX user/rchijiiwa1
* QUOTA user/rchijiiwa1 (STORAGE 654 9765)
b OK Completed
*/
$fp = $conn->fp;
$result = false;
$quota_line = '';
//get line containing quota info
if (iil_PutLine($fp, 'QUOT1 GETQUOTAROOT "INBOX"')) {
do {
$line=chop(iil_ReadLine($fp, 5000));
if (iil_StartsWith($line, '* QUOTA ')) {
$quota_line = $line;
}
} while (!iil_StartsWith($line, 'QUOT1'));
}
//return false if not found, parse if found
if (!empty($quota_line)) {
$quota_line = eregi_replace('[()]', '', $quota_line);
$parts = explode(' ', $quota_line);
$storage_part = array_search('STORAGE', $parts);
if ($storage_part > 0) {
$result['used'] = intval($parts[$storage_part+1]);
$result['total'] = intval($parts[$storage_part+2]);
$result['percent'] = min(100, round(($result['used']/max(1,$result['total']))*100));
$result['free'] = 100 - $result['percent'];
}
}
return $result;
}
function iil_C_ClearFolder(&$conn, $folder) {
$num_in_trash = iil_C_CountMessages($conn, $folder);
if ($num_in_trash > 0) {
iil_C_Delete($conn, $folder, '1:' . $num_in_trash);
}
return (iil_C_Expunge($conn, $folder) >= 0);
}
?>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Mar 1, 8:44 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
166426
Default Alt Text
(153 KB)
Attached To
Mode
R3 roundcubemail
Attached
Detach File
Event Timeline
Log In to Comment