5139f27c29703226523c89b3bd7a43e4b09d973d
6 * Copyright (c) 1999-2005 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
12 * @package squirrelmail
17 require_once(SM_PATH
. 'functions/imap_utf7_local.php');
24 * FIXME. This class should be extracted and placed in a separate file that
25 * can be included before we start the session. That makes caching of the tree
26 * possible. On a refresh mailboxes from left_main.php the only function that
27 * should be called is the sqimap_get_status_mbx_tree. In case of subscribe
28 * / rename / delete / new we have to create methods for adding/changing the
29 * mailbox in the mbx_tree without the need for a refresh.
30 * @package squirrelmail
35 var $mailboxname_full = '', $mailboxname_sub= '', $is_noselect = false, $is_noinferiors = false,
36 $is_special = false, $is_root = false, $is_inbox = false, $is_sent = false,
37 $is_trash = false, $is_draft = false, $mbxs = array(),
38 $unseen = false, $total = false;
40 function addMbx($mbx, $delimiter, $start, $specialfirst) {
41 $ary = explode($delimiter, $mbx->mailboxname_full
);
43 for ($i = $start, $c = count($ary)-1; $i < $c; $i++
) {
44 $mbx_childs =& $mbx_parent->mbxs
;
47 foreach ($mbx_childs as $key => $parent) {
48 if ($parent->mailboxname_sub
== $ary[$i]) {
49 $mbx_parent =& $mbx_parent->mbxs
[$key];
56 $no_select_mbx = new mailboxes();
57 if (isset($mbx_parent->mailboxname_full
) && $mbx_parent->mailboxname_full
!= '') {
58 $no_select_mbx->mailboxname_full
= $mbx_parent->mailboxname_full
.$delimiter.$ary[$i];
60 $no_select_mbx->mailboxname_full
= $ary[$i];
62 $no_select_mbx->mailboxname_sub
= $ary[$i];
63 $no_select_mbx->is_noselect
= true;
64 $mbx_parent->mbxs
[] = $no_select_mbx;
68 $mbx_parent->mbxs
[] = $mbx;
69 if ($mbx->is_special
&& $specialfirst) {
70 usort($mbx_parent->mbxs
, 'sortSpecialMbx');
76 * array callback used for sorting in mailboxes class
81 function sortSpecialMbx($a, $b) {
83 $acmp = '0'. $a->mailboxname_full
;
84 } else if ($a->is_special
) {
85 $acmp = '1'. $a->mailboxname_full
;
87 $acmp = '2' . $a->mailboxname_full
;
90 $bcmp = '0'. $b->mailboxname_full
;
91 }else if ($b->is_special
) {
92 $bcmp = '1' . $b->mailboxname_full
;
94 $bcmp = '2' . $b->mailboxname_full
;
96 return strnatcasecmp($acmp, $bcmp);
104 function compact_mailboxes_response($ary) {
106 * Workaround for mailboxes returned as literal
107 * FIXME : Doesn't work if the mailbox name is multiple lines
108 * (larger then fgets buffer)
110 for ($i = 0, $iCnt=count($ary); $i < $iCnt; $i++
) {
111 if (isset($ary[$i +
1]) && substr($ary[$i], -3) == "}\r\n") {
112 if (ereg("^(\\* [A-Z]+.*)\\{[0-9]+\\}([ \n\r\t]*)$",
114 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+
1])) . '"' . $regs[2];
115 array_splice($ary, $i+
1, 2);
119 /* remove duplicates and ensure array is contiguous */
120 return array_values(array_unique($ary));
124 * Extract the mailbox name from an untagged LIST (7.2.2) or LSUB (7.2.3) answer
125 * (LIST|LSUB) (<Flags list>) (NIL|"<separator atom>") <mailbox name string>\r\n
126 * mailbox name in quoted string MUST be unquoted and stripslashed (sm API)
128 * Originally stored in functions/strings.php. Since 1.2.6 stored in
129 * functions/imap_mailbox.php
130 * @param string $line imap LIST/LSUB response line
131 * @return string mailbox name
133 function find_mailbox_name($line) {
134 if (preg_match('/^\* (?:LIST|LSUB) \([^\)]*\) (?:NIL|\"[^\"]*\") ([^\r\n]*)[\r\n]*$/i', $line, $regs)) {
135 if (substr($regs[1], 0, 1) == '"')
136 return stripslashes(substr($regs[1], 1, -1));
143 * Detects if mailbox has noselect flag (can't store messages)
144 * @param string $lsub_line mailbox line from untagged LIST or LSUB response
145 * @return bool whether this is a Noselect mailbox.
148 function check_is_noselect ($lsub_line) {
149 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i", $lsub_line);
153 * Detects if mailbox has noinferiors flag (can't store subfolders)
154 * @param string $lsub_line mailbox line from untagged LIST or LSUB response
155 * @return bool whether this is a Noinferiors mailbox.
158 function check_is_noinferiors ($lsub_line) {
159 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noinferiors[^\)]*\)/i", $lsub_line);
163 * If $haystack is a full mailbox name, and $needle is the mailbox
164 * separator character, returns the second last part of the full
165 * mailbox name (i.e. the mailbox's parent mailbox)
167 function readMailboxParent($haystack, $needle) {
171 $parts = explode($needle, $haystack);
172 $elem = array_pop($parts);
173 while ($elem == '' && count($parts)) {
174 $elem = array_pop($parts);
176 $ret = join($needle, $parts);
182 * Check if $subbox is below the specified $parentbox
184 function isBoxBelow( $subbox, $parentbox ) {
187 * Eliminate the obvious mismatch, where the
188 * subfolder path is shorter than that of the potential parent
190 if ( strlen($subbox) < strlen($parentbox) ) {
193 /* check for delimiter */
194 if (substr($parentbox,-1) != $delimiter) {
195 $parentbox .= $delimiter;
198 return (substr($subbox,0,strlen($parentbox)) == $parentbox);
202 * Defines special mailboxes: given a mailbox name, it checks if this is a
203 * "special" one: INBOX, Trash, Sent or Draft.
205 function isSpecialMailbox( $box ) {
206 $ret = ( (strtolower($box) == 'inbox') ||
207 isTrashMailbox($box) ||
isSentMailbox($box) ||
isDraftMailbox($box) );
210 $ret = boolean_hook_function('special_mailbox',$box,1);
216 * @return bool whether this is a Trash folder
218 function isTrashMailbox ($box) {
219 global $trash_folder, $move_to_trash;
220 return $move_to_trash && $trash_folder &&
221 ( $box == $trash_folder ||
isBoxBelow($box, $trash_folder) );
225 * @return bool whether this is a Sent folder
227 function isSentMailbox($box) {
228 global $sent_folder, $move_to_sent;
229 return $move_to_sent && $sent_folder &&
230 ( $box == $sent_folder ||
isBoxBelow($box, $sent_folder) );
234 * @return bool whether this is a Draft folder
236 function isDraftMailbox($box) {
237 global $draft_folder, $save_as_draft;
238 return $save_as_draft &&
239 ( $box == $draft_folder ||
isBoxBelow($box, $draft_folder) );
243 * Expunges a mailbox, ie. delete all contents.
245 function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
248 $id = sqimap_message_list_squisher($id);
255 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
256 $response, $message, $uid);
259 if (is_array($read)) {
260 foreach ($read as $r) {
261 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
270 * Checks whether or not the specified mailbox exists
272 function sqimap_mailbox_exists ($imap_stream, $mailbox) {
273 if (!isset($mailbox) ||
empty($mailbox)) {
276 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mailbox),
277 true, $response, $message);
278 return isset($mbx[0]);
284 function sqimap_mailbox_select ($imap_stream, $mailbox) {
285 if ($mailbox == 'None') {
289 $read = sqimap_run_command($imap_stream, 'SELECT ' . sqimap_encode_mailbox_name($mailbox),
290 true, $response, $message);
292 for ($i = 0, $cnt = count($read); $i < $cnt; $i++
) {
293 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
294 $result[strtoupper($regs[1])] = $regs[2];
295 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
296 $result[strtoupper($regs[2])] = $regs[1];
298 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
299 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
300 $result['PERMANENTFLAGS'] = explode(' ',strtolower($regs[1]));
301 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
302 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
303 $result['FLAGS'] = explode(' ',strtolower($regs[1]));
307 if (!isset($result['PERMANENTFLAGS'])) {
308 $result['PERMANENTFLAGS'] = $result['FLAGS'];
310 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
311 $result['RIGHTS']=strtoupper($regs[1]);
320 function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
322 if (strtolower($type) == 'noselect') {
323 $mailbox .= $delimiter;
326 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' .
327 sqimap_encode_mailbox_name($mailbox),
328 true, $response, $message);
329 sqimap_subscribe ($imap_stream, $mailbox);
333 * Subscribes to an existing folder.
335 function sqimap_subscribe ($imap_stream, $mailbox,$debug=true) {
336 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' .
337 sqimap_encode_mailbox_name($mailbox),
338 $debug, $response, $message);
342 * Unsubscribes from an existing folder
344 function sqimap_unsubscribe ($imap_stream, $mailbox) {
345 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' .
346 sqimap_encode_mailbox_name($mailbox),
347 false, $response, $message);
351 * Deletes the given folder
353 function sqimap_mailbox_delete ($imap_stream, $mailbox) {
354 global $data_dir, $username;
355 sqimap_unsubscribe ($imap_stream, $mailbox);
356 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' .
357 sqimap_encode_mailbox_name($mailbox),
358 true, $response, $message);
359 if ($response !== 'OK') {
361 sqimap_subscribe ($imap_stream, $mailbox);
363 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
364 removePref($data_dir, $username, "thread_$mailbox");
365 removePref($data_dir, $username, "collapse_folder_$mailbox");
370 * Determines if the user is subscribed to the folder or not
372 function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
373 $boxesall = sqimap_mailbox_list ($imap_stream);
374 foreach ($boxesall as $ref) {
375 if ($ref['unformatted'] == $folder) {
385 function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
386 if ( $old_name != $new_name ) {
387 global $delimiter, $imap_server_type, $data_dir, $username;
388 if ( substr( $old_name, -1 ) == $delimiter ) {
389 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
390 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
391 $postfix = $delimiter;
396 $boxesall = sqimap_mailbox_list_all($imap_stream);
397 $cmd = 'RENAME ' . sqimap_encode_mailbox_name($old_name) .
398 ' ' . sqimap_encode_mailbox_name($new_name);
399 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
400 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
401 $oldpref_thread = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
402 $oldpref_collapse = getPref($data_dir, $username, 'collapse_folder_'.$old_name.$postfix);
403 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
404 removePref($data_dir, $username, 'collapse_folder_'.$old_name.$postfix);
405 sqimap_subscribe($imap_stream, $new_name.$postfix);
406 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref_thread);
407 setPref($data_dir, $username, 'collapse_folder_'.$new_name.$postfix, $oldpref_collapse);
408 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
409 $l = strlen( $old_name ) +
1;
412 foreach ($boxesall as $box) {
413 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
414 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
415 /* With Cyrus IMAPd >= 2.0 rename is recursive, so don't check for errors here */
416 if ($imap_server_type == 'cyrus') {
417 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
418 $data = sqimap_run_command($imap_stream, $cmd, false,
419 $response, $message);
421 $was_subscribed = sqimap_mailbox_is_subscribed($imap_stream, $box[$p]);
422 if ( $was_subscribed ) {
423 sqimap_unsubscribe($imap_stream, $box[$p]);
425 $oldpref_thread = getPref($data_dir, $username, 'thread_'.$box[$p]);
426 $oldpref_collapse = getPref($data_dir, $username, 'collapse_folder_'.$box[$p]);
427 removePref($data_dir, $username, 'thread_'.$box[$p]);
428 removePref($data_dir, $username, 'collapse_folder_'.$box[$p]);
429 if ( $was_subscribed ) {
430 sqimap_subscribe($imap_stream, $new_sub);
432 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref_thread);
433 setPref($data_dir, $username, 'collapse_folder_'.$new_sub, $oldpref_collapse);
434 do_hook_function('rename_or_delete_folder',
435 $args = array($box[$p], 'rename', $new_sub));
442 * Formats a mailbox into parts for the $boxesall array
446 * raw - Raw LIST/LSUB response from the IMAP server
447 * formatted - nicely formatted folder name
448 * unformatted - unformatted, but with delimiter at end removed
449 * unformatted-dm - folder name as it appears in raw response
450 * unformatted-disp - unformatted without $folder_prefix
452 function sqimap_mailbox_parse ($line, $line_lsub) {
453 global $folder_prefix, $delimiter;
455 /* Process each folder line */
456 for ($g = 0, $cnt = count($line); $g < $cnt; ++
$g) {
457 /* Store the raw IMAP reply */
458 if (isset($line[$g])) {
459 $boxesall[$g]['raw'] = $line[$g];
461 $boxesall[$g]['raw'] = '';
464 /* Count number of delimiters ($delimiter) in folder name */
465 $mailbox = /*trim(*/$line_lsub[$g]/*)*/;
466 $dm_count = substr_count($mailbox, $delimiter);
467 if (substr($mailbox, -1) == $delimiter) {
468 /* If name ends in delimiter, decrement count by one */
472 /* Format folder name, but only if it's a INBOX.* or has a parent. */
473 $boxesallbyname[$mailbox] = $g;
474 $parentfolder = readMailboxParent($mailbox, $delimiter);
475 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
476 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
477 (isset($boxesallbyname[$parentfolder]) &&
478 (strlen($parentfolder) > 0) ) ) {
479 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
481 $boxesall[$g]['formatted'] = str_repeat(' ', $indent);
483 $boxesall[$g]['formatted'] = '';
485 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
487 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
490 $boxesall[$g]['unformatted-dm'] = $mailbox;
491 if (substr($mailbox, -1) == $delimiter) {
492 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
494 $boxesall[$g]['unformatted'] = $mailbox;
495 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
496 $mailbox = substr($mailbox, strlen($folder_prefix));
498 $boxesall[$g]['unformatted-disp'] = $mailbox;
499 $boxesall[$g]['id'] = $g;
501 $boxesall[$g]['flags'] = array();
502 if (isset($line[$g])) {
503 ereg("\(([^)]*)\)",$line[$g],$regs);
504 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
505 // and $MDNSent <= last one doesn't have the \
506 // It's better to follow RFC3501 instead of using our own naming.
507 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
509 $boxesall[$g]['flags'] = explode(' ', $flags);
517 * Returns list of options (to be echoed into select statement
518 * based on available mailboxes and separators
519 * Caller should surround options with <select ...> </select> and
521 * $imap_stream - $imapConnection to query for mailboxes
522 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
523 * $folder_skip - array of folders to keep out of option list (compared in lower)
524 * $boxes - list of already fetched boxes (for places like folder panel, where
525 * you know these options will be shown 3 times in a row.. (most often unset).
526 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
527 * 'noselect' by default to remove unselectable mailboxes.
528 * 'noinferiors' used to filter out folders that can not contain subfolders.
529 * NULL to avoid flag check entirely.
530 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
531 * \NoSelect and \NoInferiors
532 * $use_long_format - override folder display preference and always show full folder name.
534 function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
535 $flag = 'noselect', $use_long_format = false ) {
536 global $username, $data_dir;
538 if ( $use_long_format ) {
539 $shorten_box_names = 0;
541 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF
);
545 $boxes = sqimap_mailbox_list($imap_stream);
548 foreach ($boxes as $boxes_part) {
549 if ($flag == NULL ||
(is_array($boxes_part['flags'])
550 && !in_array($flag, $boxes_part['flags']))) {
551 $box = $boxes_part['unformatted'];
553 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
556 $lowerbox = strtolower($box);
557 // mailboxes are casesensitive => inbox.sent != inbox.Sent
558 // nevermind, to many dependencies this should be fixed!
560 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
563 switch ($shorten_box_names)
565 case 2: /* delimited, style = 2 */
566 $box2 = str_replace('&nbsp;&nbsp;', '. ', htmlspecialchars($boxes_part['formatted']));
568 case 1: /* indent, style = 1 */
569 $box2 = str_replace('&nbsp;&nbsp;', ' ', htmlspecialchars($boxes_part['formatted']));
571 default: /* default, long names, style = 0 */
572 $box2 = str_replace(' ', ' ', htmlspecialchars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
576 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
577 $mbox_options .= '<option value="' . htmlspecialchars($box) .'" selected="selected">'.$box2.'</option>' . "\n";
579 $mbox_options .= '<option value="' . htmlspecialchars($box) .'">'.$box2.'</option>' . "\n";
583 return $mbox_options;
587 * Returns sorted mailbox lists in several different ways.
588 * See comment on sqimap_mailbox_parse() for info about the returned array.
589 * @param resource $imap_stream imap connection resource
590 * @param boolean $force force update of mailbox listing. available since 1.4.2 and 1.5.0
591 * @return array list of mailboxes
593 function sqimap_mailbox_list($imap_stream, $force=false) {
594 if (!sqgetGlobalVar('boxesnew',$boxesnew,SQ_SESSION
) ||
$force) {
595 global $data_dir, $username, $list_special_folders_first,
596 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
597 $move_to_trash, $move_to_sent, $save_as_draft,
598 $delimiter, $noselect_fix_enable, $imap_server_type,
599 $show_only_subscribed_folders;
600 $inbox_subscribed = false;
601 $listsubscribed = sqimap_capability($imap_stream,'LIST-SUBSCRIBED');
603 require_once(SM_PATH
. 'include/load_prefs.php');
605 if (!$show_only_subscribed_folders) {
607 } elseif ($listsubscribed) {
608 $lsub = 'LIST (SUBSCRIBED)';
613 if ($noselect_fix_enable) {
614 $lsub_args = "$lsub \"$folder_prefix\" \"*%\"";
616 $lsub_args = "$lsub \"$folder_prefix\" \"*\"";
619 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
620 true, $response, $message);
621 $lsub_ary = compact_mailboxes_response($lsub_ary);
623 $sorted_lsub_ary = array();
624 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++
) {
626 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
627 $sorted_lsub_ary[] = $temp_mailbox_name;
628 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
629 $inbox_subscribed = true;
633 /* natural sort mailboxes */
634 if (isset($sorted_lsub_ary)) {
635 usort($sorted_lsub_ary, 'strnatcasecmp');
638 * The LSUB response doesn't provide us information about \Noselect
639 * mail boxes. The LIST response does, that's why we need to do a LIST
640 * call to retrieve the flags for the mailbox
641 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
642 * in other words, we cannot rely on it.
644 $sorted_list_ary = array();
645 // if (!$listsubscribed) {
646 for ($i=0; $i < count($sorted_lsub_ary); $i++
) {
647 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
648 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
651 $mbx = $sorted_lsub_ary[$i];
654 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mbx),
655 true, $response, $message);
657 $read = compact_mailboxes_response($read);
659 if (isset($read[0])) {
660 $sorted_list_ary[$i] = $read[0];
662 $sorted_list_ary[$i] = '';
667 * Just in case they're not subscribed to their inbox,
668 * we'll get it for them anyway
670 if (!$inbox_subscribed) {
671 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" "INBOX"',
672 true, $response, $message);
673 $sorted_list_ary[] = implode('',compact_mailboxes_response($inbox_ary));
674 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
677 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
679 /* Now, lets sort for special folders */
680 $boxesnew = $used = array();
683 $cnt = count($boxesall);
684 $used = array_pad($used,$cnt,false);
685 for($k = 0; $k < $cnt; ++
$k) {
686 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
687 $boxesnew[] = $boxesall[$k];
692 /* List special folders and their subfolders, if requested. */
693 if ($list_special_folders_first) {
694 for($k = 0; $k < $cnt; ++
$k) {
695 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
696 $boxesnew[] = $boxesall[$k];
702 /* Find INBOX's children */
703 for($k = 0; $k < $cnt; ++
$k) {
704 if (!$used[$k] && isBoxBelow(strtolower($boxesall[$k]['unformatted']), 'inbox') &&
705 strtolower($boxesall[$k]['unformatted']) != 'inbox') {
706 $boxesnew[] = $boxesall[$k];
711 /* Rest of the folders */
712 for($k = 0; $k < $cnt; $k++
) {
714 $boxesnew[] = $boxesall[$k];
717 sqsession_register($boxesnew,'boxesnew');
723 * Returns a list of all folders, subscribed or not
725 function sqimap_mailbox_list_all($imap_stream) {
726 global $list_special_folders_first, $folder_prefix, $delimiter;
728 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
729 $read_ary = compact_mailboxes_response($read_ary);
732 $fld_pre_length = strlen($folder_prefix);
733 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++
) {
734 /* Store the raw IMAP reply */
735 $boxes[$g]['raw'] = $read_ary[$i];
737 /* Count number of delimiters ($delimiter) in folder name */
738 $mailbox = find_mailbox_name($read_ary[$i]);
739 $dm_count = substr_count($mailbox, $delimiter);
740 if (substr($mailbox, -1) == $delimiter) {
741 /* If name ends in delimiter - decrement count by one */
745 /* Format folder name, but only if it's a INBOX.* or has a parent. */
746 $boxesallbyname[$mailbox] = $g;
747 $parentfolder = readMailboxParent($mailbox, $delimiter);
748 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
749 (ereg('^'.$folder_prefix, $mailbox)) ||
750 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
752 $boxes[$g]['formatted'] = str_repeat(' ', $dm_count);
754 $boxes[$g]['formatted'] = '';
756 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
758 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
761 $boxes[$g]['unformatted-dm'] = $mailbox;
762 if (substr($mailbox, -1) == $delimiter) {
763 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
765 $boxes[$g]['unformatted'] = $mailbox;
766 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
768 $boxes[$g]['id'] = $g;
770 /* Now lets get the flags for this mailbox */
771 $read_mlbx = $read_ary[$i];
772 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+
1);
773 $flags = substr($flags, 0, strpos($flags, ')'));
774 $flags = str_replace('\\', '', $flags);
775 $flags = trim(strtolower($flags));
777 $boxes[$g]['flags'] = explode(' ', $flags);
779 $boxes[$g]['flags'] = array();
783 if(is_array($boxes)) {
790 function sqimap_mailbox_tree($imap_stream) {
791 global $default_folder_prefix;
793 global $data_dir, $username, $list_special_folders_first,
794 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
795 $imap_server_type, $show_only_subscribed_folders;
798 $noinferiors = false;
800 require_once(SM_PATH
. 'include/load_prefs.php');
802 if ($show_only_subscribed_folders) {
809 $lsub_ary = sqimap_run_command ($imap_stream, "$lsub_cmd \"$folder_prefix\" \"*\"",
810 true, $response, $message);
811 $lsub_ary = compact_mailboxes_response($lsub_ary);
813 /* Check to see if we have an INBOX */
816 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++
) {
817 if (preg_match("/^\*\s+$lsub_cmd.*\s\"?INBOX\"?[^(\/\.)].*$/i",$lsub_ary[$i])) {
818 $lsub_ary[$i] = strtoupper($lsub_ary[$i]);
819 // in case of an unsubscribed inbox an imap server can
820 // return the inbox in the lsub results with a \NoSelect
822 if (!preg_match("/\*\s+$lsub_cmd\s+\(.*\\\\NoSelect.*\).*/i",$lsub_ary[$i])) {
825 // remove the result and request it again with a list
826 // response at a later stage.
827 unset($lsub_ary[$i]);
828 // re-index the array otherwise the addition of the LIST
829 // response will fail in PHP 4.1.2 and probably other older versions
830 $lsub_ary = array_values($lsub_ary);
836 if ($has_inbox == false) {
837 // do a list request for inbox because we should always show
838 // inbox even if the user isn't subscribed to it.
839 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" "INBOX"',
840 true, $response, $message);
841 $inbox_ary = compact_mailboxes_response($inbox_ary);
842 if (count($inbox_ary)) {
843 $lsub_ary[] = $inbox_ary[0];
848 * Section about removing the last element was removed
849 * We don't return "* OK" anymore from sqimap_read_data
852 $sorted_lsub_ary = array();
853 $cnt = count($lsub_ary);
854 for ($i = 0; $i < $cnt; $i++
) {
855 $mbx = find_mailbox_name($lsub_ary[$i]);
857 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
858 if ($imap_server_type != "uw") {
859 $noselect = check_is_noselect($lsub_ary[$i]);
860 $noinferiors = check_is_noinferiors($lsub_ary[$i]);
862 if (substr($mbx, -1) == $delimiter) {
863 $mbx = substr($mbx, 0, strlen($mbx) - 1);
865 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
867 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
868 if ($imap_server_type == "uw") {
871 // prepare an array with queries
872 foreach ($sorted_lsub_ary as $aMbx) {
873 $mbx = stripslashes($aMbx['mbx']);
874 sqimap_prepare_pipelined_query('LIST "" ' . sqimap_encode_mailbox_name($mbx), $tag, $aQuery, false);
877 $sorted_lsub_ary = array();
878 // execute all the queries at once
879 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
880 foreach($aTag as $tag => $mbx) {
881 if ($aServerResponse[$tag] == 'OK') {
882 $sResponse = implode('', $aResponse[$tag]);
883 $noselect = check_is_noselect($sResponse);
884 $noinferiors = check_is_noinferiors($sResponse);
885 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
888 $cnt = count($sorted_lsub_ary);
890 $sorted_lsub_ary = array_values($sorted_lsub_ary);
891 usort($sorted_lsub_ary, 'mbxSort');
892 $boxestree = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
897 function mbxSort($a, $b) {
898 return strnatcasecmp($a['mbx'], $b['mbx']);
901 function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
902 global $data_dir, $username, $list_special_folders_first,
903 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
904 $move_to_trash, $move_to_sent, $save_as_draft,
905 $delimiter, $imap_server_type;
907 // $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
909 /* create virtual root node */
910 $mailboxes= new mailboxes();
911 $mailboxes->is_root
= true;
916 if (isset($folder_prefix) && ($folder_prefix != '')) {
917 $start = substr_count($folder_prefix,$delimiter);
918 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
919 $mailboxes->mailboxname_full
= substr($folder_prefix,0, (strlen($folder_prefix)-1));
921 $mailboxes->mailboxname_full
= $folder_prefix;
924 $mailboxes->mailboxname_sub
= $mailboxes->mailboxname_full
;
929 $cnt = count($mbx_ary);
930 for ($i=0; $i < $cnt; $i++
) {
931 if ($mbx_ary[$i]['mbx'] !='' ) {
932 $mbx = new mailboxes();
933 $mailbox = $mbx_ary[$i]['mbx'];
936 sent subfolders messes up using existing code as subfolders
937 were marked, but the parents were ordered somewhere else in
938 the list, despite having "special folders at top" option set.
939 Need a better method than this.
942 if ($mailbox == 'INBOX') {
943 $mbx->is_special = true;
944 } elseif (stristr($trash_folder , $mailbox)) {
945 $mbx->is_special = true;
946 } elseif (stristr($sent_folder , $mailbox)) {
947 $mbx->is_special = true;
948 } elseif (stristr($draft_folder , $mailbox)) {
949 $mbx->is_special = true;
954 $mbx->is_inbox = true;
955 $mbx->is_special = true;
956 $mbx_ary[$i]['noselect'] = false;
959 $mbx->is_trash = true;
960 $mbx->is_special = true;
963 $mbx->is_sent = true;
964 $mbx->is_special = true;
967 $mbx->is_draft = true;
968 $mbx->is_special = true;
972 $mbx->is_special |
= ($mbx->is_inbox
= (strtoupper($mailbox) == 'INBOX'));
973 $mbx->is_special |
= ($mbx->is_trash
= isTrashMailbox($mailbox));
974 $mbx->is_special |
= ($mbx->is_sent
= isSentMailbox($mailbox));
975 $mbx->is_special |
= ($mbx->is_draft
= isDraftMailbox($mailbox));
976 if (!$mbx->is_special
)
977 $mbx->is_special
= boolean_hook_function('special_mailbox', $mailbox, 1);
979 if (isset($mbx_ary[$i]['unseen'])) {
980 $mbx->unseen
= $mbx_ary[$i]['unseen'];
982 if (isset($mbx_ary[$i]['nummessages'])) {
983 $mbx->total
= $mbx_ary[$i]['nummessages'];
986 $mbx->is_noselect
= $mbx_ary[$i]['noselect'];
987 $mbx->is_noinferiors
= $mbx_ary[$i]['noinferiors'];
989 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
991 $mbx->mailboxname_sub
= substr($mbx_ary[$i]['mbx'],$r_del_pos+
1);
992 } else { /* mailbox is root folder */
993 $mbx->mailboxname_sub
= $mbx_ary[$i]['mbx'];
995 $mbx->mailboxname_full
= $mbx_ary[$i]['mbx'];
997 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
1000 sqimap_utf7_decode_mbx_tree($mailboxes);
1001 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
1005 function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
1006 if (strtoupper($mbx_tree->mailboxname_full
) == 'INBOX')
1007 $mbx_tree->mailboxname_sub
= _("INBOX");
1009 $mbx_tree->mailboxname_sub
= imap_utf7_decode_local($mbx_tree->mailboxname_sub
);
1010 if ($mbx_tree->mbxs
) {
1011 $iCnt = count($mbx_tree->mbxs
);
1012 for ($i=0;$i<$iCnt;++
$i) {
1013 $mbxs_tree->mbxs
[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs
[$i]);
1019 function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
1021 $aMbxs[] =& $mbx_tree;
1022 if ($mbx_tree->mbxs
) {
1023 $iCnt = count($mbx_tree->mbxs
);
1024 for ($i=0;$i<$iCnt;++
$i) {
1025 sqimap_tree_to_ref_array($mbx_tree->mbxs
[$i],$aMbxs);
1030 function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
1031 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
1032 $aMbxs = $aQuery = array();
1033 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
1034 // remove the root node
1035 array_shift($aMbxs);
1037 if($unseen_notify == 3) {
1038 $cnt = count($aMbxs);
1039 for($i=0;$i<$cnt;++
$i) {
1040 $oMbx =& $aMbxs[$i];
1041 if (!$oMbx->is_noselect
) {
1042 $mbx = $oMbx->mailboxname_full
;
1043 if ($unseen_type == 2 ||
1044 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
1045 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
1047 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (UNSEEN)';
1049 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
1051 $oMbx->unseen
= $oMbx->total
= false;
1055 $aMbxs[$i] =& $oMbx;
1057 // execute all the queries at once
1058 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
1059 $cnt = count($aMbxs);
1060 for($i=0;$i<$cnt;++
$i) {
1061 $oMbx =& $aMbxs[$i];
1063 if ($tag && $aServerResponse[$tag] == 'OK') {
1064 $sResponse = implode('', $aResponse[$tag]);
1065 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
1066 $oMbx->unseen
= $regs[1];
1068 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
1069 $oMbx->total
= $regs[1];
1074 } else if ($unseen_notify == 2) { // INBOX only
1075 $cnt = count($aMbxs);
1076 for($i=0;$i<$cnt;++
$i) {
1077 $oMbx =& $aMbxs[$i];
1078 if (strtoupper($oMbx->mailboxname_full
) == 'INBOX' ||
1079 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
1080 if ($unseen_type == 2 ||
1081 ($oMbx->mailboxname_full
== $trash_folder && $move_to_trash)) {
1082 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full
);
1083 $oMbx->unseen
= $aStatus['UNSEEN'];
1084 $oMbx->total
= $aStatus['MESSAGES'];
1086 $oMbx->unseen
= sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full
);
1088 $aMbxs[$i] =& $oMbx;
1089 if (!$move_to_trash && $trash_folder) {
1092 // trash comes after INBOX
1093 if ($oMbx->mailboxname_full
== $trash_folder) {