Removed kind of insane check. The check mailbox is subscribed call retrieved
[squirrelmail.git] / functions / imap_mailbox.php
1 <?php
2
3 /**
4 * imap_mailbox.php
5 *
6 * Copyright (c) 1999-2004 The SquirrelMail Project Team
7 * Licensed under the GNU GPL. For full terms see the file COPYING.
8 *
9 * This impliments all functions that manipulate mailboxes
10 *
11 * @version $Id$
12 * @package squirrelmail
13 * @subpackage imap
14 */
15
16 /** UTF7 support */
17 require_once(SM_PATH . 'functions/imap_utf7_local.php');
18
19 global $boxesnew;
20
21 /**
22 * Mailboxes class
23 *
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
31 */
32
33 class mailboxes {
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;
38
39 function addMbx($mbx, $delimiter, $start, $specialfirst) {
40 $ary = explode($delimiter, $mbx->mailboxname_full);
41 $mbx_parent =& $this;
42 for ($i = $start, $c = count($ary)-1; $i < $c; $i++) {
43 $mbx_childs =& $mbx_parent->mbxs;
44 $found = false;
45 if ($mbx_childs) {
46 foreach ($mbx_childs as $key => $parent) {
47 if ($parent->mailboxname_sub == $ary[$i]) {
48 $mbx_parent =& $mbx_parent->mbxs[$key];
49 $found = true;
50 break;
51 }
52 }
53 }
54 if (!$found) {
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];
58 } else {
59 $no_select_mbx->mailboxname_full = $ary[$i];
60 }
61 $no_select_mbx->mailboxname_sub = $ary[$i];
62 $no_select_mbx->is_noselect = true;
63 $mbx_parent->mbxs[] = $no_select_mbx;
64 $i--;
65 }
66 }
67 $mbx_parent->mbxs[] = $mbx;
68 if ($mbx->is_special && $specialfirst) {
69 usort($mbx_parent->mbxs, 'sortSpecialMbx');
70 }
71 }
72 }
73
74 function sortSpecialMbx($a, $b) {
75 if ($a->is_inbox) {
76 $acmp = '0'. $a->mailboxname_full;
77 } else if ($a->is_special) {
78 $acmp = '1'. $a->mailboxname_full;
79 } else {
80 $acmp = '2' . $a->mailboxname_full;
81 }
82 if ($b->is_inbox) {
83 $bcmp = '0'. $b->mailboxname_full;
84 }else if ($b->is_special) {
85 $bcmp = '1' . $b->mailboxname_full;
86 } else {
87 $bcmp = '2' . $b->mailboxname_full;
88 }
89 return strnatcasecmp($acmp, $bcmp);
90 }
91
92 function compact_mailboxes_response($ary)
93 {
94 /*
95 * Workaround for mailboxes returned as literal
96 * FIXME : Doesn't work if the mailbox name is multiple lines
97 * (larger then fgets buffer)
98 */
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]*)$",
102 $ary[$i], $regs)) {
103 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+1])) . '"' . $regs[2];
104 array_splice($ary, $i+1, 2);
105 }
106 }
107 }
108 /* remove duplicates and ensure array is contiguous */
109 return array_values(array_unique($ary));
110 }
111
112 /**
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)
116 */
117 function find_mailbox_name($line)
118 {
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));
122 return $regs[1];
123 }
124 return '';
125 }
126
127 /**
128 * @return bool whether this is a Noselect mailbox.
129 */
130 function check_is_noselect ($lsub_line) {
131 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i", $lsub_line);
132 }
133
134 /**
135 * @return bool whether this is a Noinferiors mailbox.
136 */
137 function check_is_noinferiors ($lsub_line) {
138 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noinferiors[^\)]*\)/i", $lsub_line);
139 }
140
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 */
146 function readMailboxParent($haystack, $needle) {
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
160 /**
161 * Check if $subbox is below the specified $parentbox
162 */
163 function isBoxBelow( $subbox, $parentbox ) {
164 global $delimiter;
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 }
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 }
181 }
182
183 /**
184 * Defines special mailboxes: given a mailbox name, it checks if this is a
185 * "special" one: INBOX, Trash, Sent or Draft.
186 */
187 function isSpecialMailbox( $box ) {
188 $ret = ( (strtolower($box) == 'inbox') ||
189 isTrashMailbox($box) || isSentMailbox($box) || isDraftMailbox($box) );
190
191 if ( !$ret ) {
192 $ret = boolean_hook_function('special_mailbox',$box,1);
193 }
194 return $ret;
195 }
196
197 /**
198 * @return bool whether this is a Trash folder
199 */
200 function isTrashMailbox ($box) {
201 global $trash_folder, $move_to_trash;
202 return $move_to_trash && $trash_folder &&
203 ( $box == $trash_folder || isBoxBelow($box, $trash_folder) );
204 }
205
206 /**
207 * @return bool whether this is a Sent folder
208 */
209 function isSentMailbox($box) {
210 global $sent_folder, $move_to_sent;
211 return $move_to_sent && $sent_folder &&
212 ( $box == $sent_folder || isBoxBelow($box, $sent_folder) );
213 }
214
215 /**
216 * @return bool whether this is a Draft folder
217 */
218 function isDraftMailbox($box) {
219 global $draft_folder, $save_as_draft;
220 return $save_as_draft &&
221 ( $box == $draft_folder || isBoxBelow($box, $draft_folder) );
222 }
223
224 /**
225 * Expunges a mailbox, ie. delete all contents.
226 */
227 function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
228 if ($id) {
229 if (is_array($id)) {
230 $id = sqimap_message_list_squisher($id);
231 }
232 $id = ' '.$id;
233 $uid = TRUE;
234 } else {
235 $uid = false;
236 }
237 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
238 $response, $message, $uid);
239 $cnt = 0;
240
241 if (is_array($read)) {
242 foreach ($read as $r) {
243 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
244 $cnt++;
245 }
246 }
247 }
248 return $cnt;
249 }
250
251 /**
252 * Expunge specified message, updated $msgs and $msort
253 *
254 * Until Marc and I come up with a better way to maintain
255 * these stupid arrays, we'll use this wrapper function to
256 * remove the message with the matching UID .. the order
257 * won't be changed - the array element for the message
258 * will just be removed.
259 */
260 function sqimap_mailbox_expunge_dmn($message_id)
261 {
262 global $msgs, $msort, $sort, $imapConnection,
263 $mailbox, $mbx_response, $auto_expunge,
264 $sort, $allow_server_sort, $thread_sort_messages, $allow_thread_sort,
265 $username, $data_dir;
266 $cnt = 0;
267
268 // Got to grab this out of prefs, since it isn't saved from mailbox_view.php
269 if ($allow_thread_sort) {
270 $thread_sort_messages = getPref($data_dir, $username, "thread_$mailbox",0);
271 }
272
273 for ($i = 0; $i < count($msort); $i++) {
274 if ($msgs[$i]['ID'] == $message_id) {
275 break;
276 }
277 }
278
279 if ( isset($msgs) ) {
280 unset($msgs[$i]);
281 $msgs = array_values($msgs);
282 sqsession_register($msgs, 'msgs');
283 }
284
285 if ( isset($msort) ) {
286 unset($msort[$i]);
287 $msort = array_values($msort);
288 sqsession_register($msort, 'msort');
289 }
290
291 if ($auto_expunge) {
292 $cnt = sqimap_mailbox_expunge($imapConnection, $mailbox, true);
293 }
294
295 // And after all that mucking around, update the sort list!
296 // Remind me why the hell we need those two arrays again?!
297 if ( $allow_thread_sort && $thread_sort_messages ) {
298 $server_sort_array = get_thread_sort($imapConnection);
299 } elseif ( $allow_server_sort ) {
300 $server_sort_array = sqimap_get_sort_order($imapConnection, $sort, $mbx_response);
301 } else {
302 $server_sort_array = sqimap_get_php_sort_order($imapConnection, $mbx_response);
303 }
304 return $cnt;
305 }
306
307 /**
308 * Checks whether or not the specified mailbox exists
309 */
310 function sqimap_mailbox_exists ($imap_stream, $mailbox) {
311 if (!isset($mailbox) || empty($mailbox)) {
312 return false;
313 }
314 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mailbox),
315 true, $response, $message);
316 return isset($mbx[0]);
317 }
318
319 /**
320 * Selects a mailbox
321 */
322 function sqimap_mailbox_select ($imap_stream, $mailbox) {
323 if ($mailbox == 'None') {
324 return;
325 }
326
327 $read = sqimap_run_command($imap_stream, 'SELECT ' . sqimap_encode_mailbox_name($mailbox),
328 true, $response, $message);
329 $result = array();
330 for ($i = 0, $cnt = count($read); $i < $cnt; $i++) {
331 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
332 $result[strtoupper($regs[1])] = $regs[2];
333 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
334 $result[strtoupper($regs[2])] = $regs[1];
335 } else {
336 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
337 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
338 $result['PERMANENTFLAGS'] = $regs[1];
339 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
340 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
341 $result['FLAGS'] = $regs[1];
342 }
343 }
344 }
345 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
346 $result['RIGHTS']=$regs[1];
347 }
348
349 return $result;
350 }
351
352 /**
353 * Creates a folder.
354 */
355 function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
356 global $delimiter;
357 if (strtolower($type) == 'noselect') {
358 $mailbox .= $delimiter;
359 }
360
361 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' .
362 sqimap_encode_mailbox_name($mailbox),
363 true, $response, $message);
364 sqimap_subscribe ($imap_stream, $mailbox);
365 }
366
367 /**
368 * Subscribes to an existing folder.
369 */
370 function sqimap_subscribe ($imap_stream, $mailbox,$debug=true) {
371 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' .
372 sqimap_encode_mailbox_name($mailbox),
373 $debug, $response, $message);
374 }
375
376 /**
377 * Unsubscribes from an existing folder
378 */
379 function sqimap_unsubscribe ($imap_stream, $mailbox) {
380 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' .
381 sqimap_encode_mailbox_name($mailbox),
382 false, $response, $message);
383 }
384
385 /**
386 * Deletes the given folder
387 */
388 function sqimap_mailbox_delete ($imap_stream, $mailbox) {
389 global $data_dir, $username;
390 sqimap_unsubscribe ($imap_stream, $mailbox);
391 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' .
392 sqimap_encode_mailbox_name($mailbox),
393 true, $response, $message);
394 if ($response !== 'OK') {
395 // subscribe again
396 sqimap_subscribe ($imap_stream, $mailbox);
397 } else {
398 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
399 removePref($data_dir, $username, "thread_$mailbox");
400 }
401 }
402
403 /**
404 * Determines if the user is subscribed to the folder or not
405 */
406 function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
407 $boxesall = sqimap_mailbox_list ($imap_stream);
408 foreach ($boxesall as $ref) {
409 if ($ref['unformatted'] == $folder) {
410 return true;
411 }
412 }
413 return false;
414 }
415
416 /**
417 * Renames a mailbox.
418 */
419 function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
420 if ( $old_name != $new_name ) {
421 global $delimiter, $imap_server_type, $data_dir, $username;
422 if ( substr( $old_name, -1 ) == $delimiter ) {
423 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
424 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
425 $postfix = $delimiter;
426 } else {
427 $postfix = '';
428 }
429
430 $boxesall = sqimap_mailbox_list($imap_stream);
431 $cmd = 'RENAME ' . sqimap_encode_mailbox_name($old_name) .
432 ' ' . sqimap_encode_mailbox_name($new_name);
433 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
434 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
435 $oldpref = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
436 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
437 sqimap_subscribe($imap_stream, $new_name.$postfix);
438 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref);
439 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
440 $l = strlen( $old_name ) + 1;
441 $p = 'unformatted';
442
443 foreach ($boxesall as $box) {
444 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
445 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
446 if ($imap_server_type == 'cyrus') {
447 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
448 $data = sqimap_run_command($imap_stream, $cmd, true,
449 $response, $message);
450 }
451 sqimap_unsubscribe($imap_stream, $box[$p]);
452 $oldpref = getPref($data_dir, $username, 'thread_'.$box[$p]);
453 removePref($data_dir, $username, 'thread_'.$box[$p]);
454 sqimap_subscribe($imap_stream, $new_sub);
455 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref);
456 do_hook_function('rename_or_delete_folder',
457 $args = array($box[$p], 'rename', $new_sub));
458 }
459 }
460 }
461 }
462
463 /**
464 * Formats a mailbox into parts for the $boxesall array
465 *
466 * The parts are:
467 *
468 * raw - Raw LIST/LSUB response from the IMAP server
469 * formatted - nicely formatted folder name
470 * unformatted - unformatted, but with delimiter at end removed
471 * unformatted-dm - folder name as it appears in raw response
472 * unformatted-disp - unformatted without $folder_prefix
473 */
474 function sqimap_mailbox_parse ($line, $line_lsub) {
475 global $folder_prefix, $delimiter;
476
477 /* Process each folder line */
478 for ($g = 0, $cnt = count($line); $g < $cnt; ++$g) {
479 /* Store the raw IMAP reply */
480 if (isset($line[$g])) {
481 $boxesall[$g]['raw'] = $line[$g];
482 } else {
483 $boxesall[$g]['raw'] = '';
484 }
485
486 /* Count number of delimiters ($delimiter) in folder name */
487 $mailbox = /*trim(*/$line_lsub[$g]/*)*/;
488 $dm_count = substr_count($mailbox, $delimiter);
489 if (substr($mailbox, -1) == $delimiter) {
490 /* If name ends in delimiter, decrement count by one */
491 $dm_count--;
492 }
493
494 /* Format folder name, but only if it's a INBOX.* or has a parent. */
495 $boxesallbyname[$mailbox] = $g;
496 $parentfolder = readMailboxParent($mailbox, $delimiter);
497 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
498 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
499 (isset($boxesallbyname[$parentfolder]) &&
500 (strlen($parentfolder) > 0) ) ) {
501 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
502 if ($indent > 0) {
503 $boxesall[$g]['formatted'] = str_repeat('&nbsp;&nbsp;', $indent);
504 } else {
505 $boxesall[$g]['formatted'] = '';
506 }
507 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
508 } else {
509 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
510 }
511
512 $boxesall[$g]['unformatted-dm'] = $mailbox;
513 if (substr($mailbox, -1) == $delimiter) {
514 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
515 }
516 $boxesall[$g]['unformatted'] = $mailbox;
517 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
518 $mailbox = substr($mailbox, strlen($folder_prefix));
519 }
520 $boxesall[$g]['unformatted-disp'] = $mailbox;
521 $boxesall[$g]['id'] = $g;
522
523 $boxesall[$g]['flags'] = array();
524 if (isset($line[$g])) {
525 ereg("\(([^)]*)\)",$line[$g],$regs);
526 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
527 // and $MDNSent <= last one doesn't have the \
528 // It's better to follow RFC3501 instead of using our own naming.
529 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
530 if ($flags) {
531 $boxesall[$g]['flags'] = explode(' ', $flags);
532 }
533 }
534 }
535 return $boxesall;
536 }
537
538 /**
539 * Returns list of options (to be echoed into select statement
540 * based on available mailboxes and separators
541 * Caller should surround options with <SELECT..> </SELECT> and
542 * any formatting.
543 * $imap_stream - $imapConnection to query for mailboxes
544 * $show_selected - array containing list of mailboxes to pre-select (0 if none)
545 * $folder_skip - array of folders to keep out of option list (compared in lower)
546 * $boxes - list of already fetched boxes (for places like folder panel, where
547 * you know these options will be shown 3 times in a row.. (most often unset).
548 * $flag - flag to check for in mailbox flags, used to filter out mailboxes.
549 * 'noselect' by default to remove unselectable mailboxes.
550 * 'noinferiors' used to filter out folders that can not contain subfolders.
551 * NULL to avoid flag check entirely.
552 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
553 * \NoSelect and \NoInferiors
554 * $use_long_format - override folder display preference and always show full folder name.
555 */
556 function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
557 $flag = 'noselect', $use_long_format = false ) {
558 global $username, $data_dir;
559 $mbox_options = '';
560 if ( $use_long_format ) {
561 $shorten_box_names = 0;
562 } else {
563 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF);
564 }
565
566 if ($boxes == 0) {
567 $boxes = sqimap_mailbox_list($imap_stream);
568 }
569
570 foreach ($boxes as $boxes_part) {
571 if ($flag == NULL || !in_array($flag, $boxes_part['flags'])) {
572 $box = $boxes_part['unformatted'];
573
574 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
575 continue;
576 }
577 $lowerbox = strtolower($box);
578 // mailboxes are casesensitive => inbox.sent != inbox.Sent
579 // nevermind, to many dependencies this should be fixed!
580
581 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
582 $box2 = _("INBOX");
583 } else {
584 switch ($shorten_box_names)
585 {
586 case 2: /* delimited, style = 2 */
587 $box2 = str_replace('&nbsp;&nbsp;', '.&nbsp;', $boxes_part['formatted']);
588 break;
589 case 1: /* indent, style = 1 */
590 $box2 = $boxes_part['formatted'];
591 break;
592 default: /* default, long names, style = 0 */
593 $box2 = str_replace(' ', '&nbsp;', htmlspecialchars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
594 break;
595 }
596 }
597 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
598 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'" SELECTED>'.$box2.'</OPTION>' . "\n";
599 } else {
600 $mbox_options .= '<OPTION VALUE="' . htmlspecialchars($box) .'">'.$box2.'</OPTION>' . "\n";
601 }
602 }
603 }
604 return $mbox_options;
605 }
606
607 /**
608 * Returns sorted mailbox lists in several different ways.
609 * See comment on sqimap_mailbox_parse() for info about the returned array.
610 */
611 function sqimap_mailbox_list($imap_stream) {
612 echo "TEST<BR>";
613 global $default_folder_prefix;
614
615 if (!isset($boxesnew)) {
616 global $data_dir, $username, $list_special_folders_first,
617 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
618 $move_to_trash, $move_to_sent, $save_as_draft,
619 $delimiter, $noselect_fix_enable;
620
621 $inbox_in_list = false;
622 $inbox_subscribed = false;
623
624 require_once(SM_PATH . 'include/load_prefs.php');
625
626 if ($noselect_fix_enable) {
627 $lsub_args = "LSUB \"$folder_prefix\" \"*%\"";
628 } else {
629 $lsub_args = "LSUB \"$folder_prefix\" \"*\"";
630 }
631 /* LSUB array */
632 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
633 true, $response, $message);
634 $lsub_ary = compact_mailboxes_response($lsub_ary);
635
636 $sorted_lsub_ary = array();
637 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++) {
638 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
639 $sorted_lsub_ary[] = $temp_mailbox_name;
640 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
641 $inbox_subscribed = true;
642 }
643 }
644
645 /* natural sort mailboxes */
646 if (isset($sorted_lsub_ary)) {
647 usort($sorted_lsub_ary, 'strnatcasecmp');
648 }
649 /*
650 * The LSUB response doesn't provide us information about \Noselect
651 * mail boxes. The LIST response does, that's why we need to do a LIST
652 * call to retrieve the flags for the mailbox
653 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
654 * in other words, we cannot rely on it.
655 */
656 $sorted_list_ary = array();
657 for ($i=0; $i < count($sorted_lsub_ary); $i++) {
658 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
659 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
660 }
661 else {
662 $mbx = $sorted_lsub_ary[$i];
663 }
664 $mbx = stripslashes($mbx);
665 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mbx),
666 true, $response, $message);
667 $read = compact_mailboxes_response($read);
668 if (isset($read[0])) {
669 $sorted_list_ary[$i] = $read[0];
670 } else {
671 $sorted_list_ary[$i] = '';
672 }
673 }
674 /*
675 * Just in case they're not subscribed to their inbox,
676 * we'll get it for them anyway
677 */
678 if (!$inbox_subscribed) {
679 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" INBOX',
680 true, $response, $message);
681 $sorted_list_ary[] = implode('', compact_mailboxes_response($inbox_ary));
682 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
683 }
684
685 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
686
687 /* Now, lets sort for special folders */
688 $boxesnew = $used = array();
689
690 /* Find INBOX */
691 $cnt = count($boxesall);
692 $used = array_pad($used,$cnt,false);
693 for($k = 0; $k < $cnt; ++$k) {
694 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
695 $boxesnew[] = $boxesall[$k];
696 $used[$k] = true;
697 break;
698 }
699 }
700 /* List special folders and their subfolders, if requested. */
701 if ($list_special_folders_first) {
702 for($k = 0; $k < $cnt; ++$k) {
703 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
704 $boxesnew[] = $boxesall[$k];
705 $used[$k] = true;
706 }
707 }
708 }
709
710 /* Rest of the folders */
711 for($k = 0; $k < $cnt; $k++) {
712 if (!$used[$k]) {
713 $boxesnew[] = $boxesall[$k];
714 }
715 }
716 }
717
718 return $boxesnew;
719 }
720
721 /**
722 * Returns a list of all folders, subscribed or not
723 */
724 function sqimap_mailbox_list_all($imap_stream) {
725 global $list_special_folders_first, $folder_prefix, $delimiter;
726
727 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
728 $read_ary = compact_mailboxes_response($read_ary);
729
730 $g = 0;
731 $phase = 'inbox';
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];
736
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 */
742 $dm_count--;
743 }
744
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) ) ) {
751 if ($dm_count) {
752 $boxes[$g]['formatted'] = str_repeat('&nbsp;&nbsp;', $dm_count);
753 } else {
754 $boxes[$g]['formatted'] = '';
755 }
756 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
757 } else {
758 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
759 }
760
761 $boxes[$g]['unformatted-dm'] = $mailbox;
762 if (substr($mailbox, -1) == $delimiter) {
763 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
764 }
765 $boxes[$g]['unformatted'] = $mailbox;
766 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
767
768 $boxes[$g]['id'] = $g;
769
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));
776 if ($flags) {
777 $boxes[$g]['flags'] = explode(' ', $flags);
778 } else {
779 $boxes[$g]['flags'] = array();
780 }
781 $g++;
782 }
783 if(is_array($boxes)) {
784 sort ($boxes);
785 }
786
787 return $boxes;
788 }
789
790 function sqimap_mailbox_tree($imap_stream) {
791 global $boxesnew, $default_folder_prefix, $unseen_notify, $unseen_type;
792 if (!isset($boxesnew)) {
793
794 global $data_dir, $username, $list_special_folders_first,
795 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
796 $imap_server_type;
797
798
799 $inbox_in_list = false;
800 $inbox_subscribed = false;
801 $noselect = false;
802 $noinferiors = false;
803
804 require_once(SM_PATH . 'include/load_prefs.php');
805
806 /* LSUB array */
807 $lsub_ary = sqimap_run_command ($imap_stream, "LSUB \"$folder_prefix\" \"*\"",
808 true, $response, $message);
809 $lsub_ary = compact_mailboxes_response($lsub_ary);
810
811 /* Check to see if we have an INBOX */
812 $has_inbox = false;
813
814 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++) {
815 if (preg_match("/^\*\s+LSUB.*\s\"?INBOX\"?[^(\/\.)].*$/i",$lsub_ary[$i])) {
816 $lsub_ary[$i] = strtoupper($lsub_ary[$i]);
817 // in case of an unsubscribed inbox an imap server can
818 // return the inbox in the lsub results with a \NoSelect
819 // flag.
820 if (!preg_match("/\*\s+LSUB\s+\(.*\\\\NoSelect.*\).*/i",$lsub_ary[$i])) {
821 $has_inbox = true;
822 } else {
823 // remove the result and request it again with a list
824 // response at a later stage.
825 unset($lsub_ary[$i]);
826 // re-index the array otherwise the addition of the LIST
827 // response will fail in PHP 4.1.2 and probably other older versions
828 $lsub_ary = array_values($lsub_ary);
829 }
830 break;
831 }
832 }
833
834 if ($has_inbox == false) {
835 // do a list request for inbox because we should always show
836 // inbox even if the user isn't subscribed to it.
837 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" INBOX',
838 true, $response, $message);
839 $inbox_ary = compact_mailboxes_response($inbox_ary);
840 if (count($inbox_ary)) {
841 $lsub_ary[] = $inbox_ary[0];
842 }
843 }
844
845 /*
846 * Section about removing the last element was removed
847 * We don't return "* OK" anymore from sqimap_read_data
848 */
849
850 $sorted_lsub_ary = array();
851 $cnt = count($lsub_ary);
852 for ($i = 0; $i < $cnt; $i++) {
853 $mbx = find_mailbox_name($lsub_ary[$i]);
854
855 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
856 if ($imap_server_type != "uw") {
857 $noselect = check_is_noselect($lsub_ary[$i]);
858 $noinferiors = check_is_noinferiors($lsub_ary[$i]);
859 }
860 if (substr($mbx, -1) == $delimiter) {
861 $mbx = substr($mbx, 0, strlen($mbx) - 1);
862 }
863 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
864 }
865 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
866 if ($imap_server_type == "uw") {
867 $aQuery = array();
868 $aTag = array();
869 // prepare an array with queries
870 foreach ($sorted_lsub_ary as $aMbx) {
871 $mbx = stripslashes($aMbx['mbx']);
872 sqimap_prepare_pipelined_query('LIST "" ' . sqimap_encode_mailbox_name($mbx), $tag, $aQuery, false);
873 $aTag[$tag] = $mbx;
874 }
875 $sorted_lsub_ary = array();
876 // execute all the queries at once
877 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
878 foreach($aTag as $tag => $mbx) {
879 if ($aServerResponse[$tag] == 'OK') {
880 $sResponse = implode('', $aResponse[$tag]);
881 $noselect = check_is_noselect($sResponse);
882 $noinferiors = check_is_noinferiors($sResponse);
883 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
884 }
885 }
886 $cnt = count($sorted_lsub_ary);
887 }
888 $sorted_lsub_ary = array_values($sorted_lsub_ary);
889 array_multisort($sorted_lsub_ary, SORT_ASC, SORT_REGULAR);
890 $boxesnew = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
891 return $boxesnew;
892 }
893 }
894
895 function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
896 global $data_dir, $username, $list_special_folders_first,
897 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
898 $move_to_trash, $move_to_sent, $save_as_draft,
899 $delimiter, $imap_server_type;
900
901 $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
902
903 /* create virtual root node */
904 $mailboxes= new mailboxes();
905 $mailboxes->is_root = true;
906 $trail_del = false;
907 $start = 0;
908
909
910 if (isset($folder_prefix) && ($folder_prefix != '')) {
911 $start = substr_count($folder_prefix,$delimiter);
912 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
913 $trail_del = true;
914 $mailboxes->mailboxname_full = substr($folder_prefix,0, (strlen($folder_prefix)-1));
915 } else {
916 $mailboxes->mailboxname_full = $folder_prefix;
917 $start++;
918 }
919 $mailboxes->mailboxname_sub = $mailboxes->mailboxname_full;
920 } else {
921 $start = 0;
922 }
923
924 $cnt = count($mbx_ary);
925 for ($i=0; $i < $cnt; $i++) {
926 if ($mbx_ary[$i]['mbx'] !='' ) {
927 $mbx = new mailboxes();
928 $mailbox = $mbx_ary[$i]['mbx'];
929
930 /*
931 sent subfolders messes up using existing code as subfolders
932 were marked, but the parents were ordered somewhere else in
933 the list, despite having "special folders at top" option set.
934 Need a better method than this.
935 */
936 /*
937 if ($mailbox == 'INBOX') {
938 $mbx->is_special = true;
939 } elseif (stristr($trash_folder , $mailbox)) {
940 $mbx->is_special = true;
941 } elseif (stristr($sent_folder , $mailbox)) {
942 $mbx->is_special = true;
943 } elseif (stristr($draft_folder , $mailbox)) {
944 $mbx->is_special = true;
945 }
946
947 switch ($mailbox) {
948 case 'INBOX':
949 $mbx->is_inbox = true;
950 $mbx->is_special = true;
951 $mbx_ary[$i]['noselect'] = false;
952 break;
953 case $trash_folder:
954 $mbx->is_trash = true;
955 $mbx->is_special = true;
956 break;
957 case $sent_folder:
958 $mbx->is_sent = true;
959 $mbx->is_special = true;
960 break;
961 case $draft_folder:
962 $mbx->is_draft = true;
963 $mbx->is_special = true;
964 break;
965 }
966 */
967 $mbx->is_special |= ($mbx->is_inbox = (strtoupper($mailbox) == 'INBOX'));
968 $mbx->is_special |= ($mbx->is_trash = isTrashMailbox($mailbox));
969 $mbx->is_special |= ($mbx->is_sent = isSentMailbox($mailbox));
970 $mbx->is_special |= ($mbx->is_draft = isDraftMailbox($mailbox));
971 if (!$mbx->is_special)
972 $mbx->is_special = boolean_hook_function('special_mailbox', $mailbox, 1);
973
974 if (isset($mbx_ary[$i]['unseen'])) {
975 $mbx->unseen = $mbx_ary[$i]['unseen'];
976 }
977 if (isset($mbx_ary[$i]['nummessages'])) {
978 $mbx->total = $mbx_ary[$i]['nummessages'];
979 }
980
981 $mbx->is_noselect = $mbx_ary[$i]['noselect'];
982 $mbx->is_noinferiors = $mbx_ary[$i]['noinferiors'];
983
984 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
985 if ($r_del_pos) {
986 $mbx->mailboxname_sub = substr($mbx_ary[$i]['mbx'],$r_del_pos+1);
987 } else { /* mailbox is root folder */
988 $mbx->mailboxname_sub = $mbx_ary[$i]['mbx'];
989 }
990 $mbx->mailboxname_full = $mbx_ary[$i]['mbx'];
991
992 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
993 }
994 }
995 sqimap_utf7_decode_mbx_tree($mailboxes);
996 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
997 return $mailboxes;
998 }
999
1000 function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
1001 if (strtoupper($mbx_tree->mailboxname_full) == 'INBOX')
1002 $mbx_tree->mailboxname_sub = _("INBOX");
1003 else
1004 $mbx_tree->mailboxname_sub = imap_utf7_decode_local($mbx_tree->mailboxname_sub);
1005 if ($mbx_tree->mbxs) {
1006 $iCnt = count($mbx_tree->mbxs);
1007 for ($i=0;$i<$iCnt;++$i) {
1008 $mbxs_tree->mbxs[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs[$i]);
1009 }
1010 }
1011 }
1012
1013
1014 function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
1015 if ($mbx_tree)
1016 $aMbxs[] =& $mbx_tree;
1017 if ($mbx_tree->mbxs) {
1018 $iCnt = count($mbx_tree->mbxs);
1019 for ($i=0;$i<$iCnt;++$i) {
1020 sqimap_tree_to_ref_array($mbx_tree->mbxs[$i],$aMbxs);
1021 }
1022 }
1023 }
1024
1025 function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
1026 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
1027 $aMbxs = $aQuery = $aTag = array();
1028 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
1029 // remove the root node
1030 array_shift($aMbxs);
1031
1032 if($unseen_notify == 3) {
1033 $cnt = count($aMbxs);
1034 for($i=0;$i<$cnt;++$i) {
1035 $oMbx =& $aMbxs[$i];
1036 if (!$oMbx->is_noselect) {
1037 $mbx = $oMbx->mailboxname_full;
1038 if ($unseen_type == 2 ||
1039 ($move_to_trash && $oMbx->mailboxname_full == $trash_folder)) {
1040 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
1041 } else {
1042 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (UNSEEN)';
1043 }
1044 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
1045 } else {
1046 $oMbx->unseen = $oMbx->total = false;
1047 $tag = false;
1048 }
1049 $oMbx->tag = $tag;
1050 $aMbxs[$i] =& $oMbx;
1051 }
1052 // execute all the queries at once
1053 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
1054 $cnt = count($aMbxs);
1055 for($i=0;$i<$cnt;++$i) {
1056 $oMbx =& $aMbxs[$i];
1057 $tag = $oMbx->tag;
1058 if ($tag && $aServerResponse[$tag] == 'OK') {
1059 $sResponse = implode('', $aResponse[$tag]);
1060 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
1061 $oMbx->unseen = $regs[1];
1062 }
1063 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
1064 $oMbx->total = $regs[1];
1065 }
1066 }
1067 unset($oMbx->tag);
1068 }
1069 } else if ($unseen_notify == 2) { // INBOX only
1070 $cnt = count($aMbxs);
1071 for($i=0;$i<$cnt;++$i) {
1072 $oMbx =& $aMbxs[$i];
1073 if (strtoupper($oMbx->mailboxname_full) == 'INBOX' ||
1074 ($move_to_trash && $oMbx->mailboxname_full == $trash_folder)) {
1075 if ($unseen_type == 2 ||
1076 ($oMbx->mailboxname_full == $trash_folder && $move_to_trash)) {
1077 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full);
1078 $oMbx->unseen = $aStatus['UNSEEN'];
1079 $oMbx->total = $aStatus['MESSAGES'];
1080 } else {
1081 $oMbx->unseen = sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full);
1082 }
1083 $aMbxs[$i] =& $oMbx;
1084 if (!$move_to_trash && $trash_folder) {
1085 break;
1086 } else {
1087 // trash comes after INBOX
1088 if ($oMbx->mailboxname_full == $trash_folder) {
1089 break;
1090 }
1091 }
1092 }
1093 }
1094 }
1095 }
1096
1097 ?>