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,
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;
87 function find_mailbox_name ($mailbox) {
88 if (preg_match('/\*.+\"([^\r\n\"]*)\"[\s\r\n]*$/', $mailbox, $regs))
90 if (ereg(" *\"([^\r\n\"]*)\"[ \r\n]*$", $mailbox, $regs))
92 ereg(" *([^ \r\n\"]*)[ \r\n]*$",$mailbox,$regs);
97 // Extract the mailbox name from an untagged LIST (7.2.2) or LSUB (7.2.3) answer
98 // * (LIST|LSUB) (<Flags list>) (NIL|"<separator atom>") <mailbox name string>\r\n
99 // mailbox name in quoted string MUST be unquoted and stripslashed (sm API)
100 function find_mailbox_name($line)
102 if (preg_match('/^\* (?:LIST|LSUB) \([^\)]*\) (?:NIL|\"[^\"]*\") ([^\r\n]*)[\r\n]*$/i', $line, $regs)) {
103 if (substr($regs[1], 0, 1) == '"')
104 return stripslashes(substr($regs[1], 1, -1));
110 function encode_mailbox_name($what)
112 if (ereg("[\"\\\r\n]", $what))
113 return '{' . strlen($what) . "}\r\n" . $what; /* 4.3 literal form */
114 return '"' . $what . '"'; /* 4.3 quoted string form */
117 function compact_mailboxes_response($ary)
120 * Workaround for mailboxes returned as literal
121 * FIXME : Doesn't work if the mailbox name is multiple lines
122 * (larger then fgets buffer)
124 for ($i = 0; $i < count($ary); $i++
) {
125 if (isset($ary[$i +
1]) && substr($ary[$i], -3) == "}\r\n") {
126 if (ereg("^(\\* [A-Z]+.*)\\{[0-9]+\\}([ \n\r\t]*)$",
128 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+
1])) . '"' . $regs[2];
129 array_splice($ary, $i+
1, 2);
133 /* remove duplicates and ensure array is contiguous */
134 return array_values(array_unique($ary));
137 function check_is_noselect ($lsub_line) {
138 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/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) {
183 /* Defines special mailboxes */
184 function isSpecialMailbox( $box ) {
185 global $trash_folder, $sent_folder, $draft_folder,
186 $move_to_trash, $move_to_sent, $save_as_draft;
188 $ret = ( (strtolower($box) == 'inbox') ||
189 isTrashMailbox($box) ||
isSentMailbox($box) ||
isDraftMailbox($box) );
192 $ret = do_hook_function( 'special_mailbox', $box );
197 function isTrashMailbox ($box) {
198 global $trash_folder, $move_to_trash;
199 return $move_to_trash && $trash_folder &&
200 ( $box == $trash_folder ||
isBoxBelow($box, $trash_folder) );
203 function isSentMailbox($box) {
204 global $sent_folder, $move_to_sent;
205 return $move_to_sent && $sent_folder &&
206 ( $box == $sent_folder ||
isBoxBelow($box, $sent_folder) );
209 function isDraftMailbox($box) {
210 global $draft_folder, $save_as_draft;
211 return $save_as_draft &&
212 ( $box == $draft_folder ||
isBoxBelow($box, $draft_folder) );
215 /* Expunges a mailbox */
216 function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
220 $id = sqimap_message_list_squisher($id);
227 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
228 $response, $message, $uid);
231 if (is_array($read)) {
232 foreach ($read as $r) {
233 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
241 /* Checks whether or not the specified mailbox exists */
242 function sqimap_mailbox_exists ($imap_stream, $mailbox) {
243 if (!isset($mailbox) ||
empty($mailbox)) {
246 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . encode_mailbox_name($mailbox),
247 true, $response, $message);
248 return isset($mbx[0]);
251 /* Selects a mailbox */
252 function sqimap_mailbox_select ($imap_stream, $mailbox) {
253 global $auto_expunge;
255 if ($mailbox == 'None') {
259 $read = sqimap_run_command($imap_stream, 'SELECT ' . encode_mailbox_name($mailbox),
260 true, $response, $message);
262 for ($i = 0, $cnt = count($read); $i < $cnt; $i++
) {
263 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
264 $result[strtoupper($regs[1])] = $regs[2];
265 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
266 $result[strtoupper($regs[2])] = $regs[1];
268 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
269 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
270 $result['PERMANENTFLAGS'] = $regs[1];
271 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
272 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
273 $result['FLAGS'] = $regs[1];
277 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
278 $result['RIGHTS']=$regs[1];
282 $tmp = sqimap_run_command($imap_stream, 'EXPUNGE', false, $a, $b);
287 /* Creates a folder */
288 function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
290 if (strtolower($type) == 'noselect') {
291 $mailbox .= $delimiter;
294 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' . encode_mailbox_name($mailbox),
295 true, $response, $message);
296 sqimap_subscribe ($imap_stream, $mailbox);
299 /* Subscribes to an existing folder */
300 function sqimap_subscribe ($imap_stream, $mailbox) {
301 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' . encode_mailbox_name($mailbox),
302 true, $response, $message);
305 /* Unsubscribes to an existing folder */
306 function sqimap_unsubscribe ($imap_stream, $mailbox) {
307 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' . encode_mailbox_name($mailbox),
308 true, $response, $message);
311 /* Deletes the given folder */
312 function sqimap_mailbox_delete ($imap_stream, $mailbox) {
313 global $data_dir, $username;
314 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' . encode_mailbox_name($mailbox),
315 true, $response, $message);
316 sqimap_unsubscribe ($imap_stream, $mailbox);
317 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
318 removePref($data_dir, $username, "thread_$mailbox");
321 /* Determines if the user is subscribed to the folder or not */
322 function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
323 $boxesall = sqimap_mailbox_list ($imap_stream);
324 foreach ($boxesall as $ref) {
325 if ($ref['unformatted'] == $folder) {
332 /* Renames a mailbox */
333 function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
334 if ( $old_name != $new_name ) {
335 global $delimiter, $imap_server_type, $data_dir, $username;
336 if ( substr( $old_name, -1 ) == $delimiter ) {
337 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
338 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
339 $postfix = $delimiter;
344 $boxesall = sqimap_mailbox_list($imap_stream);
345 $cmd = 'RENAME ' . encode_mailbox_name($old_name) . ' ' . encode_mailbox_name($new_name);
346 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
347 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
348 $oldpref = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
349 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
350 sqimap_subscribe($imap_stream, $new_name.$postfix);
351 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref);
352 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
353 $l = strlen( $old_name ) +
1;
356 foreach ($boxesall as $box) {
357 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
358 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
359 if ($imap_server_type == 'cyrus') {
360 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
361 $data = sqimap_run_command($imap_stream, $cmd, true,
362 $response, $message);
364 sqimap_unsubscribe($imap_stream, $box[$p]);
365 $oldpref = getPref($data_dir, $username, 'thread_'.$box[$p]);
366 removePref($data_dir, $username, 'thread_'.$box[$p]);
367 sqimap_subscribe($imap_stream, $new_sub);
368 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref);
369 do_hook_function('rename_or_delete_folder',
370 $args = array($box[$p], 'rename', $new_sub));
377 * Formats a mailbox into 4 parts for the $boxesall array
379 * The four parts are:
381 * raw - Raw LIST/LSUB response from the IMAP server
382 * formatted - nicely formatted folder name
383 * unformatted - unformatted, but with delimiter at end removed
384 * unformatted-dm - folder name as it appears in raw response
385 * unformatted-disp - unformatted without $folder_prefix
387 function sqimap_mailbox_parse ($line, $line_lsub) {
388 global $folder_prefix, $delimiter;
390 /* Process each folder line */
391 for ($g = 0, $cnt = count($line); $g < $cnt; ++
$g) {
392 /* Store the raw IMAP reply */
393 if (isset($line[$g])) {
394 $boxesall[$g]['raw'] = $line[$g];
396 $boxesall[$g]['raw'] = '';
399 /* Count number of delimiters ($delimiter) in folder name */
400 $mailbox = trim($line_lsub[$g]);
401 $dm_count = substr_count($mailbox, $delimiter);
402 if (substr($mailbox, -1) == $delimiter) {
403 /* If name ends in delimiter, decrement count by one */
407 /* Format folder name, but only if it's a INBOX.* or has a parent. */
408 $boxesallbyname[$mailbox] = $g;
409 $parentfolder = readMailboxParent($mailbox, $delimiter);
410 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
411 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
412 (isset($boxesallbyname[$parentfolder]) &&
413 (strlen($parentfolder) > 0) ) ) {
414 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
416 $boxesall[$g]['formatted'] = str_repeat(' ', $indent);
418 $boxesall[$g]['formatted'] = '';
420 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
422 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
425 $boxesall[$g]['unformatted-dm'] = $mailbox;
426 if (substr($mailbox, -1) == $delimiter) {
427 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
429 $boxesall[$g]['unformatted'] = $mailbox;
430 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
431 $mailbox = substr($mailbox, strlen($folder_prefix));
433 $boxesall[$g]['unformatted-disp'] = $mailbox;
434 $boxesall[$g]['id'] = $g;
436 $boxesall[$g]['flags'] = array();
437 if (isset($line[$g])) {
438 ereg("\(([^)]*)\)",$line[$g],$regs);
439 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
440 // and $MDNSent <= last one doesn't have the \
441 // It's better to follow RFC3501 instead of using our own naming.
442 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
444 $boxesall[$g]['flags'] = explode(' ', $flags);
452 * Sorting function used to sort mailbox names.
453 * + Original patch from dave_michmerhuizen@yahoo.com
454 * + Allows case insensitivity when sorting folders
455 * + Takes care of the delimiter being sorted to the end, causing
456 * subfolders to be listed in below folders that are prefixed
457 * with their parent folders name.
459 * For example: INBOX.foo, INBOX.foobar, and INBOX.foo.bar
460 * Without special sort function: foobar between foo and foo.bar
461 * With special sort function: foobar AFTER foo and foo.bar :)
463 function user_strcasecmp($a, $b) {
464 return strnatcasecmp($a, $b);
468 * Returns list of options (to be echoed into select statement
469 * based on available mailboxes and separators
470 * Caller should surround options with <SELECT..> </SELECT> and
472 * $imap_stream - $imapConnection to query for mailboxes
473 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
474 * $folder_skip - array of folders to keep out of option list (compared in lower)
475 * $boxes - list of already fetched boxes (for places like folder panel, where
476 * you know these options will be shown 3 times in a row.. (most often unset).
477 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
478 * 'noselect' by default to remove unselectable mailboxes.
479 * 'noinferiors' used to filter out folders that can not contain subfolders.
480 * NULL to avoid flag check entirely.
481 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
482 * \NoSelect and \NoInferiors
483 * $use_long_format - override folder display preference and always show full folder name.
485 function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
486 $flag = 'noselect', $use_long_format = false ) {
487 global $username, $data_dir;
489 if ( $use_long_format ) {
490 $shorten_box_names = 0;
492 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF
);
496 $boxes = sqimap_mailbox_list($imap_stream);
499 foreach ($boxes as $boxes_part) {
500 if ($flag == NULL ||
!in_array($flag, $boxes_part['flags'])) {
501 $box = $boxes_part['unformatted'];
503 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
506 $lowerbox = strtolower($box);
507 // mailboxes are casesensitive => inbox.sent != inbox.Sent
508 // nevermind, to many dependencies this should be fixed!
510 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
513 switch ($shorten_box_names)
515 case 2: /* delimited, style = 2 */
516 $box2 = str_replace(' ', '. ', $boxes_part['formatted']);
518 case 1: /* indent, style = 1 */
519 $box2 = $boxes_part['formatted'];
521 default: /* default, long names, style = 0 */
522 $box2 = str_replace(' ', ' ', imap_utf7_decode_local($boxes_part['unformatted-disp']));
526 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
527 $mbox_options .= '<OPTION VALUE="'.$box.'" SELECTED>'.$box2.'</OPTION>' . "\n";
529 $mbox_options .= '<OPTION VALUE="'.$box.'">'.$box2.'</OPTION>' . "\n";
533 return $mbox_options;
537 * Returns sorted mailbox lists in several different ways.
538 * See comment on sqimap_mailbox_parse() for info about the returned array.
540 function sqimap_mailbox_list($imap_stream) {
541 global $default_folder_prefix;
543 if (!isset($boxesnew)) {
544 global $data_dir, $username, $list_special_folders_first,
545 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
546 $move_to_trash, $move_to_sent, $save_as_draft,
547 $delimiter, $noselect_fix_enable;
549 $inbox_in_list = false;
550 $inbox_subscribed = false;
552 require_once(SM_PATH
. 'include/load_prefs.php');
554 if ($noselect_fix_enable) {
555 $lsub_args = "LSUB \"$folder_prefix\" \"*%\"";
557 $lsub_args = "LSUB \"$folder_prefix\" \"*\"";
560 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
561 true, $response, $message);
562 $lsub_ary = compact_mailboxes_response($lsub_ary);
564 $sorted_lsub_ary = array();
565 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++
) {
566 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
567 $sorted_lsub_ary[] = $temp_mailbox_name;
568 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
569 $inbox_subscribed = true;
573 /* natural sort mailboxes */
574 if (isset($sorted_lsub_ary)) {
575 usort($sorted_lsub_ary, 'user_strcasecmp');
578 * The LSUB response doesn't provide us information about \Noselect
579 * mail boxes. The LIST response does, that's why we need to do a LIST
580 * call to retrieve the flags for the mailbox
581 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
582 * in other words, we cannot rely on it.
584 $sorted_list_ary = array();
585 for ($i=0; $i < count($sorted_lsub_ary); $i++
) {
586 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
587 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
590 $mbx = $sorted_lsub_ary[$i];
592 $mbx = stripslashes($mbx);
593 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . encode_mailbox_name($mbx),
594 true, $response, $message);
595 $read = compact_mailboxes_response($read);
596 if (isset($read[0])) {
597 $sorted_list_ary[$i] = $read[0];
599 $sorted_list_ary[$i] = '';
603 * Just in case they're not subscribed to their inbox,
604 * we'll get it for them anyway
606 if (!$inbox_subscribed) {
607 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" ' . encode_mailbox_name('INBOX'),
608 true, $response, $message);
609 $sorted_list_ary[] = implode('', compact_mailboxes_response($inbox_ary));
610 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
613 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
615 /* Now, lets sort for special folders */
616 $boxesnew = $used = array();
619 $cnt = count($boxesall);
620 $used = array_pad($used,$cnt,false);
621 for($k = 0; $k < $cnt; ++
$k) {
622 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
623 $boxesnew[] = $boxesall[$k];
628 /* List special folders and their subfolders, if requested. */
629 if ($list_special_folders_first) {
630 for($k = 0; $k < $cnt; ++
$k) {
631 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
632 $boxesnew[] = $boxesall[$k];
638 /* Rest of the folders */
639 for($k = 0; $k < $cnt; $k++
) {
641 $boxesnew[] = $boxesall[$k];
649 * Returns a list of all folders, subscribed or not
651 function sqimap_mailbox_list_all($imap_stream) {
652 global $list_special_folders_first, $folder_prefix, $delimiter;
654 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
655 $read_ary = compact_mailboxes_response($read_ary);
659 $fld_pre_length = strlen($folder_prefix);
660 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++
) {
661 /* Store the raw IMAP reply */
662 $boxes[$g]['raw'] = $read_ary[$i];
664 /* Count number of delimiters ($delimiter) in folder name */
665 $mailbox = find_mailbox_name($read_ary[$i]);
666 $dm_count = substr_count($mailbox, $delimiter);
667 if (substr($mailbox, -1) == $delimiter) {
668 /* If name ends in delimiter - decrement count by one */
672 /* Format folder name, but only if it's a INBOX.* or has a parent. */
673 $boxesallbyname[$mailbox] = $g;
674 $parentfolder = readMailboxParent($mailbox, $delimiter);
675 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
676 (ereg('^'.$folder_prefix, $mailbox)) ||
677 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
679 $boxes[$g]['formatted'] = str_repeat(' ', $dm_count);
681 $boxes[$g]['formatted'] = '';
683 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
685 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
688 $boxes[$g]['unformatted-dm'] = $mailbox;
689 if (substr($mailbox, -1) == $delimiter) {
690 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
692 $boxes[$g]['unformatted'] = $mailbox;
693 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
695 $boxes[$g]['id'] = $g;
697 /* Now lets get the flags for this mailbox */
698 $read_mlbx = $read_ary[$i];
699 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+
1);
700 $flags = substr($flags, 0, strpos($flags, ')'));
701 $flags = str_replace('\\', '', $flags);
702 $flags = trim(strtolower($flags));
704 $boxes[$g]['flags'] = explode(' ', $flags);
706 $boxes[$g]['flags'] = array();
710 if(is_array($boxes)) {
717 function sqimap_mailbox_tree($imap_stream) {
718 global $boxesnew, $default_folder_prefix, $unseen_notify, $unseen_type;
719 if (!isset($boxesnew)) {
721 global $data_dir, $username, $list_special_folders_first,
722 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
726 $inbox_in_list = false;
727 $inbox_subscribed = false;
730 require_once(SM_PATH
. 'include/load_prefs.php');
733 $lsub_ary = sqimap_run_command ($imap_stream, "LSUB \"$folder_prefix\" \"*\"",
734 true, $response, $message);
735 $lsub_ary = compact_mailboxes_response($lsub_ary);
737 /* Check to see if we have an INBOX */
740 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++
) {
741 if (preg_match("/^\*\s+LSUB\s+(.*)\"?INBOX\"?[^(\/\.)].*$/",$lsub_ary[$i])) {
747 if ($has_inbox == false) {
748 $lsub_ary[] = '* LSUB () "' . $folder_prefix . '" INBOX';
752 * Section about removing the last element was removed
753 * We don't return "* OK" anymore from sqimap_read_data
756 $sorted_lsub_ary = array();
757 $cnt = count($lsub_ary);
758 for ($i = 0; $i < $cnt; $i++
) {
759 $mbx = find_mailbox_name($lsub_ary[$i]);
761 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
762 if ($imap_server_type != "uw") {
763 $noselect = check_is_noselect($lsub_ary[$i]);
765 if (substr($mbx, -1) == $delimiter) {
766 $mbx = substr($mbx, 0, strlen($mbx) - 1);
768 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect);
770 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
771 if ($imap_server_type == "uw") {
774 // prepare an array with queries
775 foreach ($sorted_lsub_ary as $aMbx) {
776 $mbx = stripslashes($aMbx['mbx']);
777 sqimap_prepare_pipelined_query('LIST "" ' . encode_mailbox_name($mbx), $tag, $aQuery, false);
780 $sorted_lsub_ary = array();
781 // execute all the queries at once
782 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
783 foreach($aTag as $tag => $mbx) {
784 if ($aServerResponse[$tag] == 'OK') {
785 $sResponse = implode('', $aResponse[$tag]);
786 $noselect = check_is_noselect($sResponse);
787 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect);
790 $cnt = count($sorted_lsub_ary);
792 $sorted_lsub_ary = array_values($sorted_lsub_ary);
793 array_multisort($sorted_lsub_ary, SORT_ASC
, SORT_REGULAR
);
794 $boxesnew = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
799 function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
800 global $data_dir, $username, $list_special_folders_first,
801 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
802 $move_to_trash, $move_to_sent, $save_as_draft,
803 $delimiter, $imap_server_type;
805 $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
807 /* create virtual root node */
808 $mailboxes= new mailboxes();
809 $mailboxes->is_root
= true;
814 if (isset($folder_prefix) && ($folder_prefix != '')) {
815 $start = substr_count($folder_prefix,$delimiter);
816 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
818 $mailboxes->mailboxname_full
= substr($folder_prefix,0, (strlen($folder_prefix)-1));
820 $mailboxes->mailboxname_full
= $folder_prefix;
823 $mailboxes->mailboxname_sub
= $mailboxes->mailboxname_full
;
828 $cnt = count($mbx_ary);
829 for ($i=0; $i < $cnt; $i++
) {
830 if ($mbx_ary[$i]['mbx'] !='' ) {
831 $mbx = new mailboxes();
832 $mailbox = $mbx_ary[$i]['mbx'];
835 sent subfolders messes up using existing code as subfolders
836 were marked, but the parents were ordered somewhere else in
837 the list, despite having "special folders at top" option set.
838 Need a better method than this.
841 if ($mailbox == 'INBOX') {
842 $mbx->is_special
= true;
843 } elseif (stristr($trash_folder , $mailbox)) {
844 $mbx->is_special
= true;
845 } elseif (stristr($sent_folder , $mailbox)) {
846 $mbx->is_special
= true;
847 } elseif (stristr($draft_folder , $mailbox)) {
848 $mbx->is_special
= true;
853 $mbx->is_inbox
= true;
854 $mbx->is_special
= true;
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;
871 if ($mailbox == 'INBOX') {
872 $mbx->is_inbox
= true;
873 $mbx->is_special
= true;
874 } elseif (stristr($trash_folder , $mailbox)) {
875 $mbx->is_trash
= true;
876 $mbx->is_special
= true;
877 } elseif (stristr($sent_folder , $mailbox)) {
878 $mbx->is_sent
= true;
879 $mbx->is_special
= true;
880 } elseif (stristr($draft_folder , $mailbox)) {
881 $mbx->is_draft
= true;
882 $mbx->is_special
= true;
886 if (isset($mbx_ary[$i]['unseen'])) {
887 $mbx->unseen
= $mbx_ary[$i]['unseen'];
889 if (isset($mbx_ary[$i]['nummessages'])) {
890 $mbx->total
= $mbx_ary[$i]['nummessages'];
893 $mbx->is_noselect
= $mbx_ary[$i]['noselect'];
895 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
897 $mbx->mailboxname_sub
= substr($mbx_ary[$i]['mbx'],$r_del_pos+
1);
898 } else { /* mailbox is root folder */
899 $mbx->mailboxname_sub
= $mbx_ary[$i]['mbx'];
901 $mbx->mailboxname_full
= $mbx_ary[$i]['mbx'];
903 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
906 sqimap_utf7_decode_mbx_tree($mailboxes);
907 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
911 function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
912 if (strtoupper($mbx_tree->mailboxname_sub
) == 'INBOX')
913 $mbx_tree->mailboxname_sub
= _("INBOX");
915 $mbx_tree->mailboxname_sub
= imap_utf7_decode_local($mbx_tree->mailboxname_sub
);
916 if ($mbx_tree->mbxs
) {
917 $iCnt = count($mbx_tree->mbxs
);
918 for ($i=0;$i<$iCnt;++
$i) {
919 $mbxs_tree->mbxs
[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs
[$i]);
925 function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
927 $aMbxs[] =& $mbx_tree;
928 if ($mbx_tree->mbxs
) {
929 $iCnt = count($mbx_tree->mbxs
);
930 for ($i=0;$i<$iCnt;++
$i) {
931 sqimap_tree_to_ref_array($mbx_tree->mbxs
[$i],$aMbxs);
937 /* Define preferences for folder settings. */
938 /* FIXME, we should load constants.php
940 define('SMPREF_UNSEEN_NONE', 1);
941 define('SMPREF_UNSEEN_INBOX', 2);
942 define('SMPREF_UNSEEN_ALL', 3);
944 define('SMPREF_UNSEEN_SPECIAL', 4); // Only special folders
945 define('SMPREF_UNSEEN_NORMAL', 5); // Only normal folders
948 define('SMPREF_UNSEEN_ONLY', 1);
949 define('SMPREF_UNSEEN_TOTAL', 2);
952 function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
953 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
954 $aMbxs = $aQuery = $aTag = array();
955 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
956 // remove the root node
959 if($unseen_notify == 3) {
960 $cnt = count($aMbxs);
961 for($i=0;$i<$cnt;++
$i) {
963 if (!$oMbx->is_noselect
) {
964 $mbx = $oMbx->mailboxname_full
;
965 if ($unseen_type == 2 ||
966 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
967 $query = 'STATUS ' . encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
969 $query = 'STATUS ' . encode_mailbox_name($mbx) . ' (UNSEEN)';
971 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
973 $oMbx->unseen
= $oMbx->total
= false;
979 // execute all the queries at once
980 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
981 $cnt = count($aMbxs);
982 for($i=0;$i<$cnt;++
$i) {
985 if ($tag && $aServerResponse[$tag] == 'OK') {
986 $sResponse = implode('', $aResponse[$tag]);
987 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
988 $oMbx->unseen
= $regs[1];
990 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
991 $oMbx->total
= $regs[1];
996 } else if ($unseen_notify == 2) { // INBOX only
997 $cnt = count($aMbxs);
998 for($i=0;$i<$cnt;++
$i) {
1000 if (strtoupper($oMbx->mailboxname_full
) == 'INBOX' ||
1001 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
1002 if ($unseen_type == 2 ||
1003 ($oMbx->mailboxname_full
== $trash_folder && $move_to_trash)) {
1004 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full
);
1005 $oMbx->unseen
= $aStatus['UNSEEN'];
1006 $oMbx->total
= $aStatus['MESSAGES'];
1008 $oMbx->unseen
= sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full
);
1010 $aMbxs[$i] =& $oMbx;
1011 if (!$move_to_trash && $trash_folder) {
1014 // trash comes after INBOX
1015 if ($oMbx->mailboxname_full
== $trash_folder) {