Check for flags column
[squirrelmail.git] / functions / imap_mailbox.php
CommitLineData
59177427 1<?php
62f7daa5 2
35586184 3/**
4 * imap_mailbox.php
5 *
6c84ba1e 6 * Copyright (c) 1999-2005 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 *
eb19bc67 11 * @version $Id$
d6c32258 12 * @package squirrelmail
eb19bc67 13 * @subpackage imap
35586184 14 */
d6c32258 15
16/** UTF7 support */
334a77f8 17require_once(SM_PATH . 'functions/imap_utf7_local.php');
18
3411d4ec 19global $boxesnew;
1da22cda 20
d6c32258 21/**
22 * Mailboxes class
4669e892 23 *
24 * FIXME. This class should be extracted and placed in a separate file that
d6c32258 25 * can be included before we start the session. That makes caching of the tree
4669e892 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
d6c32258 29 * mailbox in the mbx_tree without the need for a refresh.
30 * @package squirrelmail
dc027257 31 * @subpackage imap
32 * @since 1.3.0
33 */
60b5724d 34class mailboxes {
86c2763d 35 var $mailboxname_full = '', $mailboxname_sub= '', $is_noselect = false, $is_noinferiors = false,
ff245fbd 36 $is_special = false, $is_root = false, $is_inbox = false, $is_sent = false,
4669e892 37 $is_trash = false, $is_draft = false, $mbxs = array(),
ff245fbd 38 $unseen = false, $total = false;
39
40 function addMbx($mbx, $delimiter, $start, $specialfirst) {
41 $ary = explode($delimiter, $mbx->mailboxname_full);
ae7df16e 42 $mbx_parent =& $this;
ff245fbd 43 for ($i = $start, $c = count($ary)-1; $i < $c; $i++) {
ae7df16e 44 $mbx_childs =& $mbx_parent->mbxs;
ff245fbd 45 $found = false;
46 if ($mbx_childs) {
47 foreach ($mbx_childs as $key => $parent) {
48 if ($parent->mailboxname_sub == $ary[$i]) {
ae7df16e 49 $mbx_parent =& $mbx_parent->mbxs[$key];
ff245fbd 50 $found = true;
ae7df16e 51 break;
ff245fbd 52 }
53 }
54 }
55 if (!$found) {
56 $no_select_mbx = new mailboxes();
57 if (isset($mbx_parent->mailboxname_full) && $mbx_parent->mailboxname_full != '') {
58 $no_select_mbx->mailboxname_full = $mbx_parent->mailboxname_full.$delimiter.$ary[$i];
59 } else {
60 $no_select_mbx->mailboxname_full = $ary[$i];
61 }
587ec647 62 $no_select_mbx->mailboxname_sub = $ary[$i];
ff245fbd 63 $no_select_mbx->is_noselect = true;
64 $mbx_parent->mbxs[] = $no_select_mbx;
65 $i--;
66 }
67 }
68 $mbx_parent->mbxs[] = $mbx;
69 if ($mbx->is_special && $specialfirst) {
70 usort($mbx_parent->mbxs, 'sortSpecialMbx');
71 }
72 }
60b5724d 73}
74
dc027257 75/**
76 * array callback used for sorting in mailboxes class
77 * @param object $a
78 * @param object $b
3131bc8d 79 * @return integer see php strnatcasecmp()
dc027257 80 * @since 1.3.0
81 */
60b5724d 82function sortSpecialMbx($a, $b) {
83 if ($a->is_inbox) {
ff245fbd 84 $acmp = '0'. $a->mailboxname_full;
60b5724d 85 } else if ($a->is_special) {
ff245fbd 86 $acmp = '1'. $a->mailboxname_full;
60b5724d 87 } else {
ff245fbd 88 $acmp = '2' . $a->mailboxname_full;
89 }
60b5724d 90 if ($b->is_inbox) {
ff245fbd 91 $bcmp = '0'. $b->mailboxname_full;
60b5724d 92 }else if ($b->is_special) {
ff245fbd 93 $bcmp = '1' . $b->mailboxname_full;
60b5724d 94 } else {
ff245fbd 95 $bcmp = '2' . $b->mailboxname_full;
60b5724d 96 }
4669e892 97 return strnatcasecmp($acmp, $bcmp);
ff245fbd 98}
60b5724d 99
dc027257 100/**
101 * @param array $ary
102 * @return array
103 * @since 1.5.0
104 */
105function compact_mailboxes_response($ary) {
86c2763d 106 /*
107 * Workaround for mailboxes returned as literal
4669e892 108 * FIXME : Doesn't work if the mailbox name is multiple lines
86c2763d 109 * (larger then fgets buffer)
110 */
111 for ($i = 0, $iCnt=count($ary); $i < $iCnt; $i++) {
112 if (isset($ary[$i + 1]) && substr($ary[$i], -3) == "}\r\n") {
113 if (ereg("^(\\* [A-Z]+.*)\\{[0-9]+\\}([ \n\r\t]*)$",
114 $ary[$i], $regs)) {
115 $ary[$i] = $regs[1] . '"' . addslashes(trim($ary[$i+1])) . '"' . $regs[2];
116 array_splice($ary, $i+1, 2);
117 }
118 }
119 }
120 /* remove duplicates and ensure array is contiguous */
121 return array_values(array_unique($ary));
122}
123
48af4b64 124/**
125 * Extract the mailbox name from an untagged LIST (7.2.2) or LSUB (7.2.3) answer
126 * (LIST|LSUB) (<Flags list>) (NIL|"<separator atom>") <mailbox name string>\r\n
127 * mailbox name in quoted string MUST be unquoted and stripslashed (sm API)
dc027257 128 *
129 * Originally stored in functions/strings.php. Since 1.2.6 stored in
130 * functions/imap_mailbox.php
131 * @param string $line imap LIST/LSUB response line
132 * @return string mailbox name
48af4b64 133 */
dc027257 134function find_mailbox_name($line) {
bac13dd7 135 if (preg_match('/^\* (?:LIST|LSUB) \([^\)]*\) (?:NIL|\"[^\"]*\") ([^\r\n]*)[\r\n]*$/i', $line, $regs)) {
136 if (substr($regs[1], 0, 1) == '"')
137 return stripslashes(substr($regs[1], 1, -1));
138 return $regs[1];
139 }
140 return '';
141}
142
48af4b64 143/**
dc027257 144 * Detects if mailbox has noselect flag (can't store messages)
145 * @param string $lsub_line mailbox line from untagged LIST or LSUB response
48af4b64 146 * @return bool whether this is a Noselect mailbox.
dc027257 147 * @since 1.3.2
48af4b64 148 */
f73348a3 149function check_is_noselect ($lsub_line) {
3698bd49 150 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i", $lsub_line);
79e07c7e 151}
152
48af4b64 153/**
dc027257 154 * Detects if mailbox has noinferiors flag (can't store subfolders)
155 * @param string $lsub_line mailbox line from untagged LIST or LSUB response
48af4b64 156 * @return bool whether this is a Noinferiors mailbox.
dc027257 157 * @since 1.5.0
48af4b64 158 */
86c2763d 159function check_is_noinferiors ($lsub_line) {
160 return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noinferiors[^\)]*\)/i", $lsub_line);
161}
162
97b1248c 163/**
d43853c7 164 * Detects mailbox's parent folder
165 *
97b1248c 166 * If $haystack is a full mailbox name, and $needle is the mailbox
167 * separator character, returns the second last part of the full
168 * mailbox name (i.e. the mailbox's parent mailbox)
d43853c7 169 *
170 * Originally stored in functions/strings.php. Since 1.2.6 stored in
171 * functions/imap_mailbox.php
172 * @param string $haystack full mailbox name
173 * @param string $needle delimiter
174 * @return string parent mailbox
97b1248c 175 */
176function readMailboxParent($haystack, $needle) {
97b1248c 177 if ($needle == '') {
178 $ret = '';
179 } else {
180 $parts = explode($needle, $haystack);
181 $elem = array_pop($parts);
182 while ($elem == '' && count($parts)) {
183 $elem = array_pop($parts);
184 }
185 $ret = join($needle, $parts);
186 }
187 return( $ret );
188}
189
4669e892 190/**
6a8e7cae 191 * Check if $subbox is below the specified $parentbox
d43853c7 192 * @param string $subbox potential sub folder
193 * @param string $parentbox potential parent
194 * @return boolean
195 * @since 1.2.3
6a8e7cae 196 */
197function isBoxBelow( $subbox, $parentbox ) {
cef054e4 198 global $delimiter;
4669e892 199 /*
200 * Eliminate the obvious mismatch, where the
6a8e7cae 201 * subfolder path is shorter than that of the potential parent
202 */
203 if ( strlen($subbox) < strlen($parentbox) ) {
204 return false;
205 }
cef054e4 206 /* check for delimiter */
c3ce607e 207 if (substr($parentbox,-1) != $delimiter) {
208 $parentbox .= $delimiter;
209 }
210
211 return (substr($subbox,0,strlen($parentbox)) == $parentbox);
1e18bf95 212}
213
48af4b64 214/**
215 * Defines special mailboxes: given a mailbox name, it checks if this is a
216 * "special" one: INBOX, Trash, Sent or Draft.
d43853c7 217 *
218 * Since 1.2.5 function includes special_mailbox hook.<br>
219 * Since 1.4.3 hook supports more than one plugin.
220 * @param string $box mailbox name
221 * @return boolean
222 * @since 1.2.3
48af4b64 223 */
1e18bf95 224function isSpecialMailbox( $box ) {
90de1755 225 $ret = ( (strtolower($box) == 'inbox') ||
6a8e7cae 226 isTrashMailbox($box) || isSentMailbox($box) || isDraftMailbox($box) );
90de1755 227
2586d588 228 if ( !$ret ) {
5576644b 229 $ret = boolean_hook_function('special_mailbox',$box,1);
2586d588 230 }
3411d4ec 231 return $ret;
90de1755 232}
233
48af4b64 234/**
d43853c7 235 * Detects if mailbox is a Trash folder or subfolder of Trash
236 * @param string $box mailbox name
48af4b64 237 * @return bool whether this is a Trash folder
d43853c7 238 * @since 1.4.0
48af4b64 239 */
6a8e7cae 240function isTrashMailbox ($box) {
241 global $trash_folder, $move_to_trash;
242 return $move_to_trash && $trash_folder &&
243 ( $box == $trash_folder || isBoxBelow($box, $trash_folder) );
244}
245
48af4b64 246/**
d43853c7 247 * Detects if mailbox is a Sent folder or subfolder of Sent
248 * @param string $box mailbox name
48af4b64 249 * @return bool whether this is a Sent folder
d43853c7 250 * @since 1.4.0
48af4b64 251 */
6a8e7cae 252function isSentMailbox($box) {
253 global $sent_folder, $move_to_sent;
254 return $move_to_sent && $sent_folder &&
255 ( $box == $sent_folder || isBoxBelow($box, $sent_folder) );
256}
257
48af4b64 258/**
d43853c7 259 * Detects if mailbox is a Drafts folder or subfolder of Drafts
260 * @param string $box mailbox name
48af4b64 261 * @return bool whether this is a Draft folder
d43853c7 262 * @since 1.4.0
48af4b64 263 */
6a8e7cae 264function isDraftMailbox($box) {
265 global $draft_folder, $save_as_draft;
266 return $save_as_draft &&
267 ( $box == $draft_folder || isBoxBelow($box, $draft_folder) );
268}
269
48af4b64 270/**
d43853c7 271 * Expunges a mailbox
272 *
273 * WARNING: Select mailbox before calling this function.
274 *
275 * permanently removes all messages that have the \Deleted flag
276 * set from the selected mailbox. See EXPUNGE command chapter in
277 * IMAP RFC.
278 * @param stream $imap_stream imap connection resource
279 * @param string $mailbox mailbox name (unused since 1.1.3).
280 * @param boolean $handle_errors error handling control (displays error_box on error).
281 * @param mixed $id (since 1.3.0) integer message id or array with integer ids
282 * @return integer number of expunged messages
3131bc8d 283 * @since 1.0 or older
48af4b64 284 */
8f6505f6 285function sqimap_mailbox_expunge ($imap_stream, $mailbox, $handle_errors = true, $id='') {
06b5c3ff 286 if ($id) {
ff245fbd 287 if (is_array($id)) {
288 $id = sqimap_message_list_squisher($id);
289 }
290 $id = ' '.$id;
9d8d1ebd 291 $uid = TRUE;
06b5c3ff 292 } else {
ff245fbd 293 $uid = false;
8f6505f6 294 }
06b5c3ff 295 $read = sqimap_run_command($imap_stream, 'EXPUNGE'.$id, $handle_errors,
296 $response, $message, $uid);
63240b90 297 $cnt = 0;
ff245fbd 298
299 if (is_array($read)) {
63240b90 300 foreach ($read as $r) {
ff245fbd 301 if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
302 $cnt++;
303 }
63240b90 304 }
8f6505f6 305 }
ff245fbd 306 return $cnt;
43b698c7 307}
308
48af4b64 309/**
310 * Checks whether or not the specified mailbox exists
d43853c7 311 * @param stream $imap_stream imap connection resource
312 * @param string $mailbox mailbox name
313 * @return boolean
314 * @since 1.0 or older
48af4b64 315 */
1da22cda 316function sqimap_mailbox_exists ($imap_stream, $mailbox) {
247f700e 317 if (!isset($mailbox) || empty($mailbox)) {
43b698c7 318 return false;
319 }
568cb884 320 $mbx = sqimap_run_command($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mailbox),
3411d4ec 321 true, $response, $message);
43b698c7 322 return isset($mbx[0]);
323}
324
48af4b64 325/**
326 * Selects a mailbox
d43853c7 327 * Before 1.3.0 used more arguments and returned data depended on those argumements.
328 * @param stream $imap_stream imap connection resource
329 * @param string $mailbox mailbox name
330 * @return array results of select command (on success - permanentflags, flags and rights)
331 * @since 1.0 or older
48af4b64 332 */
e4c6fe41 333function sqimap_mailbox_select ($imap_stream, $mailbox) {
ff245fbd 334 if ($mailbox == 'None') {
43b698c7 335 return;
336 }
f69feefe 337
568cb884 338 $read = sqimap_run_command($imap_stream, 'SELECT ' . sqimap_encode_mailbox_name($mailbox),
3411d4ec 339 true, $response, $message);
e4c6fe41 340 $result = array();
ff245fbd 341 for ($i = 0, $cnt = count($read); $i < $cnt; $i++) {
e4c6fe41 342 if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i], $regs)) {
ff245fbd 343 $result[strtoupper($regs[1])] = $regs[2];
344 } else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i], $regs)) {
345 $result[strtoupper($regs[2])] = $regs[1];
346 } else {
347 if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i], $regs)) {
348 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/","/\]/") ,'', $regs[1])) ;
4669e892 349 $result['PERMANENTFLAGS'] = explode(' ',strtolower($regs[1]));
ff245fbd 350 } else if (preg_match("/FLAGS(.*)/i",$read[$i], $regs)) {
351 $regs[1]=trim(preg_replace ( array ("/\(/","/\)/") ,'', $regs[1])) ;
4669e892 352 $result['FLAGS'] = explode(' ',strtolower($regs[1]));
ff245fbd 353 }
354 }
e4c6fe41 355 }
4669e892 356 if (!isset($result['PERMANENTFLAGS'])) {
357 $result['PERMANENTFLAGS'] = $result['FLAGS'];
358 }
e4c6fe41 359 if (preg_match('/^\[(.+)\]/',$message, $regs)) {
4669e892 360 $result['RIGHTS']=strtoupper($regs[1]);
e4c6fe41 361 }
f69feefe 362
e4c6fe41 363 return $result;
43b698c7 364}
365
48af4b64 366/**
367 * Creates a folder.
d43853c7 368 *
369 * Mailbox is automatically subscribed.
370 *
371 * Set $type to string that does not match 'noselect' (case insensitive),
372 * if you don't want to prepend delimiter to mailbox name. Please note
373 * that 'noinferiors' might be used someday as keyword for folders
374 * that store only messages.
375 * @param stream $imap_steam imap connection resource
376 * @param string $mailbox mailbox name
377 * @param string $type folder type.
378 * @since 1.0 or older
48af4b64 379 */
3411d4ec 380function sqimap_mailbox_create ($imap_stream, $mailbox, $type) {
43b698c7 381 global $delimiter;
382 if (strtolower($type) == 'noselect') {
3411d4ec 383 $mailbox .= $delimiter;
43b698c7 384 }
e429f014 385
48af4b64 386 $read_ary = sqimap_run_command($imap_stream, 'CREATE ' .
387 sqimap_encode_mailbox_name($mailbox),
3411d4ec 388 true, $response, $message);
43b698c7 389 sqimap_subscribe ($imap_stream, $mailbox);
390}
391
48af4b64 392/**
393 * Subscribes to an existing folder.
d43853c7 394 * @param stream $imap_stream imap connection resource
395 * @param string $mailbox mailbox name
396 * @param boolean $debug (since 1.5.1)
397 * @since 1.0 or older
48af4b64 398 */
852abae7 399function sqimap_subscribe ($imap_stream, $mailbox,$debug=true) {
48af4b64 400 $read_ary = sqimap_run_command($imap_stream, 'SUBSCRIBE ' .
401 sqimap_encode_mailbox_name($mailbox),
852abae7 402 $debug, $response, $message);
43b698c7 403}
404
48af4b64 405/**
406 * Unsubscribes from an existing folder
d43853c7 407 * @param stream $imap_stream imap connection resource
408 * @param string $mailbox mailbox name
409 * @since 1.0 or older
48af4b64 410 */
3411d4ec 411function sqimap_unsubscribe ($imap_stream, $mailbox) {
48af4b64 412 $read_ary = sqimap_run_command($imap_stream, 'UNSUBSCRIBE ' .
413 sqimap_encode_mailbox_name($mailbox),
e2e8b92b 414 false, $response, $message);
43b698c7 415}
416
48af4b64 417/**
418 * Deletes the given folder
3131bc8d 419 * Since 1.2.6 and 1.3.0 contains rename_or_delete_folder hook
420 * @param stream $imap_stream imap connection resource
421 * @param string $mailbox mailbox name
422 * @since 1.0 or older
48af4b64 423 */
3411d4ec 424function sqimap_mailbox_delete ($imap_stream, $mailbox) {
78cc4b12 425 global $data_dir, $username;
e2e8b92b 426 sqimap_unsubscribe ($imap_stream, $mailbox);
48af4b64 427 $read_ary = sqimap_run_command($imap_stream, 'DELETE ' .
428 sqimap_encode_mailbox_name($mailbox),
3411d4ec 429 true, $response, $message);
e2e8b92b 430 if ($response !== 'OK') {
431 // subscribe again
432 sqimap_subscribe ($imap_stream, $mailbox);
433 } else {
434 do_hook_function('rename_or_delete_folder', $args = array($mailbox, 'delete', ''));
435 removePref($data_dir, $username, "thread_$mailbox");
5659ad2e 436 removePref($data_dir, $username, "collapse_folder_$mailbox");
e2e8b92b 437 }
43b698c7 438}
439
48af4b64 440/**
441 * Determines if the user is subscribed to the folder or not
3131bc8d 442 * @param stream $imap_stream imap connection resource
443 * @param string $mailbox mailbox name
444 * @return boolean
445 * @since 1.2.0
48af4b64 446 */
1da22cda 447function sqimap_mailbox_is_subscribed($imap_stream, $folder) {
1da22cda 448 $boxesall = sqimap_mailbox_list ($imap_stream);
449 foreach ($boxesall as $ref) {
43b698c7 450 if ($ref['unformatted'] == $folder) {
3411d4ec 451 return true;
43b698c7 452 }
453 }
454 return false;
455}
456
48af4b64 457/**
458 * Renames a mailbox.
3131bc8d 459 * Since 1.2.6 and 1.3.0 contains rename_or_delete_folder hook
460 * @param stream $imap_stream imap connection resource
461 * @param string $old_name mailbox name
462 * @param string $new_name new mailbox name
463 * @since 1.2.3
48af4b64 464 */
1c52ba77 465function sqimap_mailbox_rename( $imap_stream, $old_name, $new_name ) {
3411d4ec 466 if ( $old_name != $new_name ) {
78cc4b12 467 global $delimiter, $imap_server_type, $data_dir, $username;
1c52ba77 468 if ( substr( $old_name, -1 ) == $delimiter ) {
469 $old_name = substr( $old_name, 0, strlen( $old_name ) - 1 );
470 $new_name = substr( $new_name, 0, strlen( $new_name ) - 1 );
471 $postfix = $delimiter;
1c52ba77 472 } else {
473 $postfix = '';
1c52ba77 474 }
68f2ce7a 475
5659ad2e 476 $boxesall = sqimap_mailbox_list_all($imap_stream);
48af4b64 477 $cmd = 'RENAME ' . sqimap_encode_mailbox_name($old_name) .
91e0dccc 478 ' ' . sqimap_encode_mailbox_name($new_name);
3411d4ec 479 $data = sqimap_run_command($imap_stream, $cmd, true, $response, $message);
1c52ba77 480 sqimap_unsubscribe($imap_stream, $old_name.$postfix);
5659ad2e 481 $oldpref_thread = getPref($data_dir, $username, 'thread_'.$old_name.$postfix);
482 $oldpref_collapse = getPref($data_dir, $username, 'collapse_folder_'.$old_name.$postfix);
68f2ce7a 483 removePref($data_dir, $username, 'thread_'.$old_name.$postfix);
5659ad2e 484 removePref($data_dir, $username, 'collapse_folder_'.$old_name.$postfix);
1c52ba77 485 sqimap_subscribe($imap_stream, $new_name.$postfix);
5659ad2e 486 setPref($data_dir, $username, 'thread_'.$new_name.$postfix, $oldpref_thread);
487 setPref($data_dir, $username, 'collapse_folder_'.$new_name.$postfix, $oldpref_collapse);
e429f014 488 do_hook_function('rename_or_delete_folder',$args = array($old_name, 'rename', $new_name));
648713af 489 $l = strlen( $old_name ) + 1;
490 $p = 'unformatted';
68f2ce7a 491
ff245fbd 492 foreach ($boxesall as $box) {
493 if (substr($box[$p], 0, $l) == $old_name . $delimiter) {
648713af 494 $new_sub = $new_name . $delimiter . substr($box[$p], $l);
5659ad2e 495 /* With Cyrus IMAPd >= 2.0 rename is recursive, so don't check for errors here */
648713af 496 if ($imap_server_type == 'cyrus') {
68f2ce7a 497 $cmd = 'RENAME "' . $box[$p] . '" "' . $new_sub . '"';
5659ad2e 498 $data = sqimap_run_command($imap_stream, $cmd, false,
648713af 499 $response, $message);
1c52ba77 500 }
5659ad2e 501 $was_subscribed = sqimap_mailbox_is_subscribed($imap_stream, $box[$p]);
502 if ( $was_subscribed ) {
503 sqimap_unsubscribe($imap_stream, $box[$p]);
504 }
505 $oldpref_thread = getPref($data_dir, $username, 'thread_'.$box[$p]);
506 $oldpref_collapse = getPref($data_dir, $username, 'collapse_folder_'.$box[$p]);
68f2ce7a 507 removePref($data_dir, $username, 'thread_'.$box[$p]);
5659ad2e 508 removePref($data_dir, $username, 'collapse_folder_'.$box[$p]);
509 if ( $was_subscribed ) {
510 sqimap_subscribe($imap_stream, $new_sub);
511 }
512 setPref($data_dir, $username, 'thread_'.$new_sub, $oldpref_thread);
513 setPref($data_dir, $username, 'collapse_folder_'.$new_sub, $oldpref_collapse);
68f2ce7a 514 do_hook_function('rename_or_delete_folder',
3411d4ec 515 $args = array($box[$p], 'rename', $new_sub));
1c52ba77 516 }
517 }
1c52ba77 518 }
1c52ba77 519}
43b698c7 520
48af4b64 521/**
86c2763d 522 * Formats a mailbox into parts for the $boxesall array
3411d4ec 523 *
86c2763d 524 * The parts are:
3131bc8d 525 * <ul>
526 * <li>raw - Raw LIST/LSUB response from the IMAP server
527 * <li>formatted - nicely formatted folder name
528 * <li>unformatted - unformatted, but with delimiter at end removed
529 * <li>unformatted-dm - folder name as it appears in raw response
530 * <li>unformatted-disp - unformatted without $folder_prefix
531 * <li>id - TODO: document me
532 * <li>flags - TODO: document me
533 * </ul>
534 * Before 1.2.0 used third argument for delimiter.
535 * @param $line
536 * @param $line_lsub
537 * @return array
538 * @since 1.0 or older
539 * @todo document id and flags keys in boxes array and function arguments.
3411d4ec 540 */
541function sqimap_mailbox_parse ($line, $line_lsub) {
43b698c7 542 global $folder_prefix, $delimiter;
3411d4ec 543
43b698c7 544 /* Process each folder line */
cef054e4 545 for ($g = 0, $cnt = count($line); $g < $cnt; ++$g) {
43b698c7 546 /* Store the raw IMAP reply */
547 if (isset($line[$g])) {
e429f014 548 $boxesall[$g]['raw'] = $line[$g];
ff245fbd 549 } else {
e429f014 550 $boxesall[$g]['raw'] = '';
43b698c7 551 }
3411d4ec 552
43b698c7 553 /* Count number of delimiters ($delimiter) in folder name */
86c2763d 554 $mailbox = /*trim(*/$line_lsub[$g]/*)*/;
ff245fbd 555 $dm_count = substr_count($mailbox, $delimiter);
43b698c7 556 if (substr($mailbox, -1) == $delimiter) {
3411d4ec 557 /* If name ends in delimiter, decrement count by one */
558 $dm_count--;
43b698c7 559 }
3411d4ec 560
561 /* Format folder name, but only if it's a INBOX.* or has a parent. */
1da22cda 562 $boxesallbyname[$mailbox] = $g;
43b698c7 563 $parentfolder = readMailboxParent($mailbox, $delimiter);
564 if ( (strtolower(substr($mailbox, 0, 5)) == "inbox") ||
565 (substr($mailbox, 0, strlen($folder_prefix)) == $folder_prefix) ||
ff245fbd 566 (isset($boxesallbyname[$parentfolder]) &&
567 (strlen($parentfolder) > 0) ) ) {
568 $indent = $dm_count - (substr_count($folder_prefix, $delimiter));
43b698c7 569 if ($indent > 0) {
ff245fbd 570 $boxesall[$g]['formatted'] = str_repeat('&nbsp;&nbsp;', $indent);
571 } else {
1da22cda 572 $boxesall[$g]['formatted'] = '';
43b698c7 573 }
447b2166 574 $boxesall[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
ff245fbd 575 } else {
447b2166 576 $boxesall[$g]['formatted'] = imap_utf7_decode_local($mailbox);
43b698c7 577 }
90de1755 578
1da22cda 579 $boxesall[$g]['unformatted-dm'] = $mailbox;
43b698c7 580 if (substr($mailbox, -1) == $delimiter) {
8e9e8afa 581 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
43b698c7 582 }
1da22cda 583 $boxesall[$g]['unformatted'] = $mailbox;
43b698c7 584 if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix) {
631b9da3 585 $mailbox = substr($mailbox, strlen($folder_prefix));
43b698c7 586 }
1da22cda 587 $boxesall[$g]['unformatted-disp'] = $mailbox;
588 $boxesall[$g]['id'] = $g;
90de1755 589
1da22cda 590 $boxesall[$g]['flags'] = array();
43b698c7 591 if (isset($line[$g])) {
36dfb0c9 592 ereg("\(([^)]*)\)",$line[$g],$regs);
4669e892 593 // FIXME Flags do contain the \ character. \NoSelect \NoInferiors
5c300c60 594 // and $MDNSent <= last one doesn't have the \
595 // It's better to follow RFC3501 instead of using our own naming.
1a7e1e97 596 $flags = trim(strtolower(str_replace('\\', '',$regs[1])));
43b698c7 597 if ($flags) {
1da22cda 598 $boxesall[$g]['flags'] = explode(' ', $flags);
43b698c7 599 }
600 }
601 }
1da22cda 602 return $boxesall;
43b698c7 603}
604
48af4b64 605/**
be2d5495 606 * Returns list of options (to be echoed into select statement
607 * based on available mailboxes and separators
6fd95361 608 * Caller should surround options with <select ...> </select> and
be2d5495 609 * any formatting.
3131bc8d 610 * @param stream $imap_stream imap connection resource to query for mailboxes
611 * @param array $show_selected array containing list of mailboxes to pre-select (0 if none)
612 * @param array $folder_skip array of folders to keep out of option list (compared in lower)
613 * @param $boxes list of already fetched boxes (for places like folder panel, where
be2d5495 614 * you know these options will be shown 3 times in a row.. (most often unset).
3131bc8d 615 * @param string $flag (since 1.4.1) flag to check for in mailbox flags, used to filter out mailboxes.
59a8e3e8 616 * 'noselect' by default to remove unselectable mailboxes.
617 * 'noinferiors' used to filter out folders that can not contain subfolders.
618 * NULL to avoid flag check entirely.
d0928dd5 619 * NOTE: noselect and noiferiors are used internally. The IMAP representation is
620 * \NoSelect and \NoInferiors
3131bc8d 621 * @param boolean $use_long_format (since 1.4.1) override folder display preference and always show full folder name.
622 * @return string html formated mailbox selection options
623 * @since 1.3.2
be2d5495 624 */
4669e892 625function sqimap_mailbox_option_list($imap_stream, $show_selected = 0, $folder_skip = 0, $boxes = 0,
59a8e3e8 626 $flag = 'noselect', $use_long_format = false ) {
be2d5495 627 global $username, $data_dir;
628 $mbox_options = '';
45f836eb 629 if ( $use_long_format ) {
630 $shorten_box_names = 0;
631 } else {
632 $shorten_box_names = getPref($data_dir, $username, 'mailbox_select_style', SMPREF_OFF);
633 }
ff245fbd 634
635 if ($boxes == 0) {
be2d5495 636 $boxes = sqimap_mailbox_list($imap_stream);
ff245fbd 637 }
59a8e3e8 638
be2d5495 639 foreach ($boxes as $boxes_part) {
4df5d823 640 if ($flag == NULL || (is_array($boxes_part['flags'])
641 && !in_array($flag, $boxes_part['flags']))) {
be2d5495 642 $box = $boxes_part['unformatted'];
be2d5495 643
d0928dd5 644 if ($folder_skip != 0 && in_array($box, $folder_skip) ) {
be2d5495 645 continue;
646 }
4669e892 647 $lowerbox = strtolower($box);
5c300c60 648 // mailboxes are casesensitive => inbox.sent != inbox.Sent
649 // nevermind, to many dependencies this should be fixed!
4669e892 650
d0928dd5 651 if (strtolower($box) == 'inbox') { // inbox is special and not casesensitive
be2d5495 652 $box2 = _("INBOX");
4669e892 653 } else {
5c300c60 654 switch ($shorten_box_names)
655 {
656 case 2: /* delimited, style = 2 */
c77a0487 657 $box2 = str_replace('&amp;nbsp;&amp;nbsp;', '.&nbsp;', htmlspecialchars($boxes_part['formatted']));
5c300c60 658 break;
d0928dd5 659 case 1: /* indent, style = 1 */
c77a0487 660 $box2 = str_replace('&amp;nbsp;&amp;nbsp;', '&nbsp;&nbsp;', htmlspecialchars($boxes_part['formatted']));
5c300c60 661 break;
d0928dd5 662 default: /* default, long names, style = 0 */
6c4f0fa1 663 $box2 = str_replace(' ', '&nbsp;', htmlspecialchars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
5c300c60 664 break;
665 }
be2d5495 666 }
667 if ($show_selected != 0 && in_array($lowerbox, $show_selected) ) {
ff0969a0 668 $mbox_options .= '<option value="' . htmlspecialchars($box) .'" selected="selected">'.$box2.'</option>' . "\n";
be2d5495 669 } else {
ff0969a0 670 $mbox_options .= '<option value="' . htmlspecialchars($box) .'">'.$box2.'</option>' . "\n";
be2d5495 671 }
672 }
673 }
674 return $mbox_options;
675}
43b698c7 676
48af4b64 677/**
4669e892 678 * Returns sorted mailbox lists in several different ways.
3411d4ec 679 * See comment on sqimap_mailbox_parse() for info about the returned array.
3e9441a2 680 * @param resource $imap_stream imap connection resource
681 * @param boolean $force force update of mailbox listing. available since 1.4.2 and 1.5.0
682 * @return array list of mailboxes
3131bc8d 683 * @since 1.0 or older
3411d4ec 684 */
fe6efa94 685function sqimap_mailbox_list($imap_stream, $force=false) {
fe6efa94 686 if (!sqgetGlobalVar('boxesnew',$boxesnew,SQ_SESSION) || $force) {
3411d4ec 687 global $data_dir, $username, $list_special_folders_first,
7e235a1a 688 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
689 $move_to_trash, $move_to_sent, $save_as_draft,
4d5f2b31 690 $delimiter, $noselect_fix_enable, $imap_server_type,
691 $show_only_subscribed_folders;
3411d4ec 692 $inbox_subscribed = false;
60a132f8 693 $listsubscribed = sqimap_capability($imap_stream,'LIST-SUBSCRIBED');
7e235a1a 694
08185f2a 695 require_once(SM_PATH . 'include/load_prefs.php');
7e235a1a 696
4d5f2b31 697 if (!$show_only_subscribed_folders) {
698 $lsub = 'LIST';
699 } elseif ($listsubscribed) {
60a132f8 700 $lsub = 'LIST (SUBSCRIBED)';
701 } else {
702 $lsub = 'LSUB';
4669e892 703 }
704
ff245fbd 705 if ($noselect_fix_enable) {
60a132f8 706 $lsub_args = "$lsub \"$folder_prefix\" \"*%\"";
ff245fbd 707 } else {
60a132f8 708 $lsub_args = "$lsub \"$folder_prefix\" \"*\"";
ff245fbd 709 }
3411d4ec 710 /* LSUB array */
ca85aabe 711 $lsub_ary = sqimap_run_command ($imap_stream, $lsub_args,
3411d4ec 712 true, $response, $message);
4669e892 713 $lsub_ary = compact_mailboxes_response($lsub_ary);
7e235a1a 714
7e235a1a 715 $sorted_lsub_ary = array();
ff245fbd 716 for ($i = 0, $cnt = count($lsub_ary);$i < $cnt; $i++) {
159d2af7 717
7e235a1a 718 $temp_mailbox_name = find_mailbox_name($lsub_ary[$i]);
719 $sorted_lsub_ary[] = $temp_mailbox_name;
cef054e4 720 if (!$inbox_subscribed && strtoupper($temp_mailbox_name) == 'INBOX') {
3411d4ec 721 $inbox_subscribed = true;
7e235a1a 722 }
723 }
cef054e4 724
5c300c60 725 /* natural sort mailboxes */
7e235a1a 726 if (isset($sorted_lsub_ary)) {
159d2af7 727 usort($sorted_lsub_ary, 'strnatcasecmp');
7e235a1a 728 }
5c300c60 729 /*
730 * The LSUB response doesn't provide us information about \Noselect
731 * mail boxes. The LIST response does, that's why we need to do a LIST
732 * call to retrieve the flags for the mailbox
cef054e4 733 * Note: according RFC2060 an imap server may provide \NoSelect flags in the LSUB response.
734 * in other words, we cannot rely on it.
fe6efa94 735 */
cef054e4 736 $sorted_list_ary = array();
60a132f8 737 // if (!$listsubscribed) {
738 for ($i=0; $i < count($sorted_lsub_ary); $i++) {
7e235a1a 739 if (substr($sorted_lsub_ary[$i], -1) == $delimiter) {
740 $mbx = substr($sorted_lsub_ary[$i], 0, strlen($sorted_lsub_ary[$i])-1);
741 }
742 else {
743 $mbx = $sorted_lsub_ary[$i];
744 }
fe6efa94 745
159d2af7 746 $read = sqimap_run_command ($imap_stream, 'LIST "" ' . sqimap_encode_mailbox_name($mbx),
3411d4ec 747 true, $response, $message);
4669e892 748
159d2af7 749 $read = compact_mailboxes_response($read);
4669e892 750
7e235a1a 751 if (isset($read[0])) {
752 $sorted_list_ary[$i] = $read[0];
cef054e4 753 } else {
7e235a1a 754 $sorted_list_ary[$i] = '';
755 }
60a132f8 756 }
757 // }
3411d4ec 758 /*
7e235a1a 759 * Just in case they're not subscribed to their inbox,
760 * we'll get it for them anyway
761 */
cef054e4 762 if (!$inbox_subscribed) {
159d2af7 763 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" "INBOX"',
3411d4ec 764 true, $response, $message);
159d2af7 765 $sorted_list_ary[] = implode('',compact_mailboxes_response($inbox_ary));
7e235a1a 766 $sorted_lsub_ary[] = find_mailbox_name($inbox_ary[0]);
767 }
768
769 $boxesall = sqimap_mailbox_parse ($sorted_list_ary, $sorted_lsub_ary);
770
3411d4ec 771 /* Now, lets sort for special folders */
7e235a1a 772 $boxesnew = $used = array();
773
774 /* Find INBOX */
ff245fbd 775 $cnt = count($boxesall);
5c300c60 776 $used = array_pad($used,$cnt,false);
cef054e4 777 for($k = 0; $k < $cnt; ++$k) {
ff245fbd 778 if (strtolower($boxesall[$k]['unformatted']) == 'inbox') {
779 $boxesnew[] = $boxesall[$k];
3411d4ec 780 $used[$k] = true;
5c300c60 781 break;
7e235a1a 782 }
783 }
7e235a1a 784 /* List special folders and their subfolders, if requested. */
3411d4ec 785 if ($list_special_folders_first) {
cef054e4 786 for($k = 0; $k < $cnt; ++$k) {
ff245fbd 787 if (!$used[$k] && isSpecialMailbox($boxesall[$k]['unformatted'])) {
788 $boxesnew[] = $boxesall[$k];
789 $used[$k] = true;
7e235a1a 790 }
5c300c60 791 }
792 }
c3ce607e 793
794 /* Find INBOX's children */
795 for($k = 0; $k < $cnt; ++$k) {
66c1aad9 796 if (!$used[$k] && isBoxBelow(strtolower($boxesall[$k]['unformatted']), 'inbox') &&
797 strtolower($boxesall[$k]['unformatted']) != 'inbox') {
c3ce607e 798 $boxesnew[] = $boxesall[$k];
799 $used[$k] = true;
800 }
801 }
802
803 /* Rest of the folders */
ff245fbd 804 for($k = 0; $k < $cnt; $k++) {
805 if (!$used[$k]) {
806 $boxesnew[] = $boxesall[$k];
7e235a1a 807 }
808 }
fe6efa94 809 sqsession_register($boxesnew,'boxesnew');
43b698c7 810 }
3411d4ec 811 return $boxesnew;
43b698c7 812}
813
48af4b64 814/**
3131bc8d 815 * Returns a list of all folders, subscribed or not
816 * @param stream $imap_stream imap connection resource
817 * @return array see sqimap_mailbox_parse()
818 * @since 1.0 or older
90de1755 819 */
1da22cda 820function sqimap_mailbox_list_all($imap_stream) {
3411d4ec 821 global $list_special_folders_first, $folder_prefix, $delimiter;
bac13dd7 822
823 $read_ary = sqimap_run_command($imap_stream,"LIST \"$folder_prefix\" *",true,$response, $message,false);
824 $read_ary = compact_mailboxes_response($read_ary);
825
43b698c7 826 $g = 0;
7d82bceb 827 $fld_pre_length = strlen($folder_prefix);
ff245fbd 828 for ($i = 0, $cnt = count($read_ary); $i < $cnt; $i++) {
780dd344 829 /* Store the raw IMAP reply */
830 $boxes[$g]['raw'] = $read_ary[$i];
90de1755 831
780dd344 832 /* Count number of delimiters ($delimiter) in folder name */
833 $mailbox = find_mailbox_name($read_ary[$i]);
834 $dm_count = substr_count($mailbox, $delimiter);
835 if (substr($mailbox, -1) == $delimiter) {
836 /* If name ends in delimiter - decrement count by one */
837 $dm_count--;
838 }
90de1755 839
780dd344 840 /* Format folder name, but only if it's a INBOX.* or has a parent. */
841 $boxesallbyname[$mailbox] = $g;
842 $parentfolder = readMailboxParent($mailbox, $delimiter);
843 if((eregi('^inbox'.quotemeta($delimiter), $mailbox)) ||
844 (ereg('^'.$folder_prefix, $mailbox)) ||
845 ( isset($boxesallbyname[$parentfolder]) && (strlen($parentfolder) > 0) ) ) {
846 if ($dm_count) {
847 $boxes[$g]['formatted'] = str_repeat('&nbsp;&nbsp;', $dm_count);
90de1755 848 } else {
780dd344 849 $boxes[$g]['formatted'] = '';
12d61439 850 }
780dd344 851 $boxes[$g]['formatted'] .= imap_utf7_decode_local(readShortMailboxName($mailbox, $delimiter));
852 } else {
853 $boxes[$g]['formatted'] = imap_utf7_decode_local($mailbox);
854 }
855
856 $boxes[$g]['unformatted-dm'] = $mailbox;
857 if (substr($mailbox, -1) == $delimiter) {
858 $mailbox = substr($mailbox, 0, strlen($mailbox) - 1);
859 }
860 $boxes[$g]['unformatted'] = $mailbox;
861 $boxes[$g]['unformatted-disp'] = substr($mailbox,$fld_pre_length);
862
863 $boxes[$g]['id'] = $g;
864
865 /* Now lets get the flags for this mailbox */
866 $read_mlbx = $read_ary[$i];
867 $flags = substr($read_mlbx, strpos($read_mlbx, '(')+1);
868 $flags = substr($flags, 0, strpos($flags, ')'));
869 $flags = str_replace('\\', '', $flags);
870 $flags = trim(strtolower($flags));
871 if ($flags) {
872 $boxes[$g]['flags'] = explode(' ', $flags);
873 } else {
874 $boxes[$g]['flags'] = array();
43b698c7 875 }
876 $g++;
877 }
878 if(is_array($boxes)) {
e429f014 879 sort ($boxes);
43b698c7 880 }
90de1755 881
43b698c7 882 return $boxes;
883}
5bdd7223 884
3131bc8d 885/**
886 * @param stream $imap_stream imap connection resource
887 * @return object see mailboxes class.
888 * @since 1.3.0
889 */
60b5724d 890function sqimap_mailbox_tree($imap_stream) {
ce68b76b 891 global $default_folder_prefix;
60a132f8 892 if (true) {
60b5724d 893 global $data_dir, $username, $list_special_folders_first,
a2e66c6d 894 $folder_prefix, $delimiter, $trash_folder, $move_to_trash,
4d5f2b31 895 $imap_server_type, $show_only_subscribed_folders;
60b5724d 896
f08ba804 897 $noselect = false;
86c2763d 898 $noinferiors = false;
60b5724d 899
08185f2a 900 require_once(SM_PATH . 'include/load_prefs.php');
60b5724d 901
4d5f2b31 902 if ($show_only_subscribed_folders) {
903 $lsub_cmd = 'LSUB';
904 } else {
905 $lsub_cmd = 'LIST';
906 }
907
60b5724d 908 /* LSUB array */
4d5f2b31 909 $lsub_ary = sqimap_run_command ($imap_stream, "$lsub_cmd \"$folder_prefix\" \"*\"",
60b5724d 910 true, $response, $message);
bac13dd7 911 $lsub_ary = compact_mailboxes_response($lsub_ary);
60b5724d 912
9871bdaa 913 /* Check to see if we have an INBOX */
78bc908d 914 $has_inbox = false;
915
916 for ($i = 0, $cnt = count($lsub_ary); $i < $cnt; $i++) {
4d5f2b31 917 if (preg_match("/^\*\s+$lsub_cmd.*\s\"?INBOX\"?[^(\/\.)].*$/i",$lsub_ary[$i])) {
9d8d1ebd 918 $lsub_ary[$i] = strtoupper($lsub_ary[$i]);
31a5064d 919 // in case of an unsubscribed inbox an imap server can
4669e892 920 // return the inbox in the lsub results with a \NoSelect
31a5064d 921 // flag.
4d5f2b31 922 if (!preg_match("/\*\s+$lsub_cmd\s+\(.*\\\\NoSelect.*\).*/i",$lsub_ary[$i])) {
31a5064d 923 $has_inbox = true;
924 } else {
925 // remove the result and request it again with a list
926 // response at a later stage.
927 unset($lsub_ary[$i]);
4669e892 928 // re-index the array otherwise the addition of the LIST
35314c5c 929 // response will fail in PHP 4.1.2 and probably other older versions
930 $lsub_ary = array_values($lsub_ary);
31a5064d 931 }
78bc908d 932 break;
933 }
934 }
935
936 if ($has_inbox == false) {
31a5064d 937 // do a list request for inbox because we should always show
938 // inbox even if the user isn't subscribed to it.
3d29eb78 939 $inbox_ary = sqimap_run_command ($imap_stream, 'LIST "" "INBOX"',
31a5064d 940 true, $response, $message);
941 $inbox_ary = compact_mailboxes_response($inbox_ary);
942 if (count($inbox_ary)) {
943 $lsub_ary[] = $inbox_ary[0];
944 }
78bc908d 945 }
946
60b5724d 947 /*
4669e892 948 * Section about removing the last element was removed
60b5724d 949 * We don't return "* OK" anymore from sqimap_read_data
950 */
bac13dd7 951
60b5724d 952 $sorted_lsub_ary = array();
e4c6fe41 953 $cnt = count($lsub_ary);
ff245fbd 954 for ($i = 0; $i < $cnt; $i++) {
ff245fbd 955 $mbx = find_mailbox_name($lsub_ary[$i]);
a2e66c6d 956
483f9ef9 957 // only do the noselect test if !uw, is checked later. FIX ME see conf.pl setting
4669e892 958 if ($imap_server_type != "uw") {
483f9ef9 959 $noselect = check_is_noselect($lsub_ary[$i]);
86c2763d 960 $noinferiors = check_is_noinferiors($lsub_ary[$i]);
a2e66c6d 961 }
ff245fbd 962 if (substr($mbx, -1) == $delimiter) {
963 $mbx = substr($mbx, 0, strlen($mbx) - 1);
964 }
4669e892 965 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
60b5724d 966 }
5c300c60 967 // FIX ME this requires a config setting inside conf.pl instead of checking on server type
483f9ef9 968 if ($imap_server_type == "uw") {
5c300c60 969 $aQuery = array();
970 $aTag = array();
971 // prepare an array with queries
972 foreach ($sorted_lsub_ary as $aMbx) {
bac13dd7 973 $mbx = stripslashes($aMbx['mbx']);
568cb884 974 sqimap_prepare_pipelined_query('LIST "" ' . sqimap_encode_mailbox_name($mbx), $tag, $aQuery, false);
5c300c60 975 $aTag[$tag] = $mbx;
4669e892 976 }
5c300c60 977 $sorted_lsub_ary = array();
978 // execute all the queries at once
979 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
980 foreach($aTag as $tag => $mbx) {
981 if ($aServerResponse[$tag] == 'OK') {
982 $sResponse = implode('', $aResponse[$tag]);
983 $noselect = check_is_noselect($sResponse);
86c2763d 984 $noinferiors = check_is_noinferiors($sResponse);
985 $sorted_lsub_ary[] = array ('mbx' => $mbx, 'noselect' => $noselect, 'noinferiors' => $noinferiors);
5c300c60 986 }
987 }
988 $cnt = count($sorted_lsub_ary);
483f9ef9 989 }
bac13dd7 990 $sorted_lsub_ary = array_values($sorted_lsub_ary);
9d8d1ebd 991 usort($sorted_lsub_ary, 'mbxSort');
d1404257 992 $boxestree = sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
993 return $boxestree;
60b5724d 994 }
995}
996
3131bc8d 997/**
998 * Callback function used for sorting mailboxes in sqimap_mailbox_tree
999 * @param string $a
1000 * @param string $b
1001 * @return integer see php strnatcasecmp()
1002 * @since 1.5.1
1003 */
9d8d1ebd 1004function mbxSort($a, $b) {
1005 return strnatcasecmp($a['mbx'], $b['mbx']);
1006}
1007
3131bc8d 1008/**
1009 * @param array $mbx_ary
1010 * @param $mbxs
1011 * @param stream $imap_stream (since 1.5.0) imap connection resource
1012 * @return object see mailboxes class
1013 * @since 1.3.0
1014 */
483f9ef9 1015function sqimap_fill_mailbox_tree($mbx_ary, $mbxs=false,$imap_stream) {
60b5724d 1016 global $data_dir, $username, $list_special_folders_first,
1017 $folder_prefix, $trash_folder, $sent_folder, $draft_folder,
1018 $move_to_trash, $move_to_sent, $save_as_draft,
43a31298 1019 $delimiter, $imap_server_type;
60b5724d 1020
8d8da447 1021 // $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
ff245fbd 1022
60b5724d 1023 /* create virtual root node */
1024 $mailboxes= new mailboxes();
1025 $mailboxes->is_root = true;
ff245fbd 1026 $trail_del = false;
78bc908d 1027 $start = 0;
1028
587ec647 1029
43a31298 1030 if (isset($folder_prefix) && ($folder_prefix != '')) {
ff245fbd 1031 $start = substr_count($folder_prefix,$delimiter);
1032 if (strrpos($folder_prefix, $delimiter) == (strlen($folder_prefix)-1)) {
ff245fbd 1033 $mailboxes->mailboxname_full = substr($folder_prefix,0, (strlen($folder_prefix)-1));
1034 } else {
1035 $mailboxes->mailboxname_full = $folder_prefix;
1036 $start++;
1037 }
1038 $mailboxes->mailboxname_sub = $mailboxes->mailboxname_full;
1039 } else {
1040 $start = 0;
1041 }
78bc908d 1042
9d8d1ebd 1043 $cnt = count($mbx_ary);
e4c6fe41 1044 for ($i=0; $i < $cnt; $i++) {
ff245fbd 1045 if ($mbx_ary[$i]['mbx'] !='' ) {
1046 $mbx = new mailboxes();
1047 $mailbox = $mbx_ary[$i]['mbx'];
cc82c2b7 1048
4669e892 1049 /*
cc82c2b7 1050 sent subfolders messes up using existing code as subfolders
1051 were marked, but the parents were ordered somewhere else in
1052 the list, despite having "special folders at top" option set.
1053 Need a better method than this.
97a5e85f 1054 */
8edc9f31 1055/*
97a5e85f 1056 if ($mailbox == 'INBOX') {
1057 $mbx->is_special = true;
1058 } elseif (stristr($trash_folder , $mailbox)) {
1059 $mbx->is_special = true;
1060 } elseif (stristr($sent_folder , $mailbox)) {
1061 $mbx->is_special = true;
1062 } elseif (stristr($draft_folder , $mailbox)) {
1063 $mbx->is_special = true;
1064 }
cc82c2b7 1065
ff245fbd 1066 switch ($mailbox) {
1067 case 'INBOX':
1068 $mbx->is_inbox = true;
1069 $mbx->is_special = true;
8edc9f31 1070 $mbx_ary[$i]['noselect'] = false;
ff245fbd 1071 break;
1072 case $trash_folder:
1073 $mbx->is_trash = true;
1074 $mbx->is_special = true;
1075 break;
1076 case $sent_folder:
1077 $mbx->is_sent = true;
1078 $mbx->is_special = true;
1079 break;
1080 case $draft_folder:
1081 $mbx->is_draft = true;
1082 $mbx->is_special = true;
1083 break;
1084 }
8edc9f31 1085*/
1086 $mbx->is_special |= ($mbx->is_inbox = (strtoupper($mailbox) == 'INBOX'));
1087 $mbx->is_special |= ($mbx->is_trash = isTrashMailbox($mailbox));
1088 $mbx->is_special |= ($mbx->is_sent = isSentMailbox($mailbox));
1089 $mbx->is_special |= ($mbx->is_draft = isDraftMailbox($mailbox));
1090 if (!$mbx->is_special)
fda01075 1091 $mbx->is_special = boolean_hook_function('special_mailbox', $mailbox, 1);
4669e892 1092
ff245fbd 1093 if (isset($mbx_ary[$i]['unseen'])) {
1094 $mbx->unseen = $mbx_ary[$i]['unseen'];
1095 }
1096 if (isset($mbx_ary[$i]['nummessages'])) {
1097 $mbx->total = $mbx_ary[$i]['nummessages'];
1098 }
1099
1100 $mbx->is_noselect = $mbx_ary[$i]['noselect'];
86c2763d 1101 $mbx->is_noinferiors = $mbx_ary[$i]['noinferiors'];
ff245fbd 1102
60b5724d 1103 $r_del_pos = strrpos($mbx_ary[$i]['mbx'], $delimiter);
ff245fbd 1104 if ($r_del_pos) {
587ec647 1105 $mbx->mailboxname_sub = substr($mbx_ary[$i]['mbx'],$r_del_pos+1);
ff245fbd 1106 } else { /* mailbox is root folder */
587ec647 1107 $mbx->mailboxname_sub = $mbx_ary[$i]['mbx'];
ff245fbd 1108 }
1109 $mbx->mailboxname_full = $mbx_ary[$i]['mbx'];
38068e69 1110
9871bdaa 1111 $mailboxes->addMbx($mbx, $delimiter, $start, $list_special_folders_first);
ff245fbd 1112 }
60b5724d 1113 }
587ec647 1114 sqimap_utf7_decode_mbx_tree($mailboxes);
483f9ef9 1115 sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
60b5724d 1116 return $mailboxes;
1117}
259faa39 1118
3131bc8d 1119/**
1120 * @param object $mbx_tree
1121 * @since 1.5.0
1122 */
587ec647 1123function sqimap_utf7_decode_mbx_tree(&$mbx_tree) {
bd27b70b 1124 if (strtoupper($mbx_tree->mailboxname_full) == 'INBOX')
d94c8d61 1125 $mbx_tree->mailboxname_sub = _("INBOX");
1126 else
1127 $mbx_tree->mailboxname_sub = imap_utf7_decode_local($mbx_tree->mailboxname_sub);
587ec647 1128 if ($mbx_tree->mbxs) {
1129 $iCnt = count($mbx_tree->mbxs);
1130 for ($i=0;$i<$iCnt;++$i) {
d94c8d61 1131 $mbxs_tree->mbxs[$i] = sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs[$i]);
587ec647 1132 }
1133 }
483f9ef9 1134}
1135
3131bc8d 1136/**
1137 * @param object $mbx_tree
1138 * @param array $aMbxs
1139 * @since 1.5.0
1140 */
483f9ef9 1141function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs) {
5c300c60 1142 if ($mbx_tree)
483f9ef9 1143 $aMbxs[] =& $mbx_tree;
1144 if ($mbx_tree->mbxs) {
1145 $iCnt = count($mbx_tree->mbxs);
1146 for ($i=0;$i<$iCnt;++$i) {
5c300c60 1147 sqimap_tree_to_ref_array($mbx_tree->mbxs[$i],$aMbxs);
483f9ef9 1148 }
1149 }
4669e892 1150}
483f9ef9 1151
3131bc8d 1152/**
1153 * @param stream $imap_stream imap connection resource
1154 * @param object $mbx_tree
1155 * @since since 1.5.0
1156 */
483f9ef9 1157function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree) {
1158 global $unseen_notify, $unseen_type, $trash_folder,$move_to_trash;
8d8da447 1159 $aMbxs = $aQuery = array();
483f9ef9 1160 sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
1161 // remove the root node
1162 array_shift($aMbxs);
1163
1164 if($unseen_notify == 3) {
1165 $cnt = count($aMbxs);
1166 for($i=0;$i<$cnt;++$i) {
5c300c60 1167 $oMbx =& $aMbxs[$i];
1168 if (!$oMbx->is_noselect) {
483f9ef9 1169 $mbx = $oMbx->mailboxname_full;
5c300c60 1170 if ($unseen_type == 2 ||
1171 ($move_to_trash && $oMbx->mailboxname_full == $trash_folder)) {
568cb884 1172 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (MESSAGES UNSEEN)';
5c300c60 1173 } else {
568cb884 1174 $query = 'STATUS ' . sqimap_encode_mailbox_name($mbx) . ' (UNSEEN)';
5c300c60 1175 }
483f9ef9 1176 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
5c300c60 1177 } else {
1178 $oMbx->unseen = $oMbx->total = false;
1179 $tag = false;
1180 }
1181 $oMbx->tag = $tag;
1182 $aMbxs[$i] =& $oMbx;
483f9ef9 1183 }
1184 // execute all the queries at once
1185 $aResponse = sqimap_run_pipelined_command ($imap_stream, $aQuery, false, $aServerResponse, $aServerMessage);
1186 $cnt = count($aMbxs);
1187 for($i=0;$i<$cnt;++$i) {
5c300c60 1188 $oMbx =& $aMbxs[$i];
1189 $tag = $oMbx->tag;
1190 if ($tag && $aServerResponse[$tag] == 'OK') {
1191 $sResponse = implode('', $aResponse[$tag]);
483f9ef9 1192 if (preg_match('/UNSEEN\s+([0-9]+)/i', $sResponse, $regs)) {
1193 $oMbx->unseen = $regs[1];
1194 }
1195 if (preg_match('/MESSAGES\s+([0-9]+)/i', $sResponse, $regs)) {
1196 $oMbx->total = $regs[1];
5c300c60 1197 }
1198 }
1199 unset($oMbx->tag);
1200 }
483f9ef9 1201 } else if ($unseen_notify == 2) { // INBOX only
1202 $cnt = count($aMbxs);
1203 for($i=0;$i<$cnt;++$i) {
5c300c60 1204 $oMbx =& $aMbxs[$i];
1205 if (strtoupper($oMbx->mailboxname_full) == 'INBOX' ||
1206 ($move_to_trash && $oMbx->mailboxname_full == $trash_folder)) {
4669e892 1207 if ($unseen_type == 2 ||
5c300c60 1208 ($oMbx->mailboxname_full == $trash_folder && $move_to_trash)) {
1209 $aStatus = sqimap_status_messages($imap_stream,$oMbx->mailboxname_full);
1210 $oMbx->unseen = $aStatus['UNSEEN'];
1211 $oMbx->total = $aStatus['MESSAGES'];
1212 } else {
1213 $oMbx->unseen = sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full);
1214 }
1215 $aMbxs[$i] =& $oMbx;
1216 if (!$move_to_trash && $trash_folder) {
1217 break;
1218 } else {
1219 // trash comes after INBOX
1220 if ($oMbx->mailboxname_full == $trash_folder) {
1221 break;
1222 }
1223 }
1224 }
1225 }
4669e892 1226 }
1227}
587ec647 1228
3131bc8d 1229?>