Copyright 2008-2012 Horde LLC (http://www.horde.org/)
See the enclosed file COPYING for license information (LGPL). If you
did not receive this file, see http://www.horde.org/licenses/lgpl21.
/** * Checks whether a mailbox exists and is set up properly. * * @param string $user The name of the mailbox to check. * @param string $domain The mailbox' domain. * * @return boolean True if the mailbox exists. * @throws Vilma_Exception if the mailbox doesn't exist. */ public function checkMailbox($user, $domain) { if (!$this->_imap->listMailboxes($this->_params['userhierarchy'] . $user . '@' . $domain)) { throw new Vilma_Exception(sprintf(_("Mailbox \"%s\" does not exist."), $user . '@' . $domain)); } return true; }
/** * Query the validity of a charset. * * @param string $charset The charset to query. * @param boolean $cached If true, only query cached values. * * @return boolean True if the charset is valid for searching. */ public function query($charset, $cached = false) { $charset = Horde_String::upper($charset); if (isset($this->_charsets[$charset])) { return $this->_charsets[$charset]; } elseif ($cached) { return null; } if (!$this->_baseob) { throw new RuntimeException('Base object needs to be defined to query for charset.'); } /* Use a dummy search query and search for BADCHARSET response. */ $query = new Horde_Imap_Client_Search_Query(); $query->charset($charset, false); $query->ids($this->_baseob->getIdsOb(1, true)); $query->text('a'); try { $this->_baseob->search('INBOX', $query, array('nocache' => true, 'sequence' => true)); $this->_charsets[$charset] = true; } catch (Horde_Imap_Client_Exception $e) { $this->_charsets[$charset] = $e->getCode() !== Horde_Imap_Client_Exception::BADCHARSET; } $this->notify(); return $this->_charsets[$charset]; }
/** * Gets the raw text for one section of the message. * * @param integer $id The ID of the MIME part. * @param array $options Additional options: * - decode: (boolean) Attempt to decode the bodypart on the remote * server. If successful, sets self::$_lastBodyPartDecode to * the content-type of the decoded data. * DEFAULT: No * - length: (integer) If set, only download this many bytes of the * bodypart from the server. * DEFAULT: All data is retrieved. * - mimeheaders: (boolean) Include the MIME headers also? * DEFAULT: No * - stream: (boolean) If true, return a stream. * DEFAULT: No * * @return mixed The text of the part or a stream resource if 'stream' * is true. * @todo Simplify by removing 'mimeheaders' parameter (not used). */ public function getBodyPart($id, $options) { $options = array_merge(array('decode' => false, 'mimeheaders' => false, 'stream' => false), $options); $this->_lastBodyPartDecode = null; $query = new Horde_Imap_Client_Fetch_Query(); if (!isset($options['length']) || !empty($options['length'])) { $bodypart_params = array('decode' => true, 'peek' => true); if (isset($options['length'])) { $bodypart_params['start'] = 0; $bodypart_params['length'] = $options['length']; } $query->bodyPart($id, $bodypart_params); } if (!empty($options['mimeheaders'])) { $query->mimeHeader($id, array('peek' => true)); } $fetch_res = $this->_imap->fetch($this->_mbox, $query, array('ids' => new Horde_Imap_Client_Ids(array($this->uid)))); if (empty($options['mimeheaders'])) { $this->_lastBodyPartDecode = $fetch_res[$this->uid]->getBodyPartDecode($id); return $fetch_res[$this->uid]->getBodyPart($id, $options['stream']); } elseif (empty($options['stream'])) { return $fetch_res[$this->uid]->getMimeHeader($id) . $fetch_res[$this->uid]->getBodyPart($id); } else { $swrapper = new Horde_Support_CombineStream(array($fetch_res[$this->uid]->getMimeHeader($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM), $fetch_res[$this->uid]->getBodyPart($id, true))); return $swrapper->fopen(); } }
/** * Returns a unique identifier for the current mailbox status. * * @param Horde_Imap_Client_Base $base_ob The base driver object. * @param mixed $mailbox A mailbox. Either a * Horde_Imap_Client_Mailbox * object or a string (UTF-8). * @param boolean $condstore Is CONDSTORE enabled? * @param array $addl Additional cache info to add to * the cache ID string. * * @return string The cache ID string, which will change when the * composition of the mailbox changes. The uidvalidity * will always be the first element, and will be delimited * by the '|' character. * * @throws Horde_Imap_Client_Exception */ public static function getCacheId($base_ob, $mailbox, $condstore, array $addl = array()) { $query = Horde_Imap_Client::STATUS_UIDVALIDITY | Horde_Imap_Client::STATUS_MESSAGES | Horde_Imap_Client::STATUS_UIDNEXT; /* Use MODSEQ as cache ID if CONDSTORE extension is available. */ if ($condstore) { $query |= Horde_Imap_Client::STATUS_HIGHESTMODSEQ; } else { $query |= Horde_Imap_Client::STATUS_UIDNEXT_FORCE; } $status = $base_ob->status($mailbox, $query); if (empty($status['highestmodseq'])) { $parts = array('V' . $status['uidvalidity'], 'U' . $status['uidnext'], 'M' . $status['messages']); } else { $parts = array('V' . $status['uidvalidity'], 'H' . $status['highestmodseq']); } return implode('|', array_merge($parts, $addl)); }
/** * Delete messages in the cache. * * @param string $mailbox An IMAP mailbox string. * @param array $uids The list of message UIDs to delete. */ public function deleteMsgs($mailbox, $uids) { if (empty($uids)) { return; } $mailbox = strval($mailbox); $this->_backend->deleteMsgs($mailbox, $uids); if ($this->_debug) { $this->_debug->info(sprintf('CACHE: Deleted messages [%s; %s]', $mailbox, $this->_baseob->getIdsOb($uids)->tostring_sort)); } }
/** * Delete a mailbox from the cache. * * @param string $mbox The mailbox to delete. */ protected function _deleteMailbox($mbox) { foreach (array_merge(array_keys(array_flip($this->_slicemap[$mbox]['s'])), array('slicemap')) as $slice) { $cid = $this->_getCid($mbox, $slice); $this->_cache->expire($cid); unset($this->_loaded[$cid]); } unset($this->_data[$mbox], $this->_slicemap[$mbox], $this->_update[$mbox]); if ($this->_params['debug']) { $this->_base->writeDebug('CACHE: Deleted mailbox (mailbox: ' . $mbox . ")\n", Horde_Imap_Client::DEBUG_INFO); } }
/** */ protected function _initCache($current = false) { return parent::_initCache($current) && $this->_capability('UIDL'); }
/** * @param array $opts Options: * - decrement: (boolean) If true, decrement the message count. * - pipeline: (Horde_Imap_Client_Interaction_Pipeline) Pipeline object. */ protected function _deleteMsgs(Horde_Imap_Client_Mailbox $mailbox, Horde_Imap_Client_Ids $ids, array $opts = array()) { /* If there are pending FETCH cache writes, we need to write them * before the UID -> sequence number mapping changes. */ if (isset($opts['pipeline'])) { $this->_updateCache($opts['pipeline']->fetch); } $res = parent::_deleteMsgs($mailbox, $ids); if (isset($this->_temp['expunged'])) { $this->_temp['expunged']->add($res); } if (!empty($opts['decrement'])) { $mbox_ob = $this->_mailboxOb(); $mbox_ob->setStatus(Horde_Imap_Client::STATUS_MESSAGES, $mbox_ob->getStatus(Horde_Imap_Client::STATUS_MESSAGES) - count($ids)); } }
/** */ protected function _syncMailbox() { /* QRESYNC would have already synced the mailbox. */ if (empty($this->_init['enabled']['QRESYNC'])) { parent::_syncMailbox(); } }
/** * Constructor. * * @param Horde_Imap_Client_Base $base_ob Base driver object. * @param mixed $mailbox Mailbox to sync. * @param array $sync Token sync data. * @param array $curr Current sync data. * @param integer $criteria Mask of criteria to return. * @param Horde_Imap_Client_Ids $ids List of known UIDs. * * @throws Horde_Imap_Client_Exception * @throws Horde_Imap_Client_Exception_Sync */ public function __construct(Horde_Imap_Client_Base $base_ob, $mailbox, $sync, $curr, $criteria, $ids) { foreach (self::$map as $key => $val) { if (isset($sync[$key])) { $this->{$val} = $sync[$key]; } } /* Check uidvalidity. */ if (!$this->uidvalidity || $curr['V'] != $this->uidvalidity) { throw new Horde_Imap_Client_Exception_Sync('UIDs in cached mailbox have changed.', Horde_Imap_Client_Exception_Sync::UIDVALIDITY_CHANGED); } $this->mailbox = $mailbox; /* This was a UIDVALIDITY check only. */ if (!$criteria) { return; } $sync_all = $criteria & Horde_Imap_Client::SYNC_ALL; /* New messages. */ if ($sync_all || $criteria & Horde_Imap_Client::SYNC_NEWMSGS || $criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS) { $this->newmsgs = empty($this->uidnext) ? !empty($curr['U']) : !empty($curr['U']) && $curr['U'] > $this->uidnext; if ($this->newmsgs && ($sync_all || $criteria & Horde_Imap_Client::SYNC_NEWMSGSUIDS)) { $new_ids = empty($this->uidnext) ? Horde_Imap_Client_Ids::ALL : $this->uidnext . ':' . $curr['U']; $squery = new Horde_Imap_Client_Search_Query(); $squery->ids(new Horde_Imap_Client_Ids($new_ids)); $sres = $base_ob->search($mailbox, $squery); $this->_newmsgsuids = $sres['match']; } } /* Do single status call to get all necessary data. */ if ($this->highestmodseq && ($sync_all || $criteria & Horde_Imap_Client::SYNC_FLAGS || $criteria & Horde_Imap_Client::SYNC_FLAGSUIDS || $criteria & Horde_Imap_Client::SYNC_VANISHED || $criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS)) { $status_sync = $base_ob->status($mailbox, Horde_Imap_Client::STATUS_SYNCMODSEQ | Horde_Imap_Client::STATUS_SYNCFLAGUIDS | Horde_Imap_Client::STATUS_SYNCVANISHED); if (!is_null($ids)) { $ids = $base_ob->resolveIds($mailbox, $ids); } } /* Flag changes. */ if ($sync_all || $criteria & Horde_Imap_Client::SYNC_FLAGS) { $this->flags = $this->highestmodseq ? $this->highestmodseq != $curr['H'] : true; } if ($sync_all || $criteria & Horde_Imap_Client::SYNC_FLAGSUIDS) { if ($this->highestmodseq) { if ($this->highestmodseq == $status_sync['syncmodseq']) { $this->_flagsuids = is_null($ids) ? $status_sync['syncflaguids'] : $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncflaguids']->ids)); } else { $squery = new Horde_Imap_Client_Search_Query(); $squery->modseq($this->highestmodseq + 1); $sres = $base_ob->search($mailbox, $squery, array('ids' => $ids)); $this->_flagsuids = $sres['match']; } } else { /* Without MODSEQ, need to mark all FLAGS as changed. */ $this->_flagsuids = $base_ob->resolveIds($mailbox, is_null($ids) ? $base_ob->getIdsOb(Horde_Imap_Client_Ids::ALL) : $ids); } } /* Vanished messages. */ if ($sync_all || $criteria & Horde_Imap_Client::SYNC_VANISHED || $criteria & Horde_Imap_Client::SYNC_VANISHEDUIDS) { if ($this->highestmodseq && $this->highestmodseq == $status_sync['syncmodseq']) { $vanished = is_null($ids) ? $status_sync['syncvanished'] : $base_ob->getIdsOb(array_intersect($ids->ids, $status_sync['syncvanished']->ids)); } else { $vanished = $base_ob->vanished($mailbox, $this->highestmodseq ? $this->highestmodseq : 1, array('ids' => $ids)); } $this->vanished = (bool) count($vanished); $this->_vanisheduids = $vanished; } }
/** * 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) { 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'); $error = new IMP_Imap_Exception($e); throw ($auth_e = $error->authException(false)) ? $auth_e : $error; } /* Special handling for various methods. */ switch ($method) { case 'createMailbox': case 'deleteMailbox': case 'renameMailbox': $injector->getInstance('IMP_Mailbox_SessionCache')->expire(null, 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; }
/** * Delete a set of authentication credentials. * * @param string $userId The userId to delete. * * @throws Horde_Auth_Exception */ public function removeUser($userId) { if (!empty($this->_params['domain_field']) && $this->_params['domain_field'] != 'none') { list($name, $domain) = explode('@', $userId); /* Build the SQL query. */ $query = sprintf('DELETE FROM %s WHERE %s = ? and %s = ?', $this->_params['table'], $this->_params['username_field'], $this->_params['domain_field']); $values = array($name, $domain); $query2 = 'DELETE FROM virtual WHERE dest = ?'; $values2 = array($userId); try { $this->_db->delete($query, $values); $this->_db->delete($query2, $values2); } catch (Horde_Db_Exception $e) { throw new Horde_Auth_Exception($e); } } else { parent::removeUser($userId); } /* Set ACL for mailbox deletion. */ list($admin) = explode('@', $this->_params['cyradmin']); $mailbox = $this->_params['userhierarchy']; try { $this->_imap->setACL($mailbox, $admin, array('rights' => 'lrswipcda')); $this->_imap->deleteMailbox($mailbox); } catch (Horde_Imap_Client_Exception $e) { throw new Horde_Auth_Exception($e); } }