When using sent subfolders, the special folder option is overruled because
[squirrelmail.git] / functions / imap_mailbox.php
CommitLineData
59177427 1<?php
bccadd02 2
35586184 3/**
4 * imap_mailbox.php
5 *
76911253 6 * Copyright (c) 1999-2003 The SquirrelMail Project Team
35586184 7 * Licensed under the GNU GPL. For full terms see the file COPYING.
8 *
9 * This impliments all functions that manipulate mailboxes
10 *
11 * $Id$
12 */
334a77f8 13require_once(SM_PATH . 'functions/imap_utf7_local.php');
14
3411d4ec 15global $boxesnew;
1da22cda 16
1eb028a9 17/*
18 FIXME. This class should be extracted and placed in a separate file that
19 can be included before we start the session. That makes caching of the tree
20 possible. On a refresh mailboxes from left_main.php the only function that
21 should be called is the sqimap_get_status_mbx_tree. In case of subscribe
22 / rename / delete / new we have to create methods for adding/changing the
23 mailbox in the mbx_tree without the need for a refresh.
24*/
25
60b5724d 26class mailboxes {
ff245fbd 27 var $mailboxname_full = '', $mailboxname_sub= '', $is_noselect = false,
28 $is_special = false, $is_root = false, $is_inbox = false, $is_sent = false,
29 $is_trash = false, $is_draft = false, $mbxs = array(),
30 $unseen = false, $total = false;
31
32 function addMbx($mbx, $delimiter, $start, $specialfirst) {
33 $ary = explode($delimiter, $mbx->mailboxname_full);
ae7df16e 34 $mbx_parent =& $this;
ff245fbd 35 for ($i = $start, $c = count($ary)-1; $i < $c; $i++) {
ae7df16e 36 $mbx_childs =& $mbx_parent->mbxs;
ff245fbd 37 $found = false;
38 if ($mbx_childs) {
39 foreach ($mbx_childs as $key => $parent) {
40 if ($parent->mailboxname_sub == $ary[$i]) {
ae7df16e 41 $mbx_parent =& $mbx_parent->mbxs[$key];
ff245fbd 42 $found = true;
ae7df16e 43 break;
ff245fbd 44 }
45 }
46 }
47 if (!$found) {
48 $no_select_mbx = new mailboxes();
49 if (isset($mbx_parent->mailboxname_full) && $mbx_parent->mailboxname_full != '') {
50 $no_select_mbx->mailboxname_full = $mbx_parent->mailboxname_full.$delimiter.$ary[$i];
51 } else {
52 $no_select_mbx->mailboxname_full = $ary[$i];
53 }
587ec647 54 $no_select_mbx->mailboxname_sub = $ary[$i];
ff245fbd 55 $no_select_mbx->is_noselect = true;
56 $mbx_parent->mbxs[] = $no_select_mbx;
57 $i--;
58 }
59 }
60 $mbx_parent->mbxs[] = $mbx;
61 if ($mbx->is_special && $specialfirst) {
62 usort($mbx_parent->mbxs, 'sortSpecialMbx');
63 }
64 }
60b5724d 65}
66
67function sortSpecialMbx($a, $b) {
68 if ($a->is_inbox) {
ff245fbd 69 $acmp = '0'. $a->mailboxname_full;
60b5724d 70 } else if ($a->is_special) {
ff245fbd 71 $acmp = '1'. $a->mailboxname_full;
60b5724d 72 } else {
ff245fbd 73 $acmp = '2' . $a->mailboxname_full;
74 }
60b5724d 75 if ($b->is_inbox) {
ff245fbd 76 $bcmp = '0'. $b->mailboxname_full;
60b5724d 77 }else if ($b->is_special) {
ff245fbd 78 $bcmp = '1' . $b->mailboxname_full;
60b5724d 79 } else {
ff245fbd 80 $bcmp = '2' . $b->mailboxname_full;
60b5724d 81 }
82 if ($acmp == $bcmp) return 0;
ff245fbd 83 return ($acmp > $bcmp) ? 1: -1;
84}
60b5724d 85
bac13dd7 86/*
79e07c7e 87function find_mailbox_name ($mailbox) {
d42310bd 88 if (preg_match('/\*.+\"([^\r\n\"]*)\"[\s\r\n]*$/', $mailbox, $regs))
89 return $regs[1];
79e07c7e 90 if (ereg(" *\"([^\r\n\"]*)\"[ \r\n]*$", $mailbox, $regs))
91 return $regs[1];
92 ereg(" *([^ \r\n\"]*)[ \r\n]*$",$mailbox,$regs);
93 return $regs[1];
f73348a3 94}
bac13dd7 95*/
96
97// Extract the mailbox name from an untagged LIST (7.2.2) or LSUB (7.2.3) answer
98// * (LIST|LSUB) (<Flags list>) (NIL|"<separator atom>") <mailbox name string>\r\n
99// mailbox name in quoted string MUST be unquoted and stripslashed (sm API)
100function find_mailbox_name($line)
101{
102 if (preg_match('/^\* (?:LIST|LSUB) \([^\)]*\) (?:NIL|\"[^\"]*\") ([^\r\n]*)[\r\n]*$/i', $line, $regs)) {
103 if (substr($regs[1], 0, 1) == '"')
104 return stripslashes(substr($regs[1], 1, -1));
105 return $regs[1];
106 }
107 return '';
108}
109
110function encode_mailbox_name($what)
111{
112 if (ereg("[\"\\\r\n]", $what))
113 return '{' . strlen($what) . "}\r\n" . $what; /* 4.3 literal form */
114 return '"' . $what . '"'; /* 4.3 quoted string form */
115}
116
117function compact_mailboxes_response($ary)
118{
119 /*
120 * Workaround for mailboxes returned as literal
121 * FIXME : Doesn't work if the mailbox name is multiple lines
122 * (larger then fgets buffer)
123 */
124 for ($i = 0; $i < count($ary); $i++) {
125 if (isset($ary[$i + 1]) && substr($ary[$i], -3) == "}\r\n") {
126 if (ereg("^(\\* [A-Z]+.*)\\{[0-9]+\\}([ \n\r\t]*)$",
127 $ary[$i], $regs)) {
128 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+1])) . '"' . $regs[2];
129 array_splice($ary, $i+1, 2);
130 }
131 }
132 }
133 /* remove duplicates and ensure array is contiguous */
134 return array_values(array_unique($ary));
135}
f73348a3 136
137function check_is_noselect ($lsub_line) {
3698bd49 138 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i", $lsub_line);
79e07c7e 139}
140
97b1248c 141/**
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)
145 */
146function readMailboxParent($haystack, $needle) {
97b1248c 147 if ($needle == '') {
148 $ret = '';
149 } else {
150 $parts = explode($needle, $haystack);
151 $elem = array_pop($parts);
152 while ($elem == '' && count($parts)) {
153 $elem = array_pop($parts);
154 }
155 $ret = join($needle, $parts);
156 }
157 return( $ret );
158}
159
6a8e7cae 160/**
161 * Check if $subbox is below the specified $parentbox
162 */
163function isBoxBelow( $subbox, $parentbox ) {
cef054e4 164 global $delimiter;
6a8e7cae 165 /*
166 * Eliminate the obvious mismatch, where the
167 * subfolder path is shorter than that of the potential parent
168 */
169 if ( strlen($subbox) < strlen($parentbox) ) {
170 return false;
171 }
cef054e4 172 /* check for delimiter */
173 if (!substr($parentbox,-1) == $delimiter) {
174 $parentbox.=$delimiter;
175 }
176 if (substr($subbox,0,strlen($parentbox)) == $parentbox) {
177 return true;
178 } else {
179 return false;
180 }
1e18bf95 181}
182
3411d4ec 183/* Defines special mailboxes */
1e18bf95 184function isSpecialMailbox( $box ) {
1e18bf95 185 global $trash_folder, $sent_folder, $draft_folder,
65c3ec94 186 $move_to_trash, $move_to_sent, $save_as_draft;
1e18bf95 187
90de1755 188 $ret = ( (strtolower($box) == 'inbox') ||
6a8e7cae 189 isTrashMailbox($box) || isSentMailbox($box) || isDraftMailbox($box) );
90de1755 190
2586d588 191 if ( !$ret ) {
31524bcd 192 $ret = do_hook_function( 'special_mailbox', $box );
2586d588 193 }
3411d4ec 194 return $ret;
90de1755 195}
196
6a8e7cae 197function isTrashMailbox ($box) {
198 global $trash_folder, $move_to_trash;
199 return $move_to_trash && $trash_folder &&
200 ( $box == $trash_folder || isBoxBelow($box, $trash_folder) );
201}
202
203function isSentMailbox($box) {
204 global $sent_folder, $move_to_sent;
205 return $move_to_sent && $sent_folder &&
206 ( $box == $sent_folder || isBoxBelow($box, $sent_folder) );
207}
208
209function isDraftMailbox($box) {
210 global $draft_folder, $save_as_draft;
211 return $save_as_draft &&
212 ( $box == $draft_folder || isBoxBelow($box, $draft_folder) );
213}
214
3411d4ec 215/* Expunges a mailbox */
8f6505f6 216function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
63240b90 217 global $uid_support;
06b5c3ff 218 if ($id) {
ff245fbd 219 if (is_array($id)) {
220 $id = sqimap_message_list_squisher($id);
221 }
222 $id = ' '.$id;
223 $uid = $uid_support;
06b5c3ff 224 } else {
ff245fbd 225 $uid = false;
8f6505f6 226 }
06b5c3ff 227 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
228 $response, $message, $uid);
63240b90 229 $cnt = 0;
ff245fbd 230
231 if (is_array($read)) {
63240b90 232 foreach ($read as $r) {
ff245fbd 233 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
234 $cnt++;
235 }
63240b90 236 }
8f6505f6 237 }
ff245fbd 238 return $cnt;
43b698c7 239}
240
3411d4ec 241/* Checks whether or not the specified mailbox exists */
1da22cda 242function sqimap_mailbox_exists ($imap_stream, $mailbox) {
247f700e 243 if (!isset($mailbox) || empty($mailbox)) {
43b698c7 244 return false;
245 }
bac13dd7 246 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . encode_mailbox_name($mailbox),
3411d4ec 247 true, $response, $message);
43b698c7 248 return isset($mbx[0]);
249}
250
3411d4ec 251/* Selects a mailbox */
e4c6fe41 252function sqimap_mailbox_select ($imap_stream, $mailbox) {
43b698c7 253 global $auto_expunge;
f69feefe 254
ff245fbd 255 if ($mailbox == 'None') {
43b698c7 256 return;
257 }
f69feefe 258
bac13dd7 259 $read = sqimap_run_command($imap_stream, 'SELECT ' . encode_mailbox_name($mailbox),
3411d4ec 260 true, $response, $message);
e4c6fe41 261 $result = array();
ff245fbd 262 for ($i = 0, $cnt = count($read); $i < $cnt; $i++) {
e4c6fe41 263 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
ff245fbd 264 $result[strtoupper($regs[1])] = $regs[2];
265 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
266 $result[strtoupper($regs[2])] = $regs[1];
267 } else {
268 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
269 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
270 $result['PERMANENTFLAGS'] = $regs[1];
271 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
272 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
273 $result['FLAGS'] = $regs[1];
274 }
275 }
e4c6fe41 276 }
277 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
ff245fbd 278 $result['RIGHTS']=$regs[1];
e4c6fe41 279 }
f69feefe 280
e4c6fe41 281 if ($auto_expunge) {
ff245fbd 282 $tmp = sqimap_run_command($imap_stream, 'EXPUNGE', false, $a, $b);
43b698c7 283 }
e4c6fe41 284 return $result;
43b698c7 285}
286
3411d4ec 287/* Creates a folder */
288function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
43b698c7 289 global $delimiter;
290 if (strtolower($type) == 'noselect') {
3411d4ec 291 $mailbox .= $delimiter;
43b698c7 292 }
e429f014 293
bac13dd7 294 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' . encode_mailbox_name($mailbox),
3411d4ec 295 true, $response, $message);
43b698c7 296 sqimap_subscribe ($imap_stream, $mailbox);
297}
298
3411d4ec 299/* Subscribes to an existing folder */
300function sqimap_subscribe ($imap_stream, $mailbox) {
bac13dd7 301 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' . encode_mailbox_name($mailbox),
3411d4ec 302 true, $response, $message);
43b698c7 303}
304
3411d4ec 305/* Unsubscribes to an existing folder */
306function sqimap_unsubscribe ($imap_stream, $mailbox) {
bac13dd7 307 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' . encode_mailbox_name($mailbox),
3411d4ec 308 true, $response, $message);
43b698c7 309}
310
3411d4ec 311/* Deletes the given folder */
312function sqimap_mailbox_delete ($imap_stream, $mailbox) {
78cc4b12 313 global $data_dir, $username;
bac13dd7 314 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' . encode_mailbox_name($mailbox),
3411d4ec 315 true, $response, $message);
43b698c7 316 sqimap_unsubscribe ($imap_stream, $mailbox);
e429f014 317 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
78cc4b12 318 removePref($data_dir, $username, "thread_$mailbox");
43b698c7 319}
320
3411d4ec 321/* Determines if the user is subscribed to the folder or not */
1da22cda 322function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
1da22cda 323 $boxesall = sqimap_mailbox_list ($imap_stream);
324 foreach ($boxesall as $ref) {
43b698c7 325 if ($ref['unformatted'] == $folder) {
3411d4ec 326 return true;
43b698c7 327 }
328 }
329 return false;
330}
331
3411d4ec 332/* Renames a mailbox */
1c52ba77 333function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
3411d4ec 334 if ( $old_name != $new_name ) {
78cc4b12 335 global $delimiter, $imap_server_type, $data_dir, $username;
1c52ba77 336 if ( substr( $old_name, -1 ) == $delimiter ) {
337 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
338 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
339 $postfix = $delimiter;
1c52ba77 340 } else {
341 $postfix = '';
1c52ba77 342 }
68f2ce7a 343
648713af 344 $boxesall = sqimap_mailbox_list($imap_stream);
bac13dd7 345 $cmd = 'RENAME ' . encode_mailbox_name($old_name) . ' ' . encode_mailbox_name($new_name);
3411d4ec 346 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
1c52ba77 347 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
68f2ce7a 348 $oldpref = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
349 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
1c52ba77 350 sqimap_subscribe($imap_stream, $new_name.$postfix);
68f2ce7a 351 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref);
e429f014 352 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
648713af 353 $l = strlen( $old_name ) + 1;
354 $p = 'unformatted';
68f2ce7a 355
ff245fbd 356 foreach ($boxesall as $box) {
357 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
648713af 358 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
359 if ($imap_server_type == 'cyrus') {
68f2ce7a 360 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
3411d4ec 361 $data = sqimap_run_command($imap_stream, $cmd, true,
648713af 362 $response, $message);
1c52ba77 363 }
648713af 364 sqimap_unsubscribe($imap_stream, $box[$p]);
68f2ce7a 365 $oldpref = getPref($data_dir, $username, 'thread_'.$box[$p]);
366 removePref($data_dir, $username, 'thread_'.$box[$p]);
648713af 367 sqimap_subscribe($imap_stream, $new_sub);
68f2ce7a 368 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref);
369 do_hook_function('rename_or_delete_folder',
3411d4ec 370 $args = array($box[$p], 'rename', $new_sub));
1c52ba77 371 }
372 }
1c52ba77 373 }
1c52ba77 374}
43b698c7 375
3411d4ec 376/*
377 * Formats a mailbox into 4 parts for the $boxesall array
378 *
379 * The four parts are:
380 *
381 * raw - Raw LIST/LSUB response from the IMAP server
382 * formatted - nicely formatted folder name
383 * unformatted - unformatted, but with delimiter at end removed
384 * unformatted-dm - folder name as it appears in raw response
385 * unformatted-disp - unformatted without $folder_prefix
386 */
387function sqimap_mailbox_parse ($line, $line_lsub) {
43b698c7 388 global $folder_prefix, $delimiter;
3411d4ec 389
43b698c7 390 /* Process each folder line */
cef054e4 391 for ($g = 0, $cnt = count($line); $g < $cnt; ++$g) {
43b698c7 392 /* Store the raw IMAP reply */
393 if (isset($line[$g])) {
e429f014 394 $boxesall[$g]['raw'] = $line[$g];
ff245fbd 395 } else {
e429f014 396 $boxesall[$g]['raw'] = '';
43b698c7 397 }
3411d4ec 398
43b698c7 399 /* Count number of delimiters ($delimiter) in folder name */
ff245fbd 400 $mailbox = trim($line_lsub[$g]);
401 $dm_count = substr_count($mailbox, $delimiter);
43b698c7 402 if (substr($mailbox, -1) == $delimiter) {
3411d4ec 403 /* If name ends in delimiter, decrement count by one */
404 $dm_count--;
43b698c7 405 }
3411d4ec 406
407 /* Format folder name, but only if it's a INBOX.* or has a parent. */
1da22cda 408 $boxesallbyname[$mailbox] = $g;
43b698c7 409 $parentfolder = readMailboxParent($mailbox, $delimiter);
410 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
411 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
ff245fbd 412 (isset($boxesallbyname[$parentfolder]) &&
413 (strlen($parentfolder) > 0) ) ) {
414 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
43b698c7 415 if ($indent > 0) {
ff245fbd 416 $boxesall[$g]['formatted'] = str_repeat('&nbsp;&nbsp;', $indent);
417 } else {
1da22cda 418 $boxesall[$g]['formatted'] = '';
43b698c7 419 }
447b2166 420 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
ff245fbd 421 } else {
447b2166 422 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
43b698c7 423 }
90de1755 424
1da22cda 425 $boxesall[$g]['unformatted-dm'] = $mailbox;
43b698c7 426 if (substr($mailbox, -1) == $delimiter) {
8e9e8afa 427 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
43b698c7 428 }
1da22cda 429 $boxesall[$g]['unformatted'] = $mailbox;
43b698c7 430 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
631b9da3 431 $mailbox = substr($mailbox, strlen($folder_prefix));
43b698c7 432 }
1da22cda 433 $boxesall[$g]['unformatted-disp'] = $mailbox;
434 $boxesall[$g]['id'] = $g;
90de1755 435
1da22cda 436 $boxesall[$g]['flags'] = array();
43b698c7 437 if (isset($line[$g])) {
36dfb0c9 438 ereg("\(([^)]*)\)",$line[$g],$regs);
5c300c60 439 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
440 // and $MDNSent <= last one doesn't have the \
441 // It's better to follow RFC3501 instead of using our own naming.
1a7e1e97 442 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
43b698c7 443 if ($flags) {
1da22cda 444 $boxesall[$g]['flags'] = explode(' ', $flags);
43b698c7 445 }
446 }
447 }
1da22cda 448 return $boxesall;
43b698c7 449}
450
3411d4ec 451/*
a3439b27 452 * Sorting function used to sort mailbox names.
3411d4ec 453 * + Original patch from dave_michmerhuizen@yahoo.com
454 * + Allows case insensitivity when sorting folders
455 * + Takes care of the delimiter being sorted to the end, causing
456 * subfolders to be listed in below folders that are prefixed
457 * with their parent folders name.
458 *
459 * For example: INBOX.foo, INBOX.foobar, and INBOX.foo.bar
460 * Without special sort function: foobar between foo and foo.bar
461 * With special sort function: foobar AFTER foo and foo.bar :)
43b698c7 462 */
a3439b27 463function user_strcasecmp($a, $b) {
d42310bd 464 return strnatcasecmp($a, $b);
43b698c7 465}
466
be2d5495 467/*
468 * Returns list of options (to be echoed into select statement
469 * based on available mailboxes and separators
470 * Caller should surround options with <SELECT..> </SELECT> and
471 * any formatting.
472 * $imap_stream - $imapConnection to query for mailboxes
473 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
474 * $folder_skip - array of folders to keep out of option list (compared in lower)
475 * $boxes - list of already fetched boxes (for places like folder panel, where
476 * you know these options will be shown 3 times in a row.. (most often unset).
59a8e3e8 477 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
478 * 'noselect' by default to remove unselectable mailboxes.
479 * 'noinferiors' used to filter out folders that can not contain subfolders.
480 * NULL to avoid flag check entirely.
d0928dd5 481 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
482 * \NoSelect and \NoInferiors
59a8e3e8 483 * $use_long_format - override folder display preference and always show full folder name.
be2d5495 484 */
59a8e3e8 485function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
486 $flag = 'noselect', $use_long_format = false ) {
be2d5495 487 global $username, $data_dir;
488 $mbox_options = '';
45f836eb 489 if ( $use_long_format ) {
490 $shorten_box_names = 0;
491 } else {
492 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF);
493 }
ff245fbd 494
495 if ($boxes == 0) {
be2d5495 496 $boxes = sqimap_mailbox_list($imap_stream);
ff245fbd 497 }
59a8e3e8 498
be2d5495 499 foreach ($boxes as $boxes_part) {
59a8e3e8 500 if ($flag == NULL || !in_array($flag, $boxes_part['flags'])) {
be2d5495 501 $box = $boxes_part['unformatted'];
be2d5495 502
d0928dd5 503 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
be2d5495 504 continue;
505 }
d0928dd5 506 $lowerbox = strtolower($box);
5c300c60 507 // mailboxes are casesensitive => inbox.sent != inbox.Sent
508 // nevermind, to many dependencies this should be fixed!
509
d0928dd5 510 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
be2d5495 511 $box2 = _("INBOX");
d0928dd5 512 } else {
5c300c60 513 switch ($shorten_box_names)
514 {
515 case 2: /* delimited, style = 2 */
d0928dd5 516 $box2 = str_replace('&nbsp;&nbsp;', '.&nbsp;', $boxes_part['formatted']);
5c300c60 517 break;
d0928dd5 518 case 1: /* indent, style = 1 */
519 $box2 = $boxes_part['formatted'];
5c300c60 520 break;
d0928dd5 521 default: /* default, long names, style = 0 */
522 $box2 = str_replace(' ', '&nbsp;', imap_utf7_decode_local($boxes_part['unformatted-disp']));
5c300c60 523 break;
524 }
be2d5495 525 }
526 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
527 $mbox_options .= '<OPTION VALUE="'.$box.'" SELECTED>'.$box2.'</OPTION>' . "\n";
528 } else {
529 $mbox_options .= '<OPTION VALUE="'.$box.'">'.$box2.'</OPTION>' . "\n";
530 }
531 }
532 }
533 return $mbox_options;
534}
43b698c7 535
3411d4ec 536/*
537 * Returns sorted mailbox lists in several different ways.
538 * See comment on sqimap_mailbox_parse() for info about the returned array.
539 */
1da22cda 540function sqimap_mailbox_list($imap_stream) {
4b2fe13a 541 global $default_folder_prefix;
7e235a1a 542
ff245fbd 543 if (!isset($boxesnew)) {
3411d4ec 544 global $data_dir, $username, $list_special_folders_first,
7e235a1a 545 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
546 $move_to_trash, $move_to_sent, $save_as_draft,
ca85aabe 547 $delimiter, $noselect_fix_enable;
7e235a1a 548
3411d4ec 549 $inbox_in_list = false;
550 $inbox_subscribed = false;
7e235a1a 551
08185f2a 552 require_once(SM_PATH . 'include/load_prefs.php');
7e235a1a 553
ff245fbd 554 if ($noselect_fix_enable) {
555 $lsub_args = "LSUB \"$folder_prefix\" \"*%\"";
556 } else {
557 $lsub_args = "LSUB \"$folder_prefix\" \"*\"";
558 }
3411d4ec 559 /* LSUB array */
ca85aabe 560 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
3411d4ec 561 true, $response, $message);
bac13dd7 562 $lsub_ary = compact_mailboxes_response($lsub_ary);
7e235a1a 563
7e235a1a 564 $sorted_lsub_ary = array();
ff245fbd 565 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++) {
7e235a1a 566 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
567 $sorted_lsub_ary[] = $temp_mailbox_name;
cef054e4 568 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
3411d4ec 569 $inbox_subscribed = true;
7e235a1a 570 }
571 }
cef054e4 572
5c300c60 573 /* natural sort mailboxes */
7e235a1a 574 if (isset($sorted_lsub_ary)) {
575 usort($sorted_lsub_ary, 'user_strcasecmp');
576 }
5c300c60 577 /*
578 * The LSUB response doesn't provide us information about \Noselect
579 * mail boxes. The LIST response does, that's why we need to do a LIST
580 * call to retrieve the flags for the mailbox
cef054e4 581 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
582 * in other words, we cannot rely on it.
5c300c60 583 */
cef054e4 584 $sorted_list_ary = array();
ff245fbd 585 for ($i=0; $i < count($sorted_lsub_ary); $i++) {
7e235a1a 586 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
587 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
588 }
589 else {
590 $mbx = $sorted_lsub_ary[$i];
591 }
bac13dd7 592 $mbx = stripslashes($mbx);
593 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . encode_mailbox_name($mbx),
3411d4ec 594 true, $response, $message);
bac13dd7 595 $read = compact_mailboxes_response($read);
7e235a1a 596 if (isset($read[0])) {
597 $sorted_list_ary[$i] = $read[0];
cef054e4 598 } else {
7e235a1a 599 $sorted_list_ary[$i] = '';
600 }
7e235a1a 601 }
3411d4ec 602 /*
7e235a1a 603 * Just in case they're not subscribed to their inbox,
604 * we'll get it for them anyway
605 */
cef054e4 606 if (!$inbox_subscribed) {
bac13dd7 607 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" ' . encode_mailbox_name('INBOX'),
3411d4ec 608 true, $response, $message);
bac13dd7 609 $sorted_list_ary[] = implode('', compact_mailboxes_response($inbox_ary));
7e235a1a 610 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
611 }
612
613 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
614
3411d4ec 615 /* Now, lets sort for special folders */
7e235a1a 616 $boxesnew = $used = array();
617
618 /* Find INBOX */
ff245fbd 619 $cnt = count($boxesall);
5c300c60 620 $used = array_pad($used,$cnt,false);
cef054e4 621 for($k = 0; $k < $cnt; ++$k) {
ff245fbd 622 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
623 $boxesnew[] = $boxesall[$k];
3411d4ec 624 $used[$k] = true;
5c300c60 625 break;
7e235a1a 626 }
627 }
7e235a1a 628 /* List special folders and their subfolders, if requested. */
3411d4ec 629 if ($list_special_folders_first) {
cef054e4 630 for($k = 0; $k < $cnt; ++$k) {
ff245fbd 631 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
632 $boxesnew[] = $boxesall[$k];
633 $used[$k] = true;
7e235a1a 634 }
5c300c60 635 }
636 }
7e235a1a 637
7e235a1a 638 /* Rest of the folders */
ff245fbd 639 for($k = 0; $k < $cnt; $k++) {
640 if (!$used[$k]) {
641 $boxesnew[] = $boxesall[$k];
7e235a1a 642 }
643 }
43b698c7 644 }
3411d4ec 645 return $boxesnew;
43b698c7 646}
647
90de1755 648/*
649 * Returns a list of all folders, subscribed or not
650 */
1da22cda 651function sqimap_mailbox_list_all($imap_stream) {
3411d4ec 652 global $list_special_folders_first, $folder_prefix, $delimiter;
bac13dd7 653
654 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
655 $read_ary = compact_mailboxes_response($read_ary);
656
43b698c7 657 $g = 0;
90de1755 658 $phase = 'inbox';
7d82bceb 659 $fld_pre_length = strlen($folder_prefix);
ff245fbd 660 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++) {
780dd344 661 /* Store the raw IMAP reply */
662 $boxes[$g]['raw'] = $read_ary[$i];
90de1755 663
780dd344 664 /* Count number of delimiters ($delimiter) in folder name */
665 $mailbox = find_mailbox_name($read_ary[$i]);
666 $dm_count = substr_count($mailbox, $delimiter);
667 if (substr($mailbox, -1) == $delimiter) {
668 /* If name ends in delimiter - decrement count by one */
669 $dm_count--;
670 }
90de1755 671
780dd344 672 /* Format folder name, but only if it's a INBOX.* or has a parent. */
673 $boxesallbyname[$mailbox] = $g;
674 $parentfolder = readMailboxParent($mailbox, $delimiter);
675 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
676 (ereg('^'.$folder_prefix, $mailbox)) ||
677 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
678 if ($dm_count) {
679 $boxes[$g]['formatted'] = str_repeat('&nbsp;&nbsp;', $dm_count);
90de1755 680 } else {
780dd344 681 $boxes[$g]['formatted'] = '';
12d61439 682 }
780dd344 683 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
684 } else {
685 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
686 }
687
688 $boxes[$g]['unformatted-dm'] = $mailbox;
689 if (substr($mailbox, -1) == $delimiter) {
690 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
691 }
692 $boxes[$g]['unformatted'] = $mailbox;
693 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
694
695 $boxes[$g]['id'] = $g;
696
697 /* Now lets get the flags for this mailbox */
698 $read_mlbx = $read_ary[$i];
699 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+1);
700 $flags = substr($flags, 0, strpos($flags, ')'));
701 $flags = str_replace('\\', '', $flags);
702 $flags = trim(strtolower($flags));
703 if ($flags) {
704 $boxes[$g]['flags'] = explode(' ', $flags);
705 } else {
706 $boxes[$g]['flags'] = array();
43b698c7 707 }
708 $g++;
709 }
710 if(is_array($boxes)) {
e429f014 711 sort ($boxes);
43b698c7 712 }
90de1755 713
43b698c7 714 return $boxes;
715}
5bdd7223 716
60b5724d 717function sqimap_mailbox_tree($imap_stream) {
718 global $boxesnew, $default_folder_prefix, $unseen_notify, $unseen_type;
ff245fbd 719 if (!isset($boxesnew)) {
60b5724d 720
721 global $data_dir, $username, $list_special_folders_first,
a2e66c6d 722 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
723 $imap_server_type;
60b5724d 724
725
726 $inbox_in_list = false;
727 $inbox_subscribed = false;
f08ba804 728 $noselect = false;
60b5724d 729
08185f2a 730 require_once(SM_PATH . 'include/load_prefs.php');
60b5724d 731
732 /* LSUB array */
2c617aa5 733 $lsub_ary = sqimap_run_command ($imap_stream, "LSUB \"$folder_prefix\" \"*\"",
60b5724d 734 true, $response, $message);
bac13dd7 735 $lsub_ary = compact_mailboxes_response($lsub_ary);
60b5724d 736
9871bdaa 737 /* Check to see if we have an INBOX */
78bc908d 738 $has_inbox = false;
739
740 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++) {
43a31298 741 if (preg_match("/^\*\s+LSUB\s+(.*)\"?INBOX\"?[^(\/\.)].*$/",$lsub_ary[$i])) {
78bc908d 742 $has_inbox = true;
743 break;
744 }
745 }
746
747 if ($has_inbox == false) {
d94c8d61 748 $lsub_ary[] = '* LSUB () "' . $folder_prefix . '" INBOX';
78bc908d 749 }
750
60b5724d 751 /*
752 * Section about removing the last element was removed
753 * We don't return "* OK" anymore from sqimap_read_data
754 */
bac13dd7 755
60b5724d 756 $sorted_lsub_ary = array();
e4c6fe41 757 $cnt = count($lsub_ary);
ff245fbd 758 for ($i = 0; $i < $cnt; $i++) {
ff245fbd 759 $mbx = find_mailbox_name($lsub_ary[$i]);
a2e66c6d 760
483f9ef9 761 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
762 if ($imap_server_type != "uw") {
763 $noselect = check_is_noselect($lsub_ary[$i]);
a2e66c6d 764 }
ff245fbd 765 if (substr($mbx, -1) == $delimiter) {
766 $mbx = substr($mbx, 0, strlen($mbx) - 1);
767 }
768 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect);
60b5724d 769 }
5c300c60 770 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
483f9ef9 771 if ($imap_server_type == "uw") {
5c300c60 772 $aQuery = array();
773 $aTag = array();
774 // prepare an array with queries
775 foreach ($sorted_lsub_ary as $aMbx) {
bac13dd7 776 $mbx = stripslashes($aMbx['mbx']);
777 sqimap_prepare_pipelined_query('LIST "" ' . encode_mailbox_name($mbx), $tag, $aQuery, false);
5c300c60 778 $aTag[$tag] = $mbx;
779 }
780 $sorted_lsub_ary = array();
781 // execute all the queries at once
782 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
783 foreach($aTag as $tag => $mbx) {
784 if ($aServerResponse[$tag] == 'OK') {
785 $sResponse = implode('', $aResponse[$tag]);
786 $noselect = check_is_noselect($sResponse);
787 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect);
788 }
789 }
790 $cnt = count($sorted_lsub_ary);
483f9ef9 791 }
bac13dd7 792 $sorted_lsub_ary = array_values($sorted_lsub_ary);
483f9ef9 793 array_multisort($sorted_lsub_ary, SORT_ASC, SORT_REGULAR);
794 $boxesnew = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
795 return $boxesnew;
60b5724d 796 }
797}
798
483f9ef9 799function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
60b5724d 800 global $data_dir, $username, $list_special_folders_first,
801 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
802 $move_to_trash, $move_to_sent, $save_as_draft,
43a31298 803 $delimiter, $imap_server_type;
60b5724d 804
805 $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
ff245fbd 806
60b5724d 807 /* create virtual root node */
808 $mailboxes= new mailboxes();
809 $mailboxes->is_root = true;
ff245fbd 810 $trail_del = false;
78bc908d 811 $start = 0;
812
587ec647 813
43a31298 814 if (isset($folder_prefix) && ($folder_prefix != '')) {
ff245fbd 815 $start = substr_count($folder_prefix,$delimiter);
816 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
817 $trail_del = true;
818 $mailboxes->mailboxname_full = substr($folder_prefix,0, (strlen($folder_prefix)-1));
819 } else {
820 $mailboxes->mailboxname_full = $folder_prefix;
821 $start++;
822 }
823 $mailboxes->mailboxname_sub = $mailboxes->mailboxname_full;
824 } else {
825 $start = 0;
826 }
78bc908d 827
e4c6fe41 828 $cnt = count($mbx_ary);
829 for ($i=0; $i < $cnt; $i++) {
ff245fbd 830 if ($mbx_ary[$i]['mbx'] !='' ) {
831 $mbx = new mailboxes();
832 $mailbox = $mbx_ary[$i]['mbx'];
cc82c2b7 833
834 /* @@@ FIXME @@@
835 sent subfolders messes up using existing code as subfolders
836 were marked, but the parents were ordered somewhere else in
837 the list, despite having "special folders at top" option set.
838 Need a better method than this.
839
ff245fbd 840 switch ($mailbox) {
841 case 'INBOX':
842 $mbx->is_inbox = true;
843 $mbx->is_special = true;
844 break;
845 case $trash_folder:
846 $mbx->is_trash = true;
847 $mbx->is_special = true;
848 break;
849 case $sent_folder:
850 $mbx->is_sent = true;
851 $mbx->is_special = true;
852 break;
853 case $draft_folder:
854 $mbx->is_draft = true;
855 $mbx->is_special = true;
856 break;
857 }
cc82c2b7 858
859 /FIXME/
860 */
861
862 if ($mailbox == 'INBOX') {
863 $mbx->is_inbox = true;
864 $mbx->is_special = true;
865 } elseif (stristr($trash_folder , $mailbox)) {
866 $mbx->is_trash = true;
867 $mbx->is_special = true;
868 } elseif (stristr($sent_folder , $mailbox)) {
869 $mbx->is_sent = true;
870 $mbx->is_special = true;
871 } elseif (stristr($draft_folder , $mailbox)) {
872 $mbx->is_draft = true;
873 $mbx->is_special = true;
874 }
875
ff245fbd 876
877 if (isset($mbx_ary[$i]['unseen'])) {
878 $mbx->unseen = $mbx_ary[$i]['unseen'];
879 }
880 if (isset($mbx_ary[$i]['nummessages'])) {
881 $mbx->total = $mbx_ary[$i]['nummessages'];
882 }
883
884 $mbx->is_noselect = $mbx_ary[$i]['noselect'];
885
60b5724d 886 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
ff245fbd 887 if ($r_del_pos) {
587ec647 888 $mbx->mailboxname_sub = substr($mbx_ary[$i]['mbx'],$r_del_pos+1);
ff245fbd 889 } else { /* mailbox is root folder */
587ec647 890 $mbx->mailboxname_sub = $mbx_ary[$i]['mbx'];
ff245fbd 891 }
892 $mbx->mailboxname_full = $mbx_ary[$i]['mbx'];
38068e69 893
9871bdaa 894 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
ff245fbd 895 }
60b5724d 896 }
587ec647 897 sqimap_utf7_decode_mbx_tree($mailboxes);
483f9ef9 898 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
60b5724d 899 return $mailboxes;
900}
259faa39 901
587ec647 902function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
d94c8d61 903 if (strtoupper($mbx_tree->mailboxname_sub) == 'INBOX')
904 $mbx_tree->mailboxname_sub = _("INBOX");
905 else
906 $mbx_tree->mailboxname_sub = imap_utf7_decode_local($mbx_tree->mailboxname_sub);
587ec647 907 if ($mbx_tree->mbxs) {
908 $iCnt = count($mbx_tree->mbxs);
909 for ($i=0;$i<$iCnt;++$i) {
d94c8d61 910 $mbxs_tree->mbxs[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs[$i]);
587ec647 911 }
912 }
483f9ef9 913}
914
915
916function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
5c300c60 917 if ($mbx_tree)
483f9ef9 918 $aMbxs[] =& $mbx_tree;
919 if ($mbx_tree->mbxs) {
920 $iCnt = count($mbx_tree->mbxs);
921 for ($i=0;$i<$iCnt;++$i) {
5c300c60 922 sqimap_tree_to_ref_array($mbx_tree->mbxs[$i],$aMbxs);
483f9ef9 923 }
924 }
925}
926
927
928/* Define preferences for folder settings. */
929/* FIXME, we should load constants.php
930unseen_notify
931define('SMPREF_UNSEEN_NONE', 1);
932define('SMPREF_UNSEEN_INBOX', 2);
933define('SMPREF_UNSEEN_ALL', 3);
934
935define('SMPREF_UNSEEN_SPECIAL', 4); // Only special folders
936define('SMPREF_UNSEEN_NORMAL', 5); // Only normal folders
937
938unseen_type
939define('SMPREF_UNSEEN_ONLY', 1);
940define('SMPREF_UNSEEN_TOTAL', 2);
941*/
942
943function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
944 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
945 $aMbxs = $aQuery = $aTag = array();
946 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
947 // remove the root node
948 array_shift($aMbxs);
949
950 if($unseen_notify == 3) {
951 $cnt = count($aMbxs);
952 for($i=0;$i<$cnt;++$i) {
5c300c60 953 $oMbx =& $aMbxs[$i];
954 if (!$oMbx->is_noselect) {
483f9ef9 955 $mbx = $oMbx->mailboxname_full;
5c300c60 956 if ($unseen_type == 2 ||
957 ($move_to_trash && $oMbx->mailboxname_full == $trash_folder)) {
bac13dd7 958 $query = 'STATUS ' . encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
5c300c60 959 } else {
bac13dd7 960 $query = 'STATUS ' . encode_mailbox_name($mbx) . ' (UNSEEN)';
5c300c60 961 }
483f9ef9 962 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
5c300c60 963 } else {
964 $oMbx->unseen = $oMbx->total = false;
965 $tag = false;
966 }
967 $oMbx->tag = $tag;
968 $aMbxs[$i] =& $oMbx;
483f9ef9 969 }
970 // execute all the queries at once
971 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
972 $cnt = count($aMbxs);
973 for($i=0;$i<$cnt;++$i) {
5c300c60 974 $oMbx =& $aMbxs[$i];
975 $tag = $oMbx->tag;
976 if ($tag && $aServerResponse[$tag] == 'OK') {
977 $sResponse = implode('', $aResponse[$tag]);
483f9ef9 978 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
979 $oMbx->unseen = $regs[1];
980 }
981 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
982 $oMbx->total = $regs[1];
5c300c60 983 }
984 }
985 unset($oMbx->tag);
986 }
483f9ef9 987 } else if ($unseen_notify == 2) { // INBOX only
988 $cnt = count($aMbxs);
989 for($i=0;$i<$cnt;++$i) {
5c300c60 990 $oMbx =& $aMbxs[$i];
991 if (strtoupper($oMbx->mailboxname_full) == 'INBOX' ||
992 ($move_to_trash && $oMbx->mailboxname_full == $trash_folder)) {
993 if ($unseen_type == 2 ||
994 ($oMbx->mailboxname_full == $trash_folder && $move_to_trash)) {
995 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full);
996 $oMbx->unseen = $aStatus['UNSEEN'];
997 $oMbx->total = $aStatus['MESSAGES'];
998 } else {
999 $oMbx->unseen = sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full);
1000 }
1001 $aMbxs[$i] =& $oMbx;
1002 if (!$move_to_trash && $trash_folder) {
1003 break;
1004 } else {
1005 // trash comes after INBOX
1006 if ($oMbx->mailboxname_full == $trash_folder) {
1007 break;
1008 }
1009 }
1010 }
1011 }
1012 }
483f9ef9 1013}
587ec647 1014
648713af 1015?>