svncommit
2005-10-15 e02e3dc8952ab2df1338b849462a8ec1811c48b1
commit | author | age
4e17e6 1 <?php
T 2
3 /*
4  +-----------------------------------------------------------------------+
5  | program/include/rcube_imap.inc                                        |
6  |                                                                       |
7  | This file is part of the RoundCube Webmail client                     |
8  | Copyright (C) 2005, RoundCube Dev. - Switzerland                      |
30233b 9  | Licensed under the GNU GPL                                            |
4e17e6 10  |                                                                       |
T 11  | PURPOSE:                                                              |
12  |   IMAP wrapper that implements the Iloha IMAP Library (IIL)           |
13  |   See http://ilohamail.org/ for details                               |
14  |                                                                       |
15  +-----------------------------------------------------------------------+
16  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
17  +-----------------------------------------------------------------------+
18
19  $Id$
20
21 */
22
23
24 require_once('lib/imap.inc');
25 require_once('lib/mime.inc');
26
27
28 class rcube_imap
29   {
30   var $conn;
520c36 31   var $root_ns = '';
4e17e6 32   var $root_dir = '';
T 33   var $mailbox = 'INBOX';
34   var $list_page = 1;
35   var $page_size = 10;
597170 36   var $delimiter = NULL;
6dc026 37   var $caching_enabled = FALSE;
4e17e6 38   var $default_folders = array('inbox', 'drafts', 'sent', 'junk', 'trash');
T 39   var $cache = array();
40   var $cache_changes = array();  
41   var $uid_id_map = array();
42   var $msg_headers = array();
43
44
45   // PHP 5 constructor
46   function __construct()
47     {
6dc026 48     
4e17e6 49     }
T 50
51   // PHP 4 compatibility
52   function rcube_imap()
53     {
54     $this->__construct();
55     }
56
57
42b113 58   function connect($host, $user, $pass, $port=143, $use_ssl=FALSE)
4e17e6 59     {
42b113 60     global $ICL_PORT, $CONFIG;
T 61     
62     // check for Open-SSL support in PHP build
63     if ($use_ssl && in_array('openssl', get_loaded_extensions()))
64       $ICL_SSL = TRUE;
520c36 65     else if ($use_ssl)
T 66       {
67       raise_error(array('code' => 403,
68                         'type' => 'imap',
69                         'message' => 'Open SSL not available;'), TRUE, FALSE);
70       $port = 143;
71       }
4e17e6 72
T 73     $ICL_PORT = $port;
42b113 74     $this->conn = iil_Connect($host, $user, $pass, array('imap' => 'check'));
4e17e6 75     $this->host = $host;
T 76     $this->user = $user;
77     $this->pass = $pass;
520c36 78     $this->port = $port;
T 79     $this->ssl = $use_ssl;
42b113 80     
520c36 81     // print trace mesages
42b113 82     if ($this->conn && ($CONFIG['debug_level'] & 8))
520c36 83       console($this->conn->message);
T 84     
85     // write error log
42b113 86     else if (!$this->conn && $GLOBALS['iil_error'])
T 87       {
88       raise_error(array('code' => 403,
89                        'type' => 'imap',
90                        'message' => $GLOBALS['iil_error']), TRUE, FALSE);
520c36 91       }
T 92
93     // get account namespace
94     if ($this->conn)
95       {
96       iil_C_NameSpace($this->conn);
97       
98       if (!empty($this->conn->delimiter))
99         $this->delimiter = $this->conn->delimiter;
100       if (!empty($this->conn->rootdir))
101         $this->root_ns = $this->conn->rootdir;
42b113 102       }
4e17e6 103
T 104     return $this->conn ? TRUE : FALSE;
105     }
106
107
108   function close()
109     {    
110     if ($this->conn)
111       iil_Close($this->conn);
112     }
113
114
520c36 115   function reconnect()
T 116     {
117     $this->close();
118     $this->connect($this->host, $this->user, $this->pass, $this->port, $this->ssl);
119     }
120
121
4e17e6 122   function set_rootdir($root)
T 123     {
520c36 124     if (ereg('[\.\/]$', $root)) //(substr($root, -1, 1)==='/')
4e17e6 125       $root = substr($root, 0, -1);
T 126
127     $this->root_dir = $root;
520c36 128     
T 129     if (empty($this->delimiter))
130       $this->get_hierarchy_delimiter();
4e17e6 131     }
T 132
133
134   function set_default_mailboxes($arr)
135     {
136     if (is_array($arr))
137       {
138       $this->default_folders = array();
139       
140       // add mailbox names lower case
141       foreach ($arr as $mbox)
142         $this->default_folders[] = strtolower($mbox);
143       
144       // add inbox if not included
145       if (!in_array('inbox', $this->default_folders))
146         array_unshift($arr, 'inbox');
147       }
148     }
149
150
151   function set_mailbox($mbox)
152     {
153     $mailbox = $this->_mod_mailbox($mbox);
154
155     if ($this->mailbox == $mailbox)
156       return;
157
158     $this->mailbox = $mailbox;
159
160     // clear messagecount cache for this mailbox
161     $this->_clear_messagecount($mailbox);
162     }
163
164
165   function set_page($page)
166     {
167     $this->list_page = (int)$page;
168     }
169
170
171   function set_pagesize($size)
172     {
173     $this->page_size = (int)$size;
174     }
175
176
177   function get_mailbox_name()
178     {
179     return $this->conn ? $this->_mod_mailbox($this->mailbox, 'out') : '';
180     }
181
182
597170 183   function get_hierarchy_delimiter()
T 184     {
185     if ($this->conn && empty($this->delimiter))
186       $this->delimiter = iil_C_GetHierarchyDelimiter($this->conn);
187
188     return $this->delimiter;
189     }
190
4e17e6 191   // public method for mailbox listing
T 192   // convert mailbox name with root dir first
193   function list_mailboxes($root='', $filter='*')
194     {
195     $a_out = array();
196     $a_mboxes = $this->_list_mailboxes($root, $filter);
197
198     foreach ($a_mboxes as $mbox)
199       {
200       $name = $this->_mod_mailbox($mbox, 'out');
201       if (strlen($name))
202         $a_out[] = $name;
203       }
204
205     // sort mailboxes
206     $a_out = $this->_sort_mailbox_list($a_out);
207
208     return $a_out;
209     }
210
211   // private method for mailbox listing
212   function _list_mailboxes($root='', $filter='*')
213     {
214     $a_defaults = $a_out = array();
215     
216     // get cached folder list    
217     $a_mboxes = $this->get_cache('mailboxes');
218     if (is_array($a_mboxes))
219       return $a_mboxes;
220
221     // retrieve list of folders from IMAP server
222     $a_folders = iil_C_ListSubscribed($this->conn, $this->_mod_mailbox($root), $filter);
223     
224     if (!is_array($a_folders) || !sizeof($a_folders))
225       $a_folders = array();
226
227     // create INBOX if it does not exist
228     if (!in_array_nocase('INBOX', $a_folders))
229       {
230       $this->create_mailbox('INBOX', TRUE);
231       array_unshift($a_folders, 'INBOX');
232       }
233
234     $a_mailbox_cache = array();
235
236     // write mailboxlist to cache
237     $this->update_cache('mailboxes', $a_folders);
238     
239     return $a_folders;
240     }
241
242
243   // get message count for a specific mailbox; acceptes modes are: ALL, UNSEEN
244   function messagecount($mbox='', $mode='ALL', $force=FALSE)
245     {
246     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
247     return $this->_messagecount($mailbox, $mode, $force);
248     }
249
250   // private method for getting nr of mesages
251   function _messagecount($mailbox='', $mode='ALL', $force=FALSE)
252     {
253     $a_mailbox_cache = FALSE;
254     $mode = strtoupper($mode);
255
256     if (!$mailbox)
257       $mailbox = $this->mailbox;
258
259     $a_mailbox_cache = $this->get_cache('messagecount');
260     
261     // return cached value
262     if (!$force && is_array($a_mailbox_cache[$mailbox]) && isset($a_mailbox_cache[$mailbox][$mode]))
263       return $a_mailbox_cache[$mailbox][$mode];      
264
265     // get message count and store in cache
266     if ($mode == 'UNSEEN')
267       $count = iil_C_CountUnseen($this->conn, $mailbox);
268     else
269       $count = iil_C_CountMessages($this->conn, $mailbox);
270
271 //print "/**** get messagecount for $mailbox ($mode): $count ****/\n";
272
273     if (is_array($a_mailbox_cache[$mailbox]))
274       $a_mailbox_cache[$mailbox] = array();
275       
276     $a_mailbox_cache[$mailbox][$mode] = (int)$count;
277     
278     // write back to cache
279     $this->update_cache('messagecount', $a_mailbox_cache);
280
281 //var_dump($a_mailbox_cache);
282
283     return (int)$count;
284     }
285
286
287   // public method for listing headers
288   // convert mailbox name with root dir first
289   function list_headers($mbox='', $page=NULL, $sort_field='date', $sort_order='DESC')
290     {
291     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
292     return $this->_list_headers($mailbox, $page, $sort_field, $sort_order);
293     }
294
295
296   // private method for listing message header
297   function _list_headers($mailbox='', $page=NULL, $sort_field='date', $sort_order='DESC')
298     {
299     $max = $this->_messagecount($mailbox /*, 'ALL', TRUE*/);
300     
301     if (!strlen($mailbox))
17fc71 302       return array();
4e17e6 303
T 304     // get cached headers
305     $a_msg_headers = $this->get_cache($mailbox.'.msg');
306
307 // print "/**** count = $max; headers = ".sizeof($a_msg_headers)." ****/\n";
308
309     // retrieve headers from IMAP
310     if (!is_array($a_msg_headers) || sizeof($a_msg_headers) != $max)
311       {
312       $a_header_index = iil_C_FetchHeaders($this->conn, $mailbox, "1:$max");
313       $a_msg_headers = array();
314       foreach ($a_header_index as $i => $headers)
17fc71 315         if (!$headers->deleted)
S 316             $a_msg_headers[$headers->uid] = $headers;
4e17e6 317         
T 318 // print "/**** fetch headers ****/\n";
319       }
320     else
321       $headers_cached = TRUE;
322
e02e3d 323     if (!is_array(a_msg_headers))
S 324         return array();
325         
4e17e6 326     // sort headers by a specific col
T 327     $a_headers = iil_SortHeaders($a_msg_headers, $sort_field, $sort_order);
17fc71 328     // free memory
S 329     unset($a_msg_headers);
330     
4e17e6 331     // write headers list to cache
T 332     if (!$headers_cached)
17fc71 333       $this->update_cache($mailbox.'.msg', $a_headers);
4e17e6 334
T 335     // return complete list of messages
336     if (strtolower($page)=='all')
17fc71 337       return $a_headers;
S 338     
4e17e6 339     $start_msg = ($this->list_page-1) * $this->page_size;
17fc71 340     return array_slice($a_headers, $start_msg, $this->page_size);
4e17e6 341     }
T 342
343
344   // return sorted array of message UIDs
345   function message_index($mbox='', $sort_field='date', $sort_order='DESC')
346     {
347     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
348     $a_out = array();
349
350     // get array of message headers
351     $a_headers = $this->_list_headers($mailbox, 'all', $sort_field, $sort_order);
352
353     if (is_array($a_headers))
354       foreach ($a_headers as $header)
355         $a_out[] = $header->uid;
356
357     return $a_out;
358     }
359
360
361   function sync_header_index($mbox=NULL)
362     {
363     
364     }
365
366
367   function search($mbox='', $criteria='ALL')
368     {
369     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
370     $a_messages = iil_C_Search($this->conn, $mailbox, $criteria);
371     return $a_messages;
372     }
373
374
375   function get_headers($uid, $mbox=NULL)
376     {
377     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
378     
379     // get cached headers
380     $a_msg_headers = $this->get_cache($mailbox.'.msg');
381     
382     // return cached header
383     if ($a_msg_headers[$uid])
384       return $a_msg_headers[$uid];
520c36 385
4e17e6 386     $msg_id = $this->_uid2id($uid);
T 387     $header = iil_C_FetchHeader($this->conn, $mailbox, $msg_id);
520c36 388
4e17e6 389     // write headers cache
T 390     $a_msg_headers[$uid] = $header;
391     $this->update_cache($mailbox.'.msg', $a_msg_headers);
392
393     return $header;
394     }
395
396
397   function get_body($uid, $part=1)
398     {
399     if (!($msg_id = $this->_uid2id($uid)))
400       return FALSE;
401
402     $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id); 
403     $structure = iml_GetRawStructureArray($structure_str);
404     $body = iil_C_FetchPartBody($this->conn, $this->mailbox, $msg_id, $part);
405
406     $encoding = iml_GetPartEncodingCode($structure, $part);
407     
408     if ($encoding==3) $body = $this->mime_decode($body, 'base64');
409     else if ($encoding==4) $body = $this->mime_decode($body, 'quoted-printable');
410
411     return $body;
412     }
413
414
415   function get_raw_body($uid)
416     {
417     if (!($msg_id = $this->_uid2id($uid)))
418       return FALSE;
419
420     $body = iil_C_FetchPartHeader($this->conn, $this->mailbox, $msg_id, NULL);
421     $body .= iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, NULL, 1);
422
423     return $body;    
424     }
425
426
427   // set message flag to one or several messages
428   // possible flgs are: SEEN, DELETED, RECENT, ANSWERED, DRAFT
429   function set_flag($uids, $flag)
430     {
431     $flag = strtoupper($flag);
432     $msg_ids = array();
433     if (!is_array($uids))
434       $uids = array($uids);
435       
436     foreach ($uids as $uid)
437       $msg_ids[] = $this->_uid2id($uid);
438       
439     if ($flag=='UNSEEN')
440       $result = iil_C_Unseen($this->conn, $this->mailbox, join(',', $msg_ids));
441     else
442       $result = iil_C_Flag($this->conn, $this->mailbox, join(',', $msg_ids), $flag);
443
444     // reload message headers if cached
445     $cache_key = $this->mailbox.'.msg';
520c36 446     if ($this->caching_enabled && $result && ($a_cached_headers = $this->get_cache($cache_key)))
4e17e6 447       {
520c36 448       // close and re-open connection      
T 449       $this->reconnect();
450
4e17e6 451       foreach ($uids as $uid)
T 452         {
453         if (isset($a_cached_headers[$uid]))
454           {
455           unset($this->cache[$cache_key][$uid]);
456           $this->get_headers($uid);
457           }
458         }
459       }
460
461     // set nr of messages that were flaged
462     $count = sizeof($msg_ids);
463
464     // clear message count cache
465     if ($result && $flag=='SEEN')
466       $this->_set_messagecount($this->mailbox, 'UNSEEN', $count*(-1));
467     else if ($result && $flag=='UNSEEN')
468       $this->_set_messagecount($this->mailbox, 'UNSEEN', $count);
469     else if ($result && $flag=='DELETED')
470       $this->_set_messagecount($this->mailbox, 'ALL', $count*(-1));
471
472     return $result;
473     }
474
475
476   // append a mail message (source) to a specific mailbox
477   function save_message($mbox, $message)
478     {
479     $mailbox = $this->_mod_mailbox($mbox);
480
481     // make shure mailbox exists
482     if (in_array($mailbox, $this->_list_mailboxes()))
483       $saved = iil_C_Append($this->conn, $mailbox, $message);
484     
485     if ($saved)
486       {
487       // increase messagecount of the target mailbox
488       $this->_set_messagecount($mailbox, 'ALL', 1);
489       }
490           
491     return $saved;
492     }
493
494
495   // move a message from one mailbox to another
496   function move_message($uids, $to_mbox, $from_mbox='')
497     {
498     $to_mbox = $this->_mod_mailbox($to_mbox);
499     $from_mbox = $from_mbox ? $this->_mod_mailbox($from_mbox) : $this->mailbox;
500
501     // make shure mailbox exists
502     if (!in_array($to_mbox, $this->_list_mailboxes()))
503       return FALSE;
504     
505     // convert the list of uids to array
506     $a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL);
507     
508     // exit if no message uids are specified
509     if (!is_array($a_uids))
510       return false;
520c36 511
4e17e6 512     // convert uids to message ids
T 513     $a_mids = array();
514     foreach ($a_uids as $uid)
515       $a_mids[] = $this->_uid2id($uid, $from_mbox);
520c36 516
4e17e6 517     $moved = iil_C_Move($this->conn, join(',', $a_mids), $from_mbox, $to_mbox);
T 518     
519     // send expunge command in order to have the moved message
520     // really deleted from the source mailbox
521     if ($moved)
522       {
523       $this->expunge($from_mbox, FALSE);
524       $this->clear_cache($to_mbox.'.msg');
525       $this->_clear_messagecount($from_mbox);
526       $this->_clear_messagecount($to_mbox);
527       }
528
529     // update cached message headers
530     $cache_key = $from_mbox.'.msg';
531     if ($moved && ($a_cached_headers = $this->get_cache($cache_key)))
532       {
533       foreach ($a_uids as $uid)
534         unset($a_cached_headers[$uid]);
535
536       $this->update_cache($cache_key, $a_cached_headers);
537       }
538
539     return $moved;
540     }
541
542
543   // mark messages as deleted and expunge mailbox
544   function delete_message($uids, $mbox='')
545     {
546     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
547
548     // convert the list of uids to array
549     $a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL);
550     
551     // exit if no message uids are specified
552     if (!is_array($a_uids))
553       return false;
554
555
556     // convert uids to message ids
557     $a_mids = array();
558     foreach ($a_uids as $uid)
559       $a_mids[] = $this->_uid2id($uid, $mailbox);
560         
561     $deleted = iil_C_Delete($this->conn, $mailbox, join(',', $a_mids));
562     
563     // send expunge command in order to have the deleted message
564     // really deleted from the mailbox
565     if ($deleted)
566       {
567       $this->expunge($mailbox, FALSE);
568       $this->_clear_messagecount($mailbox);
569       }
570
571     // remove deleted messages from cache
572     if ($deleted && ($a_cached_headers = $this->get_cache($mailbox.'.msg')))
573       {
574       foreach ($a_uids as $uid)
575         unset($a_cached_headers[$uid]);
576
577       $this->update_cache($mailbox.'.msg', $a_cached_headers);
578       }
579
580     return $deleted;
581     }
582
583
584   // send IMAP expunge command and clear cache
585   function expunge($mbox='', $clear_cache=TRUE)
586     {
587     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
588     
589     $result = iil_C_Expunge($this->conn, $mailbox);
590
591     if ($result>=0 && $clear_cache)
592       {
593       $this->clear_cache($mailbox.'.msg');
594       $this->_clear_messagecount($mailbox);
595       }
596       
597     return $result;
598     }
599
600
601
602   /* --------------------------------
603    *        folder managment
604    * --------------------------------*/
605
606
607   // return an array with all folders available in IMAP server
608   function list_unsubscribed($root='')
609     {
610     static $sa_unsubscribed;
611     
612     if (is_array($sa_unsubscribed))
613       return $sa_unsubscribed;
614       
615     // retrieve list of folders from IMAP server
616     $a_mboxes = iil_C_ListMailboxes($this->conn, $this->_mod_mailbox($root), '*');
617
618     // modify names with root dir
619     foreach ($a_mboxes as $mbox)
620       {
621       $name = $this->_mod_mailbox($mbox, 'out');
622       if (strlen($name))
623         $a_folders[] = $name;
624       }
625
626     // filter folders and sort them
627     $sa_unsubscribed = $this->_sort_mailbox_list($a_folders);
628     return $sa_unsubscribed;
629     }
630
631
632   // subscribe to a specific mailbox(es)
633   function subscribe($mbox, $mode='subscribe')
634     {
635     if (is_array($mbox))
636       $a_mboxes = $mbox;
637     else if (is_string($mbox) && strlen($mbox))
638       $a_mboxes = explode(',', $mbox);
639     
640     // let this common function do the main work
641     return $this->_change_subscription($a_mboxes, 'subscribe');
642     }
643
644
645   // unsubscribe mailboxes
646   function unsubscribe($mbox)
647     {
648     if (is_array($mbox))
649       $a_mboxes = $mbox;
650     else if (is_string($mbox) && strlen($mbox))
651       $a_mboxes = explode(',', $mbox);
652
653     // let this common function do the main work
654     return $this->_change_subscription($a_mboxes, 'unsubscribe');
655     }
656
657
658   // create a new mailbox on the server and register it in local cache
659   function create_mailbox($name, $subscribe=FALSE)
660     {
661     $result = FALSE;
662     $abs_name = $this->_mod_mailbox($name);
663     $a_mailbox_cache = $this->get_cache('mailboxes');
520c36 664     
T 665     if (strlen($this->root_ns))
666       $abs_name = $this->root_ns.$abs_name;
4e17e6 667
T 668     if (strlen($abs_name) && (!is_array($a_mailbox_cache) || !in_array($abs_name, $a_mailbox_cache)))
520c36 669       $result = iil_C_CreateFolder($this->conn, iil_utf7_encode($abs_name));
4e17e6 670
T 671     // update mailboxlist cache
672     if ($result && $subscribe)
520c36 673       $this->subscribe($this->root_ns.$name);
4e17e6 674
520c36 675     return $result ? $this->root_ns.$name : FALSE;
4e17e6 676     }
T 677
678
679   // set a new name to an existing mailbox
680   function rename_mailbox($mbox, $new_name)
681     {
682     // not implemented yet
683     }
684
685
686   // remove mailboxes from server
687   function delete_mailbox($mbox)
688     {
689     $deleted = FALSE;
690
691     if (is_array($mbox))
692       $a_mboxes = $mbox;
693     else if (is_string($mbox) && strlen($mbox))
694       $a_mboxes = explode(',', $mbox);
695
696     if (is_array($a_mboxes))
697       foreach ($a_mboxes as $mbox)
698         {
699         $mailbox = $this->_mod_mailbox($mbox);
700
701         // unsubscribe mailbox before deleting
702         iil_C_UnSubscribe($this->conn, $mailbox);
703         
704         // send delete command to server
705         $result = iil_C_DeleteFolder($this->conn, $mailbox);
706         if ($result>=0)
707           $deleted = TRUE;
708         }
709
710     // clear mailboxlist cache
711     if ($deleted)
712       $this->clear_cache('mailboxes');
713
714     return $updated;
715     }
716
717
718
719
720   /* --------------------------------
6dc026 721    *   internal caching functions
4e17e6 722    * --------------------------------*/
6dc026 723
T 724
725   function set_caching($set)
726     {
727     if ($set && function_exists('rcube_read_cache'))
728       $this->caching_enabled = TRUE;
729     else
730       $this->caching_enabled = FALSE;
731     }
4e17e6 732
T 733   function get_cache($key)
734     {
735     // read cache
6dc026 736     if (!isset($this->cache[$key]) && $this->caching_enabled)
4e17e6 737       {
T 738       $cache_data = rcube_read_cache('IMAP.'.$key);
739       $this->cache[$key] = strlen($cache_data) ? unserialize($cache_data) : FALSE;
740       }
741     
742     return $this->cache[$key];         
743     }
744
745
746   function update_cache($key, $data)
747     {
748     $this->cache[$key] = $data;
749     $this->cache_changed = TRUE;
750     $this->cache_changes[$key] = TRUE;
751     }
752
753
754   function write_cache()
755     {
6dc026 756     if ($this->caching_enabled && $this->cache_changed)
4e17e6 757       {
T 758       foreach ($this->cache as $key => $data)
759         {
760         if ($this->cache_changes[$key])
761           rcube_write_cache('IMAP.'.$key, serialize($data));
762         }
763       }    
764     }
765
766
767   function clear_cache($key=NULL)
768     {
769     if ($key===NULL)
770       {
771       foreach ($this->cache as $key => $data)
772         rcube_clear_cache('IMAP.'.$key);
773
774       $this->cache = array();
775       $this->cache_changed = FALSE;
776       $this->cache_changes = array();
777       }
778     else
779       {
780       rcube_clear_cache('IMAP.'.$key);
781       $this->cache_changes[$key] = FALSE;
782       unset($this->cache[$key]);
783       }
784     }
785
786
787
788   /* --------------------------------
789    *   encoding/decoding functions
790    * --------------------------------*/
791
792   
793   function decode_address_list($input, $max=NULL)
794     {
795     $a = $this->_parse_address_list($input);
796     $out = array();
797
798     if (!is_array($a))
799       return $out;
800
801     $c = count($a);
802     $j = 0;
803
804     foreach ($a as $val)
805       {
806       $j++;
807       $address = $val['address'];
808       $name = preg_replace(array('/^[\'"]/', '/[\'"]$/'), '', trim($val['name']));
809       $string = $name!==$address ? sprintf('%s <%s>', strpos($name, ',')!==FALSE ? '"'.$name.'"' : $name, $address) : $address;
810       
811       $out[$j] = array('name' => $name,
812                        'mailto' => $address,
813                        'string' => $string);
814               
815       if ($max && $j==$max)
816         break;
817       }
818     
819     return $out;
820     }
821
822
823   function decode_header($input)
824     {
825     $out = '';
826
827     $pos = strpos($input, '=?');
828     if ($pos !== false)
829       {
830       $out = substr($input, 0, $pos);
831   
832       $end_cs_pos = strpos($input, "?", $pos+2);
833       $end_en_pos = strpos($input, "?", $end_cs_pos+1);
834       $end_pos = strpos($input, "?=", $end_en_pos+1);
835   
836       $encstr = substr($input, $pos+2, ($end_pos-$pos-2));
837       $rest = substr($input, $end_pos+2);
838
839       $out .= $this->decode_mime_string($encstr);
840       $out .= $this->decode_header($rest);
841
842       return $out;
843       }
844     else
845       return $input;
846     }
847
848
849   function decode_mime_string($str)
850     {
851     $a = explode('?', $str);
852     $count = count($a);
853
854     // should be in format "charset?encoding?base64_string"
855     if ($count >= 3)
856       {
857       for ($i=2; $i<$count; $i++)
858         $rest.=$a[$i];
859
860       if (($a[1]=="B")||($a[1]=="b"))
861         $rest = base64_decode($rest);
862       else if (($a[1]=="Q")||($a[1]=="q"))
863         {
864         $rest = str_replace("_", " ", $rest);
865         $rest = quoted_printable_decode($rest);
866         }
867
868       return decode_specialchars($rest, $a[0]);
869       }
870     else
871       return $str;    //we dont' know what to do with this  
872     }
873
874
875   function mime_decode($input, $encoding='7bit')
876     {
877     switch (strtolower($encoding))
878       {
879       case '7bit':
880         return $input;
881         break;
882       
883       case 'quoted-printable':
884         return quoted_printable_decode($input);
885         break;
886       
887       case 'base64':
888         return base64_decode($input);
889         break;
890       
891       default:
892         return $input;
893       }
894     }
895
896
897   function mime_encode($input, $encoding='7bit')
898     {
899     switch ($encoding)
900       {
901       case 'quoted-printable':
902         return quoted_printable_encode($input);
903         break;
904
905       case 'base64':
906         return base64_encode($input);
907         break;
908
909       default:
910         return $input;
911       }
912     }
913
914
915   // convert body chars according to the ctype_parameters
916   function charset_decode($body, $ctype_param)
917     {
918     if (is_array($ctype_param) && strlen($ctype_param['charset']))
919       return decode_specialchars($body, $ctype_param['charset']);
920
921     return $body;
922     }
923
924
925   /* --------------------------------
926    *         private methods
927    * --------------------------------*/
928
929
930   function _mod_mailbox($mbox, $mode='in')
931     {
520c36 932     if (!empty($this->root_dir) && $mode=='in')
T 933       $mbox = $this->root_dir.$this->delimiter.$mbox;
934     else if (strlen($this->root_dir) && $mode=='out')
4e17e6 935       $mbox = substr($mbox, strlen($this->root_dir)+1);
T 936
937     return $mbox;
938     }
939
940
941   // sort mailboxes first by default folders and then in alphabethical order
942   function _sort_mailbox_list($a_folders)
943     {
944     $a_out = $a_defaults = array();
945
946     // find default folders and skip folders starting with '.'
947     foreach($a_folders as $i => $folder)
948       {
949       if ($folder{0}=='.')
950           continue;
951           
952       if (($p = array_search(strtolower($folder), $this->default_folders))!==FALSE)
953           $a_defaults[$p] = $folder;
954       else
955         $a_out[] = $folder;
956       }
957
958     sort($a_out);
959     ksort($a_defaults);
960     
961     return array_merge($a_defaults, $a_out);
962     }
963
964
965   function _uid2id($uid, $mbox=NULL)
966     {
967     if (!$mbox)
968       $mbox = $this->mailbox;
969       
970     if (!isset($this->uid_id_map[$mbox][$uid]))
971       $this->uid_id_map[$mbox][$uid] = iil_C_UID2ID($this->conn, $mbox, $uid);
972
973     return $this->uid_id_map[$mbox][$uid];
974     }
975
976
977   // subscribe/unsubscribe a list of mailboxes and update local cache
978   function _change_subscription($a_mboxes, $mode)
979     {
980     $updated = FALSE;
981     
982     if (is_array($a_mboxes))
983       foreach ($a_mboxes as $i => $mbox)
984         {
985         $mailbox = $this->_mod_mailbox($mbox);
986         $a_mboxes[$i] = $mailbox;
987
988         if ($mode=='subscribe')
989           $result = iil_C_Subscribe($this->conn, $mailbox);
990         else if ($mode=='unsubscribe')
991           $result = iil_C_UnSubscribe($this->conn, $mailbox);
992
993         if ($result>=0)
994           $updated = TRUE;
995         }
996         
997     // get cached mailbox list    
998     if ($updated)
999       {
1000       $a_mailbox_cache = $this->get_cache('mailboxes');
1001       if (!is_array($a_mailbox_cache))
1002         return $updated;
1003
1004       // modify cached list
1005       if ($mode=='subscribe')
1006         $a_mailbox_cache = array_merge($a_mailbox_cache, $a_mboxes);
1007       else if ($mode=='unsubscribe')
1008         $a_mailbox_cache = array_diff($a_mailbox_cache, $a_mboxes);
1009         
1010       // write mailboxlist to cache
1011       $this->update_cache('mailboxes', $this->_sort_mailbox_list($a_mailbox_cache));
1012       }
1013
1014     return $updated;
1015     }
1016
1017
1018   // increde/decrese messagecount for a specific mailbox
1019   function _set_messagecount($mbox, $mode, $increment)
1020     {
1021     $a_mailbox_cache = FALSE;
1022     $mailbox = $mbox ? $mbox : $this->mailbox;
1023     $mode = strtoupper($mode);
1024
1025     $a_mailbox_cache = $this->get_cache('messagecount');
1026     
1027     if (!is_array($a_mailbox_cache[$mailbox]) || !isset($a_mailbox_cache[$mailbox][$mode]) || !is_numeric($increment))
1028       return FALSE;
1029     
1030     // add incremental value to messagecount
1031     $a_mailbox_cache[$mailbox][$mode] += $increment;
1032
1033     // write back to cache
1034     $this->update_cache('messagecount', $a_mailbox_cache);
1035     
1036     return TRUE;
1037     }
1038
1039
1040   // remove messagecount of a specific mailbox from cache
1041   function _clear_messagecount($mbox='')
1042     {
1043     $a_mailbox_cache = FALSE;
1044     $mailbox = $mbox ? $mbox : $this->mailbox;
1045
1046     $a_mailbox_cache = $this->get_cache('messagecount');
1047
1048     if (is_array($a_mailbox_cache[$mailbox]))
1049       {
1050       unset($a_mailbox_cache[$mailbox]);
1051       $this->update_cache('messagecount', $a_mailbox_cache);
1052       }
1053     }
1054
1055
1056   function _parse_address_list($str)
1057     {
1058     $a = $this->_explode_quoted_string(',', $str);
1059     $result = array();
1060
1061     foreach ($a as $key => $val)
1062       {
1063       $val = str_replace("\"<", "\" <", $val);
1064       $sub_a = $this->_explode_quoted_string(' ', $val);
1065       
1066       foreach ($sub_a as $k => $v)
1067         {
1068         if ((strpos($v, '@') > 0) && (strpos($v, '.') > 0)) 
1069           $result[$key]['address'] = str_replace('<', '', str_replace('>', '', $v));
1070         else
1071           $result[$key]['name'] .= (empty($result[$key]['name'])?'':' ').str_replace("\"",'',stripslashes($v));
1072         }
1073         
1074       if (empty($result[$key]['name']))
1075         $result[$key]['name'] = $result[$key]['address'];
1076         
1077       $result[$key]['name'] = $this->decode_header($result[$key]['name']);
1078       }
1079     
1080     return $result;
1081     }
1082
1083
1084   function _explode_quoted_string($delimiter, $string)
1085     {
1086     $quotes = explode("\"", $string);
1087     foreach ($quotes as $key => $val)
1088       if (($key % 2) == 1)
1089         $quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]);
1090         
1091     $string = implode("\"", $quotes);
1092
1093     $result = explode($delimiter, $string);
1094     foreach ($result as $key => $val) 
1095       $result[$key] = str_replace("_!@!_", $delimiter, $result[$key]);
1096     
1097     return $result;
1098     }
1099   }
1100
1101
1102
1103
1104
1105 function quoted_printable_encode($input="", $line_max=76, $space_conv=false)
1106   {
1107   $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
1108   $lines = preg_split("/(?:\r\n|\r|\n)/", $input);
1109   $eol = "\r\n";
1110   $escape = "=";
1111   $output = "";
1112
1113   while( list(, $line) = each($lines))
1114     {
1115     //$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary
1116     $linlen = strlen($line);
1117     $newline = "";
1118     for($i = 0; $i < $linlen; $i++)
1119       {
1120       $c = substr( $line, $i, 1 );
1121       $dec = ord( $c );
1122       if ( ( $i == 0 ) && ( $dec == 46 ) ) // convert first point in the line into =2E
1123         {
1124         $c = "=2E";
1125         }
1126       if ( $dec == 32 )
1127         {
1128         if ( $i == ( $linlen - 1 ) ) // convert space at eol only
1129           {
1130           $c = "=20";
1131           }
1132         else if ( $space_conv )
1133           {
1134           $c = "=20";
1135           }
1136         }
1137       else if ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) )  // always encode "\t", which is *not* required
1138         {
1139         $h2 = floor($dec/16);
1140         $h1 = floor($dec%16);
1141         $c = $escape.$hex["$h2"].$hex["$h1"];
1142         }
1143          
1144       if ( (strlen($newline) + strlen($c)) >= $line_max )  // CRLF is not counted
1145         {
1146         $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay
1147         $newline = "";
1148         // check if newline first character will be point or not
1149         if ( $dec == 46 )
1150           {
1151           $c = "=2E";
1152           }
1153         }
1154       $newline .= $c;
1155       } // end of for
1156     $output .= $newline.$eol;
1157     } // end of while
1158
1159   return trim($output);
1160   }
1161
1162 ?>