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
34 var $mailboxname_full = '', $mailboxname_sub= '', $is_noselect = false, $is_noinferiors = false,
35 $is_special = false, $is_root = false, $is_inbox = false, $is_sent = false,
36 $is_trash = false, $is_draft = false, $mbxs = array(),
37 $unseen = false, $total = false;
39 function addMbx($mbx, $delimiter, $start, $specialfirst) {
40 $ary = explode($delimiter, $mbx->mailboxname_full
);
42 for ($i = $start, $c = count($ary)-1; $i < $c; $i++
) {
43 $mbx_childs =& $mbx_parent->mbxs
;
46 foreach ($mbx_childs as $key => $parent) {
47 if ($parent->mailboxname_sub
== $ary[$i]) {
48 $mbx_parent =& $mbx_parent->mbxs
[$key];
55 $no_select_mbx = new mailboxes();
56 if (isset($mbx_parent->mailboxname_full
) && $mbx_parent->mailboxname_full
!= '') {
57 $no_select_mbx->mailboxname_full
= $mbx_parent->mailboxname_full
.$delimiter.$ary[$i];
59 $no_select_mbx->mailboxname_full
= $ary[$i];
61 $no_select_mbx->mailboxname_sub
= $ary[$i];
62 $no_select_mbx->is_noselect
= true;
63 $mbx_parent->mbxs
[] = $no_select_mbx;
67 $mbx_parent->mbxs
[] = $mbx;
68 if ($mbx->is_special
&& $specialfirst) {
69 usort($mbx_parent->mbxs
, 'sortSpecialMbx');
74 function sortSpecialMbx($a, $b) {
76 $acmp = '0'. $a->mailboxname_full
;
77 } else if ($a->is_special
) {
78 $acmp = '1'. $a->mailboxname_full
;
80 $acmp = '2' . $a->mailboxname_full
;
83 $bcmp = '0'. $b->mailboxname_full
;
84 }else if ($b->is_special
) {
85 $bcmp = '1' . $b->mailboxname_full
;
87 $bcmp = '2' . $b->mailboxname_full
;
89 return strnatcasecmp($acmp, $bcmp);
92 function compact_mailboxes_response($ary)
95 * Workaround for mailboxes returned as literal
96 * FIXME : Doesn't work if the mailbox name is multiple lines
97 * (larger then fgets buffer)
99 for ($i = 0, $iCnt=count($ary); $i < $iCnt; $i++
) {
100 if (isset($ary[$i +
1]) && substr($ary[$i], -3) == "}\r\n") {
101 if (ereg("^(\\* [A-Z]+.*)\\{[0-9]+\\}([ \n\r\t]*)$",
103 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+
1])) . '"' . $regs[2];
104 array_splice($ary, $i+
1, 2);
108 /* remove duplicates and ensure array is contiguous */
109 return array_values(array_unique($ary));
113 * Extract the mailbox name from an untagged LIST (7.2.2) or LSUB (7.2.3) answer
114 * (LIST|LSUB) (<Flags list>) (NIL|"<separator atom>") <mailbox name string>\r\n
115 * mailbox name in quoted string MUST be unquoted and stripslashed (sm API)
117 function find_mailbox_name($line)
119 if (preg_match('/^\* (?:LIST|LSUB) \([^\)]*\) (?:NIL|\"[^\"]*\") ([^\r\n]*)[\r\n]*$/i', $line, $regs)) {
120 if (substr($regs[1], 0, 1) == '"')
121 return stripslashes(substr($regs[1], 1, -1));
128 * @return bool whether this is a Noselect mailbox.
130 function check_is_noselect ($lsub_line) {
131 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i", $lsub_line);
135 * @return bool whether this is a Noinferiors mailbox.
137 function check_is_noinferiors ($lsub_line) {
138 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noinferiors[^\)]*\)/i", $lsub_line);
142 * If $haystack is a full mailbox name, and $needle is the mailbox
143 * separator character, returns the second last part of the full
144 * mailbox name (i.e. the mailbox's parent mailbox)
146 function readMailboxParent($haystack, $needle) {
150 $parts = explode($needle, $haystack);
151 $elem = array_pop($parts);
152 while ($elem == '' && count($parts)) {
153 $elem = array_pop($parts);
155 $ret = join($needle, $parts);
161 * Check if $subbox is below the specified $parentbox
163 function isBoxBelow( $subbox, $parentbox ) {
166 * Eliminate the obvious mismatch, where the
167 * subfolder path is shorter than that of the potential parent
169 if ( strlen($subbox) < strlen($parentbox) ) {
172 /* check for delimiter */
173 if (substr($parentbox,-1) != $delimiter) {
174 $parentbox .= $delimiter;
177 return (substr($subbox,0,strlen($parentbox)) == $parentbox);
181 * Defines special mailboxes: given a mailbox name, it checks if this is a
182 * "special" one: INBOX, Trash, Sent or Draft.
184 function isSpecialMailbox( $box ) {
185 $ret = ( (strtolower($box) == 'inbox') ||
186 isTrashMailbox($box) ||
isSentMailbox($box) ||
isDraftMailbox($box) );
189 $ret = boolean_hook_function('special_mailbox',$box,1);
195 * @return bool whether this is a Trash folder
197 function isTrashMailbox ($box) {
198 global $trash_folder, $move_to_trash;
199 return $move_to_trash && $trash_folder &&
200 ( $box == $trash_folder ||
isBoxBelow($box, $trash_folder) );
204 * @return bool whether this is a Sent folder
206 function isSentMailbox($box) {
207 global $sent_folder, $move_to_sent;
208 return $move_to_sent && $sent_folder &&
209 ( $box == $sent_folder ||
isBoxBelow($box, $sent_folder) );
213 * @return bool whether this is a Draft folder
215 function isDraftMailbox($box) {
216 global $draft_folder, $save_as_draft;
217 return $save_as_draft &&
218 ( $box == $draft_folder ||
isBoxBelow($box, $draft_folder) );
222 * Expunges a mailbox, ie. delete all contents.
224 function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
227 $id = sqimap_message_list_squisher($id);
234 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
235 $response, $message, $uid);
238 if (is_array($read)) {
239 foreach ($read as $r) {
240 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
249 * Checks whether or not the specified mailbox exists
251 function sqimap_mailbox_exists ($imap_stream, $mailbox) {
252 if (!isset($mailbox) ||
empty($mailbox)) {
255 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mailbox),
256 true, $response, $message);
257 return isset($mbx[0]);
263 function sqimap_mailbox_select ($imap_stream, $mailbox) {
264 if ($mailbox == 'None') {
268 $read = sqimap_run_command($imap_stream, 'SELECT ' . sqimap_encode_mailbox_name($mailbox),
269 true, $response, $message);
271 for ($i = 0, $cnt = count($read); $i < $cnt; $i++
) {
272 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
273 $result[strtoupper($regs[1])] = $regs[2];
274 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
275 $result[strtoupper($regs[2])] = $regs[1];
277 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
278 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
279 $result['PERMANENTFLAGS'] = explode(' ',strtolower($regs[1]));
280 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
281 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
282 $result['FLAGS'] = explode(' ',strtolower($regs[1]));
286 if (!isset($result['PERMANENTFLAGS'])) {
287 $result['PERMANENTFLAGS'] = $result['FLAGS'];
289 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
290 $result['RIGHTS']=strtoupper($regs[1]);
299 function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
301 if (strtolower($type) == 'noselect') {
302 $mailbox .= $delimiter;
305 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' .
306 sqimap_encode_mailbox_name($mailbox),
307 true, $response, $message);
308 sqimap_subscribe ($imap_stream, $mailbox);
312 * Subscribes to an existing folder.
314 function sqimap_subscribe ($imap_stream, $mailbox,$debug=true) {
315 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' .
316 sqimap_encode_mailbox_name($mailbox),
317 $debug, $response, $message);
321 * Unsubscribes from an existing folder
323 function sqimap_unsubscribe ($imap_stream, $mailbox) {
324 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' .
325 sqimap_encode_mailbox_name($mailbox),
326 false, $response, $message);
330 * Deletes the given folder
332 function sqimap_mailbox_delete ($imap_stream, $mailbox) {
333 global $data_dir, $username;
334 sqimap_unsubscribe ($imap_stream, $mailbox);
335 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' .
336 sqimap_encode_mailbox_name($mailbox),
337 true, $response, $message);
338 if ($response !== 'OK') {
340 sqimap_subscribe ($imap_stream, $mailbox);
342 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
343 removePref($data_dir, $username, "thread_$mailbox");
344 removePref($data_dir, $username, "collapse_folder_$mailbox");
349 * Determines if the user is subscribed to the folder or not
351 function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
352 $boxesall = sqimap_mailbox_list ($imap_stream);
353 foreach ($boxesall as $ref) {
354 if ($ref['unformatted'] == $folder) {
364 function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
365 if ( $old_name != $new_name ) {
366 global $delimiter, $imap_server_type, $data_dir, $username;
367 if ( substr( $old_name, -1 ) == $delimiter ) {
368 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
369 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
370 $postfix = $delimiter;
375 $boxesall = sqimap_mailbox_list_all($imap_stream);
376 $cmd = 'RENAME ' . sqimap_encode_mailbox_name($old_name) .
377 ' ' . sqimap_encode_mailbox_name($new_name);
378 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
379 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
380 $oldpref_thread = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
381 $oldpref_collapse = getPref($data_dir, $username, 'collapse_folder_'.$old_name.$postfix);
382 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
383 removePref($data_dir, $username, 'collapse_folder_'.$old_name.$postfix);
384 sqimap_subscribe($imap_stream, $new_name.$postfix);
385 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref_thread);
386 setPref($data_dir, $username, 'collapse_folder_'.$new_name.$postfix, $oldpref_collapse);
387 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
388 $l = strlen( $old_name ) +
1;
391 foreach ($boxesall as $box) {
392 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
393 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
394 /* With Cyrus IMAPd >= 2.0 rename is recursive, so don't check for errors here */
395 if ($imap_server_type == 'cyrus') {
396 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
397 $data = sqimap_run_command($imap_stream, $cmd, false,
398 $response, $message);
400 $was_subscribed = sqimap_mailbox_is_subscribed($imap_stream, $box[$p]);
401 if ( $was_subscribed ) {
402 sqimap_unsubscribe($imap_stream, $box[$p]);
404 $oldpref_thread = getPref($data_dir, $username, 'thread_'.$box[$p]);
405 $oldpref_collapse = getPref($data_dir, $username, 'collapse_folder_'.$box[$p]);
406 removePref($data_dir, $username, 'thread_'.$box[$p]);
407 removePref($data_dir, $username, 'collapse_folder_'.$box[$p]);
408 if ( $was_subscribed ) {
409 sqimap_subscribe($imap_stream, $new_sub);
411 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref_thread);
412 setPref($data_dir, $username, 'collapse_folder_'.$new_sub, $oldpref_collapse);
413 do_hook_function('rename_or_delete_folder',
414 $args = array($box[$p], 'rename', $new_sub));
421 * Formats a mailbox into parts for the $boxesall array
425 * raw - Raw LIST/LSUB response from the IMAP server
426 * formatted - nicely formatted folder name
427 * unformatted - unformatted, but with delimiter at end removed
428 * unformatted-dm - folder name as it appears in raw response
429 * unformatted-disp - unformatted without $folder_prefix
431 function sqimap_mailbox_parse ($line, $line_lsub) {
432 global $folder_prefix, $delimiter;
434 /* Process each folder line */
435 for ($g = 0, $cnt = count($line); $g < $cnt; ++
$g) {
436 /* Store the raw IMAP reply */
437 if (isset($line[$g])) {
438 $boxesall[$g]['raw'] = $line[$g];
440 $boxesall[$g]['raw'] = '';
443 /* Count number of delimiters ($delimiter) in folder name */
444 $mailbox = /*trim(*/$line_lsub[$g]/*)*/;
445 $dm_count = substr_count($mailbox, $delimiter);
446 if (substr($mailbox, -1) == $delimiter) {
447 /* If name ends in delimiter, decrement count by one */
451 /* Format folder name, but only if it's a INBOX.* or has a parent. */
452 $boxesallbyname[$mailbox] = $g;
453 $parentfolder = readMailboxParent($mailbox, $delimiter);
454 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
455 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
456 (isset($boxesallbyname[$parentfolder]) &&
457 (strlen($parentfolder) > 0) ) ) {
458 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
460 $boxesall[$g]['formatted'] = str_repeat(' ', $indent);
462 $boxesall[$g]['formatted'] = '';
464 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
466 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
469 $boxesall[$g]['unformatted-dm'] = $mailbox;
470 if (substr($mailbox, -1) == $delimiter) {
471 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
473 $boxesall[$g]['unformatted'] = $mailbox;
474 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
475 $mailbox = substr($mailbox, strlen($folder_prefix));
477 $boxesall[$g]['unformatted-disp'] = $mailbox;
478 $boxesall[$g]['id'] = $g;
480 $boxesall[$g]['flags'] = array();
481 if (isset($line[$g])) {
482 ereg("\(([^)]*)\)",$line[$g],$regs);
483 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
484 // and $MDNSent <= last one doesn't have the \
485 // It's better to follow RFC3501 instead of using our own naming.
486 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
488 $boxesall[$g]['flags'] = explode(' ', $flags);
496 * Returns list of options (to be echoed into select statement
497 * based on available mailboxes and separators
498 * Caller should surround options with <select ...> </select> and
500 * $imap_stream - $imapConnection to query for mailboxes
501 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
502 * $folder_skip - array of folders to keep out of option list (compared in lower)
503 * $boxes - list of already fetched boxes (for places like folder panel, where
504 * you know these options will be shown 3 times in a row.. (most often unset).
505 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
506 * 'noselect' by default to remove unselectable mailboxes.
507 * 'noinferiors' used to filter out folders that can not contain subfolders.
508 * NULL to avoid flag check entirely.
509 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
510 * \NoSelect and \NoInferiors
511 * $use_long_format - override folder display preference and always show full folder name.
513 function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
514 $flag = 'noselect', $use_long_format = false ) {
515 global $username, $data_dir;
517 if ( $use_long_format ) {
518 $shorten_box_names = 0;
520 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF
);
524 $boxes = sqimap_mailbox_list($imap_stream);
527 foreach ($boxes as $boxes_part) {
528 if ($flag == NULL ||
(is_array($boxes_part['flags'])
529 && !in_array($flag, $boxes_part['flags']))) {
530 $box = $boxes_part['unformatted'];
532 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
535 $lowerbox = strtolower($box);
536 // mailboxes are casesensitive => inbox.sent != inbox.Sent
537 // nevermind, to many dependencies this should be fixed!
539 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
542 switch ($shorten_box_names)
544 case 2: /* delimited, style = 2 */
545 $box2 = str_replace('&nbsp;&nbsp;', '. ', htmlspecialchars($boxes_part['formatted']));
547 case 1: /* indent, style = 1 */
548 $box2 = str_replace('&nbsp;&nbsp;', ' ', htmlspecialchars($boxes_part['formatted']));
550 default: /* default, long names, style = 0 */
551 $box2 = str_replace(' ', ' ', htmlspecialchars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
555 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
556 $mbox_options .= '<option value="' . htmlspecialchars($box) .'" selected="selected">'.$box2.'</option>' . "\n";
558 $mbox_options .= '<option value="' . htmlspecialchars($box) .'">'.$box2.'</option>' . "\n";
562 return $mbox_options;
566 * Returns sorted mailbox lists in several different ways.
567 * See comment on sqimap_mailbox_parse() for info about the returned array.
568 * @param resource $imap_stream imap connection resource
569 * @param boolean $force force update of mailbox listing. available since 1.4.2 and 1.5.0
570 * @return array list of mailboxes
572 function sqimap_mailbox_list($imap_stream, $force=false) {
573 if (!sqgetGlobalVar('boxesnew',$boxesnew,SQ_SESSION
) ||
$force) {
574 global $data_dir, $username, $list_special_folders_first,
575 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
576 $move_to_trash, $move_to_sent, $save_as_draft,
577 $delimiter, $noselect_fix_enable, $imap_server_type,
578 $show_only_subscribed_folders;
579 $inbox_subscribed = false;
580 $listsubscribed = sqimap_capability($imap_stream,'LIST-SUBSCRIBED');
582 require_once(SM_PATH
. 'include/load_prefs.php');
584 if (!$show_only_subscribed_folders) {
586 } elseif ($listsubscribed) {
587 $lsub = 'LIST (SUBSCRIBED)';
592 if ($noselect_fix_enable) {
593 $lsub_args = "$lsub \"$folder_prefix\" \"*%\"";
595 $lsub_args = "$lsub \"$folder_prefix\" \"*\"";
598 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
599 true, $response, $message);
600 $lsub_ary = compact_mailboxes_response($lsub_ary);
602 $sorted_lsub_ary = array();
603 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++
) {
605 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
606 $sorted_lsub_ary[] = $temp_mailbox_name;
607 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
608 $inbox_subscribed = true;
612 /* natural sort mailboxes */
613 if (isset($sorted_lsub_ary)) {
614 usort($sorted_lsub_ary, 'strnatcasecmp');
617 * The LSUB response doesn't provide us information about \Noselect
618 * mail boxes. The LIST response does, that's why we need to do a LIST
619 * call to retrieve the flags for the mailbox
620 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
621 * in other words, we cannot rely on it.
623 $sorted_list_ary = array();
624 // if (!$listsubscribed) {
625 for ($i=0; $i < count($sorted_lsub_ary); $i++
) {
626 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
627 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
630 $mbx = $sorted_lsub_ary[$i];
633 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mbx),
634 true, $response, $message);
636 $read = compact_mailboxes_response($read);
638 if (isset($read[0])) {
639 $sorted_list_ary[$i] = $read[0];
641 $sorted_list_ary[$i] = '';
646 * Just in case they're not subscribed to their inbox,
647 * we'll get it for them anyway
649 if (!$inbox_subscribed) {
650 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" "INBOX"',
651 true, $response, $message);
652 $sorted_list_ary[] = implode('',compact_mailboxes_response($inbox_ary));
653 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
656 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
658 /* Now, lets sort for special folders */
659 $boxesnew = $used = array();
662 $cnt = count($boxesall);
663 $used = array_pad($used,$cnt,false);
664 for($k = 0; $k < $cnt; ++
$k) {
665 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
666 $boxesnew[] = $boxesall[$k];
671 /* List special folders and their subfolders, if requested. */
672 if ($list_special_folders_first) {
673 for($k = 0; $k < $cnt; ++
$k) {
674 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
675 $boxesnew[] = $boxesall[$k];
681 /* Find INBOX's children */
682 for($k = 0; $k < $cnt; ++
$k) {
683 if (isBoxBelow(strtolower($boxesall[$k]['unformatted']), 'inbox') &&
684 strtolower($boxesall[$k]['unformatted']) <> 'inbox') {
685 $boxesnew[] = $boxesall[$k];
690 /* Rest of the folders */
691 for($k = 0; $k < $cnt; $k++
) {
693 $boxesnew[] = $boxesall[$k];
696 sqsession_register($boxesnew,'boxesnew');
702 * Returns a list of all folders, subscribed or not
704 function sqimap_mailbox_list_all($imap_stream) {
705 global $list_special_folders_first, $folder_prefix, $delimiter;
707 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
708 $read_ary = compact_mailboxes_response($read_ary);
711 $fld_pre_length = strlen($folder_prefix);
712 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++
) {
713 /* Store the raw IMAP reply */
714 $boxes[$g]['raw'] = $read_ary[$i];
716 /* Count number of delimiters ($delimiter) in folder name */
717 $mailbox = find_mailbox_name($read_ary[$i]);
718 $dm_count = substr_count($mailbox, $delimiter);
719 if (substr($mailbox, -1) == $delimiter) {
720 /* If name ends in delimiter - decrement count by one */
724 /* Format folder name, but only if it's a INBOX.* or has a parent. */
725 $boxesallbyname[$mailbox] = $g;
726 $parentfolder = readMailboxParent($mailbox, $delimiter);
727 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
728 (ereg('^'.$folder_prefix, $mailbox)) ||
729 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
731 $boxes[$g]['formatted'] = str_repeat(' ', $dm_count);
733 $boxes[$g]['formatted'] = '';
735 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
737 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
740 $boxes[$g]['unformatted-dm'] = $mailbox;
741 if (substr($mailbox, -1) == $delimiter) {
742 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
744 $boxes[$g]['unformatted'] = $mailbox;
745 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
747 $boxes[$g]['id'] = $g;
749 /* Now lets get the flags for this mailbox */
750 $read_mlbx = $read_ary[$i];
751 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+
1);
752 $flags = substr($flags, 0, strpos($flags, ')'));
753 $flags = str_replace('\\', '', $flags);
754 $flags = trim(strtolower($flags));
756 $boxes[$g]['flags'] = explode(' ', $flags);
758 $boxes[$g]['flags'] = array();
762 if(is_array($boxes)) {
769 function sqimap_mailbox_tree($imap_stream) {
770 global $default_folder_prefix;
772 global $data_dir, $username, $list_special_folders_first,
773 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
774 $imap_server_type, $show_only_subscribed_folders;
777 $noinferiors = false;
779 require_once(SM_PATH
. 'include/load_prefs.php');
781 if ($show_only_subscribed_folders) {
788 $lsub_ary = sqimap_run_command ($imap_stream, "$lsub_cmd \"$folder_prefix\" \"*\"",
789 true, $response, $message);
790 $lsub_ary = compact_mailboxes_response($lsub_ary);
792 /* Check to see if we have an INBOX */
795 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++
) {
796 if (preg_match("/^\*\s+$lsub_cmd.*\s\"?INBOX\"?[^(\/\.)].*$/i",$lsub_ary[$i])) {
797 $lsub_ary[$i] = strtoupper($lsub_ary[$i]);
798 // in case of an unsubscribed inbox an imap server can
799 // return the inbox in the lsub results with a \NoSelect
801 if (!preg_match("/\*\s+$lsub_cmd\s+\(.*\\\\NoSelect.*\).*/i",$lsub_ary[$i])) {
804 // remove the result and request it again with a list
805 // response at a later stage.
806 unset($lsub_ary[$i]);
807 // re-index the array otherwise the addition of the LIST
808 // response will fail in PHP 4.1.2 and probably other older versions
809 $lsub_ary = array_values($lsub_ary);
815 if ($has_inbox == false) {
816 // do a list request for inbox because we should always show
817 // inbox even if the user isn't subscribed to it.
818 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" "INBOX"',
819 true, $response, $message);
820 $inbox_ary = compact_mailboxes_response($inbox_ary);
821 if (count($inbox_ary)) {
822 $lsub_ary[] = $inbox_ary[0];
827 * Section about removing the last element was removed
828 * We don't return "* OK" anymore from sqimap_read_data
831 $sorted_lsub_ary = array();
832 $cnt = count($lsub_ary);
833 for ($i = 0; $i < $cnt; $i++
) {
834 $mbx = find_mailbox_name($lsub_ary[$i]);
836 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
837 if ($imap_server_type != "uw") {
838 $noselect = check_is_noselect($lsub_ary[$i]);
839 $noinferiors = check_is_noinferiors($lsub_ary[$i]);
841 if (substr($mbx, -1) == $delimiter) {
842 $mbx = substr($mbx, 0, strlen($mbx) - 1);
844 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
846 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
847 if ($imap_server_type == "uw") {
850 // prepare an array with queries
851 foreach ($sorted_lsub_ary as $aMbx) {
852 $mbx = stripslashes($aMbx['mbx']);
853 sqimap_prepare_pipelined_query('LIST "" ' . sqimap_encode_mailbox_name($mbx), $tag, $aQuery, false);
856 $sorted_lsub_ary = array();
857 // execute all the queries at once
858 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
859 foreach($aTag as $tag => $mbx) {
860 if ($aServerResponse[$tag] == 'OK') {
861 $sResponse = implode('', $aResponse[$tag]);
862 $noselect = check_is_noselect($sResponse);
863 $noinferiors = check_is_noinferiors($sResponse);
864 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
867 $cnt = count($sorted_lsub_ary);
869 $sorted_lsub_ary = array_values($sorted_lsub_ary);
870 usort($sorted_lsub_ary, 'mbxSort');
871 $boxestree = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
876 function mbxSort($a, $b) {
877 return strnatcasecmp($a['mbx'], $b['mbx']);
880 function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
881 global $data_dir, $username, $list_special_folders_first,
882 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
883 $move_to_trash, $move_to_sent, $save_as_draft,
884 $delimiter, $imap_server_type;
886 // $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
888 /* create virtual root node */
889 $mailboxes= new mailboxes();
890 $mailboxes->is_root
= true;
895 if (isset($folder_prefix) && ($folder_prefix != '')) {
896 $start = substr_count($folder_prefix,$delimiter);
897 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
898 $mailboxes->mailboxname_full
= substr($folder_prefix,0, (strlen($folder_prefix)-1));
900 $mailboxes->mailboxname_full
= $folder_prefix;
903 $mailboxes->mailboxname_sub
= $mailboxes->mailboxname_full
;
908 $cnt = count($mbx_ary);
909 for ($i=0; $i < $cnt; $i++
) {
910 if ($mbx_ary[$i]['mbx'] !='' ) {
911 $mbx = new mailboxes();
912 $mailbox = $mbx_ary[$i]['mbx'];
915 sent subfolders messes up using existing code as subfolders
916 were marked, but the parents were ordered somewhere else in
917 the list, despite having "special folders at top" option set.
918 Need a better method than this.
921 if ($mailbox == 'INBOX') {
922 $mbx->is_special = true;
923 } elseif (stristr($trash_folder , $mailbox)) {
924 $mbx->is_special = true;
925 } elseif (stristr($sent_folder , $mailbox)) {
926 $mbx->is_special = true;
927 } elseif (stristr($draft_folder , $mailbox)) {
928 $mbx->is_special = true;
933 $mbx->is_inbox = true;
934 $mbx->is_special = true;
935 $mbx_ary[$i]['noselect'] = false;
938 $mbx->is_trash = true;
939 $mbx->is_special = true;
942 $mbx->is_sent = true;
943 $mbx->is_special = true;
946 $mbx->is_draft = true;
947 $mbx->is_special = true;
951 $mbx->is_special |
= ($mbx->is_inbox
= (strtoupper($mailbox) == 'INBOX'));
952 $mbx->is_special |
= ($mbx->is_trash
= isTrashMailbox($mailbox));
953 $mbx->is_special |
= ($mbx->is_sent
= isSentMailbox($mailbox));
954 $mbx->is_special |
= ($mbx->is_draft
= isDraftMailbox($mailbox));
955 if (!$mbx->is_special
)
956 $mbx->is_special
= boolean_hook_function('special_mailbox', $mailbox, 1);
958 if (isset($mbx_ary[$i]['unseen'])) {
959 $mbx->unseen
= $mbx_ary[$i]['unseen'];
961 if (isset($mbx_ary[$i]['nummessages'])) {
962 $mbx->total
= $mbx_ary[$i]['nummessages'];
965 $mbx->is_noselect
= $mbx_ary[$i]['noselect'];
966 $mbx->is_noinferiors
= $mbx_ary[$i]['noinferiors'];
968 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
970 $mbx->mailboxname_sub
= substr($mbx_ary[$i]['mbx'],$r_del_pos+
1);
971 } else { /* mailbox is root folder */
972 $mbx->mailboxname_sub
= $mbx_ary[$i]['mbx'];
974 $mbx->mailboxname_full
= $mbx_ary[$i]['mbx'];
976 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
979 sqimap_utf7_decode_mbx_tree($mailboxes);
980 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
984 function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
985 if (strtoupper($mbx_tree->mailboxname_full
) == 'INBOX')
986 $mbx_tree->mailboxname_sub
= _("INBOX");
988 $mbx_tree->mailboxname_sub
= imap_utf7_decode_local($mbx_tree->mailboxname_sub
);
989 if ($mbx_tree->mbxs
) {
990 $iCnt = count($mbx_tree->mbxs
);
991 for ($i=0;$i<$iCnt;++
$i) {
992 $mbxs_tree->mbxs
[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs
[$i]);
998 function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
1000 $aMbxs[] =& $mbx_tree;
1001 if ($mbx_tree->mbxs
) {
1002 $iCnt = count($mbx_tree->mbxs
);
1003 for ($i=0;$i<$iCnt;++
$i) {
1004 sqimap_tree_to_ref_array($mbx_tree->mbxs
[$i],$aMbxs);
1009 function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
1010 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
1011 $aMbxs = $aQuery = array();
1012 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
1013 // remove the root node
1014 array_shift($aMbxs);
1016 if($unseen_notify == 3) {
1017 $cnt = count($aMbxs);
1018 for($i=0;$i<$cnt;++
$i) {
1019 $oMbx =& $aMbxs[$i];
1020 if (!$oMbx->is_noselect
) {
1021 $mbx = $oMbx->mailboxname_full
;
1022 if ($unseen_type == 2 ||
1023 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
1024 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
1026 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (UNSEEN)';
1028 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
1030 $oMbx->unseen
= $oMbx->total
= false;
1034 $aMbxs[$i] =& $oMbx;
1036 // execute all the queries at once
1037 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
1038 $cnt = count($aMbxs);
1039 for($i=0;$i<$cnt;++
$i) {
1040 $oMbx =& $aMbxs[$i];
1042 if ($tag && $aServerResponse[$tag] == 'OK') {
1043 $sResponse = implode('', $aResponse[$tag]);
1044 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
1045 $oMbx->unseen
= $regs[1];
1047 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
1048 $oMbx->total
= $regs[1];
1053 } else if ($unseen_notify == 2) { // INBOX only
1054 $cnt = count($aMbxs);
1055 for($i=0;$i<$cnt;++
$i) {
1056 $oMbx =& $aMbxs[$i];
1057 if (strtoupper($oMbx->mailboxname_full
) == 'INBOX' ||
1058 ($move_to_trash && $oMbx->mailboxname_full
== $trash_folder)) {
1059 if ($unseen_type == 2 ||
1060 ($oMbx->mailboxname_full
== $trash_folder && $move_to_trash)) {
1061 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full
);
1062 $oMbx->unseen
= $aStatus['UNSEEN'];
1063 $oMbx->total
= $aStatus['MESSAGES'];
1065 $oMbx->unseen
= sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full
);
1067 $aMbxs[$i] =& $oMbx;
1068 if (!$move_to_trash && $trash_folder) {
1071 // trash comes after INBOX
1072 if ($oMbx->mailboxname_full
== $trash_folder) {