PageRenderTime 64ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/horde/framework/Horde/Imap/Client/Base.php

http://github.com/moodle/moodle
PHP | 4082 lines | 1821 code | 428 blank | 1833 comment | 255 complexity | 352de1c56e0596fa41d6654dba822b6c MD5 | raw file
Possible License(s): MIT, AGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, Apache-2.0, LGPL-2.1, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Copyright 2008-2017 Horde LLC (http://www.horde.org/)
  4. *
  5. * See the enclosed file LICENSE for license information (LGPL). If you
  6. * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  7. *
  8. * @category Horde
  9. * @copyright 2008-2017 Horde LLC
  10. * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
  11. * @package Imap_Client
  12. */
  13. /**
  14. * An abstracted API interface to IMAP backends supporting the IMAP4rev1
  15. * protocol (RFC 3501).
  16. *
  17. * @author Michael Slusarz <slusarz@horde.org>
  18. * @category Horde
  19. * @copyright 2008-2017 Horde LLC
  20. * @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
  21. * @package Imap_Client
  22. *
  23. * @property-read Horde_Imap_Client_Base_Alert $alerts_ob
  24. The alert reporting object (@since 2.26.0)
  25. * @property-read Horde_Imap_Client_Data_Capability $capability
  26. * A capability object. (@since 2.24.0)
  27. * @property-read Horde_Imap_Client_Data_SearchCharset $search_charset
  28. * A search charset object. (@since 2.24.0)
  29. * @property-read Horde_Imap_Client_Url $url The URL object for the current
  30. * connection parameters (@since 2.24.0)
  31. */
  32. abstract class Horde_Imap_Client_Base
  33. implements Serializable, SplObserver
  34. {
  35. /** Serialized version. */
  36. const VERSION = 3;
  37. /** Cache names for miscellaneous data. */
  38. const CACHE_MODSEQ = '_m';
  39. const CACHE_SEARCH = '_s';
  40. /* @since 2.9.0 */
  41. const CACHE_SEARCHID = '_i';
  42. /** Cache names used exclusively within this class. @since 2.11.0 */
  43. const CACHE_DOWNGRADED = 'HICdg';
  44. /**
  45. * The list of fetch fields that can be cached, and their cache names.
  46. *
  47. * @var array
  48. */
  49. public $cacheFields = array(
  50. Horde_Imap_Client::FETCH_ENVELOPE => 'HICenv',
  51. Horde_Imap_Client::FETCH_FLAGS => 'HICflags',
  52. Horde_Imap_Client::FETCH_HEADERS => 'HIChdrs',
  53. Horde_Imap_Client::FETCH_IMAPDATE => 'HICdate',
  54. Horde_Imap_Client::FETCH_SIZE => 'HICsize',
  55. Horde_Imap_Client::FETCH_STRUCTURE => 'HICstruct'
  56. );
  57. /**
  58. * Has the internal configuration changed?
  59. *
  60. * @var boolean
  61. */
  62. public $changed = false;
  63. /**
  64. * Horde_Imap_Client is optimized for short (i.e. 1 seconds) scripts. It
  65. * makes heavy use of mailbox caching to save on server accesses. This
  66. * property should be set to false for long-running scripts, or else
  67. * status() data may not reflect the current state of the mailbox on the
  68. * server.
  69. *
  70. * @since 2.14.0
  71. *
  72. * @var boolean
  73. */
  74. public $statuscache = true;
  75. /**
  76. * Alerts reporting object.
  77. *
  78. * @var Horde_Imap_Client_Base_Alerts
  79. */
  80. protected $_alerts;
  81. /**
  82. * The Horde_Imap_Client_Cache object.
  83. *
  84. * @var Horde_Imap_Client_Cache
  85. */
  86. protected $_cache = null;
  87. /**
  88. * Connection to the IMAP server.
  89. *
  90. * @var Horde\Socket\Client
  91. */
  92. protected $_connection = null;
  93. /**
  94. * The debug object.
  95. *
  96. * @var Horde_Imap_Client_Base_Debug
  97. */
  98. protected $_debug = null;
  99. /**
  100. * The default ports to use for a connection.
  101. * First element is non-secure, second is SSL.
  102. *
  103. * @var array
  104. */
  105. protected $_defaultPorts = array();
  106. /**
  107. * The fetch data object type to return.
  108. *
  109. * @var string
  110. */
  111. protected $_fetchDataClass = 'Horde_Imap_Client_Data_Fetch';
  112. /**
  113. * Cached server data.
  114. *
  115. * @var array
  116. */
  117. protected $_init;
  118. /**
  119. * Is there an active authenticated connection to the IMAP Server?
  120. *
  121. * @var boolean
  122. */
  123. protected $_isAuthenticated = false;
  124. /**
  125. * The current mailbox selection mode.
  126. *
  127. * @var integer
  128. */
  129. protected $_mode = 0;
  130. /**
  131. * Hash containing connection parameters.
  132. * This hash never changes.
  133. *
  134. * @var array
  135. */
  136. protected $_params = array();
  137. /**
  138. * The currently selected mailbox.
  139. *
  140. * @var Horde_Imap_Client_Mailbox
  141. */
  142. protected $_selected = null;
  143. /**
  144. * Temp array (destroyed at end of process).
  145. *
  146. * @var array
  147. */
  148. protected $_temp = array();
  149. /**
  150. * Constructor.
  151. *
  152. * @param array $params Configuration parameters:
  153. * <pre>
  154. * - cache: (array) If set, caches data from fetch(), search(), and
  155. * thread() calls. Requires the horde/Cache package to be
  156. * installed. The array can contain the following keys (see
  157. * Horde_Imap_Client_Cache for default values):
  158. * - backend: [REQUIRED (or cacheob)] (Horde_Imap_Client_Cache_Backend)
  159. * Backend cache driver [@since 2.9.0].
  160. * - fetch_ignore: (array) A list of mailboxes to ignore when storing
  161. * fetch data.
  162. * - fields: (array) The fetch criteria to cache. If not defined, all
  163. * cacheable data is cached. The following is a list of
  164. * criteria that can be cached:
  165. * - Horde_Imap_Client::FETCH_ENVELOPE
  166. * - Horde_Imap_Client::FETCH_FLAGS
  167. * Only if server supports CONDSTORE extension
  168. * - Horde_Imap_Client::FETCH_HEADERS
  169. * Only for queries that specifically request caching
  170. * - Horde_Imap_Client::FETCH_IMAPDATE
  171. * - Horde_Imap_Client::FETCH_SIZE
  172. * - Horde_Imap_Client::FETCH_STRUCTURE
  173. * - capability_ignore: (array) A list of IMAP capabilites to ignore, even
  174. * if they are supported on the server.
  175. * DEFAULT: No supported capabilities are ignored.
  176. * - comparator: (string) The search comparator to use instead of the
  177. * default server comparator. See setComparator() for
  178. * format.
  179. * DEFAULT: Use the server default
  180. * - context: (array) Any context parameters passed to
  181. * stream_create_context(). @since 2.27.0
  182. * - debug: (string) If set, will output debug information to the stream
  183. * provided. The value can be any PHP supported wrapper that can
  184. * be opened via PHP's fopen() function.
  185. * DEFAULT: No debug output
  186. * - hostspec: (string) The hostname or IP address of the server.
  187. * DEFAULT: 'localhost'
  188. * - id: (array) Send ID information to the server (only if server
  189. * supports the ID extension). An array with the keys as the fields
  190. * to send and the values being the associated values. See RFC 2971
  191. * [3.3] for a list of standard field values.
  192. * DEFAULT: No info sent to server
  193. * - lang: (array) A list of languages (in priority order) to be used to
  194. * display human readable messages.
  195. * DEFAULT: Messages output in IMAP server default language
  196. * - password: (mixed) The user password. Either a string or a
  197. * Horde_Imap_Client_Base_Password object [@since 2.14.0].
  198. * - port: (integer) The server port to which we will connect.
  199. * DEFAULT: 143 (imap or imap w/TLS) or 993 (imaps)
  200. * - secure: (string) Use SSL or TLS to connect. Values:
  201. * - false (No encryption)
  202. * - 'ssl' (Auto-detect SSL version)
  203. * - 'sslv2' (Force SSL version 3)
  204. * - 'sslv3' (Force SSL version 2)
  205. * - 'tls' (TLS; started via protocol-level negotation over
  206. * unencrypted channel; RECOMMENDED way of initiating secure
  207. * connection)
  208. * - 'tlsv1' (TLS direct version 1.x connection to server) [@since
  209. * 2.16.0]
  210. * - true (TLS if available/necessary) [@since 2.15.0]
  211. * DEFAULT: false
  212. * - timeout: (integer) Connection timeout, in seconds.
  213. * DEFAULT: 30 seconds
  214. * - username: (string) [REQUIRED] The username.
  215. * - authusername (string) The username used for SASL authentication.
  216. * If specified this is the user name whose password is used
  217. * (e.g. administrator).
  218. * Only valid for RFC 2595/4616 - PLAIN SASL mechanism.
  219. * DEFAULT: the same value provided in the username parameter.
  220. * </pre>
  221. */
  222. public function __construct(array $params = array())
  223. {
  224. if (!isset($params['username'])) {
  225. throw new InvalidArgumentException('Horde_Imap_Client requires a username.');
  226. }
  227. $this->_setInit();
  228. // Default values.
  229. $params = array_merge(array(
  230. 'context' => array(),
  231. 'hostspec' => 'localhost',
  232. 'secure' => false,
  233. 'timeout' => 30
  234. ), array_filter($params));
  235. if (!isset($params['port']) && strpos($params['hostspec'], 'unix://') !== 0) {
  236. $params['port'] = (!empty($params['secure']) && in_array($params['secure'], array('ssl', 'sslv2', 'sslv3'), true))
  237. ? $this->_defaultPorts[1]
  238. : $this->_defaultPorts[0];
  239. }
  240. if (empty($params['cache'])) {
  241. $params['cache'] = array('fields' => array());
  242. } elseif (empty($params['cache']['fields'])) {
  243. $params['cache']['fields'] = $this->cacheFields;
  244. } else {
  245. $params['cache']['fields'] = array_flip($params['cache']['fields']);
  246. }
  247. if (empty($params['cache']['fetch_ignore'])) {
  248. $params['cache']['fetch_ignore'] = array();
  249. }
  250. $this->_params = $params;
  251. if (isset($params['password'])) {
  252. $this->setParam('password', $params['password']);
  253. }
  254. $this->changed = true;
  255. $this->_initOb();
  256. }
  257. /**
  258. * Get encryption key.
  259. *
  260. * @deprecated Pass callable into 'password' parameter instead.
  261. *
  262. * @return string The encryption key.
  263. */
  264. protected function _getEncryptKey()
  265. {
  266. if (is_callable($ekey = $this->getParam('encryptKey'))) {
  267. return call_user_func($ekey);
  268. }
  269. throw new InvalidArgumentException('encryptKey parameter is not a valid callback.');
  270. }
  271. /**
  272. * Do initialization tasks.
  273. */
  274. protected function _initOb()
  275. {
  276. register_shutdown_function(array($this, 'shutdown'));
  277. $this->_alerts = new Horde_Imap_Client_Base_Alerts();
  278. // @todo: Remove (BC)
  279. $this->_alerts->attach($this);
  280. $this->_debug = ($debug = $this->getParam('debug'))
  281. ? new Horde_Imap_Client_Base_Debug($debug)
  282. : new Horde_Support_Stub();
  283. // @todo: Remove (BC purposes)
  284. if (isset($this->_init['capability']) &&
  285. !is_object($this->_init['capability'])) {
  286. $this->_setInit('capability');
  287. }
  288. foreach (array('capability', 'search_charset') as $val) {
  289. if (isset($this->_init[$val])) {
  290. $this->_init[$val]->attach($this);
  291. }
  292. }
  293. }
  294. /**
  295. * Shutdown actions.
  296. */
  297. public function shutdown()
  298. {
  299. try {
  300. $this->logout();
  301. } catch (Horde_Imap_Client_Exception $e) {
  302. }
  303. }
  304. /**
  305. * This object can not be cloned.
  306. */
  307. public function __clone()
  308. {
  309. throw new LogicException('Object cannot be cloned.');
  310. }
  311. /**
  312. */
  313. public function update(SplSubject $subject)
  314. {
  315. if (($subject instanceof Horde_Imap_Client_Data_Capability) ||
  316. ($subject instanceof Horde_Imap_Client_Data_SearchCharset)) {
  317. $this->changed = true;
  318. }
  319. /* @todo: BC - remove */
  320. if ($subject instanceof Horde_Imap_Client_Base_Alerts) {
  321. $this->_temp['alerts'][] = $subject->getLast()->alert;
  322. }
  323. }
  324. /**
  325. */
  326. public function serialize()
  327. {
  328. return serialize(array(
  329. 'i' => $this->_init,
  330. 'p' => $this->_params,
  331. 'v' => self::VERSION
  332. ));
  333. }
  334. /**
  335. */
  336. public function unserialize($data)
  337. {
  338. $data = @unserialize($data);
  339. if (!is_array($data) ||
  340. !isset($data['v']) ||
  341. ($data['v'] != self::VERSION)) {
  342. throw new Exception('Cache version change');
  343. }
  344. $this->_init = $data['i'];
  345. $this->_params = $data['p'];
  346. $this->_initOb();
  347. }
  348. /**
  349. */
  350. public function __get($name)
  351. {
  352. switch ($name) {
  353. case 'alerts_ob':
  354. return $this->_alerts;
  355. case 'capability':
  356. return $this->_capability();
  357. case 'search_charset':
  358. if (!isset($this->_init['search_charset'])) {
  359. $this->_init['search_charset'] = new Horde_Imap_Client_Data_SearchCharset();
  360. $this->_init['search_charset']->attach($this);
  361. }
  362. $this->_init['search_charset']->setBaseOb($this);
  363. return $this->_init['search_charset'];
  364. case 'url':
  365. $url = new Horde_Imap_Client_Url();
  366. $url->hostspec = $this->getParam('hostspec');
  367. $url->port = $this->getParam('port');
  368. $url->protocol = 'imap';
  369. return $url;
  370. }
  371. }
  372. /**
  373. * Set an initialization value.
  374. *
  375. * @param string $key The initialization key. If null, resets all keys.
  376. * @param mixed $val The cached value. If null, removes the key.
  377. */
  378. public function _setInit($key = null, $val = null)
  379. {
  380. if (is_null($key)) {
  381. $this->_init = array();
  382. } elseif (is_null($val)) {
  383. unset($this->_init[$key]);
  384. } else {
  385. switch ($key) {
  386. case 'capability':
  387. if ($ci = $this->getParam('capability_ignore')) {
  388. $ignored = array();
  389. foreach ($ci as $val2) {
  390. $c = explode('=', $val2);
  391. if ($val->query($c[0], isset($c[1]) ? $c[1] : null)) {
  392. $ignored[] = $val2;
  393. $val->remove($c[0], isset($c[1]) ? $c[1] : null);
  394. }
  395. }
  396. if ($this->_debug->debug && !empty($ignored)) {
  397. $this->_debug->info(sprintf(
  398. 'CONFIG: IGNORING these IMAP capabilities: %s',
  399. implode(', ', $ignored)
  400. ));
  401. }
  402. }
  403. $val->attach($this);
  404. break;
  405. }
  406. /* Nothing has changed. */
  407. if (isset($this->_init[$key]) && ($this->_init[$key] === $val)) {
  408. return;
  409. }
  410. $this->_init[$key] = $val;
  411. }
  412. $this->changed = true;
  413. }
  414. /**
  415. * Initialize the Horde_Imap_Client_Cache object, if necessary.
  416. *
  417. * @param boolean $current If true, we are going to update the currently
  418. * selected mailbox. Add an additional check to
  419. * see if caching is available in current
  420. * mailbox.
  421. *
  422. * @return boolean Returns true if caching is enabled.
  423. */
  424. protected function _initCache($current = false)
  425. {
  426. $c = $this->getParam('cache');
  427. if (empty($c['fields'])) {
  428. return false;
  429. }
  430. if (is_null($this->_cache)) {
  431. if (isset($c['backend'])) {
  432. $backend = $c['backend'];
  433. } elseif (isset($c['cacheob'])) {
  434. /* Deprecated */
  435. $backend = new Horde_Imap_Client_Cache_Backend_Cache($c);
  436. } else {
  437. return false;
  438. }
  439. $this->_cache = new Horde_Imap_Client_Cache(array(
  440. 'backend' => $backend,
  441. 'baseob' => $this,
  442. 'debug' => $this->_debug
  443. ));
  444. }
  445. return $current
  446. /* If UIDs are labeled as not sticky, don't cache since UIDs will
  447. * change on every access. */
  448. ? !($this->_mailboxOb()->getStatus(Horde_Imap_Client::STATUS_UIDNOTSTICKY))
  449. : true;
  450. }
  451. /**
  452. * Returns a value from the internal params array.
  453. *
  454. * @param string $key The param key.
  455. *
  456. * @return mixed The param value, or null if not found.
  457. */
  458. public function getParam($key)
  459. {
  460. /* Passwords may be stored encrypted. */
  461. switch ($key) {
  462. case 'password':
  463. if (isset($this->_params[$key]) &&
  464. ($this->_params[$key] instanceof Horde_Imap_Client_Base_Password)) {
  465. return $this->_params[$key]->getPassword();
  466. }
  467. // DEPRECATED
  468. if (!empty($this->_params['_passencrypt'])) {
  469. try {
  470. $secret = new Horde_Secret();
  471. return $secret->read($this->_getEncryptKey(), $this->_params['password']);
  472. } catch (Exception $e) {
  473. return null;
  474. }
  475. }
  476. break;
  477. }
  478. return isset($this->_params[$key])
  479. ? $this->_params[$key]
  480. : null;
  481. }
  482. /**
  483. * Sets a configuration parameter value.
  484. *
  485. * @param string $key The param key.
  486. * @param mixed $val The param value.
  487. */
  488. public function setParam($key, $val)
  489. {
  490. switch ($key) {
  491. case 'password':
  492. if ($val instanceof Horde_Imap_Client_Base_Password) {
  493. break;
  494. }
  495. // DEPRECATED: Encrypt password.
  496. try {
  497. $encrypt_key = $this->_getEncryptKey();
  498. if (strlen($encrypt_key)) {
  499. $secret = new Horde_Secret();
  500. $val = $secret->write($encrypt_key, $val);
  501. $this->_params['_passencrypt'] = true;
  502. }
  503. } catch (Exception $e) {}
  504. break;
  505. }
  506. $this->_params[$key] = $val;
  507. $this->changed = true;
  508. }
  509. /**
  510. * Returns the Horde_Imap_Client_Cache object used, if available.
  511. *
  512. * @return mixed Either the cache object or null.
  513. */
  514. public function getCache()
  515. {
  516. $this->_initCache();
  517. return $this->_cache;
  518. }
  519. /**
  520. * Returns the correct IDs object for use with this driver.
  521. *
  522. * @param mixed $ids Either self::ALL, self::SEARCH_RES, self::LARGEST,
  523. * Horde_Imap_Client_Ids object, array, or sequence
  524. * string.
  525. * @param boolean $sequence Are $ids message sequence numbers?
  526. *
  527. * @return Horde_Imap_Client_Ids The IDs object.
  528. */
  529. public function getIdsOb($ids = null, $sequence = false)
  530. {
  531. return new Horde_Imap_Client_Ids($ids, $sequence);
  532. }
  533. /**
  534. * Returns whether the IMAP server supports the given capability
  535. * (See RFC 3501 [6.1.1]).
  536. *
  537. * @deprecated Use $capability property instead.
  538. *
  539. * @param string $capability The capability string to query.
  540. *
  541. * @return mixed True if the server supports the queried capability,
  542. * false if it doesn't, or an array if the capability can
  543. * contain multiple values.
  544. */
  545. public function queryCapability($capability)
  546. {
  547. try {
  548. $c = $this->_capability();
  549. return ($out = $c->getParams($capability))
  550. ? $out
  551. : $c->query($capability);
  552. } catch (Horde_Imap_Client_Exception $e) {
  553. return false;
  554. }
  555. }
  556. /**
  557. * Get CAPABILITY information from the IMAP server.
  558. *
  559. * @deprecated Use $capability property instead.
  560. *
  561. * @return array The capability array.
  562. *
  563. * @throws Horde_Imap_Client_Exception
  564. */
  565. public function capability()
  566. {
  567. return $this->_capability()->toArray();
  568. }
  569. /**
  570. * Query server capability.
  571. *
  572. * Required because internal code can't call capability via magic method
  573. * directly - it may not exist yet, the creation code may call capability
  574. * recursively, and __get() doesn't allow recursive calls to the same
  575. * property (chicken/egg issue).
  576. *
  577. * @return mixed The capability object if no arguments provided. If
  578. * arguments are provided, they are passed to the query()
  579. * method and this value is returned.
  580. * @throws Horde_Imap_Client_Exception
  581. */
  582. protected function _capability()
  583. {
  584. if (!isset($this->_init['capability'])) {
  585. $this->_initCapability();
  586. }
  587. return ($args = func_num_args())
  588. ? $this->_init['capability']->query(func_get_arg(0), ($args > 1) ? func_get_arg(1) : null)
  589. : $this->_init['capability'];
  590. }
  591. /**
  592. * Retrieve capability information from the IMAP server.
  593. *
  594. * @throws Horde_Imap_Client_Exception
  595. */
  596. abstract protected function _initCapability();
  597. /**
  598. * Send a NOOP command (RFC 3501 [6.1.2]).
  599. *
  600. * @throws Horde_Imap_Client_Exception
  601. */
  602. public function noop()
  603. {
  604. if (!$this->_connection) {
  605. // NOOP can be called in the unauthenticated state.
  606. $this->_connect();
  607. }
  608. $this->_noop();
  609. }
  610. /**
  611. * Send a NOOP command.
  612. *
  613. * @throws Horde_Imap_Client_Exception
  614. */
  615. abstract protected function _noop();
  616. /**
  617. * Get the NAMESPACE information from the IMAP server (RFC 2342).
  618. *
  619. * @param array $additional If the server supports namespaces, any
  620. * additional namespaces to add to the
  621. * namespace list that are not broadcast by
  622. * the server. The namespaces must be UTF-8
  623. * strings.
  624. * @param array $opts Additional options:
  625. * - ob_return: (boolean) If true, returns a
  626. * Horde_Imap_Client_Namespace_List object instead of an
  627. * array.
  628. *
  629. * @return mixed A Horde_Imap_Client_Namespace_List object if
  630. * 'ob_return', is true. Otherwise, an array of namespace
  631. * objects (@deprecated) with the name as the key (UTF-8)
  632. * and the following values:
  633. * <pre>
  634. * - delimiter: (string) The namespace delimiter.
  635. * - hidden: (boolean) Is this a hidden namespace?
  636. * - name: (string) The namespace name (UTF-8).
  637. * - translation: (string) Returns the translated name of the namespace
  638. * (UTF-8). Requires RFC 5255 and a previous call to
  639. * setLanguage().
  640. * - type: (integer) The namespace type. Either:
  641. * - Horde_Imap_Client::NS_PERSONAL
  642. * - Horde_Imap_Client::NS_OTHER
  643. * - Horde_Imap_Client::NS_SHARED
  644. * </pre>
  645. *
  646. * @throws Horde_Imap_Client_Exception
  647. */
  648. public function getNamespaces(
  649. array $additional = array(), array $opts = array()
  650. )
  651. {
  652. $additional = array_map('strval', $additional);
  653. $sig = hash(
  654. 'md5',
  655. json_encode($additional) . intval(empty($opts['ob_return']))
  656. );
  657. if (isset($this->_init['namespace'][$sig])) {
  658. $ns = $this->_init['namespace'][$sig];
  659. } else {
  660. $this->login();
  661. $ns = $this->_getNamespaces();
  662. /* Skip namespaces if we have already auto-detected them. Also,
  663. * hidden namespaces cannot be empty. */
  664. $to_process = array_diff(array_filter($additional, 'strlen'), array_map('strlen', iterator_to_array($ns)));
  665. if (!empty($to_process)) {
  666. foreach ($this->listMailboxes($to_process, Horde_Imap_Client::MBOX_ALL, array('delimiter' => true)) as $key => $val) {
  667. $ob = new Horde_Imap_Client_Data_Namespace();
  668. $ob->delimiter = $val['delimiter'];
  669. $ob->hidden = true;
  670. $ob->name = $key;
  671. $ob->type = $ob::NS_SHARED;
  672. $ns[$val] = $ob;
  673. }
  674. }
  675. if (!count($ns)) {
  676. /* This accurately determines the namespace information of the
  677. * base namespace if the NAMESPACE command is not supported.
  678. * See: RFC 3501 [6.3.8] */
  679. $mbox = $this->listMailboxes('', Horde_Imap_Client::MBOX_ALL, array('delimiter' => true));
  680. $first = reset($mbox);
  681. $ob = new Horde_Imap_Client_Data_Namespace();
  682. $ob->delimiter = $first['delimiter'];
  683. $ns[''] = $ob;
  684. }
  685. $this->_init['namespace'][$sig] = $ns;
  686. $this->_setInit('namespace', $this->_init['namespace']);
  687. }
  688. if (!empty($opts['ob_return'])) {
  689. return $ns;
  690. }
  691. /* @todo Remove for 3.0 */
  692. $out = array();
  693. foreach ($ns as $key => $val) {
  694. $out[$key] = array(
  695. 'delimiter' => $val->delimiter,
  696. 'hidden' => $val->hidden,
  697. 'name' => $val->name,
  698. 'translation' => $val->translation,
  699. 'type' => $val->type
  700. );
  701. }
  702. return $out;
  703. }
  704. /**
  705. * Get the NAMESPACE information from the IMAP server.
  706. *
  707. * @return Horde_Imap_Client_Namespace_List Namespace list object.
  708. *
  709. * @throws Horde_Imap_Client_Exception
  710. */
  711. abstract protected function _getNamespaces();
  712. /**
  713. * Display if connection to the server has been secured via TLS or SSL.
  714. *
  715. * @return boolean True if the IMAP connection is secured.
  716. */
  717. public function isSecureConnection()
  718. {
  719. return ($this->_connection && $this->_connection->secure);
  720. }
  721. /**
  722. * Connect to the remote server.
  723. *
  724. * @throws Horde_Imap_Client_Exception
  725. */
  726. abstract protected function _connect();
  727. /**
  728. * Return a list of alerts that MUST be presented to the user (RFC 3501
  729. * [7.1]).
  730. *
  731. * @deprecated Add an observer to the $alerts_ob property instead.
  732. *
  733. * @return array An array of alert messages.
  734. */
  735. public function alerts()
  736. {
  737. $alerts = isset($this->_temp['alerts'])
  738. ? $this->_temp['alerts']
  739. : array();
  740. unset($this->_temp['alerts']);
  741. return $alerts;
  742. }
  743. /**
  744. * Login to the IMAP server.
  745. *
  746. * @throws Horde_Imap_Client_Exception
  747. */
  748. public function login()
  749. {
  750. if (!$this->_isAuthenticated && $this->_login()) {
  751. if ($this->getParam('id')) {
  752. try {
  753. $this->sendID();
  754. /* ID is queued - force sending the queued command. */
  755. $this->_sendCmd($this->_pipeline());
  756. } catch (Horde_Imap_Client_Exception_NoSupportExtension $e) {
  757. // Ignore if server doesn't support ID extension.
  758. }
  759. }
  760. if ($this->getParam('comparator')) {
  761. try {
  762. $this->setComparator();
  763. } catch (Horde_Imap_Client_Exception_NoSupportExtension $e) {
  764. // Ignore if server doesn't support I18NLEVEL=2
  765. }
  766. }
  767. }
  768. $this->_isAuthenticated = true;
  769. }
  770. /**
  771. * Login to the IMAP server.
  772. *
  773. * @return boolean Return true if global login tasks should be run.
  774. *
  775. * @throws Horde_Imap_Client_Exception
  776. */
  777. abstract protected function _login();
  778. /**
  779. * Logout from the IMAP server (see RFC 3501 [6.1.3]).
  780. */
  781. public function logout()
  782. {
  783. if ($this->_isAuthenticated && $this->_connection->connected) {
  784. $this->_logout();
  785. $this->_connection->close();
  786. }
  787. $this->_connection = $this->_selected = null;
  788. $this->_isAuthenticated = false;
  789. $this->_mode = 0;
  790. }
  791. /**
  792. * Logout from the IMAP server (see RFC 3501 [6.1.3]).
  793. */
  794. abstract protected function _logout();
  795. /**
  796. * Send ID information to the IMAP server (RFC 2971).
  797. *
  798. * @param array $info Overrides the value of the 'id' param and sends
  799. * this information instead.
  800. *
  801. * @throws Horde_Imap_Client_Exception
  802. * @throws Horde_Imap_Client_Exception_NoSupportExtension
  803. */
  804. public function sendID($info = null)
  805. {
  806. if (!$this->_capability('ID')) {
  807. throw new Horde_Imap_Client_Exception_NoSupportExtension('ID');
  808. }
  809. $this->_sendID(is_null($info) ? ($this->getParam('id') ?: array()) : $info);
  810. }
  811. /**
  812. * Send ID information to the IMAP server (RFC 2971).
  813. *
  814. * @param array $info The information to send to the server.
  815. *
  816. * @throws Horde_Imap_Client_Exception
  817. */
  818. abstract protected function _sendID($info);
  819. /**
  820. * Return ID information from the IMAP server (RFC 2971).
  821. *
  822. * @return array An array of information returned, with the keys as the
  823. * 'field' and the values as the 'value'.
  824. *
  825. * @throws Horde_Imap_Client_Exception
  826. * @throws Horde_Imap_Client_Exception_NoSupportExtension
  827. */
  828. public function getID()
  829. {
  830. if (!$this->_capability('ID')) {
  831. throw new Horde_Imap_Client_Exception_NoSupportExtension('ID');
  832. }
  833. return $this->_getID();
  834. }
  835. /**
  836. * Return ID information from the IMAP server (RFC 2971).
  837. *
  838. * @return array An array of information returned, with the keys as the
  839. * 'field' and the values as the 'value'.
  840. *
  841. * @throws Horde_Imap_Client_Exception
  842. */
  843. abstract protected function _getID();
  844. /**
  845. * Sets the preferred language for server response messages (RFC 5255).
  846. *
  847. * @param array $langs Overrides the value of the 'lang' param and sends
  848. * this list of preferred languages instead. The
  849. * special string 'i-default' can be used to restore
  850. * the language to the server default.
  851. *
  852. * @return string The language accepted by the server, or null if the
  853. * default language is used.
  854. *
  855. * @throws Horde_Imap_Client_Exception
  856. */
  857. public function setLanguage($langs = null)
  858. {
  859. $lang = null;
  860. if ($this->_capability('LANGUAGE')) {
  861. $lang = is_null($langs)
  862. ? $this->getParam('lang')
  863. : $langs;
  864. }
  865. return is_null($lang)
  866. ? null
  867. : $this->_setLanguage($lang);
  868. }
  869. /**
  870. * Sets the preferred language for server response messages (RFC 5255).
  871. *
  872. * @param array $langs The preferred list of languages.
  873. *
  874. * @return string The language accepted by the server, or null if the
  875. * default language is used.
  876. *
  877. * @throws Horde_Imap_Client_Exception
  878. */
  879. abstract protected function _setLanguage($langs);
  880. /**
  881. * Gets the preferred language for server response messages (RFC 5255).
  882. *
  883. * @param array $list If true, return the list of available languages.
  884. *
  885. * @return mixed If $list is true, the list of languages available on the
  886. * server (may be empty). If false, the language used by
  887. * the server, or null if the default language is used.
  888. *
  889. * @throws Horde_Imap_Client_Exception
  890. */
  891. public function getLanguage($list = false)
  892. {
  893. if (!$this->_capability('LANGUAGE')) {
  894. return $list ? array() : null;
  895. }
  896. return $this->_getLanguage($list);
  897. }
  898. /**
  899. * Gets the preferred language for server response messages (RFC 5255).
  900. *
  901. * @param array $list If true, return the list of available languages.
  902. *
  903. * @return mixed If $list is true, the list of languages available on the
  904. * server (may be empty). If false, the language used by
  905. * the server, or null if the default language is used.
  906. *
  907. * @throws Horde_Imap_Client_Exception
  908. */
  909. abstract protected function _getLanguage($list);
  910. /**
  911. * Open a mailbox.
  912. *
  913. * @param mixed $mailbox The mailbox to open. Either a
  914. * Horde_Imap_Client_Mailbox object or a string
  915. * (UTF-8).
  916. * @param integer $mode The access mode. Either
  917. * - Horde_Imap_Client::OPEN_READONLY
  918. * - Horde_Imap_Client::OPEN_READWRITE
  919. * - Horde_Imap_Client::OPEN_AUTO
  920. *
  921. * @throws Horde_Imap_Client_Exception
  922. */
  923. public function openMailbox($mailbox, $mode = Horde_Imap_Client::OPEN_AUTO)
  924. {
  925. $this->login();
  926. $change = false;
  927. $mailbox = Horde_Imap_Client_Mailbox::get($mailbox);
  928. if ($mode == Horde_Imap_Client::OPEN_AUTO) {
  929. if (is_null($this->_selected) ||
  930. !$mailbox->equals($this->_selected)) {
  931. $mode = Horde_Imap_Client::OPEN_READONLY;
  932. $change = true;
  933. }
  934. } else {
  935. $change = (is_null($this->_selected) ||
  936. !$mailbox->equals($this->_selected) ||
  937. ($mode != $this->_mode));
  938. }
  939. if ($change) {
  940. $this->_openMailbox($mailbox, $mode);
  941. $this->_mailboxOb()->open = true;
  942. if ($this->_initCache(true)) {
  943. $this->_condstoreSync();
  944. }
  945. }
  946. }
  947. /**
  948. * Open a mailbox.
  949. *
  950. * @param Horde_Imap_Client_Mailbox $mailbox The mailbox to open.
  951. * @param integer $mode The access mode.
  952. *
  953. * @throws Horde_Imap_Client_Exception
  954. */
  955. abstract protected function _openMailbox(Horde_Imap_Client_Mailbox $mailbox,
  956. $mode);
  957. /**
  958. * Called when the selected mailbox is changed.
  959. *
  960. * @param mixed $mailbox The selected mailbox or null.
  961. * @param integer $mode The access mode.
  962. */
  963. protected function _changeSelected($mailbox = null, $mode = null)
  964. {
  965. $this->_mode = $mode;
  966. if (is_null($mailbox)) {
  967. $this->_selected = null;
  968. } else {
  969. $this->_selected = clone $mailbox;
  970. $this->_mailboxOb()->reset();
  971. }
  972. }
  973. /**
  974. * Return the Horde_Imap_Client_Base_Mailbox object.
  975. *
  976. * @param string $mailbox The mailbox name. Defaults to currently
  977. * selected mailbox.
  978. *
  979. * @return Horde_Imap_Client_Base_Mailbox Mailbox object.
  980. */
  981. protected function _mailboxOb($mailbox = null)
  982. {
  983. $name = is_null($mailbox)
  984. ? strval($this->_selected)
  985. : strval($mailbox);
  986. if (!isset($this->_temp['mailbox_ob'][$name])) {
  987. $this->_temp['mailbox_ob'][$name] = new Horde_Imap_Client_Base_Mailbox();
  988. }
  989. return $this->_temp['mailbox_ob'][$name];
  990. }
  991. /**
  992. * Return the currently opened mailbox and access mode.
  993. *
  994. * @return mixed Null if no mailbox selected, or an array with two
  995. * elements:
  996. * - mailbox: (Horde_Imap_Client_Mailbox) The mailbox object.
  997. * - mode: (integer) Current mode.
  998. *
  999. * @throws Horde_Imap_Client_Exception
  1000. */
  1001. public function currentMailbox()
  1002. {
  1003. return is_null($this->_selected)
  1004. ? null
  1005. : array(
  1006. 'mailbox' => clone $this->_selected,
  1007. 'mode' => $this->_mode
  1008. );
  1009. }
  1010. /**
  1011. * Create a mailbox.
  1012. *
  1013. * @param mixed $mailbox The mailbox to create. Either a
  1014. * Horde_Imap_Client_Mailbox object or a string
  1015. * (UTF-8).
  1016. * @param array $opts Additional options:
  1017. * - special_use: (array) An array of special-use flags to mark the
  1018. * mailbox with. The server MUST support RFC 6154.
  1019. *
  1020. * @throws Horde_Imap_Client_Exception
  1021. */
  1022. public function createMailbox($mailbox, array $opts = array())
  1023. {
  1024. $this->login();
  1025. if (!$this->_capability('CREATE-SPECIAL-USE')) {
  1026. unset($opts['special_use']);
  1027. }
  1028. $this->_createMailbox(Horde_Imap_Client_Mailbox::get($mailbox), $opts);
  1029. }
  1030. /**
  1031. * Create a mailbox.
  1032. *
  1033. * @param Horde_Imap_Client_Mailbox $mailbox The mailbox to create.
  1034. * @param array $opts Additional options. See
  1035. * createMailbox().
  1036. *
  1037. * @throws Horde_Imap_Client_Exception
  1038. */
  1039. abstract protected function _createMailbox(Horde_Imap_Client_Mailbox $mailbox,
  1040. $opts);
  1041. /**
  1042. * Delete a mailbox.
  1043. *
  1044. * @param mixed $mailbox The mailbox to delete. Either a
  1045. * Horde_Imap_Client_Mailbox object or a string
  1046. * (UTF-8).
  1047. *
  1048. * @throws Horde_Imap_Client_Exception
  1049. */
  1050. public function deleteMailbox($mailbox)
  1051. {
  1052. $this->login();
  1053. $mailbox = Horde_Imap_Client_Mailbox::get($mailbox);
  1054. $this->_deleteMailbox($mailbox);
  1055. $this->_deleteMailboxPost($mailbox);
  1056. }
  1057. /**
  1058. * Delete a mailbox.
  1059. *
  1060. * @param Horde_Imap_Client_Mailbox $mailbox The mailbox to delete.
  1061. *
  1062. * @throws Horde_Imap_Client_Exception
  1063. */
  1064. abstract protected function _deleteMailbox(Horde_Imap_Client_Mailbox $mailbox);
  1065. /**
  1066. * Actions to perform after a mailbox delete.
  1067. *
  1068. * @param Horde_Imap_Client_Mailbox $mailbox The deleted mailbox.
  1069. */
  1070. protected function _deleteMailboxPost(Horde_Imap_Client_Mailbox $mailbox)
  1071. {
  1072. /* Delete mailbox caches. */
  1073. if ($this->_initCache()) {
  1074. $this->_cache->deleteMailbox($mailbox);
  1075. }
  1076. unset($this->_temp['mailbox_ob'][strval($mailbox)]);
  1077. /* Unsubscribe from mailbox. */
  1078. try {
  1079. $this->subscribeMailbox($mailbox, false);
  1080. } catch (Horde_Imap_Client_Exception $e) {
  1081. // Ignore failed unsubscribe request
  1082. }
  1083. }
  1084. /**
  1085. * Rename a mailbox.
  1086. *
  1087. * @param mixed $old The old mailbox name. Either a
  1088. * Horde_Imap_Client_Mailbox object or a string (UTF-8).
  1089. * @param mixed $new The new mailbox name. Either a
  1090. * Horde_Imap_Client_Mailbox object or a string (UTF-8).
  1091. *
  1092. * @throws Horde_Imap_Client_Exception
  1093. */
  1094. public function renameMailbox($old, $new)
  1095. {
  1096. // Login will be handled by first listMailboxes() call.
  1097. $old = Horde_Imap_Client_Mailbox::get($old);
  1098. $new = Horde_Imap_Client_Mailbox::get($new);
  1099. /* Check if old mailbox(es) were subscribed to. */
  1100. $base = $this->listMailboxes($old, Horde_Imap_Client::MBOX_SUBSCRIBED, array('delimiter' => true));
  1101. if (empty($base)) {
  1102. $base = $this->listMailboxes($old, Horde_Imap_Client::MBOX_ALL, array('delimiter' => true));
  1103. $base = reset($base);
  1104. $subscribed = array();
  1105. } else {
  1106. $base = reset($base);
  1107. $subscribed = array($base['mailbox']);
  1108. }
  1109. $all_mboxes = array($base['mailbox']);
  1110. if (strlen($base['delimiter'])) {
  1111. $search = $old->list_escape . $base['delimiter'] . '*';
  1112. $all_mboxes = array_merge($all_mboxes, $this->listMailboxes($search, Horde_Imap_Client::MBOX_ALL, array('flat' => true)));
  1113. $subscribed = array_merge($subscribed, $this->listMailboxes($search, Horde_Imap_Client::MBOX_SUBSCRIBED, array('flat' => true)));
  1114. }
  1115. $this->_renameMailbox($old, $new);
  1116. /* Delete mailbox actions. */
  1117. foreach ($all_mboxes as $val) {
  1118. $this->_deleteMailboxPost($val);
  1119. }
  1120. foreach ($subscribed as $val) {
  1121. try {
  1122. $this->subscribeMailbox(new Horde_Imap_Client_Mailbox(substr_replace($val, $new, 0, strlen($old))));
  1123. } catch (Horde_Imap_Client_Exception $e) {
  1124. // Ignore failed subscription requests
  1125. }
  1126. }
  1127. }
  1128. /**
  1129. * Rename a mailbox.
  1130. *
  1131. * @param Horde_Imap_Client_Mailbox $old The old mailbox name.
  1132. * @param Horde_Imap_Client_Mailbox $new The new mailbox name.
  1133. *
  1134. * @throws Horde_Imap_Client_Exception
  1135. */
  1136. abstract protected function _renameMailbox(Horde_Imap_Client_Mailbox $old,
  1137. Horde_Imap_Client_Mailbox $new);
  1138. /**
  1139. * Manage subscription status for a mailbox.
  1140. *
  1141. * @param mixed $mailbox The mailbox to [un]subscribe to. Either a
  1142. * Horde_Imap_Client_Mailbox object or a string
  1143. * (UTF-8).
  1144. * @param boolean $subscribe True to subscribe, false to unsubscribe.
  1145. *
  1146. * @throws Horde_Imap_Client_Exception
  1147. */
  1148. public function subscribeMailbox($mailbox, $subscribe = true)
  1149. {
  1150. $this->login();
  1151. $this->_subscribeMailbox(Horde_Imap_Client_Mailbox::get($mailbox), (bool)$subscribe);
  1152. }
  1153. /**
  1154. * Manage subscription status for a mailbox.
  1155. *
  1156. * @param Horde_Imap_Client_Mailbox $mailbox The mailbox to [un]subscribe
  1157. * to.
  1158. * @param boolean $subscribe True to subscribe, false to
  1159. * unsubscribe.
  1160. *
  1161. * @throws Horde_Imap_Client_Exception
  1162. */
  1163. abstract protected function _subscribeMailbox(Horde_Imap_Client_Mailbox $mailbox,
  1164. $subscribe);
  1165. /**
  1166. * Obtain a list of mailboxes matching a pattern.
  1167. *
  1168. * @param mixed $pattern The mailbox search pattern(s) (see RFC 3501
  1169. * [6.3.8] for the format). A UTF-8 string or an
  1170. * array of strings. If a Horde_Imap_Client_Mailbox
  1171. * object is given, it is escaped (i.e. wildcard
  1172. * patterns are converted to return the miminal
  1173. * number of matches possible).
  1174. * @param integer $mode Which mailboxes to return. Either:
  1175. * - Horde_Imap_Client::MBOX_SUBSCRIBED
  1176. * Return subscribed mailboxes.
  1177. * - Horde_Imap_Client::MBOX_SUBSCRIBED_EXISTS
  1178. * Return subscribed mailboxes that exist on the server.
  1179. * - Horde_Imap_Client::MBOX_UNSUBSCRIBED
  1180. * Return unsubscribed mailboxes.
  1181. * - Horde_Imap_Client::MBOX_ALL
  1182. * Return all mailboxes regardless of subscription status.
  1183. * - Horde_Imap_Client::MBOX_ALL_SUBSCRIBED (@since 2.23.0)
  1184. * Return all mailboxes regardless of subscription status, and ensure
  1185. * the '\subscribed' attribute is set if mailbox is subscribed
  1186. * (implies 'attributes' option is true).
  1187. * @param array $options Additional options:
  1188. * <pre>
  1189. * - attributes: (boolean) If true, return attribute information under
  1190. * the 'attributes' key.
  1191. * DEFAULT: Do not return this information.
  1192. * - children: (boolean) Tell server to return children attribute
  1193. * information (\HasChildren, \HasNoChildren). Requires the
  1194. * LIST-EXTENDED extension to guarantee this information is
  1195. * returned. Server MAY return this attribute without this
  1196. * option, or if the CHILDREN extension is available, but it
  1197. * is not guaranteed.
  1198. * DEFAULT: false
  1199. * - flat: (boolean) If true, return a flat list of mailbox names only.
  1200. * Overrides the 'attributes' option.
  1201. * DEFAULT: Do not return flat list.
  1202. * - recursivematch: (boolean) Force the server to return information
  1203. * about parent mailboxes that don't match other
  1204. * selection options, but have some sub-mailboxes that
  1205. * do. Information about children is returned in the
  1206. * CHILDINFO extended data item ('extended'). Requires
  1207. * the LIST-EXTENDED extension.
  1208. * DEFAULT: false
  1209. * - remote: (boolean) Tell server to return mailboxes that reside on
  1210. * another server. Requires the LIST-EXTENDED extension.
  1211. * DEFAULT: false
  1212. * - special_use: (boolean) Tell server to return special-use attribute
  1213. * information (see Horde_Imap_Client SPECIALUSE_*
  1214. * constants). Server must support the SPECIAL-USE return
  1215. * option for this setting to have any effect.
  1216. * DEFAULT: false
  1217. * - status: (integer) Tell server to return status information. The
  1218. * value is a bitmask that may contain any of:
  1219. * - Horde_Imap_Client::STATUS_MESSAGES
  1220. * - Horde_Imap_Client::STATUS_RECENT
  1221. * - Horde_Imap_Client::STATUS_UIDNEXT
  1222. * - Horde_Imap_Client::STATUS_UIDVALIDITY
  1223. * - Horde_Imap_Client::STATUS_UNSEEN
  1224. * - Horde_Imap_Client::STATUS_HIGHESTMODSEQ
  1225. * DEFAULT: 0
  1226. * - sort: (boolean) If true, return a sorted list of mailboxes?
  1227. * DEFAULT: Do not sort the list.
  1228. * - sort_delimiter: (string) If 'sort' is true, this is the delimiter
  1229. * used to sort the mailboxes.
  1230. * DEFAULT: '.'
  1231. * </pre>
  1232. *
  1233. * @return array If 'flat' option is true, the array values are a list
  1234. * of Horde_Imap_Client_Mailbox objects. Otherwise, the
  1235. * keys are UTF-8 mailbox names and the values are arrays
  1236. * with these keys:
  1237. * - attributes: (array) List of lower-cased attributes [only if
  1238. * 'attributes' option is true].
  1239. * - delimiter: (string) The delimiter for the mailbox.
  1240. * - extended: (TODO) TODO [only if 'recursivematch' option is true and
  1241. * LIST-EXTENDED extension is supported on the server].
  1242. * - mailbox: (Horde_Imap_Client_Mailbox) The mailbox object.
  1243. * - status: (array) See status() [only if 'status' option is true].
  1244. *
  1245. * @throws Horde_Imap_Client_Exception
  1246. */
  1247. public function listMailboxes($pattern,
  1248. $mode = Horde_Imap_Client::MBOX_ALL,
  1249. array $options = array())
  1250. {
  1251. $this->login();
  1252. $pattern = is_array($pattern)
  1253. ? array_unique($pattern)
  1254. : array($pattern);
  1255. /* Prepare patterns. */
  1256. $plist = array();
  1257. foreach ($pattern as $val) {
  1258. if ($val instanceof Horde_Imap_Client_Mailbox) {
  1259. $val = $val->list_escape;
  1260. }
  1261. $plist[] = Horde_Imap_Client_Mailbox::get(preg_replace(
  1262. array("/\*{2,}/", "/\%{2,}/"),
  1263. array('*', '%'),
  1264. Horde_Imap_Client_Utf7imap::Utf8ToUtf7Imap($val)
  1265. ), true);
  1266. }
  1267. if (isset($options['special_use']) &&
  1268. !$this->_capability('SPECIAL-USE')) {
  1269. unset($options['special_use']);
  1270. }
  1271. $ret = $this->_listMailboxes($plist, $mode, $options);
  1272. if (!empty($options['status']) &&
  1273. !$this->_capability('LIST-STATUS')) {
  1274. foreach ($this->status(array_keys($ret), $options['status']) as $key => $val) {
  1275. $ret[$key]['status'] = $val;
  1276. }
  1277. }
  1278. if (empty($options['sort'])) {
  1279. return $ret;
  1280. }
  1281. $list_ob = new Horde_Imap_Client_Mailbox_List(empty($options['flat']) ? array_keys($ret) : $ret);
  1282. $sorted = $list_ob->sort(array(
  1283. 'delimiter' => empty($options['sort_delimiter']) ? '.' : $options['sort_delimiter']
  1284. ));
  1285. if (!empty($options['flat'])) {
  1286. return $sorted;
  1287. }
  1288. $out = array();
  1289. foreach ($sorted as $val) {
  1290. $out[$val] = $ret[$val];
  1291. }
  1292. return $out;
  1293. }
  1294. /**
  1295. * Obtain a list of mailboxes matching a pattern.
  1296. *
  1297. * @param array $pattern The mailbox search patterns
  1298. * (Horde_Imap_Client_Mailbox objects).
  1299. * @param integer $mode Which mailboxes to return.
  1300. * @param array $options Additional options.
  1301. *
  1302. * @return array See listMailboxes().
  1303. *
  1304. * @throws Horde_Imap_Client_Exception
  1305. */
  1306. abstract protected function _listMailboxes($pattern, $mode, $options);
  1307. /**
  1308. * Obtain status information for a mailbox.
  1309. *
  1310. * @param mixed $mailbox The mailbox(es) to query. Either a
  1311. * Horde_Imap_Client_Mailbox object, a string
  1312. * (UTF-8), or an array of objects/strings (since
  1313. * 2.10.0).
  1314. * @param integer $flags A bitmask of information requested from the
  1315. * server. Allowed flags:
  1316. * <pre>
  1317. * - Horde_Imap_Client::STATUS_MESSAGES
  1318. * Return key: messages
  1319. * Return format: (integer) The number of messages in the mailbox.
  1320. *
  1321. * - Horde_Imap_Client::STATUS_RECENT
  1322. * Return key: recent
  1323. * Return format: (integer) The number of messages with the \Recent
  1324. * flag set as currently reported in the mailbox
  1325. *
  1326. * - Horde_Imap_Client::STATUS_RECENT_TOTAL
  1327. * Return key: recent_total
  1328. * Return format: (integer) The number of messages with the \Recent
  1329. * flag set. This returns the total number of messages
  1330. * that have been marked as recent in this …

Large files files are truncated, but you can click here to view the full file