1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20: class IMP_Imap implements Serializable
21: {
22:
23: const ACCESS_FOLDERS = 1;
24: const ACCESS_SEARCH = 2;
25: const ACCESS_FLAGS = 3;
26: const ACCESS_UNSEEN = 4;
27: const ACCESS_TRASH = 5;
28:
29: 30: 31: 32: 33:
34: public $ob = null;
35:
36: 37: 38: 39: 40:
41: static protected $_config;
42:
43: 44: 45: 46: 47:
48: protected $_changed = false;
49:
50: 51: 52: 53: 54:
55: protected $_login = false;
56:
57: 58: 59: 60: 61:
62: protected $_nsdefault = null;
63:
64: 65: 66: 67: 68:
69: protected $_temp = array();
70:
71: 72:
73: public function __get($key)
74: {
75: switch ($key) {
76: case 'changed':
77: return $this->_changed || ($this->ob && $this->ob->changed);
78:
79: case 'imap':
80: return $this->ob && ($this->ob instanceof Horde_Imap_Client_Socket);
81:
82: case 'pop3':
83: return $this->ob && ($this->ob instanceof Horde_Imap_Client_Socket_Pop3);
84: }
85: }
86:
87: 88: 89: 90: 91: 92: 93: 94: 95: 96:
97: public function createImapObject($username, $password, $key)
98: {
99: global $prefs;
100:
101: if (!is_null($this->ob)) {
102: return $this->ob;
103: }
104:
105: if (($server = $this->loadServerConfig($key)) === false) {
106: $error = new IMP_Imap_Exception('Could not load server configuration.');
107: $error->log();
108: throw $error;
109: }
110:
111: $protocol = isset($server['protocol'])
112: ? strtolower($server['protocol'])
113: : 'imap';
114:
115: $imap_config = array(
116: 'capability_ignore' => empty($server['capability_ignore']) ? array() : $server['capability_ignore'],
117: 'comparator' => empty($server['comparator']) ? false : $server['comparator'],
118: 'debug' => isset($server['debug']) ? $server['debug'] : null,
119: 'debug_literal' => !empty($server['debug_raw']),
120: 'encryptKey' => array(__CLASS__, 'getEncryptKey'),
121: 'hostspec' => isset($server['hostspec']) ? $server['hostspec'] : null,
122: 'id' => empty($server['id']) ? false : $server['id'],
123: 'lang' => empty($server['lang']) ? false : $server['lang'],
124: 'password' => $password,
125: 'port' => isset($server['port']) ? $server['port'] : null,
126: 'secure' => isset($server['secure']) ? $server['secure'] : false,
127: 'statuscache' => true,
128: 'timeout' => empty($server['timeout']) ? null : $server['timeout'],
129: 'username' => $username,
130: );
131:
132:
133: if (!empty($server['cache'])) {
134: $imap_config['cache'] = $this->loadCacheConfig(is_array($server['cache']) ? $server['cache'] : array());
135: }
136:
137: try {
138: $ob = Horde_Imap_Client::factory(($protocol == 'imap') ? 'Socket' : 'Socket_Pop3', $imap_config);
139: } catch (Horde_Imap_Client_Exception $e) {
140: $error = new IMP_Imap_Exception($e);
141: $error->log();
142: throw $error;
143: }
144:
145: $this->ob = $ob;
146:
147: if ($protocol == 'pop') {
148:
149: $prefs->setValue('save_sent_mail', false);
150: $prefs->setLocked('save_sent_mail', true);
151: $prefs->setLocked('sent_mail_folder', true);
152: $prefs->setLocked('drafts_folder', true);
153: $prefs->setLocked('trash_folder', true);
154: }
155:
156: return $ob;
157: }
158:
159: 160: 161: 162: 163: 164: 165: 166: 167:
168: public function loadCacheConfig($config)
169: {
170: if (!($ob = $GLOBALS['injector']->getInstance('Horde_Cache'))) {
171: return array();
172: }
173:
174: if (is_string($config)) {
175: if ((($server = $this->loadServerConfig($config)) === false) ||
176: empty($server['cache'])) {
177: return array();
178: }
179: $config = $server['cache'];
180: }
181:
182: return array(
183: 'cacheob' => $ob,
184: 'lifetime' => empty($config['lifetime']) ? false : $config['lifetime'],
185: 'slicesize' => empty($config['slicesize']) ? false : $config['slicesize'],
186: );
187: }
188:
189: 190: 191: 192:
193: public function updateFetchIgnore()
194: {
195: if ($this->imap) {
196: $special = IMP_Mailbox::getSpecialMailboxes();
197:
198: $this->ob->fetchCacheIgnore(array_filter(array(
199: strval($special[IMP_Mailbox::SPECIAL_SPAM]),
200: strval($special[IMP_Mailbox::SPECIAL_TRASH])
201: )));
202: }
203: }
204:
205: 206: 207: 208: 209: 210: 211:
212: public function access($right)
213: {
214: switch ($right) {
215: case self::ACCESS_FOLDERS:
216: case self::ACCESS_TRASH:
217: return (!empty($GLOBALS['conf']['user']['allow_folders']) &&
218: !$this->pop3);
219:
220: case self::ACCESS_FLAGS:
221: case self::ACCESS_SEARCH:
222: case self::ACCESS_UNSEEN:
223: return !$this->pop3;
224: }
225:
226: return false;
227: }
228:
229: 230: 231: 232: 233:
234: public function getNamespaceList()
235: {
236: try {
237: return $this->ob->getNamespaces($GLOBALS['session']->get('imp', 'imap_namespace', Horde_Session::TYPE_ARRAY));
238: } catch (Horde_Imap_Client_Exception $e) {
239: return array();
240: }
241: }
242:
243: 244: 245: 246: 247: 248: 249: 250: 251: 252:
253: public function getNamespace($mailbox = null, $personal = false)
254: {
255: if ($this->pop3) {
256: return null;
257: }
258:
259: $ns = $this->getNamespaceList();
260:
261: if (is_null($mailbox)) {
262: reset($ns);
263: $mailbox = key($ns);
264: }
265:
266: foreach ($ns as $key => $val) {
267: $mbox = $mailbox . $val['delimiter'];
268: if (!empty($key) && (strpos($mbox, $key) === 0)) {
269: return $val;
270: }
271: }
272:
273: return (isset($ns['']) && (!$personal || ($val['type'] == Horde_Imap_Client::NS_PERSONAL)))
274: ? $ns['']
275: : null;
276: }
277:
278: 279: 280: 281: 282:
283: public function defaultNamespace()
284: {
285: if ($this->pop3) {
286: return null;
287: }
288:
289: if ($this->_login && !isset($this->_nsdefault)) {
290: foreach ($this->getNamespaceList() as $val) {
291: if ($val['type'] == Horde_Imap_Client::NS_PERSONAL) {
292: $this->_nsdefault = $val;
293: $this->_changed = true;
294: break;
295: }
296: }
297: }
298:
299: return $this->_nsdefault;
300: }
301:
302: 303: 304: 305: 306:
307: public function getUtils()
308: {
309: return $this->ob
310: ? $this->ob->utils
311: : $GLOBALS['injector']->createInstance('Horde_Imap_Client_Utils');
312: }
313:
314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324:
325: public function __call($method, $params)
326: {
327: if (!$this->ob || !method_exists($this->ob, $method)) {
328: if ($GLOBALS['registry']->getAuth()) {
329: $GLOBALS['injector']->getInstance('Horde_Core_Factory_Auth')->create()->setError(Horde_Auth::REASON_SESSION);
330: $GLOBALS['registry']->authenticateFailure('imp');
331: } else {
332: throw new BadMethodCallException(sprintf('%s: Invalid method call "%s".', __CLASS__, $method));
333: }
334: }
335:
336: switch ($method) {
337: case 'search':
338: $params = call_user_func_array(array($this, '_search'), $params);
339: break;
340: }
341:
342: try {
343: $result = call_user_func_array(array($this->ob, $method), $params);
344: } catch (Horde_Imap_Client_Exception $e) {
345: $error = new IMP_Imap_Exception($e);
346:
347: switch ($e->getCode()) {
348: case Horde_Imap_Client_Exception::DISCONNECT:
349: $error->notify(_("Unexpectedly disconnected from the mail server."));
350: break;
351:
352: case Horde_Imap_Client_Exception::SERVER_READERROR:
353: $error->notify(_("Error when communicating with the mail server."));
354: break;
355:
356: case Horde_Imap_Client_Exception::MAILBOX_NOOPEN:
357: if (strcasecmp($method, 'openMailbox') === 0) {
358: $error->notify(sprintf(_("Could not open mailbox \"%s\"."), IMP_Mailbox::get(reset($params))->label));
359: } else {
360: $error->notify(_("Could not open mailbox."));
361: }
362: break;
363:
364: case Horde_Imap_Client_Exception::CATENATE_TOOBIG:
365: $error->notify(_("Could not save message data because it is too large."));
366: break;
367:
368: case Horde_Imap_Client_Exception::NOPERM:
369: $error->notify(_("You do not have adequate permissions to carry out this operation."));
370: break;
371:
372: case Horde_Imap_Client_Exception::INUSE:
373: case Horde_Imap_Client_Exception::POP3_TEMP_ERROR:
374: $error->notify(_("There was a temporary issue when attempting this operation. Please try again later."));
375: break;
376:
377: case Horde_Imap_Client_Exception::CORRUPTION:
378: case Horde_Imap_Client_Exception::POP3_PERM_ERROR:
379: $error->notify(_("The mail server is reporting corrupt data in your mailbox. Details have been logged for the administrator."));
380: break;
381:
382: case Horde_Imap_Client_Exception::LIMIT:
383: $error->notify(_("The mail server has denied the request. Details have been logged for the administrator."));
384: break;
385:
386: case Horde_Imap_Client_Exception::OVERQUOTA:
387: $error->notify(_("The operation failed because you have exceeded your quota on the mail server."));
388: break;
389:
390: case Horde_Imap_Client_Exception::ALREADYEXISTS:
391: $error->notify(_("The object could not be created because it already exists."));
392: break;
393:
394: case Horde_Imap_Client_Exception::NONEXISTENT:
395: $error->notify(_("The object could not be deleted because it does not exist."));
396: break;
397: }
398:
399: $error->log();
400:
401: throw $error;
402: }
403:
404:
405: switch ($method) {
406: case 'createMailbox':
407: case 'renameMailbox':
408:
409: IMP_Mailbox::get($params[0])->expire();
410: break;
411:
412: case 'login':
413: if (!$this->_login) {
414:
415: if ($this->pop3 &&
416: !$this->queryCapability('UIDL')) {
417: $error = new IMP_Imap_Exception('The POP3 server does not support the REQUIRED UIDL capability.');
418: $error->log();
419: throw $error;
420: }
421:
422: $this->_changed = $this->_login = true;
423: }
424: break;
425:
426: case 'parseCacheId':
427: 428:
429: if ((($pos = strrpos($params[0], '|')) !== false) &&
430: (substr($params[0], $pos + 1, 1) == 'D')) {
431: $result['date'] = substr($params[0], $pos + 2);
432: }
433: break;
434:
435: case 'setACL':
436: IMP_Mailbox::get($params[0])->expire(IMP_Mailbox::CACHE_ACL);
437: break;
438: }
439:
440: return $result;
441: }
442:
443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453:
454: protected function _search($mailbox, $query = null, array $opts = array())
455: {
456: $imap_charset = null;
457:
458: if (!empty($opts['sort'])) {
459: 460:
461: if ($sort_cap = $this->queryCapability('SORT')) {
462: $imap_charset = 'UTF-8';
463: }
464:
465: 466: 467: 468:
469: if (is_array($sort_cap) &&
470: in_array('DISPLAY', $sort_cap) &&
471: IMP_Mailbox::get($mailbox)->access_sort) {
472: $pos = array_search(Horde_Imap_Client::SORT_FROM, $opts['sort']);
473: if ($pos !== false) {
474: $opts['sort'][$pos] = Horde_Imap_Client::SORT_DISPLAYFROM;
475: }
476:
477: $pos = array_search(Horde_Imap_Client::SORT_TO, $opts['sort']);
478: if ($pos !== false) {
479: $opts['sort'][$pos] = Horde_Imap_Client::SORT_DISPLAYTO;
480: }
481: }
482: }
483:
484:
485: if (!is_null($query)) {
486: $query = clone $query;
487: if (is_null($imap_charset)) {
488: $imap_charset = $this->validSearchCharset('UTF-8')
489: ? 'UTF-8'
490: : 'US-ASCII';
491: }
492: $query->charset($imap_charset, array('Horde_String', 'convertCharset'));
493: }
494:
495: return array($mailbox, $query, $opts);
496: }
497:
498:
499:
500: 501: 502: 503: 504: 505: 506: 507:
508: static public function loadServerConfig($server = null)
509: {
510: if (isset(self::$_config)) {
511: $servers = self::$_config;
512: } else {
513: try {
514: $servers = Horde::loadConfiguration('backends.php', 'servers', 'imp');
515: if (is_null($servers)) {
516: return false;
517: }
518: } catch (Horde_Exception $e) {
519: Horde::logMessage($e, 'ERR');
520: return false;
521: }
522:
523: foreach (array_keys($servers) as $key) {
524: if (!empty($servers[$key]['disabled'])) {
525: unset($servers[$key]);
526: }
527: }
528: self::$_config = $servers;
529: }
530:
531: if (is_null($server)) {
532: return $servers;
533: }
534:
535:
536: if (empty($servers[$server]) || !is_array($servers[$server])) {
537: $entry = sprintf('Invalid server key "%s" from client [%s]', $server, $_SERVER['REMOTE_ADDR']);
538: Horde::logMessage($entry, 'ERR');
539: return false;
540: }
541:
542: return $servers[$server];
543: }
544:
545:
546:
547: static public function getEncryptKey()
548: {
549: return $GLOBALS['injector']->getInstance('Horde_Secret')->getKey('imp');
550: }
551:
552:
553:
554: 555:
556: public function serialize()
557: {
558: return serialize(array(
559: $this->ob,
560: $this->_nsdefault,
561: $this->_login
562: ));
563: }
564:
565: 566:
567: public function unserialize($data)
568: {
569: list(
570: $this->ob,
571: $this->_nsdefault,
572: $this->_login
573: ) = unserialize($data);
574: }
575:
576: }
577: