5 * Copyright (c) 1999-2004 The SquirrelMail Project Team
6 * Licensed under the GNU GPL. For full terms see the file COPYING.
8 * This impliments all functions that manipulate mailboxes
11 * @package squirrelmail
16 require_once(SM_PATH
. 'functions/imap_utf7_local.php');
23 * FIXME. This class should be extracted and placed in a separate file that
24 * can be included before we start the session. That makes caching of the tree
25 * possible. On a refresh mailboxes from left_main.php the only function that
26 * should be called is the sqimap_get_status_mbx_tree. In case of subscribe
27 * / rename / delete / new we have to create methods for adding/changing the
28 * mailbox in the mbx_tree without the need for a refresh.
29 * @package squirrelmail
33 var $mailboxname_full = '', $mailboxname_sub= '', $is_noselect = false, $is_noinferiors = false,
34 $is_special = false, $is_root = false, $is_inbox = false, $is_sent = false,
35 $is_trash = false, $is_draft = false, $mbxs = array(),
36 $unseen = false, $total = false;
38 function addMbx($mbx, $delimiter, $start, $specialfirst) {
39 $ary = explode($delimiter, $mbx->mailboxname_full
);
41 for ($i = $start, $c = count($ary)-1; $i < $c; $i++
) {
42 $mbx_childs =& $mbx_parent->mbxs
;
45 foreach ($mbx_childs as $key => $parent) {
46 if ($parent->mailboxname_sub
== $ary[$i]) {
47 $mbx_parent =& $mbx_parent->mbxs
[$key];
54 $no_select_mbx = new mailboxes();
55 if (isset($mbx_parent->mailboxname_full
) && $mbx_parent->mailboxname_full
!= '') {
56 $no_select_mbx->mailboxname_full
= $mbx_parent->mailboxname_full
.$delimiter.$ary[$i];
58 $no_select_mbx->mailboxname_full
= $ary[$i];
60 $no_select_mbx->mailboxname_sub
= $ary[$i];
61 $no_select_mbx->is_noselect
= true;
62 $mbx_parent->mbxs
[] = $no_select_mbx;
66 $mbx_parent->mbxs
[] = $mbx;
67 if ($mbx->is_special
&& $specialfirst) {
68 usort($mbx_parent->mbxs
, 'sortSpecialMbx');
73 function sortSpecialMbx($a, $b) {
75 $acmp = '0'. $a->mailboxname_full
;
76 } else if ($a->is_special
) {
77 $acmp = '1'. $a->mailboxname_full
;
79 $acmp = '2' . $a->mailboxname_full
;
82 $bcmp = '0'. $b->mailboxname_full
;
83 }else if ($b->is_special
) {
84 $bcmp = '1' . $b->mailboxname_full
;
86 $bcmp = '2' . $b->mailboxname_full
;
88 return strnatcasecmp($acmp, $bcmp);
91 function compact_mailboxes_response($ary)
94 * Workaround for mailboxes returned as literal
95 * FIXME : Doesn't work if the mailbox name is multiple lines
96 * (larger then fgets buffer)
98 for ($i = 0, $iCnt=count($ary); $i < $iCnt; $i++
) {
99 if (isset($ary[$i +
1]) && substr($ary[$i], -3) == "}\r\n") {
100 if (ereg("^(\\* [A-Z]+.*)\\{[0-9]+\\}([ \n\r\t]*)$",
102 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+
1])) . '"' . $regs[2];
103 array_splice($ary, $i+
1, 2);
107 /* remove duplicates and ensure array is contiguous */
108 return array_values(array_unique($ary));
112 * Extract the mailbox name from an untagged LIST (7.2.2) or LSUB (7.2.3) answer
113 * (LIST|LSUB) (<Flags list>) (NIL|"<separator atom>") <mailbox name string>\r\n
114 * mailbox name in quoted string MUST be unquoted and stripslashed (sm API)
116 function find_mailbox_name($line)
118 if (preg_match('/^\* (?:LIST|LSUB) \([^\)]*\) (?:NIL|\"[^\"]*\") ([^\r\n]*)[\r\n]*$/i', $line, $regs)) {
119 if (substr($regs[1], 0, 1) == '"')
120 return stripslashes(substr($regs[1], 1, -1));
127 * @return bool whether this is a Noselect mailbox.
129 function check_is_noselect ($lsub_line) {
130 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i", $lsub_line);
134 * @return bool whether this is a Noinferiors mailbox.
136 function check_is_noinferiors ($lsub_line) {
137 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noinferiors[^\)]*\)/i", $lsub_line);
141 * If $haystack is a full mailbox name, and $needle is the mailbox
142 * separator character, returns the second last part of the full
143 * mailbox name (i.e. the mailbox's parent mailbox)
145 function readMailboxParent($haystack, $needle) {
149 $parts = explode($needle, $haystack);
150 $elem = array_pop($parts);
151 while ($elem == '' && count($parts)) {
152 $elem = array_pop($parts);
154 $ret = join($needle, $parts);
160 * Check if $subbox is below the specified $parentbox
162 function isBoxBelow( $subbox, $parentbox ) {
165 * Eliminate the obvious mismatch, where the
166 * subfolder path is shorter than that of the potential parent
168 if ( strlen($subbox) < strlen($parentbox) ) {
171 /* check for delimiter */
172 if (!substr($parentbox,-1) == $delimiter) {
173 $parentbox.=$delimiter;
175 if (substr($subbox,0,strlen($parentbox)) == $parentbox) {
183 * Defines special mailboxes: given a mailbox name, it checks if this is a
184 * "special" one: INBOX, Trash, Sent or Draft.
186 function isSpecialMailbox( $box ) {
187 $ret = ( (strtolower($box) == 'inbox') ||
188 isTrashMailbox($box) ||
isSentMailbox($box) ||
isDraftMailbox($box) );
191 $ret = boolean_hook_function('special_mailbox',$box,1);
197 * @return bool whether this is a Trash folder
199 function isTrashMailbox ($box) {
200 global $trash_folder, $move_to_trash;
201 return $move_to_trash && $trash_folder &&
202 ( $box == $trash_folder ||
isBoxBelow($box, $trash_folder) );
206 * @return bool whether this is a Sent folder
208 function isSentMailbox($box) {
209 global $sent_folder, $move_to_sent;
210 return $move_to_sent && $sent_folder &&
211 ( $box == $sent_folder ||
isBoxBelow($box, $sent_folder) );
215 * @return bool whether this is a Draft folder
217 function isDraftMailbox($box) {
218 global $draft_folder, $save_as_draft;
219 return $save_as_draft &&
220 ( $box == $draft_folder ||
isBoxBelow($box, $draft_folder) );
224 * Expunges a mailbox, ie. delete all contents.
226 function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
229 $id = sqimap_message_list_squisher($id);
236 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
237 $response, $message, $uid);
240 if (is_array($read)) {
241 foreach ($read as $r) {
242 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
252 * Expunge specified message, updated $msgs and $msort
254 * Until Marc and I come up with a better way to maintain
255 * these stupid arrays, we'll use this wrapper function to
256 * remove the message with the matching UID .. the order
257 * won't be changed - the array element for the message
258 * will just be removed.
260 function sqimap_mailbox_expunge_dmn($message_id, $aMbxResponse, &$server_sort_array)
262 global $msgs, $msort, $sort, $imapConnection,
263 $mailbox, $auto_expunge,
264 $sort, $allow_server_sort, $thread_sort_messages, $allow_thread_sort,
265 $username, $data_dir;
268 if (!isset($sort) ||
$sort === false) {
269 sqgetGlobalVar('sort',$sort,SQ_GET
);
273 $cnt = sqimap_mailbox_expunge($imapConnection, $mailbox, true);
278 // Got to grab this out of prefs, since it isn't saved from mailbox_view.php
279 if ($allow_thread_sort && getPref($data_dir, $username, "thread_$mailbox",0)) {
281 } else if ($allow_server_sort) {
282 $mode = 'server_sort';
287 if (is_array($server_sort_array)) {
288 $key = array_search($message_id,$server_sort_array,true);
289 if ($key !== false) {
290 unset($server_sort_array[$key]);
291 $server_sort_array = array_values($server_sort_array);
293 $server_sort_array = get_sorted_msgs_list($imapConnection,$sort,$mode,$error);
296 $server_sort_array = get_sorted_msgs_list($imapConnection,$sort,$mode,$error);
298 sqsession_register($server_sort_array,'server_sort_array');
303 * Checks whether or not the specified mailbox exists
305 function sqimap_mailbox_exists ($imap_stream, $mailbox) {
306 if (!isset($mailbox) ||
empty($mailbox)) {
309 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mailbox),
310 true, $response, $message);
311 return isset($mbx[0]);
317 function sqimap_mailbox_select ($imap_stream, $mailbox) {
318 if ($mailbox == 'None') {
322 $read = sqimap_run_command($imap_stream, 'SELECT ' . sqimap_encode_mailbox_name($mailbox),
323 true, $response, $message);
325 for ($i = 0, $cnt = count($read); $i < $cnt; $i++
) {
326 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
327 $result[strtoupper($regs[1])] = $regs[2];
328 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
329 $result[strtoupper($regs[2])] = $regs[1];
331 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
332 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
333 $result['PERMANENTFLAGS'] = explode(' ',strtolower($regs[1]));
334 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
335 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
336 $result['FLAGS'] = explode(' ',strtolower($regs[1]));
340 if (!isset($result['PERMANENTFLAGS'])) {
341 $result['PERMANENTFLAGS'] = $result['FLAGS'];
343 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
344 $result['RIGHTS']=strtoupper($regs[1]);
353 function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
355 if (strtolower($type) == 'noselect') {
356 $mailbox .= $delimiter;
359 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' .
360 sqimap_encode_mailbox_name($mailbox),
361 true, $response, $message);
362 sqimap_subscribe ($imap_stream, $mailbox);
366 * Subscribes to an existing folder.
368 function sqimap_subscribe ($imap_stream, $mailbox,$debug=true) {
369 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' .
370 sqimap_encode_mailbox_name($mailbox),
371 $debug, $response, $message);
375 * Unsubscribes from an existing folder
377 function sqimap_unsubscribe ($imap_stream, $mailbox) {
378 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' .
379 sqimap_encode_mailbox_name($mailbox),
380 false, $response, $message);
384 * Deletes the given folder
386 function sqimap_mailbox_delete ($imap_stream, $mailbox) {
387 global $data_dir, $username;
388 sqimap_unsubscribe ($imap_stream, $mailbox);
389 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' .
390 sqimap_encode_mailbox_name($mailbox),
391 true, $response, $message);
392 if ($response !== 'OK') {
394 sqimap_subscribe ($imap_stream, $mailbox);
396 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
397 removePref($data_dir, $username, "thread_$mailbox");
402 * Determines if the user is subscribed to the folder or not
404 function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
405 $boxesall = sqimap_mailbox_list ($imap_stream);
406 foreach ($boxesall as $ref) {
407 if ($ref['unformatted'] == $folder) {
417 function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
418 if ( $old_name != $new_name ) {
419 global $delimiter, $imap_server_type, $data_dir, $username;
420 if ( substr( $old_name, -1 ) == $delimiter ) {
421 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
422 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
423 $postfix = $delimiter;
428 $boxesall = sqimap_mailbox_list($imap_stream);
429 $cmd = 'RENAME ' . sqimap_encode_mailbox_name($old_name) .
430 ' ' . sqimap_encode_mailbox_name($new_name);
431 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
432 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
433 $oldpref = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
434 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
435 sqimap_subscribe($imap_stream, $new_name.$postfix);
436 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref);
437 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
438 $l = strlen( $old_name ) +
1;
441 foreach ($boxesall as $box) {
442 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
443 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
444 if ($imap_server_type == 'cyrus') {
445 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
446 $data = sqimap_run_command($imap_stream, $cmd, true,
447 $response, $message);
449 sqimap_unsubscribe($imap_stream, $box[$p]);
450 $oldpref = getPref($data_dir, $username, 'thread_'.$box[$p]);
451 removePref($data_dir, $username, 'thread_'.$box[$p]);
452 sqimap_subscribe($imap_stream, $new_sub);
453 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref);
454 do_hook_function('rename_or_delete_folder',
455 $args = array($box[$p], 'rename', $new_sub));
462 * Formats a mailbox into parts for the $boxesall array
466 * raw - Raw LIST/LSUB response from the IMAP server
467 * formatted - nicely formatted folder name
468 * unformatted - unformatted, but with delimiter at end removed
469 * unformatted-dm - folder name as it appears in raw response
470 * unformatted-disp - unformatted without $folder_prefix
472 function sqimap_mailbox_parse ($line, $line_lsub) {
473 global $folder_prefix, $delimiter;
475 /* Process each folder line */
476 for ($g = 0, $cnt = count($line); $g < $cnt; ++
$g) {
477 /* Store the raw IMAP reply */
478 if (isset($line[$g])) {
479 $boxesall[$g]['raw'] = $line[$g];
481 $boxesall[$g]['raw'] = '';
484 /* Count number of delimiters ($delimiter) in folder name */
485 $mailbox = /*trim(*/$line_lsub[$g]/*)*/;
486 $dm_count = substr_count($mailbox, $delimiter);
487 if (substr($mailbox, -1) == $delimiter) {
488 /* If name ends in delimiter, decrement count by one */
492 /* Format folder name, but only if it's a INBOX.* or has a parent. */
493 $boxesallbyname[$mailbox] = $g;
494 $parentfolder = readMailboxParent($mailbox, $delimiter);
495 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
496 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
497 (isset($boxesallbyname[$parentfolder]) &&
498 (strlen($parentfolder) > 0) ) ) {
499 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
501 $boxesall[$g]['formatted'] = str_repeat(' ', $indent);
503 $boxesall[$g]['formatted'] = '';
505 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
507 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
510 $boxesall[$g]['unformatted-dm'] = $mailbox;
511 if (substr($mailbox, -1) == $delimiter) {
512 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
514 $boxesall[$g]['unformatted'] = $mailbox;
515 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
516 $mailbox = substr($mailbox, strlen($folder_prefix));
518 $boxesall[$g]['unformatted-disp'] = $mailbox;
519 $boxesall[$g]['id'] = $g;
521 $boxesall[$g]['flags'] = array();
522 if (isset($line[$g])) {
523 ereg("\(([^)]*)\)",$line[$g],$regs);
524 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
525 // and $MDNSent <= last one doesn't have the \
526 // It's better to follow RFC3501 instead of using our own naming.
527 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
529 $boxesall[$g]['flags'] = explode(' ', $flags);
537 * Returns list of options (to be echoed into select statement
538 * based on available mailboxes and separators
539 * Caller should surround options with <SELECT..> </SELECT> and
541 * $imap_stream - $imapConnection to query for mailboxes
542 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
543 * $folder_skip - array of folders to keep out of option list (compared in lower)
544 * $boxes - list of already fetched boxes (for places like folder panel, where
545 * you know these options will be shown 3 times in a row.. (most often unset).
546 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
547 * 'noselect' by default to remove unselectable mailboxes.
548 * 'noinferiors' used to filter out folders that can not contain subfolders.
549 * NULL to avoid flag check entirely.
550 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
551 * \NoSelect and \NoInferiors
552 * $use_long_format - override folder display preference and always show full folder name.
554 function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
555 $flag = 'noselect', $use_long_format = false ) {
556 global $username, $data_dir;
558 if ( $use_long_format ) {
559 $shorten_box_names = 0;
561 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF
);
565 $boxes = sqimap_mailbox_list($imap_stream);
568 foreach ($boxes as $boxes_part) {
569 if ($flag == NULL ||
!in_array($flag, $boxes_part['flags'])) {
570 $box = $boxes_part['unformatted'];
572 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
575 $lowerbox = strtolower($box);
576 // mailboxes are casesensitive => inbox.sent != inbox.Sent
577 // nevermind, to many dependencies this should be fixed!
579 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
582 switch ($shorten_box_names)
584 case 2: /* delimited, style = 2 */
585 $box2 = str_replace(' ', '. ', $boxes_part['formatted']);
587 case 1: /* indent, style = 1 */
588 $box2 = $boxes_part['formatted'];
590 default: /* default, long names, style = 0 */
591 $box2 = str_replace(' ', ' ', htmlspecialchars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
595 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
596 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'" SELECTED>'.$box2.'</OPTION>' . "\n";
598 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'">'.$box2.'</OPTION>' . "\n";
602 return $mbox_options;
606 * Returns sorted mailbox lists in several different ways.
607 * See comment on sqimap_mailbox_parse() for info about the returned array.
611 function sqimap_mailbox_list($imap_stream, $force=false) {
612 global $default_folder_prefix;
614 if (!sqgetGlobalVar('boxesnew',$boxesnew,SQ_SESSION
) ||
$force) {
615 global $data_dir, $username, $list_special_folders_first,
616 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
617 $move_to_trash, $move_to_sent, $save_as_draft,
618 $delimiter, $noselect_fix_enable, $imap_server_type;
619 $inbox_in_list = false;
620 $inbox_subscribed = false;
621 $listsubscribed = sqimap_capability($imap_stream,'LIST-SUBSCRIBED');
623 require_once(SM_PATH
. 'include/load_prefs.php');
626 if ($listsubscribed) {
627 $lsub = 'LIST (SUBSCRIBED)';
632 if ($noselect_fix_enable) {
634 $lsub_args = "$lsub \"$folder_prefix\" \"*%\"";
636 $lsub_args = "$lsub \"$folder_prefix\" \"*\"";
639 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
640 true, $response, $message);
641 $lsub_ary = compact_mailboxes_response($lsub_ary);
643 $sorted_lsub_ary = array();
644 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++
) {
646 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
647 $sorted_lsub_ary[] = $temp_mailbox_name;
648 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
649 $inbox_subscribed = true;
653 /* natural sort mailboxes */
654 if (isset($sorted_lsub_ary)) {
655 usort($sorted_lsub_ary, 'strnatcasecmp');
658 * The LSUB response doesn't provide us information about \Noselect
659 * mail boxes. The LIST response does, that's why we need to do a LIST
660 * call to retrieve the flags for the mailbox
661 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
662 * in other words, we cannot rely on it.
664 $sorted_list_ary = array();
665 // if (!$listsubscribed) {
666 for ($i=0; $i < count($sorted_lsub_ary); $i++
) {
667 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
668 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
671 $mbx = $sorted_lsub_ary[$i];
674 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mbx),
675 true, $response, $message);
677 $read = compact_mailboxes_response($read);
679 if (isset($read[0])) {
680 $sorted_list_ary[$i] = $read[0];
682 $sorted_list_ary[$i] = '';
687 * Just in case they're not subscribed to their inbox,
688 * we'll get it for them anyway
690 if (!$inbox_subscribed) {
691 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" "INBOX"',
692 true, $response, $message);
693 $sorted_list_ary[] = implode('',compact_mailboxes_response($inbox_ary));
694 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
697 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
699 /* Now, lets sort for special folders */
700 $boxesnew = $used = array();
703 $cnt = count($boxesall);
704 $used = array_pad($used,$cnt,false);
705 for($k = 0; $k < $cnt; ++
$k) {
706 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
707 $boxesnew[] = $boxesall[$k];
712 /* List special folders and their subfolders, if requested. */
713 if ($list_special_folders_first) {
714 for($k = 0; $k < $cnt; ++
$k) {
715 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
716 $boxesnew[] = $boxesall[$k];
721 /* Rest of the folders */
722 for($k = 0; $k < $cnt; $k++
) {
724 $boxesnew[] = $boxesall[$k];
727 sqsession_register($boxesnew,'boxesnew');
733 * Returns a list of all folders, subscribed or not
735 function sqimap_mailbox_list_all($imap_stream) {
736 global $list_special_folders_first, $folder_prefix, $delimiter;
738 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
739 $read_ary = compact_mailboxes_response($read_ary);
743 $fld_pre_length = strlen($folder_prefix);
744 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++
) {
745 /* Store the raw IMAP reply */
746 $boxes[$g]['raw'] = $read_ary[$i];
748 /* Count number of delimiters ($delimiter) in folder name */
749 $mailbox = find_mailbox_name($read_ary[$i]);
750 $dm_count = substr_count($mailbox, $delimiter);
751 if (substr($mailbox, -1) == $delimiter) {
752 /* If name ends in delimiter - decrement count by one */
756 /* Format folder name, but only if it's a INBOX.* or has a parent. */
757 $boxesallbyname[$mailbox] = $g;
758 $parentfolder = readMailboxParent($mailbox, $delimiter);
759 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
760 (ereg('^'.$folder_prefix, $mailbox)) ||
761 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
763 $boxes[$g]['formatted'] = str_repeat(' ', $dm_count);
765 $boxes[$g]['formatted'] = '';
767 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
769 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
772 $boxes[$g]['unformatted-dm'] = $mailbox;
773 if (substr($mailbox, -1) == $delimiter) {
774 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
776 $boxes[$g]['unformatted'] = $mailbox;
777 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
779 $boxes[$g]['id'] = $g;
781 /* Now lets get the flags for this mailbox */
782 $read_mlbx = $read_ary[$i];
783 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+
1);
784 $flags = substr($flags, 0, strpos($flags, ')'));
785 $flags = str_replace('\\', '', $flags);
786 $flags = trim(strtolower($flags));
788 $boxes[$g]['flags'] = explode(' ', $flags);
790 $boxes[$g]['flags'] = array();
794 if(is_array($boxes)) {
801 function sqimap_mailbox_tree($imap_stream) {
802 global $boxesnew, $default_folder_prefix, $unseen_notify, $unseen_type;
805 global $data_dir, $username, $list_special_folders_first,
806 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
810 $inbox_in_list = false;
811 $inbox_subscribed = false;
813 $noinferiors = false;
815 require_once(SM_PATH
. 'include/load_prefs.php');
818 $lsub_ary = sqimap_run_command ($imap_stream, "LSUB \"$folder_prefix\" \"*\"",
819 true, $response, $message);
820 $lsub_ary = compact_mailboxes_response($lsub_ary);
822 /* Check to see if we have an INBOX */
825 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++
) {
826 if (preg_match("/^\*\s+LSUB.*\s\"?INBOX\"?[^(\/\.)].*$/i",$lsub_ary[$i])) {
827 $lsub_ary[$i] = strtoupper($lsub_ary[$i]);
828 // in case of an unsubscribed inbox an imap server can
829 // return the inbox in the lsub results with a \NoSelect
831 if (!preg_match("/\*\s+LSUB\s+\(.*\\\\NoSelect.*\).*/i",$lsub_ary[$i])) {
834 // remove the result and request it again with a list
835 // response at a later stage.
836 unset($lsub_ary[$i]);
837 // re-index the array otherwise the addition of the LIST
838 // response will fail in PHP 4.1.2 and probably other older versions
839 $lsub_ary = array_values($lsub_ary);
845 if ($has_inbox == false) {
846 // do a list request for inbox because we should always show
847 // inbox even if the user isn't subscribed to it.
848 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" INBOX',
849 true, $response, $message);
850 $inbox_ary = compact_mailboxes_response($inbox_ary);
851 if (count($inbox_ary)) {
852 $lsub_ary[] = $inbox_ary[0];
857 * Section about removing the last element was removed
858 * We don't return "* OK" anymore from sqimap_read_data
861 $sorted_lsub_ary = array();
862 $cnt = count($lsub_ary);
863 for ($i = 0; $i < $cnt; $i++
) {
864 $mbx = find_mailbox_name($lsub_ary[$i]);
866 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
867 if ($imap_server_type != "uw") {
868 $noselect = check_is_noselect($lsub_ary[$i]);
869 $noinferiors = check_is_noinferiors($lsub_ary[$i]);
871 if (substr($mbx, -1) == $delimiter) {
872 $mbx = substr($mbx, 0, strlen($mbx) - 1);
874 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
876 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
877 if ($imap_server_type == "uw") {
880 // prepare an array with queries
881 foreach ($sorted_lsub_ary as $aMbx) {
882 $mbx = stripslashes($aMbx['mbx']);
883 sqimap_prepare_pipelined_query('LIST "" ' . sqimap_encode_mailbox_name($mbx), $tag, $aQuery, false);
886 $sorted_lsub_ary = array();
887 // execute all the queries at once
888 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
889 foreach($aTag as $tag => $mbx) {
890 if ($aServerResponse[$tag] == 'OK') {
891 $sResponse = implode('', $aResponse[$tag]);
892 $noselect = check_is_noselect($sResponse);
893 $noinferiors = check_is_noinferiors($sResponse);
894 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
897 $cnt = count($sorted_lsub_ary);
899 $sorted_lsub_ary = array_values($sorted_lsub_ary);
900 array_multisort($sorted_lsub_ary, SORT_ASC
, SORT_REGULAR
);
901 $boxesnew = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
906 function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
907 global $data_dir, $username, $list_special_folders_first,
908 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
909 $move_to_trash, $move_to_sent, $save_as_draft,
910 $delimiter, $imap_server_type;
912 $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
914 /* create virtual root node */
915 $mailboxes= new mailboxes();
916 $mailboxes->is_root
= true;
921 if (isset($folder_prefix) && ($folder_prefix != '')) {
922 $start = substr_count($folder_prefix,$delimiter);
923 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
925 $mailboxes->mailboxname_full
= substr($folder_prefix,0, (strlen($folder_prefix)-1));
927 $mailboxes->mailboxname_full
= $folder_prefix;
930 $mailboxes->mailboxname_sub
= $mailboxes->mailboxname_full
;
935 $cnt = count($mbx_ary);
936 for ($i=0; $i < $cnt; $i++
) {
937 if ($mbx_ary[$i]['mbx'] !='' ) {
938 $mbx = new mailboxes();
939 $mailbox = $mbx_ary[$i]['mbx'];
942 sent subfolders messes up using existing code as subfolders
943 were marked, but the parents were ordered somewhere else in
944 the list, despite having "special folders at top" option set.
945 Need a better method than this.
948 if ($mailbox == 'INBOX') {
949 $mbx->is_special = true;
950 } elseif (stristr($trash_folder , $mailbox)) {
951 $mbx->is_special = true;
952 } elseif (stristr($sent_folder , $mailbox)) {
953 $mbx->is_special = true;
954 } elseif (stristr($draft_folder , $mailbox)) {
955 $mbx->is_special = true;
960 $mbx->is_inbox = true;
961 $mbx->is_special = true;
962 $mbx_ary[$i]['noselect'] = false;
965 $mbx->is_trash = true;
966 $mbx->is_special = true;
969 $mbx->is_sent = true;
970 $mbx->is_special = true;
973 $mbx->is_draft = true;
974 $mbx->is_special = true;
978 $mbx->is_special |
= ($mbx->is_inbox
= (strtoupper($mailbox) == 'INBOX'));
979 $mbx->is_special |
= ($mbx->is_trash
= isTrashMailbox($mailbox));
980 $mbx->is_special |
= ($mbx->is_sent
= isSentMailbox($mailbox));
981 $mbx->is_special |
= ($mbx->is_draft
= isDraftMailbox($mailbox));
982 if (!$mbx->is_special
)
983 $mbx->is_special
= boolean_hook_function('special_mailbox', $mailbox, 1);
985 if (isset($mbx_ary[$i]['unseen'])) {
986 $mbx->unseen
= $mbx_ary[$i]['unseen'];
988 if (isset($mbx_ary[$i]['nummessages'])) {
989 $mbx->total
= $mbx_ary[$i]['nummessages'];
992 $mbx->is_noselect
= $mbx_ary[$i]['noselect'];
993 $mbx->is_noinferiors
= $mbx_ary[$i]['noinferiors'];
995 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
997 $mbx->mailboxname_sub
= substr($mbx_ary[$i]['mbx'],$r_del_pos+
1);
998 } else { /* mailbox is root folder */
999 $mbx->mailboxname_sub
= $mbx_ary[$i]['mbx'];
1001 $mbx->mailboxname_full
= $mbx_ary[$i]['mbx'];
1003 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
1006 sqimap_utf7_decode_mbx_tree($mailboxes);
1007 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
1011 function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
1012 if (strtoupper($mbx_tree->mailboxname_full
) == 'INBOX')
1013 $mbx_tree->mailboxname_sub
= _("INBOX");
1015 $mbx_tree->mailboxname_sub
= imap_utf7_decode_local($mbx_tree->mailboxname_sub
);
1016 if ($mbx_tree->mbxs
) {
1017 $iCnt = count($mbx_tree->mbxs
);
1018 for ($i=0;$i<$iCnt;++
$i) {
1019 $mbxs_tree->mbxs
[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs
[$i]);
1025 function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
1027 $aMbxs[] =& $mbx_tree;
1028 if ($mbx_tree->mbxs
) {
1029 $iCnt = count($mbx_tree->mbxs
);
1030 for ($i=0;$i<$iCnt;++
$i) {
1031 sqimap_tree_to_ref_array($mbx_tree->mbxs
[$i],$aMbxs);
1036 function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
1037 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
1038 $aMbxs = $aQuery = $aTag = array();
1039 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
1040 // remove the root node
1041 array_shift($aMbxs);
1043 if($unseen_notify == 3) {
1044 $cnt = count($aMbxs);
1045 for($i=0;$i<$cnt;++
$i) {
1046 $oMbx =& $aMbxs[$i];
1047 if (!$oMbx->is_noselect
) {
1048 $mbx = $oMbx->mailboxname_full
;
1049 if ($unseen_type == 2 ||
1050 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
1051 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
1053 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (UNSEEN)';
1055 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
1057 $oMbx->unseen
= $oMbx->total
= false;
1061 $aMbxs[$i] =& $oMbx;
1063 // execute all the queries at once
1064 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
1065 $cnt = count($aMbxs);
1066 for($i=0;$i<$cnt;++
$i) {
1067 $oMbx =& $aMbxs[$i];
1069 if ($tag && $aServerResponse[$tag] == 'OK') {
1070 $sResponse = implode('', $aResponse[$tag]);
1071 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
1072 $oMbx->unseen
= $regs[1];
1074 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
1075 $oMbx->total
= $regs[1];
1080 } else if ($unseen_notify == 2) { // INBOX only
1081 $cnt = count($aMbxs);
1082 for($i=0;$i<$cnt;++
$i) {
1083 $oMbx =& $aMbxs[$i];
1084 if (strtoupper($oMbx->mailboxname_full
) == 'INBOX' ||
1085 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
1086 if ($unseen_type == 2 ||
1087 ($oMbx->mailboxname_full
== $trash_folder && $move_to_trash)) {
1088 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full
);
1089 $oMbx->unseen
= $aStatus['UNSEEN'];
1090 $oMbx->total
= $aStatus['MESSAGES'];
1092 $oMbx->unseen
= sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full
);
1094 $aMbxs[$i] =& $oMbx;
1095 if (!$move_to_trash && $trash_folder) {
1098 // trash comes after INBOX
1099 if ($oMbx->mailboxname_full
== $trash_folder) {