6 * Copyright (c) 1999-2005 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 * Checks whether or not the specified mailbox exists
254 function sqimap_mailbox_exists ($imap_stream, $mailbox) {
255 if (!isset($mailbox) ||
empty($mailbox)) {
258 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mailbox),
259 true, $response, $message);
260 return isset($mbx[0]);
266 function sqimap_mailbox_select ($imap_stream, $mailbox) {
267 if ($mailbox == 'None') {
271 $read = sqimap_run_command($imap_stream, 'SELECT ' . sqimap_encode_mailbox_name($mailbox),
272 true, $response, $message);
274 for ($i = 0, $cnt = count($read); $i < $cnt; $i++
) {
275 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
276 $result[strtoupper($regs[1])] = $regs[2];
277 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
278 $result[strtoupper($regs[2])] = $regs[1];
280 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
281 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
282 $result['PERMANENTFLAGS'] = explode(' ',strtolower($regs[1]));
283 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
284 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
285 $result['FLAGS'] = explode(' ',strtolower($regs[1]));
289 if (!isset($result['PERMANENTFLAGS'])) {
290 $result['PERMANENTFLAGS'] = $result['FLAGS'];
292 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
293 $result['RIGHTS']=strtoupper($regs[1]);
302 function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
304 if (strtolower($type) == 'noselect') {
305 $mailbox .= $delimiter;
308 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' .
309 sqimap_encode_mailbox_name($mailbox),
310 true, $response, $message);
311 sqimap_subscribe ($imap_stream, $mailbox);
315 * Subscribes to an existing folder.
317 function sqimap_subscribe ($imap_stream, $mailbox,$debug=true) {
318 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' .
319 sqimap_encode_mailbox_name($mailbox),
320 $debug, $response, $message);
324 * Unsubscribes from an existing folder
326 function sqimap_unsubscribe ($imap_stream, $mailbox) {
327 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' .
328 sqimap_encode_mailbox_name($mailbox),
329 false, $response, $message);
333 * Deletes the given folder
335 function sqimap_mailbox_delete ($imap_stream, $mailbox) {
336 global $data_dir, $username;
337 sqimap_unsubscribe ($imap_stream, $mailbox);
338 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' .
339 sqimap_encode_mailbox_name($mailbox),
340 true, $response, $message);
341 if ($response !== 'OK') {
343 sqimap_subscribe ($imap_stream, $mailbox);
345 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
346 removePref($data_dir, $username, "thread_$mailbox");
351 * Determines if the user is subscribed to the folder or not
353 function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
354 $boxesall = sqimap_mailbox_list ($imap_stream);
355 foreach ($boxesall as $ref) {
356 if ($ref['unformatted'] == $folder) {
366 function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
367 if ( $old_name != $new_name ) {
368 global $delimiter, $imap_server_type, $data_dir, $username;
369 if ( substr( $old_name, -1 ) == $delimiter ) {
370 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
371 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
372 $postfix = $delimiter;
377 $boxesall = sqimap_mailbox_list($imap_stream);
378 $cmd = 'RENAME ' . sqimap_encode_mailbox_name($old_name) .
379 ' ' . sqimap_encode_mailbox_name($new_name);
380 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
381 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
382 $oldpref = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
383 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
384 sqimap_subscribe($imap_stream, $new_name.$postfix);
385 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref);
386 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
387 $l = strlen( $old_name ) +
1;
390 foreach ($boxesall as $box) {
391 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
392 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
393 if ($imap_server_type == 'cyrus') {
394 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
395 $data = sqimap_run_command($imap_stream, $cmd, true,
396 $response, $message);
398 sqimap_unsubscribe($imap_stream, $box[$p]);
399 $oldpref = getPref($data_dir, $username, 'thread_'.$box[$p]);
400 removePref($data_dir, $username, 'thread_'.$box[$p]);
401 sqimap_subscribe($imap_stream, $new_sub);
402 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref);
403 do_hook_function('rename_or_delete_folder',
404 $args = array($box[$p], 'rename', $new_sub));
411 * Formats a mailbox into parts for the $boxesall array
415 * raw - Raw LIST/LSUB response from the IMAP server
416 * formatted - nicely formatted folder name
417 * unformatted - unformatted, but with delimiter at end removed
418 * unformatted-dm - folder name as it appears in raw response
419 * unformatted-disp - unformatted without $folder_prefix
421 function sqimap_mailbox_parse ($line, $line_lsub) {
422 global $folder_prefix, $delimiter;
424 /* Process each folder line */
425 for ($g = 0, $cnt = count($line); $g < $cnt; ++
$g) {
426 /* Store the raw IMAP reply */
427 if (isset($line[$g])) {
428 $boxesall[$g]['raw'] = $line[$g];
430 $boxesall[$g]['raw'] = '';
433 /* Count number of delimiters ($delimiter) in folder name */
434 $mailbox = /*trim(*/$line_lsub[$g]/*)*/;
435 $dm_count = substr_count($mailbox, $delimiter);
436 if (substr($mailbox, -1) == $delimiter) {
437 /* If name ends in delimiter, decrement count by one */
441 /* Format folder name, but only if it's a INBOX.* or has a parent. */
442 $boxesallbyname[$mailbox] = $g;
443 $parentfolder = readMailboxParent($mailbox, $delimiter);
444 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
445 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
446 (isset($boxesallbyname[$parentfolder]) &&
447 (strlen($parentfolder) > 0) ) ) {
448 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
450 $boxesall[$g]['formatted'] = str_repeat(' ', $indent);
452 $boxesall[$g]['formatted'] = '';
454 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
456 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
459 $boxesall[$g]['unformatted-dm'] = $mailbox;
460 if (substr($mailbox, -1) == $delimiter) {
461 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
463 $boxesall[$g]['unformatted'] = $mailbox;
464 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
465 $mailbox = substr($mailbox, strlen($folder_prefix));
467 $boxesall[$g]['unformatted-disp'] = $mailbox;
468 $boxesall[$g]['id'] = $g;
470 $boxesall[$g]['flags'] = array();
471 if (isset($line[$g])) {
472 ereg("\(([^)]*)\)",$line[$g],$regs);
473 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
474 // and $MDNSent <= last one doesn't have the \
475 // It's better to follow RFC3501 instead of using our own naming.
476 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
478 $boxesall[$g]['flags'] = explode(' ', $flags);
486 * Returns list of options (to be echoed into select statement
487 * based on available mailboxes and separators
488 * Caller should surround options with <select ...> </select> and
490 * $imap_stream - $imapConnection to query for mailboxes
491 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
492 * $folder_skip - array of folders to keep out of option list (compared in lower)
493 * $boxes - list of already fetched boxes (for places like folder panel, where
494 * you know these options will be shown 3 times in a row.. (most often unset).
495 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
496 * 'noselect' by default to remove unselectable mailboxes.
497 * 'noinferiors' used to filter out folders that can not contain subfolders.
498 * NULL to avoid flag check entirely.
499 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
500 * \NoSelect and \NoInferiors
501 * $use_long_format - override folder display preference and always show full folder name.
503 function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
504 $flag = 'noselect', $use_long_format = false ) {
505 global $username, $data_dir;
507 if ( $use_long_format ) {
508 $shorten_box_names = 0;
510 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF
);
514 $boxes = sqimap_mailbox_list($imap_stream);
517 foreach ($boxes as $boxes_part) {
518 if ($flag == NULL ||
(is_array($boxes_part['flags'])
519 && !in_array($flag, $boxes_part['flags']))) {
520 $box = $boxes_part['unformatted'];
522 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
525 $lowerbox = strtolower($box);
526 // mailboxes are casesensitive => inbox.sent != inbox.Sent
527 // nevermind, to many dependencies this should be fixed!
529 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
532 switch ($shorten_box_names)
534 case 2: /* delimited, style = 2 */
535 $box2 = str_replace('&nbsp;&nbsp;', '. ', htmlspecialchars($boxes_part['formatted']));
537 case 1: /* indent, style = 1 */
538 $box2 = str_replace('&nbsp;&nbsp;', ' ', htmlspecialchars($boxes_part['formatted']));
540 default: /* default, long names, style = 0 */
541 $box2 = str_replace(' ', ' ', htmlspecialchars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
545 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
546 $mbox_options .= '<option value="' . htmlspecialchars($box) .'" selected="selected">'.$box2.'</option>' . "\n";
548 $mbox_options .= '<option value="' . htmlspecialchars($box) .'">'.$box2.'</option>' . "\n";
552 return $mbox_options;
556 * Returns sorted mailbox lists in several different ways.
557 * See comment on sqimap_mailbox_parse() for info about the returned array.
561 function sqimap_mailbox_list($imap_stream, $force=false) {
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_subscribed = false;
568 $listsubscribed = sqimap_capability($imap_stream,'LIST-SUBSCRIBED');
570 require_once(SM_PATH
. 'include/load_prefs.php');
573 if ($listsubscribed) {
574 $lsub = 'LIST (SUBSCRIBED)';
579 if ($noselect_fix_enable) {
581 $lsub_args = "$lsub \"$folder_prefix\" \"*%\"";
583 $lsub_args = "$lsub \"$folder_prefix\" \"*\"";
586 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
587 true, $response, $message);
588 $lsub_ary = compact_mailboxes_response($lsub_ary);
590 $sorted_lsub_ary = array();
591 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++
) {
593 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
594 $sorted_lsub_ary[] = $temp_mailbox_name;
595 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
596 $inbox_subscribed = true;
600 /* natural sort mailboxes */
601 if (isset($sorted_lsub_ary)) {
602 usort($sorted_lsub_ary, 'strnatcasecmp');
605 * The LSUB response doesn't provide us information about \Noselect
606 * mail boxes. The LIST response does, that's why we need to do a LIST
607 * call to retrieve the flags for the mailbox
608 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
609 * in other words, we cannot rely on it.
611 $sorted_list_ary = array();
612 // if (!$listsubscribed) {
613 for ($i=0; $i < count($sorted_lsub_ary); $i++
) {
614 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
615 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
618 $mbx = $sorted_lsub_ary[$i];
621 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mbx),
622 true, $response, $message);
624 $read = compact_mailboxes_response($read);
626 if (isset($read[0])) {
627 $sorted_list_ary[$i] = $read[0];
629 $sorted_list_ary[$i] = '';
634 * Just in case they're not subscribed to their inbox,
635 * we'll get it for them anyway
637 if (!$inbox_subscribed) {
638 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" "INBOX"',
639 true, $response, $message);
640 $sorted_list_ary[] = implode('',compact_mailboxes_response($inbox_ary));
641 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
644 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
646 /* Now, lets sort for special folders */
647 $boxesnew = $used = array();
650 $cnt = count($boxesall);
651 $used = array_pad($used,$cnt,false);
652 for($k = 0; $k < $cnt; ++
$k) {
653 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
654 $boxesnew[] = $boxesall[$k];
659 /* List special folders and their subfolders, if requested. */
660 if ($list_special_folders_first) {
661 for($k = 0; $k < $cnt; ++
$k) {
662 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
663 $boxesnew[] = $boxesall[$k];
668 /* Rest of the folders */
669 for($k = 0; $k < $cnt; $k++
) {
671 $boxesnew[] = $boxesall[$k];
674 sqsession_register($boxesnew,'boxesnew');
680 * Returns a list of all folders, subscribed or not
682 function sqimap_mailbox_list_all($imap_stream) {
683 global $list_special_folders_first, $folder_prefix, $delimiter;
685 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
686 $read_ary = compact_mailboxes_response($read_ary);
689 $fld_pre_length = strlen($folder_prefix);
690 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++
) {
691 /* Store the raw IMAP reply */
692 $boxes[$g]['raw'] = $read_ary[$i];
694 /* Count number of delimiters ($delimiter) in folder name */
695 $mailbox = find_mailbox_name($read_ary[$i]);
696 $dm_count = substr_count($mailbox, $delimiter);
697 if (substr($mailbox, -1) == $delimiter) {
698 /* If name ends in delimiter - decrement count by one */
702 /* Format folder name, but only if it's a INBOX.* or has a parent. */
703 $boxesallbyname[$mailbox] = $g;
704 $parentfolder = readMailboxParent($mailbox, $delimiter);
705 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
706 (ereg('^'.$folder_prefix, $mailbox)) ||
707 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
709 $boxes[$g]['formatted'] = str_repeat(' ', $dm_count);
711 $boxes[$g]['formatted'] = '';
713 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
715 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
718 $boxes[$g]['unformatted-dm'] = $mailbox;
719 if (substr($mailbox, -1) == $delimiter) {
720 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
722 $boxes[$g]['unformatted'] = $mailbox;
723 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
725 $boxes[$g]['id'] = $g;
727 /* Now lets get the flags for this mailbox */
728 $read_mlbx = $read_ary[$i];
729 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+
1);
730 $flags = substr($flags, 0, strpos($flags, ')'));
731 $flags = str_replace('\\', '', $flags);
732 $flags = trim(strtolower($flags));
734 $boxes[$g]['flags'] = explode(' ', $flags);
736 $boxes[$g]['flags'] = array();
740 if(is_array($boxes)) {
747 function sqimap_mailbox_tree($imap_stream) {
748 global $default_folder_prefix;
750 global $data_dir, $username, $list_special_folders_first,
751 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
755 $noinferiors = false;
757 require_once(SM_PATH
. 'include/load_prefs.php');
760 $lsub_ary = sqimap_run_command ($imap_stream, "LSUB \"$folder_prefix\" \"*\"",
761 true, $response, $message);
762 $lsub_ary = compact_mailboxes_response($lsub_ary);
764 /* Check to see if we have an INBOX */
767 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++
) {
768 if (preg_match("/^\*\s+LSUB.*\s\"?INBOX\"?[^(\/\.)].*$/i",$lsub_ary[$i])) {
769 $lsub_ary[$i] = strtoupper($lsub_ary[$i]);
770 // in case of an unsubscribed inbox an imap server can
771 // return the inbox in the lsub results with a \NoSelect
773 if (!preg_match("/\*\s+LSUB\s+\(.*\\\\NoSelect.*\).*/i",$lsub_ary[$i])) {
776 // remove the result and request it again with a list
777 // response at a later stage.
778 unset($lsub_ary[$i]);
779 // re-index the array otherwise the addition of the LIST
780 // response will fail in PHP 4.1.2 and probably other older versions
781 $lsub_ary = array_values($lsub_ary);
787 if ($has_inbox == false) {
788 // do a list request for inbox because we should always show
789 // inbox even if the user isn't subscribed to it.
790 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" INBOX',
791 true, $response, $message);
792 $inbox_ary = compact_mailboxes_response($inbox_ary);
793 if (count($inbox_ary)) {
794 $lsub_ary[] = $inbox_ary[0];
799 * Section about removing the last element was removed
800 * We don't return "* OK" anymore from sqimap_read_data
803 $sorted_lsub_ary = array();
804 $cnt = count($lsub_ary);
805 for ($i = 0; $i < $cnt; $i++
) {
806 $mbx = find_mailbox_name($lsub_ary[$i]);
808 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
809 if ($imap_server_type != "uw") {
810 $noselect = check_is_noselect($lsub_ary[$i]);
811 $noinferiors = check_is_noinferiors($lsub_ary[$i]);
813 if (substr($mbx, -1) == $delimiter) {
814 $mbx = substr($mbx, 0, strlen($mbx) - 1);
816 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
818 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
819 if ($imap_server_type == "uw") {
822 // prepare an array with queries
823 foreach ($sorted_lsub_ary as $aMbx) {
824 $mbx = stripslashes($aMbx['mbx']);
825 sqimap_prepare_pipelined_query('LIST "" ' . sqimap_encode_mailbox_name($mbx), $tag, $aQuery, false);
828 $sorted_lsub_ary = array();
829 // execute all the queries at once
830 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
831 foreach($aTag as $tag => $mbx) {
832 if ($aServerResponse[$tag] == 'OK') {
833 $sResponse = implode('', $aResponse[$tag]);
834 $noselect = check_is_noselect($sResponse);
835 $noinferiors = check_is_noinferiors($sResponse);
836 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
839 $cnt = count($sorted_lsub_ary);
841 $sorted_lsub_ary = array_values($sorted_lsub_ary);
842 usort($sorted_lsub_ary, 'mbxSort');
843 $boxestree = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
848 function mbxSort($a, $b) {
849 return strnatcasecmp($a['mbx'], $b['mbx']);
852 function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
853 global $data_dir, $username, $list_special_folders_first,
854 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
855 $move_to_trash, $move_to_sent, $save_as_draft,
856 $delimiter, $imap_server_type;
858 // $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
860 /* create virtual root node */
861 $mailboxes= new mailboxes();
862 $mailboxes->is_root
= true;
867 if (isset($folder_prefix) && ($folder_prefix != '')) {
868 $start = substr_count($folder_prefix,$delimiter);
869 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
870 $mailboxes->mailboxname_full
= substr($folder_prefix,0, (strlen($folder_prefix)-1));
872 $mailboxes->mailboxname_full
= $folder_prefix;
875 $mailboxes->mailboxname_sub
= $mailboxes->mailboxname_full
;
880 $cnt = count($mbx_ary);
881 for ($i=0; $i < $cnt; $i++
) {
882 if ($mbx_ary[$i]['mbx'] !='' ) {
883 $mbx = new mailboxes();
884 $mailbox = $mbx_ary[$i]['mbx'];
887 sent subfolders messes up using existing code as subfolders
888 were marked, but the parents were ordered somewhere else in
889 the list, despite having "special folders at top" option set.
890 Need a better method than this.
893 if ($mailbox == 'INBOX') {
894 $mbx->is_special = true;
895 } elseif (stristr($trash_folder , $mailbox)) {
896 $mbx->is_special = true;
897 } elseif (stristr($sent_folder , $mailbox)) {
898 $mbx->is_special = true;
899 } elseif (stristr($draft_folder , $mailbox)) {
900 $mbx->is_special = true;
905 $mbx->is_inbox = true;
906 $mbx->is_special = true;
907 $mbx_ary[$i]['noselect'] = false;
910 $mbx->is_trash = true;
911 $mbx->is_special = true;
914 $mbx->is_sent = true;
915 $mbx->is_special = true;
918 $mbx->is_draft = true;
919 $mbx->is_special = true;
923 $mbx->is_special |
= ($mbx->is_inbox
= (strtoupper($mailbox) == 'INBOX'));
924 $mbx->is_special |
= ($mbx->is_trash
= isTrashMailbox($mailbox));
925 $mbx->is_special |
= ($mbx->is_sent
= isSentMailbox($mailbox));
926 $mbx->is_special |
= ($mbx->is_draft
= isDraftMailbox($mailbox));
927 if (!$mbx->is_special
)
928 $mbx->is_special
= boolean_hook_function('special_mailbox', $mailbox, 1);
930 if (isset($mbx_ary[$i]['unseen'])) {
931 $mbx->unseen
= $mbx_ary[$i]['unseen'];
933 if (isset($mbx_ary[$i]['nummessages'])) {
934 $mbx->total
= $mbx_ary[$i]['nummessages'];
937 $mbx->is_noselect
= $mbx_ary[$i]['noselect'];
938 $mbx->is_noinferiors
= $mbx_ary[$i]['noinferiors'];
940 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
942 $mbx->mailboxname_sub
= substr($mbx_ary[$i]['mbx'],$r_del_pos+
1);
943 } else { /* mailbox is root folder */
944 $mbx->mailboxname_sub
= $mbx_ary[$i]['mbx'];
946 $mbx->mailboxname_full
= $mbx_ary[$i]['mbx'];
948 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
951 sqimap_utf7_decode_mbx_tree($mailboxes);
952 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
956 function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
957 if (strtoupper($mbx_tree->mailboxname_full
) == 'INBOX')
958 $mbx_tree->mailboxname_sub
= _("INBOX");
960 $mbx_tree->mailboxname_sub
= imap_utf7_decode_local($mbx_tree->mailboxname_sub
);
961 if ($mbx_tree->mbxs
) {
962 $iCnt = count($mbx_tree->mbxs
);
963 for ($i=0;$i<$iCnt;++
$i) {
964 $mbxs_tree->mbxs
[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs
[$i]);
970 function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
972 $aMbxs[] =& $mbx_tree;
973 if ($mbx_tree->mbxs
) {
974 $iCnt = count($mbx_tree->mbxs
);
975 for ($i=0;$i<$iCnt;++
$i) {
976 sqimap_tree_to_ref_array($mbx_tree->mbxs
[$i],$aMbxs);
981 function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
982 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
983 $aMbxs = $aQuery = array();
984 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
985 // remove the root node
988 if($unseen_notify == 3) {
989 $cnt = count($aMbxs);
990 for($i=0;$i<$cnt;++
$i) {
992 if (!$oMbx->is_noselect
) {
993 $mbx = $oMbx->mailboxname_full
;
994 if ($unseen_type == 2 ||
995 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
996 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
998 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (UNSEEN)';
1000 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
1002 $oMbx->unseen
= $oMbx->total
= false;
1006 $aMbxs[$i] =& $oMbx;
1008 // execute all the queries at once
1009 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
1010 $cnt = count($aMbxs);
1011 for($i=0;$i<$cnt;++
$i) {
1012 $oMbx =& $aMbxs[$i];
1014 if ($tag && $aServerResponse[$tag] == 'OK') {
1015 $sResponse = implode('', $aResponse[$tag]);
1016 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
1017 $oMbx->unseen
= $regs[1];
1019 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
1020 $oMbx->total
= $regs[1];
1025 } else if ($unseen_notify == 2) { // INBOX only
1026 $cnt = count($aMbxs);
1027 for($i=0;$i<$cnt;++
$i) {
1028 $oMbx =& $aMbxs[$i];
1029 if (strtoupper($oMbx->mailboxname_full
) == 'INBOX' ||
1030 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
1031 if ($unseen_type == 2 ||
1032 ($oMbx->mailboxname_full
== $trash_folder && $move_to_trash)) {
1033 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full
);
1034 $oMbx->unseen
= $aStatus['UNSEEN'];
1035 $oMbx->total
= $aStatus['MESSAGES'];
1037 $oMbx->unseen
= sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full
);
1039 $aMbxs[$i] =& $oMbx;
1040 if (!$move_to_trash && $trash_folder) {
1043 // trash comes after INBOX
1044 if ($oMbx->mailboxname_full
== $trash_folder) {