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