6 * Copyright (c) 1999-2003 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
13 require_once(SM_PATH
. 'functions/imap_utf7_local.php');
18 FIXME. This class should be extracted and placed in a separate file that
19 can be included before we start the session. That makes caching of the tree
20 possible. On a refresh mailboxes from left_main.php the only function that
21 should be called is the sqimap_get_status_mbx_tree. In case of subscribe
22 / rename / delete / new we have to create methods for adding/changing the
23 mailbox in the mbx_tree without the need for a refresh.
27 var $mailboxname_full = '', $mailboxname_sub= '', $is_noselect = false, $is_noinferiors = false,
28 $is_special = false, $is_root = false, $is_inbox = false, $is_sent = false,
29 $is_trash = false, $is_draft = false, $mbxs = array(),
30 $unseen = false, $total = false;
32 function addMbx($mbx, $delimiter, $start, $specialfirst) {
33 $ary = explode($delimiter, $mbx->mailboxname_full
);
35 for ($i = $start, $c = count($ary)-1; $i < $c; $i++
) {
36 $mbx_childs =& $mbx_parent->mbxs
;
39 foreach ($mbx_childs as $key => $parent) {
40 if ($parent->mailboxname_sub
== $ary[$i]) {
41 $mbx_parent =& $mbx_parent->mbxs
[$key];
48 $no_select_mbx = new mailboxes();
49 if (isset($mbx_parent->mailboxname_full
) && $mbx_parent->mailboxname_full
!= '') {
50 $no_select_mbx->mailboxname_full
= $mbx_parent->mailboxname_full
.$delimiter.$ary[$i];
52 $no_select_mbx->mailboxname_full
= $ary[$i];
54 $no_select_mbx->mailboxname_sub
= $ary[$i];
55 $no_select_mbx->is_noselect
= true;
56 $mbx_parent->mbxs
[] = $no_select_mbx;
60 $mbx_parent->mbxs
[] = $mbx;
61 if ($mbx->is_special
&& $specialfirst) {
62 usort($mbx_parent->mbxs
, 'sortSpecialMbx');
67 function sortSpecialMbx($a, $b) {
69 $acmp = '0'. $a->mailboxname_full
;
70 } else if ($a->is_special
) {
71 $acmp = '1'. $a->mailboxname_full
;
73 $acmp = '2' . $a->mailboxname_full
;
76 $bcmp = '0'. $b->mailboxname_full
;
77 }else if ($b->is_special
) {
78 $bcmp = '1' . $b->mailboxname_full
;
80 $bcmp = '2' . $b->mailboxname_full
;
82 return user_strcasecmp($acmp, $bcmp);
85 function compact_mailboxes_response($ary)
88 * Workaround for mailboxes returned as literal
89 * FIXME : Doesn't work if the mailbox name is multiple lines
90 * (larger then fgets buffer)
92 for ($i = 0, $iCnt=count($ary); $i < $iCnt; $i++
) {
93 if (isset($ary[$i +
1]) && substr($ary[$i], -3) == "}\r\n") {
94 if (ereg("^(\\* [A-Z]+.*)\\{[0-9]+\\}([ \n\r\t]*)$",
96 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+
1])) . '"' . $regs[2];
97 array_splice($ary, $i+
1, 2);
101 /* remove duplicates and ensure array is contiguous */
102 return array_values(array_unique($ary));
106 function find_mailbox_name ($mailbox) {
107 if (preg_match('/\*.+\"([^\r\n\"]*)\"[\s\r\n]*$/', $mailbox, $regs))
109 if (ereg(" *\"([^\r\n\"]*)\"[ \r\n]*$", $mailbox, $regs))
111 ereg(" *([^ \r\n\"]*)[ \r\n]*$",$mailbox,$regs);
116 // Extract the mailbox name from an untagged LIST (7.2.2) or LSUB (7.2.3) answer
117 // * (LIST|LSUB) (<Flags list>) (NIL|"<separator atom>") <mailbox name string>\r\n
118 // mailbox name in quoted string MUST be unquoted and stripslashed (sm API)
119 function find_mailbox_name($line)
121 if (preg_match('/^\* (?:LIST|LSUB) \([^\)]*\) (?:NIL|\"[^\"]*\") ([^\r\n]*)[\r\n]*$/i', $line, $regs)) {
122 if (substr($regs[1], 0, 1) == '"')
123 return stripslashes(substr($regs[1], 1, -1));
129 function check_is_noselect ($lsub_line) {
130 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i", $lsub_line);
133 function check_is_noinferiors ($lsub_line) {
134 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noinferiors[^\)]*\)/i", $lsub_line);
138 * If $haystack is a full mailbox name, and $needle is the mailbox
139 * separator character, returns the second last part of the full
140 * mailbox name (i.e. the mailbox's parent mailbox)
142 function readMailboxParent($haystack, $needle) {
146 $parts = explode($needle, $haystack);
147 $elem = array_pop($parts);
148 while ($elem == '' && count($parts)) {
149 $elem = array_pop($parts);
151 $ret = join($needle, $parts);
157 * Check if $subbox is below the specified $parentbox
159 function isBoxBelow( $subbox, $parentbox ) {
162 * Eliminate the obvious mismatch, where the
163 * subfolder path is shorter than that of the potential parent
165 if ( strlen($subbox) < strlen($parentbox) ) {
168 /* check for delimiter */
169 if (!substr($parentbox,-1) == $delimiter) {
170 $parentbox.=$delimiter;
172 if (substr($subbox,0,strlen($parentbox)) == $parentbox) {
179 /* Defines special mailboxes */
180 function isSpecialMailbox( $box ) {
181 $ret = ( (strtolower($box) == 'inbox') ||
182 isTrashMailbox($box) ||
isSentMailbox($box) ||
isDraftMailbox($box) );
185 $ret = boolean_hook_function('special_mailbox',$box,1);
190 function isTrashMailbox ($box) {
191 global $trash_folder, $move_to_trash;
192 return $move_to_trash && $trash_folder &&
193 ( $box == $trash_folder ||
isBoxBelow($box, $trash_folder) );
196 function isSentMailbox($box) {
197 global $sent_folder, $move_to_sent;
198 return $move_to_sent && $sent_folder &&
199 ( $box == $sent_folder ||
isBoxBelow($box, $sent_folder) );
202 function isDraftMailbox($box) {
203 global $draft_folder, $save_as_draft;
204 return $save_as_draft &&
205 ( $box == $draft_folder ||
isBoxBelow($box, $draft_folder) );
208 /* Expunges a mailbox */
209 function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
213 $id = sqimap_message_list_squisher($id);
220 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
221 $response, $message, $uid);
224 if (is_array($read)) {
225 foreach ($read as $r) {
226 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
234 /* Checks whether or not the specified mailbox exists */
235 function sqimap_mailbox_exists ($imap_stream, $mailbox) {
236 if (!isset($mailbox) ||
empty($mailbox)) {
239 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mailbox),
240 true, $response, $message);
241 return isset($mbx[0]);
244 /* Selects a mailbox */
245 function sqimap_mailbox_select ($imap_stream, $mailbox) {
246 global $auto_expunge;
248 if ($mailbox == 'None') {
252 $read = sqimap_run_command($imap_stream, 'SELECT ' . sqimap_encode_mailbox_name($mailbox),
253 true, $response, $message);
255 for ($i = 0, $cnt = count($read); $i < $cnt; $i++
) {
256 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
257 $result[strtoupper($regs[1])] = $regs[2];
258 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
259 $result[strtoupper($regs[2])] = $regs[1];
261 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
262 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
263 $result['PERMANENTFLAGS'] = $regs[1];
264 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
265 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
266 $result['FLAGS'] = $regs[1];
270 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
271 $result['RIGHTS']=$regs[1];
275 $tmp = sqimap_run_command($imap_stream, 'EXPUNGE', false, $a, $b);
280 /* Creates a folder */
281 function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
283 if (strtolower($type) == 'noselect') {
284 $mailbox .= $delimiter;
287 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' . sqimap_encode_mailbox_name($mailbox),
288 true, $response, $message);
289 sqimap_subscribe ($imap_stream, $mailbox);
292 /* Subscribes to an existing folder */
293 function sqimap_subscribe ($imap_stream, $mailbox) {
294 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' . sqimap_encode_mailbox_name($mailbox),
295 true, $response, $message);
298 /* Unsubscribes to an existing folder */
299 function sqimap_unsubscribe ($imap_stream, $mailbox) {
300 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' . sqimap_encode_mailbox_name($mailbox),
301 true, $response, $message);
304 /* Deletes the given folder */
305 function sqimap_mailbox_delete ($imap_stream, $mailbox) {
306 global $data_dir, $username;
307 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' . sqimap_encode_mailbox_name($mailbox),
308 true, $response, $message);
309 sqimap_unsubscribe ($imap_stream, $mailbox);
310 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
311 removePref($data_dir, $username, "thread_$mailbox");
314 /* Determines if the user is subscribed to the folder or not */
315 function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
316 $boxesall = sqimap_mailbox_list ($imap_stream);
317 foreach ($boxesall as $ref) {
318 if ($ref['unformatted'] == $folder) {
325 /* Renames a mailbox */
326 function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
327 if ( $old_name != $new_name ) {
328 global $delimiter, $imap_server_type, $data_dir, $username;
329 if ( substr( $old_name, -1 ) == $delimiter ) {
330 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
331 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
332 $postfix = $delimiter;
337 $boxesall = sqimap_mailbox_list($imap_stream);
338 $cmd = 'RENAME ' . sqimap_encode_mailbox_name($old_name) . ' ' . sqimap_encode_mailbox_name($new_name);
339 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
340 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
341 $oldpref = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
342 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
343 sqimap_subscribe($imap_stream, $new_name.$postfix);
344 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref);
345 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
346 $l = strlen( $old_name ) +
1;
349 foreach ($boxesall as $box) {
350 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
351 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
352 if ($imap_server_type == 'cyrus') {
353 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
354 $data = sqimap_run_command($imap_stream, $cmd, true,
355 $response, $message);
357 sqimap_unsubscribe($imap_stream, $box[$p]);
358 $oldpref = getPref($data_dir, $username, 'thread_'.$box[$p]);
359 removePref($data_dir, $username, 'thread_'.$box[$p]);
360 sqimap_subscribe($imap_stream, $new_sub);
361 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref);
362 do_hook_function('rename_or_delete_folder',
363 $args = array($box[$p], 'rename', $new_sub));
370 * Formats a mailbox into parts for the $boxesall array
374 * raw - Raw LIST/LSUB response from the IMAP server
375 * formatted - nicely formatted folder name
376 * unformatted - unformatted, but with delimiter at end removed
377 * unformatted-dm - folder name as it appears in raw response
378 * unformatted-disp - unformatted without $folder_prefix
380 function sqimap_mailbox_parse ($line, $line_lsub) {
381 global $folder_prefix, $delimiter;
383 /* Process each folder line */
384 for ($g = 0, $cnt = count($line); $g < $cnt; ++
$g) {
385 /* Store the raw IMAP reply */
386 if (isset($line[$g])) {
387 $boxesall[$g]['raw'] = $line[$g];
389 $boxesall[$g]['raw'] = '';
392 /* Count number of delimiters ($delimiter) in folder name */
393 $mailbox = /*trim(*/$line_lsub[$g]/*)*/;
394 $dm_count = substr_count($mailbox, $delimiter);
395 if (substr($mailbox, -1) == $delimiter) {
396 /* If name ends in delimiter, decrement count by one */
400 /* Format folder name, but only if it's a INBOX.* or has a parent. */
401 $boxesallbyname[$mailbox] = $g;
402 $parentfolder = readMailboxParent($mailbox, $delimiter);
403 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
404 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
405 (isset($boxesallbyname[$parentfolder]) &&
406 (strlen($parentfolder) > 0) ) ) {
407 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
409 $boxesall[$g]['formatted'] = str_repeat(' ', $indent);
411 $boxesall[$g]['formatted'] = '';
413 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
415 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
418 $boxesall[$g]['unformatted-dm'] = $mailbox;
419 if (substr($mailbox, -1) == $delimiter) {
420 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
422 $boxesall[$g]['unformatted'] = $mailbox;
423 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
424 $mailbox = substr($mailbox, strlen($folder_prefix));
426 $boxesall[$g]['unformatted-disp'] = $mailbox;
427 $boxesall[$g]['id'] = $g;
429 $boxesall[$g]['flags'] = array();
430 if (isset($line[$g])) {
431 ereg("\(([^)]*)\)",$line[$g],$regs);
432 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
433 // and $MDNSent <= last one doesn't have the \
434 // It's better to follow RFC3501 instead of using our own naming.
435 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
437 $boxesall[$g]['flags'] = explode(' ', $flags);
445 * Sorting function used to sort mailbox names.
446 * + Original patch from dave_michmerhuizen@yahoo.com
447 * + Allows case insensitivity when sorting folders
448 * + Takes care of the delimiter being sorted to the end, causing
449 * subfolders to be listed in below folders that are prefixed
450 * with their parent folders name.
452 * For example: INBOX.foo, INBOX.foobar, and INBOX.foo.bar
453 * Without special sort function: foobar between foo and foo.bar
454 * With special sort function: foobar AFTER foo and foo.bar :)
456 function user_strcasecmp($a, $b) {
457 return strnatcasecmp($a, $b);
461 * Returns list of options (to be echoed into select statement
462 * based on available mailboxes and separators
463 * Caller should surround options with <SELECT..> </SELECT> and
465 * $imap_stream - $imapConnection to query for mailboxes
466 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
467 * $folder_skip - array of folders to keep out of option list (compared in lower)
468 * $boxes - list of already fetched boxes (for places like folder panel, where
469 * you know these options will be shown 3 times in a row.. (most often unset).
470 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
471 * 'noselect' by default to remove unselectable mailboxes.
472 * 'noinferiors' used to filter out folders that can not contain subfolders.
473 * NULL to avoid flag check entirely.
474 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
475 * \NoSelect and \NoInferiors
476 * $use_long_format - override folder display preference and always show full folder name.
478 function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
479 $flag = 'noselect', $use_long_format = false ) {
480 global $username, $data_dir;
482 if ( $use_long_format ) {
483 $shorten_box_names = 0;
485 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF
);
489 $boxes = sqimap_mailbox_list($imap_stream);
492 foreach ($boxes as $boxes_part) {
493 if ($flag == NULL ||
!in_array($flag, $boxes_part['flags'])) {
494 $box = $boxes_part['unformatted'];
496 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
499 $lowerbox = strtolower($box);
500 // mailboxes are casesensitive => inbox.sent != inbox.Sent
501 // nevermind, to many dependencies this should be fixed!
503 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
506 switch ($shorten_box_names)
508 case 2: /* delimited, style = 2 */
509 $box2 = str_replace(' ', '. ', $boxes_part['formatted']);
511 case 1: /* indent, style = 1 */
512 $box2 = $boxes_part['formatted'];
514 default: /* default, long names, style = 0 */
515 $box2 = str_replace(' ', ' ', htmlspecialchars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
519 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
520 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'" SELECTED>'.$box2.'</OPTION>' . "\n";
522 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'">'.$box2.'</OPTION>' . "\n";
526 return $mbox_options;
530 * Returns sorted mailbox lists in several different ways.
531 * See comment on sqimap_mailbox_parse() for info about the returned array.
533 function sqimap_mailbox_list($imap_stream) {
534 global $default_folder_prefix;
536 if (!isset($boxesnew)) {
537 global $data_dir, $username, $list_special_folders_first,
538 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
539 $move_to_trash, $move_to_sent, $save_as_draft,
540 $delimiter, $noselect_fix_enable;
542 $inbox_in_list = false;
543 $inbox_subscribed = false;
545 require_once(SM_PATH
. 'include/load_prefs.php');
547 if ($noselect_fix_enable) {
548 $lsub_args = "LSUB \"$folder_prefix\" \"*%\"";
550 $lsub_args = "LSUB \"$folder_prefix\" \"*\"";
553 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
554 true, $response, $message);
555 $lsub_ary = compact_mailboxes_response($lsub_ary);
557 $sorted_lsub_ary = array();
558 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++
) {
559 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
560 $sorted_lsub_ary[] = $temp_mailbox_name;
561 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
562 $inbox_subscribed = true;
566 /* natural sort mailboxes */
567 if (isset($sorted_lsub_ary)) {
568 usort($sorted_lsub_ary, 'user_strcasecmp');
571 * The LSUB response doesn't provide us information about \Noselect
572 * mail boxes. The LIST response does, that's why we need to do a LIST
573 * call to retrieve the flags for the mailbox
574 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
575 * in other words, we cannot rely on it.
577 $sorted_list_ary = array();
578 for ($i=0; $i < count($sorted_lsub_ary); $i++
) {
579 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
580 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
583 $mbx = $sorted_lsub_ary[$i];
585 $mbx = stripslashes($mbx);
586 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mbx),
587 true, $response, $message);
588 $read = compact_mailboxes_response($read);
589 if (isset($read[0])) {
590 $sorted_list_ary[$i] = $read[0];
592 $sorted_list_ary[$i] = '';
596 * Just in case they're not subscribed to their inbox,
597 * we'll get it for them anyway
599 if (!$inbox_subscribed) {
600 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" INBOX',
601 true, $response, $message);
602 $sorted_list_ary[] = implode('', compact_mailboxes_response($inbox_ary));
603 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
606 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
608 /* Now, lets sort for special folders */
609 $boxesnew = $used = array();
612 $cnt = count($boxesall);
613 $used = array_pad($used,$cnt,false);
614 for($k = 0; $k < $cnt; ++
$k) {
615 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
616 $boxesnew[] = $boxesall[$k];
621 /* List special folders and their subfolders, if requested. */
622 if ($list_special_folders_first) {
623 for($k = 0; $k < $cnt; ++
$k) {
624 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
625 $boxesnew[] = $boxesall[$k];
631 /* Rest of the folders */
632 for($k = 0; $k < $cnt; $k++
) {
634 $boxesnew[] = $boxesall[$k];
643 * Returns a list of all folders, subscribed or not
645 function sqimap_mailbox_list_all($imap_stream) {
646 global $list_special_folders_first, $folder_prefix, $delimiter;
648 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
649 $read_ary = compact_mailboxes_response($read_ary);
653 $fld_pre_length = strlen($folder_prefix);
654 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++
) {
655 /* Store the raw IMAP reply */
656 $boxes[$g]['raw'] = $read_ary[$i];
658 /* Count number of delimiters ($delimiter) in folder name */
659 $mailbox = find_mailbox_name($read_ary[$i]);
660 $dm_count = substr_count($mailbox, $delimiter);
661 if (substr($mailbox, -1) == $delimiter) {
662 /* If name ends in delimiter - decrement count by one */
666 /* Format folder name, but only if it's a INBOX.* or has a parent. */
667 $boxesallbyname[$mailbox] = $g;
668 $parentfolder = readMailboxParent($mailbox, $delimiter);
669 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
670 (ereg('^'.$folder_prefix, $mailbox)) ||
671 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
673 $boxes[$g]['formatted'] = str_repeat(' ', $dm_count);
675 $boxes[$g]['formatted'] = '';
677 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
679 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
682 $boxes[$g]['unformatted-dm'] = $mailbox;
683 if (substr($mailbox, -1) == $delimiter) {
684 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
686 $boxes[$g]['unformatted'] = $mailbox;
687 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
689 $boxes[$g]['id'] = $g;
691 /* Now lets get the flags for this mailbox */
692 $read_mlbx = $read_ary[$i];
693 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+
1);
694 $flags = substr($flags, 0, strpos($flags, ')'));
695 $flags = str_replace('\\', '', $flags);
696 $flags = trim(strtolower($flags));
698 $boxes[$g]['flags'] = explode(' ', $flags);
700 $boxes[$g]['flags'] = array();
704 if(is_array($boxes)) {
711 function sqimap_mailbox_tree($imap_stream) {
712 global $boxesnew, $default_folder_prefix, $unseen_notify, $unseen_type;
713 if (!isset($boxesnew)) {
715 global $data_dir, $username, $list_special_folders_first,
716 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
720 $inbox_in_list = false;
721 $inbox_subscribed = false;
723 $noinferiors = false;
725 require_once(SM_PATH
. 'include/load_prefs.php');
728 $lsub_ary = sqimap_run_command ($imap_stream, "LSUB \"$folder_prefix\" \"*\"",
729 true, $response, $message);
730 $lsub_ary = compact_mailboxes_response($lsub_ary);
732 /* Check to see if we have an INBOX */
735 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++
) {
736 if (preg_match("/^\*\s+LSUB\s+(.*)\"?INBOX\"?[^(\/\.)].*$/i",$lsub_ary[$i])) {
737 $lsub_ary[$i] = strtoupper($lsub_ary[$i]);
743 if ($has_inbox == false) {
744 $lsub_ary[] = '* LSUB () NIL INBOX';
748 * Section about removing the last element was removed
749 * We don't return "* OK" anymore from sqimap_read_data
752 $sorted_lsub_ary = array();
753 $cnt = count($lsub_ary);
754 for ($i = 0; $i < $cnt; $i++
) {
755 $mbx = find_mailbox_name($lsub_ary[$i]);
757 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
758 if ($imap_server_type != "uw") {
759 $noselect = check_is_noselect($lsub_ary[$i]);
760 $noinferiors = check_is_noinferiors($lsub_ary[$i]);
762 if (substr($mbx, -1) == $delimiter) {
763 $mbx = substr($mbx, 0, strlen($mbx) - 1);
765 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
767 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
768 if ($imap_server_type == "uw") {
771 // prepare an array with queries
772 foreach ($sorted_lsub_ary as $aMbx) {
773 $mbx = stripslashes($aMbx['mbx']);
774 sqimap_prepare_pipelined_query('LIST "" ' . sqimap_encode_mailbox_name($mbx), $tag, $aQuery, false);
777 $sorted_lsub_ary = array();
778 // execute all the queries at once
779 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
780 foreach($aTag as $tag => $mbx) {
781 if ($aServerResponse[$tag] == 'OK') {
782 $sResponse = implode('', $aResponse[$tag]);
783 $noselect = check_is_noselect($sResponse);
784 $noinferiors = check_is_noinferiors($sResponse);
785 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
788 $cnt = count($sorted_lsub_ary);
790 $sorted_lsub_ary = array_values($sorted_lsub_ary);
791 array_multisort($sorted_lsub_ary, SORT_ASC
, SORT_REGULAR
);
792 $boxesnew = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
797 function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
798 global $data_dir, $username, $list_special_folders_first,
799 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
800 $move_to_trash, $move_to_sent, $save_as_draft,
801 $delimiter, $imap_server_type;
803 $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
805 /* create virtual root node */
806 $mailboxes= new mailboxes();
807 $mailboxes->is_root
= true;
812 if (isset($folder_prefix) && ($folder_prefix != '')) {
813 $start = substr_count($folder_prefix,$delimiter);
814 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
816 $mailboxes->mailboxname_full
= substr($folder_prefix,0, (strlen($folder_prefix)-1));
818 $mailboxes->mailboxname_full
= $folder_prefix;
821 $mailboxes->mailboxname_sub
= $mailboxes->mailboxname_full
;
826 $cnt = count($mbx_ary);
827 for ($i=0; $i < $cnt; $i++
) {
828 if ($mbx_ary[$i]['mbx'] !='' ) {
829 $mbx = new mailboxes();
830 $mailbox = $mbx_ary[$i]['mbx'];
833 sent subfolders messes up using existing code as subfolders
834 were marked, but the parents were ordered somewhere else in
835 the list, despite having "special folders at top" option set.
836 Need a better method than this.
839 if ($mailbox == 'INBOX') {
840 $mbx->is_special = true;
841 } elseif (stristr($trash_folder , $mailbox)) {
842 $mbx->is_special = true;
843 } elseif (stristr($sent_folder , $mailbox)) {
844 $mbx->is_special = true;
845 } elseif (stristr($draft_folder , $mailbox)) {
846 $mbx->is_special = true;
851 $mbx->is_inbox = true;
852 $mbx->is_special = true;
853 $mbx_ary[$i]['noselect'] = false;
856 $mbx->is_trash = true;
857 $mbx->is_special = true;
860 $mbx->is_sent = true;
861 $mbx->is_special = true;
864 $mbx->is_draft = true;
865 $mbx->is_special = true;
869 $mbx->is_special |
= ($mbx->is_inbox
= (strtoupper($mailbox) == 'INBOX'));
870 $mbx->is_special |
= ($mbx->is_trash
= isTrashMailbox($mailbox));
871 $mbx->is_special |
= ($mbx->is_sent
= isSentMailbox($mailbox));
872 $mbx->is_special |
= ($mbx->is_draft
= isDraftMailbox($mailbox));
873 if (!$mbx->is_special
)
874 $mbx->is_special
= do_hook_function('special_mailbox', $mailbox);
876 if (isset($mbx_ary[$i]['unseen'])) {
877 $mbx->unseen
= $mbx_ary[$i]['unseen'];
879 if (isset($mbx_ary[$i]['nummessages'])) {
880 $mbx->total
= $mbx_ary[$i]['nummessages'];
883 $mbx->is_noselect
= $mbx_ary[$i]['noselect'];
884 $mbx->is_noinferiors
= $mbx_ary[$i]['noinferiors'];
886 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
888 $mbx->mailboxname_sub
= substr($mbx_ary[$i]['mbx'],$r_del_pos+
1);
889 } else { /* mailbox is root folder */
890 $mbx->mailboxname_sub
= $mbx_ary[$i]['mbx'];
892 $mbx->mailboxname_full
= $mbx_ary[$i]['mbx'];
894 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
897 sqimap_utf7_decode_mbx_tree($mailboxes);
898 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
902 function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
903 if (strtoupper($mbx_tree->mailboxname_sub
) == 'INBOX')
904 $mbx_tree->mailboxname_sub
= _("INBOX");
906 $mbx_tree->mailboxname_sub
= imap_utf7_decode_local($mbx_tree->mailboxname_sub
);
907 if ($mbx_tree->mbxs
) {
908 $iCnt = count($mbx_tree->mbxs
);
909 for ($i=0;$i<$iCnt;++
$i) {
910 $mbxs_tree->mbxs
[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs
[$i]);
916 function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
918 $aMbxs[] =& $mbx_tree;
919 if ($mbx_tree->mbxs
) {
920 $iCnt = count($mbx_tree->mbxs
);
921 for ($i=0;$i<$iCnt;++
$i) {
922 sqimap_tree_to_ref_array($mbx_tree->mbxs
[$i],$aMbxs);
928 /* Define preferences for folder settings. */
929 /* FIXME, we should load constants.php
931 define('SMPREF_UNSEEN_NONE', 1);
932 define('SMPREF_UNSEEN_INBOX', 2);
933 define('SMPREF_UNSEEN_ALL', 3);
935 define('SMPREF_UNSEEN_SPECIAL', 4); // Only special folders
936 define('SMPREF_UNSEEN_NORMAL', 5); // Only normal folders
939 define('SMPREF_UNSEEN_ONLY', 1);
940 define('SMPREF_UNSEEN_TOTAL', 2);
943 function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
944 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
945 $aMbxs = $aQuery = $aTag = array();
946 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
947 // remove the root node
950 if($unseen_notify == 3) {
951 $cnt = count($aMbxs);
952 for($i=0;$i<$cnt;++
$i) {
954 if (!$oMbx->is_noselect
) {
955 $mbx = $oMbx->mailboxname_full
;
956 if ($unseen_type == 2 ||
957 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
958 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
960 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (UNSEEN)';
962 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
964 $oMbx->unseen
= $oMbx->total
= false;
970 // execute all the queries at once
971 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
972 $cnt = count($aMbxs);
973 for($i=0;$i<$cnt;++
$i) {
976 if ($tag && $aServerResponse[$tag] == 'OK') {
977 $sResponse = implode('', $aResponse[$tag]);
978 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
979 $oMbx->unseen
= $regs[1];
981 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
982 $oMbx->total
= $regs[1];
987 } else if ($unseen_notify == 2) { // INBOX only
988 $cnt = count($aMbxs);
989 for($i=0;$i<$cnt;++
$i) {
991 if (strtoupper($oMbx->mailboxname_full
) == 'INBOX' ||
992 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
993 if ($unseen_type == 2 ||
994 ($oMbx->mailboxname_full
== $trash_folder && $move_to_trash)) {
995 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full
);
996 $oMbx->unseen
= $aStatus['UNSEEN'];
997 $oMbx->total
= $aStatus['MESSAGES'];
999 $oMbx->unseen
= sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full
);
1001 $aMbxs[$i] =& $oMbx;
1002 if (!$move_to_trash && $trash_folder) {
1005 // trash comes after INBOX
1006 if ($oMbx->mailboxname_full
== $trash_folder) {