971 lines
29 KiB
PHP
971 lines
29 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2008-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file COPYING for license information (GPL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/gpl.
|
|
*
|
|
* @category Horde
|
|
* @copyright 2008-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/gpl GPL
|
|
* @package IMP
|
|
*/
|
|
|
|
/**
|
|
* Provides common functions for interaction with IMAP/POP3 servers via the
|
|
* Horde_Imap_Client package.
|
|
*
|
|
* @author Michael Slusarz <slusarz@horde.org>
|
|
* @category Horde
|
|
* @copyright 2008-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/gpl GPL
|
|
* @package IMP
|
|
*
|
|
* @property-read boolean $changed If true, this object has changed.
|
|
* @property-read Horde_Imap_Client_Base $client_ob The IMAP client object.
|
|
* @property-read IMP_Imap_Config $config Base backend config settings.
|
|
* @property-read boolean $init Has the IMAP object been initialized?
|
|
* @property-read integer $max_compose_bodysize The maximum size (in bytes)
|
|
* of the compose message body.
|
|
* @property-read integer $max_compose_recipients The maximum number of
|
|
* recipients to send to per
|
|
* compose message.
|
|
* @property-read integer $max_compose_timelimit The maximum number of
|
|
* recipients to send to in the
|
|
* configured timelimit.
|
|
* @property-read integer $max_create_mboxes The maximum number of mailboxes
|
|
* a user can create.
|
|
* @property-read string $server_key Server key used to login.
|
|
* @property-read string $thread_algo The threading algorithm to use.
|
|
* @property-read Horde_Imap_Client_Url $url A URL object.
|
|
*/
|
|
class IMP_Imap implements Serializable
|
|
{
|
|
/* Access constants. */
|
|
const ACCESS_FOLDERS = 1;
|
|
const ACCESS_SEARCH = 2;
|
|
const ACCESS_FLAGS = 3;
|
|
const ACCESS_UNSEEN = 4;
|
|
const ACCESS_TRASH = 5;
|
|
const ACCESS_CREATEMBOX = 6;
|
|
const ACCESS_CREATEMBOX_MAX = 7;
|
|
const ACCESS_COMPOSE_BODYSIZE = 13;
|
|
const ACCESS_COMPOSE_RECIPIENTS = 8;
|
|
const ACCESS_COMPOSE_TIMELIMIT = 9;
|
|
const ACCESS_ACL = 10;
|
|
const ACCESS_DRAFTS = 11;
|
|
const ACCESS_REMOTE = 12;
|
|
const ACCESS_IMPORT = 14;
|
|
const ACCESS_SORT = 15;
|
|
|
|
/* Default namespace. */
|
|
const NS_DEFAULT = "\0default";
|
|
|
|
/**
|
|
* Cached backend configuration.
|
|
*
|
|
* @var array
|
|
*/
|
|
static protected $_backends = array();
|
|
|
|
/**
|
|
* Has this object changed?
|
|
*
|
|
* @var boolean
|
|
*/
|
|
protected $_changed = false;
|
|
|
|
/**
|
|
* Backend config.
|
|
*
|
|
* @var IMP_Imap_Config
|
|
*/
|
|
protected $_config;
|
|
|
|
/**
|
|
* Object identifier.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_id;
|
|
|
|
/**
|
|
* The IMAP client object.
|
|
*
|
|
* @var Horde_Imap_Client_Base
|
|
*/
|
|
protected $_ob;
|
|
|
|
/**
|
|
* Temporary data cache (destroyed at end of request).
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_temp = array();
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param string $id Object identifier.
|
|
*/
|
|
public function __construct($id)
|
|
{
|
|
$this->_id = strval($id);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
public function __get($key)
|
|
{
|
|
switch ($key) {
|
|
case 'changed':
|
|
return $this->_changed;
|
|
|
|
case 'client_ob':
|
|
return $this->init
|
|
? $this->_ob
|
|
: null;
|
|
|
|
case 'config':
|
|
return isset($this->_config)
|
|
? $this->_config
|
|
: new Horde_Support_Stub();
|
|
|
|
case 'init':
|
|
return isset($this->_ob);
|
|
|
|
case 'max_compose_bodysize':
|
|
case 'max_compose_recipients':
|
|
case 'max_compose_timelimit':
|
|
$perm = $GLOBALS['injector']->getInstance('Horde_Perms')->getPermissions('imp:' . str_replace('max_compose', 'max', $key), $GLOBALS['registry']->getAuth());
|
|
return intval($perm[0]);
|
|
|
|
case 'max_create_mboxes':
|
|
$perm = $GLOBALS['injector']->getInstance('Horde_Perms')->getPermissions('imp:' . $this->_getPerm($key), $GLOBALS['registry']->getAuth());
|
|
return intval($perm[0]);
|
|
|
|
case 'server_key':
|
|
return $this->init
|
|
? $this->_ob->getParam('imp:backend')
|
|
: null;
|
|
|
|
case 'thread_algo':
|
|
if (!$this->init) {
|
|
return 'ORDEREDSUBJECT';
|
|
}
|
|
|
|
if ($thread = $this->_ob->getParam('imp:thread_algo')) {
|
|
return $thread;
|
|
}
|
|
|
|
$thread = $this->config->thread;
|
|
$thread_cap = $this->queryCapability('THREAD');
|
|
if (!in_array($thread, is_array($thread_cap) ? $thread_cap : array())) {
|
|
$thread = 'ORDEREDSUBJECT';
|
|
}
|
|
|
|
$this->_ob->setParam('imp:thread_algo', $thread);
|
|
$this->_changed = true;
|
|
|
|
return $thread;
|
|
|
|
case 'url':
|
|
$url = new Horde_Imap_Client_Url();
|
|
if ($this->init) {
|
|
$url->hostspec = $this->getParam('hostspec');
|
|
$url->port = $this->getParam('port');
|
|
$url->protocol = $this->isImap() ? 'imap' : 'pop';
|
|
}
|
|
return $url;
|
|
}
|
|
}
|
|
|
|
/**
|
|
*/
|
|
public function __toString()
|
|
{
|
|
return $this->_id;
|
|
}
|
|
|
|
/**
|
|
* Get the full permission name for a permission.
|
|
*
|
|
* @param string $perm The permission.
|
|
*
|
|
* @return string The full (backend-specific) permission name.
|
|
*/
|
|
protected function _getPerm($perm)
|
|
{
|
|
return 'backends:' . ($this->init ? $this->server_key . ':' : '') . $perm;
|
|
}
|
|
|
|
/**
|
|
* Determine if this is a connection to an IMAP server.
|
|
*
|
|
* @return boolean True if connected to IMAP server.
|
|
*/
|
|
public function isImap()
|
|
{
|
|
return ($this->init &&
|
|
($this->_ob instanceof Horde_Imap_Client_Socket));
|
|
}
|
|
|
|
/**
|
|
* Determine if this is a connection to an IMAP server.
|
|
*
|
|
* @return boolean True if connected to IMAP server.
|
|
*/
|
|
public function isPop3()
|
|
{
|
|
return ($this->init &&
|
|
($this->_ob instanceof Horde_Imap_Client_Socket_Pop3));
|
|
}
|
|
|
|
/**
|
|
* Create the base Horde_Imap_Client object (from an entry in
|
|
* backends.php).
|
|
*
|
|
* @param string $username The username to authenticate with.
|
|
* @param string $password The password to authenticate with.
|
|
* @param string $skey Create a new object using this server key.
|
|
*
|
|
* @return Horde_Imap_Client_Base Client object.
|
|
* @throws IMP_Imap_Exception
|
|
*/
|
|
public function createBaseImapObject($username, $password, $skey)
|
|
{
|
|
if ($this->init) {
|
|
return $this->client_ob;
|
|
}
|
|
|
|
if (($config = $this->loadServerConfig($skey)) === false) {
|
|
$error = new IMP_Imap_Exception('Could not load server configuration.');
|
|
Horde::log($error);
|
|
throw $error;
|
|
}
|
|
|
|
$imap_config = array(
|
|
'hostspec' => $config->hostspec,
|
|
'id' => $config->id,
|
|
'password' => new IMP_Imap_Password($password),
|
|
'port' => $config->port,
|
|
'secure' => (($secure = $config->secure) ? $secure : false),
|
|
'username' => $username,
|
|
// IMP specific config
|
|
'imp:backend' => $skey
|
|
);
|
|
|
|
/* Needed here to set config information in createImapObject(). */
|
|
$this->_config = $config;
|
|
|
|
try {
|
|
return $this->createImapObject($imap_config, ($config->protocol == 'imap'));
|
|
} catch (IMP_Imap_Exception $e) {
|
|
unset($this->_config);
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a Horde_Imap_Client object.
|
|
*
|
|
* @param array $config The IMAP configuration.
|
|
* @param boolean $imap True if IMAP connection, false if POP3.
|
|
*
|
|
* @return Horde_Imap_Client_Base Client object.
|
|
* @throws IMP_Imap_Exception
|
|
*/
|
|
public function createImapObject($config, $imap = true)
|
|
{
|
|
if ($this->init) {
|
|
return $this->_ob;
|
|
}
|
|
|
|
$sconfig = $this->config;
|
|
$config = array_merge(array(
|
|
'cache' => $sconfig->cache_params,
|
|
'capability_ignore' => $sconfig->capability_ignore,
|
|
'comparator' => $sconfig->comparator,
|
|
'debug' => $sconfig->debug,
|
|
'debug_literal' => $sconfig->debug_raw,
|
|
'lang' => $sconfig->lang,
|
|
'timeout' => $sconfig->timeout,
|
|
// 'imp:login' - Set in __call()
|
|
), $config);
|
|
|
|
try {
|
|
$this->_ob = $imap
|
|
? new Horde_Imap_Client_Socket($config)
|
|
: new Horde_Imap_Client_Socket_Pop3($config);
|
|
return $this->_ob;
|
|
} catch (Horde_Imap_Client_Exception $e) {
|
|
Horde::log($e->raw_msg);
|
|
throw new IMP_Imap_Exception($e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform post-login tasks.
|
|
*/
|
|
public function doPostLoginTasks()
|
|
{
|
|
global $prefs;
|
|
|
|
switch ($this->_config->protocol) {
|
|
case 'imap':
|
|
/* Overwrite default special mailbox names. */
|
|
foreach ($this->_config->special_mboxes as $key => $val) {
|
|
if ($key != IMP_Mailbox::MBOX_USERSPECIAL) {
|
|
$prefs->setValue($key, $val, array(
|
|
'force' => true,
|
|
'nosave' => true
|
|
));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'pop':
|
|
/* Turn some options off if we are working with POP3. */
|
|
foreach (array('newmail_notify', 'save_sent_mail') as $val) {
|
|
$prefs->setValue($val, false, array(
|
|
'force' => true,
|
|
'nosave' => true
|
|
));
|
|
$prefs->setLocked($val, true);
|
|
}
|
|
$prefs->setLocked(IMP_Mailbox::MBOX_DRAFTS, true);
|
|
$prefs->setLocked(IMP_Mailbox::MBOX_SENT, true);
|
|
$prefs->setLocked(IMP_Mailbox::MBOX_SPAM, true);
|
|
$prefs->setLocked(IMP_Mailbox::MBOX_TEMPLATES, true);
|
|
$prefs->setLocked(IMP_Mailbox::MBOX_TRASH, true);
|
|
break;
|
|
}
|
|
|
|
$this->updateFetchIgnore();
|
|
}
|
|
|
|
/**
|
|
* Update the list of mailboxes to ignore when caching FETCH data in the
|
|
* IMAP client object.
|
|
*/
|
|
public function updateFetchIgnore()
|
|
{
|
|
if ($this->isImap()) {
|
|
$special = IMP_Mailbox::getSpecialMailboxes();
|
|
$cache = $this->_ob->getParam('cache');
|
|
$cache['fetch_ignore'] = array_filter(array(
|
|
strval($special[IMP_Mailbox::SPECIAL_SPAM]),
|
|
strval($special[IMP_Mailbox::SPECIAL_TRASH])
|
|
));
|
|
$this->_ob->setParam('cache', $cache);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks access rights for a server.
|
|
*
|
|
* @param integer $right Access right.
|
|
*
|
|
* @return boolean Does the access right exist?
|
|
*/
|
|
public function access($right)
|
|
{
|
|
global $injector;
|
|
|
|
if (!$this->init) {
|
|
return false;
|
|
}
|
|
|
|
switch ($right) {
|
|
case self::ACCESS_ACL:
|
|
return ($this->config->acl && $this->queryCapability('ACL'));
|
|
|
|
case self::ACCESS_CREATEMBOX:
|
|
return ($this->isImap() &&
|
|
$injector->getInstance('Horde_Core_Perms')->hasAppPermission($this->_getPerm('create_mboxes')));
|
|
|
|
case self::ACCESS_CREATEMBOX_MAX:
|
|
return ($this->isImap() &&
|
|
$injector->getInstance('Horde_Core_Perms')->hasAppPermission($this->_getPerm('max_create_mboxes')));
|
|
|
|
case self::ACCESS_DRAFTS:
|
|
case self::ACCESS_FLAGS:
|
|
case self::ACCESS_IMPORT:
|
|
case self::ACCESS_SEARCH:
|
|
case self::ACCESS_UNSEEN:
|
|
return $this->isImap();
|
|
|
|
case self::ACCESS_FOLDERS:
|
|
case self::ACCESS_TRASH:
|
|
return ($this->isImap() &&
|
|
$injector->getInstance('Horde_Core_Perms')->hasAppPermission($this->_getPerm('allow_folders')));
|
|
|
|
case self::ACCESS_REMOTE:
|
|
return $injector->getInstance('Horde_Core_Perms')->hasAppPermission($this->_getPerm('allow_remote'));
|
|
|
|
case self::ACCESS_SORT:
|
|
return ($this->isImap() &&
|
|
($this->config->sort_force || $this->_ob->queryCapability('SORT')));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks compose access rights for a server.
|
|
*
|
|
* @param integer $right Access right.
|
|
* @param integer $data Data required to check the rights:
|
|
* <pre>
|
|
* - ACCESS_COMPOSE_BODYSIZE
|
|
* The size of the body data.
|
|
*
|
|
* - ACCESS_COMPOSE_RECIPIENTS
|
|
* - ACCESS_COMPOSE_TIMELIMIT
|
|
* The number of e-mail recipients.
|
|
* </pre>
|
|
*
|
|
* @return boolean Is the access allowed?
|
|
*/
|
|
public function accessCompose($right, $data)
|
|
{
|
|
switch ($right) {
|
|
case self::ACCESS_COMPOSE_BODYSIZE:
|
|
$perm_name = 'max_bodysize';
|
|
break;
|
|
|
|
case self::ACCESS_COMPOSE_RECIPIENTS:
|
|
$perm_name = 'max_recipients';
|
|
break;
|
|
|
|
case self::ACCESS_COMPOSE_TIMELIMIT:
|
|
$perm_name = 'max_timelimit';
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return $GLOBALS['injector']->getInstance('Horde_Core_Perms')->hasAppPermission(
|
|
$perm_name,
|
|
array(
|
|
'opts' => array(
|
|
'value' => $data
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get namespace info for a full mailbox path.
|
|
*
|
|
* @param string $mailbox The mailbox path. (self:NS_DEFAULT will
|
|
* return the default personal namespace.)
|
|
* @param boolean $personal If true, will return empty namespace only
|
|
* if it is a personal namespace.
|
|
*
|
|
* @return mixed The namespace info for the mailbox path or null if the
|
|
* path doesn't exist.
|
|
*/
|
|
public function getNamespace($mailbox, $personal = false)
|
|
{
|
|
if ($this->isImap()) {
|
|
$ns = $this->getNamespaces();
|
|
if ($mailbox !== self::NS_DEFAULT) {
|
|
return $ns->getNamespace($mailbox, $personal);
|
|
}
|
|
|
|
foreach ($ns as $val) {
|
|
if ($val->type === $val::NS_PERSONAL) {
|
|
return $val;
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Return the cache ID for this mailbox.
|
|
*
|
|
* @param string $mailbox The mailbox name (UTF-8).
|
|
* @param array $addl Local IMP metadata to add to the cache ID.
|
|
*
|
|
* @return string The cache ID.
|
|
*/
|
|
public function getCacheId($mailbox, array $addl = array())
|
|
{
|
|
return $this->getSyncToken($mailbox) .
|
|
(empty($addl) ? '' : ('|' . implode('|', $addl)));
|
|
}
|
|
|
|
/**
|
|
* Parses the cache ID for this mailbox.
|
|
*
|
|
* @param string $id Cache ID generated by getCacheId().
|
|
*
|
|
* @return array Two element array:
|
|
* - date: (integer) Date information (day of year), if embedded in
|
|
* cache ID.
|
|
* - token: (string) Mailbox sync token.
|
|
*/
|
|
public function parseCacheId($id)
|
|
{
|
|
$out = array('date' => null);
|
|
|
|
if ((($pos = strrpos($id, '|')) !== false) &&
|
|
(substr($id, $pos + 1, 1) == 'D')) {
|
|
$out['date'] = substr($id, $pos + 2);
|
|
}
|
|
|
|
$out['token'] = (($pos = strpos($id, '|')) === false)
|
|
? $id
|
|
: substr($id, 0, $pos);
|
|
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* Returns a list of messages, split into slices based on the total
|
|
* message size.
|
|
*
|
|
* @param string $mbox IMAP mailbox.
|
|
* @param Horde_Imap_Client_Ids $ids ID list.
|
|
* @param integer $size Maximum size of a slice.
|
|
*
|
|
* @return array An array of Horde_Imap_Client_Ids objects.
|
|
*/
|
|
public function getSlices(
|
|
$mbox, Horde_Imap_Client_Ids $ids, $size = 5242880
|
|
)
|
|
{
|
|
$imp_imap = IMP_Mailbox::get($mbox)->imp_imap;
|
|
|
|
$query = new Horde_Imap_Client_Fetch_Query();
|
|
$query->size();
|
|
|
|
try {
|
|
$res = $imp_imap->fetch($mbox, $query, array(
|
|
'ids' => $ids,
|
|
'nocache' => true
|
|
));
|
|
} catch (IMP_Imap_Exception $e) {
|
|
return array();
|
|
}
|
|
|
|
$curr = $slices = array();
|
|
$curr_size = 0;
|
|
|
|
foreach ($res as $key => $val) {
|
|
$curr_size += $val->getSize();
|
|
if ($curr_size > $size) {
|
|
$slices[] = $imp_imap->getIdsOb($curr, $ids->sequence);
|
|
$curr = array();
|
|
}
|
|
$curr[] = $key;
|
|
}
|
|
|
|
$slices[] = $imp_imap->getIdsOb($curr, $ids->sequence);
|
|
|
|
return $slices;
|
|
}
|
|
|
|
/**
|
|
* Handle status() calls. This call may hit multiple servers.
|
|
*
|
|
* @see Horde_Imap_Client_Base#status()
|
|
*/
|
|
protected function _status($args)
|
|
{
|
|
global $injector;
|
|
|
|
$accounts = $mboxes = $out = array();
|
|
$imap_factory = $injector->getInstance('IMP_Factory_Imap');
|
|
|
|
foreach (IMP_Mailbox::get($args[0]) as $val) {
|
|
if ($raccount = $val->remote_account) {
|
|
$accounts[strval($raccount)] = $raccount;
|
|
}
|
|
$mboxes[strval($raccount)][] = $val;
|
|
}
|
|
|
|
foreach ($mboxes as $key => $val) {
|
|
$imap = $imap_factory->create($key);
|
|
if ($imap->init) {
|
|
foreach (call_user_func_array(array($imap, 'impStatus'), array($val) + $args) as $key2 => $val2) {
|
|
$out[isset($accounts[$key]) ? $accounts[$key]->mailbox($key2) : $key2] = $val2;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $out;
|
|
}
|
|
|
|
/**
|
|
* All other calls to this class are routed to the underlying
|
|
* Horde_Imap_Client_Base object.
|
|
*
|
|
* @param string $method Method name.
|
|
* @param array $params Method parameters.
|
|
*
|
|
* @return mixed The return from the requested method.
|
|
* @throws BadMethodCallException
|
|
* @throws IMP_Imap_Exception
|
|
*/
|
|
public function __call($method, $params)
|
|
{
|
|
global $injector;
|
|
|
|
if (!$this->init) {
|
|
/* Fallback for these methods. */
|
|
switch ($method) {
|
|
case 'getIdsOb':
|
|
$ob = new Horde_Imap_Client_Ids();
|
|
call_user_func_array(array($ob, 'add'), $params);
|
|
return $ob;
|
|
}
|
|
|
|
throw new Horde_Exception_AuthenticationFailure(
|
|
'IMP is marked as authenticated, but no credentials can be found in the session.',
|
|
Horde_Auth::REASON_SESSION
|
|
);
|
|
}
|
|
|
|
switch ($method) {
|
|
case 'append':
|
|
case 'createMailbox':
|
|
case 'deleteMailbox':
|
|
case 'expunge':
|
|
case 'fetch':
|
|
case 'getACL':
|
|
case 'getMetadata':
|
|
case 'getMyACLRights':
|
|
case 'getQuota':
|
|
case 'getQuotaRoot':
|
|
case 'getSyncToken':
|
|
case 'setMetadata':
|
|
case 'setQuota':
|
|
case 'store':
|
|
case 'subscribeMailbox':
|
|
case 'sync':
|
|
case 'thread':
|
|
// Horde_Imap_Client_Mailbox: these calls all have the mailbox as
|
|
// their first parameter.
|
|
$params[0] = IMP_Mailbox::getImapMboxOb($params[0]);
|
|
break;
|
|
|
|
case 'copy':
|
|
case 'renameMailbox':
|
|
// These calls may hit multiple servers.
|
|
$source = IMP_Mailbox::get($params[0]);
|
|
$dest = IMP_Mailbox::get($params[1]);
|
|
if ($source->remote_account != $dest->remote_account) {
|
|
return call_user_func_array(array($this, '_' . $method), $params);
|
|
}
|
|
|
|
// Horde_Imap_Client_Mailbox: these calls all have the mailbox as
|
|
// their first two parameters.
|
|
$params[0] = $source->imap_mbox_ob;
|
|
$params[1] = $dest->imap_mbox_ob;
|
|
break;
|
|
|
|
case 'getNamespaces':
|
|
if (isset($this->_temp['ns'])) {
|
|
return $this->_temp['ns'];
|
|
}
|
|
$nsconfig = $this->config->namespace;
|
|
$params[0] = is_null($nsconfig) ? array() : $nsconfig;
|
|
$params[1] = array('ob_return' => true);
|
|
break;
|
|
|
|
case 'impStatus':
|
|
/* Internal method: allows status call with array of mailboxes,
|
|
* guaranteeing they are all on this server. */
|
|
$params[0] = IMP_Mailbox::getImapMboxOb($params[0]);
|
|
$method = 'status';
|
|
break;
|
|
|
|
case 'openMailbox':
|
|
$mbox = IMP_Mailbox::get($params[0]);
|
|
if ($mbox->search) {
|
|
/* Can't open a search mailbox. */
|
|
return;
|
|
}
|
|
$params[0] = $mbox->imap_mbox_ob;
|
|
break;
|
|
|
|
case 'search':
|
|
$params = call_user_func_array(array($this, '_search'), $params);
|
|
break;
|
|
|
|
case 'status':
|
|
if (is_array($params[0])) {
|
|
return $this->_status($params);
|
|
}
|
|
$params[0] = IMP_Mailbox::getImapMboxOb($params[0]);
|
|
break;
|
|
|
|
default:
|
|
if (!method_exists($this->_ob, $method)) {
|
|
throw new BadMethodCallException(
|
|
sprintf('%s: Invalid method call "%s".', __CLASS__, $method)
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
|
|
try {
|
|
$result = call_user_func_array(array($this->_ob, $method), $params);
|
|
} catch (Horde_Imap_Client_Exception $e) {
|
|
$error = new IMP_Imap_Exception($e);
|
|
|
|
if (!$error->authError()) {
|
|
switch ($method) {
|
|
case 'getNamespaces':
|
|
return new Horde_Imap_Client_Namespace_List();
|
|
}
|
|
}
|
|
|
|
Horde::log(
|
|
new Exception(
|
|
sprintf('[%s] %s', $method, $e->raw_msg),
|
|
$e->getCode(),
|
|
$e
|
|
),
|
|
'WARN'
|
|
);
|
|
|
|
throw $error;
|
|
}
|
|
|
|
/* Special handling for various methods. */
|
|
switch ($method) {
|
|
case 'createMailbox':
|
|
case 'deleteMailbox':
|
|
case 'renameMailbox':
|
|
$injector->getInstance('IMP_Mailbox_SessionCache')->expire(
|
|
null,
|
|
// Mailbox is first parameter.
|
|
IMP_Mailbox::get($params[0])
|
|
);
|
|
break;
|
|
|
|
case 'getNamespaces':
|
|
$this->_temp['ns'] = $result;
|
|
break;
|
|
|
|
case 'login':
|
|
if (!$this->_ob->getParam('imp:login')) {
|
|
/* Check for POP3 UIDL support. */
|
|
if ($this->isPop3() && !$this->queryCapability('UIDL')) {
|
|
Horde::log(
|
|
sprintf(
|
|
'The POP3 server does not support the REQUIRED UIDL capability. [server key: %s]',
|
|
$this->server_key
|
|
),
|
|
'CRIT'
|
|
);
|
|
throw new Horde_Exception_AuthenticationFailure(
|
|
_("The mail server is not currently avaliable."),
|
|
Horde_Auth::REASON_MESSAGE
|
|
);
|
|
}
|
|
|
|
$this->_ob->setParam('imp:login', true);
|
|
$this->_changed = true;
|
|
}
|
|
break;
|
|
|
|
case 'setACL':
|
|
$injector->getInstance('IMP_Mailbox_SessionCache')->expire(
|
|
IMP_Mailbox_SessionCache::CACHE_ACL,
|
|
IMP_Mailbox::get($params[0])
|
|
);
|
|
break;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Prepares an IMAP search query. Needed because certain configuration
|
|
* parameters may need to be dynamically altered before passed to the
|
|
* Imap_Client object.
|
|
*
|
|
* @param string $mailbox The mailbox to search.
|
|
* @param Horde_Imap_Client_Search_Query $query The search query object.
|
|
* @param array $opts Additional options.
|
|
*
|
|
* @return array Parameters to use in the search() call.
|
|
*/
|
|
protected function _search($mailbox, $query = null, array $opts = array())
|
|
{
|
|
$mailbox = IMP_Mailbox::get($mailbox);
|
|
|
|
if (!empty($opts['sort']) && $mailbox->access_sort) {
|
|
/* If doing a from/to search, use display sorting if possible.
|
|
* Although there is a fallback to a PHP-based display sort, for
|
|
* performance reasons only do a display sort if it is supported
|
|
* on the server. */
|
|
foreach ($opts['sort'] as $key => $val) {
|
|
switch ($val) {
|
|
case Horde_Imap_Client::SORT_FROM:
|
|
$opts['sort'][$key] = Horde_Imap_Client::SORT_DISPLAYFROM_FALLBACK;
|
|
break;
|
|
|
|
case Horde_Imap_Client::SORT_TO:
|
|
$opts['sort'][$key] = Horde_Imap_Client::SORT_DISPLAYTO_FALLBACK;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!is_null($query)) {
|
|
$query->charset('UTF-8', false);
|
|
}
|
|
|
|
return array($mailbox->imap_mbox_ob, $query, $opts);
|
|
}
|
|
|
|
/**
|
|
* Handle copy() calls that hit multiple servers.
|
|
*
|
|
* @see Horde_Imap_Client_Base#copy()
|
|
*/
|
|
protected function _copy()
|
|
{
|
|
global $injector;
|
|
|
|
$args = func_get_args();
|
|
$imap_factory = $injector->getInstance('IMP_Factory_Imap');
|
|
$source_imap = $imap_factory->create($args[0]);
|
|
$dest_imap = $imap_factory->create($args[1]);
|
|
|
|
$create = !empty($args[2]['create']);
|
|
$ids = isset($args[2]['ids'])
|
|
? $args[2]['ids']
|
|
: $source_imap->getIdsOb(Horde_Imap_Client_Ids::ALL);
|
|
$move = !empty($args[2]['move']);
|
|
$retval = true;
|
|
|
|
$query = new Horde_Imap_Client_Fetch_Query();
|
|
$query->fullText(array(
|
|
'peek' => true
|
|
));
|
|
|
|
foreach ($this->getSlices($args[0], $ids) as $val) {
|
|
try {
|
|
$res = $source_imap->fetch($args[0], $query, array(
|
|
'ids' => $val,
|
|
'nocache' => true
|
|
));
|
|
|
|
$append = array();
|
|
foreach ($res as $msg) {
|
|
$append[] = array(
|
|
'data' => $msg->getFullMsg(true)
|
|
);
|
|
}
|
|
|
|
$dest_imap->append($args[1], $append, array(
|
|
'create' => $create
|
|
));
|
|
|
|
if ($move) {
|
|
$source_imap->expunge($args[0], array(
|
|
'delete' => true,
|
|
'ids' => $val
|
|
));
|
|
}
|
|
} catch (IMP_Imap_Exception $e) {
|
|
$retval = false;
|
|
}
|
|
}
|
|
|
|
return $retval;
|
|
}
|
|
|
|
/**
|
|
* Handle copy() calls. This call may hit multiple servers, so
|
|
* need to handle separately from other IMAP calls.
|
|
*
|
|
* @see Horde_Imap_Client_Base#renameMailbox()
|
|
*/
|
|
protected function _renameMailbox()
|
|
{
|
|
$args = func_get_args();
|
|
$source = IMP_Mailbox::get($args[0]);
|
|
|
|
if ($source->create() && $this->copy($source, $args[1])) {
|
|
$source->delete();
|
|
} else {
|
|
throw new IMP_Imap_Exception(_("Could not move all messages between mailboxes, so the original mailbox was not removed."));
|
|
}
|
|
}
|
|
|
|
/* Static methods. */
|
|
|
|
/**
|
|
* Loads the IMP server configuration from backends.php.
|
|
*
|
|
* @param string $server Returns this labeled entry only.
|
|
*
|
|
* @return mixed If $server is set return this entry; else, return the
|
|
* entire servers array. Returns false on error.
|
|
*/
|
|
static public function loadServerConfig($server = null)
|
|
{
|
|
global $registry;
|
|
|
|
if (empty(self::$_backends)) {
|
|
try {
|
|
$s = $registry->loadConfigFile('backends.php', 'servers', 'imp')->config['servers'];
|
|
} catch (Horde_Exception $e) {
|
|
Horde::log($e, 'ERR');
|
|
return false;
|
|
}
|
|
|
|
foreach ($s as $key => $val) {
|
|
if (empty($val['disabled'])) {
|
|
self::$_backends[$key] = new IMP_Imap_Config($val);
|
|
}
|
|
}
|
|
}
|
|
|
|
return is_null($server)
|
|
? self::$_backends
|
|
: (isset(self::$_backends[$server]) ? self::$_backends[$server] : false);
|
|
}
|
|
|
|
/* Serializable methods. */
|
|
|
|
/**
|
|
*/
|
|
public function serialize()
|
|
{
|
|
return $GLOBALS['injector']->getInstance('Horde_Pack')->pack(
|
|
array(
|
|
$this->_ob,
|
|
$this->_id,
|
|
$this->_config
|
|
),
|
|
array(
|
|
'compression' => false,
|
|
'phpob' => true
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
public function unserialize($data)
|
|
{
|
|
list(
|
|
$this->_ob,
|
|
$this->_id,
|
|
$this->_config
|
|
) = $GLOBALS['injector']->getInstance('Horde_Pack')->unpack($data);
|
|
}
|
|
|
|
}
|