svncommit
2005-10-14 17fc718915a856a5bec7a415031acbba76c8bcfe
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
323     // sort headers by a specific col
324     $a_headers = iil_SortHeaders($a_msg_headers, $sort_field, $sort_order);
17fc71 325     // free memory
S 326     unset($a_msg_headers);
327     
4e17e6 328     // write headers list to cache
T 329     if (!$headers_cached)
17fc71 330       $this->update_cache($mailbox.'.msg', $a_headers);
4e17e6 331
T 332     // return complete list of messages
333     if (strtolower($page)=='all')
17fc71 334       return $a_headers;
S 335     
4e17e6 336     $start_msg = ($this->list_page-1) * $this->page_size;
17fc71 337     return array_slice($a_headers, $start_msg, $this->page_size);
4e17e6 338     }
T 339
340
341   // return sorted array of message UIDs
342   function message_index($mbox='', $sort_field='date', $sort_order='DESC')
343     {
344     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
345     $a_out = array();
346
347     // get array of message headers
348     $a_headers = $this->_list_headers($mailbox, 'all', $sort_field, $sort_order);
349
350     if (is_array($a_headers))
351       foreach ($a_headers as $header)
352         $a_out[] = $header->uid;
353
354     return $a_out;
355     }
356
357
358   function sync_header_index($mbox=NULL)
359     {
360     
361     }
362
363
364   function search($mbox='', $criteria='ALL')
365     {
366     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
367     $a_messages = iil_C_Search($this->conn, $mailbox, $criteria);
368     return $a_messages;
369     }
370
371
372   function get_headers($uid, $mbox=NULL)
373     {
374     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
375     
376     // get cached headers
377     $a_msg_headers = $this->get_cache($mailbox.'.msg');
378     
379     // return cached header
380     if ($a_msg_headers[$uid])
381       return $a_msg_headers[$uid];
520c36 382
4e17e6 383     $msg_id = $this->_uid2id($uid);
T 384     $header = iil_C_FetchHeader($this->conn, $mailbox, $msg_id);
520c36 385
4e17e6 386     // write headers cache
T 387     $a_msg_headers[$uid] = $header;
388     $this->update_cache($mailbox.'.msg', $a_msg_headers);
389
390     return $header;
391     }
392
393
394   function get_body($uid, $part=1)
395     {
396     if (!($msg_id = $this->_uid2id($uid)))
397       return FALSE;
398
399     $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id); 
400     $structure = iml_GetRawStructureArray($structure_str);
401     $body = iil_C_FetchPartBody($this->conn, $this->mailbox, $msg_id, $part);
402
403     $encoding = iml_GetPartEncodingCode($structure, $part);
404     
405     if ($encoding==3) $body = $this->mime_decode($body, 'base64');
406     else if ($encoding==4) $body = $this->mime_decode($body, 'quoted-printable');
407
408     return $body;
409     }
410
411
412   function get_raw_body($uid)
413     {
414     if (!($msg_id = $this->_uid2id($uid)))
415       return FALSE;
416
417     $body = iil_C_FetchPartHeader($this->conn, $this->mailbox, $msg_id, NULL);
418     $body .= iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, NULL, 1);
419
420     return $body;    
421     }
422
423
424   // set message flag to one or several messages
425   // possible flgs are: SEEN, DELETED, RECENT, ANSWERED, DRAFT
426   function set_flag($uids, $flag)
427     {
428     $flag = strtoupper($flag);
429     $msg_ids = array();
430     if (!is_array($uids))
431       $uids = array($uids);
432       
433     foreach ($uids as $uid)
434       $msg_ids[] = $this->_uid2id($uid);
435       
436     if ($flag=='UNSEEN')
437       $result = iil_C_Unseen($this->conn, $this->mailbox, join(',', $msg_ids));
438     else
439       $result = iil_C_Flag($this->conn, $this->mailbox, join(',', $msg_ids), $flag);
440
441     // reload message headers if cached
442     $cache_key = $this->mailbox.'.msg';
520c36 443     if ($this->caching_enabled && $result && ($a_cached_headers = $this->get_cache($cache_key)))
4e17e6 444       {
520c36 445       // close and re-open connection      
T 446       $this->reconnect();
447
4e17e6 448       foreach ($uids as $uid)
T 449         {
450         if (isset($a_cached_headers[$uid]))
451           {
452           unset($this->cache[$cache_key][$uid]);
453           $this->get_headers($uid);
454           }
455         }
456       }
457
458     // set nr of messages that were flaged
459     $count = sizeof($msg_ids);
460
461     // clear message count cache
462     if ($result && $flag=='SEEN')
463       $this->_set_messagecount($this->mailbox, 'UNSEEN', $count*(-1));
464     else if ($result && $flag=='UNSEEN')
465       $this->_set_messagecount($this->mailbox, 'UNSEEN', $count);
466     else if ($result && $flag=='DELETED')
467       $this->_set_messagecount($this->mailbox, 'ALL', $count*(-1));
468
469     return $result;
470     }
471
472
473   // append a mail message (source) to a specific mailbox
474   function save_message($mbox, $message)
475     {
476     $mailbox = $this->_mod_mailbox($mbox);
477
478     // make shure mailbox exists
479     if (in_array($mailbox, $this->_list_mailboxes()))
480       $saved = iil_C_Append($this->conn, $mailbox, $message);
481     
482     if ($saved)
483       {
484       // increase messagecount of the target mailbox
485       $this->_set_messagecount($mailbox, 'ALL', 1);
486       }
487           
488     return $saved;
489     }
490
491
492   // move a message from one mailbox to another
493   function move_message($uids, $to_mbox, $from_mbox='')
494     {
495     $to_mbox = $this->_mod_mailbox($to_mbox);
496     $from_mbox = $from_mbox ? $this->_mod_mailbox($from_mbox) : $this->mailbox;
497
498     // make shure mailbox exists
499     if (!in_array($to_mbox, $this->_list_mailboxes()))
500       return FALSE;
501     
502     // convert the list of uids to array
503     $a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL);
504     
505     // exit if no message uids are specified
506     if (!is_array($a_uids))
507       return false;
520c36 508
4e17e6 509     // convert uids to message ids
T 510     $a_mids = array();
511     foreach ($a_uids as $uid)
512       $a_mids[] = $this->_uid2id($uid, $from_mbox);
520c36 513
4e17e6 514     $moved = iil_C_Move($this->conn, join(',', $a_mids), $from_mbox, $to_mbox);
T 515     
516     // send expunge command in order to have the moved message
517     // really deleted from the source mailbox
518     if ($moved)
519       {
520       $this->expunge($from_mbox, FALSE);
521       $this->clear_cache($to_mbox.'.msg');
522       $this->_clear_messagecount($from_mbox);
523       $this->_clear_messagecount($to_mbox);
524       }
525
526     // update cached message headers
527     $cache_key = $from_mbox.'.msg';
528     if ($moved && ($a_cached_headers = $this->get_cache($cache_key)))
529       {
530       foreach ($a_uids as $uid)
531         unset($a_cached_headers[$uid]);
532
533       $this->update_cache($cache_key, $a_cached_headers);
534       }
535
536     return $moved;
537     }
538
539
540   // mark messages as deleted and expunge mailbox
541   function delete_message($uids, $mbox='')
542     {
543     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
544
545     // convert the list of uids to array
546     $a_uids = is_string($uids) ? explode(',', $uids) : (is_array($uids) ? $uids : NULL);
547     
548     // exit if no message uids are specified
549     if (!is_array($a_uids))
550       return false;
551
552
553     // convert uids to message ids
554     $a_mids = array();
555     foreach ($a_uids as $uid)
556       $a_mids[] = $this->_uid2id($uid, $mailbox);
557         
558     $deleted = iil_C_Delete($this->conn, $mailbox, join(',', $a_mids));
559     
560     // send expunge command in order to have the deleted message
561     // really deleted from the mailbox
562     if ($deleted)
563       {
564       $this->expunge($mailbox, FALSE);
565       $this->_clear_messagecount($mailbox);
566       }
567
568     // remove deleted messages from cache
569     if ($deleted && ($a_cached_headers = $this->get_cache($mailbox.'.msg')))
570       {
571       foreach ($a_uids as $uid)
572         unset($a_cached_headers[$uid]);
573
574       $this->update_cache($mailbox.'.msg', $a_cached_headers);
575       }
576
577     return $deleted;
578     }
579
580
581   // send IMAP expunge command and clear cache
582   function expunge($mbox='', $clear_cache=TRUE)
583     {
584     $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
585     
586     $result = iil_C_Expunge($this->conn, $mailbox);
587
588     if ($result>=0 && $clear_cache)
589       {
590       $this->clear_cache($mailbox.'.msg');
591       $this->_clear_messagecount($mailbox);
592       }
593       
594     return $result;
595     }
596
597
598
599   /* --------------------------------
600    *        folder managment
601    * --------------------------------*/
602
603
604   // return an array with all folders available in IMAP server
605   function list_unsubscribed($root='')
606     {
607     static $sa_unsubscribed;
608     
609     if (is_array($sa_unsubscribed))
610       return $sa_unsubscribed;
611       
612     // retrieve list of folders from IMAP server
613     $a_mboxes = iil_C_ListMailboxes($this->conn, $this->_mod_mailbox($root), '*');
614
615     // modify names with root dir
616     foreach ($a_mboxes as $mbox)
617       {
618       $name = $this->_mod_mailbox($mbox, 'out');
619       if (strlen($name))
620         $a_folders[] = $name;
621       }
622
623     // filter folders and sort them
624     $sa_unsubscribed = $this->_sort_mailbox_list($a_folders);
625     return $sa_unsubscribed;
626     }
627
628
629   // subscribe to a specific mailbox(es)
630   function subscribe($mbox, $mode='subscribe')
631     {
632     if (is_array($mbox))
633       $a_mboxes = $mbox;
634     else if (is_string($mbox) && strlen($mbox))
635       $a_mboxes = explode(',', $mbox);
636     
637     // let this common function do the main work
638     return $this->_change_subscription($a_mboxes, 'subscribe');
639     }
640
641
642   // unsubscribe mailboxes
643   function unsubscribe($mbox)
644     {
645     if (is_array($mbox))
646       $a_mboxes = $mbox;
647     else if (is_string($mbox) && strlen($mbox))
648       $a_mboxes = explode(',', $mbox);
649
650     // let this common function do the main work
651     return $this->_change_subscription($a_mboxes, 'unsubscribe');
652     }
653
654
655   // create a new mailbox on the server and register it in local cache
656   function create_mailbox($name, $subscribe=FALSE)
657     {
658     $result = FALSE;
659     $abs_name = $this->_mod_mailbox($name);
660     $a_mailbox_cache = $this->get_cache('mailboxes');
520c36 661     
T 662     if (strlen($this->root_ns))
663       $abs_name = $this->root_ns.$abs_name;
4e17e6 664
T 665     if (strlen($abs_name) && (!is_array($a_mailbox_cache) || !in_array($abs_name, $a_mailbox_cache)))
520c36 666       $result = iil_C_CreateFolder($this->conn, iil_utf7_encode($abs_name));
4e17e6 667
T 668     // update mailboxlist cache
669     if ($result && $subscribe)
520c36 670       $this->subscribe($this->root_ns.$name);
4e17e6 671
520c36 672     return $result ? $this->root_ns.$name : FALSE;
4e17e6 673     }
T 674
675
676   // set a new name to an existing mailbox
677   function rename_mailbox($mbox, $new_name)
678     {
679     // not implemented yet
680     }
681
682
683   // remove mailboxes from server
684   function delete_mailbox($mbox)
685     {
686     $deleted = FALSE;
687
688     if (is_array($mbox))
689       $a_mboxes = $mbox;
690     else if (is_string($mbox) && strlen($mbox))
691       $a_mboxes = explode(',', $mbox);
692
693     if (is_array($a_mboxes))
694       foreach ($a_mboxes as $mbox)
695         {
696         $mailbox = $this->_mod_mailbox($mbox);
697
698         // unsubscribe mailbox before deleting
699         iil_C_UnSubscribe($this->conn, $mailbox);
700         
701         // send delete command to server
702         $result = iil_C_DeleteFolder($this->conn, $mailbox);
703         if ($result>=0)
704           $deleted = TRUE;
705         }
706
707     // clear mailboxlist cache
708     if ($deleted)
709       $this->clear_cache('mailboxes');
710
711     return $updated;
712     }
713
714
715
716
717   /* --------------------------------
6dc026 718    *   internal caching functions
4e17e6 719    * --------------------------------*/
6dc026 720
T 721
722   function set_caching($set)
723     {
724     if ($set && function_exists('rcube_read_cache'))
725       $this->caching_enabled = TRUE;
726     else
727       $this->caching_enabled = FALSE;
728     }
4e17e6 729
T 730   function get_cache($key)
731     {
732     // read cache
6dc026 733     if (!isset($this->cache[$key]) && $this->caching_enabled)
4e17e6 734       {
T 735       $cache_data = rcube_read_cache('IMAP.'.$key);
736       $this->cache[$key] = strlen($cache_data) ? unserialize($cache_data) : FALSE;
737       }
738     
739     return $this->cache[$key];         
740     }
741
742
743   function update_cache($key, $data)
744     {
745     $this->cache[$key] = $data;
746     $this->cache_changed = TRUE;
747     $this->cache_changes[$key] = TRUE;
748     }
749
750
751   function write_cache()
752     {
6dc026 753     if ($this->caching_enabled && $this->cache_changed)
4e17e6 754       {
T 755       foreach ($this->cache as $key => $data)
756         {
757         if ($this->cache_changes[$key])
758           rcube_write_cache('IMAP.'.$key, serialize($data));
759         }
760       }    
761     }
762
763
764   function clear_cache($key=NULL)
765     {
766     if ($key===NULL)
767       {
768       foreach ($this->cache as $key => $data)
769         rcube_clear_cache('IMAP.'.$key);
770
771       $this->cache = array();
772       $this->cache_changed = FALSE;
773       $this->cache_changes = array();
774       }
775     else
776       {
777       rcube_clear_cache('IMAP.'.$key);
778       $this->cache_changes[$key] = FALSE;
779       unset($this->cache[$key]);
780       }
781     }
782
783
784
785   /* --------------------------------
786    *   encoding/decoding functions
787    * --------------------------------*/
788
789   
790   function decode_address_list($input, $max=NULL)
791     {
792     $a = $this->_parse_address_list($input);
793     $out = array();
794
795     if (!is_array($a))
796       return $out;
797
798     $c = count($a);
799     $j = 0;
800
801     foreach ($a as $val)
802       {
803       $j++;
804       $address = $val['address'];
805       $name = preg_replace(array('/^[\'"]/', '/[\'"]$/'), '', trim($val['name']));
806       $string = $name!==$address ? sprintf('%s <%s>', strpos($name, ',')!==FALSE ? '"'.$name.'"' : $name, $address) : $address;
807       
808       $out[$j] = array('name' => $name,
809                        'mailto' => $address,
810                        'string' => $string);
811               
812       if ($max && $j==$max)
813         break;
814       }
815     
816     return $out;
817     }
818
819
820   function decode_header($input)
821     {
822     $out = '';
823
824     $pos = strpos($input, '=?');
825     if ($pos !== false)
826       {
827       $out = substr($input, 0, $pos);
828   
829       $end_cs_pos = strpos($input, "?", $pos+2);
830       $end_en_pos = strpos($input, "?", $end_cs_pos+1);
831       $end_pos = strpos($input, "?=", $end_en_pos+1);
832   
833       $encstr = substr($input, $pos+2, ($end_pos-$pos-2));
834       $rest = substr($input, $end_pos+2);
835
836       $out .= $this->decode_mime_string($encstr);
837       $out .= $this->decode_header($rest);
838
839       return $out;
840       }
841     else
842       return $input;
843     }
844
845
846   function decode_mime_string($str)
847     {
848     $a = explode('?', $str);
849     $count = count($a);
850
851     // should be in format "charset?encoding?base64_string"
852     if ($count >= 3)
853       {
854       for ($i=2; $i<$count; $i++)
855         $rest.=$a[$i];
856
857       if (($a[1]=="B")||($a[1]=="b"))
858         $rest = base64_decode($rest);
859       else if (($a[1]=="Q")||($a[1]=="q"))
860         {
861         $rest = str_replace("_", " ", $rest);
862         $rest = quoted_printable_decode($rest);
863         }
864
865       return decode_specialchars($rest, $a[0]);
866       }
867     else
868       return $str;    //we dont' know what to do with this  
869     }
870
871
872   function mime_decode($input, $encoding='7bit')
873     {
874     switch (strtolower($encoding))
875       {
876       case '7bit':
877         return $input;
878         break;
879       
880       case 'quoted-printable':
881         return quoted_printable_decode($input);
882         break;
883       
884       case 'base64':
885         return base64_decode($input);
886         break;
887       
888       default:
889         return $input;
890       }
891     }
892
893
894   function mime_encode($input, $encoding='7bit')
895     {
896     switch ($encoding)
897       {
898       case 'quoted-printable':
899         return quoted_printable_encode($input);
900         break;
901
902       case 'base64':
903         return base64_encode($input);
904         break;
905
906       default:
907         return $input;
908       }
909     }
910
911
912   // convert body chars according to the ctype_parameters
913   function charset_decode($body, $ctype_param)
914     {
915     if (is_array($ctype_param) && strlen($ctype_param['charset']))
916       return decode_specialchars($body, $ctype_param['charset']);
917
918     return $body;
919     }
920
921
922   /* --------------------------------
923    *         private methods
924    * --------------------------------*/
925
926
927   function _mod_mailbox($mbox, $mode='in')
928     {
520c36 929     if (!empty($this->root_dir) && $mode=='in')
T 930       $mbox = $this->root_dir.$this->delimiter.$mbox;
931     else if (strlen($this->root_dir) && $mode=='out')
4e17e6 932       $mbox = substr($mbox, strlen($this->root_dir)+1);
T 933
934     return $mbox;
935     }
936
937
938   // sort mailboxes first by default folders and then in alphabethical order
939   function _sort_mailbox_list($a_folders)
940     {
941     $a_out = $a_defaults = array();
942
943     // find default folders and skip folders starting with '.'
944     foreach($a_folders as $i => $folder)
945       {
946       if ($folder{0}=='.')
947           continue;
948           
949       if (($p = array_search(strtolower($folder), $this->default_folders))!==FALSE)
950           $a_defaults[$p] = $folder;
951       else
952         $a_out[] = $folder;
953       }
954
955     sort($a_out);
956     ksort($a_defaults);
957     
958     return array_merge($a_defaults, $a_out);
959     }
960
961
962   function _uid2id($uid, $mbox=NULL)
963     {
964     if (!$mbox)
965       $mbox = $this->mailbox;
966       
967     if (!isset($this->uid_id_map[$mbox][$uid]))
968       $this->uid_id_map[$mbox][$uid] = iil_C_UID2ID($this->conn, $mbox, $uid);
969
970     return $this->uid_id_map[$mbox][$uid];
971     }
972
973
974   // subscribe/unsubscribe a list of mailboxes and update local cache
975   function _change_subscription($a_mboxes, $mode)
976     {
977     $updated = FALSE;
978     
979     if (is_array($a_mboxes))
980       foreach ($a_mboxes as $i => $mbox)
981         {
982         $mailbox = $this->_mod_mailbox($mbox);
983         $a_mboxes[$i] = $mailbox;
984
985         if ($mode=='subscribe')
986           $result = iil_C_Subscribe($this->conn, $mailbox);
987         else if ($mode=='unsubscribe')
988           $result = iil_C_UnSubscribe($this->conn, $mailbox);
989
990         if ($result>=0)
991           $updated = TRUE;
992         }
993         
994     // get cached mailbox list    
995     if ($updated)
996       {
997       $a_mailbox_cache = $this->get_cache('mailboxes');
998       if (!is_array($a_mailbox_cache))
999         return $updated;
1000
1001       // modify cached list
1002       if ($mode=='subscribe')
1003         $a_mailbox_cache = array_merge($a_mailbox_cache, $a_mboxes);
1004       else if ($mode=='unsubscribe')
1005         $a_mailbox_cache = array_diff($a_mailbox_cache, $a_mboxes);
1006         
1007       // write mailboxlist to cache
1008       $this->update_cache('mailboxes', $this->_sort_mailbox_list($a_mailbox_cache));
1009       }
1010
1011     return $updated;
1012     }
1013
1014
1015   // increde/decrese messagecount for a specific mailbox
1016   function _set_messagecount($mbox, $mode, $increment)
1017     {
1018     $a_mailbox_cache = FALSE;
1019     $mailbox = $mbox ? $mbox : $this->mailbox;
1020     $mode = strtoupper($mode);
1021
1022     $a_mailbox_cache = $this->get_cache('messagecount');
1023     
1024     if (!is_array($a_mailbox_cache[$mailbox]) || !isset($a_mailbox_cache[$mailbox][$mode]) || !is_numeric($increment))
1025       return FALSE;
1026     
1027     // add incremental value to messagecount
1028     $a_mailbox_cache[$mailbox][$mode] += $increment;
1029
1030     // write back to cache
1031     $this->update_cache('messagecount', $a_mailbox_cache);
1032     
1033     return TRUE;
1034     }
1035
1036
1037   // remove messagecount of a specific mailbox from cache
1038   function _clear_messagecount($mbox='')
1039     {
1040     $a_mailbox_cache = FALSE;
1041     $mailbox = $mbox ? $mbox : $this->mailbox;
1042
1043     $a_mailbox_cache = $this->get_cache('messagecount');
1044
1045     if (is_array($a_mailbox_cache[$mailbox]))
1046       {
1047       unset($a_mailbox_cache[$mailbox]);
1048       $this->update_cache('messagecount', $a_mailbox_cache);
1049       }
1050     }
1051
1052
1053   function _parse_address_list($str)
1054     {
1055     $a = $this->_explode_quoted_string(',', $str);
1056     $result = array();
1057
1058     foreach ($a as $key => $val)
1059       {
1060       $val = str_replace("\"<", "\" <", $val);
1061       $sub_a = $this->_explode_quoted_string(' ', $val);
1062       
1063       foreach ($sub_a as $k => $v)
1064         {
1065         if ((strpos($v, '@') > 0) && (strpos($v, '.') > 0)) 
1066           $result[$key]['address'] = str_replace('<', '', str_replace('>', '', $v));
1067         else
1068           $result[$key]['name'] .= (empty($result[$key]['name'])?'':' ').str_replace("\"",'',stripslashes($v));
1069         }
1070         
1071       if (empty($result[$key]['name']))
1072         $result[$key]['name'] = $result[$key]['address'];
1073         
1074       $result[$key]['name'] = $this->decode_header($result[$key]['name']);
1075       }
1076     
1077     return $result;
1078     }
1079
1080
1081   function _explode_quoted_string($delimiter, $string)
1082     {
1083     $quotes = explode("\"", $string);
1084     foreach ($quotes as $key => $val)
1085       if (($key % 2) == 1)
1086         $quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]);
1087         
1088     $string = implode("\"", $quotes);
1089
1090     $result = explode($delimiter, $string);
1091     foreach ($result as $key => $val) 
1092       $result[$key] = str_replace("_!@!_", $delimiter, $result[$key]);
1093     
1094     return $result;
1095     }
1096   }
1097
1098
1099
1100
1101
1102 function quoted_printable_encode($input="", $line_max=76, $space_conv=false)
1103   {
1104   $hex = array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
1105   $lines = preg_split("/(?:\r\n|\r|\n)/", $input);
1106   $eol = "\r\n";
1107   $escape = "=";
1108   $output = "";
1109
1110   while( list(, $line) = each($lines))
1111     {
1112     //$line = rtrim($line); // remove trailing white space -> no =20\r\n necessary
1113     $linlen = strlen($line);
1114     $newline = "";
1115     for($i = 0; $i < $linlen; $i++)
1116       {
1117       $c = substr( $line, $i, 1 );
1118       $dec = ord( $c );
1119       if ( ( $i == 0 ) && ( $dec == 46 ) ) // convert first point in the line into =2E
1120         {
1121         $c = "=2E";
1122         }
1123       if ( $dec == 32 )
1124         {
1125         if ( $i == ( $linlen - 1 ) ) // convert space at eol only
1126           {
1127           $c = "=20";
1128           }
1129         else if ( $space_conv )
1130           {
1131           $c = "=20";
1132           }
1133         }
1134       else if ( ($dec == 61) || ($dec < 32 ) || ($dec > 126) )  // always encode "\t", which is *not* required
1135         {
1136         $h2 = floor($dec/16);
1137         $h1 = floor($dec%16);
1138         $c = $escape.$hex["$h2"].$hex["$h1"];
1139         }
1140          
1141       if ( (strlen($newline) + strlen($c)) >= $line_max )  // CRLF is not counted
1142         {
1143         $output .= $newline.$escape.$eol; // soft line break; " =\r\n" is okay
1144         $newline = "";
1145         // check if newline first character will be point or not
1146         if ( $dec == 46 )
1147           {
1148           $c = "=2E";
1149           }
1150         }
1151       $newline .= $c;
1152       } // end of for
1153     $output .= $newline.$eol;
1154     } // end of while
1155
1156   return trim($output);
1157   }
1158
1159 ?>