d0369fd38798fcac0866b7eaee4e5dad2d99908c
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 compact_mailboxes_response($ary)
113 * Workaround for mailboxes returned as literal
114 * FIXME : Doesn't work if the mailbox name is multiple lines
115 * (larger then fgets buffer)
117 for ($i = 0; $i < count($ary); $i++
) {
118 if (isset($ary[$i +
1]) && substr($ary[$i], -3) == "}\r\n") {
119 if (ereg("^(\\* [A-Z]+.*)\\{[0-9]+\\}([ \n\r\t]*)$",
121 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+
1])) . '"' . $regs[2];
122 array_splice($ary, $i+
1, 2);
126 /* remove duplicates and ensure array is contiguous */
127 return array_values(array_unique($ary));
130 function check_is_noselect ($lsub_line) {
131 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i", $lsub_line);
135 * If $haystack is a full mailbox name, and $needle is the mailbox
136 * separator character, returns the second last part of the full
137 * mailbox name (i.e. the mailbox's parent mailbox)
139 function readMailboxParent($haystack, $needle) {
143 $parts = explode($needle, $haystack);
144 $elem = array_pop($parts);
145 while ($elem == '' && count($parts)) {
146 $elem = array_pop($parts);
148 $ret = join($needle, $parts);
154 * Check if $subbox is below the specified $parentbox
156 function isBoxBelow( $subbox, $parentbox ) {
159 * Eliminate the obvious mismatch, where the
160 * subfolder path is shorter than that of the potential parent
162 if ( strlen($subbox) < strlen($parentbox) ) {
165 /* check for delimiter */
166 if (!substr($parentbox,-1) == $delimiter) {
167 $parentbox.=$delimiter;
169 if (substr($subbox,0,strlen($parentbox)) == $parentbox) {
176 /* Defines special mailboxes */
177 function isSpecialMailbox( $box ) {
178 $ret = ( (strtolower($box) == 'inbox') ||
179 isTrashMailbox($box) ||
isSentMailbox($box) ||
isDraftMailbox($box) );
182 $ret = do_hook_function( 'special_mailbox', $box );
187 function isTrashMailbox ($box) {
188 global $trash_folder, $move_to_trash;
189 return $move_to_trash && $trash_folder &&
190 ( $box == $trash_folder ||
isBoxBelow($box, $trash_folder) );
193 function isSentMailbox($box) {
194 global $sent_folder, $move_to_sent;
195 return $move_to_sent && $sent_folder &&
196 ( $box == $sent_folder ||
isBoxBelow($box, $sent_folder) );
199 function isDraftMailbox($box) {
200 global $draft_folder, $save_as_draft;
201 return $save_as_draft &&
202 ( $box == $draft_folder ||
isBoxBelow($box, $draft_folder) );
205 /* Expunges a mailbox */
206 function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
210 $id = sqimap_message_list_squisher($id);
217 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
218 $response, $message, $uid);
221 if (is_array($read)) {
222 foreach ($read as $r) {
223 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
231 /* Checks whether or not the specified mailbox exists */
232 function sqimap_mailbox_exists ($imap_stream, $mailbox) {
233 if (!isset($mailbox) ||
empty($mailbox)) {
236 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mailbox),
237 true, $response, $message);
238 return isset($mbx[0]);
241 /* Selects a mailbox */
242 function sqimap_mailbox_select ($imap_stream, $mailbox) {
243 global $auto_expunge;
245 if ($mailbox == 'None') {
249 $read = sqimap_run_command($imap_stream, 'SELECT ' . sqimap_encode_mailbox_name($mailbox),
250 true, $response, $message);
252 for ($i = 0, $cnt = count($read); $i < $cnt; $i++
) {
253 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
254 $result[strtoupper($regs[1])] = $regs[2];
255 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
256 $result[strtoupper($regs[2])] = $regs[1];
258 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
259 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
260 $result['PERMANENTFLAGS'] = $regs[1];
261 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
262 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
263 $result['FLAGS'] = $regs[1];
267 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
268 $result['RIGHTS']=$regs[1];
272 $tmp = sqimap_run_command($imap_stream, 'EXPUNGE', false, $a, $b);
277 /* Creates a folder */
278 function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
280 if (strtolower($type) == 'noselect') {
281 $mailbox .= $delimiter;
284 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' . sqimap_encode_mailbox_name($mailbox),
285 true, $response, $message);
286 sqimap_subscribe ($imap_stream, $mailbox);
289 /* Subscribes to an existing folder */
290 function sqimap_subscribe ($imap_stream, $mailbox) {
291 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' . sqimap_encode_mailbox_name($mailbox),
292 true, $response, $message);
295 /* Unsubscribes to an existing folder */
296 function sqimap_unsubscribe ($imap_stream, $mailbox) {
297 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' . sqimap_encode_mailbox_name($mailbox),
298 true, $response, $message);
301 /* Deletes the given folder */
302 function sqimap_mailbox_delete ($imap_stream, $mailbox) {
303 global $data_dir, $username;
304 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' . sqimap_encode_mailbox_name($mailbox),
305 true, $response, $message);
306 sqimap_unsubscribe ($imap_stream, $mailbox);
307 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
308 removePref($data_dir, $username, "thread_$mailbox");
311 /* Determines if the user is subscribed to the folder or not */
312 function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
313 $boxesall = sqimap_mailbox_list ($imap_stream);
314 foreach ($boxesall as $ref) {
315 if ($ref['unformatted'] == $folder) {
322 /* Renames a mailbox */
323 function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
324 if ( $old_name != $new_name ) {
325 global $delimiter, $imap_server_type, $data_dir, $username;
326 if ( substr( $old_name, -1 ) == $delimiter ) {
327 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
328 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
329 $postfix = $delimiter;
334 $boxesall = sqimap_mailbox_list($imap_stream);
335 $cmd = 'RENAME ' . sqimap_encode_mailbox_name($old_name) . ' ' . sqimap_encode_mailbox_name($new_name);
336 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
337 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
338 $oldpref = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
339 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
340 sqimap_subscribe($imap_stream, $new_name.$postfix);
341 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref);
342 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
343 $l = strlen( $old_name ) +
1;
346 foreach ($boxesall as $box) {
347 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
348 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
349 if ($imap_server_type == 'cyrus') {
350 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
351 $data = sqimap_run_command($imap_stream, $cmd, true,
352 $response, $message);
354 sqimap_unsubscribe($imap_stream, $box[$p]);
355 $oldpref = getPref($data_dir, $username, 'thread_'.$box[$p]);
356 removePref($data_dir, $username, 'thread_'.$box[$p]);
357 sqimap_subscribe($imap_stream, $new_sub);
358 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref);
359 do_hook_function('rename_or_delete_folder',
360 $args = array($box[$p], 'rename', $new_sub));
367 * Formats a mailbox into 4 parts for the $boxesall array
369 * The four parts are:
371 * raw - Raw LIST/LSUB response from the IMAP server
372 * formatted - nicely formatted folder name
373 * unformatted - unformatted, but with delimiter at end removed
374 * unformatted-dm - folder name as it appears in raw response
375 * unformatted-disp - unformatted without $folder_prefix
377 function sqimap_mailbox_parse ($line, $line_lsub) {
378 global $folder_prefix, $delimiter;
380 /* Process each folder line */
381 for ($g = 0, $cnt = count($line); $g < $cnt; ++
$g) {
382 /* Store the raw IMAP reply */
383 if (isset($line[$g])) {
384 $boxesall[$g]['raw'] = $line[$g];
386 $boxesall[$g]['raw'] = '';
389 /* Count number of delimiters ($delimiter) in folder name */
390 $mailbox = trim($line_lsub[$g]);
391 $dm_count = substr_count($mailbox, $delimiter);
392 if (substr($mailbox, -1) == $delimiter) {
393 /* If name ends in delimiter, decrement count by one */
397 /* Format folder name, but only if it's a INBOX.* or has a parent. */
398 $boxesallbyname[$mailbox] = $g;
399 $parentfolder = readMailboxParent($mailbox, $delimiter);
400 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
401 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
402 (isset($boxesallbyname[$parentfolder]) &&
403 (strlen($parentfolder) > 0) ) ) {
404 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
406 $boxesall[$g]['formatted'] = str_repeat(' ', $indent);
408 $boxesall[$g]['formatted'] = '';
410 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
412 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
415 $boxesall[$g]['unformatted-dm'] = $mailbox;
416 if (substr($mailbox, -1) == $delimiter) {
417 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
419 $boxesall[$g]['unformatted'] = $mailbox;
420 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
421 $mailbox = substr($mailbox, strlen($folder_prefix));
423 $boxesall[$g]['unformatted-disp'] = $mailbox;
424 $boxesall[$g]['id'] = $g;
426 $boxesall[$g]['flags'] = array();
427 if (isset($line[$g])) {
428 ereg("\(([^)]*)\)",$line[$g],$regs);
429 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
430 // and $MDNSent <= last one doesn't have the \
431 // It's better to follow RFC3501 instead of using our own naming.
432 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
434 $boxesall[$g]['flags'] = explode(' ', $flags);
442 * Sorting function used to sort mailbox names.
443 * + Original patch from dave_michmerhuizen@yahoo.com
444 * + Allows case insensitivity when sorting folders
445 * + Takes care of the delimiter being sorted to the end, causing
446 * subfolders to be listed in below folders that are prefixed
447 * with their parent folders name.
449 * For example: INBOX.foo, INBOX.foobar, and INBOX.foo.bar
450 * Without special sort function: foobar between foo and foo.bar
451 * With special sort function: foobar AFTER foo and foo.bar :)
453 function user_strcasecmp($a, $b) {
454 return strnatcasecmp($a, $b);
458 * Returns list of options (to be echoed into select statement
459 * based on available mailboxes and separators
460 * Caller should surround options with <SELECT..> </SELECT> and
462 * $imap_stream - $imapConnection to query for mailboxes
463 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
464 * $folder_skip - array of folders to keep out of option list (compared in lower)
465 * $boxes - list of already fetched boxes (for places like folder panel, where
466 * you know these options will be shown 3 times in a row.. (most often unset).
467 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
468 * 'noselect' by default to remove unselectable mailboxes.
469 * 'noinferiors' used to filter out folders that can not contain subfolders.
470 * NULL to avoid flag check entirely.
471 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
472 * \NoSelect and \NoInferiors
473 * $use_long_format - override folder display preference and always show full folder name.
475 function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
476 $flag = 'noselect', $use_long_format = false ) {
477 global $username, $data_dir;
479 if ( $use_long_format ) {
480 $shorten_box_names = 0;
482 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF
);
486 $boxes = sqimap_mailbox_list($imap_stream);
489 foreach ($boxes as $boxes_part) {
490 if ($flag == NULL ||
!in_array($flag, $boxes_part['flags'])) {
491 $box = $boxes_part['unformatted'];
493 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
496 $lowerbox = strtolower($box);
497 // mailboxes are casesensitive => inbox.sent != inbox.Sent
498 // nevermind, to many dependencies this should be fixed!
500 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
503 switch ($shorten_box_names)
505 case 2: /* delimited, style = 2 */
506 $box2 = str_replace(' ', '. ', $boxes_part['formatted']);
508 case 1: /* indent, style = 1 */
509 $box2 = $boxes_part['formatted'];
511 default: /* default, long names, style = 0 */
512 $box2 = str_replace(' ', ' ', imap_utf7_decode_local($boxes_part['unformatted-disp']));
516 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
517 $mbox_options .= '<OPTION VALUE="'.$box.'" SELECTED>'.$box2.'</OPTION>' . "\n";
519 $mbox_options .= '<OPTION VALUE="'.$box.'">'.$box2.'</OPTION>' . "\n";
523 return $mbox_options;
527 * Returns sorted mailbox lists in several different ways.
528 * See comment on sqimap_mailbox_parse() for info about the returned array.
530 function sqimap_mailbox_list($imap_stream) {
531 global $default_folder_prefix;
533 if (!isset($boxesnew)) {
534 global $data_dir, $username, $list_special_folders_first,
535 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
536 $move_to_trash, $move_to_sent, $save_as_draft,
537 $delimiter, $noselect_fix_enable;
539 $inbox_in_list = false;
540 $inbox_subscribed = false;
542 require_once(SM_PATH
. 'include/load_prefs.php');
544 if ($noselect_fix_enable) {
545 $lsub_args = "LSUB \"$folder_prefix\" \"*%\"";
547 $lsub_args = "LSUB \"$folder_prefix\" \"*\"";
550 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
551 true, $response, $message);
552 $lsub_ary = compact_mailboxes_response($lsub_ary);
554 $sorted_lsub_ary = array();
555 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++
) {
556 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
557 $sorted_lsub_ary[] = $temp_mailbox_name;
558 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
559 $inbox_subscribed = true;
563 /* natural sort mailboxes */
564 if (isset($sorted_lsub_ary)) {
565 usort($sorted_lsub_ary, 'user_strcasecmp');
568 * The LSUB response doesn't provide us information about \Noselect
569 * mail boxes. The LIST response does, that's why we need to do a LIST
570 * call to retrieve the flags for the mailbox
571 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
572 * in other words, we cannot rely on it.
574 $sorted_list_ary = array();
575 for ($i=0; $i < count($sorted_lsub_ary); $i++
) {
576 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
577 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
580 $mbx = $sorted_lsub_ary[$i];
582 $mbx = stripslashes($mbx);
583 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mbx),
584 true, $response, $message);
585 $read = compact_mailboxes_response($read);
586 if (isset($read[0])) {
587 $sorted_list_ary[$i] = $read[0];
589 $sorted_list_ary[$i] = '';
593 * Just in case they're not subscribed to their inbox,
594 * we'll get it for them anyway
596 if (!$inbox_subscribed) {
597 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" INBOX',
598 true, $response, $message);
599 $sorted_list_ary[] = implode('', compact_mailboxes_response($inbox_ary));
600 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
603 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
605 /* Now, lets sort for special folders */
606 $boxesnew = $used = array();
609 $cnt = count($boxesall);
610 $used = array_pad($used,$cnt,false);
611 for($k = 0; $k < $cnt; ++
$k) {
612 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
613 $boxesnew[] = $boxesall[$k];
618 /* List special folders and their subfolders, if requested. */
619 if ($list_special_folders_first) {
620 for($k = 0; $k < $cnt; ++
$k) {
621 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
622 $boxesnew[] = $boxesall[$k];
628 /* Rest of the folders */
629 for($k = 0; $k < $cnt; $k++
) {
631 $boxesnew[] = $boxesall[$k];
639 * Returns a list of all folders, subscribed or not
641 function sqimap_mailbox_list_all($imap_stream) {
642 global $list_special_folders_first, $folder_prefix, $delimiter;
644 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
645 $read_ary = compact_mailboxes_response($read_ary);
649 $fld_pre_length = strlen($folder_prefix);
650 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++
) {
651 /* Store the raw IMAP reply */
652 $boxes[$g]['raw'] = $read_ary[$i];
654 /* Count number of delimiters ($delimiter) in folder name */
655 $mailbox = find_mailbox_name($read_ary[$i]);
656 $dm_count = substr_count($mailbox, $delimiter);
657 if (substr($mailbox, -1) == $delimiter) {
658 /* If name ends in delimiter - decrement count by one */
662 /* Format folder name, but only if it's a INBOX.* or has a parent. */
663 $boxesallbyname[$mailbox] = $g;
664 $parentfolder = readMailboxParent($mailbox, $delimiter);
665 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
666 (ereg('^'.$folder_prefix, $mailbox)) ||
667 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
669 $boxes[$g]['formatted'] = str_repeat(' ', $dm_count);
671 $boxes[$g]['formatted'] = '';
673 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
675 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
678 $boxes[$g]['unformatted-dm'] = $mailbox;
679 if (substr($mailbox, -1) == $delimiter) {
680 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
682 $boxes[$g]['unformatted'] = $mailbox;
683 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
685 $boxes[$g]['id'] = $g;
687 /* Now lets get the flags for this mailbox */
688 $read_mlbx = $read_ary[$i];
689 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+
1);
690 $flags = substr($flags, 0, strpos($flags, ')'));
691 $flags = str_replace('\\', '', $flags);
692 $flags = trim(strtolower($flags));
694 $boxes[$g]['flags'] = explode(' ', $flags);
696 $boxes[$g]['flags'] = array();
700 if(is_array($boxes)) {
707 function sqimap_mailbox_tree($imap_stream) {
708 global $boxesnew, $default_folder_prefix, $unseen_notify, $unseen_type;
709 if (!isset($boxesnew)) {
711 global $data_dir, $username, $list_special_folders_first,
712 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
716 $inbox_in_list = false;
717 $inbox_subscribed = false;
720 require_once(SM_PATH
. 'include/load_prefs.php');
723 $lsub_ary = sqimap_run_command ($imap_stream, "LSUB \"$folder_prefix\" \"*\"",
724 true, $response, $message);
725 $lsub_ary = compact_mailboxes_response($lsub_ary);
727 /* Check to see if we have an INBOX */
730 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++
) {
731 if (preg_match("/^\*\s+LSUB\s+(.*)\"?INBOX\"?[^(\/\.)].*$/",$lsub_ary[$i])) {
737 if ($has_inbox == false) {
738 /* $lsub_ary[] = '* LSUB () "' . $folder_prefix . '" INBOX';*/
739 $lsub_ary[] = '* LSUB () NIL INBOX';
743 * Section about removing the last element was removed
744 * We don't return "* OK" anymore from sqimap_read_data
747 $sorted_lsub_ary = array();
748 $cnt = count($lsub_ary);
749 for ($i = 0; $i < $cnt; $i++
) {
750 $mbx = find_mailbox_name($lsub_ary[$i]);
752 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
753 if ($imap_server_type != "uw") {
754 $noselect = check_is_noselect($lsub_ary[$i]);
756 if (substr($mbx, -1) == $delimiter) {
757 $mbx = substr($mbx, 0, strlen($mbx) - 1);
759 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect);
761 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
762 if ($imap_server_type == "uw") {
765 // prepare an array with queries
766 foreach ($sorted_lsub_ary as $aMbx) {
767 $mbx = stripslashes($aMbx['mbx']);
768 sqimap_prepare_pipelined_query('LIST "" ' . sqimap_encode_mailbox_name($mbx), $tag, $aQuery, false);
771 $sorted_lsub_ary = array();
772 // execute all the queries at once
773 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
774 foreach($aTag as $tag => $mbx) {
775 if ($aServerResponse[$tag] == 'OK') {
776 $sResponse = implode('', $aResponse[$tag]);
777 $noselect = check_is_noselect($sResponse);
778 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect);
781 $cnt = count($sorted_lsub_ary);
783 $sorted_lsub_ary = array_values($sorted_lsub_ary);
784 array_multisort($sorted_lsub_ary, SORT_ASC
, SORT_REGULAR
);
785 $boxesnew = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
790 function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
791 global $data_dir, $username, $list_special_folders_first,
792 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
793 $move_to_trash, $move_to_sent, $save_as_draft,
794 $delimiter, $imap_server_type;
796 $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
798 /* create virtual root node */
799 $mailboxes= new mailboxes();
800 $mailboxes->is_root
= true;
805 if (isset($folder_prefix) && ($folder_prefix != '')) {
806 $start = substr_count($folder_prefix,$delimiter);
807 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
809 $mailboxes->mailboxname_full
= substr($folder_prefix,0, (strlen($folder_prefix)-1));
811 $mailboxes->mailboxname_full
= $folder_prefix;
814 $mailboxes->mailboxname_sub
= $mailboxes->mailboxname_full
;
819 $cnt = count($mbx_ary);
820 for ($i=0; $i < $cnt; $i++
) {
821 if ($mbx_ary[$i]['mbx'] !='' ) {
822 $mbx = new mailboxes();
823 $mailbox = $mbx_ary[$i]['mbx'];
826 sent subfolders messes up using existing code as subfolders
827 were marked, but the parents were ordered somewhere else in
828 the list, despite having "special folders at top" option set.
829 Need a better method than this.
832 if ($mailbox == 'INBOX') {
833 $mbx->is_special
= true;
834 } elseif (stristr($trash_folder , $mailbox)) {
835 $mbx->is_special
= true;
836 } elseif (stristr($sent_folder , $mailbox)) {
837 $mbx->is_special
= true;
838 } elseif (stristr($draft_folder , $mailbox)) {
839 $mbx->is_special
= true;
844 $mbx->is_inbox
= true;
845 $mbx->is_special
= true;
848 $mbx->is_trash
= true;
849 $mbx->is_special
= true;
852 $mbx->is_sent
= true;
853 $mbx->is_special
= true;
856 $mbx->is_draft
= true;
857 $mbx->is_special
= true;
862 if ($mailbox == 'INBOX') {
863 $mbx->is_inbox
= true;
864 $mbx->is_special
= true;
865 } elseif (stristr($trash_folder , $mailbox)) {
866 $mbx->is_trash
= true;
867 $mbx->is_special
= true;
868 } elseif (stristr($sent_folder , $mailbox)) {
869 $mbx->is_sent
= true;
870 $mbx->is_special
= true;
871 } elseif (stristr($draft_folder , $mailbox)) {
872 $mbx->is_draft
= true;
873 $mbx->is_special
= true;
877 if (isset($mbx_ary[$i]['unseen'])) {
878 $mbx->unseen
= $mbx_ary[$i]['unseen'];
880 if (isset($mbx_ary[$i]['nummessages'])) {
881 $mbx->total
= $mbx_ary[$i]['nummessages'];
884 $mbx->is_noselect
= $mbx_ary[$i]['noselect'];
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) {