Source for file imap_mailbox.php

Documentation is available at imap_mailbox.php

  1. <?php
  2.  
  3. /**
  4.  * imap_mailbox.php
  5.  *
  6.  * This implements all functions that manipulate mailboxes
  7.  *
  8.  * @copyright 1999-2014 The SquirrelMail Project Team
  9.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  10.  * @version $Id: imap_mailbox.php 14420 2014-01-01 20:33:20Z pdontthink $
  11.  * @package squirrelmail
  12.  * @subpackage imap
  13.  */
  14.  
  15. /** UTF7 support */
  16. require_once(SM_PATH 'functions/imap_utf7_local.php');
  17.  
  18.  
  19. /**
  20.  * Mailboxes class
  21.  *
  22.  * FIXME. This class should be extracted and placed in a separate file that
  23.  * can be included before we start the session. That makes caching of the tree
  24.  * possible. On a refresh mailboxes from left_main.php the only function that
  25.  * should be called is the sqimap_get_status_mbx_tree. In case of subscribe
  26.  * / rename / delete / new we have to create methods for adding/changing the
  27.  * mailbox in the mbx_tree without the need for a refresh.
  28.  *
  29.  * Some code fragments are present in 1.3.0 - 1.4.4.
  30.  * @package squirrelmail
  31.  * @subpackage imap
  32.  * @since 1.5.0
  33.  */
  34. class mailboxes {
  35.     var $mailboxname_full = ''$mailboxname_sub''$is_noselect = false$is_noinferiors = false,
  36.         $is_special = false$is_root = false$is_inbox = false$is_sent = false,
  37.         $is_trash = false$is_draft = false,  $mbxs = array(),
  38.         $unseen = false$total = false$recent = false;
  39.  
  40.     function addMbx($mbx$delimiter$start$specialfirst{
  41.         $ary explode($delimiter$mbx->mailboxname_full);
  42.         $mbx_parent =$this;
  43.         for ($i $start$c count($ary)-1$i $c$i++{
  44.             $mbx_childs =$mbx_parent->mbxs;
  45.             $found false;
  46.             if ($mbx_childs{
  47.                 foreach ($mbx_childs as $key => $parent{
  48.                     if ($parent->mailboxname_sub == $ary[$i]{
  49.                         $mbx_parent =$mbx_parent->mbxs[$key];
  50.                         $found true;
  51.                         break;
  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.                 }
  62.                 $no_select_mbx->mailboxname_sub $ary[$i];
  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.     }
  73. }
  74.  
  75. /**
  76.  * array callback used for sorting in mailboxes class
  77.  * @param object $a 
  78.  * @param object $b 
  79.  * @return integer see php strnatcasecmp()
  80.  * @since 1.3.0
  81.  */
  82. function sortSpecialMbx($a$b{
  83.     if ($a->is_inbox{
  84.         $acmp '0'$a->mailboxname_full;
  85.     else if ($a->is_special{
  86.         $acmp '1'$a->mailboxname_full;
  87.     else {
  88.         $acmp '2' $a->mailboxname_full;
  89.     }
  90.     if ($b->is_inbox{
  91.         $bcmp '0'$b->mailboxname_full;
  92.     }else if ($b->is_special{
  93.         $bcmp '1' $b->mailboxname_full;
  94.     else {
  95.         $bcmp '2' $b->mailboxname_full;
  96.     }
  97.     return strnatcasecmp($acmp$bcmp);
  98. }
  99.  
  100. /**
  101.  * @param array $ary 
  102.  * @return array 
  103.  * @since 1.5.0
  104.  */
  105. function compact_mailboxes_response($ary{
  106.     /*
  107.      * Workaround for mailboxes returned as literal
  108.      * FIXME : Doesn't work if the mailbox name is multiple lines
  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 (preg_match('/^(\* [A-Z]+.*)\{[0-9]+\}([ \n\r\t]*)$/'$ary[$i]$regs)) {
  114.                 $ary[$i$regs[1'"' addslashes(trim($ary[$i+1])) '"' $regs[2];
  115.                 array_splice($ary$i+12);
  116.             }
  117.         }
  118.     }
  119.     /* remove duplicates and ensure array is contiguous */
  120.     return array_values(array_unique($ary));
  121. }
  122.  
  123. /**
  124.  * Extract the mailbox name from an untagged LIST (7.2.2) or LSUB (7.2.3) answer
  125.  * (LIST|LSUB) (<Flags list>) (NIL|"<separator atom>") <mailbox name string>\r\n
  126.  * mailbox name in quoted string MUST be unquoted and stripslashed (sm API)
  127.  *
  128.  * Originally stored in functions/strings.php. Since 1.2.6 stored in
  129.  * functions/imap_mailbox.php
  130.  * @param string $line imap LIST/LSUB response line
  131.  * @return string mailbox name
  132.  */
  133. function find_mailbox_name($line{
  134.     if (preg_match('/^\* (?:LIST|LSUB) \([^\)]*\) (?:NIL|\"[^\"]*\") ([^\r\n]*)[\r\n]*$/i'$line$regs)) {
  135.         if (substr($regs[1]01== '"')
  136.             return stripslashes(substr($regs[1]1-1));
  137.         return $regs[1];
  138.     }
  139.     return '';
  140. }
  141.  
  142. /**
  143.  * Detects if mailbox has noselect flag (can't store messages)
  144.  * In versions older than 1.4.5 function checks only LSUB responses
  145.  * and can produce pcre warnings.
  146.  * @param string $lsub_line mailbox line from untagged LIST or LSUB response
  147.  * @return bool whether this is a Noselect mailbox.
  148.  * @since 1.3.2
  149.  */
  150. function check_is_noselect ($lsub_line{
  151.     return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noselect[^\)]*\)/i"$lsub_line);
  152. }
  153.  
  154. /**
  155.  * Detects if mailbox has noinferiors flag (can't store subfolders)
  156.  * @param string $lsub_line mailbox line from untagged LIST or LSUB response
  157.  * @return bool whether this is a Noinferiors mailbox.
  158.  * @since 1.5.0
  159.  */
  160. function check_is_noinferiors ($lsub_line{
  161.     return preg_match("/^\* (LSUB|LIST) \([^\)]*\\\\Noinferiors[^\)]*\)/i"$lsub_line);
  162. }
  163.  
  164. /**
  165.  * Detects mailbox's parent folder
  166.  *
  167.  * If $haystack is a full mailbox name, and $needle is the mailbox
  168.  * separator character, returns the second last part of the full
  169.  * mailbox name (i.e. the mailbox's parent mailbox)
  170.  *
  171.  * Originally stored in functions/strings.php. Since 1.2.6 stored in
  172.  * functions/imap_mailbox.php
  173.  * @param string $haystack full mailbox name
  174.  * @param string $needle delimiter
  175.  * @return string parent mailbox
  176.  */
  177. function readMailboxParent($haystack$needle{
  178.     if ($needle == ''{
  179.         $ret '';
  180.     else {
  181.         $parts explode($needle$haystack);
  182.         $elem array_pop($parts);
  183.         while ($elem == '' && count($parts)) {
  184.             $elem array_pop($parts);
  185.         }
  186.         $ret join($needle$parts);
  187.     }
  188.     return$ret );
  189. }
  190.  
  191. /**
  192.  * Check if $subbox is below the specified $parentbox
  193.  * @param string $subbox potential sub folder
  194.  * @param string $parentbox potential parent
  195.  * @return boolean 
  196.  * @since 1.2.3
  197.  */
  198. function isBoxBelow$subbox$parentbox {
  199.     global $delimiter;
  200.     /*
  201.      * Eliminate the obvious mismatch, where the
  202.      * subfolder path is shorter than that of the potential parent
  203.      */
  204.     if strlen($subboxstrlen($parentbox) ) {
  205.       return false;
  206.     }
  207.     /* check for delimiter */
  208.     if (substr($parentbox,-1!= $delimiter{
  209.         $parentbox .= $delimiter;
  210.     }
  211.  
  212.     return (substr($subbox,0,strlen($parentbox)) == $parentbox);
  213. }
  214.  
  215. /**
  216.  * Defines special mailboxes: given a mailbox name, it checks if this is a
  217.  * "special" one: INBOX, Trash, Sent or Draft.
  218.  *
  219.  * Since 1.2.5 function includes special_mailbox hook.
  220.  *
  221.  * Since 1.4.3 hook supports more than one plugin.
  222.  *
  223. //FIXME: make $subfolders_of_inbox_are_special a configuration setting in conf.pl and config.php
  224.  * Since 1.4.22/1.5.2, the administrator can add
  225.  * $subfolders_of_inbox_are_special = TRUE;
  226.  * to config/config_local.php and all subfolders
  227.  * of the INBOX will be treated as special.
  228.  *
  229.  * @param string $box mailbox name
  230.  * @param boolean $include_subs (since 1.5.2) if true, subfolders of system
  231.  *   folders are special. if false, subfolders are not special mailboxes
  232.  *   unless they are tagged as special in 'special_mailbox' hook.
  233.  * @return boolean 
  234.  * @since 1.2.3
  235.  */
  236. function isSpecialMailbox($box,$include_subs=true{
  237.     global $subfolders_of_inbox_are_special;
  238.     $ret ( ($subfolders_of_inbox_are_special && isInboxMailbox($box,$include_subs)) ||
  239.              (!$subfolders_of_inbox_are_special && strtolower($box== 'inbox'||
  240.              isTrashMailbox($box,$include_subs|| 
  241.              isSentMailbox($box,$include_subs|| 
  242.              isDraftMailbox($box,$include_subs) );
  243.  
  244.     if !$ret {
  245.         $ret boolean_hook_function('special_mailbox'$box1);
  246.     }
  247.     return $ret;
  248. }
  249.  
  250. /**
  251.  * Detects if mailbox is the Inbox folder or subfolder of the Inbox
  252.  *
  253.  * @param string $box The mailbox name to test
  254.  * @param boolean $include_subs If true, subfolders of system folders
  255.  *                               are special.  If false, subfolders are
  256.  *                               not special mailboxes.
  257.  *
  258.  * @return boolean Whether this is the Inbox or a child thereof.
  259.  *
  260.  * @since 1.4.22
  261.  */
  262. function isInboxMailbox($box$include_subs=TRUE{
  263.    return ((strtolower($box== 'inbox')
  264.         || ($include_subs && isBoxBelow(strtolower($box)'inbox')));
  265. }
  266.  
  267. /**
  268.  * Detects if mailbox is a Trash folder or subfolder of Trash
  269.  * @param string $box mailbox name
  270.  * @param boolean $include_subs (since 1.5.2) if true, subfolders of system
  271.  *   folders are special. if false, subfolders are not special mailboxes.
  272.  * @return bool whether this is a Trash folder
  273.  * @since 1.4.0
  274.  */
  275. function isTrashMailbox ($box,$include_subs=true{
  276.     global $trash_folder$move_to_trash;
  277.     return $move_to_trash && $trash_folder &&
  278.            $box == $trash_folder || 
  279.              ($include_subs && isBoxBelow($box$trash_folder)) );
  280. }
  281.  
  282. /**
  283.  * Detects if mailbox is a Sent folder or subfolder of Sent
  284.  * @param string $box mailbox name
  285.  * @param boolean $include_subs (since 1.5.2) if true, subfolders of system
  286.  *   folders are special. if false, subfolders are not special mailboxes.
  287.  * @return bool whether this is a Sent folder
  288.  * @since 1.4.0
  289.  */
  290. function isSentMailbox($box,$include_subs=true{
  291.    global $sent_folder$move_to_sent;
  292.    return $move_to_sent && $sent_folder &&
  293.           $box == $sent_folder || 
  294.             ($include_subs && isBoxBelow($box$sent_folder)) );
  295. }
  296.  
  297. /**
  298.  * Detects if mailbox is a Drafts folder or subfolder of Drafts
  299.  * @param string $box mailbox name
  300.  * @param boolean $include_subs (since 1.5.2) if true, subfolders of system
  301.  *   folders are special. if false, subfolders are not special mailboxes.
  302.  * @return bool whether this is a Draft folder
  303.  * @since 1.4.0
  304.  */
  305. function isDraftMailbox($box,$include_subs=true{
  306.    global $draft_folder$save_as_draft;
  307.    return $save_as_draft &&
  308.           $box == $draft_folder || 
  309.             ($include_subs && isBoxBelow($box$draft_folder)) );
  310. }
  311.  
  312. /**
  313.  * Is the given folder "sent-like" in nature?
  314.  *
  315.  * The most obvious use of this is to know what folders you usually
  316.  * want to show the To field instead of the From field on the mailbox list
  317.  *
  318.  * This function returns TRUE if the given folder is the sent
  319.  * folder (or any of its subfolders) or if it is the draft
  320.  * folder (or any of its subfolders)
  321.  *
  322.  * @param string $mailbox 
  323.  *
  324.  * @return boolean See explanation above
  325.  *
  326.  */
  327. function handleAsSent($mailbox{
  328.     global $handleAsSent_result;
  329.  
  330.     /* First check if this is the sent or draft folder. */
  331.     $handleAsSent_result isSentMailbox($mailbox|| isDraftMailbox($mailbox);
  332.  
  333.     /* Then check the result of the handleAsSent hook. */
  334.     do_hook('check_handleAsSent_result'$mailbox);
  335.  
  336.     /* And return the result. */
  337.     return $handleAsSent_result;
  338. }
  339.  
  340. /**
  341.  * Expunges a mailbox
  342.  *
  343.  * WARNING: Select mailbox before calling this function.
  344.  *
  345.  * permanently removes all messages that have the \Deleted flag
  346.  * set from the selected mailbox. See EXPUNGE command chapter in
  347.  * IMAP RFC.
  348.  * @param stream $imap_stream imap connection resource
  349.  * @param string $mailbox mailbox name (unused since 1.1.3).
  350.  * @param boolean $handle_errors error handling control (displays error_box on error).
  351.  * @param mixed $id (since 1.3.0) integer message id or array with integer ids
  352.  * @return integer number of expunged messages
  353.  * @since 1.0 or older
  354.  */
  355. function sqimap_mailbox_expunge ($imap_stream$mailbox$handle_errors true$id=''{
  356.     if ($id{
  357.         if (is_array($id)) {
  358.             $id sqimap_message_list_squisher($id);
  359.         }
  360.         $id ' '.$id;
  361.         $uid TRUE;
  362.     else {
  363.         $uid false;
  364.     }
  365.     $read sqimap_run_command($imap_stream'EXPUNGE'.$id$handle_errors,
  366.                                $response$message$uid);
  367.     $cnt 0;
  368.  
  369.     if (is_array($read)) {
  370.         foreach ($read as $r{
  371.             if (preg_match('/^\*\s[0-9]+\sEXPUNGE/AUi',$r,$regs)) {
  372.                 $cnt++;
  373.             }
  374.         }
  375.     }
  376.     return $cnt;
  377. }
  378.  
  379. /**
  380.  * Checks whether or not the specified mailbox exists
  381.  *
  382.  * @param stream $imap_stream imap connection resource
  383.  * @param string $mailbox mailbox name
  384.  * @param array $mailboxlist (since 1.5.1) optional array of mailboxes from
  385.  *   sqimap_get_mailboxes() (to avoid having to talk to imap server)
  386.  * @return boolean 
  387.  * @since 1.0 or older
  388.  */
  389. function sqimap_mailbox_exists ($imap_stream$mailbox$mailboxlist=null{
  390.     if (!isset($mailbox|| empty($mailbox)) {
  391.         return false;
  392.     }
  393.  
  394.     if (is_array($mailboxlist)) {
  395.         // use previously retrieved mailbox list
  396.         foreach ($mailboxlist as $mbox{
  397.             if ($mbox['unformatted-dm'== $mailboxreturn true}
  398.         }
  399.         return false;
  400.     else {
  401.         // go to imap server
  402.         $mbx sqimap_run_command($imap_stream'LIST "" ' sqimap_encode_mailbox_name($mailbox),
  403.                                   true$response$message);
  404.         return isset($mbx[0]);
  405.     }
  406. }
  407.  
  408. /**
  409.  * Selects a mailbox
  410.  * Before 1.3.0 used more arguments and returned data depended on those arguments.
  411.  * @param stream $imap_stream imap connection resource
  412.  * @param string $mailbox mailbox name
  413.  * @return array results of select command (on success - permanentflags, flags and rights)
  414.  * @since 1.0 or older
  415.  */
  416. function sqimap_mailbox_select ($imap_stream$mailbox{
  417.     if (empty($mailbox)) {
  418.         return;
  419.     }
  420.  
  421.     // cleanup $mailbox in order to prevent IMAP injection attacks
  422.     $mailbox str_replace(array("\r","\n")array("",""),$mailbox);
  423.  
  424.     /**
  425.      * Default UW IMAP server configuration allows to access other files
  426.      * on server. $imap_server_type is not checked because interface can
  427.      * be used with 'other' or any other server type setting. $mailbox
  428.      * variable can be modified in any script that uses variable from GET
  429.      * or POST. This code blocks all standard SquirrelMail IMAP API requests
  430.      * that use mailbox with full path (/etc/passwd) or with ../ characters
  431.      * in path (../../etc/passwd)
  432.      */
  433.     if (strstr($mailbox'../'|| substr($mailbox01== '/'{
  434.         global $oTemplate;
  435.         error_box(sprintf(_("Invalid mailbox name: %s"),sm_encode_html_special_chars($mailbox)));
  436.         sqimap_logout($imap_stream);
  437.         $oTemplate->display('footer.tpl');
  438.         die();
  439.     }
  440.  
  441.     $read sqimap_run_command($imap_stream'SELECT ' sqimap_encode_mailbox_name($mailbox),
  442.                                true$response$message);
  443.     $result array();
  444.     for ($i 0$cnt count($read)$i $cnt$i++{
  445.         if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i]$regs)) {
  446.             $result[strtoupper($regs[1])$regs[2];
  447.         else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i]$regs)) {
  448.             $result[strtoupper($regs[2])$regs[1];
  449.         else {
  450.             if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i]$regs)) {
  451.                 $regs[1]=trim(preg_replace (  array ("/\(/","/\)/","/\]/",''$regs[1])) ;
  452.                 $result['PERMANENTFLAGS'explode(' ',strtolower($regs[1]));
  453.             else if (preg_match("/FLAGS(.*)/i",$read[$i]$regs)) {
  454.                 $regs[1]=trim(preg_replace (  array ("/\(/","/\)/",''$regs[1])) ;
  455.                 $result['FLAGS'explode(' ',strtolower($regs[1]));
  456.             }
  457.         }
  458.     }
  459.     if (!isset($result['PERMANENTFLAGS'])) {
  460.         $result['PERMANENTFLAGS'$result['FLAGS'];
  461.     }
  462.     if (preg_match('/^\[(.+)\]/',$message$regs)) {
  463.         $result['RIGHTS']=strtoupper($regs[1]);
  464.     }
  465.  
  466.     return $result;
  467. }
  468.  
  469. /**
  470.  * Creates a folder.
  471.  *
  472.  * Mailbox is automatically subscribed.
  473.  *
  474.  * Set $type to string that does not match 'noselect' (case insensitive),
  475.  * if you don't want to prepend delimiter to mailbox name. Please note
  476.  * that 'noinferiors' might be used someday as keyword for folders
  477.  * that store only messages.
  478.  * @param stream $imap_steam imap connection resource
  479.  * @param string $mailbox mailbox name
  480.  * @param string $type folder type.
  481.  * @since 1.0 or older
  482.  */
  483. function sqimap_mailbox_create ($imap_stream$mailbox$type{
  484.     global $delimiter;
  485.     if (strtolower($type== 'noselect'{
  486.         $create_mailbox $mailbox $delimiter;
  487.     else {
  488.         $create_mailbox $mailbox;
  489.     }
  490.  
  491.     $read_ary sqimap_run_command($imap_stream'CREATE ' .
  492.                                    sqimap_encode_mailbox_name($create_mailbox),
  493.                                    true$response$message);
  494.     sqimap_subscribe ($imap_stream$mailbox);
  495. }
  496.  
  497. /**
  498.  * Subscribes to an existing folder.
  499.  * @param stream $imap_stream imap connection resource
  500.  * @param string $mailbox mailbox name
  501.  * @param boolean $debug (since 1.5.1)
  502.  * @since 1.0 or older
  503.  */
  504. function sqimap_subscribe ($imap_stream$mailbox,$debug=true{
  505.     $read_ary sqimap_run_command($imap_stream'SUBSCRIBE ' .
  506.                                    sqimap_encode_mailbox_name($mailbox),
  507.                                    $debug$response$message);
  508. }
  509.  
  510. /**
  511.  * Unsubscribes from an existing folder
  512.  * @param stream $imap_stream imap connection resource
  513.  * @param string $mailbox mailbox name
  514.  * @since 1.0 or older
  515.  */
  516. function sqimap_unsubscribe ($imap_stream$mailbox{
  517.     $read_ary sqimap_run_command($imap_stream'UNSUBSCRIBE ' .
  518.                                    sqimap_encode_mailbox_name($mailbox),
  519.                                    false$response$message);
  520. }
  521.  
  522. /**
  523.  * Deletes the given folder
  524.  * Since 1.2.6 and 1.3.0 contains rename_or_delete_folder hook
  525.  * @param stream $imap_stream imap connection resource
  526.  * @param string $mailbox mailbox name
  527.  * @since 1.0 or older
  528.  */
  529. function sqimap_mailbox_delete ($imap_stream$mailbox{
  530.     global $data_dir$username;
  531.     sqimap_unsubscribe ($imap_stream$mailbox);
  532.  
  533.     if (sqimap_mailbox_exists($imap_stream$mailbox)) {
  534.  
  535.         $read_ary sqimap_run_command($imap_stream'DELETE ' .
  536.                                        sqimap_encode_mailbox_name($mailbox),
  537.                                        true$response$message);
  538.         if ($response !== 'OK'{
  539.             // subscribe again
  540.             sqimap_subscribe ($imap_stream$mailbox);
  541.         else {
  542.             $temp array(&$mailbox'delete''');
  543.             do_hook('rename_or_delete_folder'$temp);
  544.             removePref($data_dir$username"thread_$mailbox");
  545.             removePref($data_dir$username"collapse_folder_$mailbox");
  546.         }
  547.     }
  548. }
  549.  
  550. /**
  551.  * Determines if the user is subscribed to the folder or not
  552.  * @param stream $imap_stream imap connection resource
  553.  * @param string $mailbox mailbox name
  554.  * @return boolean 
  555.  * @since 1.2.0
  556.  */
  557. function sqimap_mailbox_is_subscribed($imap_stream$folder{
  558.     $boxesall sqimap_mailbox_list ($imap_stream);
  559.     foreach ($boxesall as $ref{
  560.         if ($ref['unformatted'== $folder{
  561.             return true;
  562.         }
  563.     }
  564.     return false;
  565. }
  566.  
  567. /**
  568.  * Renames a mailbox.
  569.  * Since 1.2.6 and 1.3.0 contains rename_or_delete_folder hook
  570.  * @param stream $imap_stream imap connection resource
  571.  * @param string $old_name mailbox name
  572.  * @param string $new_name new mailbox name
  573.  * @since 1.2.3
  574.  */
  575. function sqimap_mailbox_rename$imap_stream$old_name$new_name {
  576.     if $old_name != $new_name {
  577.         global $delimiter$imap_server_type$data_dir$username;
  578.         if substr$old_name-== $delimiter  {
  579.             $old_name substr$old_name0strlen$old_name );
  580.             $new_name substr$new_name0strlen$new_name );
  581.             $postfix $delimiter;
  582.         else {
  583.             $postfix '';
  584.         }
  585.  
  586.         $boxesall sqimap_mailbox_list_all($imap_stream);
  587.         $cmd 'RENAME ' sqimap_encode_mailbox_name($old_name.
  588.                      ' ' sqimap_encode_mailbox_name($new_name);
  589.         $data sqimap_run_command($imap_stream$cmdtrue$response$message);
  590.         sqimap_unsubscribe($imap_stream$old_name.$postfix);
  591.         $oldpref_thread getPref($data_dir$username'thread_'.$old_name.$postfix);
  592.         $oldpref_collapse getPref($data_dir$username'collapse_folder_'.$old_name.$postfix);
  593.         removePref($data_dir$username'thread_'.$old_name.$postfix);
  594.         removePref($data_dir$username'collapse_folder_'.$old_name.$postfix);
  595.         sqimap_subscribe($imap_stream$new_name.$postfix);
  596.         setPref($data_dir$username'thread_'.$new_name.$postfix$oldpref_thread);
  597.         setPref($data_dir$username'collapse_folder_'.$new_name.$postfix$oldpref_collapse);
  598.         $temp array(&$old_name'rename'&$new_name);
  599.         do_hook('rename_or_delete_folder'$temp);
  600.         $l strlen$old_name 1;
  601.         $p 'unformatted';
  602.  
  603.         foreach ($boxesall as $box{
  604.             if (substr($box[$p]0$l== $old_name $delimiter{
  605.                 $new_sub $new_name $delimiter substr($box[$p]$l);
  606.                 /* With Cyrus IMAPd >= 2.0 rename is recursive, so don't check for errors here */
  607.                 if ($imap_server_type == 'cyrus'{
  608.                     $cmd 'RENAME "' $box[$p'" "' $new_sub '"';
  609.                     $data sqimap_run_command($imap_stream$cmdfalse,
  610.                                                $response$message);
  611.                 }
  612.                 $was_subscribed sqimap_mailbox_is_subscribed($imap_stream$box[$p]);
  613.                 if $was_subscribed {
  614.                     sqimap_unsubscribe($imap_stream$box[$p]);
  615.                 }
  616.                 $oldpref_thread getPref($data_dir$username'thread_'.$box[$p]);
  617.                 $oldpref_collapse getPref($data_dir$username'collapse_folder_'.$box[$p]);
  618.                 removePref($data_dir$username'thread_'.$box[$p]);
  619.                 removePref($data_dir$username'collapse_folder_'.$box[$p]);
  620.                 if $was_subscribed {
  621.                     sqimap_subscribe($imap_stream$new_sub);
  622.                 }
  623.                 setPref($data_dir$username'thread_'.$new_sub$oldpref_thread);
  624.                 setPref($data_dir$username'collapse_folder_'.$new_sub$oldpref_collapse);
  625.                 $temp array(&$box[$p]'rename'&$new_sub);
  626.                 do_hook('rename_or_delete_folder'$temp);
  627.             }
  628.         }
  629.     }
  630. }
  631.  
  632. /**
  633.  * Formats a mailbox into parts for the $boxesall array
  634.  *
  635.  * The parts are:
  636.  * <ul>
  637.  *   <li>raw            - Raw LIST/LSUB response from the IMAP server
  638.  *   <li>formatted      - nicely formatted folder name
  639.  *   <li>unformatted    - unformatted, but with delimiter at end removed
  640.  *   <li>unformatted-dm - folder name as it appears in raw response
  641.  *   <li>unformatted-disp - unformatted without $folder_prefix
  642.  *   <li>id             - TODO: document me
  643.  *   <li>flags          - TODO: document me
  644.  * </ul>
  645.  * Before 1.2.0 used third argument for delimiter.
  646.  *
  647.  * Before 1.5.1 used second argument for lsub line. Argument was removed in order to use
  648.  * find_mailbox_name() on the raw input. Since 1.5.1 includes RFC3501 names in flags
  649.  * array (for example, "\NoSelect" in addition to "noselect")
  650.  * @param array $line 
  651.  * @return array 
  652.  * @since 1.0 or older
  653.  * @todo document id and flags keys in boxes array and function arguments.
  654.  */
  655. function sqimap_mailbox_parse ($line{
  656.     global $folder_prefix$delimiter;
  657.  
  658.     /* Process each folder line */
  659.     for ($g 0$cnt count($line)$g $cnt++$g{
  660.         /* Store the raw IMAP reply */
  661.         if (isset($line[$g])) {
  662.             $boxesall[$g]['raw'$line[$g];
  663.         else {
  664.             $boxesall[$g]['raw''';
  665.         }
  666.  
  667.         /* Count number of delimiters ($delimiter) in folder name */
  668.         $mailbox find_mailbox_name($line[$g]);
  669.         $dm_count substr_count($mailbox$delimiter);
  670.         if (substr($mailbox-1== $delimiter{
  671.             /* If name ends in delimiter, decrement count by one */
  672.             $dm_count--;
  673.         }
  674.  
  675.         /* Format folder name, but only if it's a INBOX.* or has a parent. */
  676.         $boxesallbyname[$mailbox$g;
  677.         $parentfolder readMailboxParent($mailbox$delimiter);
  678.         if ( (strtolower(substr($mailbox05)) == "inbox"||
  679.              (substr($mailbox0strlen($folder_prefix)) == $folder_prefix||
  680.              (isset($boxesallbyname[$parentfolder]&&
  681.               (strlen($parentfolder0) ) ) {
  682.             $indent $dm_count (substr_count($folder_prefix$delimiter));
  683.             if ($indent 0{
  684.                 $boxesall[$g]['formatted'str_repeat('&nbsp;&nbsp;'$indent);
  685.             else {
  686.                 $boxesall[$g]['formatted''';
  687.             }
  688.             $boxesall[$g]['formatted'.= imap_utf7_decode_local(readShortMailboxName($mailbox$delimiter));
  689.         else {
  690.             $boxesall[$g]['formatted']  imap_utf7_decode_local($mailbox);
  691.         }
  692.  
  693.         $boxesall[$g]['unformatted-dm'$mailbox;
  694.         if (substr($mailbox-1== $delimiter{
  695.             $mailbox substr($mailbox0strlen($mailbox1);
  696.         }
  697.         $boxesall[$g]['unformatted'$mailbox;
  698.         if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix{
  699.             $mailbox substr($mailboxstrlen($folder_prefix));
  700.         }
  701.         $boxesall[$g]['unformatted-disp'$mailbox;
  702.         $boxesall[$g]['id'$g;
  703.  
  704.         $boxesall[$g]['flags'array();
  705.         if (isset($line[$g]&& preg_match('/\(([^)]*)\)/',$line[$g],$regs) ) {
  706.             /**
  707.              * Since 1.5.1 flags are stored with RFC3501 naming
  708.              * and also the old way for backwards compatibility
  709.              * so for example "\NoSelect" and "noselect"
  710.              */
  711.             $flags trim($regs[1]);
  712.             if ($flags{
  713.                 $flagsarr explode(' ',$flags);
  714.                 $flagsarrnew=$flagsarr;
  715.                 // add old type
  716.                 foreach ($flagsarr as $flag{
  717.                     $flagsarrnew[]=strtolower(str_replace('\\''',$flag));
  718.                 }
  719.                 $boxesall[$g]['flags']=$flagsarrnew;
  720.             }
  721.         }
  722.     }
  723.     return $boxesall;
  724. }
  725.  
  726. /**
  727.  * Returns an array of mailboxes available.  Separated from sqimap_mailbox_option_list()
  728.  * below for template development.
  729.  * 
  730.  * @author Steve Brown
  731.  * @since 1.5.2
  732.  */
  733. function sqimap_mailbox_option_array($imap_stream$folder_skip 0$boxes 0,
  734.                                     $flag 'noselect'$use_long_format false {
  735.     global $username$data_dir$translate_special_folders$sent_folder
  736.         $trash_folder$draft_folder;
  737.  
  738.     $delimiter sqimap_get_delimiter($imap_stream);
  739.  
  740.     $mbox_options '';
  741.     if $use_long_format {
  742.         $shorten_box_names 0;
  743.     else {
  744.         $shorten_box_names getPref($data_dir$username'mailbox_select_style'SMPREF_MAILBOX_SELECT_INDENTED);
  745.     }
  746.  
  747.     if ($boxes == 0{
  748.         $boxes sqimap_mailbox_list($imap_stream);
  749.     }
  750.  
  751.     $a array();
  752.     foreach ($boxes as $boxes_part{
  753.         if ($flag == NULL || (is_array($boxes_part['flags'])
  754.                       && !in_array($flag$boxes_part['flags']))) {
  755.             $box $boxes_part['unformatted'];
  756.  
  757.             if ($folder_skip != && in_array($box$folder_skip) ) {
  758.                 continue;
  759.             }
  760.             $lowerbox strtolower($box);
  761.             // mailboxes are casesensitive => inbox.sent != inbox.Sent
  762.             // nevermind, to many dependencies this should be fixed!
  763.  
  764.             if (strtolower($box== 'inbox'// inbox is special and not casesensitive
  765.                 $box2 _("INBOX");
  766.             else {
  767.                 switch ($shorten_box_names)
  768.                 {
  769.                   case SMPREF_MAILBOX_SELECT_DELIMITED:
  770.                       if ($translate_special_folders && $boxes_part['unformatted-dm']==$sent_folder{
  771.                           /*
  772.                            * calculate pad level from number of delimiters. do it inside if control in order 
  773.                            * to reduce number of calculations. Other folders don't need it.
  774.                            */
  775.                           $pad str_pad('',(count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'.&nbsp;');
  776.                           // i18n: Name of Sent folder
  777.                           $box2 $pad _("Sent");
  778.                       elseif ($translate_special_folders && $boxes_part['unformatted-dm']==$trash_folder{
  779.                           $pad str_pad('',(count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'.&nbsp;');
  780.                           // i18n: Name of Trash folder
  781.                           $box2 $pad _("Trash");
  782.                       elseif ($translate_special_folders && $boxes_part['unformatted-dm']==$draft_folder{
  783.                           $pad str_pad('',(count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'.&nbsp;');
  784.                           // i18n: Name of Drafts folder
  785.                           $box2 $pad _("Drafts");
  786.                       else {
  787.                           $box2 str_replace('&amp;nbsp;&amp;nbsp;''.&nbsp;'sm_encode_html_special_chars($boxes_part['formatted']));
  788.                       }
  789.                     break;
  790.                   case SMPREF_MAILBOX_SELECT_INDENTED:
  791.                       if ($translate_special_folders && $boxes_part['unformatted-dm']==$sent_folder{
  792.                           $pad str_pad('',12 (count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'&nbsp;&nbsp;');
  793.                           $box2 $pad _("Sent");
  794.                       elseif ($translate_special_folders && $boxes_part['unformatted-dm']==$trash_folder{
  795.                           $pad str_pad('',12 (count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'&nbsp;&nbsp;');
  796.                           $box2 $pad _("Trash");
  797.                       elseif ($translate_special_folders && $boxes_part['unformatted-dm']==$draft_folder{
  798.                           $pad str_pad('',12 (count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'&nbsp;&nbsp;');
  799.                           $box2 $pad _("Drafts");
  800.                       else {
  801.                           $box2 str_replace('&amp;nbsp;&amp;nbsp;''&nbsp;&nbsp;'sm_encode_html_special_chars($boxes_part['formatted']));
  802.                       }
  803.                     break;
  804.                   default:  /* default, long names, style = 0 */
  805.                     $box2 str_replace(' ''&nbsp;'sm_encode_html_special_chars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
  806.                     break;
  807.                 }
  808.             }
  809.             
  810.             $a[sm_encode_html_special_chars($box)$box2;
  811.         }
  812.     }
  813.     
  814.     return $a;
  815. }
  816.  
  817. /**
  818.  * Returns list of options (to be echoed into select statement
  819.  * based on available mailboxes and separators
  820.  * Caller should surround options with <select ...> </select> and
  821.  * any formatting.
  822.  * @param stream $imap_stream imap connection resource to query for mailboxes
  823.  * @param array $show_selected array containing list of mailboxes to pre-select (0 if none)
  824.  * @param array $folder_skip array of folders to keep out of option list (compared in lower)
  825.  * @param $boxes list of already fetched boxes (for places like folder panel, where
  826.  *             you know these options will be shown 3 times in a row.. (most often unset).
  827.  * @param string $flag (since 1.4.1) flag to check for in mailbox flags, used to filter out mailboxes.
  828.  *            'noselect' by default to remove unselectable mailboxes.
  829.  *            'noinferiors' used to filter out folders that can not contain subfolders.
  830.  *            NULL to avoid flag check entirely.
  831.  *            NOTE: noselect and noiferiors are used internally. The IMAP representation is
  832.  *                  \NoSelect and \NoInferiors
  833.  * @param boolean $use_long_format (since 1.4.1) override folder display preference and always show full folder name.
  834.  * @return string html formated mailbox selection options
  835.  * @since 1.3.2
  836.  */
  837. function sqimap_mailbox_option_list($imap_stream$show_selected 0$folder_skip 0$boxes 0,
  838.                                     $flag 'noselect'$use_long_format false {
  839.     global $username$data_dir$translate_special_folders$sent_folder
  840.         $trash_folder$draft_folder;
  841.  
  842.     $boxes sqimap_mailbox_option_array($imap_stream$folder_skip$boxes$flag$use_long_format);
  843.     
  844.     $str '';
  845.     foreach ($boxes as $value=>$option{
  846.         $lowerbox strtolower(sm_encode_html_special_chars($value));
  847.         $sel false;
  848.         if ($show_selected != 0{
  849.             reset($show_selected);
  850.             while (!$sel && (list($x$valeach($show_selected))) {
  851.                 if (strtolower($value== strtolower(sm_encode_html_special_chars($val))) {
  852.                     $sel true;
  853.                 }
  854.             }
  855.         }
  856.         
  857.         $str .= '<option value="'$value .'"'($sel ' selected="selected"' '').'>'$option ."</option>\n";
  858.     }
  859.     
  860.     return $str;
  861. }
  862.  
  863. /**
  864.  * Returns sorted mailbox lists in several different ways.
  865.  *
  866.  * Since 1.5.1 most of the functionality has been moved to new function sqimap_get_mailboxes
  867.  *
  868.  * See comment on sqimap_mailbox_parse() for info about the returned array.
  869.  * @param resource $imap_stream imap connection resource
  870.  * @param boolean $force force update of mailbox listing. available since 1.4.2 and 1.5.0
  871.  * @return array list of mailboxes
  872.  * @since 1.0 or older
  873.  */
  874. function sqimap_mailbox_list($imap_stream$force=false{
  875.     global $boxesnew,$show_only_subscribed_folders;
  876.     if (!sqgetGlobalVar('boxesnew',$boxesnew,SQ_SESSION|| $force{
  877.         $boxesnew=sqimap_get_mailboxes($imap_stream,$force,$show_only_subscribed_folders);
  878.     }
  879.     return $boxesnew;
  880. }
  881.  
  882. /**
  883.  * Returns a list of all folders, subscribed or not
  884.  *
  885.  * Since 1.5.1 code moved to sqimap_get_mailboxes()
  886.  *
  887.  * @param stream $imap_stream imap connection resource
  888.  * @return array see sqimap_mailbox_parse()
  889.  * @since 1.0 or older
  890.  */
  891. function sqimap_mailbox_list_all($imap_stream{
  892.     global $show_only_subscribed_folders;
  893.     // fourth argument prevents registration of retrieved list of mailboxes in session
  894.     $boxes=sqimap_get_mailboxes($imap_stream,true,false,false);
  895.     return $boxes;
  896. }
  897.  
  898.  
  899. /**
  900.  * Gets the list of mailboxes for sqimap_maolbox_tree and sqimap_mailbox_list
  901.  *
  902.  * This is because both of those functions had duplicated logic, but with slightly different
  903.  * implementations. This will make both use the same implementation, which should make it
  904.  * easier to maintain and easier to modify in the future
  905.  * @param stream $imap_stream imap connection resource
  906.  * @param bool $force force a reload and ignore cache
  907.  * @param bool $show_only_subscribed controls listing of visible or all folders
  908.  * @param bool $session_register controls registration of retrieved data in session.
  909.  * @return object boxesnew - array of mailboxes and their attributes
  910.  * @since 1.5.1
  911.  */
  912. function sqimap_get_mailboxes($imap_stream,$force=false,$show_only_subscribed=true,$session_register=true{
  913.     global    $show_only_subscribed_folders,$noselect_fix_enable,$folder_prefix,
  914.     $inbox_subscribed false;
  915.     $listsubscribed sqimap_capability($imap_stream,'LIST-SUBSCRIBED');
  916.  
  917.     if ($show_only_subscribed$show_only_subscribed=$show_only_subscribed_folders}
  918.  
  919.     //require_once(SM_PATH . 'include/load_prefs.php');
  920.  
  921.     /**
  922.      * There are three main listing commands we can use in IMAP:
  923.      * LSUB        shows just the list of subscribed folders
  924.      *            may include flags, but these are not necessarily accurate or authoratative
  925.      *            \NoSelect has special meaning: the folder does not exist -OR- it means this
  926.      *            folder is not subscribed but children may be
  927.      *            [RFC-2060]
  928.      * LIST        this shows every mailbox on the system
  929.      *            flags are always included and are accurate and authoratative
  930.      *            \NoSelect means folder should not be selected
  931.      *            [RFC-2060]
  932.      * LIST (SUBSCRIBED)    implemented with LIST-SUBSCRIBED extension
  933.      *            this is like list but returns only subscribed folders
  934.      *            flag meanings are like LIST, not LSUB
  935.      *            \NonExistent means mailbox doesn't exist
  936.      *            \PlaceHolder means parent is not valid (selectable), but one or more children are
  937.      *            \NoSelect indeed means that the folder should not be selected
  938.      *            IMAPEXT-LIST-EXTENSIONS-04 August 2003 B. Leiba
  939.      */
  940.     if (!$show_only_subscribed{
  941.         $lsub 'LIST';
  942.         $sub_cache_name='list_cache';
  943.     }  elseif ($listsubscribed{
  944.         $lsub 'LIST (SUBSCRIBED)';
  945.         $sub_cache_name='listsub_cache';
  946.     else {
  947.         $lsub 'LSUB';
  948.         $sub_cache_name='lsub_cache';
  949.     }
  950.  
  951.     // Some IMAP servers allow subfolders to exist even if the parent folders do not
  952.     // This fixes some problems with the folder list when this is the case, causing the
  953.     // NoSelect folders to be displayed
  954.     if ($noselect_fix_enable{
  955.         $lsub_args "$lsub \"$folder_prefix\" \"*%\"";
  956.         $list_args "LIST \"$folder_prefix\" \"*%\"";
  957.     else {
  958.         $lsub_args "$lsub \"$folder_prefix\" \"*\"";
  959.         $list_args "LIST \"$folder_prefix\" \"*\"";
  960.     }
  961.  
  962.     // get subscribed mailbox list from cache (session)
  963.     // if not there, then get it from the imap server and store in cache
  964.  
  965.     if (!$force{
  966.         sqgetGlobalVar($sub_cache_name,$lsub_cache,SQ_SESSION);
  967.     }
  968.  
  969.     $lsub_assoc_ary=array();
  970.     if (!empty($lsub_cache)) {
  971.         $lsub_assoc_ary=$lsub_cache;
  972.     else {
  973.         $lsub_ary sqimap_run_command ($imap_stream$lsub_argstrue$response$message);
  974.         $lsub_ary compact_mailboxes_response($lsub_ary);
  975.         if (!empty($lsub_ary)) {
  976.             foreach ($lsub_ary as $rawline{
  977.                 $temp_mailbox_name=find_mailbox_name($rawline);
  978.                 $lsub_assoc_ary[$temp_mailbox_name]=$rawline;
  979.             }
  980.             unset($lsub_ary);
  981.             sqsession_register($lsub_assoc_ary,$sub_cache_name);
  982.         }
  983.     }
  984.  
  985.     // Now to get the mailbox flags
  986.     // The LSUB response may return \NoSelect flags, etc. but it is optional
  987.     // according to RFC3501, and even when returned it may not be accurate
  988.     // or authoratative. LIST will always return accurate results.
  989.     if (($lsub == 'LIST'|| ($lsub == 'LIST (SUBSCRIBED)')) {
  990.         // we've already done a LIST or LIST (SUBSCRIBED)
  991.         // and NOT a LSUB, so no need to do it again
  992.         $list_assoc_ary  $lsub_assoc_ary;
  993.     else {
  994.         // we did a LSUB so now we need to do a LIST
  995.         // first see if it is in cache
  996.         $list_cache_name='list_cache';
  997.         if (!$force{
  998.             sqgetGlobalVar($list_cache_name,$list_cache,SQ_SESSION);
  999.         }
  1000.  
  1001.         if (!empty($list_cache)) {
  1002.             $list_assoc_ary=$list_cache;
  1003.             // we could store this in list_cache_name but not necessary
  1004.         else {
  1005.             // not in cache so we need to go get it from the imap server
  1006.             $list_assoc_ary array();
  1007.             $list_ary sqimap_run_command($imap_stream$list_args,
  1008.                                            true$response$message);
  1009.             $list_ary compact_mailboxes_response($list_ary);
  1010.             if (!empty($list_ary)) {
  1011.                 foreach ($list_ary as $rawline{
  1012.                     $temp_mailbox_name=find_mailbox_name($rawline);
  1013.                     $list_assoc_ary[$temp_mailbox_name]=$rawline;
  1014.                 }
  1015.                 unset($list_ary);
  1016.                 sqsession_register($list_assoc_ary,$list_cache_name);
  1017.             }
  1018.         }
  1019.     }
  1020.  
  1021.     // If they aren't subscribed to the inbox, then add it anyway (if its in LIST)
  1022.     $inbox_subscribed=false;
  1023.     if (!empty($lsub_assoc_ary)) {
  1024.         foreach ($lsub_assoc_ary as $temp_mailbox_name=>$rawline{
  1025.             if (strtoupper($temp_mailbox_name== 'INBOX'{
  1026.                 $inbox_subscribed=true;
  1027.             }
  1028.         }
  1029.     }
  1030.     if (!$inbox_subscribed)  {
  1031.         if (!empty($list_assoc_ary)) {
  1032.             foreach ($list_assoc_ary as $temp_mailbox_name=>$rawline{
  1033.                 if (strtoupper($temp_mailbox_name== 'INBOX'{
  1034.                     $lsub_assoc_ary[$temp_mailbox_name]=$rawline;
  1035.                 }
  1036.             }
  1037.         }
  1038.     }
  1039.  
  1040.     // Now we have the raw output, we need to create an array of mailbox names we will return
  1041.     if (!$show_only_subscribed{
  1042.         $final_folders_assoc_ary=$list_assoc_ary;
  1043.     else {
  1044.         /**
  1045.          * only show subscribed folders
  1046.          * we need to merge the folders here... we can't trust the flags, etc. from the lsub_assoc_array
  1047.          * so we use the lsub_assoc_array as the list of folders and the values come from list_assoc_array
  1048.          */
  1049.         if (!empty($lsub_assoc_ary)) {
  1050.             foreach ($lsub_assoc_ary as $temp_mailbox_name=>$rawline{
  1051.                 if (!empty($list_assoc_ary[$temp_mailbox_name])) {
  1052.                     $final_folders_assoc_ary[$temp_mailbox_name]=$list_assoc_ary[$temp_mailbox_name];
  1053.                 }
  1054.             }
  1055.         }
  1056.     }
  1057.  
  1058.  
  1059.     // Now produce a flat, sorted list
  1060.     if (!empty($final_folders_assoc_ary)) {
  1061.         uksort($final_folders_assoc_ary,'strnatcasecmp');
  1062.         foreach ($final_folders_assoc_ary as $temp_mailbox_name=>$rawline{
  1063.             $final_folders_ary[]=$rawline;
  1064.         }
  1065.     }
  1066.  
  1067.     // this will put it into an array we can use later
  1068.     // containing:
  1069.     // raw    - Raw LIST/LSUB response from the IMAP server
  1070.     // formatted - formatted folder name
  1071.     // unformatted - unformatted, but with the delimiter at the end removed
  1072.     // unformated-dm - folder name as it appears in raw response
  1073.     // unformatted-disp - unformatted without $folder_prefix
  1074.     // id - the array element number (0, 1, 2, etc.)
  1075.     // flags - mailbox flags
  1076.     if (!empty($final_folders_ary)) {
  1077.         $boxesall sqimap_mailbox_parse($final_folders_ary);
  1078.     else {
  1079.         // they have no mailboxes
  1080.         $boxesall=array();
  1081.     }
  1082.  
  1083.     /* Now, lets sort for special folders */
  1084.     $boxesnew $used array();
  1085.  
  1086.     /* Find INBOX */
  1087.     $cnt count($boxesall);
  1088.     $used array_pad($used,$cnt,false);
  1089.     $has_inbox false;
  1090.     for($k 0$k $cnt++$k{
  1091.         if (strtoupper($boxesall[$k]['unformatted']== 'INBOX'{
  1092.             $boxesnew[$boxesall[$k];
  1093.             $used[$ktrue;
  1094.             $has_inbox true;
  1095.             break;
  1096.         }
  1097.     }
  1098.  
  1099.     if ($has_inbox == false{
  1100.         // do a list request for inbox because we should always show
  1101.         // inbox even if the user isn't subscribed to it.
  1102.         $inbox_ary sqimap_run_command($imap_stream'LIST "" "INBOX"',
  1103.                                         true$response$message);
  1104.         $inbox_ary compact_mailboxes_response($inbox_ary);
  1105.         if (count($inbox_ary)) {
  1106.             $inbox_entry sqimap_mailbox_parse($inbox_ary);
  1107.             // add it on top of the list
  1108.             if (!empty($boxesnew)) {
  1109.                 array_unshift($boxesnew,$inbox_entry[0]);
  1110.             else {
  1111.                 $boxesnew[]=$inbox_entry[0];
  1112.             }
  1113.             /* array_unshift($used,true); */
  1114.         }
  1115.     }
  1116.  
  1117.     /* List special folders and their subfolders, if requested. */
  1118.     if ($list_special_folders_first{
  1119.         for($k 0$k $cnt++$k{
  1120.             if (!$used[$k&& isSpecialMailbox($boxesall[$k]['unformatted'])) {
  1121.                 $boxesnew[$boxesall[$k];
  1122.                 $used[$k]   true;
  1123.             }
  1124.         }
  1125.     }
  1126.  
  1127.     /* Find INBOX's children */
  1128.     for($k 0$k $cnt++$k{
  1129.         $isboxbelow=isBoxBelow(strtoupper($boxesall[$k]['unformatted']),'INBOX');
  1130.         if (strtoupper($boxesall[$k]['unformatted']== 'INBOX'{
  1131.             $is_inbox=1;
  1132.         else {
  1133.             $is_inbox=0;
  1134.         }
  1135.  
  1136.         if (!$used[$k&& $isboxbelow && $is_inbox{
  1137.             $boxesnew[$boxesall[$k];
  1138.             $used[$ktrue;
  1139.         }
  1140.     }
  1141.  
  1142.     /* Rest of the folders */
  1143.     for($k 0$k $cnt$k++{
  1144.         if (!$used[$k]{
  1145.             $boxesnew[$boxesall[$k];
  1146.         }
  1147.     }
  1148.     /**
  1149.      * Don't register boxes in session, if $session_register is set to false
  1150.      * Prevents registration of sqimap_mailbox_list_all() results.
  1151.      */
  1152.     if ($session_registersqsession_register($boxesnew,'boxesnew');
  1153.     return $boxesnew;
  1154. }
  1155.  
  1156. /**
  1157.  * Fills mailbox object
  1158.  *
  1159.  * this is passed the mailbox array by left_main.php
  1160.  * who has previously obtained it from sqimap_get_mailboxes
  1161.  * that way, the raw mailbox list is available in left_main to other
  1162.  * things besides just sqimap_mailbox_tree
  1163.  * imap_stream is just used now to get status info
  1164.  *
  1165.  * most of the functionality is moved to sqimap_get_mailboxes
  1166.  * also takes care of TODO items:
  1167.  * caching mailbox tree
  1168.  * config setting for UW imap section (not needed now)
  1169.  *
  1170.  * Some code fragments are present in 1.3.0 - 1.4.4.
  1171.  * @param stream $imap_stream imap connection resource
  1172.  * @param array $lsub_ary output array from sqimap_get_mailboxes (contains mailboxes and flags)
  1173.  * @return object see mailboxes class.
  1174.  * @since 1.5.0
  1175.  */
  1176. function sqimap_mailbox_tree($imap_stream,$lsub_ary{
  1177.  
  1178.     $sorted_lsub_ary array();
  1179.     $cnt count($lsub_ary);
  1180.     for ($i 0$i $cnt$i++{
  1181.         $mbx=$lsub_ary[$i]['unformatted'];
  1182.         $flags=$lsub_ary[$i]['flags'];
  1183.  
  1184.         $noinferiors=0;
  1185.         if (in_array('\Noinferiors',$flags)) $noinferiors=1}
  1186.         if (in_array('\NoInferiors',$flags)) $noinferiors=1}
  1187.         if (in_array('\HasNoChildren',$flags)) $noinferiors=1}
  1188.  
  1189.         $noselect=0;
  1190.         if (in_array('\NoSelect',$flags)) $noselect=1}
  1191.         /**
  1192.          * LIST (SUBSCRIBED) has two new flags, \NonExistent which means the mailbox is subscribed to
  1193.          * but doesn't exist, and \PlaceHolder which is similar (but not the same) as \NoSelect
  1194.          * For right now, we'll treat these the same as \NoSelect and this behavior can be changed
  1195.          * later if needed
  1196.          */
  1197.         if (in_array('\NonExistent',$flags)) $noselect=1}
  1198.         if (in_array('\PlaceHolder',$flags)) $noselect=1}
  1199.         $sorted_lsub_ary[array ('mbx' => $mbx'noselect' => $noselect'noinferiors' => $noinferiors);
  1200.     }
  1201.  
  1202.     $sorted_lsub_ary array_values($sorted_lsub_ary);
  1203.     usort($sorted_lsub_ary'mbxSort');
  1204.     $boxestree sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
  1205.     return $boxestree;
  1206. }
  1207.  
  1208. /**
  1209.  * Callback function used for sorting mailboxes in sqimap_mailbox_tree
  1210.  * @param string $a 
  1211.  * @param string $b 
  1212.  * @return integer see php strnatcasecmp()
  1213.  * @since 1.5.1
  1214.  */
  1215. function mbxSort($a$b{
  1216.     return strnatcasecmp($a['mbx']$b['mbx']);
  1217. }
  1218.  
  1219. /**
  1220.  * Fills mailbox object
  1221.  *
  1222.  * Some code fragments are present in 1.3.0 - 1.4.4.
  1223.  * @param array $mbx_ary 
  1224.  * @param $mbxs 
  1225.  * @param stream $imap_stream imap connection resource
  1226.  * @return object see mailboxes class
  1227.  * @since 1.5.0
  1228.  */
  1229. function sqimap_fill_mailbox_tree($mbx_ary$mbxs=false,$imap_stream{
  1230.     global $data_dir$username$list_special_folders_first,
  1231.            $folder_prefix$trash_folder$sent_folder$draft_folder,
  1232.            $move_to_trash$move_to_sent$save_as_draft,
  1233.            $delimiter$imap_server_type;
  1234.  
  1235.     // $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
  1236.  
  1237.     /* create virtual root node */
  1238.     $mailboxesnew mailboxes();
  1239.     $mailboxes->is_root true;
  1240.     $trail_del false;
  1241.     $start 0;
  1242.  
  1243.     if (isset($folder_prefix&& ($folder_prefix != '')) {
  1244.         $start substr_count($folder_prefix,$delimiter);
  1245.         if (strrpos($folder_prefix$delimiter== (strlen($folder_prefix)-1)) {
  1246.             $mailboxes->mailboxname_full substr($folder_prefix,0(strlen($folder_prefix)-1));
  1247.         else {
  1248.             $mailboxes->mailboxname_full $folder_prefix;
  1249.             $start++;
  1250.         }
  1251.         $mailboxes->mailboxname_sub $mailboxes->mailboxname_full;
  1252.     else {
  1253.         $start 0;
  1254.     }
  1255.  
  1256.     $cnt count($mbx_ary);
  1257.     for ($i=0$i $cnt$i++{
  1258.         if ($mbx_ary[$i]['mbx'!='' {
  1259.             $mbx new mailboxes();
  1260.             $mailbox $mbx_ary[$i]['mbx'];
  1261.  
  1262.             /*
  1263.              * Set the is_special flag if it concerned a special mailbox.
  1264.              * Used for displaying the special folders on top in the mailbox
  1265.              * tree displaying code.
  1266.              */
  1267.             $mbx->is_special |= ($mbx->is_inbox (strtoupper($mailbox== 'INBOX'));
  1268.             $mbx->is_special |= ($mbx->is_trash isTrashMailbox($mailbox));
  1269.             $mbx->is_special |= ($mbx->is_sent isSentMailbox($mailbox));
  1270.             $mbx->is_special |= ($mbx->is_draft isDraftMailbox($mailbox));
  1271.  
  1272.             if (!$mbx->is_special)
  1273.                 $mbx->is_special boolean_hook_function('special_mailbox'$mailbox1);
  1274.  
  1275.             if (isset($mbx_ary[$i]['unseen'])) {
  1276.                 $mbx->unseen $mbx_ary[$i]['unseen'];
  1277.             }
  1278.             if (isset($mbx_ary[$i]['nummessages'])) {
  1279.                 $mbx->total $mbx_ary[$i]['nummessages'];
  1280.             }
  1281.  
  1282.             $mbx->is_noselect $mbx_ary[$i]['noselect'];
  1283.             $mbx->is_noinferiors $mbx_ary[$i]['noinferiors'];
  1284.  
  1285.             $r_del_pos strrpos($mbx_ary[$i]['mbx']$delimiter);
  1286.             if ($r_del_pos{
  1287.                 $mbx->mailboxname_sub substr($mbx_ary[$i]['mbx'],$r_del_pos+1);
  1288.             else {   /* mailbox is root folder */
  1289.                 $mbx->mailboxname_sub $mbx_ary[$i]['mbx'];
  1290.             }
  1291.             $mbx->mailboxname_full $mbx_ary[$i]['mbx'];
  1292.  
  1293.             $mailboxes->addMbx($mbx$delimiter$start$list_special_folders_first);
  1294.         }
  1295.     }
  1296.     sqimap_utf7_decode_mbx_tree($mailboxes);
  1297.     sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
  1298.     return $mailboxes;
  1299. }
  1300.  
  1301. /**
  1302.  * @param object $mbx_tree 
  1303.  * @since 1.5.0
  1304.  */
  1305. function sqimap_utf7_decode_mbx_tree(&$mbx_tree{
  1306.     global $draft_folder$sent_folder$trash_folder$translate_special_folders;
  1307.  
  1308.     /* decode folder name and set mailboxname_sub */
  1309.     if ($translate_special_folders && strtoupper($mbx_tree->mailboxname_full== 'INBOX'{
  1310.         $mbx_tree->mailboxname_sub _("INBOX");
  1311.     elseif ($translate_special_folders && $mbx_tree->mailboxname_full == $draft_folder{
  1312.         $mbx_tree->mailboxname_sub _("Drafts");
  1313.     elseif ($translate_special_folders && $mbx_tree->mailboxname_full == $sent_folder{
  1314.         $mbx_tree->mailboxname_sub _("Sent");
  1315.     elseif ($translate_special_folders && $mbx_tree->mailboxname_full == $trash_folder{
  1316.         $mbx_tree->mailboxname_sub _("Trash");
  1317.     else {
  1318.         $mbx_tree->mailboxname_sub imap_utf7_decode_local($mbx_tree->mailboxname_sub);
  1319.     }
  1320.  
  1321.     if ($mbx_tree->mbxs{
  1322.         $iCnt count($mbx_tree->mbxs);
  1323.         for ($i=0;$i<$iCnt;++$i{
  1324.             sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs[$i]);
  1325.         }
  1326.     }
  1327. }
  1328.  
  1329. /**
  1330.  * @param object $mbx_tree 
  1331.  * @param array $aMbxs 
  1332.  * @since 1.5.0
  1333.  */
  1334. function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs{
  1335.    if ($mbx_tree)
  1336.    $aMbxs[=$mbx_tree;
  1337.    if ($mbx_tree->mbxs{
  1338.       $iCnt count($mbx_tree->mbxs);
  1339.       for ($i=0;$i<$iCnt;++$i{
  1340.          sqimap_tree_to_ref_array($mbx_tree->mbxs[$i],$aMbxs);
  1341.       }
  1342.    }
  1343. }
  1344.  
  1345. /**
  1346.  * @param stream $imap_stream imap connection resource
  1347.  * @param object $mbx_tree 
  1348.  * @since since 1.5.0
  1349.  */
  1350. function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree{
  1351.     global $unseen_notify$unseen_type$trash_folder,$move_to_trash;
  1352.     $aMbxs $aQuery array();
  1353.     sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
  1354.     // remove the root node
  1355.     array_shift($aMbxs);
  1356.  
  1357.     if($unseen_notify == 3{
  1358.         $cnt count($aMbxs);
  1359.         for($i=0;$i<$cnt;++$i{
  1360.             $oMbx =$aMbxs[$i];
  1361.             if (!$oMbx->is_noselect{
  1362.                 $mbx $oMbx->mailboxname_full;
  1363.                 if ($unseen_type == ||
  1364.                    ($move_to_trash && $oMbx->mailboxname_full == $trash_folder)) {
  1365.                    $query 'STATUS ' sqimap_encode_mailbox_name($mbx' (MESSAGES UNSEEN RECENT)';
  1366.                 else {
  1367.                    $query 'STATUS ' sqimap_encode_mailbox_name($mbx' (UNSEEN RECENT)';
  1368.                 }
  1369.                 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
  1370.             else {
  1371.                 $oMbx->unseen $oMbx->total $oMbx->recent false;
  1372.                 $tag false;
  1373.             }
  1374.             $oMbx->tag $tag;
  1375.             $aMbxs[$i=$oMbx;
  1376.         }
  1377.         // execute all the queries at once
  1378.         $aResponse sqimap_run_pipelined_command ($imap_stream$aQueryfalse$aServerResponse$aServerMessage);
  1379.         $cnt count($aMbxs);
  1380.         for($i=0;$i<$cnt;++$i{
  1381.             $oMbx =$aMbxs[$i];
  1382.             $tag $oMbx->tag;
  1383.             if ($tag && $aServerResponse[$tag== 'OK'{
  1384.                 $sResponse implode(''$aResponse[$tag]);
  1385.                 if (preg_match('/UNSEEN\s+([0-9]+)/i'$sResponse$regs)) {
  1386.                     $oMbx->unseen $regs[1];
  1387.                 }
  1388.                 if (preg_match('/MESSAGES\s+([0-9]+)/i'$sResponse$regs)) {
  1389.                     $oMbx->total $regs[1];
  1390.                 }
  1391.                 if (preg_match('/RECENT\s+([0-9]+)/i'$sResponse$regs)) {
  1392.                     $oMbx->recent $regs[1];
  1393.                 }
  1394.  
  1395.            }
  1396.            unset($oMbx->tag);
  1397.         }
  1398.     else if ($unseen_notify == 2// INBOX only
  1399.         $cnt count($aMbxs);
  1400.         for($i=0;$i<$cnt;++$i{
  1401.             $oMbx =$aMbxs[$i];
  1402.             if (strtoupper($oMbx->mailboxname_full== 'INBOX' ||
  1403.                ($move_to_trash && $oMbx->mailboxname_full == $trash_folder)) {
  1404.                  if ($unseen_type == ||
  1405.                    ($oMbx->mailboxname_full == $trash_folder && $move_to_trash)) {
  1406.                     $aStatus sqimap_status_messages($imap_stream,$oMbx->mailboxname_full);
  1407.                     $oMbx->unseen $aStatus['UNSEEN'];
  1408.                     $oMbx->total  $aStatus['MESSAGES'];
  1409.                     $oMbx->recent $aStatus['RECENT'];
  1410.                 else {
  1411.                     $oMbx->unseen sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full);
  1412.                 }
  1413.                 $aMbxs[$i=$oMbx;
  1414.                 if (!$move_to_trash && $trash_folder{
  1415.                     break;
  1416.                 else {
  1417.                    // trash comes after INBOX
  1418.                    if ($oMbx->mailboxname_full == $trash_folder{
  1419.                       break;
  1420.                    }
  1421.                 }
  1422.             }
  1423.         }
  1424.     }
  1425.  
  1426.     $cnt count($aMbxs);
  1427.     for($i=0;$i<$cnt;++$i{
  1428.          $oMbx =$aMbxs[$i];
  1429.          unset($hook_status);
  1430.          if (!empty($oMbx->unseen)) $hook_status['UNSEEN']=$oMbx->unseen}
  1431.          if (!empty($oMbx->total)) $hook_status['MESSAGES']=$oMbx->total}
  1432.          if (!empty($oMbx->recent)) $hook_status['RECENT']=$oMbx->recent}
  1433.          if (!empty($hook_status))
  1434.          {
  1435.               $hook_status['MAILBOX']=$oMbx->mailboxname_full;
  1436.               $hook_status['CALLER']='sqimap_get_status_mbx_tree'// helps w/ debugging
  1437.               do_hook('folder_status'$hook_status);
  1438.          }
  1439.     }
  1440. }
  1441.  
  1442. /**
  1443.  * Checks if folder is noselect (can't store messages)
  1444.  *
  1445.  * Function does not check if folder subscribed.
  1446.  * @param stream $oImapStream imap connection resource
  1447.  * @param string $sImapFolder imap folder name
  1448.  * @param object $oBoxes mailboxes class object.
  1449.  * @return boolean true, when folder has noselect flag. false in any other case.
  1450.  * @since 1.5.1
  1451.  */
  1452. function sqimap_mailbox_is_noselect($oImapStream,$sImapFolder,&$oBoxes{
  1453.     // build mailbox object if it is not available
  1454.     if (is_object($oBoxes)) $oBoxes=sqimap_mailbox_list($oImapStream);
  1455.     foreach($oBoxes as $box{
  1456.         if ($box['unformatted']==$sImapFolder{
  1457.             return (bool) check_is_noselect($box['raw']);
  1458.         }
  1459.     }
  1460.     return false;
  1461. }
  1462.  
  1463. /**
  1464.  * Checks if folder is noinferiors (can't store other folders)
  1465.  *
  1466.  * Function does not check if folder subscribed.
  1467.  * @param stream $oImapStream imap connection resource
  1468.  * @param string $sImapFolder imap folder name
  1469.  * @param object $oBoxes mailboxes class object.
  1470.  * @return boolean true, when folder has noinferiors flag. false in any other case.
  1471.  * @since 1.5.1
  1472.  */
  1473. function sqimap_mailbox_is_noinferiors($oImapStream,$sImapFolder,&$oBoxes{
  1474.     // build mailbox object if it is not available
  1475.     if (is_object($oBoxes)) $oBoxes=sqimap_mailbox_list($oImapStream);
  1476.     foreach($oBoxes as $box{
  1477.         if ($box['unformatted']==$sImapFolder{
  1478.             return (bool) check_is_noinferiors($box['raw']);
  1479.         }
  1480.     }
  1481.     return false;
  1482. }

Documentation generated on Mon, 22 Sep 2014 04:18:21 +0200 by phpDocumentor 1.4.3