imap_mailbox.php 45 KB

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