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

Documentation generated on Sat, 07 Oct 2006 16:11:45 +0300 by phpDocumentor 1.3.0RC6