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 if ($acmp == $bcmp) return 0;
83 return ($acmp > $bcmp) ?
1: -1;
86 function compact_mailboxes_response($ary)
89 * Workaround for mailboxes returned as literal
90 * FIXME : Doesn't work if the mailbox name is multiple lines
91 * (larger then fgets buffer)
93 for ($i = 0, $iCnt=count($ary); $i < $iCnt; $i++
) {
94 if (isset($ary[$i +
1]) && substr($ary[$i], -3) == "}\r\n") {
95 if (ereg("^(\\* [A-Z]+.*)\\{[0-9]+\\}([ \n\r\t]*)$",
97 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+
1])) . '"' . $regs[2];
98 array_splice($ary, $i+
1, 2);
102 /* remove duplicates and ensure array is contiguous */
103 return array_values(array_unique($ary));
107 function find_mailbox_name ($mailbox) {
108 if (preg_match('/\*.+\"([^\r\n\"]*)\"[\s\r\n]*$/', $mailbox, $regs))
110 if (ereg(" *\"([^\r\n\"]*)\"[ \r\n]*$", $mailbox, $regs))
112 ereg(" *([^ \r\n\"]*)[ \r\n]*$",$mailbox,$regs);
117 // Extract the mailbox name from an untagged LIST (7.2.2) or LSUB (7.2.3) answer
118 // * (LIST|LSUB) (<Flags list>) (NIL|"<separator atom>") <mailbox name string>\r\n
119 // mailbox name in quoted string MUST be unquoted and stripslashed (sm API)
120 function find_mailbox_name($line)
122 if (preg_match('/^\* (?:LIST|LSUB) \([^\)]*\) (?:NIL|\"[^\"]*\") ([^\r\n]*)[\r\n]*$/i', $line, $regs)) {
123 if (substr($regs[1], 0, 1) == '"')
124 return stripslashes(substr($regs[1], 1, -1));
130 function check_is_noselect ($lsub_line) {
131 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i", $lsub_line);
134 function check_is_noinferiors ($lsub_line) {
135 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noinferiors[^\)]*\)/i", $lsub_line);
139 * If $haystack is a full mailbox name, and $needle is the mailbox
140 * separator character, returns the second last part of the full
141 * mailbox name (i.e. the mailbox's parent mailbox)
143 function readMailboxParent($haystack, $needle) {
147 $parts = explode($needle, $haystack);
148 $elem = array_pop($parts);
149 while ($elem == '' && count($parts)) {
150 $elem = array_pop($parts);
152 $ret = join($needle, $parts);
158 * Check if $subbox is below the specified $parentbox
160 function isBoxBelow( $subbox, $parentbox ) {
163 * Eliminate the obvious mismatch, where the
164 * subfolder path is shorter than that of the potential parent
166 if ( strlen($subbox) < strlen($parentbox) ) {
169 /* check for delimiter */
170 if (!substr($parentbox,-1) == $delimiter) {
171 $parentbox.=$delimiter;
173 if (substr($subbox,0,strlen($parentbox)) == $parentbox) {
180 /* Defines special mailboxes */
181 function isSpecialMailbox( $box ) {
182 $ret = ( (strtolower($box) == 'inbox') ||
183 isTrashMailbox($box) ||
isSentMailbox($box) ||
isDraftMailbox($box) );
186 $ret = do_hook_function( 'special_mailbox', $box );
191 function isTrashMailbox ($box) {
192 global $trash_folder, $move_to_trash;
193 return $move_to_trash && $trash_folder &&
194 ( $box == $trash_folder ||
isBoxBelow($box, $trash_folder) );
197 function isSentMailbox($box) {
198 global $sent_folder, $move_to_sent;
199 return $move_to_sent && $sent_folder &&
200 ( $box == $sent_folder ||
isBoxBelow($box, $sent_folder) );
203 function isDraftMailbox($box) {
204 global $draft_folder, $save_as_draft;
205 return $save_as_draft &&
206 ( $box == $draft_folder ||
isBoxBelow($box, $draft_folder) );
209 /* Expunges a mailbox */
210 function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
214 $id = sqimap_message_list_squisher($id);
221 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
222 $response, $message, $uid);
225 if (is_array($read)) {
226 foreach ($read as $r) {
227 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
235 /* Checks whether or not the specified mailbox exists */
236 function sqimap_mailbox_exists ($imap_stream, $mailbox) {
237 if (!isset($mailbox) ||
empty($mailbox)) {
240 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mailbox),
241 true, $response, $message);
242 return isset($mbx[0]);
245 /* Selects a mailbox */
246 function sqimap_mailbox_select ($imap_stream, $mailbox) {
247 global $auto_expunge;
249 if ($mailbox == 'None') {
253 $read = sqimap_run_command($imap_stream, 'SELECT ' . sqimap_encode_mailbox_name($mailbox),
254 true, $response, $message);
256 for ($i = 0, $cnt = count($read); $i < $cnt; $i++
) {
257 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
258 $result[strtoupper($regs[1])] = $regs[2];
259 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
260 $result[strtoupper($regs[2])] = $regs[1];
262 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
263 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
264 $result['PERMANENTFLAGS'] = $regs[1];
265 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
266 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
267 $result['FLAGS'] = $regs[1];
271 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
272 $result['RIGHTS']=$regs[1];
276 $tmp = sqimap_run_command($imap_stream, 'EXPUNGE', false, $a, $b);
281 /* Creates a folder */
282 function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
284 if (strtolower($type) == 'noselect') {
285 $mailbox .= $delimiter;
288 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' . sqimap_encode_mailbox_name($mailbox),
289 true, $response, $message);
290 sqimap_subscribe ($imap_stream, $mailbox);
293 /* Subscribes to an existing folder */
294 function sqimap_subscribe ($imap_stream, $mailbox) {
295 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' . sqimap_encode_mailbox_name($mailbox),
296 true, $response, $message);
299 /* Unsubscribes to an existing folder */
300 function sqimap_unsubscribe ($imap_stream, $mailbox) {
301 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' . sqimap_encode_mailbox_name($mailbox),
302 true, $response, $message);
305 /* Deletes the given folder */
306 function sqimap_mailbox_delete ($imap_stream, $mailbox) {
307 global $data_dir, $username;
308 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' . sqimap_encode_mailbox_name($mailbox),
309 true, $response, $message);
310 sqimap_unsubscribe ($imap_stream, $mailbox);
311 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
312 removePref($data_dir, $username, "thread_$mailbox");
315 /* Determines if the user is subscribed to the folder or not */
316 function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
317 $boxesall = sqimap_mailbox_list ($imap_stream);
318 foreach ($boxesall as $ref) {
319 if ($ref['unformatted'] == $folder) {
326 /* Renames a mailbox */
327 function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
328 if ( $old_name != $new_name ) {
329 global $delimiter, $imap_server_type, $data_dir, $username;
330 if ( substr( $old_name, -1 ) == $delimiter ) {
331 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
332 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
333 $postfix = $delimiter;
338 $boxesall = sqimap_mailbox_list($imap_stream);
339 $cmd = 'RENAME ' . sqimap_encode_mailbox_name($old_name) . ' ' . sqimap_encode_mailbox_name($new_name);
340 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
341 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
342 $oldpref = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
343 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
344 sqimap_subscribe($imap_stream, $new_name.$postfix);
345 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref);
346 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
347 $l = strlen( $old_name ) +
1;
350 foreach ($boxesall as $box) {
351 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
352 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
353 if ($imap_server_type == 'cyrus') {
354 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
355 $data = sqimap_run_command($imap_stream, $cmd, true,
356 $response, $message);
358 sqimap_unsubscribe($imap_stream, $box[$p]);
359 $oldpref = getPref($data_dir, $username, 'thread_'.$box[$p]);
360 removePref($data_dir, $username, 'thread_'.$box[$p]);
361 sqimap_subscribe($imap_stream, $new_sub);
362 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref);
363 do_hook_function('rename_or_delete_folder',
364 $args = array($box[$p], 'rename', $new_sub));
371 * Formats a mailbox into parts for the $boxesall array
375 * raw - Raw LIST/LSUB response from the IMAP server
376 * formatted - nicely formatted folder name
377 * unformatted - unformatted, but with delimiter at end removed
378 * unformatted-dm - folder name as it appears in raw response
379 * unformatted-disp - unformatted without $folder_prefix
381 function sqimap_mailbox_parse ($line, $line_lsub) {
382 global $folder_prefix, $delimiter;
384 /* Process each folder line */
385 for ($g = 0, $cnt = count($line); $g < $cnt; ++
$g) {
386 /* Store the raw IMAP reply */
387 if (isset($line[$g])) {
388 $boxesall[$g]['raw'] = $line[$g];
390 $boxesall[$g]['raw'] = '';
393 /* Count number of delimiters ($delimiter) in folder name */
394 $mailbox = /*trim(*/$line_lsub[$g]/*)*/;
395 $dm_count = substr_count($mailbox, $delimiter);
396 if (substr($mailbox, -1) == $delimiter) {
397 /* If name ends in delimiter, decrement count by one */
401 /* Format folder name, but only if it's a INBOX.* or has a parent. */
402 $boxesallbyname[$mailbox] = $g;
403 $parentfolder = readMailboxParent($mailbox, $delimiter);
404 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
405 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
406 (isset($boxesallbyname[$parentfolder]) &&
407 (strlen($parentfolder) > 0) ) ) {
408 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
410 $boxesall[$g]['formatted'] = str_repeat(' ', $indent);
412 $boxesall[$g]['formatted'] = '';
414 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
416 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
419 $boxesall[$g]['unformatted-dm'] = $mailbox;
420 if (substr($mailbox, -1) == $delimiter) {
421 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
423 $boxesall[$g]['unformatted'] = $mailbox;
424 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
425 $mailbox = substr($mailbox, strlen($folder_prefix));
427 $boxesall[$g]['unformatted-disp'] = $mailbox;
428 $boxesall[$g]['id'] = $g;
430 $boxesall[$g]['flags'] = array();
431 if (isset($line[$g])) {
432 ereg("\(([^)]*)\)",$line[$g],$regs);
433 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
434 // and $MDNSent <= last one doesn't have the \
435 // It's better to follow RFC3501 instead of using our own naming.
436 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
438 $boxesall[$g]['flags'] = explode(' ', $flags);
446 * Sorting function used to sort mailbox names.
447 * + Original patch from dave_michmerhuizen@yahoo.com
448 * + Allows case insensitivity when sorting folders
449 * + Takes care of the delimiter being sorted to the end, causing
450 * subfolders to be listed in below folders that are prefixed
451 * with their parent folders name.
453 * For example: INBOX.foo, INBOX.foobar, and INBOX.foo.bar
454 * Without special sort function: foobar between foo and foo.bar
455 * With special sort function: foobar AFTER foo and foo.bar :)
457 function user_strcasecmp($a, $b) {
458 return strnatcasecmp($a, $b);
462 * Returns list of options (to be echoed into select statement
463 * based on available mailboxes and separators
464 * Caller should surround options with <SELECT..> </SELECT> and
466 * $imap_stream - $imapConnection to query for mailboxes
467 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
468 * $folder_skip - array of folders to keep out of option list (compared in lower)
469 * $boxes - list of already fetched boxes (for places like folder panel, where
470 * you know these options will be shown 3 times in a row.. (most often unset).
471 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
472 * 'noselect' by default to remove unselectable mailboxes.
473 * 'noinferiors' used to filter out folders that can not contain subfolders.
474 * NULL to avoid flag check entirely.
475 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
476 * \NoSelect and \NoInferiors
477 * $use_long_format - override folder display preference and always show full folder name.
479 function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
480 $flag = 'noselect', $use_long_format = false ) {
481 global $username, $data_dir;
483 if ( $use_long_format ) {
484 $shorten_box_names = 0;
486 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF
);
490 $boxes = sqimap_mailbox_list($imap_stream);
493 foreach ($boxes as $boxes_part) {
494 if ($flag == NULL ||
!in_array($flag, $boxes_part['flags'])) {
495 $box = $boxes_part['unformatted'];
497 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
500 $lowerbox = strtolower($box);
501 // mailboxes are casesensitive => inbox.sent != inbox.Sent
502 // nevermind, to many dependencies this should be fixed!
504 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
507 switch ($shorten_box_names)
509 case 2: /* delimited, style = 2 */
510 $box2 = str_replace(' ', '. ', $boxes_part['formatted']);
512 case 1: /* indent, style = 1 */
513 $box2 = $boxes_part['formatted'];
515 default: /* default, long names, style = 0 */
516 $box2 = str_replace(' ', ' ', htmlspecialchars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
520 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
521 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'" SELECTED>'.$box2.'</OPTION>' . "\n";
523 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'">'.$box2.'</OPTION>' . "\n";
527 return $mbox_options;
531 * Returns sorted mailbox lists in several different ways.
532 * See comment on sqimap_mailbox_parse() for info about the returned array.
534 function sqimap_mailbox_list($imap_stream) {
535 global $default_folder_prefix;
537 if (!isset($boxesnew)) {
538 global $data_dir, $username, $list_special_folders_first,
539 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
540 $move_to_trash, $move_to_sent, $save_as_draft,
541 $delimiter, $noselect_fix_enable;
543 $inbox_in_list = false;
544 $inbox_subscribed = false;
546 require_once(SM_PATH
. 'include/load_prefs.php');
548 if ($noselect_fix_enable) {
549 $lsub_args = "LSUB \"$folder_prefix\" \"*%\"";
551 $lsub_args = "LSUB \"$folder_prefix\" \"*\"";
554 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
555 true, $response, $message);
556 $lsub_ary = compact_mailboxes_response($lsub_ary);
558 $sorted_lsub_ary = array();
559 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++
) {
560 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
561 $sorted_lsub_ary[] = $temp_mailbox_name;
562 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
563 $inbox_subscribed = true;
567 /* natural sort mailboxes */
568 if (isset($sorted_lsub_ary)) {
569 usort($sorted_lsub_ary, 'user_strcasecmp');
572 * The LSUB response doesn't provide us information about \Noselect
573 * mail boxes. The LIST response does, that's why we need to do a LIST
574 * call to retrieve the flags for the mailbox
575 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
576 * in other words, we cannot rely on it.
578 $sorted_list_ary = array();
579 for ($i=0; $i < count($sorted_lsub_ary); $i++
) {
580 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
581 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
584 $mbx = $sorted_lsub_ary[$i];
586 $mbx = stripslashes($mbx);
587 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mbx),
588 true, $response, $message);
589 $read = compact_mailboxes_response($read);
590 if (isset($read[0])) {
591 $sorted_list_ary[$i] = $read[0];
593 $sorted_list_ary[$i] = '';
597 * Just in case they're not subscribed to their inbox,
598 * we'll get it for them anyway
600 if (!$inbox_subscribed) {
601 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" INBOX',
602 true, $response, $message);
603 $sorted_list_ary[] = implode('', compact_mailboxes_response($inbox_ary));
604 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
607 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
609 /* Now, lets sort for special folders */
610 $boxesnew = $used = array();
613 $cnt = count($boxesall);
614 $used = array_pad($used,$cnt,false);
615 for($k = 0; $k < $cnt; ++
$k) {
616 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
617 $boxesnew[] = $boxesall[$k];
622 /* List special folders and their subfolders, if requested. */
623 if ($list_special_folders_first) {
624 for($k = 0; $k < $cnt; ++
$k) {
625 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
626 $boxesnew[] = $boxesall[$k];
632 /* Rest of the folders */
633 for($k = 0; $k < $cnt; $k++
) {
635 $boxesnew[] = $boxesall[$k];
644 * Returns a list of all folders, subscribed or not
646 function sqimap_mailbox_list_all($imap_stream) {
647 global $list_special_folders_first, $folder_prefix, $delimiter;
649 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
650 $read_ary = compact_mailboxes_response($read_ary);
654 $fld_pre_length = strlen($folder_prefix);
655 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++
) {
656 /* Store the raw IMAP reply */
657 $boxes[$g]['raw'] = $read_ary[$i];
659 /* Count number of delimiters ($delimiter) in folder name */
660 $mailbox = find_mailbox_name($read_ary[$i]);
661 $dm_count = substr_count($mailbox, $delimiter);
662 if (substr($mailbox, -1) == $delimiter) {
663 /* If name ends in delimiter - decrement count by one */
667 /* Format folder name, but only if it's a INBOX.* or has a parent. */
668 $boxesallbyname[$mailbox] = $g;
669 $parentfolder = readMailboxParent($mailbox, $delimiter);
670 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
671 (ereg('^'.$folder_prefix, $mailbox)) ||
672 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
674 $boxes[$g]['formatted'] = str_repeat(' ', $dm_count);
676 $boxes[$g]['formatted'] = '';
678 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
680 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
683 $boxes[$g]['unformatted-dm'] = $mailbox;
684 if (substr($mailbox, -1) == $delimiter) {
685 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
687 $boxes[$g]['unformatted'] = $mailbox;
688 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
690 $boxes[$g]['id'] = $g;
692 /* Now lets get the flags for this mailbox */
693 $read_mlbx = $read_ary[$i];
694 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+
1);
695 $flags = substr($flags, 0, strpos($flags, ')'));
696 $flags = str_replace('\\', '', $flags);
697 $flags = trim(strtolower($flags));
699 $boxes[$g]['flags'] = explode(' ', $flags);
701 $boxes[$g]['flags'] = array();
705 if(is_array($boxes)) {
712 function sqimap_mailbox_tree($imap_stream) {
713 global $boxesnew, $default_folder_prefix, $unseen_notify, $unseen_type;
714 if (!isset($boxesnew)) {
716 global $data_dir, $username, $list_special_folders_first,
717 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
721 $inbox_in_list = false;
722 $inbox_subscribed = false;
724 $noinferiors = false;
726 require_once(SM_PATH
. 'include/load_prefs.php');
729 $lsub_ary = sqimap_run_command ($imap_stream, "LSUB \"$folder_prefix\" \"*\"",
730 true, $response, $message);
731 $lsub_ary = compact_mailboxes_response($lsub_ary);
733 /* Check to see if we have an INBOX */
736 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++
) {
737 if (preg_match("/^\*\s+LSUB\s+(.*)\"?INBOX\"?[^(\/\.)].*$/i",$lsub_ary[$i])) {
738 $lsub_ary[$i] = strtoupper($lsub_ary[$i]);
744 if ($has_inbox == false) {
745 $lsub_ary[] = '* LSUB () NIL INBOX';
749 * Section about removing the last element was removed
750 * We don't return "* OK" anymore from sqimap_read_data
753 $sorted_lsub_ary = array();
754 $cnt = count($lsub_ary);
755 for ($i = 0; $i < $cnt; $i++
) {
756 $mbx = find_mailbox_name($lsub_ary[$i]);
758 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
759 if ($imap_server_type != "uw") {
760 $noselect = check_is_noselect($lsub_ary[$i]);
761 $noinferiors = check_is_noinferiors($lsub_ary[$i]);
763 if (substr($mbx, -1) == $delimiter) {
764 $mbx = substr($mbx, 0, strlen($mbx) - 1);
766 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
768 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
769 if ($imap_server_type == "uw") {
772 // prepare an array with queries
773 foreach ($sorted_lsub_ary as $aMbx) {
774 $mbx = stripslashes($aMbx['mbx']);
775 sqimap_prepare_pipelined_query('LIST "" ' . sqimap_encode_mailbox_name($mbx), $tag, $aQuery, false);
778 $sorted_lsub_ary = array();
779 // execute all the queries at once
780 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
781 foreach($aTag as $tag => $mbx) {
782 if ($aServerResponse[$tag] == 'OK') {
783 $sResponse = implode('', $aResponse[$tag]);
784 $noselect = check_is_noselect($sResponse);
785 $noinferiors = check_is_noinferiors($sResponse);
786 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
789 $cnt = count($sorted_lsub_ary);
791 $sorted_lsub_ary = array_values($sorted_lsub_ary);
792 array_multisort($sorted_lsub_ary, SORT_ASC
, SORT_REGULAR
);
793 $boxesnew = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
798 function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
799 global $data_dir, $username, $list_special_folders_first,
800 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
801 $move_to_trash, $move_to_sent, $save_as_draft,
802 $delimiter, $imap_server_type;
804 $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
806 /* create virtual root node */
807 $mailboxes= new mailboxes();
808 $mailboxes->is_root
= true;
813 if (isset($folder_prefix) && ($folder_prefix != '')) {
814 $start = substr_count($folder_prefix,$delimiter);
815 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
817 $mailboxes->mailboxname_full
= substr($folder_prefix,0, (strlen($folder_prefix)-1));
819 $mailboxes->mailboxname_full
= $folder_prefix;
822 $mailboxes->mailboxname_sub
= $mailboxes->mailboxname_full
;
827 $cnt = count($mbx_ary);
828 for ($i=0; $i < $cnt; $i++
) {
829 if ($mbx_ary[$i]['mbx'] !='' ) {
830 $mbx = new mailboxes();
831 $mailbox = $mbx_ary[$i]['mbx'];
834 sent subfolders messes up using existing code as subfolders
835 were marked, but the parents were ordered somewhere else in
836 the list, despite having "special folders at top" option set.
837 Need a better method than this.
840 if ($mailbox == 'INBOX') {
841 $mbx->is_special
= true;
842 } elseif (stristr($trash_folder , $mailbox)) {
843 $mbx->is_special
= true;
844 } elseif (stristr($sent_folder , $mailbox)) {
845 $mbx->is_special
= true;
846 } elseif (stristr($draft_folder , $mailbox)) {
847 $mbx->is_special
= true;
852 $mbx->is_inbox
= true;
853 $mbx->is_special
= true;
854 $mbx_ary[$i]['noselect'] = false;
857 $mbx->is_trash
= true;
858 $mbx->is_special
= true;
861 $mbx->is_sent
= true;
862 $mbx->is_special
= true;
865 $mbx->is_draft
= true;
866 $mbx->is_special
= true;
870 if (isset($mbx_ary[$i]['unseen'])) {
871 $mbx->unseen
= $mbx_ary[$i]['unseen'];
873 if (isset($mbx_ary[$i]['nummessages'])) {
874 $mbx->total
= $mbx_ary[$i]['nummessages'];
877 $mbx->is_noselect
= $mbx_ary[$i]['noselect'];
878 $mbx->is_noinferiors
= $mbx_ary[$i]['noinferiors'];
880 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
882 $mbx->mailboxname_sub
= substr($mbx_ary[$i]['mbx'],$r_del_pos+
1);
883 } else { /* mailbox is root folder */
884 $mbx->mailboxname_sub
= $mbx_ary[$i]['mbx'];
886 $mbx->mailboxname_full
= $mbx_ary[$i]['mbx'];
888 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
891 sqimap_utf7_decode_mbx_tree($mailboxes);
892 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
896 function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
897 if (strtoupper($mbx_tree->mailboxname_sub
) == 'INBOX')
898 $mbx_tree->mailboxname_sub
= _("INBOX");
900 $mbx_tree->mailboxname_sub
= imap_utf7_decode_local($mbx_tree->mailboxname_sub
);
901 if ($mbx_tree->mbxs
) {
902 $iCnt = count($mbx_tree->mbxs
);
903 for ($i=0;$i<$iCnt;++
$i) {
904 $mbxs_tree->mbxs
[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs
[$i]);
910 function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
912 $aMbxs[] =& $mbx_tree;
913 if ($mbx_tree->mbxs
) {
914 $iCnt = count($mbx_tree->mbxs
);
915 for ($i=0;$i<$iCnt;++
$i) {
916 sqimap_tree_to_ref_array($mbx_tree->mbxs
[$i],$aMbxs);
922 /* Define preferences for folder settings. */
923 /* FIXME, we should load constants.php
925 define('SMPREF_UNSEEN_NONE', 1);
926 define('SMPREF_UNSEEN_INBOX', 2);
927 define('SMPREF_UNSEEN_ALL', 3);
929 define('SMPREF_UNSEEN_SPECIAL', 4); // Only special folders
930 define('SMPREF_UNSEEN_NORMAL', 5); // Only normal folders
933 define('SMPREF_UNSEEN_ONLY', 1);
934 define('SMPREF_UNSEEN_TOTAL', 2);
937 function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
938 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
939 $aMbxs = $aQuery = $aTag = array();
940 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
941 // remove the root node
944 if($unseen_notify == 3) {
945 $cnt = count($aMbxs);
946 for($i=0;$i<$cnt;++
$i) {
948 if (!$oMbx->is_noselect
) {
949 $mbx = $oMbx->mailboxname_full
;
950 if ($unseen_type == 2 ||
951 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
952 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
954 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (UNSEEN)';
956 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
958 $oMbx->unseen
= $oMbx->total
= false;
964 // execute all the queries at once
965 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
966 $cnt = count($aMbxs);
967 for($i=0;$i<$cnt;++
$i) {
970 if ($tag && $aServerResponse[$tag] == 'OK') {
971 $sResponse = implode('', $aResponse[$tag]);
972 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
973 $oMbx->unseen
= $regs[1];
975 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
976 $oMbx->total
= $regs[1];
981 } else if ($unseen_notify == 2) { // INBOX only
982 $cnt = count($aMbxs);
983 for($i=0;$i<$cnt;++
$i) {
985 if (strtoupper($oMbx->mailboxname_full
) == 'INBOX' ||
986 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
987 if ($unseen_type == 2 ||
988 ($oMbx->mailboxname_full
== $trash_folder && $move_to_trash)) {
989 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full
);
990 $oMbx->unseen
= $aStatus['UNSEEN'];
991 $oMbx->total
= $aStatus['MESSAGES'];
993 $oMbx->unseen
= sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full
);
996 if (!$move_to_trash && $trash_folder) {
999 // trash comes after INBOX
1000 if ($oMbx->mailboxname_full
== $trash_folder) {