5 * Copyright (c) 1999-2004 The SquirrelMail Project Team
6 * Licensed under the GNU GPL. For full terms see the file COPYING.
8 * This impliments all functions that manipulate mailboxes
11 * @package squirrelmail
16 require_once(SM_PATH
. 'functions/imap_utf7_local.php');
23 * FIXME. This class should be extracted and placed in a separate file that
24 * can be included before we start the session. That makes caching of the tree
25 * possible. On a refresh mailboxes from left_main.php the only function that
26 * should be called is the sqimap_get_status_mbx_tree. In case of subscribe
27 * / rename / delete / new we have to create methods for adding/changing the
28 * mailbox in the mbx_tree without the need for a refresh.
29 * @package squirrelmail
33 var $mailboxname_full = '', $mailboxname_sub= '', $is_noselect = false, $is_noinferiors = false,
34 $is_special = false, $is_root = false, $is_inbox = false, $is_sent = false,
35 $is_trash = false, $is_draft = false, $mbxs = array(),
36 $unseen = false, $total = false;
38 function addMbx($mbx, $delimiter, $start, $specialfirst) {
39 $ary = explode($delimiter, $mbx->mailboxname_full
);
41 for ($i = $start, $c = count($ary)-1; $i < $c; $i++
) {
42 $mbx_childs =& $mbx_parent->mbxs
;
45 foreach ($mbx_childs as $key => $parent) {
46 if ($parent->mailboxname_sub
== $ary[$i]) {
47 $mbx_parent =& $mbx_parent->mbxs
[$key];
54 $no_select_mbx = new mailboxes();
55 if (isset($mbx_parent->mailboxname_full
) && $mbx_parent->mailboxname_full
!= '') {
56 $no_select_mbx->mailboxname_full
= $mbx_parent->mailboxname_full
.$delimiter.$ary[$i];
58 $no_select_mbx->mailboxname_full
= $ary[$i];
60 $no_select_mbx->mailboxname_sub
= $ary[$i];
61 $no_select_mbx->is_noselect
= true;
62 $mbx_parent->mbxs
[] = $no_select_mbx;
66 $mbx_parent->mbxs
[] = $mbx;
67 if ($mbx->is_special
&& $specialfirst) {
68 usort($mbx_parent->mbxs
, 'sortSpecialMbx');
73 function sortSpecialMbx($a, $b) {
75 $acmp = '0'. $a->mailboxname_full
;
76 } else if ($a->is_special
) {
77 $acmp = '1'. $a->mailboxname_full
;
79 $acmp = '2' . $a->mailboxname_full
;
82 $bcmp = '0'. $b->mailboxname_full
;
83 }else if ($b->is_special
) {
84 $bcmp = '1' . $b->mailboxname_full
;
86 $bcmp = '2' . $b->mailboxname_full
;
88 return strnatcasecmp($acmp, $bcmp);
91 function compact_mailboxes_response($ary)
94 * Workaround for mailboxes returned as literal
95 * FIXME : Doesn't work if the mailbox name is multiple lines
96 * (larger then fgets buffer)
98 for ($i = 0, $iCnt=count($ary); $i < $iCnt; $i++
) {
99 if (isset($ary[$i +
1]) && substr($ary[$i], -3) == "}\r\n") {
100 if (ereg("^(\\* [A-Z]+.*)\\{[0-9]+\\}([ \n\r\t]*)$",
102 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+
1])) . '"' . $regs[2];
103 array_splice($ary, $i+
1, 2);
107 /* remove duplicates and ensure array is contiguous */
108 return array_values(array_unique($ary));
112 * Extract the mailbox name from an untagged LIST (7.2.2) or LSUB (7.2.3) answer
113 * (LIST|LSUB) (<Flags list>) (NIL|"<separator atom>") <mailbox name string>\r\n
114 * mailbox name in quoted string MUST be unquoted and stripslashed (sm API)
116 function find_mailbox_name($line)
118 if (preg_match('/^\* (?:LIST|LSUB) \([^\)]*\) (?:NIL|\"[^\"]*\") ([^\r\n]*)[\r\n]*$/i', $line, $regs)) {
119 if (substr($regs[1], 0, 1) == '"')
120 return stripslashes(substr($regs[1], 1, -1));
127 * @return bool whether this is a Noselect mailbox.
129 function check_is_noselect ($lsub_line) {
130 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i", $lsub_line);
134 * @return bool whether this is a Noinferiors mailbox.
136 function check_is_noinferiors ($lsub_line) {
137 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noinferiors[^\)]*\)/i", $lsub_line);
141 * If $haystack is a full mailbox name, and $needle is the mailbox
142 * separator character, returns the second last part of the full
143 * mailbox name (i.e. the mailbox's parent mailbox)
145 function readMailboxParent($haystack, $needle) {
149 $parts = explode($needle, $haystack);
150 $elem = array_pop($parts);
151 while ($elem == '' && count($parts)) {
152 $elem = array_pop($parts);
154 $ret = join($needle, $parts);
160 * Check if $subbox is below the specified $parentbox
162 function isBoxBelow( $subbox, $parentbox ) {
165 * Eliminate the obvious mismatch, where the
166 * subfolder path is shorter than that of the potential parent
168 if ( strlen($subbox) < strlen($parentbox) ) {
171 /* check for delimiter */
172 if (!substr($parentbox,-1) == $delimiter) {
173 $parentbox.=$delimiter;
175 if (substr($subbox,0,strlen($parentbox)) == $parentbox) {
183 * Defines special mailboxes: given a mailbox name, it checks if this is a
184 * "special" one: INBOX, Trash, Sent or Draft.
186 function isSpecialMailbox( $box ) {
187 $ret = ( (strtolower($box) == 'inbox') ||
188 isTrashMailbox($box) ||
isSentMailbox($box) ||
isDraftMailbox($box) );
191 $ret = boolean_hook_function('special_mailbox',$box,1);
197 * @return bool whether this is a Trash folder
199 function isTrashMailbox ($box) {
200 global $trash_folder, $move_to_trash;
201 return $move_to_trash && $trash_folder &&
202 ( $box == $trash_folder ||
isBoxBelow($box, $trash_folder) );
206 * @return bool whether this is a Sent folder
208 function isSentMailbox($box) {
209 global $sent_folder, $move_to_sent;
210 return $move_to_sent && $sent_folder &&
211 ( $box == $sent_folder ||
isBoxBelow($box, $sent_folder) );
215 * @return bool whether this is a Draft folder
217 function isDraftMailbox($box) {
218 global $draft_folder, $save_as_draft;
219 return $save_as_draft &&
220 ( $box == $draft_folder ||
isBoxBelow($box, $draft_folder) );
224 * Expunges a mailbox, ie. delete all contents.
226 function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
229 $id = sqimap_message_list_squisher($id);
236 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
237 $response, $message, $uid);
240 if (is_array($read)) {
241 foreach ($read as $r) {
242 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
251 * Checks whether or not the specified mailbox exists
253 function sqimap_mailbox_exists ($imap_stream, $mailbox) {
254 if (!isset($mailbox) ||
empty($mailbox)) {
257 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mailbox),
258 true, $response, $message);
259 return isset($mbx[0]);
265 function sqimap_mailbox_select ($imap_stream, $mailbox) {
266 if ($mailbox == 'None') {
270 $read = sqimap_run_command($imap_stream, 'SELECT ' . sqimap_encode_mailbox_name($mailbox),
271 true, $response, $message);
273 for ($i = 0, $cnt = count($read); $i < $cnt; $i++
) {
274 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
275 $result[strtoupper($regs[1])] = $regs[2];
276 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
277 $result[strtoupper($regs[2])] = $regs[1];
279 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
280 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
281 $result['PERMANENTFLAGS'] = explode(' ',strtolower($regs[1]));
282 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
283 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
284 $result['FLAGS'] = explode(' ',strtolower($regs[1]));
288 if (!isset($result['PERMANENTFLAGS'])) {
289 $result['PERMANENTFLAGS'] = $result['FLAGS'];
291 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
292 $result['RIGHTS']=strtoupper($regs[1]);
301 function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
303 if (strtolower($type) == 'noselect') {
304 $mailbox .= $delimiter;
307 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' .
308 sqimap_encode_mailbox_name($mailbox),
309 true, $response, $message);
310 sqimap_subscribe ($imap_stream, $mailbox);
314 * Subscribes to an existing folder.
316 function sqimap_subscribe ($imap_stream, $mailbox,$debug=true) {
317 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' .
318 sqimap_encode_mailbox_name($mailbox),
319 $debug, $response, $message);
323 * Unsubscribes from an existing folder
325 function sqimap_unsubscribe ($imap_stream, $mailbox) {
326 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' .
327 sqimap_encode_mailbox_name($mailbox),
328 false, $response, $message);
332 * Deletes the given folder
334 function sqimap_mailbox_delete ($imap_stream, $mailbox) {
335 global $data_dir, $username;
336 sqimap_unsubscribe ($imap_stream, $mailbox);
337 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' .
338 sqimap_encode_mailbox_name($mailbox),
339 true, $response, $message);
340 if ($response !== 'OK') {
342 sqimap_subscribe ($imap_stream, $mailbox);
344 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
345 removePref($data_dir, $username, "thread_$mailbox");
350 * Determines if the user is subscribed to the folder or not
352 function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
353 $boxesall = sqimap_mailbox_list ($imap_stream);
354 foreach ($boxesall as $ref) {
355 if ($ref['unformatted'] == $folder) {
365 function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
366 if ( $old_name != $new_name ) {
367 global $delimiter, $imap_server_type, $data_dir, $username;
368 if ( substr( $old_name, -1 ) == $delimiter ) {
369 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
370 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
371 $postfix = $delimiter;
376 $boxesall = sqimap_mailbox_list($imap_stream);
377 $cmd = 'RENAME ' . sqimap_encode_mailbox_name($old_name) .
378 ' ' . sqimap_encode_mailbox_name($new_name);
379 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
380 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
381 $oldpref = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
382 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
383 sqimap_subscribe($imap_stream, $new_name.$postfix);
384 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref);
385 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
386 $l = strlen( $old_name ) +
1;
389 foreach ($boxesall as $box) {
390 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
391 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
392 if ($imap_server_type == 'cyrus') {
393 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
394 $data = sqimap_run_command($imap_stream, $cmd, true,
395 $response, $message);
397 sqimap_unsubscribe($imap_stream, $box[$p]);
398 $oldpref = getPref($data_dir, $username, 'thread_'.$box[$p]);
399 removePref($data_dir, $username, 'thread_'.$box[$p]);
400 sqimap_subscribe($imap_stream, $new_sub);
401 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref);
402 do_hook_function('rename_or_delete_folder',
403 $args = array($box[$p], 'rename', $new_sub));
410 * Formats a mailbox into parts for the $boxesall array
414 * raw - Raw LIST/LSUB response from the IMAP server
415 * formatted - nicely formatted folder name
416 * unformatted - unformatted, but with delimiter at end removed
417 * unformatted-dm - folder name as it appears in raw response
418 * unformatted-disp - unformatted without $folder_prefix
420 function sqimap_mailbox_parse ($line, $line_lsub) {
421 global $folder_prefix, $delimiter;
423 /* Process each folder line */
424 for ($g = 0, $cnt = count($line); $g < $cnt; ++
$g) {
425 /* Store the raw IMAP reply */
426 if (isset($line[$g])) {
427 $boxesall[$g]['raw'] = $line[$g];
429 $boxesall[$g]['raw'] = '';
432 /* Count number of delimiters ($delimiter) in folder name */
433 $mailbox = /*trim(*/$line_lsub[$g]/*)*/;
434 $dm_count = substr_count($mailbox, $delimiter);
435 if (substr($mailbox, -1) == $delimiter) {
436 /* If name ends in delimiter, decrement count by one */
440 /* Format folder name, but only if it's a INBOX.* or has a parent. */
441 $boxesallbyname[$mailbox] = $g;
442 $parentfolder = readMailboxParent($mailbox, $delimiter);
443 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
444 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
445 (isset($boxesallbyname[$parentfolder]) &&
446 (strlen($parentfolder) > 0) ) ) {
447 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
449 $boxesall[$g]['formatted'] = str_repeat(' ', $indent);
451 $boxesall[$g]['formatted'] = '';
453 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
455 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
458 $boxesall[$g]['unformatted-dm'] = $mailbox;
459 if (substr($mailbox, -1) == $delimiter) {
460 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
462 $boxesall[$g]['unformatted'] = $mailbox;
463 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
464 $mailbox = substr($mailbox, strlen($folder_prefix));
466 $boxesall[$g]['unformatted-disp'] = $mailbox;
467 $boxesall[$g]['id'] = $g;
469 $boxesall[$g]['flags'] = array();
470 if (isset($line[$g])) {
471 ereg("\(([^)]*)\)",$line[$g],$regs);
472 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
473 // and $MDNSent <= last one doesn't have the \
474 // It's better to follow RFC3501 instead of using our own naming.
475 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
477 $boxesall[$g]['flags'] = explode(' ', $flags);
485 * Returns list of options (to be echoed into select statement
486 * based on available mailboxes and separators
487 * Caller should surround options with <SELECT..> </SELECT> and
489 * $imap_stream - $imapConnection to query for mailboxes
490 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
491 * $folder_skip - array of folders to keep out of option list (compared in lower)
492 * $boxes - list of already fetched boxes (for places like folder panel, where
493 * you know these options will be shown 3 times in a row.. (most often unset).
494 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
495 * 'noselect' by default to remove unselectable mailboxes.
496 * 'noinferiors' used to filter out folders that can not contain subfolders.
497 * NULL to avoid flag check entirely.
498 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
499 * \NoSelect and \NoInferiors
500 * $use_long_format - override folder display preference and always show full folder name.
502 function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
503 $flag = 'noselect', $use_long_format = false ) {
504 global $username, $data_dir;
506 if ( $use_long_format ) {
507 $shorten_box_names = 0;
509 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF
);
513 $boxes = sqimap_mailbox_list($imap_stream);
516 foreach ($boxes as $boxes_part) {
517 if ($flag == NULL ||
!in_array($flag, $boxes_part['flags'])) {
518 $box = $boxes_part['unformatted'];
520 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
523 $lowerbox = strtolower($box);
524 // mailboxes are casesensitive => inbox.sent != inbox.Sent
525 // nevermind, to many dependencies this should be fixed!
527 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
530 switch ($shorten_box_names)
532 case 2: /* delimited, style = 2 */
533 $box2 = str_replace(' ', '. ', $boxes_part['formatted']);
535 case 1: /* indent, style = 1 */
536 $box2 = $boxes_part['formatted'];
538 default: /* default, long names, style = 0 */
539 $box2 = str_replace(' ', ' ', htmlspecialchars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
543 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
544 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'" SELECTED>'.$box2.'</OPTION>' . "\n";
546 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'">'.$box2.'</OPTION>' . "\n";
550 return $mbox_options;
554 * Returns sorted mailbox lists in several different ways.
555 * See comment on sqimap_mailbox_parse() for info about the returned array.
559 function sqimap_mailbox_list($imap_stream, $force=false) {
560 global $default_folder_prefix;
562 if (!sqgetGlobalVar('boxesnew',$boxesnew,SQ_SESSION
) ||
$force) {
563 global $data_dir, $username, $list_special_folders_first,
564 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
565 $move_to_trash, $move_to_sent, $save_as_draft,
566 $delimiter, $noselect_fix_enable, $imap_server_type;
567 $inbox_in_list = false;
568 $inbox_subscribed = false;
569 $listsubscribed = sqimap_capability($imap_stream,'LIST-SUBSCRIBED');
571 require_once(SM_PATH
. 'include/load_prefs.php');
574 if ($listsubscribed) {
575 $lsub = 'LIST (SUBSCRIBED)';
580 if ($noselect_fix_enable) {
582 $lsub_args = "$lsub \"$folder_prefix\" \"*%\"";
584 $lsub_args = "$lsub \"$folder_prefix\" \"*\"";
587 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
588 true, $response, $message);
589 $lsub_ary = compact_mailboxes_response($lsub_ary);
591 $sorted_lsub_ary = array();
592 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++
) {
594 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
595 $sorted_lsub_ary[] = $temp_mailbox_name;
596 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
597 $inbox_subscribed = true;
601 /* natural sort mailboxes */
602 if (isset($sorted_lsub_ary)) {
603 usort($sorted_lsub_ary, 'strnatcasecmp');
606 * The LSUB response doesn't provide us information about \Noselect
607 * mail boxes. The LIST response does, that's why we need to do a LIST
608 * call to retrieve the flags for the mailbox
609 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
610 * in other words, we cannot rely on it.
612 $sorted_list_ary = array();
613 // if (!$listsubscribed) {
614 for ($i=0; $i < count($sorted_lsub_ary); $i++
) {
615 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
616 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
619 $mbx = $sorted_lsub_ary[$i];
622 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mbx),
623 true, $response, $message);
625 $read = compact_mailboxes_response($read);
627 if (isset($read[0])) {
628 $sorted_list_ary[$i] = $read[0];
630 $sorted_list_ary[$i] = '';
635 * Just in case they're not subscribed to their inbox,
636 * we'll get it for them anyway
638 if (!$inbox_subscribed) {
639 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" "INBOX"',
640 true, $response, $message);
641 $sorted_list_ary[] = implode('',compact_mailboxes_response($inbox_ary));
642 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
645 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
647 /* Now, lets sort for special folders */
648 $boxesnew = $used = array();
651 $cnt = count($boxesall);
652 $used = array_pad($used,$cnt,false);
653 for($k = 0; $k < $cnt; ++
$k) {
654 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
655 $boxesnew[] = $boxesall[$k];
660 /* List special folders and their subfolders, if requested. */
661 if ($list_special_folders_first) {
662 for($k = 0; $k < $cnt; ++
$k) {
663 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
664 $boxesnew[] = $boxesall[$k];
669 /* Rest of the folders */
670 for($k = 0; $k < $cnt; $k++
) {
672 $boxesnew[] = $boxesall[$k];
675 sqsession_register($boxesnew,'boxesnew');
681 * Returns a list of all folders, subscribed or not
683 function sqimap_mailbox_list_all($imap_stream) {
684 global $list_special_folders_first, $folder_prefix, $delimiter;
686 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
687 $read_ary = compact_mailboxes_response($read_ary);
691 $fld_pre_length = strlen($folder_prefix);
692 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++
) {
693 /* Store the raw IMAP reply */
694 $boxes[$g]['raw'] = $read_ary[$i];
696 /* Count number of delimiters ($delimiter) in folder name */
697 $mailbox = find_mailbox_name($read_ary[$i]);
698 $dm_count = substr_count($mailbox, $delimiter);
699 if (substr($mailbox, -1) == $delimiter) {
700 /* If name ends in delimiter - decrement count by one */
704 /* Format folder name, but only if it's a INBOX.* or has a parent. */
705 $boxesallbyname[$mailbox] = $g;
706 $parentfolder = readMailboxParent($mailbox, $delimiter);
707 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
708 (ereg('^'.$folder_prefix, $mailbox)) ||
709 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
711 $boxes[$g]['formatted'] = str_repeat(' ', $dm_count);
713 $boxes[$g]['formatted'] = '';
715 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
717 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
720 $boxes[$g]['unformatted-dm'] = $mailbox;
721 if (substr($mailbox, -1) == $delimiter) {
722 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
724 $boxes[$g]['unformatted'] = $mailbox;
725 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
727 $boxes[$g]['id'] = $g;
729 /* Now lets get the flags for this mailbox */
730 $read_mlbx = $read_ary[$i];
731 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+
1);
732 $flags = substr($flags, 0, strpos($flags, ')'));
733 $flags = str_replace('\\', '', $flags);
734 $flags = trim(strtolower($flags));
736 $boxes[$g]['flags'] = explode(' ', $flags);
738 $boxes[$g]['flags'] = array();
742 if(is_array($boxes)) {
749 function sqimap_mailbox_tree($imap_stream) {
750 global $boxesnew, $default_folder_prefix, $unseen_notify, $unseen_type;
753 global $data_dir, $username, $list_special_folders_first,
754 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
758 $inbox_in_list = false;
759 $inbox_subscribed = false;
761 $noinferiors = false;
763 require_once(SM_PATH
. 'include/load_prefs.php');
766 $lsub_ary = sqimap_run_command ($imap_stream, "LSUB \"$folder_prefix\" \"*\"",
767 true, $response, $message);
768 $lsub_ary = compact_mailboxes_response($lsub_ary);
770 /* Check to see if we have an INBOX */
773 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++
) {
774 if (preg_match("/^\*\s+LSUB.*\s\"?INBOX\"?[^(\/\.)].*$/i",$lsub_ary[$i])) {
775 $lsub_ary[$i] = strtoupper($lsub_ary[$i]);
776 // in case of an unsubscribed inbox an imap server can
777 // return the inbox in the lsub results with a \NoSelect
779 if (!preg_match("/\*\s+LSUB\s+\(.*\\\\NoSelect.*\).*/i",$lsub_ary[$i])) {
782 // remove the result and request it again with a list
783 // response at a later stage.
784 unset($lsub_ary[$i]);
785 // re-index the array otherwise the addition of the LIST
786 // response will fail in PHP 4.1.2 and probably other older versions
787 $lsub_ary = array_values($lsub_ary);
793 if ($has_inbox == false) {
794 // do a list request for inbox because we should always show
795 // inbox even if the user isn't subscribed to it.
796 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" INBOX',
797 true, $response, $message);
798 $inbox_ary = compact_mailboxes_response($inbox_ary);
799 if (count($inbox_ary)) {
800 $lsub_ary[] = $inbox_ary[0];
805 * Section about removing the last element was removed
806 * We don't return "* OK" anymore from sqimap_read_data
809 $sorted_lsub_ary = array();
810 $cnt = count($lsub_ary);
811 for ($i = 0; $i < $cnt; $i++
) {
812 $mbx = find_mailbox_name($lsub_ary[$i]);
814 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
815 if ($imap_server_type != "uw") {
816 $noselect = check_is_noselect($lsub_ary[$i]);
817 $noinferiors = check_is_noinferiors($lsub_ary[$i]);
819 if (substr($mbx, -1) == $delimiter) {
820 $mbx = substr($mbx, 0, strlen($mbx) - 1);
822 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
824 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
825 if ($imap_server_type == "uw") {
828 // prepare an array with queries
829 foreach ($sorted_lsub_ary as $aMbx) {
830 $mbx = stripslashes($aMbx['mbx']);
831 sqimap_prepare_pipelined_query('LIST "" ' . sqimap_encode_mailbox_name($mbx), $tag, $aQuery, false);
834 $sorted_lsub_ary = array();
835 // execute all the queries at once
836 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
837 foreach($aTag as $tag => $mbx) {
838 if ($aServerResponse[$tag] == 'OK') {
839 $sResponse = implode('', $aResponse[$tag]);
840 $noselect = check_is_noselect($sResponse);
841 $noinferiors = check_is_noinferiors($sResponse);
842 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
845 $cnt = count($sorted_lsub_ary);
847 $sorted_lsub_ary = array_values($sorted_lsub_ary);
848 array_multisort($sorted_lsub_ary, SORT_ASC
, SORT_REGULAR
);
849 $boxesnew = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
854 function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
855 global $data_dir, $username, $list_special_folders_first,
856 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
857 $move_to_trash, $move_to_sent, $save_as_draft,
858 $delimiter, $imap_server_type;
860 $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
862 /* create virtual root node */
863 $mailboxes= new mailboxes();
864 $mailboxes->is_root
= true;
869 if (isset($folder_prefix) && ($folder_prefix != '')) {
870 $start = substr_count($folder_prefix,$delimiter);
871 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
873 $mailboxes->mailboxname_full
= substr($folder_prefix,0, (strlen($folder_prefix)-1));
875 $mailboxes->mailboxname_full
= $folder_prefix;
878 $mailboxes->mailboxname_sub
= $mailboxes->mailboxname_full
;
883 $cnt = count($mbx_ary);
884 for ($i=0; $i < $cnt; $i++
) {
885 if ($mbx_ary[$i]['mbx'] !='' ) {
886 $mbx = new mailboxes();
887 $mailbox = $mbx_ary[$i]['mbx'];
890 sent subfolders messes up using existing code as subfolders
891 were marked, but the parents were ordered somewhere else in
892 the list, despite having "special folders at top" option set.
893 Need a better method than this.
896 if ($mailbox == 'INBOX') {
897 $mbx->is_special = true;
898 } elseif (stristr($trash_folder , $mailbox)) {
899 $mbx->is_special = true;
900 } elseif (stristr($sent_folder , $mailbox)) {
901 $mbx->is_special = true;
902 } elseif (stristr($draft_folder , $mailbox)) {
903 $mbx->is_special = true;
908 $mbx->is_inbox = true;
909 $mbx->is_special = true;
910 $mbx_ary[$i]['noselect'] = false;
913 $mbx->is_trash = true;
914 $mbx->is_special = true;
917 $mbx->is_sent = true;
918 $mbx->is_special = true;
921 $mbx->is_draft = true;
922 $mbx->is_special = true;
926 $mbx->is_special |
= ($mbx->is_inbox
= (strtoupper($mailbox) == 'INBOX'));
927 $mbx->is_special |
= ($mbx->is_trash
= isTrashMailbox($mailbox));
928 $mbx->is_special |
= ($mbx->is_sent
= isSentMailbox($mailbox));
929 $mbx->is_special |
= ($mbx->is_draft
= isDraftMailbox($mailbox));
930 if (!$mbx->is_special
)
931 $mbx->is_special
= boolean_hook_function('special_mailbox', $mailbox, 1);
933 if (isset($mbx_ary[$i]['unseen'])) {
934 $mbx->unseen
= $mbx_ary[$i]['unseen'];
936 if (isset($mbx_ary[$i]['nummessages'])) {
937 $mbx->total
= $mbx_ary[$i]['nummessages'];
940 $mbx->is_noselect
= $mbx_ary[$i]['noselect'];
941 $mbx->is_noinferiors
= $mbx_ary[$i]['noinferiors'];
943 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
945 $mbx->mailboxname_sub
= substr($mbx_ary[$i]['mbx'],$r_del_pos+
1);
946 } else { /* mailbox is root folder */
947 $mbx->mailboxname_sub
= $mbx_ary[$i]['mbx'];
949 $mbx->mailboxname_full
= $mbx_ary[$i]['mbx'];
951 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
954 sqimap_utf7_decode_mbx_tree($mailboxes);
955 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
959 function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
960 if (strtoupper($mbx_tree->mailboxname_full
) == 'INBOX')
961 $mbx_tree->mailboxname_sub
= _("INBOX");
963 $mbx_tree->mailboxname_sub
= imap_utf7_decode_local($mbx_tree->mailboxname_sub
);
964 if ($mbx_tree->mbxs
) {
965 $iCnt = count($mbx_tree->mbxs
);
966 for ($i=0;$i<$iCnt;++
$i) {
967 $mbxs_tree->mbxs
[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs
[$i]);
973 function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
975 $aMbxs[] =& $mbx_tree;
976 if ($mbx_tree->mbxs
) {
977 $iCnt = count($mbx_tree->mbxs
);
978 for ($i=0;$i<$iCnt;++
$i) {
979 sqimap_tree_to_ref_array($mbx_tree->mbxs
[$i],$aMbxs);
984 function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
985 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
986 $aMbxs = $aQuery = $aTag = array();
987 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
988 // remove the root node
991 if($unseen_notify == 3) {
992 $cnt = count($aMbxs);
993 for($i=0;$i<$cnt;++
$i) {
995 if (!$oMbx->is_noselect
) {
996 $mbx = $oMbx->mailboxname_full
;
997 if ($unseen_type == 2 ||
998 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
999 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
1001 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (UNSEEN)';
1003 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
1005 $oMbx->unseen
= $oMbx->total
= false;
1009 $aMbxs[$i] =& $oMbx;
1011 // execute all the queries at once
1012 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
1013 $cnt = count($aMbxs);
1014 for($i=0;$i<$cnt;++
$i) {
1015 $oMbx =& $aMbxs[$i];
1017 if ($tag && $aServerResponse[$tag] == 'OK') {
1018 $sResponse = implode('', $aResponse[$tag]);
1019 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
1020 $oMbx->unseen
= $regs[1];
1022 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
1023 $oMbx->total
= $regs[1];
1028 } else if ($unseen_notify == 2) { // INBOX only
1029 $cnt = count($aMbxs);
1030 for($i=0;$i<$cnt;++
$i) {
1031 $oMbx =& $aMbxs[$i];
1032 if (strtoupper($oMbx->mailboxname_full
) == 'INBOX' ||
1033 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
1034 if ($unseen_type == 2 ||
1035 ($oMbx->mailboxname_full
== $trash_folder && $move_to_trash)) {
1036 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full
);
1037 $oMbx->unseen
= $aStatus['UNSEEN'];
1038 $oMbx->total
= $aStatus['MESSAGES'];
1040 $oMbx->unseen
= sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full
);
1042 $aMbxs[$i] =& $oMbx;
1043 if (!$move_to_trash && $trash_folder) {
1046 // trash comes after INBOX
1047 if ($oMbx->mailboxname_full
== $trash_folder) {