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-2020 The SquirrelMail Project Team
  9.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  10.  * @version $Id: imap_mailbox.php 14845 2020-01-07 08:09:34Z 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.  * @param boolean $handle_errors When TRUE, IMAP errors
  414.  *                                are handled herein, causing
  415.  *                                an error to be displayed on
  416.  *                                screen and execution to stop
  417.  *                                and when FALSE, error status
  418.  *                                is returned to the caller
  419.  *                                (OPTIONAL; default is TRUE)
  420.  * @return array results of select command (on success - permanentflags, flags and rights)
  421.  *                (on failure (and when $handle_errors is false), empty array)
  422.  * @since 1.0 or older
  423.  */
  424. function sqimap_mailbox_select ($imap_stream$mailbox$handle_errors=true{
  425.     if (empty($mailbox)) {
  426.         return;
  427.     }
  428.  
  429.     // cleanup $mailbox in order to prevent IMAP injection attacks
  430.     $mailbox str_replace(array("\r","\n")array("",""),$mailbox);
  431.  
  432.     /**
  433.      * Default UW IMAP server configuration allows to access other files
  434.      * on server. $imap_server_type is not checked because interface can
  435.      * be used with 'other' or any other server type setting. $mailbox
  436.      * variable can be modified in any script that uses variable from GET
  437.      * or POST. This code blocks all standard SquirrelMail IMAP API requests
  438.      * that use mailbox with full path (/etc/passwd) or with ../ characters
  439.      * in path (../../etc/passwd)
  440.      */
  441.     if (strstr($mailbox'../'|| substr($mailbox01== '/'{
  442.         global $oTemplate;
  443.         error_box(sprintf(_("Invalid mailbox name: %s"),sm_encode_html_special_chars($mailbox)));
  444.         sqimap_logout($imap_stream);
  445.         $oTemplate->display('footer.tpl');
  446.         die();
  447.     }
  448.  
  449.     $read sqimap_run_command($imap_stream'SELECT ' sqimap_encode_mailbox_name($mailbox),
  450.                                $handle_errors$response$message);
  451.     $result array();
  452.     for ($i 0$cnt count($read)$i $cnt$i++{
  453.         if (preg_match('/^\*\s+OK\s\[(\w+)\s(\w+)\]/',$read[$i]$regs)) {
  454.             $result[strtoupper($regs[1])$regs[2];
  455.         else if (preg_match('/^\*\s([0-9]+)\s(\w+)/',$read[$i]$regs)) {
  456.             $result[strtoupper($regs[2])$regs[1];
  457.         else {
  458.             if (preg_match("/PERMANENTFLAGS(.*)/i",$read[$i]$regs)) {
  459.                 $regs[1]=trim(preg_replace (  array ("/\(/","/\)/","/\]/",''$regs[1])) ;
  460.                 $result['PERMANENTFLAGS'explode(' ',strtolower($regs[1]));
  461.             else if (preg_match("/FLAGS(.*)/i",$read[$i]$regs)) {
  462.                 $regs[1]=trim(preg_replace (  array ("/\(/","/\)/",''$regs[1])) ;
  463.                 $result['FLAGS'explode(' ',strtolower($regs[1]));
  464.             }
  465.         }
  466.     }
  467.     if (!empty($result&& !isset($result['PERMANENTFLAGS'])) {
  468.         $result['PERMANENTFLAGS'$result['FLAGS'];
  469.     }
  470.     if (preg_match('/^\[(.+)\]/',$message$regs)) {
  471.         $result['RIGHTS']=strtoupper($regs[1]);
  472.     }
  473.  
  474.     return $result;
  475. }
  476.  
  477. /**
  478.  * Creates a folder.
  479.  *
  480.  * Mailbox is automatically subscribed.
  481.  *
  482.  * Set $type to string that does not match 'noselect' (case insensitive),
  483.  * if you don't want to prepend delimiter to mailbox name. Please note
  484.  * that 'noinferiors' might be used someday as keyword for folders
  485.  * that store only messages.
  486.  * @param stream $imap_steam imap connection resource
  487.  * @param string $mailbox mailbox name
  488.  * @param string $type folder type.
  489.  * @since 1.0 or older
  490.  */
  491. function sqimap_mailbox_create ($imap_stream$mailbox$type{
  492.     global $delimiter;
  493.     if (strtolower($type== 'noselect'{
  494.         $create_mailbox $mailbox $delimiter;
  495.     else {
  496.         $create_mailbox $mailbox;
  497.     }
  498.  
  499.     $read_ary sqimap_run_command($imap_stream'CREATE ' .
  500.                                    sqimap_encode_mailbox_name($create_mailbox),
  501.                                    true$response$message);
  502.     sqimap_subscribe ($imap_stream$mailbox);
  503. }
  504.  
  505. /**
  506.  * Subscribes to an existing folder.
  507.  * @param stream $imap_stream imap connection resource
  508.  * @param string $mailbox mailbox name
  509.  * @param boolean $debug (since 1.5.1)
  510.  * @since 1.0 or older
  511.  */
  512. function sqimap_subscribe ($imap_stream$mailbox,$debug=true{
  513.     $read_ary sqimap_run_command($imap_stream'SUBSCRIBE ' .
  514.                                    sqimap_encode_mailbox_name($mailbox),
  515.                                    $debug$response$message);
  516. }
  517.  
  518. /**
  519.  * Unsubscribes from an existing folder
  520.  * @param stream $imap_stream imap connection resource
  521.  * @param string $mailbox mailbox name
  522.  * @since 1.0 or older
  523.  */
  524. function sqimap_unsubscribe ($imap_stream$mailbox{
  525.     $read_ary sqimap_run_command($imap_stream'UNSUBSCRIBE ' .
  526.                                    sqimap_encode_mailbox_name($mailbox),
  527.                                    false$response$message);
  528. }
  529.  
  530. /**
  531.  * Deletes the given folder
  532.  * Since 1.2.6 and 1.3.0 contains rename_or_delete_folder hook
  533.  * @param stream $imap_stream imap connection resource
  534.  * @param string $mailbox mailbox name
  535.  * @since 1.0 or older
  536.  */
  537. function sqimap_mailbox_delete ($imap_stream$mailbox{
  538.     global $data_dir$username;
  539.     sqimap_unsubscribe ($imap_stream$mailbox);
  540.  
  541.     if (sqimap_mailbox_exists($imap_stream$mailbox)) {
  542.  
  543.         $read_ary sqimap_run_command($imap_stream'DELETE ' .
  544.                                        sqimap_encode_mailbox_name($mailbox),
  545.                                        true$response$message);
  546.         if ($response !== 'OK'{
  547.             // subscribe again
  548.             sqimap_subscribe ($imap_stream$mailbox);
  549.         else {
  550.             $temp array(&$mailbox'delete''');
  551.             do_hook('rename_or_delete_folder'$temp);
  552.             removePref($data_dir$username"thread_$mailbox");
  553.             removePref($data_dir$username"collapse_folder_$mailbox");
  554.         }
  555.     }
  556. }
  557.  
  558. /**
  559.  * Determines if the user is subscribed to the folder or not
  560.  * @param stream $imap_stream imap connection resource
  561.  * @param string $mailbox mailbox name
  562.  * @return boolean 
  563.  * @since 1.2.0
  564.  */
  565. function sqimap_mailbox_is_subscribed($imap_stream$folder{
  566.     $boxesall sqimap_mailbox_list ($imap_stream);
  567.     foreach ($boxesall as $ref{
  568.         if ($ref['unformatted'== $folder{
  569.             return true;
  570.         }
  571.     }
  572.     return false;
  573. }
  574.  
  575. /**
  576.  * Renames a mailbox.
  577.  * Since 1.2.6 and 1.3.0 contains rename_or_delete_folder hook
  578.  * @param stream $imap_stream imap connection resource
  579.  * @param string $old_name mailbox name
  580.  * @param string $new_name new mailbox name
  581.  * @since 1.2.3
  582.  */
  583. function sqimap_mailbox_rename$imap_stream$old_name$new_name {
  584.     if $old_name != $new_name {
  585.         global $delimiter$imap_server_type$data_dir$username;
  586.         if substr$old_name-== $delimiter  {
  587.             $old_name substr$old_name0strlen$old_name );
  588.             $new_name substr$new_name0strlen$new_name );
  589.             $postfix $delimiter;
  590.         else {
  591.             $postfix '';
  592.         }
  593.  
  594.         $boxesall sqimap_mailbox_list_all($imap_stream);
  595.         $cmd 'RENAME ' sqimap_encode_mailbox_name($old_name.
  596.                      ' ' sqimap_encode_mailbox_name($new_name);
  597.         $data sqimap_run_command($imap_stream$cmdtrue$response$message);
  598.         sqimap_unsubscribe($imap_stream$old_name.$postfix);
  599.         $oldpref_thread getPref($data_dir$username'thread_'.$old_name.$postfix);
  600.         $oldpref_collapse getPref($data_dir$username'collapse_folder_'.$old_name.$postfix);
  601.         removePref($data_dir$username'thread_'.$old_name.$postfix);
  602.         removePref($data_dir$username'collapse_folder_'.$old_name.$postfix);
  603.         sqimap_subscribe($imap_stream$new_name.$postfix);
  604.         setPref($data_dir$username'thread_'.$new_name.$postfix$oldpref_thread);
  605.         setPref($data_dir$username'collapse_folder_'.$new_name.$postfix$oldpref_collapse);
  606.         $temp array(&$old_name'rename'&$new_name);
  607.         do_hook('rename_or_delete_folder'$temp);
  608.         $l strlen$old_name 1;
  609.         $p 'unformatted';
  610.  
  611.         foreach ($boxesall as $box{
  612.             if (substr($box[$p]0$l== $old_name $delimiter{
  613.                 $new_sub $new_name $delimiter substr($box[$p]$l);
  614.                 /* With Cyrus IMAPd >= 2.0 rename is recursive, so don't check for errors here */
  615.                 if ($imap_server_type == 'cyrus'{
  616.                     $cmd 'RENAME "' $box[$p'" "' $new_sub '"';
  617.                     $data sqimap_run_command($imap_stream$cmdfalse,
  618.                                                $response$message);
  619.                 }
  620.                 $was_subscribed sqimap_mailbox_is_subscribed($imap_stream$box[$p]);
  621.                 if $was_subscribed {
  622.                     sqimap_unsubscribe($imap_stream$box[$p]);
  623.                 }
  624.                 $oldpref_thread getPref($data_dir$username'thread_'.$box[$p]);
  625.                 $oldpref_collapse getPref($data_dir$username'collapse_folder_'.$box[$p]);
  626.                 removePref($data_dir$username'thread_'.$box[$p]);
  627.                 removePref($data_dir$username'collapse_folder_'.$box[$p]);
  628.                 if $was_subscribed {
  629.                     sqimap_subscribe($imap_stream$new_sub);
  630.                 }
  631.                 setPref($data_dir$username'thread_'.$new_sub$oldpref_thread);
  632.                 setPref($data_dir$username'collapse_folder_'.$new_sub$oldpref_collapse);
  633.                 $temp array(&$box[$p]'rename'&$new_sub);
  634.                 do_hook('rename_or_delete_folder'$temp);
  635.             }
  636.         }
  637.     }
  638. }
  639.  
  640. /**
  641.  * Formats a mailbox into parts for the $boxesall array
  642.  *
  643.  * The parts are:
  644.  * <ul>
  645.  *   <li>raw            - Raw LIST/LSUB response from the IMAP server
  646.  *   <li>formatted      - nicely formatted folder name
  647.  *   <li>unformatted    - unformatted, but with delimiter at end removed
  648.  *   <li>unformatted-dm - folder name as it appears in raw response
  649.  *   <li>unformatted-disp - unformatted without $folder_prefix
  650.  *   <li>id             - TODO: document me
  651.  *   <li>flags          - TODO: document me
  652.  * </ul>
  653.  * Before 1.2.0 used third argument for delimiter.
  654.  *
  655.  * Before 1.5.1 used second argument for lsub line. Argument was removed in order to use
  656.  * find_mailbox_name() on the raw input. Since 1.5.1 includes RFC3501 names in flags
  657.  * array (for example, "\NoSelect" in addition to "noselect")
  658.  * @param array $line 
  659.  * @return array 
  660.  * @since 1.0 or older
  661.  * @todo document id and flags keys in boxes array and function arguments.
  662.  */
  663. function sqimap_mailbox_parse ($line{
  664.     global $folder_prefix$delimiter;
  665.  
  666.     /* Process each folder line */
  667.     ksort($line)// get physical ordering same as alphabetical sort we did before now (might be a better place for this)
  668.     foreach ($line as $g => $l)
  669.     // was this but array not guaranteed to be contiguous: for ($g = 0, $cnt = count($line); $g < $cnt; ++$g)
  670.     {
  671.         /* Store the raw IMAP reply */
  672.         if (isset($line[$g])) {
  673.             $boxesall[$g]['raw'$line[$g];
  674.         else {
  675.             $boxesall[$g]['raw''';
  676.         }
  677.  
  678.         /* Count number of delimiters ($delimiter) in folder name */
  679.         $mailbox find_mailbox_name($line[$g]);
  680.         $dm_count substr_count($mailbox$delimiter);
  681.         if (substr($mailbox-1== $delimiter{
  682.             /* If name ends in delimiter, decrement count by one */
  683.             $dm_count--;
  684.         }
  685.  
  686.         /* Format folder name, but only if it's a INBOX.* or has a parent. */
  687.         $boxesallbyname[$mailbox$g;
  688.         $parentfolder readMailboxParent($mailbox$delimiter);
  689.         if ( (strtolower(substr($mailbox05)) == "inbox"||
  690.              (substr($mailbox0strlen($folder_prefix)) == $folder_prefix||
  691.              (isset($boxesallbyname[$parentfolder]&&
  692.               (strlen($parentfolder0) ) ) {
  693.             $indent $dm_count (substr_count($folder_prefix$delimiter));
  694.             if ($indent 0{
  695.                 $boxesall[$g]['formatted'str_repeat('&nbsp;&nbsp;'$indent);
  696.             else {
  697.                 $boxesall[$g]['formatted''';
  698.             }
  699.             $boxesall[$g]['formatted'.= imap_utf7_decode_local(readShortMailboxName($mailbox$delimiter));
  700.         else {
  701.             $boxesall[$g]['formatted']  imap_utf7_decode_local($mailbox);
  702.         }
  703.  
  704.         $boxesall[$g]['unformatted-dm'$mailbox;
  705.         if (substr($mailbox-1== $delimiter{
  706.             $mailbox substr($mailbox0strlen($mailbox1);
  707.         }
  708.         $boxesall[$g]['unformatted'$mailbox;
  709.         if (substr($mailbox,0,strlen($folder_prefix))==$folder_prefix{
  710.             $mailbox substr($mailboxstrlen($folder_prefix));
  711.         }
  712.         $boxesall[$g]['unformatted-disp'$mailbox;
  713.         $boxesall[$g]['id'$g;
  714.  
  715.         $boxesall[$g]['flags'array();
  716.         if (isset($line[$g]&& preg_match('/\(([^)]*)\)/',$line[$g],$regs) ) {
  717.             /**
  718.              * Since 1.5.1 flags are stored with RFC3501 naming
  719.              * and also the old way for backwards compatibility
  720.              * so for example "\NoSelect" and "noselect"
  721.              */
  722.             $flags trim($regs[1]);
  723.             if ($flags{
  724.                 $flagsarr explode(' ',$flags);
  725.                 $flagsarrnew=$flagsarr;
  726.                 // add old type
  727.                 foreach ($flagsarr as $flag{
  728.                     $flagsarrnew[]=strtolower(str_replace('\\''',$flag));
  729.                 }
  730.                 $boxesall[$g]['flags']=$flagsarrnew;
  731.             }
  732.         }
  733.     }
  734.     return $boxesall;
  735. }
  736.  
  737. /**
  738.  * Returns an array of mailboxes available.  Separated from sqimap_mailbox_option_list()
  739.  * below for template development.
  740.  * 
  741.  * @author Steve Brown
  742.  * @since 1.5.2
  743.  */
  744. function sqimap_mailbox_option_array($imap_stream$folder_skip 0$boxes 0,
  745.                                     $flag 'noselect'$use_long_format false {
  746.     global $username$data_dir$translate_special_folders$sent_folder
  747.         $trash_folder$draft_folder;
  748.  
  749.     $delimiter sqimap_get_delimiter($imap_stream);
  750.  
  751.     $mbox_options '';
  752.     if $use_long_format {
  753.         $shorten_box_names 0;
  754.     else {
  755.         $shorten_box_names getPref($data_dir$username'mailbox_select_style'SMPREF_MAILBOX_SELECT_INDENTED);
  756.     }
  757.  
  758.     if ($boxes == 0{
  759.         $boxes sqimap_mailbox_list($imap_stream);
  760.     }
  761.  
  762.     $a array();
  763.     foreach ($boxes as $boxes_part{
  764.         if ($flag == NULL || (is_array($boxes_part['flags'])
  765.                       && !in_array($flag$boxes_part['flags']))) {
  766.             $box $boxes_part['unformatted'];
  767.  
  768.             if ($folder_skip != && in_array($box$folder_skip) ) {
  769.                 continue;
  770.             }
  771.             $lowerbox strtolower($box);
  772.             // mailboxes are casesensitive => inbox.sent != inbox.Sent
  773.             // nevermind, to many dependencies this should be fixed!
  774.  
  775.             if (strtolower($box== 'inbox'// inbox is special and not casesensitive
  776.                 $box2 _("INBOX");
  777.             else {
  778.                 switch ($shorten_box_names)
  779.                 {
  780.                   case SMPREF_MAILBOX_SELECT_DELIMITED:
  781.                       if ($translate_special_folders && $boxes_part['unformatted-dm']==$sent_folder{
  782.                           /*
  783.                            * calculate pad level from number of delimiters. do it inside if control in order 
  784.                            * to reduce number of calculations. Other folders don't need it.
  785.                            */
  786.                           $pad str_pad('',(count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'.&nbsp;');
  787.                           // i18n: Name of Sent folder
  788.                           $box2 $pad _("Sent");
  789.                       elseif ($translate_special_folders && $boxes_part['unformatted-dm']==$trash_folder{
  790.                           $pad str_pad('',(count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'.&nbsp;');
  791.                           // i18n: Name of Trash folder
  792.                           $box2 $pad _("Trash");
  793.                       elseif ($translate_special_folders && $boxes_part['unformatted-dm']==$draft_folder{
  794.                           $pad str_pad('',(count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'.&nbsp;');
  795.                           // i18n: Name of Drafts folder
  796.                           $box2 $pad _("Drafts");
  797.                       else {
  798.                           $box2 str_replace('&amp;nbsp;&amp;nbsp;''.&nbsp;'sm_encode_html_special_chars($boxes_part['formatted']));
  799.                       }
  800.                     break;
  801.                   case SMPREF_MAILBOX_SELECT_INDENTED:
  802.                       if ($translate_special_folders && $boxes_part['unformatted-dm']==$sent_folder{
  803.                           $pad str_pad('',12 (count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'&nbsp;&nbsp;');
  804.                           $box2 $pad _("Sent");
  805.                       elseif ($translate_special_folders && $boxes_part['unformatted-dm']==$trash_folder{
  806.                           $pad str_pad('',12 (count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'&nbsp;&nbsp;');
  807.                           $box2 $pad _("Trash");
  808.                       elseif ($translate_special_folders && $boxes_part['unformatted-dm']==$draft_folder{
  809.                           $pad str_pad('',12 (count(explode($delimiter,$boxes_part['unformatted-dm']))-1),'&nbsp;&nbsp;');
  810.                           $box2 $pad _("Drafts");
  811.                       else {
  812.                           $box2 str_replace('&amp;nbsp;&amp;nbsp;''&nbsp;&nbsp;'sm_encode_html_special_chars($boxes_part['formatted']));
  813.                       }
  814.                     break;
  815.                   default:  /* default, long names, style = 0 */
  816.                     $box2 str_replace(' ''&nbsp;'sm_encode_html_special_chars(imap_utf7_decode_local($boxes_part['unformatted-disp'])));
  817.                     break;
  818.                 }
  819.             }
  820.             
  821.             $a[sm_encode_html_special_chars($box)$box2;
  822.         }
  823.     }
  824.     
  825.     return $a;
  826. }
  827.  
  828. /**
  829.  * Returns list of options (to be echoed into select statement
  830.  * based on available mailboxes and separators
  831.  * Caller should surround options with <select ...> </select> and
  832.  * any formatting.
  833.  * @param stream $imap_stream imap connection resource to query for mailboxes
  834.  * @param array $show_selected array containing list of mailboxes to pre-select (0 if none)
  835.  * @param array $folder_skip array of folders to keep out of option list (compared in lower)
  836.  * @param $boxes list of already fetched boxes (for places like folder panel, where
  837.  *             you know these options will be shown 3 times in a row.. (most often unset).
  838.  * @param string $flag (since 1.4.1) flag to check for in mailbox flags, used to filter out mailboxes.
  839.  *            'noselect' by default to remove unselectable mailboxes.
  840.  *            'noinferiors' used to filter out folders that can not contain subfolders.
  841.  *            NULL to avoid flag check entirely.
  842.  *            NOTE: noselect and noiferiors are used internally. The IMAP representation is
  843.  *                  \NoSelect and \NoInferiors
  844.  * @param boolean $use_long_format (since 1.4.1) override folder display preference and always show full folder name.
  845.  * @return string html formated mailbox selection options
  846.  * @since 1.3.2
  847.  */
  848. function sqimap_mailbox_option_list($imap_stream$show_selected 0$folder_skip 0$boxes 0,
  849.                                     $flag 'noselect'$use_long_format false {
  850.     global $username$data_dir$translate_special_folders$sent_folder
  851.         $trash_folder$draft_folder;
  852.  
  853.     $boxes sqimap_mailbox_option_array($imap_stream$folder_skip$boxes$flag$use_long_format);
  854.     
  855.     $str '';
  856.     foreach ($boxes as $value=>$option{
  857.         $lowerbox strtolower(sm_encode_html_special_chars($value));
  858.         $sel false;
  859.         if ($show_selected != 0{
  860.             reset($show_selected);
  861.             while (!$sel && (list($x$valeach($show_selected))) {
  862.                 if (strtolower($value== strtolower(sm_encode_html_special_chars($val))) {
  863.                     $sel true;
  864.                 }
  865.             }
  866.         }
  867.         
  868.         $str .= '<option value="'$value .'"'($sel ' selected="selected"' '').'>'$option ."</option>\n";
  869.     }
  870.     
  871.     return $str;
  872. }
  873.  
  874. /**
  875.  * Returns sorted mailbox lists in several different ways.
  876.  *
  877.  * Since 1.5.1 most of the functionality has been moved to new function sqimap_get_mailboxes
  878.  *
  879.  * See comment on sqimap_mailbox_parse() for info about the returned array.
  880.  * @param resource $imap_stream imap connection resource
  881.  * @param boolean $force force update of mailbox listing. available since 1.4.2 and 1.5.0
  882.  * @return array list of mailboxes
  883.  * @since 1.0 or older
  884.  */
  885. function sqimap_mailbox_list($imap_stream$force=false{
  886.     global $boxesnew,$show_only_subscribed_folders;
  887.     if (!sqgetGlobalVar('boxesnew',$boxesnew,SQ_SESSION|| $force{
  888.         $boxesnew=sqimap_get_mailboxes($imap_stream,$force,$show_only_subscribed_folders);
  889.     }
  890.     return $boxesnew;
  891. }
  892.  
  893. /**
  894.  * Returns a list of all folders, subscribed or not
  895.  *
  896.  * Since 1.5.1 code moved to sqimap_get_mailboxes()
  897.  *
  898.  * @param stream $imap_stream imap connection resource
  899.  * @return array see sqimap_mailbox_parse()
  900.  * @since 1.0 or older
  901.  */
  902. function sqimap_mailbox_list_all($imap_stream{
  903.     global $show_only_subscribed_folders;
  904.     // fourth argument prevents registration of retrieved list of mailboxes in session
  905.     $boxes=sqimap_get_mailboxes($imap_stream,true,false,false);
  906.     return $boxes;
  907. }
  908.  
  909.  
  910. /**
  911.  * Gets the list of mailboxes for sqimap_maolbox_tree and sqimap_mailbox_list
  912.  *
  913.  * This is because both of those functions had duplicated logic, but with slightly different
  914.  * implementations. This will make both use the same implementation, which should make it
  915.  * easier to maintain and easier to modify in the future
  916.  * @param stream $imap_stream imap connection resource
  917.  * @param bool $force force a reload and ignore cache
  918.  * @param bool $show_only_subscribed controls listing of visible or all folders
  919.  * @param bool $session_register controls registration of retrieved data in session.
  920.  * @return object boxesnew - array of mailboxes and their attributes
  921.  * @since 1.5.1
  922.  */
  923. function sqimap_get_mailboxes($imap_stream,$force=false,$show_only_subscribed=true,$session_register=true{
  924.     global    $show_only_subscribed_folders,$noselect_fix_enable,$folder_prefix,
  925.     $inbox_subscribed false;
  926.     $listsubscribed sqimap_capability($imap_stream,'LIST-SUBSCRIBED');
  927.  
  928.     if ($show_only_subscribed$show_only_subscribed=$show_only_subscribed_folders}
  929.  
  930.     //require_once(SM_PATH . 'include/load_prefs.php');
  931.  
  932.     /**
  933.      * There are three main listing commands we can use in IMAP:
  934.      * LSUB        shows just the list of subscribed folders
  935.      *            may include flags, but these are not necessarily accurate or authoratative
  936.      *            \NoSelect has special meaning: the folder does not exist -OR- it means this
  937.      *            folder is not subscribed but children may be
  938.      *            [RFC-2060]
  939.      * LIST        this shows every mailbox on the system
  940.      *            flags are always included and are accurate and authoratative
  941.      *            \NoSelect means folder should not be selected
  942.      *            [RFC-2060]
  943.      * LIST (SUBSCRIBED)    implemented with LIST-SUBSCRIBED extension
  944.      *            this is like list but returns only subscribed folders
  945.      *            flag meanings are like LIST, not LSUB
  946.      *            \NonExistent means mailbox doesn't exist
  947.      *            \PlaceHolder means parent is not valid (selectable), but one or more children are
  948.      *            \NoSelect indeed means that the folder should not be selected
  949.      *            IMAPEXT-LIST-EXTENSIONS-04 August 2003 B. Leiba
  950.      */
  951.     if (!$show_only_subscribed{
  952.         $lsub 'LIST';
  953.         $sub_cache_name='list_cache';
  954.     }  elseif ($listsubscribed{
  955.         $lsub 'LIST (SUBSCRIBED)';
  956.         $sub_cache_name='listsub_cache';
  957.     else {
  958.         $lsub 'LSUB';
  959.         $sub_cache_name='lsub_cache';
  960.     }
  961.  
  962.     // Some IMAP servers allow subfolders to exist even if the parent folders do not
  963.     // This fixes some problems with the folder list when this is the case, causing the
  964.     // NoSelect folders to be displayed
  965.     if ($noselect_fix_enable{
  966.         $lsub_args "$lsub \"$folder_prefix\" \"*%\"";
  967.         $list_args "LIST \"$folder_prefix\" \"*%\"";
  968.     else {
  969.         $lsub_args "$lsub \"$folder_prefix\" \"*\"";
  970.         $list_args "LIST \"$folder_prefix\" \"*\"";
  971.     }
  972.  
  973.     // get subscribed mailbox list from cache (session)
  974.     // if not there, then get it from the imap server and store in cache
  975.  
  976.     if (!$force{
  977.         sqgetGlobalVar($sub_cache_name,$lsub_cache,SQ_SESSION);
  978.     }
  979.  
  980.     $lsub_assoc_ary=array();
  981.     if (!empty($lsub_cache)) {
  982.         $lsub_assoc_ary=$lsub_cache;
  983.     else {
  984.         $lsub_ary sqimap_run_command ($imap_stream$lsub_argstrue$response$message);
  985.         $lsub_ary compact_mailboxes_response($lsub_ary);
  986.         if (!empty($lsub_ary)) {
  987.             foreach ($lsub_ary as $rawline{
  988.                 $temp_mailbox_name=find_mailbox_name($rawline);
  989.                 $lsub_assoc_ary[$temp_mailbox_name]=$rawline;
  990.             }
  991.             unset($lsub_ary);
  992.             sqsession_register($lsub_assoc_ary,$sub_cache_name);
  993.         }
  994.     }
  995.  
  996.     // Now to get the mailbox flags
  997.     // The LSUB response may return \NoSelect flags, etc. but it is optional
  998.     // according to RFC3501, and even when returned it may not be accurate
  999.     // or authoratative. LIST will always return accurate results.
  1000.     if (($lsub == 'LIST'|| ($lsub == 'LIST (SUBSCRIBED)')) {
  1001.         // we've already done a LIST or LIST (SUBSCRIBED)
  1002.         // and NOT a LSUB, so no need to do it again
  1003.         $list_assoc_ary  $lsub_assoc_ary;
  1004.     else {
  1005.         // we did a LSUB so now we need to do a LIST
  1006.         // first see if it is in cache
  1007.         $list_cache_name='list_cache';
  1008.         if (!$force{
  1009.             sqgetGlobalVar($list_cache_name,$list_cache,SQ_SESSION);
  1010.         }
  1011.  
  1012.         if (!empty($list_cache)) {
  1013.             $list_assoc_ary=$list_cache;
  1014.             // we could store this in list_cache_name but not necessary
  1015.         else {
  1016.             // not in cache so we need to go get it from the imap server
  1017.             $list_assoc_ary array();
  1018.             $list_ary sqimap_run_command($imap_stream$list_args,
  1019.                                            true$response$message);
  1020.             $list_ary compact_mailboxes_response($list_ary);
  1021.             if (!empty($list_ary)) {
  1022.                 foreach ($list_ary as $rawline{
  1023.                     $temp_mailbox_name=find_mailbox_name($rawline);
  1024.                     $list_assoc_ary[$temp_mailbox_name]=$rawline;
  1025.                 }
  1026.                 unset($list_ary);
  1027.                 sqsession_register($list_assoc_ary,$list_cache_name);
  1028.             }
  1029.         }
  1030.     }
  1031.  
  1032.     // If they aren't subscribed to the inbox, then add it anyway (if its in LIST)
  1033.     $inbox_subscribed=false;
  1034.     if (!empty($lsub_assoc_ary)) {
  1035.         foreach ($lsub_assoc_ary as $temp_mailbox_name=>$rawline{
  1036.             if (strtoupper($temp_mailbox_name== 'INBOX'{
  1037.                 $inbox_subscribed=true;
  1038.             }
  1039.         }
  1040.     }
  1041.     if (!$inbox_subscribed)  {
  1042.         if (!empty($list_assoc_ary)) {
  1043.             foreach ($list_assoc_ary as $temp_mailbox_name=>$rawline{
  1044.                 if (strtoupper($temp_mailbox_name== 'INBOX'{
  1045.                     $lsub_assoc_ary[$temp_mailbox_name]=$rawline;
  1046.                 }
  1047.             }
  1048.         }
  1049.     }
  1050.  
  1051.     // Now we have the raw output, we need to create an array of mailbox names we will return
  1052.     if (!$show_only_subscribed{
  1053.         $final_folders_assoc_ary=$list_assoc_ary;
  1054.     else {
  1055.         /**
  1056.          * only show subscribed folders
  1057.          * we need to merge the folders here... we can't trust the flags, etc. from the lsub_assoc_array
  1058.          * so we use the lsub_assoc_array as the list of folders and the values come from list_assoc_array
  1059.          */
  1060.         if (!empty($lsub_assoc_ary)) {
  1061.             foreach ($lsub_assoc_ary as $temp_mailbox_name=>$rawline{
  1062.                 if (!empty($list_assoc_ary[$temp_mailbox_name])) {
  1063.                     $final_folders_assoc_ary[$temp_mailbox_name]=$list_assoc_ary[$temp_mailbox_name];
  1064.                 }
  1065.             }
  1066.         }
  1067.     }
  1068.  
  1069.  
  1070.     // Now produce a flat, sorted list
  1071.     if (!empty($final_folders_assoc_ary)) {
  1072.         uksort($final_folders_assoc_ary,'strnatcasecmp');
  1073.         foreach ($final_folders_assoc_ary as $temp_mailbox_name=>$rawline{
  1074.             $final_folders_ary[]=$rawline;
  1075.         }
  1076.     }
  1077.  
  1078.     // this will put it into an array we can use later
  1079.     // containing:
  1080.     // raw    - Raw LIST/LSUB response from the IMAP server
  1081.     // formatted - formatted folder name
  1082.     // unformatted - unformatted, but with the delimiter at the end removed
  1083.     // unformated-dm - folder name as it appears in raw response
  1084.     // unformatted-disp - unformatted without $folder_prefix
  1085.     // id - the array element number (0, 1, 2, etc.)
  1086.     // flags - mailbox flags
  1087.     if (!empty($final_folders_ary)) {
  1088.         $boxesall sqimap_mailbox_parse($final_folders_ary);
  1089.     else {
  1090.         // they have no mailboxes
  1091.         $boxesall=array();
  1092.     }
  1093.  
  1094.     /* Now, lets sort for special folders */
  1095.     $boxesnew $used array();
  1096.  
  1097.     /* Find INBOX */
  1098.     $cnt count($boxesall);
  1099.     $used array_pad($used,$cnt,false);
  1100.     $has_inbox false;
  1101.     foreach ($boxesall as $k => $b)
  1102.     // was this but array not guaranteed to be contiguous: for($k = 0; $k < $cnt; ++$k)
  1103.     {
  1104.         if (strtoupper($boxesall[$k]['unformatted']== 'INBOX'{
  1105.             $boxesnew[$boxesall[$k];
  1106.             $used[$ktrue;
  1107.             $has_inbox true;
  1108.             break;
  1109.         }
  1110.     }
  1111.  
  1112.     if ($has_inbox == false{
  1113.         // do a list request for inbox because we should always show
  1114.         // inbox even if the user isn't subscribed to it.
  1115.         $inbox_ary sqimap_run_command($imap_stream'LIST "" "INBOX"',
  1116.                                         true$response$message);
  1117.         $inbox_ary compact_mailboxes_response($inbox_ary);
  1118.         if (count($inbox_ary)) {
  1119.             $inbox_entry sqimap_mailbox_parse($inbox_ary);
  1120.             // add it on top of the list
  1121.             if (!empty($boxesnew)) {
  1122.                 array_unshift($boxesnew,$inbox_entry[0]);
  1123.             else {
  1124.                 $boxesnew[]=$inbox_entry[0];
  1125.             }
  1126.             /* array_unshift($used,true); */
  1127.         }
  1128.     }
  1129.  
  1130.     /* List special folders and their subfolders, if requested. */
  1131.     if ($list_special_folders_first{
  1132.         foreach ($boxesall as $k => $b)
  1133.         // was this but array not guaranteed to be contiguous: for($k = 0; $k < $cnt; ++$k)
  1134.         {
  1135.             if (!$used[$k&& isSpecialMailbox($boxesall[$k]['unformatted'])) {
  1136.                 $boxesnew[$boxesall[$k];
  1137.                 $used[$k]   true;
  1138.             }
  1139.         }
  1140.     }
  1141.  
  1142.     /* Find INBOX's children */
  1143.     foreach ($boxesall as $k => $b)
  1144.     // was this but array not guaranteed to be contiguous: for($k = 0; $k < $cnt; ++$k)
  1145.     {
  1146.         $isboxbelow=isBoxBelow(strtoupper($boxesall[$k]['unformatted']),'INBOX');
  1147.         if (strtoupper($boxesall[$k]['unformatted']== 'INBOX'{
  1148.             $is_inbox=1;
  1149.         else {
  1150.             $is_inbox=0;
  1151.         }
  1152.  
  1153.         if (!$used[$k&& $isboxbelow && $is_inbox{
  1154.             $boxesnew[$boxesall[$k];
  1155.             $used[$ktrue;
  1156.         }
  1157.     }
  1158.  
  1159.     /* Rest of the folders */
  1160.     foreach ($boxesall as $k => $b)
  1161.     // was this but array not guaranteed to be contiguous: for($k = 0; $k < $cnt; ++$k)
  1162.     {
  1163.         if (!$used[$k]{
  1164.             $boxesnew[$boxesall[$k];
  1165.         }
  1166.     }
  1167.     /**
  1168.      * Don't register boxes in session, if $session_register is set to false
  1169.      * Prevents registration of sqimap_mailbox_list_all() results.
  1170.      */
  1171.     if ($session_registersqsession_register($boxesnew,'boxesnew');
  1172.     return $boxesnew;
  1173. }
  1174.  
  1175. /**
  1176.  * Fills mailbox object
  1177.  *
  1178.  * this is passed the mailbox array by left_main.php
  1179.  * who has previously obtained it from sqimap_get_mailboxes
  1180.  * that way, the raw mailbox list is available in left_main to other
  1181.  * things besides just sqimap_mailbox_tree
  1182.  * imap_stream is just used now to get status info
  1183.  *
  1184.  * most of the functionality is moved to sqimap_get_mailboxes
  1185.  * also takes care of TODO items:
  1186.  * caching mailbox tree
  1187.  * config setting for UW imap section (not needed now)
  1188.  *
  1189.  * Some code fragments are present in 1.3.0 - 1.4.4.
  1190.  * @param stream $imap_stream imap connection resource
  1191.  * @param array $lsub_ary output array from sqimap_get_mailboxes (contains mailboxes and flags)
  1192.  * @return object see mailboxes class.
  1193.  * @since 1.5.0
  1194.  */
  1195. function sqimap_mailbox_tree($imap_stream,$lsub_ary{
  1196.  
  1197.     $sorted_lsub_ary array();
  1198.     $cnt count($lsub_ary);
  1199.     for ($i 0$i $cnt$i++{
  1200.         $mbx=$lsub_ary[$i]['unformatted'];
  1201.         $flags=$lsub_ary[$i]['flags'];
  1202.  
  1203.         $noinferiors=0;
  1204.         if (in_array('\Noinferiors',$flags)) $noinferiors=1}
  1205.         if (in_array('\NoInferiors',$flags)) $noinferiors=1}
  1206.         if (in_array('\HasNoChildren',$flags)) $noinferiors=1}
  1207.  
  1208.         $noselect=0;
  1209.         if (in_array('\NoSelect',$flags)) $noselect=1}
  1210.         /**
  1211.          * LIST (SUBSCRIBED) has two new flags, \NonExistent which means the mailbox is subscribed to
  1212.          * but doesn't exist, and \PlaceHolder which is similar (but not the same) as \NoSelect
  1213.          * For right now, we'll treat these the same as \NoSelect and this behavior can be changed
  1214.          * later if needed
  1215.          */
  1216.         if (in_array('\NonExistent',$flags)) $noselect=1}
  1217.         if (in_array('\PlaceHolder',$flags)) $noselect=1}
  1218.         $sorted_lsub_ary[array ('mbx' => $mbx'noselect' => $noselect'noinferiors' => $noinferiors);
  1219.     }
  1220.  
  1221.     $sorted_lsub_ary array_values($sorted_lsub_ary);
  1222.     usort($sorted_lsub_ary'mbxSort');
  1223.     $boxestree sqimap_fill_mailbox_tree($sorted_lsub_ary,false,$imap_stream);
  1224.     return $boxestree;
  1225. }
  1226.  
  1227. /**
  1228.  * Callback function used for sorting mailboxes in sqimap_mailbox_tree
  1229.  * @param string $a 
  1230.  * @param string $b 
  1231.  * @return integer see php strnatcasecmp()
  1232.  * @since 1.5.1
  1233.  */
  1234. function mbxSort($a$b{
  1235.     return strnatcasecmp($a['mbx']$b['mbx']);
  1236. }
  1237.  
  1238. /**
  1239.  * Fills mailbox object
  1240.  *
  1241.  * Some code fragments are present in 1.3.0 - 1.4.4.
  1242.  * @param array $mbx_ary 
  1243.  * @param $mbxs 
  1244.  * @param stream $imap_stream imap connection resource
  1245.  * @return object see mailboxes class
  1246.  * @since 1.5.0
  1247.  */
  1248. function sqimap_fill_mailbox_tree($mbx_ary$mbxs=false,$imap_stream{
  1249.     global $data_dir$username$list_special_folders_first,
  1250.            $folder_prefix$trash_folder$sent_folder$draft_folder,
  1251.            $move_to_trash$move_to_sent$save_as_draft,
  1252.            $delimiter$imap_server_type;
  1253.  
  1254.     // $special_folders = array ('INBOX', $sent_folder, $draft_folder, $trash_folder);
  1255.  
  1256.     /* create virtual root node */
  1257.     $mailboxesnew mailboxes();
  1258.     $mailboxes->is_root true;
  1259.     $trail_del false;
  1260.     $start 0;
  1261.  
  1262.     if (isset($folder_prefix&& ($folder_prefix != '')) {
  1263.         $start substr_count($folder_prefix,$delimiter);
  1264.         if (strrpos($folder_prefix$delimiter== (strlen($folder_prefix)-1)) {
  1265.             $mailboxes->mailboxname_full substr($folder_prefix,0(strlen($folder_prefix)-1));
  1266.         else {
  1267.             $mailboxes->mailboxname_full $folder_prefix;
  1268.             $start++;
  1269.         }
  1270.         $mailboxes->mailboxname_sub $mailboxes->mailboxname_full;
  1271.     else {
  1272.         $start 0;
  1273.     }
  1274.  
  1275.     $cnt count($mbx_ary);
  1276.     for ($i=0$i $cnt$i++{
  1277.         if ($mbx_ary[$i]['mbx'!='' {
  1278.             $mbx new mailboxes();
  1279.             $mailbox $mbx_ary[$i]['mbx'];
  1280.  
  1281.             /*
  1282.              * Set the is_special flag if it concerned a special mailbox.
  1283.              * Used for displaying the special folders on top in the mailbox
  1284.              * tree displaying code.
  1285.              */
  1286.             $mbx->is_special |= ($mbx->is_inbox (strtoupper($mailbox== 'INBOX'));
  1287.             $mbx->is_special |= ($mbx->is_trash isTrashMailbox($mailbox));
  1288.             $mbx->is_special |= ($mbx->is_sent isSentMailbox($mailbox));
  1289.             $mbx->is_special |= ($mbx->is_draft isDraftMailbox($mailbox));
  1290.  
  1291.             if (!$mbx->is_special)
  1292.                 $mbx->is_special boolean_hook_function('special_mailbox'$mailbox1);
  1293.  
  1294.             if (isset($mbx_ary[$i]['unseen'])) {
  1295.                 $mbx->unseen $mbx_ary[$i]['unseen'];
  1296.             }
  1297.             if (isset($mbx_ary[$i]['nummessages'])) {
  1298.                 $mbx->total $mbx_ary[$i]['nummessages'];
  1299.             }
  1300.  
  1301.             $mbx->is_noselect $mbx_ary[$i]['noselect'];
  1302.             $mbx->is_noinferiors $mbx_ary[$i]['noinferiors'];
  1303.  
  1304.             $r_del_pos strrpos($mbx_ary[$i]['mbx']$delimiter);
  1305.             if ($r_del_pos{
  1306.                 $mbx->mailboxname_sub substr($mbx_ary[$i]['mbx'],$r_del_pos+1);
  1307.             else {   /* mailbox is root folder */
  1308.                 $mbx->mailboxname_sub $mbx_ary[$i]['mbx'];
  1309.             }
  1310.             $mbx->mailboxname_full $mbx_ary[$i]['mbx'];
  1311.  
  1312.             $mailboxes->addMbx($mbx$delimiter$start$list_special_folders_first);
  1313.         }
  1314.     }
  1315.     sqimap_utf7_decode_mbx_tree($mailboxes);
  1316.     sqimap_get_status_mbx_tree($imap_stream,$mailboxes);
  1317.     return $mailboxes;
  1318. }
  1319.  
  1320. /**
  1321.  * @param object $mbx_tree 
  1322.  * @since 1.5.0
  1323.  */
  1324. function sqimap_utf7_decode_mbx_tree(&$mbx_tree{
  1325.     global $draft_folder$sent_folder$trash_folder$translate_special_folders;
  1326.  
  1327.     /* decode folder name and set mailboxname_sub */
  1328.     if ($translate_special_folders && strtoupper($mbx_tree->mailboxname_full== 'INBOX'{
  1329.         $mbx_tree->mailboxname_sub _("INBOX");
  1330.     elseif ($translate_special_folders && $mbx_tree->mailboxname_full == $draft_folder{
  1331.         $mbx_tree->mailboxname_sub _("Drafts");
  1332.     elseif ($translate_special_folders && $mbx_tree->mailboxname_full == $sent_folder{
  1333.         $mbx_tree->mailboxname_sub _("Sent");
  1334.     elseif ($translate_special_folders && $mbx_tree->mailboxname_full == $trash_folder{
  1335.         $mbx_tree->mailboxname_sub _("Trash");
  1336.     else {
  1337.         $mbx_tree->mailboxname_sub imap_utf7_decode_local($mbx_tree->mailboxname_sub);
  1338.     }
  1339.  
  1340.     if ($mbx_tree->mbxs{
  1341.         $iCnt count($mbx_tree->mbxs);
  1342.         for ($i=0;$i<$iCnt;++$i{
  1343.             sqimap_utf7_decode_mbx_tree($mbx_tree->mbxs[$i]);
  1344.         }
  1345.     }
  1346. }
  1347.  
  1348. /**
  1349.  * @param object $mbx_tree 
  1350.  * @param array $aMbxs 
  1351.  * @since 1.5.0
  1352.  */
  1353. function sqimap_tree_to_ref_array(&$mbx_tree,&$aMbxs{
  1354.    if ($mbx_tree)
  1355.    $aMbxs[=$mbx_tree;
  1356.    if ($mbx_tree->mbxs{
  1357.       $iCnt count($mbx_tree->mbxs);
  1358.       for ($i=0;$i<$iCnt;++$i{
  1359.          sqimap_tree_to_ref_array($mbx_tree->mbxs[$i],$aMbxs);
  1360.       }
  1361.    }
  1362. }
  1363.  
  1364. /**
  1365.  * @param stream $imap_stream imap connection resource
  1366.  * @param object $mbx_tree 
  1367.  * @since since 1.5.0
  1368.  */
  1369. function sqimap_get_status_mbx_tree($imap_stream,&$mbx_tree{
  1370.     global $unseen_notify$unseen_type$trash_folder,$move_to_trash;
  1371.     $aMbxs $aQuery array();
  1372.     sqimap_tree_to_ref_array($mbx_tree,$aMbxs);
  1373.     // remove the root node
  1374.     array_shift($aMbxs);
  1375.  
  1376.     if($unseen_notify == 3{
  1377.         $cnt count($aMbxs);
  1378.         for($i=0;$i<$cnt;++$i{
  1379.             $oMbx =$aMbxs[$i];
  1380.             if (!$oMbx->is_noselect{
  1381.                 $mbx $oMbx->mailboxname_full;
  1382.                 if ($unseen_type == ||
  1383.                    ($move_to_trash && $oMbx->mailboxname_full == $trash_folder)) {
  1384.                    $query 'STATUS ' sqimap_encode_mailbox_name($mbx' (MESSAGES UNSEEN RECENT)';
  1385.                 else {
  1386.                    $query 'STATUS ' sqimap_encode_mailbox_name($mbx' (UNSEEN RECENT)';
  1387.                 }
  1388.                 sqimap_prepare_pipelined_query($query,$tag,$aQuery,false);
  1389.             else {
  1390.                 $oMbx->unseen $oMbx->total $oMbx->recent false;
  1391.                 $tag false;
  1392.             }
  1393.             $oMbx->tag $tag;
  1394.             $aMbxs[$i=$oMbx;
  1395.         }
  1396.         // execute all the queries at once
  1397.         $aResponse sqimap_run_pipelined_command ($imap_stream$aQueryfalse$aServerResponse$aServerMessage);
  1398.         $cnt count($aMbxs);
  1399.         for($i=0;$i<$cnt;++$i{
  1400.             $oMbx =$aMbxs[$i];
  1401.             $tag $oMbx->tag;
  1402.             if ($tag && $aServerResponse[$tag== 'OK'{
  1403.                 $sResponse implode(''$aResponse[$tag]);
  1404.                 if (preg_match('/UNSEEN\s+([0-9]+)/i'$sResponse$regs)) {
  1405.                     $oMbx->unseen $regs[1];
  1406.                 }
  1407.                 if (preg_match('/MESSAGES\s+([0-9]+)/i'$sResponse$regs)) {
  1408.                     $oMbx->total $regs[1];
  1409.                 }
  1410.                 if (preg_match('/RECENT\s+([0-9]+)/i'$sResponse$regs)) {
  1411.                     $oMbx->recent $regs[1];
  1412.                 }
  1413.  
  1414.            }
  1415.            unset($oMbx->tag);
  1416.         }
  1417.     else if ($unseen_notify == 2// INBOX only
  1418.         $cnt count($aMbxs);
  1419.         for($i=0;$i<$cnt;++$i{
  1420.             $oMbx =$aMbxs[$i];
  1421.             if (strtoupper($oMbx->mailboxname_full== 'INBOX' ||
  1422.                ($move_to_trash && $oMbx->mailboxname_full == $trash_folder)) {
  1423.                  if ($unseen_type == ||
  1424.                    ($oMbx->mailboxname_full == $trash_folder && $move_to_trash)) {
  1425.                     $aStatus sqimap_status_messages($imap_stream,$oMbx->mailboxname_full);
  1426.                     $oMbx->unseen $aStatus['UNSEEN'];
  1427.                     $oMbx->total  $aStatus['MESSAGES'];
  1428.                     $oMbx->recent $aStatus['RECENT'];
  1429.                 else {
  1430.                     $oMbx->unseen sqimap_unseen_messages($imap_stream,$oMbx->mailboxname_full);
  1431.                 }
  1432.                 $aMbxs[$i=$oMbx;
  1433.                 if (!$move_to_trash && $trash_folder{
  1434.                     break;
  1435.                 else {
  1436.                    // trash comes after INBOX
  1437.                    if ($oMbx->mailboxname_full == $trash_folder{
  1438.                       break;
  1439.                    }
  1440.                 }
  1441.             }
  1442.         }
  1443.     }
  1444.  
  1445.     $cnt count($aMbxs);
  1446.     for($i=0;$i<$cnt;++$i{
  1447.          $oMbx =$aMbxs[$i];
  1448.          unset($hook_status);
  1449.          if (!empty($oMbx->unseen)) $hook_status['UNSEEN']=$oMbx->unseen}
  1450.          if (!empty($oMbx->total)) $hook_status['MESSAGES']=$oMbx->total}
  1451.          if (!empty($oMbx->recent)) $hook_status['RECENT']=$oMbx->recent}
  1452.          if (!empty($hook_status))
  1453.          {
  1454.               $hook_status['MAILBOX']=$oMbx->mailboxname_full;
  1455.               $hook_status['CALLER']='sqimap_get_status_mbx_tree'// helps w/ debugging
  1456.               do_hook('folder_status'$hook_status);
  1457.          }
  1458.     }
  1459. }
  1460.  
  1461. /**
  1462.  * Checks if folder is noselect (can't store messages)
  1463.  *
  1464.  * Function does not check if folder subscribed.
  1465.  * @param stream $oImapStream imap connection resource
  1466.  * @param string $sImapFolder imap folder name
  1467.  * @param object $oBoxes mailboxes class object.
  1468.  * @return boolean true, when folder has noselect flag. false in any other case.
  1469.  * @since 1.5.1
  1470.  */
  1471. function sqimap_mailbox_is_noselect($oImapStream,$sImapFolder,&$oBoxes{
  1472.     // build mailbox object if it is not available
  1473.     if (is_object($oBoxes)) $oBoxes=sqimap_mailbox_list($oImapStream);
  1474.     foreach($oBoxes as $box{
  1475.         if ($box['unformatted']==$sImapFolder{
  1476.             return (bool) check_is_noselect($box['raw']);
  1477.         }
  1478.     }
  1479.     return false;
  1480. }
  1481.  
  1482. /**
  1483.  * Checks if folder is noinferiors (can't store other folders)
  1484.  *
  1485.  * Function does not check if folder subscribed.
  1486.  * @param stream $oImapStream imap connection resource
  1487.  * @param string $sImapFolder imap folder name
  1488.  * @param object $oBoxes mailboxes class object.
  1489.  * @return boolean true, when folder has noinferiors flag. false in any other case.
  1490.  * @since 1.5.1
  1491.  */
  1492. function sqimap_mailbox_is_noinferiors($oImapStream,$sImapFolder,&$oBoxes{
  1493.     // build mailbox object if it is not available
  1494.     if (is_object($oBoxes)) $oBoxes=sqimap_mailbox_list($oImapStream);
  1495.     foreach($oBoxes as $box{
  1496.         if ($box['unformatted']==$sImapFolder{
  1497.             return (bool) check_is_noinferiors($box['raw']);
  1498.         }
  1499.     }
  1500.     return false;
  1501. }

Documentation generated on Mon, 13 Jan 2020 04:22:47 +0100 by phpDocumentor 1.4.3