1deb9bc74030687dd875f2b6e6840fb0b68fdbac
6 * Copyright (c) 1999-2004 The SquirrelMail Project Team
7 * Licensed under the GNU GPL. For full terms see the file COPYING.
9 * This impliments all functions that manipulate mailboxes
12 * @package squirrelmail
17 require_once(SM_PATH
. 'functions/imap_utf7_local.php');
24 * FIXME. This class should be extracted and placed in a separate file that
25 * can be included before we start the session. That makes caching of the tree
26 * possible. On a refresh mailboxes from left_main.php the only function that
27 * should be called is the sqimap_get_status_mbx_tree. In case of subscribe
28 * / rename / delete / new we have to create methods for adding/changing the
29 * mailbox in the mbx_tree without the need for a refresh.
30 * @package squirrelmail
34 var $mailboxname_full = '', $mailboxname_sub= '', $is_noselect = false, $is_noinferiors = false,
35 $is_special = false, $is_root = false, $is_inbox = false, $is_sent = false,
36 $is_trash = false, $is_draft = false, $mbxs = array(),
37 $unseen = false, $total = false;
39 function addMbx($mbx, $delimiter, $start, $specialfirst) {
40 $ary = explode($delimiter, $mbx->mailboxname_full
);
42 for ($i = $start, $c = count($ary)-1; $i < $c; $i++
) {
43 $mbx_childs =& $mbx_parent->mbxs
;
46 foreach ($mbx_childs as $key => $parent) {
47 if ($parent->mailboxname_sub
== $ary[$i]) {
48 $mbx_parent =& $mbx_parent->mbxs
[$key];
55 $no_select_mbx = new mailboxes();
56 if (isset($mbx_parent->mailboxname_full
) && $mbx_parent->mailboxname_full
!= '') {
57 $no_select_mbx->mailboxname_full
= $mbx_parent->mailboxname_full
.$delimiter.$ary[$i];
59 $no_select_mbx->mailboxname_full
= $ary[$i];
61 $no_select_mbx->mailboxname_sub
= $ary[$i];
62 $no_select_mbx->is_noselect
= true;
63 $mbx_parent->mbxs
[] = $no_select_mbx;
67 $mbx_parent->mbxs
[] = $mbx;
68 if ($mbx->is_special
&& $specialfirst) {
69 usort($mbx_parent->mbxs
, 'sortSpecialMbx');
74 function sortSpecialMbx($a, $b) {
76 $acmp = '0'. $a->mailboxname_full
;
77 } else if ($a->is_special
) {
78 $acmp = '1'. $a->mailboxname_full
;
80 $acmp = '2' . $a->mailboxname_full
;
83 $bcmp = '0'. $b->mailboxname_full
;
84 }else if ($b->is_special
) {
85 $bcmp = '1' . $b->mailboxname_full
;
87 $bcmp = '2' . $b->mailboxname_full
;
89 return strnatcasecmp($acmp, $bcmp);
92 function compact_mailboxes_response($ary)
95 * Workaround for mailboxes returned as literal
96 * FIXME : Doesn't work if the mailbox name is multiple lines
97 * (larger then fgets buffer)
99 for ($i = 0, $iCnt=count($ary); $i < $iCnt; $i++
) {
100 if (isset($ary[$i +
1]) && substr($ary[$i], -3) == "}\r\n") {
101 if (ereg("^(\\* [A-Z]+.*)\\{[0-9]+\\}([ \n\r\t]*)$",
103 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+
1])) . '"' . $regs[2];
104 array_splice($ary, $i+
1, 2);
108 /* remove duplicates and ensure array is contiguous */
109 return array_values(array_unique($ary));
113 * Extract the mailbox name from an untagged LIST (7.2.2) or LSUB (7.2.3) answer
114 * (LIST|LSUB) (<Flags list>) (NIL|"<separator atom>") <mailbox name string>\r\n
115 * mailbox name in quoted string MUST be unquoted and stripslashed (sm API)
117 function find_mailbox_name($line)
119 if (preg_match('/^\* (?:LIST|LSUB) \([^\)]*\) (?:NIL|\"[^\"]*\") ([^\r\n]*)[\r\n]*$/i', $line, $regs)) {
120 if (substr($regs[1], 0, 1) == '"')
121 return stripslashes(substr($regs[1], 1, -1));
128 * @return bool whether this is a Noselect mailbox.
130 function check_is_noselect ($lsub_line) {
131 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i", $lsub_line);
135 * @return bool whether this is a Noinferiors mailbox.
137 function check_is_noinferiors ($lsub_line) {
138 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noinferiors[^\)]*\)/i", $lsub_line);
142 * If $haystack is a full mailbox name, and $needle is the mailbox
143 * separator character, returns the second last part of the full
144 * mailbox name (i.e. the mailbox's parent mailbox)
146 function readMailboxParent($haystack, $needle) {
150 $parts = explode($needle, $haystack);
151 $elem = array_pop($parts);
152 while ($elem == '' && count($parts)) {
153 $elem = array_pop($parts);
155 $ret = join($needle, $parts);
161 * Check if $subbox is below the specified $parentbox
163 function isBoxBelow( $subbox, $parentbox ) {
166 * Eliminate the obvious mismatch, where the
167 * subfolder path is shorter than that of the potential parent
169 if ( strlen($subbox) < strlen($parentbox) ) {
172 /* check for delimiter */
173 if (!substr($parentbox,-1) == $delimiter) {
174 $parentbox.=$delimiter;
176 if (substr($subbox,0,strlen($parentbox)) == $parentbox) {
184 * Defines special mailboxes: given a mailbox name, it checks if this is a
185 * "special" one: INBOX, Trash, Sent or Draft.
187 function isSpecialMailbox( $box ) {
188 $ret = ( (strtolower($box) == 'inbox') ||
189 isTrashMailbox($box) ||
isSentMailbox($box) ||
isDraftMailbox($box) );
192 $ret = boolean_hook_function('special_mailbox',$box,1);
198 * @return bool whether this is a Trash folder
200 function isTrashMailbox ($box) {
201 global $trash_folder, $move_to_trash;
202 return $move_to_trash && $trash_folder &&
203 ( $box == $trash_folder ||
isBoxBelow($box, $trash_folder) );
207 * @return bool whether this is a Sent folder
209 function isSentMailbox($box) {
210 global $sent_folder, $move_to_sent;
211 return $move_to_sent && $sent_folder &&
212 ( $box == $sent_folder ||
isBoxBelow($box, $sent_folder) );
216 * @return bool whether this is a Draft folder
218 function isDraftMailbox($box) {
219 global $draft_folder, $save_as_draft;
220 return $save_as_draft &&
221 ( $box == $draft_folder ||
isBoxBelow($box, $draft_folder) );
225 * Expunges a mailbox, ie. delete all contents.
227 function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
230 $id = sqimap_message_list_squisher($id);
237 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
238 $response, $message, $uid);
241 if (is_array($read)) {
242 foreach ($read as $r) {
243 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
252 * Expunge specified message, updated $msgs and $msort
254 * Until Marc and I come up with a better way to maintain
255 * these stupid arrays, we'll use this wrapper function to
256 * remove the message with the matching UID .. the order
257 * won't be changed - the array element for the message
258 * will just be removed.
260 function sqimap_mailbox_expunge_dmn($message_id)
262 global $msgs, $msort, $sort, $imapConnection,
263 $mailbox, $mbx_response, $auto_expunge,
264 $sort, $allow_server_sort, $thread_sort_messages, $allow_thread_sort,
265 $username, $data_dir;
268 // Got to grab this out of prefs, since it isn't saved from mailbox_view.php
269 if ($allow_thread_sort) {
270 $thread_sort_messages = getPref($data_dir, $username, "thread_$mailbox",0);
273 for ($i = 0; $i < count($msort); $i++
) {
274 if ($msgs[$i]['ID'] == $message_id) {
279 if ( isset($msgs) ) {
281 $msgs = array_values($msgs);
282 sqsession_register($msgs, 'msgs');
285 if ( isset($msort) ) {
287 $msort = array_values($msort);
288 sqsession_register($msort, 'msort');
292 $cnt = sqimap_mailbox_expunge($imapConnection, $mailbox, true);
295 // And after all that mucking around, update the sort list!
296 // Remind me why the hell we need those two arrays again?!
297 if ( $allow_thread_sort && $thread_sort_messages ) {
298 $server_sort_array = get_thread_sort($imapConnection);
299 } elseif ( $allow_server_sort ) {
300 $server_sort_array = sqimap_get_sort_order($imapConnection, $sort, $mbx_response);
302 $server_sort_array = sqimap_get_php_sort_order($imapConnection, $mbx_response);
308 * Checks whether or not the specified mailbox exists
310 function sqimap_mailbox_exists ($imap_stream, $mailbox) {
311 if (!isset($mailbox) ||
empty($mailbox)) {
314 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mailbox),
315 true, $response, $message);
316 return isset($mbx[0]);
322 function sqimap_mailbox_select ($imap_stream, $mailbox) {
323 if ($mailbox == 'None') {
327 $read = sqimap_run_command($imap_stream, 'SELECT ' . sqimap_encode_mailbox_name($mailbox),
328 true, $response, $message);
330 for ($i = 0, $cnt = count($read); $i < $cnt; $i++
) {
331 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
332 $result[strtoupper($regs[1])] = $regs[2];
333 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
334 $result[strtoupper($regs[2])] = $regs[1];
336 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
337 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
338 $result['PERMANENTFLAGS'] = $regs[1];
339 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
340 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
341 $result['FLAGS'] = $regs[1];
345 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
346 $result['RIGHTS']=$regs[1];
355 function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
357 if (strtolower($type) == 'noselect') {
358 $mailbox .= $delimiter;
361 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' .
362 sqimap_encode_mailbox_name($mailbox),
363 true, $response, $message);
364 sqimap_subscribe ($imap_stream, $mailbox);
368 * Subscribes to an existing folder.
370 function sqimap_subscribe ($imap_stream, $mailbox) {
371 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' .
372 sqimap_encode_mailbox_name($mailbox),
373 true, $response, $message);
377 * Unsubscribes from an existing folder
379 function sqimap_unsubscribe ($imap_stream, $mailbox) {
380 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' .
381 sqimap_encode_mailbox_name($mailbox),
382 false, $response, $message);
386 * Deletes the given folder
388 function sqimap_mailbox_delete ($imap_stream, $mailbox) {
389 global $data_dir, $username;
390 sqimap_unsubscribe ($imap_stream, $mailbox);
391 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' .
392 sqimap_encode_mailbox_name($mailbox),
393 true, $response, $message);
394 if ($response !== 'OK') {
396 sqimap_subscribe ($imap_stream, $mailbox);
398 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
399 removePref($data_dir, $username, "thread_$mailbox");
404 * Determines if the user is subscribed to the folder or not
406 function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
407 $boxesall = sqimap_mailbox_list ($imap_stream);
408 foreach ($boxesall as $ref) {
409 if ($ref['unformatted'] == $folder) {
419 function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
420 if ( $old_name != $new_name ) {
421 global $delimiter, $imap_server_type, $data_dir, $username;
422 if ( substr( $old_name, -1 ) == $delimiter ) {
423 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
424 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
425 $postfix = $delimiter;
430 $boxesall = sqimap_mailbox_list($imap_stream);
431 $cmd = 'RENAME ' . sqimap_encode_mailbox_name($old_name) .
432 ' ' . sqimap_encode_mailbox_name($new_name);
433 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
434 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
435 $oldpref = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
436 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
437 sqimap_subscribe($imap_stream, $new_name.$postfix);
438 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref);
439 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
440 $l = strlen( $old_name ) +
1;
443 foreach ($boxesall as $box) {
444 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
445 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
446 if ($imap_server_type == 'cyrus') {
447 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
448 $data = sqimap_run_command($imap_stream, $cmd, true,
449 $response, $message);
451 sqimap_unsubscribe($imap_stream, $box[$p]);
452 $oldpref = getPref($data_dir, $username, 'thread_'.$box[$p]);
453 removePref($data_dir, $username, 'thread_'.$box[$p]);
454 sqimap_subscribe($imap_stream, $new_sub);
455 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref);
456 do_hook_function('rename_or_delete_folder',
457 $args = array($box[$p], 'rename', $new_sub));
464 * Formats a mailbox into parts for the $boxesall array
468 * raw - Raw LIST/LSUB response from the IMAP server
469 * formatted - nicely formatted folder name
470 * unformatted - unformatted, but with delimiter at end removed
471 * unformatted-dm - folder name as it appears in raw response
472 * unformatted-disp - unformatted without $folder_prefix
474 function sqimap_mailbox_parse ($line, $line_lsub) {
475 global $folder_prefix, $delimiter;
477 /* Process each folder line */
478 for ($g = 0, $cnt = count($line); $g < $cnt; ++
$g) {
479 /* Store the raw IMAP reply */
480 if (isset($line[$g])) {
481 $boxesall[$g]['raw'] = $line[$g];
483 $boxesall[$g]['raw'] = '';
486 /* Count number of delimiters ($delimiter) in folder name */
487 $mailbox = /*trim(*/$line_lsub[$g]/*)*/;
488 $dm_count = substr_count($mailbox, $delimiter);
489 if (substr($mailbox, -1) == $delimiter) {
490 /* If name ends in delimiter, decrement count by one */
494 /* Format folder name, but only if it's a INBOX.* or has a parent. */
495 $boxesallbyname[$mailbox] = $g;
496 $parentfolder = readMailboxParent($mailbox, $delimiter);
497 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
498 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
499 (isset($boxesallbyname[$parentfolder]) &&
500 (strlen($parentfolder) > 0) ) ) {
501 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
503 $boxesall[$g]['formatted'] = str_repeat(' ', $indent);
505 $boxesall[$g]['formatted'] = '';
507 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
509 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
512 $boxesall[$g]['unformatted-dm'] = $mailbox;
513 if (substr($mailbox, -1) == $delimiter) {
514 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
516 $boxesall[$g]['unformatted'] = $mailbox;
517 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
518 $mailbox = substr($mailbox, strlen($folder_prefix));
520 $boxesall[$g]['unformatted-disp'] = $mailbox;
521 $boxesall[$g]['id'] = $g;
523 $boxesall[$g]['flags'] = array();
524 if (isset($line[$g])) {
525 ereg("\(([^)]*)\)",$line[$g],$regs);
526 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
527 // and $MDNSent <= last one doesn't have the \
528 // It's better to follow RFC3501 instead of using our own naming.
529 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
531 $boxesall[$g]['flags'] = explode(' ', $flags);
539 * Returns list of options (to be echoed into select statement
540 * based on available mailboxes and separators
541 * Caller should surround options with <SELECT..> </SELECT> and
543 * $imap_stream - $imapConnection to query for mailboxes
544 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
545 * $folder_skip - array of folders to keep out of option list (compared in lower)
546 * $boxes - list of already fetched boxes (for places like folder panel, where
547 * you know these options will be shown 3 times in a row.. (most often unset).
548 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
549 * 'noselect' by default to remove unselectable mailboxes.
550 * 'noinferiors' used to filter out folders that can not contain subfolders.
551 * NULL to avoid flag check entirely.
552 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
553 * \NoSelect and \NoInferiors
554 * $use_long_format - override folder display preference and always show full folder name.
556 function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
557 $flag = 'noselect', $use_long_format = false ) {
558 global $username, $data_dir;
560 if ( $use_long_format ) {
561 $shorten_box_names = 0;
563 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF
);
567 $boxes = sqimap_mailbox_list($imap_stream);
570 foreach ($boxes as $boxes_part) {
571 if ($flag == NULL ||
!in_array($flag, $boxes_part['flags'])) {
572 $box = $boxes_part['unformatted'];
574 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
577 $lowerbox = strtolower($box);
578 // mailboxes are casesensitive => inbox.sent != inbox.Sent
579 // nevermind, to many dependencies this should be fixed!
581 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
584 switch ($shorten_box_names)
586 case 2: /* delimited, style = 2 */
587 $box2 = str_replace(' ', '. ', $boxes_part['formatted']);
589 case 1: /* indent, style = 1 */
590 $box2 = $boxes_part['formatted'];
592 default: /* default, long names, style = 0 */
593 $box2 = str_replace(' ', ' ', htmlspecialchars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
597 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
598 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'" SELECTED>'.$box2.'</OPTION>' . "\n";
600 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'">'.$box2.'</OPTION>' . "\n";
604 return $mbox_options;
608 * Returns sorted mailbox lists in several different ways.
609 * See comment on sqimap_mailbox_parse() for info about the returned array.
611 function sqimap_mailbox_list($imap_stream) {
612 global $default_folder_prefix;
614 if (!isset($boxesnew)) {
615 global $data_dir, $username, $list_special_folders_first,
616 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
617 $move_to_trash, $move_to_sent, $save_as_draft,
618 $delimiter, $noselect_fix_enable;
620 $inbox_in_list = false;
621 $inbox_subscribed = false;
623 require_once(SM_PATH
. 'include/load_prefs.php');
625 if ($noselect_fix_enable) {
626 $lsub_args = "LSUB \"$folder_prefix\" \"*%\"";
628 $lsub_args = "LSUB \"$folder_prefix\" \"*\"";
631 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
632 true, $response, $message);
633 $lsub_ary = compact_mailboxes_response($lsub_ary);
635 $sorted_lsub_ary = array();
636 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++
) {
637 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
638 $sorted_lsub_ary[] = $temp_mailbox_name;
639 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
640 $inbox_subscribed = true;
644 /* natural sort mailboxes */
645 if (isset($sorted_lsub_ary)) {
646 usort($sorted_lsub_ary, 'strnatcasecmp');
649 * The LSUB response doesn't provide us information about \Noselect
650 * mail boxes. The LIST response does, that's why we need to do a LIST
651 * call to retrieve the flags for the mailbox
652 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
653 * in other words, we cannot rely on it.
655 $sorted_list_ary = array();
656 for ($i=0; $i < count($sorted_lsub_ary); $i++
) {
657 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
658 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
661 $mbx = $sorted_lsub_ary[$i];
663 $mbx = stripslashes($mbx);
664 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mbx),
665 true, $response, $message);
666 $read = compact_mailboxes_response($read);
667 if (isset($read[0])) {
668 $sorted_list_ary[$i] = $read[0];
670 $sorted_list_ary[$i] = '';
674 * Just in case they're not subscribed to their inbox,
675 * we'll get it for them anyway
677 if (!$inbox_subscribed) {
678 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" INBOX',
679 true, $response, $message);
680 $sorted_list_ary[] = implode('', compact_mailboxes_response($inbox_ary));
681 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
684 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
686 /* Now, lets sort for special folders */
687 $boxesnew = $used = array();
690 $cnt = count($boxesall);
691 $used = array_pad($used,$cnt,false);
692 for($k = 0; $k < $cnt; ++
$k) {
693 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
694 $boxesnew[] = $boxesall[$k];
699 /* List special folders and their subfolders, if requested. */
700 if ($list_special_folders_first) {
701 for($k = 0; $k < $cnt; ++
$k) {
702 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
703 $boxesnew[] = $boxesall[$k];
709 /* Rest of the folders */
710 for($k = 0; $k < $cnt; $k++
) {
712 $boxesnew[] = $boxesall[$k];
721 * Returns a list of all folders, subscribed or not
723 function sqimap_mailbox_list_all($imap_stream) {
724 global $list_special_folders_first, $folder_prefix, $delimiter;
726 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
727 $read_ary = compact_mailboxes_response($read_ary);
731 $fld_pre_length = strlen($folder_prefix);
732 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++
) {
733 /* Store the raw IMAP reply */
734 $boxes[$g]['raw'] = $read_ary[$i];
736 /* Count number of delimiters ($delimiter) in folder name */
737 $mailbox = find_mailbox_name($read_ary[$i]);
738 $dm_count = substr_count($mailbox, $delimiter);
739 if (substr($mailbox, -1) == $delimiter) {
740 /* If name ends in delimiter - decrement count by one */
744 /* Format folder name, but only if it's a INBOX.* or has a parent. */
745 $boxesallbyname[$mailbox] = $g;
746 $parentfolder = readMailboxParent($mailbox, $delimiter);
747 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
748 (ereg('^'.$folder_prefix, $mailbox)) ||
749 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
751 $boxes[$g]['formatted'] = str_repeat(' ', $dm_count);
753 $boxes[$g]['formatted'] = '';
755 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
757 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
760 $boxes[$g]['unformatted-dm'] = $mailbox;
761 if (substr($mailbox, -1) == $delimiter) {
762 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
764 $boxes[$g]['unformatted'] = $mailbox;
765 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
767 $boxes[$g]['id'] = $g;
769 /* Now lets get the flags for this mailbox */
770 $read_mlbx = $read_ary[$i];
771 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+
1);
772 $flags = substr($flags, 0, strpos($flags, ')'));
773 $flags = str_replace('\\', '', $flags);
774 $flags = trim(strtolower($flags));
776 $boxes[$g]['flags'] = explode(' ', $flags);
778 $boxes[$g]['flags'] = array();
782 if(is_array($boxes)) {
789 function sqimap_mailbox_tree($imap_stream) {
790 global $boxesnew, $default_folder_prefix, $unseen_notify, $unseen_type;
791 if (!isset($boxesnew)) {
793 global $data_dir, $username, $list_special_folders_first,
794 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
798 $inbox_in_list = false;
799 $inbox_subscribed = false;
801 $noinferiors = false;
803 require_once(SM_PATH
. 'include/load_prefs.php');
806 $lsub_ary = sqimap_run_command ($imap_stream, "LSUB \"$folder_prefix\" \"*\"",
807 true, $response, $message);
808 $lsub_ary = compact_mailboxes_response($lsub_ary);
810 /* Check to see if we have an INBOX */
813 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++
) {
814 if (preg_match("/^\*\s+LSUB.*\s\"?INBOX\"?[^(\/\.)].*$/i",$lsub_ary[$i])) {
815 $lsub_ary[$i] = strtoupper($lsub_ary[$i]);
816 // in case of an unsubscribed inbox an imap server can
817 // return the inbox in the lsub results with a \NoSelect
819 if (!preg_match("/\*\s+LSUB\s+\(.*\\\\NoSelect.*\).*/i",$lsub_ary[$i])) {
822 // remove the result and request it again with a list
823 // response at a later stage.
824 unset($lsub_ary[$i]);
825 // re-index the array otherwise the addition of the LIST
826 // response will fail in PHP 4.1.2 and probably other older versions
827 $lsub_ary = array_values($lsub_ary);
833 if ($has_inbox == false) {
834 // do a list request for inbox because we should always show
835 // inbox even if the user isn't subscribed to it.
836 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" INBOX',
837 true, $response, $message);
838 $inbox_ary = compact_mailboxes_response($inbox_ary);
839 if (count($inbox_ary)) {
840 $lsub_ary[] = $inbox_ary[0];
845 * Section about removing the last element was removed
846 * We don't return "* OK" anymore from sqimap_read_data
849 $sorted_lsub_ary = array();
850 $cnt = count($lsub_ary);
851 for ($i = 0; $i < $cnt; $i++
) {
852 $mbx = find_mailbox_name($lsub_ary[$i]);
854 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
855 if ($imap_server_type != "uw") {
856 $noselect = check_is_noselect($lsub_ary[$i]);
857 $noinferiors = check_is_noinferiors($lsub_ary[$i]);
859 if (substr($mbx, -1) == $delimiter) {
860 $mbx = substr($mbx, 0, strlen($mbx) - 1);
862 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
864 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
865 if ($imap_server_type == "uw") {
868 // prepare an array with queries
869 foreach ($sorted_lsub_ary as $aMbx) {
870 $mbx = stripslashes($aMbx['mbx']);
871 sqimap_prepare_pipelined_query('LIST "" ' . sqimap_encode_mailbox_name($mbx), $tag, $aQuery, false);
874 $sorted_lsub_ary = array();
875 // execute all the queries at once
876 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
877 foreach($aTag as $tag => $mbx) {
878 if ($aServerResponse[$tag] == 'OK') {
879 $sResponse = implode('', $aResponse[$tag]);
880 $noselect = check_is_noselect($sResponse);
881 $noinferiors = check_is_noinferiors($sResponse);
882 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
885 $cnt = count($sorted_lsub_ary);
887 $sorted_lsub_ary = array_values($sorted_lsub_ary);
888 array_multisort($sorted_lsub_ary, SORT_ASC
, SORT_REGULAR
);
889 $boxesnew = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
894 function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
895 global $data_dir, $username, $list_special_folders_first,
896 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
897 $move_to_trash, $move_to_sent, $save_as_draft,
898 $delimiter, $imap_server_type;
900 $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
902 /* create virtual root node */
903 $mailboxes= new mailboxes();
904 $mailboxes->is_root
= true;
909 if (isset($folder_prefix) && ($folder_prefix != '')) {
910 $start = substr_count($folder_prefix,$delimiter);
911 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
913 $mailboxes->mailboxname_full
= substr($folder_prefix,0, (strlen($folder_prefix)-1));
915 $mailboxes->mailboxname_full
= $folder_prefix;
918 $mailboxes->mailboxname_sub
= $mailboxes->mailboxname_full
;
923 $cnt = count($mbx_ary);
924 for ($i=0; $i < $cnt; $i++
) {
925 if ($mbx_ary[$i]['mbx'] !='' ) {
926 $mbx = new mailboxes();
927 $mailbox = $mbx_ary[$i]['mbx'];
930 sent subfolders messes up using existing code as subfolders
931 were marked, but the parents were ordered somewhere else in
932 the list, despite having "special folders at top" option set.
933 Need a better method than this.
936 if ($mailbox == 'INBOX') {
937 $mbx->is_special = true;
938 } elseif (stristr($trash_folder , $mailbox)) {
939 $mbx->is_special = true;
940 } elseif (stristr($sent_folder , $mailbox)) {
941 $mbx->is_special = true;
942 } elseif (stristr($draft_folder , $mailbox)) {
943 $mbx->is_special = true;
948 $mbx->is_inbox = true;
949 $mbx->is_special = true;
950 $mbx_ary[$i]['noselect'] = false;
953 $mbx->is_trash = true;
954 $mbx->is_special = true;
957 $mbx->is_sent = true;
958 $mbx->is_special = true;
961 $mbx->is_draft = true;
962 $mbx->is_special = true;
966 $mbx->is_special |
= ($mbx->is_inbox
= (strtoupper($mailbox) == 'INBOX'));
967 $mbx->is_special |
= ($mbx->is_trash
= isTrashMailbox($mailbox));
968 $mbx->is_special |
= ($mbx->is_sent
= isSentMailbox($mailbox));
969 $mbx->is_special |
= ($mbx->is_draft
= isDraftMailbox($mailbox));
970 if (!$mbx->is_special
)
971 $mbx->is_special
= boolean_hook_function('special_mailbox', $mailbox, 1);
973 if (isset($mbx_ary[$i]['unseen'])) {
974 $mbx->unseen
= $mbx_ary[$i]['unseen'];
976 if (isset($mbx_ary[$i]['nummessages'])) {
977 $mbx->total
= $mbx_ary[$i]['nummessages'];
980 $mbx->is_noselect
= $mbx_ary[$i]['noselect'];
981 $mbx->is_noinferiors
= $mbx_ary[$i]['noinferiors'];
983 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
985 $mbx->mailboxname_sub
= substr($mbx_ary[$i]['mbx'],$r_del_pos+
1);
986 } else { /* mailbox is root folder */
987 $mbx->mailboxname_sub
= $mbx_ary[$i]['mbx'];
989 $mbx->mailboxname_full
= $mbx_ary[$i]['mbx'];
991 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
994 sqimap_utf7_decode_mbx_tree($mailboxes);
995 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
999 function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
1000 if (strtoupper($mbx_tree->mailboxname_full
) == 'INBOX')
1001 $mbx_tree->mailboxname_sub
= _("INBOX");
1003 $mbx_tree->mailboxname_sub
= imap_utf7_decode_local($mbx_tree->mailboxname_sub
);
1004 if ($mbx_tree->mbxs
) {
1005 $iCnt = count($mbx_tree->mbxs
);
1006 for ($i=0;$i<$iCnt;++
$i) {
1007 $mbxs_tree->mbxs
[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs
[$i]);
1013 function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
1015 $aMbxs[] =& $mbx_tree;
1016 if ($mbx_tree->mbxs
) {
1017 $iCnt = count($mbx_tree->mbxs
);
1018 for ($i=0;$i<$iCnt;++
$i) {
1019 sqimap_tree_to_ref_array($mbx_tree->mbxs
[$i],$aMbxs);
1024 function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
1025 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
1026 $aMbxs = $aQuery = $aTag = array();
1027 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
1028 // remove the root node
1029 array_shift($aMbxs);
1031 if($unseen_notify == 3) {
1032 $cnt = count($aMbxs);
1033 for($i=0;$i<$cnt;++
$i) {
1034 $oMbx =& $aMbxs[$i];
1035 if (!$oMbx->is_noselect
) {
1036 $mbx = $oMbx->mailboxname_full
;
1037 if ($unseen_type == 2 ||
1038 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
1039 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
1041 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (UNSEEN)';
1043 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
1045 $oMbx->unseen
= $oMbx->total
= false;
1049 $aMbxs[$i] =& $oMbx;
1051 // execute all the queries at once
1052 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
1053 $cnt = count($aMbxs);
1054 for($i=0;$i<$cnt;++
$i) {
1055 $oMbx =& $aMbxs[$i];
1057 if ($tag && $aServerResponse[$tag] == 'OK') {
1058 $sResponse = implode('', $aResponse[$tag]);
1059 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
1060 $oMbx->unseen
= $regs[1];
1062 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
1063 $oMbx->total
= $regs[1];
1068 } else if ($unseen_notify == 2) { // INBOX only
1069 $cnt = count($aMbxs);
1070 for($i=0;$i<$cnt;++
$i) {
1071 $oMbx =& $aMbxs[$i];
1072 if (strtoupper($oMbx->mailboxname_full
) == 'INBOX' ||
1073 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
1074 if ($unseen_type == 2 ||
1075 ($oMbx->mailboxname_full
== $trash_folder && $move_to_trash)) {
1076 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full
);
1077 $oMbx->unseen
= $aStatus['UNSEEN'];
1078 $oMbx->total
= $aStatus['MESSAGES'];
1080 $oMbx->unseen
= sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full
);
1082 $aMbxs[$i] =& $oMbx;
1083 if (!$move_to_trash && $trash_folder) {
1086 // trash comes after INBOX
1087 if ($oMbx->mailboxname_full
== $trash_folder) {