Logo Search packages:      
Sourcecode: egroupware version File versions  Download package

class.mail_msg_wrappers.inc.php

<?php
      /**************************************************************************\
      * AngleMail - E-Mail Message Processing Functions                       *
      * http://www.anglemail.org                                                    *
      * http://www.egroupware.org                                                   *
      */
      /**************************************************************************\
      * AngleMail - E-Mail Message Processing Functions                             *
      * This file written by Angelo Puglisi (Angles) <angles@aminvestments.com>     *
      * Handles specific operations in manipulating email messages                  *
      * Copyright (C) 2001, 2002 Angelo Tony Puglisi (Angles)                       *
      * ------------------------------------------------------------------------          *
      * This library is free software; you can redistribute it and/or modify it           *
      * under the terms of the GNU Lesser General Public License as published by    *
      * the Free Software Foundation; either version 2.1 of the License,                  *
      * or any later version.                                           *
      * This library is distributed in the hope that it will be useful, but               *
      * WITHOUT ANY WARRANTY; without even the implied warranty of      *
      * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.      *
      * See the GNU Lesser General Public License for more details.                 *
      * You should have received a copy of the GNU Lesser General Public License    *
      * along with this library; if not, write to the Free Software Foundation,           *
      * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA               *
      \**************************************************************************/
      
      /* $Id: class.mail_msg_wrappers.inc.php,v 1.49.2.1 2004/08/24 10:26:17 ralfbecker Exp $ */
      
      // =====  INTERFACE FUNCTIONS AND/OR  WRAPPER FUNCTIONS =====
      
      /*!
      @class mail_msg_wrappers
      @abstract  Wrapper functions to be called as "public" functions
      @discussion  Hides the implementation details from the calling process
      Provides most args to the dcom class from variables which class msg processed and set
      Sometimes returns processed data ready to be used for display or information
      MORE DISCUSSION - Why Wrap Here?
      Answer: because once the msg class opens a mailsvr_stream, that will be the only stream
      that instance of the class will have, so WHY keep supplying it as an arg EVERY time?
      Also, same for the "msgnum", unless you are looping thru a message list, you are 
      most likely concerned with only ONE message, and the variable would be the MIME part therein
      */
00042       class mail_msg_wrappers extends mail_msg_base
      {
      
            /*!
            @function mail_msg_wrappers
            @abstract CONSTRUCTOR place holder, does nothing  
            */
00049             function mail_msg_wrappers()
            {
                  return;
            }
            
            // ====  Functions For Getting Information About A Message  ====
            
            /*!
            @function phpgw_fetchstructure
            @abstract wrapper for IMAP_FETSCSTRUCTURE, phpgw supplies the nedessary stream arg
            @param $msgnum   integer
            @result returns the IMAP_FETSCSTRUCTURE data
            @discussion Wrapper supplies the needed mailsvr_stream arg to IMAP_FETSCSTRUCTURE
            The data communications object (class mail_dcom) is supplied by the class. NOTE: this data 
            CAN ONLY BE OBTAINED FOR A MSG IN THE CURRENTLY SELECTED FOLDER. 
            This means we automatically know which folder this data applies to because it can ONLY be 
            the currently selected folder, and only one folder can be selected at any one time. 
            CACHE NOTE if $this->session_cache_extreme is True, then this data is cached and 
            manipulated by the "extreme" caching code, which will pop a cached "msg_structure" out 
            of cache if the message is moved to another folder. If $this->session_cache_extreme is False, 
            then caching is NOT used on this data.
            */
00071             function phpgw_fetchstructure($msgball='')
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).phpgw_fetchstructure: ('.__LINE__.') ENTERING<br>'); }
                  if (!(isset($msgball))
                  || ((string)$msgball == ''))
                  {
                        $msgball = $this->get_arg_value('msgball');
                  }
                  $acctnum = $msgball['acctnum'];
                  if (!(isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  // CHECK FOR CACHED ITEM
                  // if "session_cache_extreme" is not enabled, do not use caching for this data
                  
                  if ($this->session_cache_extreme == True)
                  {
                        // function read_session_cache_item($data_name='misc', $acctnum='', $extra_keys='')
                        // this key, if it exists in the cached array of msg_structures, will hold the data we want as its value 
                        // this msgball *may* not have a "folder" element because fetchstructure can only be for the current folder anyway
                        // so sometimes we feed the msgball with no folder into here because it is obvious anyway.
                        if ((isset($msgball['folder']))
                        && (trim($msgball['folder']) != ''))
                        {
                              //$extra_keys = $msgball['folder'].'_'.(string)$msgball['msgnum'];
                              $ex_folder = $msgball['folder'];
                              $ex_msgnum = $msgball['msgnum'];
                        }
                        else
                        {
                              //$extra_keys = $this->prep_folder_out().'_'.(string)$msgball['msgnum'];
                              $ex_folder = $this->prep_folder_out();
                              $ex_msgnum = $msgball['msgnum'];
                        }
                        // the cached data is returned as a ready to use object if it exists, or False if not existing
                        //$cache_msg_structure = $this->read_session_cache_item('msg_structure', $acctnum, $extra_keys);
                        $cache_msg_structure = $this->read_session_cache_item('msg_structure', $acctnum, $ex_folder, $ex_msgnum);
                        //echo '** phpgw_fetchstructure: $specific_key ['.$specific_key.'] :: $cache_msg_structure DUMP<pre>'; print_r($cache_msg_structure); echo '</pre>';
                  }
                  else
                  {
                        // provide an empty var so the following if .. then does not complain about "undefined var"
                        // because this var is tested along with the "cache_phpgw_header" flag, it should at least 
                        // exist even if caching is not turned on just so the following test is "cool" with it
                        $cache_msg_structure = '';
                  }
                  
                  if (($cache_msg_structure)
                  && ($this->session_cache_extreme == True))
                  {
                        //echo '** phpgw_fetchstructure: $specific_key ['.$specific_key.'] :: $cache_msg_structure DUMP<pre>'; print_r($cache_msg_structure); echo '</pre>';
                        if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(wrappers).phpgw_fetchstructure: ('.__LINE__.') about to return $cache_msg_structure DUMP:', $cache_msg_structure); }
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).phpgw_fetchstructure: ('.__LINE__.') LEAVING returning $cache_msg_structure<br>'); }
                        return $cache_msg_structure;
                  }
                  else
                  {
                        // NO CACHED ITEM or CACHING NOT ENABLED
                        // get  the data from the mail server
                        $this->ensure_stream_and_folder($msgball, 'phpgw_fetchstructure'.' LINE '.__LINE__);
                        $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                        $data = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->fetchstructure($mailsvr_stream, $msgball['msgnum']);
                        // PUT THIS IN CACHE
                        //  if "session_cache_extreme" is True
                        if ($this->session_cache_extreme == True)
                        {
                              // this msgball *may* not have a "folder" element because fetchstructure can only be for the current folder anyway
                              // so sometimes we feed the msgball with no folder into here because it is obvious anyway.
                              // But for caching purposes we will MAKE SURE it has folder so we can check the cache for other than the selected folder 
                              // at a later date
                              if (!(isset($msgball['folder']))
                              || ($msgball['folder'] == ''))
                              {
                                    //$msgball['folder'] = $this->get_arg_value('folder');
                                    $msgball['folder'] = $this->prep_folder_out($this->get_arg_value('folder'));
                              }
                              // this is the way we pass msg_structure data to the caching function
                              // NOTE that $extra_keys is generated FOR US in the function that saves this to appsesion cache
                              $meta_data = array();
                              $meta_data['msgball'] = array();
                              $meta_data['msgball'] = $msgball;
                              $meta_data['msg_structure'] = $data;
                              
                              // SET_CACHE_ITEM
                              //echo 'saving msg_structure to cache<br>';
                              $this->save_session_cache_item('msg_structure', $meta_data, $acctnum);
                        }
                        if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(wrappers).phpgw_fetchstructure: ('.__LINE__.') about to return $data DUMP:', $data); }
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).phpgw_fetchstructure: ('.__LINE__.') LEAVING returning $data from server<br>'); }
                        return $data;
                  }
            }
            
            /*!
            @function phpgw_header
            @abstract wrapper for IMAP_HEADER, phpgw supplies the necessary stream arg and mail_dcom reference
            @param $msgball (typed array)
            @result returns the php IMAP_HEADER data
            @discussion Wrapper supplies the needed mailsvr_stream arg to IMAP_HEADER. 
            Message Information: THE MESSAGE'S HEADERS RETURNED AS A STRUCTURE. 
            The data communications object (class mail_dcom) is supplied by the class. 
            CACHE NOTE if $this->session_cache_extreme is True, then this data is cached and 
            manipulated by the "extreme" caching code, which will pop a cached "phpgw_header" out 
            of cache if the message is moved to another folder, and manually clear a cached "phpgw_header" 
            items flag from "unseen" or "recent", if necessary, if the message is read, and put the updated 
            "phpgw_header" item back in cache, with no need to contact the mailserver about this. Eventhough 
            we still need to contact the mail server to get the body, by manually clearing the flag, if necssary, as 
            described above, then when the user goes back to the message list after reading the message, 
            it is possible that ALL information required to make that index page is "fresh" in local cache, 
            and NO login to the mailserver is done in that case. Situations where ALL the necessary data 
            is not in the cache are as follows, if the user deleted or moved ONE message, for example, it 
            may be possible that the index page needs to contact the mailserver to get one additional "phpgw_header" 
            (and also one additional "msg_structure") item to fill out the message list page. If the user had already 
            viewed the index page that had that message, such as paging forward and then backwards thru the 
            message list, the the single message that *was* on the next message list page that is now on the *current* 
            message list page, would already be in the cache. If $this->session_cache_extreme is False, 
            then caching is NOT used on this data.
            */
00192             function phpgw_header($msgball='')
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).phpgw_header: ('.__LINE__.') ENTERING<br>'); }
                  if (!(isset($msgball))
                  || ((string)$msgball == ''))
                  {
                        $msgball = $this->get_arg_value('msgball');
                  }
                  //$acctnum = $msgball['acctnum'];
                  //if (!(isset($acctnum))
                  //|| ((string)$acctnum == ''))
                  //{
                  //    $acctnum = $this->get_acctnum();
                  //}
                  if ((isset($msgball['acctnum']))
                  || ((string)$msgball['acctnum'] != ''))
                  {
                        $acctnum = $msgball['acctnum'];
                  }
                  else
                  {
                        $acctnum = $this->get_acctnum();
                        $msgball['acctnum'] = $acctnum;
                  }
                  
                  // CHECK FOR CACHED ITEM
                  // if "session_cache_extreme" is not enabled, do not use caching for this data
                  
                  if ($this->session_cache_extreme == True)
                  {
                        // function read_session_cache_item($data_name='misc', $acctnum='', $extra_keys='')
                        // this key, if it exists in the cached array of msg_structures, will hold the data we want as its value 
                        // this msgball *may* not have a "folder" element because header can only be for the current folder anyway
                        // so sometimes we feed the msgball with no folder into here because it is obvious anyway.
                        if ((isset($msgball['folder']))
                        && (trim($msgball['folder']) != ''))
                        {
                              //$extra_keys = $msgball['folder'].'_'.(string)$msgball['msgnum'];
                              $ex_folder = $msgball['folder'];
                              $ex_msgnum = $msgball['msgnum'];
                        }
                        else
                        {
                              //$extra_keys = $this->prep_folder_out().'_'.(string)$msgball['msgnum'];
                              $ex_folder = $this->prep_folder_out();
                              $ex_msgnum = $msgball['msgnum'];
                        }
                        // the cached data is returned as a ready to use object if it exists, or False if not existing
                        //$cache_phpgw_header = $this->read_session_cache_item('phpgw_header', $acctnum, $extra_keys);
                        $cache_phpgw_header = $this->read_session_cache_item('phpgw_header', $acctnum, $ex_folder, $ex_msgnum);
                        //echo '** phpgw_header: $specific_key ['.$specific_key.'] :: $cache_phpgw_header DUMP<pre>'; print_r($cache_phpgw_header); echo '</pre>';
                  }
                  else
                  {
                        // provide an empty var so the following if .. then does not complain about "undefined var"
                        // because this var is tested along with the "cache_phpgw_header" flag, it should at least 
                        // exist even if caching is not turned on just so the following test is "cool" with it
                        $cache_phpgw_header = '';
                  }
                  
                  if (($cache_phpgw_header)
                  && ($this->session_cache_extreme == True))
                  {
                        //echo '** phpgw_header: $specific_key ['.$specific_key.'] :: $cache_phpgw_header DUMP<pre>'; print_r($cache_phpgw_header); echo '</pre>';
                        if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(wrappers).phpgw_header: ('.__LINE__.') about to return $cache_phpgw_header DUMP:', $cache_phpgw_header); }
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).phpgw_header: ('.__LINE__.') LEAVING returning $cache_phpgw_header<br>'); }
                        return $cache_phpgw_header;
                  }
                  else
                  {
                        // NO CACHED ITEM or CACHING NOT ENABLED
                        // get  the data from the mail server
                        $this->ensure_stream_and_folder($msgball, 'phpgw_header'.' LINE '.__LINE__);
                        $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                        $data = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->header($mailsvr_stream, $msgball['msgnum']);
                        
                               // fix escape problem FORGET THIS FOR NOW
                              //$this->cache_clean_phpgw_header($data);
                        
                        // PUT THIS IN CACHE
                        //  if "session_cache_extreme" is True
                        if (($data)
                        && ($this->session_cache_extreme == True))
                        {
                              // this msgball *may* not have a "folder" element because header can only be for the current folder anyway
                              // so sometimes we feed the msgball with no folder into here because it is obvious anyway.
                              // But for caching purposes we will MAKE SURE it has folder so we can check the cache for other than the selected folder 
                              // at a later date
                              if (!(isset($msgball['folder']))
                              || ($msgball['folder'] == ''))
                              {
                                    //$msgball['folder'] = $this->get_arg_value('folder');
                                    $msgball['folder'] = $this->prep_folder_out($this->get_arg_value('folder'));
                              }
                              
                               // fix escape problem
                              //$this->cache_clean_phpgw_header($data);
                              
                              // this is the way we pass phpgw_header data to the caching function
                              $meta_data = array();
                              $meta_data['msgball'] = array();
                              $meta_data['msgball'] = $msgball;
                              $meta_data['phpgw_header'] = $data;
                        
                              // SET_CACHE_ITEM
                              // NOTE that $extra_keys is generated FOR US in the function that saves this to appsesion cache
                              //echo 'saving phpgw_header to cache<br>';
                              $this->save_session_cache_item('phpgw_header', $meta_data, $acctnum);
                        }
                        if (!$data)
                        {
                              if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).phpgw_header: ('.__LINE__.') LEAVING on semi-error, no $data available to return<br>'); }
                              return False;
                        }
                        else
                        {
                              if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(wrappers).phpgw_header: ('.__LINE__.') about to return $data DUMP:', $data); }
                              if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).phpgw_header: ('.__LINE__.') LEAVING returning $data from server<br>'); }
                              return $data;
                        }
                  }
            }
            
            /*!
            @function cache_clean_phpgw_header
            @abstract ?
            @param $msg_headers (php struct from header)  btw it is a refrence OOP directly on param
            @author Angles
            @discussion ?
            
            function cache_clean_phpgw_header(&$msg_headers)
            {
                  //$debug = 0;
                  $debug = 3;
                  
                  if (
                    (!isset($msg_headers))
                  || (!$msg_headers )
                  //|| ($this->session_cache_extreme == False)
                  )
                  {
                        return '';
                  }
                  // toaddress
                  // to[]
                  // fromaddress
                  // from[]
                  // reply_toaddress
                  // reply_to[]
                  // senderaddress
                  // sender[]
                  // cc[]
                  
                  if ($debug > 2) { $this->dbug->out('wrappers.cache_clean_phpgw_header('.__LINE__.'): BEFORE $msg_headers DUMP:', $msg_headers);  }
                  // toaddress
                  if ($msg_headers->toaddress)
                  {
                        $msg_headers->toaddress = addslashes($msg_headers->toaddress);
                  }
                  
                  // to[]
                  if ($msg_headers->to)
                  {
                        $loops = count($msg_headers->to);
                        for($i=0;$i<$loops;$i++)
                        {
                              $thingy =& $msg_headers->to[$i];
                              $thingy->personal = addslashes($thingy->personal);
                        }
                  }
                  // fromaddress
                  if ($msg_headers->fromaddress)
                  {
                        $msg_headers->fromaddress = addslashes($msg_headers->fromaddress);
                  }
                  // from[]
                  if ($msg_headers->from)
                  {
                        $loops = count($msg_headers->from);
                        for($i=0;$i<$loops;$i++)
                        {
                              $thingy =& $msg_headers->from[$i];
                              $thingy->personal = addslashes($thingy->personal);
                        }
                  }
                  
                  // reply_toaddress
                  if ($msg_headers->reply_toaddress)
                  {
                        $msg_headers->reply_toaddress = addslashes($msg_headers->reply_toaddress);
                  }
                  // reply_to[]
                  if ($msg_headers->reply_to)
                  {
                        $loops = count($msg_headers->reply_to);
                        for($i=0;$i<$loops;$i++)
                        {
                              $thingy =& $msg_headers->reply_to[$i];
                              $thingy->personal = addslashes($thingy->personal);
                        }
                  }
                  
                  // senderaddress
                  if ($msg_headers->senderaddress)
                  {
                        $msg_headers->senderaddress = addslashes($msg_headers->senderaddress);
                  }
                  // sender[]
                  if ($msg_headers->sender)
                  {
                        $loops = count($msg_headers->sender);
                        for($i=0;$i<$loops;$i++)
                        {
                              $thingy =& $msg_headers->sender[$i];
                              $thingy->personal = addslashes($thingy->personal);
                        }
                  }
                  
                  // cc[]
                  
                  if ($debug > 2) { $this->dbug->out('wrappers.cache_clean_phpgw_header('.__LINE__.'): AFTER $msg_headers DUMP:', $msg_headers);  }
            }
            */
            
            /*!
            @function phpgw_fetchheader
            @abstract returns the message RAW headers as a blob, or long string.
            @param $msgball (typed array) 
            @author Angles
            @discussion Used by filtering, and in other cases where testing or checking the 
            actual message headers as a text item, is necessary.
            */
00424             function phpgw_fetchheader($msgball='')
            {
                  if (!(isset($msgball))
                  || ((string)$msgball == ''))
                  {
                        $msgball = $this->get_arg_value('msgball');
                  }
                  $acctnum = $msgball['acctnum'];
                  if (!(isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  $this->ensure_stream_and_folder($msgball, 'phpgw_fetchheader'.' LINE '.__LINE__);
                  
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  // Message Information: THE MESSAGE'S HEADERS RETURNED RAW (no processing)
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->fetchheader($mailsvr_stream, $msgball['msgnum']);
            }
      
            /*!
            @function all_headers_in_folder
            @abstract wrapper for IMAP_HEADERS, phpgw supplies the nedessary stream arg and mail_dcom reference DEPRECIATED
            @param $fldball   array[folder]   string ; array[acctnum]   int
            @result returns the php IMAP_HEADERS data, php manual says 
            function.imap-headers.php
            Returns headers for all messages in a mailbox 
            Returns an array of string formatted with header info. One element per mail message
            @discussion = = = = USELESS FUNCTION = = = = 
            DEPRECIATED DEPRECIATED DEPRECIATED
            returns array of strings, each string is extremely truncated
            partial contents of date, from, and subject, also includes the msg size in chars
            */
00458             function all_headers_in_folder($fldball='')
            {
                  if (!(isset($fldball))
                  || ((string)$fldball == ''))
                  {
                        $msgball = $this->get_arg_value('fldball');
                  }
                  $acctnum = $fldball['acctnum'];
                  if (!(isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  $this->ensure_stream_and_folder($fldball, 'all_headers_in_folder');
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->headers($mailsvr_stream);
            }
            
            /*!
            @function phpgw_get_flag
            @abstract ?
            */
00481             function phpgw_get_flag($flag='')
            {
                  // sanity check
                  if ($flag == '')
                  {
                        return '';
                  }
                  else
                  {
                        $msgball = $this->get_arg_value('msgball');
                        $this->ensure_stream_and_folder($msgball , 'phpgw_get_flag'.' LINE '.__LINE__);
                        return $GLOBALS['phpgw_dcom_'.$this->acctnum]->dcom->get_flag($this->get_arg_value('mailsvr_stream'),$this->get_arg_value('["msgball"]["msgnum"]'),$flag);
                  }
            }
            
            // ====  Functions For Getting A Message Or A Part (MIME Part) Of A Message  ====
            
            /*!
            @function phpgw_body
            @abstract get the entire body for a message.
            @param $msgball (typed array) 
            @author Angles
            @discussion If only a part of the message body is desired, use "phpgw_fetchbody" instead.
            */
00505             function phpgw_body($msgball='')
            {
                  if (!(isset($msgball))
                  || ((string)$msgball == ''))
                  {
                        $msgball = $this->get_arg_value('msgball');
                  }
                  $acctnum = $msgball['acctnum'];
                  if (!(isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  $this->ensure_stream_and_folder($msgball, 'phpgw_body'.' LINE '.__LINE__);
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  // notice of event
                  $this->event_msg_seen($msgball, 'phpgw_body');
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->get_body($mailsvr_stream, $msgball['msgnum']);
            }
            
            /*!
            @function phpgw_fetchbody
            @abstract FETCHBODY get a portion, via MIME part number, of a message body, not the entire body.
            @param $msgball (typed array) 
            @param $flags (defined int) options passed to the mailserver with the php FETCHBODY command. 
            (Not related to a message flag like "unseen", this is an optional argument for the mail server.) 
            @author Angles
            */
00533             function phpgw_fetchbody($msgball='', $flags='')
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).phpgw_fetchbody: ('.__LINE__.') ENTERING<br>'); }
                  //echo 'mail_msg(_wrappers): phpgw_fetchbody: ENTERING, $msgball dump<pre>'; print_r($msgball); echo '</pre>';
                  if ( (!isset($msgball))
                  || ($msgball == '') )
                  {
                        $msgball = $this->get_arg_value('msgball');
                  }
                  $acctnum = $msgball['acctnum'];
                  // why is this next check needed?
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  // TRY CACHED DATA
                  $cached_phpgw_fetchbody = $this->read_session_cache_item('phpgw_fetchbody', $msgball['acctnum'], $msgball['folder'], $msgball['msgnum'], $msgball['part_no']);
                  if ($cached_phpgw_fetchbody)
                  {
                        // notice of event
                        $this->event_msg_seen($msgball, 'phpgw_fetchbody');
                        if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(wrappers).phpgw_fetchbody: ('.__LINE__.') about to return $cached_phpgw_fetchbody DUMP:', $cached_phpgw_fetchbody); }
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).phpgw_fetchbody: ('.__LINE__.') LEAVING cached_phpgw_fetchbody <br>'); }
                        return $cached_phpgw_fetchbody;
                  }
                  // if we get here we need to contact mailserver
                  $this->ensure_stream_and_folder($msgball, 'phpgw_fetchbody'.' LINE '.__LINE__);
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  $msgnum = $msgball['msgnum'];
                  $part_no = $msgball['part_no'];
                  //echo 'mail_msg(_wrappers): phpgw_fetchbody: processed: $acctnum: '.$acctnum.'; $mailsvr_stream: '.serialize($mailsvr_stream).'; $msgnum: '.$msgnum.'; $part_no: '.$part_no.'<br> * $msgball dump<pre>'; print_r($msgball); echo '</pre>';
                  
                  $data = '';
                  $data = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->fetchbody($mailsvr_stream, $msgnum, $part_no, $flags);
                  
                  if ($data)
                  {
                        // notice of event
                        // note even if $part_no == 0 (HEADERS only) this function in php-imap WILL set the SEEN flag
                        // use phpgw_fetchheader if you only want to PEEK the headers without changing SEEN
                        $this->event_msg_seen($msgball, 'phpgw_fetchbody');
                        // SET_CACHE_ITEM
                        $meta_data = array();
                        $meta_data['msgball'] = $msgball;
                        $meta_data['phpgw_fetchbody'] = $data;
                        $this->save_session_cache_item('phpgw_fetchbody', $meta_data, $acctnum);
                        $meta_data = array();
                        if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(wrappers).phpgw_fetchbody: ('.__LINE__.') about to return $data DUMP:', $data); }
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).phpgw_fetchbody: ('.__LINE__.') LEAVING returning $data from server<br>'); }
                        return $data;
                  }
                  else
                  {
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).phpgw_header: ('.__LINE__.') LEAVING on semi-error, no $data available to return<br>'); }
                        return False;
                  }
            }
            
            /*
            // OLD FUNCTION
            function phpgw_fetchbody($msgball='', $flags='')
            {
                  //echo 'mail_msg(_wrappers): phpgw_fetchbody: ENTERING, $msgball dump<pre>'; print_r($msgball); echo '</pre>';
                  if ( (!isset($msgball))
                  || ($msgball == '') )
                  {
                        $msgball = $this->get_arg_value('msgball');
                  }
                  $acctnum = $msgball['acctnum'];
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  $this->ensure_stream_and_folder($msgball, 'phpgw_fetchbody'.' LINE '.__LINE__);
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  $msgnum = $msgball['msgnum'];
                  $part_no = $msgball['part_no'];
                  //echo 'mail_msg(_wrappers): phpgw_fetchbody: processed: $acctnum: '.$acctnum.'; $mailsvr_stream: '.serialize($mailsvr_stream).'; $msgnum: '.$msgnum.'; $part_no: '.$part_no.'<br> * $msgball dump<pre>'; print_r($msgball); echo '</pre>';
                  
                  // notice of event
                  $this->event_msg_seen($msgball, 'phpgw_fetchbody');
                  
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->fetchbody($mailsvr_stream, $msgnum, $part_no, $flags);
            }
            */
            
      // =====  Functions For Getting Information About A Folder  =====
            // returns an array of integers which are refer to all the messages in a folder ("INBOX") sorted and ordered
            // any integer in this array can be used to request that specific message from the server
            /*!
            @function get_msgball_list
            @abstract wrapper for IMAP_SORT, sorts a folder in the desired way, then get a list of all message, as integer message numbers
            @param $acctnum int SPECIAL USE ONLY  you may supply an acctnum to get info about a folder the is not the currently selected acct / folder
            @param $folder string SPECIAL USE ONLY you may supply folder name to get info about a folder the is not the currently selected acct / folder
            @param $only_fill_cache (boolean) SPECIAL USE ONLY when we only want to make sure the cache has data, so we can reference 
            it after we call this function, so we do not actually want data returned here (EXPERIMENTAL, MAY NOT WORK)
            @author Angles
            @access public
            @result returns an flat array of strings where each string item is a "msgball" in the form if URI TYPE DATA. 
            This means each element of the array is a string of type URI data msgball. Thus each string looks something like this 
            "msgball[msgnum]=12&msgball[folder]=INBOX&msgball[acctnum]=0" 
            so it contains acctnum, foldername, message UID,  in the form of "fake URI" a.k.a. a GET URI string of type magball. 
            Typically we just want all the message numbers in the currently active folder, WHICH IS WHAT THIS FUNCTION RETURNS, 
            in which case the msgball strings of the array will differ only in that the message UID integers, which are message 
            numbers referring to messages in the folder, are the only things that differ  because the acctnum and folder are going to be the same. 
            The flat string array returned helps with memory management and speed when manipulating such arrays that are flat, 
            HOWEVER most functions in this wrapper class are set up to use msgball or fldball type data as params (i.e. not just message 
            numbers by themselves) IN THEIR EXPANDED structured array form, i.e. passing a single msgball NOT in URI form 
            is most common in this class, which expanded single msgball array form you can obtain by calling function "ball_data_parse_str" on 
            any URI type msgball string. 
            @discussion Because multiple accounts may be in use, the msgball concept allows any msgball element to refer to a 
            message in any folder in any account. However the most common usage is to get all the message numbers in the current folder 
            sorted according to the get_arg_value(sort, acctnum) AND get_arg_value(order, acctnum) which most often is a 
            single folder in a single account WHICH IS WHAT THIS FUNCTIONS RETURNS. 
            This whole wrapper class is set up to ba able to handle msgball data with each element refering to 
            different accounts and folders, however this powerful capability is almost never used. Mostly we just get all message 
            UID numbers in the current folder, such as this function will return in msgball form. 
            But the structure of the msgball, in the form is a single msgball in its expanded array form, 
            is what is generally used as in parms to most functions in this wrapper class, 
            so even in simple usage we still will use the msgball to do stuff like move mail or request a message. 
            You can call function "ball_data_parse_str" on any URI type msgball string to obtain the array 
            that is of type msgball. It is the same data in full structured array form. 
            In most cases, when we are operating in a single folder, the Folder and Account Number SHOULD be obtained from the 
            class vars which were set during begin_request(), where folder and acctnum were determined from GET POST data. 
            Thus you need not provide any args to this function because the desired folder is known to be correctly named (it exists, 
            not a bogus foldername) and associated with the correct acctnum. However, some of the filter functions do use these params, 
            but using them is discouraged in simple and most common cases. As mentioned above, the return is an array of "msgball" data, 
            which contains acctnum, foldername, message UID, in the form of "fake URI" a.k.a. a GET URI string of type magball. 
            We use this data and specifically these message numbers to request more detailed information about a message (headers, subject), 
            or the request message itself from the server. Note that getting a list of message numbers from a mailserver ALWAYS requires 
            giving the mail server Sort and Order information so the server knows how to order the message numbers it returns. 
            In this function, Sort and Order is applied by this class, so the calling process does not need to specify sorting here
            The data communications object (class mail_dcom) is supplied by the class.
            CACHE NOTE: If $this->session_cache_enabled is True, the data this function gets is cached in the appsession, 
            thus even if $this->session_cache_extreme is False, this  data this data is still cached.  
            */
00672             function get_msgball_list($acctnum='', $folder='', $only_fill_cache=False)
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).get_msgball_list:  ENTERING $acctnum ['.$acctnum.'] ; $folder ['.$folder.'] <br>'); }
                  // IF specifying a folder, as a filter search may do, we need to ensure stream and folder
                  if ((isset($acctnum))
                  && ((string)$acctnum != '')
                  && (isset($folder))
                  && ((string)$folder != ''))
                  {
                        // SPECIAL HANDLING, typical message viewing would not need to specify folder
                        // DO NOT SPECIFY FOLDER unless you *really* know what you are doing
                        // typically "best" folder and acctnum are obtained during begin request
                        // right now only specialized filter searching requires tp specify a folder
                        
                        // UPDATE WE NOW USE A FOLDER ARG IN THE DATA KEY
                        // not sure how that changes any of this, if at all
                        // in this case, not even an acctnum was passed, so obviously a folder is out of the question here
                        
                        $fake_fldball = array();
                        $fake_fldball['acctnum'] = $acctnum;
                        $fake_fldball['folder'] = $folder;
                        // WHY DO THIS HERE?
                        //$this->ensure_stream_and_folder($fake_fldball, 'get_msgball_list'.' LINE '.__LINE__);
                        // ok, so now we KNOW the stream exists and folder value is what we need for this desired account
                  }
                  elseif ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  // as I said above, rare to specify folder, if it wasn;t handled above, forget about it
                  
                  // try to restore "msgball_list" from saved session data store
                  // in appsession this data is saved like this:
                  //    $cached_msgball_data[msgball_list]
                  //    $cached_msgball_data[validity]
                  // BUT IT RETURNS TO US THE ACTUAL "msgball_list" part, *NOT* WITH THE OTHER STUFF TOO
                  //$cached_msgball_list = $this->read_session_cache_item('msgball_list', $acctnum);
                  // NOW WE USE FOLDER NAME AS A DATA KEY TOO
                  if ((!isset($folder))
                  || ((string)$folder == ''))
                  {
                        $folder = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
                  }
                  $cached_msgball_list = $this->read_session_cache_item('msgball_list', $acctnum, $folder);
                  if ($cached_msgball_list)
                  {
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).get_msgball_list: ('.__LINE__.') LEAVING, returning appsession cached "msgball_list"<br>'); }
                        // 
                        //return $cached_msgball_data['msgball_list'];
                        return $cached_msgball_list;
                  }
                  else
                  {
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).get_msgball_list: ('.__LINE__.') <font color="brown">cached had NO DATA for "msgball_list"</font><br>'); }
                        // right now only specialized filter searching requires tp specify a folder
                        $fake_fldball = array();
                        $fake_fldball['acctnum'] = $acctnum;
                        //$fake_fldball['folder'] = $this->get_arg_value('folder');
                        $fake_fldball['folder'] = $this->prep_folder_out($this->get_arg_value('folder'));
                        $this->ensure_stream_and_folder($fake_fldball, 'get_msgball_list'.' LINE '.__LINE__);
                        
                        $server_msgnum_list = array();
                        
                        //if (is_object($GLOBALS['phpgw_dcom_'.$acctnum]))
                        //{
                        //    $server_msgnum_list = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->sort($this->get_arg_value('mailsvr_stream', $acctnum), $this->get_arg_value('sort', $acctnum), $this->get_arg_value('order', $acctnum));
                        //}
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): get_msgball_list: ('.__LINE__.') <font color="red">MAILSERVER CALL</font> calling $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->sort('.$this->get_arg_value('mailsvr_stream', $acctnum).', '.$this->get_arg_value('sort', $acctnum).', '.$this->get_arg_value('order', $acctnum).')<br>'); } 
                        $server_msgnum_list = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->sort($this->get_arg_value('mailsvr_stream', $acctnum), $this->get_arg_value('sort', $acctnum), $this->get_arg_value('order', $acctnum));
                        
                        // put more information about these particular messages into the msgball_list[] structure
                        /*
                        $msgball_list = array();
                        $loops = count($server_msgnum_list);
                        // folder empty (or an error?), msg_nums_list[] count will be 0, so msgball_list[] will be empty as well
                        // because we'll never fill it with anything
                        if ($loops > 0)
                        {
                              // we store folder in URLENCODED form in the msgball and therefor the msgball_list
                              $msg_folder = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
                              for($i=0;$i<$loops;$i++)
                              {
                                    $msgball_list[$i]['msgnum'] = $server_msgnum_list[$i];
                                    $msgball_list[$i]['folder'] = $msg_folder;
                                    $msgball_list[$i]['acctnum'] = $acctnum;
                                    // see php manual page "function.parse-str.html" for explanation of the array'ing of the URI data
                                    // NOTE: this uri NEVER begins with a "&" here
                                    // YOU must add the prefix "&" if it's needed
                                    $msgball_list[$i]['uri'] = 
                                           'msgball[msgnum]='.$msgball_list[$i]['msgnum']
                                          .'&msgball[folder]='.$msgball_list[$i]['folder']
                                          .'&msgball[acctnum]='.$msgball_list[$i]['acctnum'];
                                    // NEW - try making the msgball_list URI DATA ONLY
                                    $msgball_list_uri_only[$i] = $msgball_list[$i]['uri'];
                              }
                        }
                        */
                        $msgball_list = array();
                        $loops = count($server_msgnum_list);
                        // folder empty (or an error?), msg_nums_list[] count will be 0, so msgball_list[] will be empty as well
                        // because we'll never fill it with anything
                        if ($loops > 0)
                        {
                              // we store folder in URLENCODED form in the msgball and therefor the msgball_list
                              $msg_folder = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
                              for($i=0;$i<$loops;$i++)
                              {
                                    // NEW - try making the msgball_list URI DATA ONLY
                                    // see php manual page "function.parse-str.html" for explanation of the array'ing of the URI data
                                    // NOTE: this uri NEVER begins with a "&" here
                                    // YOU must add the prefix "&" if it's needed
                                    $msgball_list[$i] = 
                                           'msgball[msgnum]='.$server_msgnum_list[$i]
                                          .'&msgball[folder]='.$msg_folder
                                          .'&msgball[acctnum]='.$acctnum;
                              }
                        }

                        // save "msgball_list" to session data store
                        // prepare the data for storage, save it with info to aid in "freshness" testing on later reading of the cache
                        // NOTE right now we do not use the $extra_keys param for this msgball_list data caching
                        $meta_data = array();
                        $meta_data['msgball_list'] = $msgball_list;
                        $meta_data['validity'] = array();
                        $meta_data['validity']['fldball'] = $fake_fldball;
                        //$this->save_session_cache_item('msgball_list', $meta_data, $acctnum);
                        // NOW WE USE FOLDER NAME ALSO IN THE DATA KEY
                        // use the folder name that was fed as a param, since this most likely represents a good key to use 
                        // just in case the msgball_list, in the future, is a virtual one composed of msg from many folders
                        //echo 'wrappers line ('.__LINE__.') about to call save_session_cache_item for msgball_list <br>';
                        $this->save_session_cache_item('msgball_list', $meta_data, $acctnum, $folder);
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): get_msgball_list: ('.__LINE__.') LEAVING, <font color="red">had to get data from server</font><br>'); } 
                        return $msgball_list;
                  }
            }
            
            /*!
            @function get_msgball_list_oldschool
            @abstract for backward compat with functions requiring a fully expanded array msgball_list instead of only URI type data. 
            @discussion The same as "get_msgball_list" EXCEPT the uri data is looped on and expanded into full array data, for 
            backwards compat with functions expected such data instead of the new style numbered list of uri string data. 
            HEAVILY DEPRECIATED. 
            @author Angles
            @access public
            */
00818             function get_msgball_list_oldschool($acctnum='', $folder='', $only_fill_cache=False)
            {
                  $msgball_list = $this->get_msgball_list($acctnum, $folder, $only_fill_cache);
                  $loops = count($msgball_list);
                  for($i=0;$i<$loops;$i++)
                  {
                        // so we do not have 2 arrays around at the same time, just use the same one for this
                        $msgball_list[$i] = $this->ball_data_parse_str($msgball_list[$i]);
                  }
                  return $msgball_list;
            }
            
            /*!
            @function get_folder_size
            @abstract uses IMAP_MAILBOXMSGINFO but returns only the size element
            @result integer returns the SIZE element of the php IMAP_MAILBOXMSGINFO data
            @discussion used only if the total size of a folder is desired, which takes time for the server to return
            The other data IMAP_MAILBOXMSGINFO returns (if size is NOT needed) is obtainable
            from "get_folder_status_info" more quickly and wth less load to the IMAP server
            The data communications object (class mail_dcom) and mailsvr_stream are supplied by the class.
            CACHE NOTE - USE THE WRAPPER FUNCTION "report_total_foldersize_conditional" which wraps this 
            function with logic to only actually call this function if various conditional allow us to use this 
            function, since getting size data is time consuming.  STREAM NOTE - this function DOES 
            require changing folders to the onr we want size data on. This may cause problems, since normal 
            status info without size data is obtained without the need to actually switch to the subject folder. 
            For example, using this in a combobox, of in a folder list, looping thru every folder getting this data 
            requires REOPENING aka switching to the desired folder, which can confuse which folder is the 
            true desired folder. Conrast to the other stats data, which can loop thru a folder list but never require 
            actually switching folders, so this keep the true subject folder in the "folder" arg because the folder 
            is never switched away from that. 
            @author Angles
            @access public
            */
00851             function get_folder_size()
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): get_folder_size: ('.__LINE__.') ENTERING<br>'); } 
                  $fldball = array();
                  $fldball['acctnum'] = $this->get_acctnum();
                  $fldball['folder'] = $this->prep_folder_out($this->get_arg_value('folder', $fldball['acctnum']));
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): get_folder_size: ('.__LINE__.') this function does not take a param, we made a fldball: ['.serialize($fldball).']<br>'); } 
                  // make sure a stream is open
                  if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('mail_msg(_wrappers): get_folder_size: ('.__LINE__.') call to $this->ensure_stream_and_folder(), $fldball ['.serialize($fldball).'] <br>'); } 
                  if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('mail_msg(_wrappers): get_folder_size: ('.__LINE__.') NOTE THIS DOES REQUIRE A CHANGE OF FOLDER to get the data, this may cause problems. <br>'); } 
                  $this->ensure_stream_and_folder($fldball, 'get_folder_size'.' LINE '.__LINE__);
                  
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): get_folder_size: ('.__LINE__.') calling $GLOBALS[phpgw_dcom_'.$this->acctnum.']->dcom->mailboxmsginfo('.$this->get_arg_value('mailsvr_stream').', '.$fldball['acctnum'].')<br>'); } 
                  $mailbox_detail = $GLOBALS['phpgw_dcom_'.$fldball['acctnum']]->dcom->mailboxmsginfo($this->get_arg_value('mailsvr_stream'), $fldball['acctnum']); 
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): get_folder_size: ('.__LINE__.') LEAVING, returning ['.(serialize($mailbox_detail)).']<br>'); } 
                  return $mailbox_detail->Size;
            }
            
            // ALIAS for get_folder_status_info() , for backward compatibility
            /*!
            @function new_message_check
            @abstract PUBLIC function to get status info for a folder, with emphesis on reporting to user about new messages
            @param $fldball (array of type fldball) OPTIONAL the folder you want info about. LEGACY CODE exists for older code 
            which does not use array type fldball but instead passes a string only. In not provided, the currently selected folder is inspected. 
            @discussion THIS IS A PUBLIC FUNCTION, use this one instead of "get_folder_status_info" because this function has legacy 
            code for string only param and also has extra checks for the integrity of the param data, such as with external code 
            calling this function and providing incomplete information. These param data checks do not exist in "get_folder_status_info". 
            BUT THE DATA RETURNED IS THE SAME. After these extra param checks the function calls "get_folder_status_info" to do the 
            reast of the job. So see the discussion there about what gets returned. 
            THIS IS FOR BACKWARD COMPAT ONLY, has a lot of checks verifying the input param before passing handling 
            off to the real function "get_folder_status_info", if you pass a real fldball you can just use that function. If you 
            simply want status info on the inbox of the current account, then use this function with no params, it will do the 
            rest for you.
            @author Angles
            @access Public
            */
00887             function new_message_check($fldball='')
            {
                  // detect OLD param which was string only
                  if ((isset($fldball))
                  && (is_string($fldball))
                  && ($fldball != ''))
                  {
                        $legacy_param = $fldball;
                        $fldball = array();
                        $fldball['folder'] = $legacy_param;
                        // legacy param ALWAYS was applicable to acct 0 only, multi-accounts did not exist then
                        $fldball['acctnum'] = 0;
                  }
                  elseif ( (!isset($fldball))
                  || (!$fldball))
                  {
                        // we have NO instructions on a folder nor acctnum, so make a INBOX acct 0 fldball legacy support
                        $fldball = array();
                        $fldball['acctnum'] = 0;
                        $fldball['folder'] = 'INBOX';
                  }
                  
                  // now we know we have a fldball structure to work with, either we made or provoded as a param
                  // see if it needs anything else we did not handle yet
                  if ((!isset($fldball['acctnum']))
                  || ((string)$fldball['acctnum'] == ''))
                  {
                        $fldball['acctnum'] = $this->get_acctnum();
                        
                  }
                  
                  // we need a folder value, this code only does allow 
                  // NOTE INTERNAL CODE WILL PASS ALREADY URLENCODED FOLDER NAME
                  // This legacy code and public code EXPECTS THE PARAM WILL BE UN-ENCODED
                  // DAMN that is confusing, hummmmmmm...
                  if ((isset($fldball['folder']))
                  && (is_string($fldball['folder']))
                  && ($fldball['folder'] != ''))
                  {
                        $fldball['folder'] = $this->prep_folder_out($fldball['folder']);
                  }
                  else
                  {
                        $fldball['folder'] = $this->prep_folder_out('INBOX');
                  }
                  return $this->get_folder_status_info($fldball);
            }
            
            /*!
            @function get_folder_status_info
            @abstract wrapper for IMAP_STATUS, get status info for the current folder, with emphesis on reporting to user about new messages
            @param $fldball  typed array  OPTIONAL  as with many functions in this class, the folder you are interested in is usually the currently 
            "selected" folder, in IMAP terms, which is selected during begin_request(), in which case it is not necessary to supply this information 
            again in this param, instead this function will use the class vars about foldername and acctnum established during begin_request(). However, 
            since there are multiple accounts, and since IMAP accounts themselves can contain many folders, it is understood that you may want 
            information about a folder other than the currently selected folder, or about an  account that you may want to move messges to.  In these 
            cases you may supply this param of type fldball, like this: parmarray[acctnum] = 1,  parmarray[folder]  = "INBOX", for example. The fldball 
            array item is pretty flexible in that only the bare minumum of data is expected to be in it, as opposed to msgball which is supposed to 
            contain quite detailed information.
            @param $force_refresh boolean  DEPRECIATED - PHASED OUT - To speed email functionality, much data collected 
            from the IMAP server is cached in some capacity, in fact the RFC on IMAP strongly encourages this. This function 
            is used by many other functions and may be called sveral times during any single operation, so the return array data 
            is cached in memory and will be returned if it is available. This is desirable in many occasions, but if for some reason 
            you need to be sure the returned information is not from this cache, set this param to TRUE. 
            =UPDATE= now this data is cached in the appsession cache IF $this->session_cache_extreme is True, and 
            assumed to be fresh for X period of time, as defined in $this->timestamp_age_limit. This param is 
            NO LONGER USED buy *MAY* be reimplemented later. 
            @result returns an associative array  with 5 named elements see the example
            @example this is the return structure
            result['is_imap'] boolean - pop3 server do not know what is "new" or not, IMAP servers do
            result['folder_checked'] string - the folder checked, as processed by the msg class, which may have done a lookup on the folder name
            result['alert_string'] string - lang'd string to show the user about status of new messages in this folder
            result['number_new'] integer - for IMAP: the number "recent" and/or "unseen"messages; for POP3: the total number of messages
            result['number_all'] integer - for IMAP and POP3: the total number messages in the folder
            @discussion gives user friendly "alert_string" element to show the user, info is for what ever folder the msg
            class is currently logged into, you may want to apply PHP function "number_format()" to
            the integers after you have done any math code and befor eyou display them to the user, it adds the thousands comma. 
            CACHE NOTE: If $this->session_cache_extreme is True, the data this function gets is cached in the appsession 
            cache and is assumed to be "fresh" for X period of time, as defined in $this->timestamp_age_limit 
            (currently hardcoded at 4 minutes). Any changes to cached elements number_new and number_all (part of this functions 
            data array) during that time are manually changed by the "extreme" caching code, we do not re-fetch this data from 
            the mailserver for changes tht we can make ourselves. 
            If $this->session_cache_extreme is False, this data is NOT put in the appsession cache, instead it is stored in a class 
            variable (L1 cache) that lasts only as long as the page view. 
            MORE CACHE NOTE: The "msgball_list" cached in the appsession cache is verified for "freshness" by comparing 
            against the "number_all" element in this functions data array. If the "number_all" of the cached "msgball_list" is 
            different from the "nunber_all" from this function,  the "msgball_list" is deemed "stale" and we request a new 
            msgball list from the server, which means calling the php SORT command and adding some data to that to make 
            the "msgball_list". If $this->session_cache_extreme is True, the "extreme" caching code manually updates the 
            "number_all" cached data for this function for X minutes, as defined in $this->timestamp_age_limit, and also manually 
            updates that "number_all" that is stored with the "msgball_list" data, so that, during that X period of time, 
            as defined in $this->timestamp_age_limit, the "msgball_list" is deemed "fresh" 
            because its "number_all" element matches the "number_all" element from this functions data array. 
            If $this->session_cache_extreme is False, the same "number_all" test is done, but the data from this 
            function is ALWAYS the latest data obtained from the server because if $this->session_cache_extreme is False, 
            this function ALWAYS gets fresh folder stats data at the start of every pageview, so the "msgball_list" will be deemed 
            "stale" as soon as a change occurs on the mailserver, such as when new mail arrives or when messages are moved or 
            deleted, in which case the "msgball_list" is expired and re-fetched as described above.
            @author Angles
            @access public
            */
00988             function get_folder_status_info($fldball='', $force_refresh=False)
            {
                  if (($this->debug_session_caching > 0) || ($this->debug_wrapper_dcom_calls > 0)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') ENTERING, $fldball: '.serialize($fldball).' ; $force_refresh (DEPRECIATED): '.serialize($force_refresh).' <br>'); }
                  
                  // note: param $fldball WAS just folder previously, watch out for old code still passing that string instead of the fldball
                  if ( (!isset($fldball))
                  || (!$fldball))
                  {
                        // we have NO instructions on a folder nor acctnum, so make a blank fldball
                        $fldball = array();
                        $fldball['acctnum'] = '';
                        $fldball['folder'] = '';
                  }
                  // now we know we have a fldball structure to work with, analyse it
                  if ((!isset($fldball['acctnum']))
                  || ((string)$fldball['acctnum'] == ''))
                  {
                        $fldball['acctnum'] = $this->get_acctnum();
                  }
                  if ((!isset($fldball['folder']))
                  || ((string)$fldball['folder'] == ''))
                  {
                        //$fldball['folder'] = $this->get_arg_value('folder', $fldball['acctnum']);
                        // we keep this folder name in its urlencoded form until the last second, 
                        // partly so we can use it as an index member in the cache, also because it is a consistant prectice to do that 
                        $fldball['folder'] = $this->prep_folder_out($this->get_arg_value('folder', $fldball['acctnum']));
                  }
                  
                  if (($this->debug_session_caching > 0) || ($this->debug_wrapper_dcom_calls > 0)) { $this->dbug->out('class_msg: get_folder_status_info('.__LINE__.'): ONLY L1 CACHE OF THIS INFO IF IN NON-EXTREME MODE<br>'); } 
                  
                  if ($this->session_cache_extreme == False)
                  {
                        if ($this->debug_session_caching > 1) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (non-extreme mode) uses L1/class var cache, NO appsession cache used in non-extreme <br>'); }
                        // do we have cached data in L1 cache / class object var, that we can use
                        // ONLY L1 CACHE OF THIS INFO IF IN NON-EXTREME MODE
                        //$folder_status_info = $this->get_arg_value('folder_status_info', $fldball['acctnum']);
                        $folder_status_info = $this->get_arg_value('["folder_status_info"]["'.$fldball['folder'].'"]', $fldball['acctnum']); 
                        //$folder_status_info = $this->get_arg_value('folder_status_info', $fldball['acctnum'], $fldball['folder']);
                        //$folder_status_info = $this->get_arg_value_ex('folder_status_info', $fldball['acctnum'], $fldball['folder']);
                        if ((!$force_refresh)
                        && ($folder_status_info)
                        && (count($folder_status_info) > 0)
                        && ($folder_status_info['folder_checked'] == $this->prep_folder_in($fldball['folder'])))
                        {
                              // this data is cached, L1 cache, temp cache, so it should still be "fresh"
                              // add this "timestamp" array element only to imitate what exists in data structure if extreme-mode were on (which it is not here). 
                              $timestamp_age = (time() - $folder_status_info['timestamp']);
                              if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (non-extreme mode) got L1/class var cached data, $timestamp_age ['.$timestamp_age.'] ; $folder_status_info dump:', $folder_status_info); } 
                              if (($this->debug_session_caching > 0) || ($this->debug_wrapper_dcom_calls > 0)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (non-extreme mode) LEAVING '.'<font color="purple">'.'successfully returning cached L1/class data'.'</font>'.'<br>'); }
                              return $folder_status_info;
                        }
                        else
                        {
                              if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (non-extreme mode) NO data found in L1/class var cached <br>'); } 
                        }
                  }
                  else
                  {
                        if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (extreme mode) uses appsession cache, no L1/class var cached is used in extreme mode, param $fldball DUMP', $fldball); } 
                        // ONLY USE APPSESSION CACHE IF IN EXTREME MODE
                        // non-extreme mose, which was handled above, does not use appsession cache for stats data, only "L1 cache" temp class var
                        // below is for extreme-mode, which only uses appsession cache for this data, does NOT use "L1 cache"
                        
                        // try to restore from saved session data store
                        if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (extreme mode) calling $this->read_session_cache_item(folder_status_info, '.serialize($fldball['acctnum']).', '.($fldball['folder']).') NOTE the folder name MUST be urlencoded as that param<br>'); } 
                        $cached_folder_status_info = $this->read_session_cache_item('folder_status_info', $fldball['acctnum'], $fldball['folder']);
                        if (($this->debug_session_caching > 2) || ($this->debug_wrapper_dcom_calls > 2)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (extreme mode) $cached_folder_status_info dump:', $cached_folder_status_info); } 
                        if ($cached_folder_status_info)
                        {
                              if (($this->debug_session_caching > 0) || ($this->debug_wrapper_dcom_calls > 0)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (extreme mode) LEAVING '.'<font color="purple">'.'returning data obtained from cache'.'</font>'.'<br>'); } 
                              return $cached_folder_status_info;
                        }
                        if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (extreme mode) NO data found in cache (or it was stale) <br>'); }
                  }
                  
                  // Make Sure Stream Exists
                  // multiple accounts means one stream may be open but another may not
                  // "ensure_stream_and_folder" will verify for us, 
                  /*!
                  @capability ensure_stream_and_folder WITHOUT changing folders
                  @discussion folder STATUS data does not require opening the folder we want information about, 
                  THEREFOR we use a "$special_fldball" which has a$special_fldball["no_switch_away"] element of 
                  which is understood by function "ensure_stream_and_folder" to NOT CHANGE 
                  the selected folder, to make sure only that the stream exists and we are logged in to the mailserver, 
                  but the actual selected folder IN THIS SPECIAL CASE is not necessary to set. If we were to change the 
                  currently selected folder just to get stats, this would add additional complication to the concept of which folder 
                  we actually wanted to be selected, because "ensure_stream_and_folder" would set the "arg_value" for "folder" 
                  IF IT DID change the selected folder, which is NOT wanted nor needed in this case. 
                  @author Angles
                  */
                  $special_fldball = array();
                  $special_fldball['acctnum'] = $fldball['acctnum'];
                  $special_fldball['folder'] = $fldball['folder'];
                  // STATUS does not require opening the folder we want information about
                  $special_fldball['no_switch_away'] = True;
                  if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') call to $this->ensure_stream_and_folder(), $special_fldball ['.serialize($special_fldball).'] <br>'); } 
                  if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') DO NOT pass a folderame IN THIS PARTICULAR case because getting folder status DOES NOT require opening that folder, "ensure_stream_and_folder" understands this.<br>'); } 
                  $this->ensure_stream_and_folder($special_fldball, 'get_folder_status_info'.' LINE '.__LINE__);
                  
                  //$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $fldball['acctnum']);
                  $server_str = $this->get_arg_value('mailsvr_callstr', $fldball['acctnum']);
                  if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') will use $mailsvr_stream ['.serialize($mailsvr_stream).'] ; $server_str ['.$server_str.'] ; $fldball: '.serialize($fldball).' <br>'); }
                  
                  $clean_folder_name = $this->prep_folder_in($fldball['folder']);
                  $urlencoded_folder = $this->prep_folder_out($clean_folder_name);
                  if (($this->debug_session_caching > 0) || ($this->debug_wrapper_dcom_calls > 0)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (extreme mode) <b>problem area: urlencoding only 1 time</b> $clean_folder_name ['.$clean_folder_name.'], $urlencoded_folder : ['.$urlencoded_folder.']);<br>'); }
                  
                  // initialize return structure
                  $return_data = Array();
                  $return_data['is_imap'] = False;
                  $return_data['fldball'] = $fldball;
                  $return_data['folder_checked'] = $clean_folder_name;
                  $return_data['folder'] = $clean_folder_name;
                  $return_data['alert_string'] = '';
                  $return_data['number_new'] = 0;
                  $return_data['number_all'] = 0;
                  // these are used to verify cached msg_list_array data, i.e. is it still any good, or is it stale
                  $return_data['uidnext'] = 0;
                  $return_data['uidvalidity'] = 0;
                  $return_data['timestamp'] = time();
                  // FIXME: make this a "ensure_stream_and_folder" call, to make a login if needed
                  //if (is_object($GLOBALS['phpgw_dcom_'.$fldball['acctnum']]))
                  //{
                  //    $mailbox_status = $GLOBALS['phpgw_dcom_'.$fldball['acctnum']]->dcom->status($mailsvr_stream,$server_str.$fldball['folder'],SA_ALL);
                  //}
                  // earlier we called $this->ensure_stream_and_folder, so stream *should* exist
                  if (($this->debug_session_caching > 0) || ($this->debug_wrapper_dcom_calls > 0)) { $this->dbug->out('mail_msg(_wrappers): get_folder_status_info: ('.__LINE__.') calling $GLOBALS[phpgw_dcom_'.$fldball['acctnum'].']->dcom->status('.$mailsvr_stream.','.$server_str.$clean_folder_name.',SA_ALL)<br>'); } 
                  $mailbox_status = $GLOBALS['phpgw_dcom_'.$fldball['acctnum']]->dcom->status($mailsvr_stream,$server_str.$clean_folder_name,SA_ALL);
                  if (($this->debug_session_caching > 2) || ($this->debug_wrapper_dcom_calls > 2)) { $this->dbug->out('mail_msg(_wrappers): get_folder_status_info: ('.__LINE__.') ->dcom->status returns: $mailbox_status DUMP', $mailbox_status); } 
                  
                  // cache validity data - will be used to cache msg_list_array data, which is good until UID_NEXT changes
                  $return_data['uidnext'] = $mailbox_status->uidnext;
                  $return_data['uidvalidity'] = $mailbox_status->uidvalidity;
                  
                  $mail_server_type = $this->get_pref_value('mail_server_type', $fldball['acctnum']);
                  if (($mail_server_type == 'imap')
                  || ($mail_server_type == 'imaps'))
                  {
                        $return_data['is_imap'] = True;
                        $return_data['number_new'] = $mailbox_status->unseen;
                        $return_data['number_all'] = $mailbox_status->messages;
                        if ($mailbox_status->unseen == 1) 
                        {
                              $return_data['alert_string'] .= lang('You have 1 new message!');
                        }
                        if ($mailbox_status->unseen > 1) 
                        {
                              $return_data['alert_string'] .= lang('You have %1 new messages!',$mailbox_status->unseen);
                        }
                        if ($mailbox_status->unseen == 0) 
                        {
                              $return_data['alert_string'] .= lang('You have no new messages');
                        }
                  }
                  else
                  {
                        $return_data['is_imap'] = False;
                        // pop3 does not know what is "new" or not
                        $return_data['number_new'] = $mailbox_status->messages;
                        $return_data['number_all'] = $mailbox_status->messages;
                        if ($mailbox_status->messages > 0) 
                        {
                              $return_data['alert_string'] .= lang('You have messages!');
                        }
                        elseif ($mailbox_status->messages == 0)
                        {
                              $return_data['alert_string'] .= lang('You have no new messages');
                        }
                        else
                        {
                              $return_data['alert_string'] .= lang('error');
                        }
                  }
                  
                  if ($this->session_cache_extreme == False)
                  {
                        if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (non-extreme mode) uses L1/class var cache, NO appsession cache used in non-extreme <br>'); }
                        // cache data in a class var (L1 Cache)
                        // USE L1 CACHE ONLY IN NON-EXTREME MODE
                        if (($this->debug_session_caching > 2) || ($this->debug_wrapper_dcom_calls > 2)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (non-extreme mode) saving to L1 class var cache, $this->set_arg_value(folder_status_info, $return_data, '.$fldball['acctnum'].') ; $return_data dump:', $return_data); }
                        //$this->set_arg_value('folder_status_info', $return_data, $fldball['acctnum']);
                        $this->set_arg_value('["folder_status_info"]["'.$fldball['folder'].'"]', $return_data, $fldball['acctnum']);
                  }
                  else
                  {
                        if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (extreme mode) uses appsession cache, no L1/class var cached is used in extreme mode <br>'); }
                        if (($this->debug_session_caching > 1) || ($this->debug_wrapper_dcom_calls > 1)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (extreme mode) saving to session cache, $this->save_session_cache_item("folder_status_info", $return_data, $acctnum) NOTE that acctnum used here is ['.$acctnum.']<br>'); }
                        if (($this->debug_session_caching > 2) || ($this->debug_wrapper_dcom_calls > 2)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') (extreme mode) $return_data DUMP', $return_data); } 
                        // NOTE that the $extra_keys param is generated FOR US in the function that saves this to appsession cache, we are doing that now
                        $this->save_session_cache_item('folder_status_info', $return_data, $fldball['acctnum'], $fldball['folder']);
                  }
                  if (($this->debug_session_caching > 0) || ($this->debug_wrapper_dcom_calls > 0)) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') LEAVING, '.'<font color="red">'.'had contact mailserver to get data'.'</font>'.'<br>'); }
                  return $return_data;
            }
            
            // FIXME: change arg to fldball
            /*!
            @function phpgw_status
            @abstract ?
            @author Angles
            @discussion Debug with flag "debug_wrapper_dcom_calls" . FIXME change param to fldball. 
            @access public
            */
01192             function phpgw_status($feed_folder_long='')
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers): phpgw_status ('.__LINE__.'): ENTERING, $feed_folder_long ['.($feed_folder_long).']<br>'); }
                  $fake_fldball = array();
                  $fake_fldball['acctnum'] = $this->get_acctnum();
                  $fake_fldball['folder'] = $feed_folder_long;
                  $this->ensure_stream_and_folder($fake_fldball, 'phpgw_status'.' LINE '.__LINE__);
                  $server_str = $this->get_arg_value('mailsvr_callstr');
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream');
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers): phpgw_status ('.__LINE__.'): calling $GLOBALS[phpgw_dcom_$fake_fldball[acctnum]('.$fake_fldball['acctnum'].')]->dcom->status($mailsvr_stream['.$mailsvr_stream.'],"$server_str"."$feed_folder_long"['.htmlspecialchars("$server_str"."$feed_folder_long").'],SA_ALL)<br>'); } 
                  $retval = $GLOBALS['phpgw_dcom_'.$fake_fldball['acctnum']]->dcom->status($mailsvr_stream,"$server_str"."$feed_folder_long",SA_ALL);
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers): phpgw_status ('.__LINE__.'): LEAVING, returning $retval ['.serialize($retval).'] <br>'); }
                  return $retval;
            }

            /*!
            @function phpgw_server_last_error
            @abstract ?
            @author Angles
            @discussion Debug with flag "debug_wrapper_dcom_calls" 
            @access public
            */
01214             function phpgw_server_last_error($acctnum='')
            {
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_server_last_error: ('.__LINE__.') calling $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->server_last_error()<br>'); } 
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->server_last_error();
            }
            
            /*!
            @function phpgw_ping
            @abstract ?
            @author Angles
            @discussion Debug with flag "debug_wrapper_dcom_calls" 
            @access public
            */
01232             function phpgw_ping($acctnum='')
            {
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_ping ('.__LINE__.'): ENTERING, $acctnum ['.$acctnum.'], we DO NOT use "ensure_stream_and_folder" here because that would open the stream we are testing, making this test useless.<br>'); } 
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_ping ('.__LINE__.'): calling $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->noop_ping_test('.$mailsvr_stream.') <br>'); } 
                  $retval = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->noop_ping_test($mailsvr_stream);
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_ping ('.__LINE__.'): LEAVING, returing $retval ['.serialize($retval).']<br>'); } 
                  return $retval;
            }
            
            /*!
            @function phpgw_search
            @abstract ?
            @author Angles
            @discussion Debug with flag "debug_wrapper_dcom_calls" 
            @access public
            */
01254             function phpgw_search($fldball='', $criteria='', $flags='')
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_search ('.__LINE__.'): ENTERING, $fldball ['.serialize($fldball).']; $criteria ['.$criteria.']; $flags['.serialize($flags).'] <br>'); } 
                  $acctnum = (int)$fldball['acctnum'];
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  $folder = $fldball['folder'];
                  // if folder is blank, we *should* assume INBOX because filters always search the INBOX
                  if ((!isset($folder))
                  || ((string)$folder == ''))
                  {
                        $folder = 'INBOX';
                  }
                  // Make Sure Stream Exists
                  // multiple accounts means one stream may be open but another may not
                  // "ensure_stream_and_folder" will verify for us, 
                  $fake_fldball = array();
                  $fake_fldball['acctnum'] = $acctnum;
                  $fake_fldball['folder'] = $folder;
                  $this->ensure_stream_and_folder($fake_fldball, 'phpgw_search LINE '.__LINE__);
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  
                  // now we have the stream and the desired folder open
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_search ('.__LINE__.'): calling $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->i_search($mailsvr_stream['.$mailsvr_stream.'], $criteria['.$criteria.'],$flags['.serialize($flags).']) <br>'); } 
                  $retval = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->i_search($mailsvr_stream,$criteria,$flags);
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_search ('.__LINE__.'): LEAVING, returing $retval ['.serialize($retval).']<br>'); } 
                  return $retval;
            }
            
            /*!
            @function phpgw_createmailbox
            @abstract ?
            @param $target_fldball (array or type "fldball") NOTE: folder element SHOULD HAVE SERVER CALLSTR.
            @author Angles
            @access public
            */
01293             function phpgw_createmailbox($target_fldball)
            {
                  $acctnum = (int)$target_fldball['acctnum'];
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  $folder = $target_fldball['folder'];
                  // if folder is blank, we *should* assume INBOX because BUT mailsvr will give an error INBOX already exists
                  if ((!isset($folder))
                  || ((string)$folder == ''))
                  {
                        $folder = 'INBOX';
                  }
                  // Make Sure Stream Exists
                  // multiple accounts means one stream may be open but another may not
                  // "ensure_stream_and_folder" will verify for us, 
                  $fake_fldball = array();
                  $fake_fldball['acctnum'] = $acctnum;
                  $fake_fldball['folder'] = $folder;
                  // tell "ensure_stream_and_folder" that its NOT NECESSARY to switch TO this folder
                  $fake_fldball['no_switch_away'] = True;
                  $this->ensure_stream_and_folder($fake_fldball, 'phpgw_createmailbox LINE ('.__LINE__.')');
                  // if $folder dies not have the {SERVERNAME}  then add it
                  if (!strstr($folder, '}'))
                  {
                        $mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
                        $folder = $mailsvr_callstr.$folder;
                  }
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->createmailbox($mailsvr_stream, $folder);
            }
            
            /*!
            @function phpgw_createmailbox_ex
            @abstract ?
            @param $target_fldball (array or type "fldball") NOTE: folder element DOES NOT NEED THE SERVER CALLSTR.
            @discussion This function handles the traditional fldball better, a fldball["folder"] is NOT ever supposed to have 
            a mailsvr_callstr, and IS ALWAYS urlencoded until just before we call the dcom class, when we urldecode it only 
            at that point. This function assumes this behavior, so the typical fldball is supported by this function. 
            This will be the future version of this function, code is migrating to this version.
            @author Angles
            @access public
            */
01338             function phpgw_createmailbox_ex($target_fldball)
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('phpgw_createmailbox_ex('.__LINE__.'): ENTERING: raw $target_fldball arg ['.htmlspecialchars($target_fldball).']<br>'); } 
                  if ((isset($target_fldball['acctnum']))
                  && ((string)$target_fldball['acctnum'] != ''))
                  {
                        $target_fldball['acctnum'] = (int)$target_fldball['acctnum'];
                  }
                  else
                  {
                        $target_fldball['acctnum'] = $this->get_acctnum();
                  }
                  $acctnum = $target_fldball['acctnum'];
                  // if folder is blank, we *should* assume INBOX because BUT mailsvr will give an error INBOX already exists
                  if ((isset($target_fldball['folder']))
                  || ((string)$target_fldball['folder'] != ''))
                  {
                        // good folder name, no change needed
                  }
                  else
                  {
                        $target_fldball['folder'] = 'INBOX';
                  }
                  // Make Sure Stream Exists
                  // multiple accounts means one stream may be open but another may not
                  // "ensure_stream_and_folder" will verify for us, 
                  // tell "ensure_stream_and_folder" that its NOT NECESSARY to switch TO this folder
                  $target_fldball['no_switch_away'] = True;
                  $this->ensure_stream_and_folder($target_fldball, 'phpgw_createmailbox_ex LINE ('.__LINE__.')');
                  // if $folder dies not have the {SERVERNAME}  then add it
                  //$target_folder_clean = $this->prep_folder_in($target_fldball['folder']);
                  $target_folder_clean = urldecode($target_fldball['folder']);
                  if (!strstr($target_folder_clean, '}'))
                  {
                        $mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
                        $target_folder_clean = $mailsvr_callstr.$target_folder_clean;
                  }
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('phpgw_createmailbox_ex('.__LINE__.'): calling $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->createmailbox('.serialize($mailsvr_stream).', '.htmlspecialchars($target_folder_clean).'<br>'); } 
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->createmailbox($mailsvr_stream, $target_folder_clean);
            }
            
            /*!
            @function phpgw_deletemailbox
            @abstract ?
            @author Angles
            @access public
            */
01386             function phpgw_deletemailbox($target_fldball)
            {
                  $this->ensure_stream_and_folder($target_fldball, 'phpgw_deletemailbox'.' LINE '.__LINE__);
                  $acctnum = $target_fldball['acctnum'];
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  $folder = $target_fldball['folder'];
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->deletemailbox($mailsvr_stream, $folder);
            }
            
            /*!
            @function phpgw_deletemailbox_ex
            @abstract ?
            @param $target_fldball (array or type "fldball") NOTE: folder element DOES NOT NEED THE SERVER CALLSTR.
            @discussion This function handles the traditional fldball better, a fldball["folder"] is NOT ever supposed to have 
            a mailsvr_callstr, and IS ALWAYS urlencoded until just before we call the dcom class, when we urldecode it only 
            at that point. This function assumes this behavior, so the typical fldball is supported by this function. 
            This will be the future version of this function, code is migrating to this version.
            @author Angles
            @access public
            */
01406             function phpgw_deletemailbox_ex($target_fldball)
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('phpgw_deletemailbox_ex('.__LINE__.'): ENTERING: raw $target_fldball arg ['.htmlspecialchars($target_fldball).']<br>'); } 
                  if ((isset($target_fldball['acctnum']))
                  && ((string)$target_fldball['acctnum'] != ''))
                  {
                        $target_fldball['acctnum'] = (int)$target_fldball['acctnum'];
                  }
                  else
                  {
                        $target_fldball['acctnum'] = $this->get_acctnum();
                  }
                  $acctnum = $target_fldball['acctnum'];
                  // if folder is blank, we *should* assume INBOX because BUT mailsvr will give an error INBOX already exists
                  if ((isset($target_fldball['folder']))
                  || ((string)$target_fldball['folder'] != ''))
                  {
                        // good folder name, no change needed
                  }
                  else
                  {
                        $target_fldball['folder'] = 'INBOX';
                  }
                  // Make Sure Stream Exists
                  // multiple accounts means one stream may be open but another may not
                  // "ensure_stream_and_folder" will verify for us, 
                  // tell "ensure_stream_and_folder" that its NOT NECESSARY to switch TO this folder
                  $target_fldball['no_switch_away'] = True;
                  $this->ensure_stream_and_folder($target_fldball, 'phpgw_deletemailbox_ex LINE ('.__LINE__.')');
                  // if $folder dies not have the {SERVERNAME}  then add it
                  //$target_folder_clean = $this->prep_folder_in($target_fldball['folder']);
                  $target_folder_clean = urldecode($target_fldball['folder']);
                  if (!strstr($target_folder_clean, '}'))
                  {
                        $mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
                        $target_folder_clean = $mailsvr_callstr.$target_folder_clean;
                  }
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('phpgw_deletemailbox_ex('.__LINE__.'): calling $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->deletemailbox('.serialize($mailsvr_stream).', '.htmlspecialchars($target_folder_clean).'<br>'); } 
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->deletemailbox($mailsvr_stream, $target_folder_clean);
            }
            
            /*!
            @function phpgw_renamemailbox
            @abstract ?
            @author Angles
            @access public
            */
01454             function phpgw_renamemailbox($source_fldball,$target_fldball)
            {
                  $this->ensure_stream_and_folder($source_fldball, 'phpgw_renamemailbox'.' LINE '.__LINE__);
                  $acctnum = (int)$source_fldball['acctnum'];
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  $folder_old = $source_fldball['folder'];
                  $folder_new = $target_fldball['folder'];
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->renamemailbox($mailsvr_stream, $folder_old, $folder_new);
            }

            /*!
            @function phpgw_renamemailbox_ex
            @abstract ?
            @param $target_fldball (array or type "fldball") NOTE: folder element DOES NOT NEED THE SERVER CALLSTR.
            @discussion This function handles the traditional fldball better, a fldball["folder"] is NOT ever supposed to have 
            a mailsvr_callstr, and IS ALWAYS urlencoded until just before we call the dcom class, when we urldecode it only 
            at that point. This function assumes this behavior, so the typical fldball is supported by this function. 
            This will be the future version of this function, code is migrating to this version.
            @author Angles
            @access public
            */
01475             function phpgw_renamemailbox_ex($source_fldball,$target_fldball)
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('phpgw_renamemailbox_ex('.__LINE__.'): ENTERING<br>'); } 
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('phpgw_renamemailbox_ex('.__LINE__.'): raw $source_fldball arg ['.htmlspecialchars($source_fldball).']; raw $target_fldball arg ['.htmlspecialchars($target_fldball).']<br>'); } 
                  if ((isset($target_fldball['acctnum']))
                  && ((string)$target_fldball['acctnum'] != ''))
                  {
                        $target_fldball['acctnum'] = (int)$target_fldball['acctnum'];
                  }
                  else
                  {
                        $target_fldball['acctnum'] = $this->get_acctnum();
                  }
                  $acctnum = $target_fldball['acctnum'];
                  // if folder is blank, we *should* assume INBOX because BUT mailsvr will give an error INBOX already exists
                  if ((isset($target_fldball['folder']))
                  || ((string)$target_fldball['folder'] != ''))
                  {
                        // good folder name, no change needed
                  }
                  else
                  {
                        $target_fldball['folder'] = 'INBOX';
                  }
                  // Make Sure Stream Exists
                  // multiple accounts means one stream may be open but another may not
                  // "ensure_stream_and_folder" will verify for us, 
                  // tell "ensure_stream_and_folder" that its NOT NECESSARY to switch TO this folder
                  $target_fldball['no_switch_away'] = True;
                  $this->ensure_stream_and_folder($target_fldball, 'phpgw_renamemailbox_ex LINE ('.__LINE__.')');
                  // if $folder dies not have the {SERVERNAME}  then add it
                  //$target_folder_clean = $this->prep_folder_in($target_fldball['folder']);
                  $target_folder_clean = urldecode($target_fldball['folder']);
                  if (!strstr($target_folder_clean, '}'))
                  {
                        $mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
                        $target_folder_clean = $mailsvr_callstr.$target_folder_clean;
                  }
                  
                  // SOURCE FOLDER NAME
                  //the folder we are renaming MUST BE from the same account
                  // so we only need to clean up its name
                  if ((isset($source_fldball['folder']))
                  || ((string)$source_fldball['folder'] != ''))
                  {
                        // good folder name, no change needed
                  }
                  else
                  {
                        $source_fldball['folder'] = 'INBOX';
                  }
                  $source_folder_clean = urldecode($source_fldball['folder']);
                  // if $folder dies not have the {SERVERNAME}  then add it
                  if (!strstr($source_folder_clean, '}'))
                  {
                        $mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
                        $source_folder_clean = $mailsvr_callstr.$source_folder_clean;
                  }
                  
                  // OK WE are ready to do it!
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('phpgw_renamemailbox_ex('.__LINE__.'): calling $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->renamemailbox('.serialize($mailsvr_stream).', '.htmlspecialchars($source_folder_clean).', '.htmlspecialchars($target_folder_clean).'<br>'); } 
                  //return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->createmailbox($mailsvr_stream, $target_folder_clean);
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->renamemailbox($mailsvr_stream, $source_folder_clean, $target_folder_clean);
            }
            
            /*!
            @function phpgw_listmailbox
            @abstract ?
            @author Angles
            @discussion Debug with flag "debug_wrapper_dcom_calls" 
            @access public
            */
01548             function phpgw_listmailbox($ref,$pattern,$acctnum)
            {
                  if (!(isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  // Make Sure Stream Exists
                  // multiple accounts means one stream may be open but another may not
                  // "ensure_stream_and_folder" will verify for us, 
                  // folder logged into does not matter for listmailbox, so pass $fake_fldball['no_switch_away'] = True
                  $fake_fldball = array();
                  $fake_fldball['acctnum'] = $acctnum;
                  $fake_fldball['folder'] = '';
                  $fake_fldball['no_switch_away'] = True;
                  $this->ensure_stream_and_folder($fake_fldball, 'phpgw_listmailbox');
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  
                  // ... so stream exists, do the transaction ...
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_listmailbox ('.__LINE__.'): calling $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->listmailbox($mailsvr_stream['.$mailsvr_stream.'],$ref['.$ref.'], $pattern['.$pattern.']); <br>'); } 
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->listmailbox($mailsvr_stream,$ref,$pattern);
            }
            
            /*!
            @function phpgw_append
            @abstract ?
            @author Angles
            @discussion Debug with flag "debug_wrapper_dcom_calls" 
            @access public
            */
01578             function phpgw_append($folder="Sent", $message, $flags=0)
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: ('.__LINE__.') ENTERING, folder: '.$folder.'<br>'); }
                  
                  $server_str = $this->get_arg_value('mailsvr_callstr');
                  
                  // ---  does the target folder actually exist ?  ---
                  // strip {server_str} string if it's there
                  $folder = $this->ensure_no_brackets($folder);
                  // attempt to find a folder match in the lookup list
                  $official_folder_long = $this->folder_lookup('', $folder);
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: ('.__LINE__.') $official_folder_long: '.$official_folder_long.'<br>'); }
                  if ($official_folder_long != '')
                  {
                        $havefolder = True;
                  }
                  else
                  {
                        $havefolder = False;
                  }
                  
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: ('.__LINE__.') $havefolder ['.serialize($havefolder).']<br>'); }
                  if ($havefolder == False)
                  {
                        // add whatever namespace we believe should exist
                        // (remember the lookup failed, so we have to guess here)
                        $folder_long = $this->get_folder_long($folder);
                        // create the specified target folder so it will exist
                        //$this->createmailbox($mailsvr_stream,"$server_str"."$folder_long");
                        //$this->phpgw_createmailbox("$server_str"."$folder_long");
                        $fake_fldball = array();
                        $fake_fldball['folder'] = $server_str.$folder_long;
                        $fake_fldball['acctnum'] = $this->get_acctnum();
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: ('.__LINE__.') calling $this->phpgw_createmailbox('.serialize($fake_fldball).')<br>'); }
                        $this->phpgw_createmailbox($fake_fldball);
                        
                        // try again to get the real long folder name of the just created trash folder
                        $official_folder_long = $this->folder_lookup('', $folder);
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: ('.__LINE__.') $official_folder_long: '.$official_folder_long.'<br>'); }
                        // did the folder get created and do we now have the official full name of that folder?
                        if ($official_folder_long != '')
                        {
                              $havefolder = True;
                        }
                  }
                  
                  // at this point we've tries 2 time to obtain the "server approved" long name for the target folder
                  // even tries creating it if necessary
                  // if we have the name, append the message to that folder
                  if (($havefolder == True)
                  && ($official_folder_long != ''))
                  {
                        // delete appsession msg array data thAt is now stale
                        // WE DO NOT GUESS ABOUT APPENDS, WE EXPIRE THE DATA AND GET FRESH
                        //$this->expire_session_cache_item('msgball_list');
                        $target_fldball = array();
                        $target_fldball['folder'] = $official_folder_long;
                        $target_fldball['acctnum'] = $this->get_acctnum();
                        $this->event_msg_append($target_fldball, 'phpgw_append'.' LINE '.__LINE__);
                        
                        $this->ensure_stream_and_folder($target_fldball, 'phpgw_append'.' LINE '.__LINE__);
                        $mailsvr_stream = $this->get_arg_value('mailsvr_stream');
                        // do the append
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: strlen($message) = ['.strlen($message).'] <br>'); } 
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: $GLOBALS["phpgw_dcom_'.$target_fldball['acctnum'].']->dcom->append('.$mailsvr_stream.', '."$server_str"."$official_folder_long".', $message, '.serialize($flags).') <br>'); } 
                        //$acctnum: ['.$acctnum.'] $mailsvr_stream: ['.$mailsvr_stream.'] $msgnum: ['.$msgnum.'] $mailbox: ['.htmlspecialchars($mailbox).']<br>'; } 
                        $retval = $GLOBALS['phpgw_dcom_'.$target_fldball['acctnum']]->dcom->append($mailsvr_stream, "$server_str"."$official_folder_long", $message, $flags);
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_append ('.__LINE__.'): LEAVING, returning $retval ['.serialize($retval).']<br>'); } 
                        return $retval;
                  }
                  else
                  {
                        // we do not have the official long folder name for the target folder
                        // we can NOT append the message to a folder name we are not SURE is corrent
                        // it will fail  HANG the browser for a while
                        // so just SKIP IT
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_append ('.__LINE__.'): LEAVING on error, returning FALSE, unable to get good foldername, unable to append <br>'); }
                        return False;
                  }
            }
            
            /*!
            @function phpgw_mail_move
            @abstract DEPRECIATED - NO LONGER USED. Use "industrial_interacct_mail_move" instead.
            @author Angles
            @access public
            */
01665             function phpgw_mail_move($msg_list,$mailbox)
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_mail_move: (DEPRECIATED) ENTERING<br>'); }
                  // OLD FUNCTION does not provide enough information, all we can do is expire
                  $this->event_msg_move_or_delete(array(), 'phpgw_mail_move');
                  // delete session msg array data thAt is now stale
                  //$this->expire_session_cache_item('msgball_list');
                  
                  $retval = $GLOBALS['phpgw_dcom_'.$this->acctnum]->dcom->mail_move($this->get_arg_value('mailsvr_stream'), $msg_list, $mailbox);
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_mail_move: (DEPRECIATED) LEAVING, $retval ['.serialize($retval).'] <br>'); } 
                  return $retval;
            }
            
            /*!
            @function interacct_mail_move
            @abstract DEPRECIATED - BEING PHASED OUT. Use "industrial_interacct_mail_move" instead.
            @author Angles
            @access public
            */
01684             function interacct_mail_move($mov_msgball='', $to_fldball='')
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): interacct_mail_move: ENTERING<br>'); }
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): interacct_mail_move: $mov_msgball ['.serialize($mov_msgball).'] ;  $to_fldball ['.serialize($to_fldball).']<br>'); } 
                  // this needs A LOT of work!!! do not rely on this yet
                  
                  // delete session msg array data thAt is now stale
                  $this->event_msg_move_or_delete($mov_msgball, 'interacct_mail_move'.' LINE '.__LINE__, $to_fldball);
                  //$this->expire_session_cache_item('msgball_list');
                  
                  // Note: Only call this function with ONE msgball at a time, i.e. NOT a list of msgballs
                  $acctnum = (int)$mov_msgball['acctnum'];
                  if (!(isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  $this->ensure_stream_and_folder($mov_msgball, 'interacct_mail_move'.' LINE '.__LINE__);
                  
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): interacct_mail_move:'.' LINE '.__LINE__.' If this is a move to a DIFFERENT account, then THIS FUNCTION is the WRONG ONE to use, it can not handle that<br>'); } 
                  
                  // NO - this function only works with folders within the same account
                  //$this->ensure_stream_and_folder($to_fldball, 'interacct_mail_move'.' LINE '.__LINE__);
                  
                  //$mailsvr_stream = (int)$this->get_arg_value('mailsvr_stream', $acctnum);
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  $msgnum = (string)$mov_msgball['msgnum'];
                  $mailbox = $to_fldball['folder'];
                  // the acctnum we are moving FROM *may* be different from the acctnum we are moving TO
                  // that requires a fetch then an append - FIXME!!!
                  
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): interacct_mail_move: $acctnum: ['.$acctnum.'] $mailsvr_stream: ['.$mailsvr_stream.'] $msgnum: ['.$msgnum.'] $mailbox: ['.htmlspecialchars($mailbox).']<br>'); } 
                  $retval = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->mail_move($mailsvr_stream ,$msgnum, $mailbox);
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): interacct_mail_move: LEAVING, $retval ['.serialize($retval).'] <br>'); }
                  return $retval;
            }
            
            /*!
            @function industrial_interacct_mail_move
            @abstract Entry point to moving or deleting mail in this class. Call this for moves, call "phpgw_delete" for deletes, which may call this.
            @param $mov_msgball (array of type msgball) the message the will be moved, i.e. a single msgball in its array form.
            @param $to_fldball (array of type fldball) the target of the move, i.e. a single fldball in its array form.
            @author Angles
            @discussion Calls "buffer_move_commands" and "track_expungable_folders" so that 
            the calling process need not worry about these things, HOWEVER the calling process 
            DOES need to call  "expunge_expungable_folders" when all the moves or deletes are done. 
            If you have a msgball list in the form if URI string data, you can call function "ball_data_parse_str" 
            on any URI type msgball string to obtain the structured array that is of type msgball. It is the same data in full 
            structured array form. That structured array form that is a single msgball is what this function expects as an arg. 
            Looping and calling this functions many times in a row may seem slow but that is the design of this function 
            to hide the gorry details from you and this function is designed to be used in a large loop 
            and still achieve speed in the actual implementation of the moves and or deletes on a large scale by 
            having aggregated all the needed info for you, before actually calling the mailserver, so as to call the 
            mailserver as few times as possible with the most info possible in each individual mailserver call. 
            NOTE: TO DELETE MAIL call "phpgw_delete" and it will call this function if conditions warrent. 
            @access public
            */
01741             function industrial_interacct_mail_move($mov_msgball='', $to_fldball='')
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): industrial_interacct_mail_move: ENTERING, handing off to $this->buffer_move_commands()<br>'); }
                  // Note: Only call this function with ONE msgball at a time, i.e. NOT a list of msgballs
                  // then we buffer each command with this function
                  $this->buffer_move_commands($mov_msgball, $to_fldball);
                  
                  //if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): industrial_interacct_mail_move ('.__LINE__.'): ok, now add this folder to this accounts "expunge_folders" arg via "track_expungable_folders"<br>'); } 
                  // do this during actual moves
                  $this->track_expungable_folders($mov_msgball);

                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): industrial_interacct_mail_move: LEAVING, return True so we do not confuse calling process<br>'); }
                  return True;
            }
            
            /*!
            @function buffer_mail_move_commands
            @abstract Called by "industrial_interacct_mail_move"
            @param $mov_msgball (array of type msgball) the message the will be moved. 
            @param $to_fldball (array of type fldball) the target of the move. 
            @author Angles
            @discussion Use function "industrial_interacct_mail_move" which calls this function as needed.
            @access private
            */
01765             function buffer_move_commands($mov_msgball='', $to_fldball='')
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): buffer_move_commands ('.__LINE__.'): ENTERING<br>'); } 
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): buffer_move_commands ('.__LINE__.'): $mov_msgball ['.serialize($mov_msgball).'] $to_fldball ['.serialize($to_fldball).']<br>'); } 
                                    
                  // assemble the URI like string that will hold the command move request instructions
                  $this_move_data = '';
                  $this_move_data = 
                         'mov_msgball[acctnum]='.$mov_msgball['acctnum']
                        .'&mov_msgball[folder]='.$mov_msgball['folder']
                        .'&to_fldball[acctnum]='.$to_fldball['acctnum']
                        .'&to_fldball[folder]='.$to_fldball['folder']
                        .'&mov_msgball[msgnum]='.$mov_msgball['msgnum'];
                  
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): buffer_move_commands ('.__LINE__.'): $this_move_data ['.htmlspecialchars($this_move_data).']<br>'); } 
                  if ($this->debug_wrapper_dcom_calls > 2)
                  {
                        $this_move_balls = array();
                        parse_str($this_move_data, $this_move_balls);
                        //echo 'mail_msg(_wrappers): buffer_move_commands ('.__LINE__.'): parse_str($this_move_data, $this_move_balls) $this_move_balls DUMP <pre>'; print_r($this_move_balls); echo '</pre>';
                        $this->dbug->out('mail_msg(_wrappers): buffer_move_commands ('.__LINE__.'): parse_str($this_move_data, $this_move_balls) $this_move_balls DUMP:', $this_move_balls);
                  }
                  
                  // add this to the array
                  $this->buffered_move_commmands[$this->buffered_move_commmands_count] = $this_move_data;
                  // increase the count, avoids calling count() every trip thru this loop
                  $this->buffered_move_commmands_count++;
                  if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(_wrappers): buffer_move_commands ('.__LINE__.'): added new item to array, new $this->buffered_move_commmands DUMP:', $this->buffered_move_commmands); } 
                  
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): buffer_move_commands ('.__LINE__.'): LEAVING: did add $this_move_data to array, new array count $this->buffered_move_commmands_count: ['.$this->buffered_move_commmands_count.'], "from" acctnum is ['.$mov_msgball['acctnum'].']<br>'); } 
                  return;
            }
            
            
            /*!
            @function flush_buffered_move_commmands
            @abstract ?
            @author Angles
            @discussion ?
            @access public
            */
01806             function flush_buffered_move_commmands($called_by='not_specified')
            {
                  $do_it_for_real = True;
                  //$do_it_for_real = False;
                  
                  // we tell the cache to flush and surn off during a big move, if we find a move is requested, just call the notice once.
                  $did_give_big_move_notice = False;
                  
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands ('.__LINE__.'): ENTERING, called by ['.$called_by.'], <br>'); } 
                  // leave now if nothing is in the buffered command array
                  if ($this->buffered_move_commmands_count == 0)
                  {
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands ('.__LINE__.'): LEAVING, nothing to do, return False, $this->buffered_move_commmands_count: ['.$this->buffered_move_commmands_count.']<br>'); } 
                        return False;
                  }
                  
                  // is this a "big move"
                  if (stristr($this->get_pref_value('mail_server_type', $acctnum), 'pop3'))
                  {
                        // pop3 does not support UID therefor message numbers are unpredictable after moves or deletes
                        // therefor ANY move or delete triggers cache reset via "big move" code
                        $big_move_thresh = 0;
                  }
                  else
                  {
                        // using imap with UIDs we can selectively manipulate the cache for increased speed
                        $big_move_thresh = 10;
                  }
                  $is_big_move = False;
                  if ($this->buffered_move_commmands_count > $big_move_thresh)
                  {
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands ('.__LINE__.'): issue $this->event_begin_big_move because $big_move_thresh: ['.$big_move_thresh.'] $this->buffered_move_commmands_count: ['.$this->buffered_move_commmands_count.']<br>'); } 
                        $this->event_begin_big_move(array(), 'mail_msg(_wrappers): buffered_move_commmands: LINE '.__LINE__);
                        $is_big_move = True;
                  }
                  // Sort will GROUP THE MOVES AS MUCH AS POSSIBLE RIGHT NOW
                  // the way we put the strings in the $this->buffered_move_commmands is designed to be 
                  // used by sort to end up grouping similar moves for us inside the array,
                  // grouping by _from_acctnum__from_folder__to_acctnum__to_folder__msgnum
                  // so similar moves are grouped as much as possible, simply, by calling sort.
                  reset($this->buffered_move_commmands);
                  sort($this->buffered_move_commmands);
                  // trying to get single digit integers grouped with each other, but I do not think this next function is any better at that
                  //sort($this->buffered_move_commmands, SORT_NUMERIC & SORT_STRING);
                  // we know the FROM acct num is the same for all commands
                  // we know the list is sorted so all FROM folders are together, and then the TO_FOLDERS
                  // note the the "del_pseudo_folder" also will be grouped together, later we determing what command to call whether move or straight delete
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands ('.__LINE__.'): we have delete instructions(s) to be processed, (sorted) $this->buffered_move_commmands DUMP:', $this->buffered_move_commmands); } 
                  
                  $grouped_move_balls = array();
                  // group the commands
                  for ($x=0; $x < $this->buffered_move_commmands_count; $x++)
                  {
                        $this_move_balls = array();
                        parse_str($this->buffered_move_commmands[$x], $this_move_balls);                    
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: loop ['.$x.']: $this_move_balls: ['.htmlspecialchars(serialize($this_move_balls)).']<br>'); }
                        // NOTE PARSE_STR ***WILL ADD SLASHES*** TO ESCAPE QUOTES
                        // NO MATTER WHAT YOUR MAGIC SLASHES SETTING IS
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out(' * mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: loop ['.$x.']: NOTE PARSE_STR ***WILL ADD SLASHES*** TO ESCAPE QUOTES NO MATTER WHAT YOUR MAGIC SLASHES SETTING IS **stripping slashes NOW***'); } 
                        if (isset($this_move_balls['mov_msgball']['folder']))
                        {
                              $this_move_balls['mov_msgball']['folder'] = stripslashes($this_move_balls['mov_msgball']['folder']);
                        }
                        if (isset($this_move_balls['to_fldball']['folder']))
                        {
                              $this_move_balls['to_fldball']['folder'] = stripslashes($this_move_balls['to_fldball']['folder']);
                        }
                        
                        // no matter what, we know we are going to move this message, so notify cache if needed
                        // IF WE ISSUED A BIG MOVE NOTICE THEN THE CACHE IS FLUSHED ALREADY
                        if ($is_big_move == False)
                        {
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: loop ['.$x.'] $is_big_move: ['.serialize($is_big_move).'] so calling $this->event_msg_move_or_delete()<br>'); }
                              $this->event_msg_move_or_delete($this_move_balls['mov_msgball'], 'flush_buffered_move_commmands'.' LINE: '.__LINE__, $this_move_balls['to_fldball']);
                        }
                        
                        // --- does the FROM folder match the previous one in the list? ---
                        $count_grouped = count($grouped_move_balls);
                        // make sure at lease one move is in this array, we need at least on previous to compare to, else just add it to start an array
                        if ($count_grouped  == 0)
                        {
                              // add it to the array to get it started
                              //array_push($grouped_move_balls, $this_move_balls);
                              $grouped_move_balls[0] = $this_move_balls;
                              if ($this->buffered_move_commmands_count > 1)
                              {
                                    // SKIP TO NEXT LOOP, we need to compare (try to group) b4 we know to issue the actual move command or not
                                    // NOTE: CONTINUE
                                    if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: added item to array, skip to next iteration<br>'); }
                                    continue;
                              }
                              else
                              {
                                    if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: added item to array, NOT skipping to next iteration because there is only 1 item in array ['.$this->buffered_move_commmands_count.']<br>'); }
                              }
                        }
                        //elseif (($count_grouped > 0)
                        //&& ($grouped_move_balls[$count_grouped-1]['mov_msgball']['folder'] == $this_move_balls['mov_msgball']['folder'])
                        //&& ($grouped_move_balls[$count_grouped-1]['to_fldball']['folder'] == $this_move_balls['to_fldball']['folder'])
                        //)
                        elseif (($count_grouped > 0)
                        && ($x != $this->buffered_move_commmands_count-1)
                        && ($grouped_move_balls[$count_grouped-1]['mov_msgball']['acctnum'] == $this_move_balls['mov_msgball']['acctnum'])
                        && ($grouped_move_balls[$count_grouped-1]['mov_msgball']['folder'] == $this_move_balls['mov_msgball']['folder'])
                        && ($grouped_move_balls[$count_grouped-1]['to_fldball']['folder'] == $this_move_balls['to_fldball']['folder'])
                        )
                        {
                              // PASSES the "is grouped" test, add to the "grouped array"
                              // AND this is NOT the last item in buffered_move_commmands (that would require action, not another loop)
                              array_push($grouped_move_balls, $this_move_balls);
                              // NOTE: CONTINUE
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: added item to array, skip to next iteration<br>'); }
                              continue;
                        }
                        elseif (($count_grouped > 0)
                        && ($x == $this->buffered_move_commmands_count-1)
                        && ($grouped_move_balls[$count_grouped-1]['mov_msgball']['acctnum'] == $this_move_balls['mov_msgball']['acctnum'])
                        && ($grouped_move_balls[$count_grouped-1]['mov_msgball']['folder'] == $this_move_balls['mov_msgball']['folder'])
                        && ($grouped_move_balls[$count_grouped-1]['to_fldball']['folder'] == $this_move_balls['to_fldball']['folder'])
                        )
                        {
                              // PASSES the "is grouped" test, add to the "grouped array"
                              // AND this is the FINAL ITEM, so KEEP GOING down to the code to issue the actual move command
                              array_push($grouped_move_balls, $this_move_balls);
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: added item to array, but NOT skipping to next iteration<br>'); }
                              // DO NOT issue "CONTINUE" here
                        }
                        else
                        {
                              //if ($this->debug_wrapper_dcom_calls > 1) { echo ' * mail_msg(_wrappers): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: UNHANDLED if .. then, $$grouped_move_balls[$count_grouped-1] DUMP<pre>'; print_r($grouped_move_balls[$count_grouped-1]); echo "\r\n".' $$this_move_balls DUMP'; print_r($this_move_balls); echo '</pre>' ; } 
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: UNHANDLED if .. then, $$grouped_move_balls[$count_grouped-1] DUMP', $grouped_move_balls[$count_grouped-1]); } 
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: UNHANDLED if .. then, $$this_move_balls DUMP', $this_move_balls); } 
                        }
                        
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: if we get here, we can not group anymore, or the series just ended, so issue the command now<br>'); }
                        // OK if we are here then we know this
                        // * "grouped_move_balls" has at least one command in it
                        // ** the current command does not match the preious one in terms or grouping them together
                        // ** OR the urrent command is the final in the $this->buffered_move_commmands array
                        // THEREFOR:
                        // 1) we need now make a IMAP command that has all the grouped msgnums from grouped_move_balls
                        // 2) if NOT the final item in $this->buffered_move_commmands we need to
                        //     2a) then we need to clear grouped_move_balls and ADD this_move_balls to it to start a new grouping array
                        //     2b) then run again thru the loop after that
                        
                        // update this, this loop may have added to it since we last checked this
                        $count_grouped = count($grouped_move_balls);
                        
                        // IF THIS MOVE IS TO ANOTHER ACCOUNT, HAND IT OFF RIGHT NOW
                        if ( ($count_grouped = 1)
                        && ((int)$grouped_move_balls[0]['mov_msgball']['acctnum'] != (int)$grouped_move_balls[0]['to_fldball']['acctnum']) ) 
                        {
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands ('.__LINE__.'): ($do_it_for_real is '.serialize($do_it_for_real).'): 1 single **DIFFERENT** Account Move item in $grouped_move_balls, hand off to "single_interacct_mail_move"<br>'); } 
                              if ($do_it_for_real == True)
                              {
                                    $this->single_interacct_mail_move($grouped_move_balls[$count_grouped-1]['mov_msgball'], $grouped_move_balls[$count_grouped-1]['to_fldball']);
                              }
                        }
                        elseif ( ($count_grouped > 1)
                        && ((int)$grouped_move_balls[$count_grouped-1]['mov_msgball']['acctnum'] != (int)$grouped_move_balls[$count_grouped-1]['to_fldball']['acctnum']) ) 
                        {
                              if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands ('.__LINE__.'): LEAVING with ERROR: ERROR: unhandled if .. then,  $grouped_move_balls has multiple items but accounts do not match, different accounts should be handled one at a time!!!<br>'); } 
                              echo 'mail_msg(_wrappers): flush_buffered_move_commmands ('.__LINE__.'): LEAVING with ERROR: unhandled if .. then,  $grouped_move_balls has multiple items but accounts do not match, different accounts should be handled one at a time!!!<br>';
                              return False;
                        }
                        else
                        {
                              // FIXME: some logic below relies on strlen of $collected_msg_num_string to determine if it has ONE DIGIT or not
                              // but a single integer can be 1 char or 5 chars, for example, this causes *very* rare errors in the string groupings
                              // causing not all message to be moved, or in the worst case, an error from the mailserver if a msgnum does not exist on it
                              // whih can happen if 2 digits are put together without a comman or colon inbetween, makes a number unrelated to the grouping
                              
                              if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(_wrappers): flush_buffered_move_commmands ('.__LINE__.'): action required $grouped_move_balls DUMP', $grouped_move_balls); } 
                              // update this, this loop may have added to it since we last checked this
                              $count_grouped = count($grouped_move_balls);
                              $collected_msg_num_string = '';
                              // super dumb but simple way, just put a comma between all msgnum's
                              //for ($group_loops=0; $group_loops < $count_grouped; $group_loops++)
                              //{
                              //    if ($group_loops > 0)
                              //    {
                              //          $collected_msg_num_string .= ',';
                              //    }
                              //    $collected_msg_num_string .= $grouped_move_balls[$group_loops]['mov_msgball']['msgnum'];
                              //}
                              // BETTER way, use rfv2060 specs to put range of msgnums together seperated by a colon
                              for ($group_loops=0; $group_loops < $count_grouped; $group_loops++)
                              {
                                    if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * flush_buffered_move_commmands ('.__LINE__.'): loop ['.$group_loops.'] of ['.(string)($count_grouped-1).'],  $collected_msg_num_string: ['.$collected_msg_num_string.']<br>'); } 
                                    if ( ($group_loops > 0)
                                    //&& (($grouped_move_balls[$group_loops-1]['mov_msgball']['msgnum']+1) == $grouped_move_balls[$group_loops]['mov_msgball']['msgnum']) ) 
                                    && ($grouped_move_balls[$group_loops-1]['mov_msgball']['msgnum']+1 == $grouped_move_balls[$group_loops]['mov_msgball']['msgnum']) ) 
                                    {
                                          // we have a contiguous series, handle string specially
                                          if (($count_grouped == 2)
                                          && ($group_loops == 1))
                                          {
                                                // two items will never make a series
                                                $collected_msg_num_string .= ','.(string)$grouped_move_balls[$group_loops]['mov_msgball']['msgnum'];
                                          }
                                          elseif ($group_loops == $count_grouped-1)
                                          {
                                                // the contiguous series of numbers just ended because the list is done
                                                // if there is not a comma nor a colon after the last number, put one there
                                                $last_char_idx = strlen((string)$collected_msg_num_string)-1;
                                                $last_char = $collected_msg_num_string[$last_char_idx];
                                                // situation is that two contiguos numbers at the end of a list like this need a comma
                                                if (($last_char != ',')
                                                && ($last_char != ':'))
                                                {
                                                      // COLON OR A COMMAN NEEDED
                                                      if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * flush_buffered_move_commmands ('.__LINE__.'): loop ['.$group_loops.'] of ['.(string)($count_grouped-1).'], COLON OR COMMA NEEDED: $last_char: ['.$last_char.'], $collected_msg_num_string: ['.$collected_msg_num_string.']<br>'); } 
                                                      if (($count_grouped > 2)
                                                      //&& (strlen((string)$collected_msg_num_string) == 1))
                                                      && (!stristr($collected_msg_num_string, ':'))
                                                      && (!stristr($collected_msg_num_string, ',')))
                                                      {
                                                            // ADD A COLON IF
                                                            // (a) if the total things the move are > 2 AND 
                                                            // (b) there is so far only ONE number in our $collected_msg_num_string
                                                            // then this is a series that had been uninterupted since it began looping here
                                                            // situation is nums are 1, 2, 3, 4, 5, so in the last loop $collected_msg_num_string = "1:5"
                                                            if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * flush_buffered_move_commmands ('.__LINE__.'): loop ['.$group_loops.'] of ['.(string)($count_grouped-1).'], ADDING A COLON: $last_char: ['.$last_char.'], $collected_msg_num_string: ['.$collected_msg_num_string.']<br>'); } 
                                                            $collected_msg_num_string .= ':';
                                                      }
                                                      else
                                                      {
                                                            // ADD A COMMA, these are 
                                                            // situation is nums are 3, 37, 38, so in the last loop $collected_msg_num_string = "3,37"
                                                            if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * flush_buffered_move_commmands ('.__LINE__.'): loop ['.$group_loops.'] of ['.(string)($count_grouped-1).'], ADDING A COMMA: $last_char: ['.$last_char.'], $collected_msg_num_string: ['.$collected_msg_num_string.']<br>'); } 
                                                            $collected_msg_num_string .= ',';
                                                      }
                                                }
                                                $collected_msg_num_string .= (string)$grouped_move_balls[$group_loops]['mov_msgball']['msgnum'];
                                          }
                                          elseif ( (strlen($collected_msg_num_string) > 1)
                                          && ($collected_msg_num_string[strlen($collected_msg_num_string)-1] != ':') )
                                          {
                                                // this is a contiguous series just starting, needs a colon
                                                $collected_msg_num_string .= ':';
                                          }
                                          else
                                          {
                                                // DO NOTHING we are in the middle of this contiguous series of numbers
                                          }
                                    }
                                    // did a series just end?
                                    elseif ( ($group_loops > 1)
                                    && (($grouped_move_balls[$group_loops-2]['mov_msgball']['msgnum']+1) == $grouped_move_balls[$group_loops-1]['mov_msgball']['msgnum']) ) 
                                    {
                                          // NOTE: ADD A COLON 
                                          // if the previous existing $collected_msg_num_string does NOT have a colon or comma as its last crag
                                          $last_char_idx = strlen((string)$collected_msg_num_string)-1;
                                          $last_char = $collected_msg_num_string[$last_char_idx];
                                          // situation is that the current end of a list needs something before we can add another number, (a comma or a colon)
                                          if (($last_char != ',')
                                          && ($last_char != ':'))
                                          {
                                                // so why colon and no check for a needed comma?  
                                                // SINCE WE KNOW A SERIES JUST ENDED, whether just 2 contiguous numbers or 20 contiguous numbers
                                                // WE KNOW that a colon would be valid because we KNOW the previous existing number 
                                                //   AND the number we are about to add are IN A SERIES
                                                // so a colon would not create an unwanted series becuase we KNOW we have a series at this point
                                                // so ... a colon can not hurt, a colon between contiguous numbers is still valid syntax (i.e. "3:4"
                                                if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * flush_buffered_move_commmands ('.__LINE__.'): loop ['.$group_loops.'] of ['.(string)($count_grouped-1).'], ADDING A COLON: $last_char: ['.$last_char.'], $collected_msg_num_string: ['.$collected_msg_num_string.']<br>'); } 
                                                $collected_msg_num_string .= ':';
                                          }
                                          //  inset the number of the end of the series, a comman, and the current non-contiguous number
                                          $collected_msg_num_string .= 
                                                 (string)$grouped_move_balls[$group_loops-1]['mov_msgball']['msgnum']
                                                .','
                                                .(string)$grouped_move_balls[$group_loops]['mov_msgball']['msgnum'];
                                    }
                                    else
                                    {
                                          // we are NOT in a contiguous series, inset  a comma, and the current number
                                          if (strlen((string)$collected_msg_num_string) > 0)
                                          {
                                                $collected_msg_num_string .= ',';
                                          }
                                          $collected_msg_num_string .= (string)$grouped_move_balls[$group_loops]['mov_msgball']['msgnum'];
                                    }
                                    if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * flush_buffered_move_commmands ('.__LINE__.'): $collected_msg_num_string: ['.$collected_msg_num_string.']<br>'); } 
                              }
                              
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: final $collected_msg_num_string: ['.$collected_msg_num_string.']<br>'); }
                              // 1b) issue the delete COMMAND finally now
                              $mov_msgball = array();
                              $mov_msgball = $grouped_move_balls[$count_grouped-1]['mov_msgball'];
                              $to_fldball = array();
                              $to_fldball = $grouped_move_balls[$count_grouped-1]['to_fldball'];
                              // the FROM acctnum we'll use as "this_acctnum"
                              $this_acctnum = $mov_msgball['acctnum'];
                              // EXPIRE MSGBALL ???? wasn't this done with the notice of big move?
                              // note since we ALWAYS turn off extreme caching when weuse this function, we *could* DIRECTLY expire it
                              //if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): flush_buffered_move_commmands: expire msgball list with call to $this->event_msg_move_or_delete<br>'; }
                              //$this->event_msg_move_or_delete($mov_msgball, 'flush_buffered_move_commmands'.' LINE: '.__LINE__.' and CACHE SHOULD BE OFF NOW', $to_fldball);
                              //if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): flush_buffered_move_commmands: expire msgball list with DIRECT call to $this->expire_session_cache_item (because we know extreme caching os turned off for the duration of this function)<br>'; }
                              //$this->expire_session_cache_item('msgball_list', $this_acctnum);
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: ($do_it_for_real is '.serialize($do_it_for_real).'): calling $this->ensure_stream_and_folder($mov_msgball ['.serialize($mov_msgball).'], who_is_calling) <br>'); }
                              if ($do_it_for_real == True)
                              {
                                    $this->ensure_stream_and_folder($mov_msgball, 'flush_buffered_move_commmands'.' LINE: '.__LINE__);
                              }
                              $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $this_acctnum);
                              // IS THIS A MOVE OR A DELETE?
                              if ($to_fldball['folder'] == $this->del_pseudo_folder || $to_fldball['folder'] == '##NOTHING##')
                              {
                                    // STRAIGHT DELETE
                                    if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: SRAIGHT DELETE ($do_it_for_real is '.serialize($do_it_for_real).'): $GLOBALS[phpgw_dcom_'.$this_acctnum.']->dcom->delete('.serialize($mailsvr_stream).' , '.$collected_msg_num_string.' )<br>'); }
                                    if ($do_it_for_real == True)
                                    {
                                          $did_delete = $GLOBALS['phpgw_dcom_'.$this_acctnum]->dcom->delete($mailsvr_stream , $collected_msg_num_string);
                                          if (!$did_delete)
                                          {
                                                $imap_err = $GLOBALS['phpgw_dcom_'.$this_acctnum]->dcom->server_last_error();
                                                if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: STRAIGHT DELETE: LEAVING on ERROR, $imap_err: ['.$imap_err.'] return False'.' LINE '.__LINE__.'<br>'); }
                                                echo 'mail_msg(_wrappers): flush_buffered_move_commmands: LEAVING on ERROR, $imap_err: ['.$imap_err.'] return False'.' LINE '.__LINE__.'<br>';
                                                echo '&nbsp; command was: $GLOBALS[phpgw_dcom_'.$this_acctnum.']->dcom->delete('.serialize($mailsvr_stream).' ,'.$collected_msg_num_string.')<br>';
                                                return False;
                                          }
                                    }
                              }
                              else
                              {
                                    // MOVE
                                    if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: ($do_it_for_real is '.serialize($do_it_for_real).'): $GLOBALS[phpgw_dcom_'.$this_acctnum.']->dcom->mail_move('.serialize($mailsvr_stream).' ,'.$collected_msg_num_string.', '.serialize($to_fldball['folder']).')<br>'); }
                                    if ($do_it_for_real == True)
                                    {
                                          $did_move = $GLOBALS['phpgw_dcom_'.$this_acctnum]->dcom->mail_move($mailsvr_stream , $collected_msg_num_string, $to_fldball['folder']);
                                          if (!$did_move)
                                          {
                                                $imap_err = $GLOBALS['phpgw_dcom_'.$this_acctnum]->dcom->server_last_error();
                                                if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: LEAVING on ERROR, $imap_err: ['.$imap_err.'] return False'.' LINE '.__LINE__.'<br>'); }
                                                echo 'mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: LEAVING on ERROR, $imap_err: ['.$imap_err.'] return False'.' LINE '.__LINE__.'<br>';
                                                echo '&nbsp; command was: $GLOBALS[phpgw_dcom_'.$this_acctnum.']->dcom->mail_move('.serialize($mailsvr_stream).' ,'.$collected_msg_num_string.', '.serialize($to_fldball['folder']).')<br>';
                                                return False;
                                          }
                                    }
                              }
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: LINE '.__LINE__.': is we get here we probably just issued a move or delete command, we may try to group more (usually only with filter usage) or we may be done with buffered command list<br>'); }
                        }
                        
                        // 2) if NOT the final item in $this->buffered_move_commmands we need to
                        //     2a) then we need to clear grouped_move_balls and ADD this_move_balls to it to start a new grouping array
                        //     2b) then run again thru the loop after that
                        if ($x != $this->buffered_move_commmands_count-1)
                        {
                              $grouped_move_balls = array();
                              array_push($grouped_move_balls, $this_move_balls);
                              // 3) then run again thru the loop after that
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: continue; ... to look for groupable move commands for acctnum ['.$mailsvr_stream.']<br>'); }
                              // doesn't this happen anyway here?
                              continue;
                        }
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: still in that loop $x: ['.$x.'] $this->buffered_move_commmands_count-1: ['.(string)($this->buffered_move_commmands_count-1).'], if we get to here we SHOULD be done with all moves, else a continue would have been hit<br>'); }
                  }
                  
                  if ($is_big_move == True)
                  {
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands: $is_big_move: ['.serialize($is_big_move).'] we get to here we SHOULD be done so call $this->event_begin_big_end <br>'); } 
                        $this->event_begin_big_end('flush_buffered_move_commmands '.__LINE__);
                  }
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): flush_buffered_move_commmands ('.__LINE__.'): LEAVING<br>'); } 
                  // FIXME return something more useful
                  return True;
            }
            
            
            /*!
            @function single_interacct_mail_move
            @abstract Primary mail move function for DIFFERENT Accounts. Moves single mails, use a loop if moving more than one mail. 
            @param $mov_msgball (array of type msgball) the message the will be moved. 
            @param $to_fldball (array of type fldball) the target of the move. 
            @author Angles
            @discussion Can handle any kind of move, same account, different account, different server.  Now 
            used mostly for different account moves, because we attempt to group single account moves elsewhere. 
            Fills arg "expunge_folders" for any account that has folders needing to be expunged. 
            @access public
            */
02186             function single_interacct_mail_move($mov_msgball='', $to_fldball='')
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: ENTERING (note: only feed ONE msgball at a time, i.e. NOT a list of msgballs) <br>'); }
                  // Note: Only call this function with ONE msgball at a time, i.e. NOT a list of msgballs
                  // INTERACCOUNT -OR- SAME ACCOUNT ?
                  if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: $mov_msgball DUMP:', $mov_msgball); }
                  if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: $to_fldball DUMP:', $to_fldball); }
                  // --- Establist account numbers ----
                  $mov_msgball['acctnum'] = (int)$mov_msgball['acctnum'];
                  if (!(isset($mov_msgball['acctnum']))
                  || ((string)$mov_msgball['acctnum'] == ''))
                  {
                        $mov_msgball['acctnum'] = $this->get_acctnum();
                  }
                  $to_fldball['acctnum'] = (int)$to_fldball['acctnum'];
                  if (!(isset($to_fldball['acctnum']))
                  || ((string)$to_fldball['acctnum'] == ''))
                  {
                        $to_fldball['acctnum'] = $this->get_acctnum();
                  }
                  
                  // Are the acctnums the same?
                  if ((string)$mov_msgball['acctnum'] == (string)$to_fldball['acctnum'])
                  {
                        // SAME ACCOUNT MAIL MOVE
                        
                        $common_acctnum = $mov_msgball['acctnum'];
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: SAME ACCOUNT MOVE $common_acctnum: '.$common_acctnum.' $mailsvr_stream: '.$mailsvr_stream.' $msgnum: '.$msgnum.' $mailsvr_callstr: '.$mailsvr_callstr.' $mailbox: '.$mailbox.'<br>'); }
                        $this->event_msg_move_or_delete($mov_msgball, 'single_interacct_mail_move'.' LINE: '.__LINE__, $to_fldball);
                        //$this->expire_session_cache_item('msgball_list', $common_acctnum);
                        // we need to SELECT the folder the message is being moved FROM
                        $mov_msgball['folder'] = urldecode($mov_msgball['folder']);
                        
                        $this->ensure_stream_and_folder($mov_msgball, 'single_interacct_mail_move'.' LINE: '.__LINE__);
                        $mov_msgball['msgnum'] = (string)$mov_msgball['msgnum'];
                        $to_fldball['folder'] = urldecode($to_fldball['folder']);
                        $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $common_acctnum);
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: $GLOBALS[phpgw_dcom_'.$common_acctnum.']->dcom->mail_move('.serialize($mailsvr_stream).' ,'.serialize($mov_msgball['msgnum']).', '.serialize($to_fldball['folder']).')<br>'); }
                        $did_move = $GLOBALS['phpgw_dcom_'.$common_acctnum]->dcom->mail_move($mailsvr_stream ,$mov_msgball['msgnum'], $to_fldball['folder']);
                        if (!$did_move)
                        {
                              if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return False'.' LINE '.__LINE__.'<br>'); }
                              return False;
                        }
                        else
                        {
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move ('.__LINE__.'): SAME ACCOUNT MOVE *SUCCESS*, $did_move ['.serialize($did_move).'], now add this folder to this accounts "expunge_folders" arg via "track_expungable_folders"<br>'); } 
                              $this->track_expungable_folders($mov_msgball);
                              //if ($this->debug_wrapper_dcom_calls > 0) { echo 'mail_msg(_wrappers): industrial_interacct_mail_move: LEAVING, about to call $this->phpgw_expunge('.$mov_msgball['acctnum'].')'.' LINE '.__LINE__.'<br>'; }
                              //return $this->phpgw_expunge($mov_msgball['acctnum']);
                              if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move ('.__LINE__.'): LEAVING, returning True, SAME ACCOUNT MOVE SUCCESS (do not forget to expunge later) <br>'); } 
                              return True;
                        }
                  }
                  else
                  {
                        // DIFFERENT ACCOUNT MAIL MOVE
                        
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: Different ACCOUNT MOVE $common_acctnum: '.$common_acctnum.' $mailsvr_stream: '.$mailsvr_stream.' $msgnum: '.$msgnum.' $mailsvr_callstr: '.$mailsvr_callstr.' $mailbox: '.$mailbox.'<br>'); }
                        $good_to_go = False;
                        // delete session msg array data thAt is now stale
                        $this->event_msg_move_or_delete($mov_msgball, 'single_interacct_mail_move'.' LINE: '.__LINE__, $to_fldball);
                        //$this->expire_session_cache_item('msgball_list', $mov_msgball['acctnum']);
                        $mov_msgball['folder'] = urldecode($mov_msgball['folder']);
                        // Make Sure Stream Exists
                        // multiple accounts means one stream may be open but another may not
                        // "ensure_stream_and_folder" will verify for us, 
                        $this->ensure_stream_and_folder($mov_msgball, 'single_interacct_mail_move');
                        // GET MESSAGE FLAGS (before you get the mgs, so unseen/seen is not tainted by our grab)
                        $hdr_envelope = $this->phpgw_header($mov_msgball);
                        $mov_msgball['flags'] = $this->make_flags_str($hdr_envelope);
                        // GET THE MESSAGE
                        // part_no 0 only used to get the headers
                        $mov_msgball['part_no'] = 0;
                        // (a)  the headers, specify part_no 0
                        //$moving_message = $GLOBALS['phpgw']->msg->phpgw_fetchbody($mov_msgball);
                        $moving_message = $this->phpgw_fetchbody($mov_msgball);
                        // (b) the body, plus a CRLF, reuse headers_msgball b/c "phpgw_body" cares not about part_no
                        //$moving_message .= $GLOBALS['phpgw']->msg->phpgw_body($mov_msgball)."\r\n";
                        $moving_message .= $this->phpgw_body($mov_msgball)."\r\n";
                        $good_to_go = (strlen($moving_message) > 3);
                        if (!$good_to_go)
                        {
                              if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return False'.' LINE '.__LINE__.'<br>'); }
                              return False;
                        }
                        
                        // APPEND TO TARGET FOLDER
                        // delete session msg array data thAt is now stale
                        // WE DO NOT GUESS ABOUT APPENDS, WE EXPIRE THE DATA AND GET FRESH
                        //$this->expire_session_cache_item('msgball_list', $to_fldball['acctnum']);
                        $this->event_msg_append($to_fldball, 'single_interacct_mail_move  Line '.__LINE__);
                        
                        
                        $to_fldball['folder'] = urldecode($to_fldball['folder']);
                        // TEMP (MUST add this back!!!) append does NOT require we open the target folder, only requires a stream
                        //$remember_to_fldball = $to_fldball['folder'];
                        //$to_fldball['folder'] = '';
                        // PASS "no_switch_away" to indicate we should NOT CHANGE FOLDERS
                        $to_fldball['no_switch_away'] = True;
                        $this->ensure_stream_and_folder($to_fldball, 'single_interacct_mail_move');
                        $mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $to_fldball['acctnum']);
                        $to_mailsvr_stream = $this->get_arg_value('mailsvr_stream', $to_fldball['acctnum']);
                        //$to_fldball['folder'] = $remember_to_fldball;
                        // $tol_folder_utldecoded = 
                        $good_to_go = $GLOBALS['phpgw_dcom_'.$to_fldball['acctnum']]->dcom->append($to_mailsvr_stream, $mailsvr_callstr.$to_fldball['folder'], $moving_message, $mov_msgball['flags']);
                        if (!$good_to_go)
                        {
                              if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return False'.' LINE '.__LINE__.'<br>'); }
                              return False;
                        }
                        // DELETE and EXPUNGE from FROM FOLDER
                        $from_mailsvr_stream = $this->get_arg_value('mailsvr_stream', $mov_msgball['acctnum']);
                        $good_to_go = $GLOBALS['phpgw_dcom_'.$mov_msgball['acctnum']]->dcom->delete($from_mailsvr_stream, $mov_msgball['msgnum']);
                        if (!$good_to_go)
                        {
                              if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return False'.' LINE '.__LINE__.'<br>'); }
                              return False;
                        }
                        //$good_to_go = $GLOBALS['phpgw']->msg->phpgw_expunge($mov_msgball['acctnum']);
                        //$good_to_go = $this->phpgw_expunge($mov_msgball['acctnum']);
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move ('.__LINE__.'): different account append and delete SUCCESS, now add this folder to this accounts "expunge_folders" arg via "track_expungable_folders"<br>'); } 
                        $this->track_expungable_folders($mov_msgball);
                        
                        if (!$good_to_go)
                        {
                              if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return False'.' LINE '.__LINE__.'<br>'); }
                              return False;
                        }
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return TRUE'.' LINE '.__LINE__.'<br>'); }
                        return True;
                  }
            }
            
            /*!
            @function track_expungable_folders
            @abstract Keeps track of what accounts folders will need to be expunged. 
            @author Angles
            @discussion Used by "industrial_interacct_mail_move" and "phpgw_delete" to keep track 
            of which folders for any account will need to be expunged. NOTE this tracking occurs 
            automatically in those functions, HOWEVER the calling function is responsible to 
            call "expunge_expungable_folders" when all the moves or deletes are done. 
            @access private
            */
02330             function track_expungable_folders($fldball='')
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): track_expungable_folders ('.__LINE__.'): ENTERING, $fldball ['.serialize($fldball).']<br>'); } 
                  if (!(isset($fldball['acctnum']))
                  || ((string)$fldball['acctnum'] == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  else
                  {
                        $acctnum = $fldball['acctnum'];
                  }
                  if (!(isset($fldball['folder']))
                  || ((string)$fldball['folder'] == ''))
                  {
                        $folder = $this->get_arg_value('folder', $acctnum);
                  }
                  else
                  {
                        $folder = $fldball['folder'];
                  }
                  $my_fldball = array();
                  $my_fldball['folder'] = $folder;
                  $my_fldball['acctnum'] = $acctnum;
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): track_expungable_folders ('.__LINE__.'): $my_fldball ['.serialize($my_fldball).']<br>'); } 
                  
                  $first_addition_to_array = False;
                  // get an array of folders that need expunging that we know of
                  if ($this->get_isset_arg('expunge_folders', $my_fldball['acctnum']) == False)
                  {
                        $expunge_folders = array();
                        $first_addition_to_array = True;
                  }
                  else
                  {
                        //$expunge_folders = $this->get_arg_value('expunge_folders', $my_fldball['acctnum']);
                        $expunge_folders =& $this->_get_arg_ref('expunge_folders', $my_fldball['acctnum']);
                  }
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): track_expungable_folders ('.__LINE__.'): $expunge_folders DUMP:', $expunge_folders); } 
                  // if this particular folder already in the array
                  $loops = count($expunge_folders);
                  $already_listed = False;
                  for ($i=0; $i<$loops;$i++)
                  {
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): track_expungable_folders ('.__LINE__.'): loop ['.$i.'] of ['.$loops.'] $expunge_folders[$i] ['.htmlspecialchars($expunge_folders[$i]).'] same as $my_fldball[folder] ['.htmlspecialchars($my_fldball['folder']).'] test<br>'); } 
                        if ($expunge_folders[$i] == $my_fldball['folder'])
                        {
                              $already_listed = True;
                              break;
                        }
                  }
                  // if this folder was NOT already in the array, put it there and save the arg  value
                  if ($already_listed == False)
                  {
                        $new_idx = count($expunge_folders);
                        $expunge_folders[$new_idx] = $my_fldball['folder'];
                        if ($first_addition_to_array == True)
                        {
                              $this->set_arg_value('expunge_folders', $expunge_folders, $my_fldball['acctnum']);
                              if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): track_expungable_folders ('.__LINE__.'): LEAVING: added first item to $my_fldball[folder] ['.$my_fldball['folder'].'] to $expunge_folders ['.htmlspecialchars(serialize($expunge_folders)).']<br>'); } 
                        }
                        else
                        {
                              if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): track_expungable_folders ('.__LINE__.'): LEAVING: added VIA REFERENCE $my_fldball[folder] ['.$my_fldball['folder'].'] to $expunge_folders ['.htmlspecialchars(serialize($expunge_folders)).']<br>'); } 
                        }
                        return True;
                  }
                  else
                  {
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): track_expungable_folders ('.__LINE__.'): LEAVING: $my_fldball[folder] ['.$my_fldball['folder'].'] was ALREADY in $expunge_folders ['.htmlspecialchars(serialize($expunge_folders)).']<br>'); } 
                        return False;
                  }
            }
            
            /*!
            @function expunge_expungable_folders
            @abstract calls "flush_buffered_move_commmands" then expunges folders. Second part of the move delete process as wrapped in this class. 
            @author Angles
            @discussion Calls "flush_buffered_move_commmands" then loops thru ALL accounts, expunges any account that has folder names in its arg "expunge_folders". 
            Recall that moving and deleting mail are related items and make use of the same sub-functions in some cases, as some deletes are simply moves 
            to a trash folder, and also that even straight deletes can benefit from the "buffering" of the commands as provided in this class. 
            Thus, to move or delete mail in anglemail, call function "industrial_interacct_mail_move" for moves OR "phpgw_delete" for deletes as many times as you need, as this 
            will in turn call "buffer_move_commands" and "track_expungable_folders" which buffers move or delete command for a single flush command to be called later, 
            WHICH IS THIS FUNCTION. This function actually implements all the moves or deletes requested by "industrial_interacct_mail_move" 
            (or phpgw_delete), i.e. it "flushes" all the actions buffered up there, by calling "flush_buffered_move_commmands", 
            and then this function also uses the folder tracking from "track_expungable_folders" (also called by "industrial_interacct_mail_move" ) 
            to know what folders in what accounts to expunge. 
            To summerize, in this class and in anglemail, to move or delete mail is a two step process for you. 
            First you call "industrial_interacct_mail_move" as many times as necessary (if moving) or use "phpgw_delete" (if deleting), then 
            Second, after you are done with that, call this function to apply the actions and expunge the necessary folders. 
            This two step process is designed to allow for speed in huge moves or deletes by calling the 
            mailserver as few times as possible and with as much info as possible in each individual mailserver call. 
            @access public
            */
02424             function expunge_expungable_folders($called_by='not_specified')
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): expunge_expungable_folders ('.__LINE__.'): ENTERING, called by ['.$called_by.'], <br>'); } 
                  
                  $this->flush_buffered_move_commmands('expunge_expungable_folders');
                  
                  $expunge_folders = array();
                  for ($i=0; $i < count($this->extra_and_default_acounts); $i++)
                  {
                        if ($this->extra_and_default_acounts[$i]['status'] == 'enabled')
                        {
                              $this_acctnum = $this->extra_and_default_acounts[$i]['acctnum'];
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): expunge_expungable_folders ('.__LINE__.'): acctnum ['.$this_acctnum.'] needs to be checked<br>'); } 
                              if ($this->get_isset_arg('expunge_folders', $this_acctnum) == True)
                              {
                                    $expunge_folders = array();
                                    $expunge_folders = $this->get_arg_value('expunge_folders', $this_acctnum);
                                    if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): expunge_expungable_folders ('.__LINE__.'): acctnum ['.$this_acctnum.'] indicates these folder(s) need to be expunged, $expunge_folders DUMP:', $expunge_folders); } 
                                    
                                    for ($x=0; $x < count($expunge_folders); $x++)
                                    {
                                          $success = False;
                                          $fake_fldball = array();
                                          $fake_fldball['acctnum'] = $i;
                                          $fake_fldball['folder'] = $expunge_folders[$x];
                                          $success = $this->phpgw_expunge('', $fake_fldball);
                                          if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): expunge_expungable_folders ('.__LINE__.'): expunge for $fake_fldball ['.htmlspecialchars(serialize($fake_fldball)).'] returns ['.serialize($success).']<br>'); } 
                                    }
                                    // we are done with this account, we expunged all expungable folders, not UNSET that arg so it is not left hanging around
                                    if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): expunge_expungable_folders ('.__LINE__.'): finished expunging folders for acctnum ['.$this_acctnum.'] , now issue: $this->unset_arg("expunge_folders", '.$this_acctnum.') <br>'); } 
                                    $this->unset_arg('expunge_folders', $this_acctnum);
                              }
                              else
                              {
                                    $expunge_folders = array();
                                    if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): expunge_expungable_folders ('.__LINE__.'): acctnum ['.$this_acctnum.'] has NO value for "expunge_folders"<br>'); } 
                              }
                        }
                  }
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): expunge_expungable_folders ('.__LINE__.'): LEAVING<br>'); } 
                  // FIXME return something more useful
                  return True;
            }
            
            
            /*!
            @function phpgw_expunge
            @abstract Expunge a folder.
            @author Angles
            @discussion Brainless function, used by "expunge_expungable_folders" which is a "smart" 
            function. This may be called directly, but it is preferable to use "expunge_expungable_folders" 
            assuming the move or delete function you used calls "track_expungable_folders", such as 
            by calling "industrial_interacct_mail_move" which handles that stuff for you.
            @access public
            */
02479             function phpgw_expunge($acctnum='', $fldball='')
            {
                  if (!(isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        if (!(isset($fldball['acctnum']))
                        || ((string)$fldball['acctnum'] == ''))
                        {
                              $acctnum = $this->get_acctnum();
                        }
                        else
                        {
                              $acctnum = $fldball['acctnum'];
                        }
                  }
                  
                  //$fake_fldball = array();
                  //$fake_fldball['folder'] = $this->get_arg_value('folder', $acctnum);
                  
                  // NOTE: it is OK to pass blank folder to "ensure_stream_and_folder" when FOLDER DOE NOT MATTER
                  // for expunge, all we need is a stream, 
                  //$fake_fldball['acctnum'] = $acctnum;
                  //$this->ensure_stream_and_folder($fake_fldball, 'phpgw_expunge'.' LINE '.__LINE__);
                  // NOTE THAT CAUSED MAILSERVER TO REOPEN *AWAY* FROM FOLDER NEEDING EXPUNGE 
                  // and re-open to INBOX because a blank folder arg was passed.
                  if ((isset($fldball['acctnum']))
                  && ((string)$fldball['acctnum'] != '')
                  && (isset($fldball['folder']))
                  && ((string)$fldball['folder'] != ''))
                  {
                        $this->ensure_stream_and_folder($fldball, 'phpgw_expunge'.' LINE '.__LINE__);
                  }
                  
                  $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                  $note_folder = $this->get_arg_value('folder', $acctnum);
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): dcom_call: phpgw_expunge: $acctnum: '.serialize($acctnum).', expunge only takes a stream arg BUT it operates only on the currently selected folder, NOTE current "folder" arg set for that acct is ['.$note_folder.']<br>'); } 
                  
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): dcom_call: phpgw_expunge: $acctnum: '.serialize($acctnum).' $mailsvr_stream: '.$mailsvr_stream.'<br>'); } 
                  return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->expunge($mailsvr_stream);
            }
            
            /*!
            @function phpgw_delete
            @abstract Delete a message, will move to "Trash" folder is necessary.
            @author Angles
            @param $msg_num (int) single msgnum of msg to "delete" (or move to trash folder) 
            @param $currentfolder (string) full name (as in folder_long) and urlencoded name of the 
            folder from which we are deleting from.
            @param $acctnum (int) (optional) acctnum this applies to
            @param $known_single_delete (boolean) this param is BEING PHASED OUT it was used to take abreviated action 
            if we know this is only a single delete, not just one in a series, BUT this logic has been moved elsewhere. 
            @discussion If the user pref wants to use the Trash folder, this function will auto-create 
            that folder if it does not already exist, and move the mail to that trash folder. If 
            the user pref is to not use a trash folder, or if deleting mail that is IN the trash folder, 
            then a straight delete is done. For both moves to trash or straight deletes, this function eventually 
            calls a sub-function "industrial_interacct_mail_move" to utilize the buffering capability there, 
            which in turn keeps track of folders needing expunging via calls to "track_expungable_folders". 
            BUT NOTE that the calling process IS RESPONSIBLE to call "expunge_expungable_folders" after all delete calls have been called.
            @access public
            */
02539             function phpgw_delete($msg_num, $flags=0, $currentfolder="", $acctnum='', $known_single_delete=False) 
            {
                  if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete: ENTERING <br>'); }
                  
                  if (!(isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  // everything from now on MUST specify this $acctnum
                  
                  // now get the stream that applies to that acctnum
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete: $acctnum: ['.$acctnum.'], $msg_num: ['.$msg_num.']<br>'); }
                                    
                  // this get arg value checks the pref for enabled or not enabled, no need to do it again
                  if ($this->get_isset_arg('verified_trash_folder_long', $acctnum) == False)
                  {
                        $trash_folder_primer = $this->get_arg_value('verified_trash_folder_long', $acctnum);
                        $trash_folder_primer = '';
                        unset($trash_folder_primer);
                  }
                  
                  // -- determine if we are moving to the trash folder or actually deleting the message
                  $trash_folder_long =& $this->_get_arg_ref('verified_trash_folder_long', $acctnum);
                  // if $trash_folder_long is not an ampty string, we need to try to move msgs to it
                  if ($trash_folder_long != '')
                  {
                        // get a clean version 
                        $currentfolder_encoded = $currentfolder;
                        $currentfolder_clean = urldecode($currentfolder);
                        
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): "Trash" folder pref is enabled, does $currentfolder_clean ['.htmlspecialchars($currentfolder_clean).'] equal $trash_folder_long ['.htmlspecialchars($trash_folder_long).'] <br>'); }
                        //echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): "Trash" folder pref is enabled, does ['.$currentfolder.'] == ['.$trash_folder_long.']<br>'; 
                        if ( ($currentfolder_clean != '')
                        && ($currentfolder_clean == $trash_folder_long) )
                        {
                              $straight_delete = True;
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): "Trash" folder pref is enabled, YES to does $currentfolder_clean ['.htmlspecialchars($currentfolder_clean).'] equal $trash_folder_long ['.htmlspecialchars($trash_folder_long).'] <br>'); }
                              //echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): "Trash" folder pref is enabled, shortcut good<br>'; 
                        }
                        else
                        {
                              $straight_delete = False;
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): "Trash" folder pref is enabled, NO to does $currentfolder_clean ['.htmlspecialchars($currentfolder_clean).'] equal $trash_folder_long ['.htmlspecialchars($trash_folder_long).'] <br>'); }
                        }
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): $straight_delete: ['.serialize($straight_delete).'], $currentfolder_clean: ['.htmlspecialchars($currentfolder_clean).'] $trash_folder_long: ['.htmlspecialchars($trash_folder_long).'] <br>'); }
                  }
                  else
                  {
                        $straight_delete = True;
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): $straight_delete: ['.serialize($straight_delete).'] because $trash_folder_long ['.htmlspecialchars($trash_folder_long).'] is empty string<br>'); }
                  }
                  
                  // now that we know if this is a straight delete or not
                  // TAKE ACTION
                  if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): taking action based on info that $straight_delete: ['.serialize($straight_delete).']<br>'); }
                  if ($straight_delete == True)
                  {
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): begin code for STRAIGHT DELETE<br>'); }
                        $mov_msgball = array();
                        if ((isset($currentfolder_encoded))
                        && ((string)$currentfolder_encoded != ''))
                        {
                              // lets trust that current folder is in long form
                              $mov_msgball['folder'] = $currentfolder_encoded;
                        }
                        else
                        {
                              $mov_msgball['folder'] = $this->prep_folder_out();                      
                        }
                        $mov_msgball['acctnum'] = $acctnum;
                        $mov_msgball['msgnum'] = $msg_num;
                        
                        // STRAIGHT DELETE has a "PSUEDO FOLDER" called "##DELETE##"
                        // that we use in the "flush_buffered_move_commmands" to indicate a delete instead of a move
                        // AND we'll use the same "acctnum" as the delete from acctnum because this will group them together during a "sort" of the array
                        // so we can use the same function  for both
                        $to_fldball = array();
                        $to_fldball['acctnum'] = $mov_msgball['acctnum'];
                        $to_fldball['folder'] = $this->del_pseudo_folder;
                        
                        // PUT THIS COMMAND IN THE BUFFERED MOVE (OR DELETE) ARRAY
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): STRAIGHT DELETE: calling $this->industrial_interacct_mail_move($mov_msgball['.serialize($mov_msgball).'],$to_fldball['.serialize($to_fldball).']) <br>'); }
                        $did_take_action = $this->industrial_interacct_mail_move($mov_msgball, $to_fldball);
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): LEAVING, returning $did_take_action ['.serialize($did_take_action).'] (does not really mean anything since we buffer the command) DO NOT FORGET TO EXPUNGE LATER<br>'); }
                        // LEAVING
                        return $did_take_action;
                        
                        /*
                        // BELOW HERE SHOULD GO INTO THE NEW STRAIGHT DELETE BUFFER
                        if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): calling $this->event_msg_move_or_delete()<br>'; }
                        $this->event_msg_move_or_delete($mov_msgball, 'phpgw_delete'.' LINE: '.__LINE__);
                        //$this->expire_session_cache_item('msgball_list', $acctnum);
                        // delete this when we start buffering straight deletes
                        if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): calling $this->ensure_stream_and_folder()<br>'; }
                        $this->ensure_stream_and_folder($mov_msgball, 'phpgw_delete'.' LINE '.__LINE__);
                  
                        if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): getting "mailsvr_stream"<br>'; }
                        $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
                        //return imap_delete($mailsvr_stream,$msg_num);
                        if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): calling GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->delete('.serialize($mailsvr_stream).', '.serialize($msg_num).') <br>'; }
                        $retval = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->delete($mailsvr_stream, $msg_num);
                        if ($retval)
                        {
                              if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): phpgw_delete ('.__LINE__.'): delete *SUCCESS*, now add this folder to this accounts "expunge_folders" arg via "track_expungable_folders"<br>'; } 
                              $this->track_expungable_folders($mov_msgball);
                        }
                        if ($this->debug_wrapper_dcom_calls > 0) { echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): EXITING with $retval ['.serialize($retval).'] DO NOT FORGET TO EXPUNGE LATER<br>'; }
                        // LEAVING
                        return $retval;
                        */
                  }
                  else
                  {
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): begin do move to trash folder<br>'); }
                        $mov_msgball = array();
                        if ((isset($currentfolder_encoded))
                        && ((string)$currentfolder_encoded != ''))
                        {
                              // lets trust that current folder is in long form (and encoded - I guess we like it that way? it came in here that way)
                              $mov_msgball['folder'] = $currentfolder_encoded;
                        }
                        else
                        {
                              $mov_msgball['folder'] = $this->prep_folder_out();                      
                        }
                        $mov_msgball['acctnum'] = $acctnum;
                        $mov_msgball['msgnum'] = $msg_num;
                        // destination Trash Folder
                        $to_fldball = array();
                        $to_fldball['folder'] = $trash_folder_long;
                        $to_fldball['acctnum'] = $acctnum;
                        // this event MOVED to flush command
                        //if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): calling $this->event_msg_move_or_delete<br>'; }
                        //$this->event_msg_move_or_delete($mov_msgball, 'phpgw_delete'.' LINE: '.__LINE__, $to_fldball);
                        //$this->expire_session_cache_item('msgball_list', $acctnum);
                        
                        //if ($known_single_delete == True)
                        //{
                        //    // we were told this is just a SINGLE delete call, NOT multiple deletes involved
                        //    if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): $known_single_delete: ['.serialize($known_single_delete).'] so calling $this->single_interacct_mail_move($mov_msgball['.serialize($mov_msgball).'],$to_fldball['.serialize($to_fldball).']) <br>'; }
                        //    $did_move = $this->single_interacct_mail_move($mov_msgball, $to_fldball);
                        //}
                        //else
                        //{
                              // most (WAS) likely multiple deletes, so use the command that buffers the moves
                              // this logic concerning single or not has been moved elsewhere
                              if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): $known_single_delete: ['.serialize($known_single_delete).'] so calling $this->industrial_interacct_mail_move($mov_msgball['.serialize($mov_msgball).'],$to_fldball['.serialize($to_fldball).']) <br>'); }
                              $did_move = $this->industrial_interacct_mail_move($mov_msgball, $to_fldball);
                        //}
                        if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): $did_move: ['.serialize($did_move).'], does not mean unless you called $this->single_interacct_mail_move()<br>'); }
                        
                        if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete ('.__LINE__.'): LEAVING, returning $did_move ['.serialize($did_move).'] DO NOT FORGET TO EXPUNGE LATER<br>'); }
                        return $did_move;
                  }
            }
            
            
            /*!
            @function get_verified_trash_folder_long
            @abstract SPECIAL HANDLER for use by "get_arg_value" for "verified_trash_folder_long"
            @param (int) the account number OPTIONAL if not supplied current acctnum is used
            @result (string) folder long name of trash folder, or empty string on failure
            @discussion First there is a pref called "trash_folder_name" that needs some processing 
            because .1. the name is stored in user friendly form, is not known to be authentic server 
            string folder long style, and .2. if the folder does not yet exist we must make it. This function 
            does that processing, the result of this is put into arg value "verified_trash_folder_long". So 
            if you need to know about the trash folder, use get_arg_value("verified_trash_folder_long") and 
            this is all transparent to you. **CACHE NOTE** this value is stored in LEVEL 1 temporary cache 
            for the duration of the script run, perhaps later we could put in appsession cache but if so then it 
            must be expired if new prefs are submited.
            @author Angles
            */
02712             function get_verified_trash_folder_long($acctnum='')
            {
                  if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: ENTERING<br>'); }
                  
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_verified_trash_folder_long('.__LINE__.'): after testing feed arg, using $acctnum: ['.$acctnum.']<br>'); }
                  
                  // L1 (temporary) CACHED data available ?
                  $class_cached_verified_trash_folder_long = $this->_direct_access_arg_value('verified_trash_folder_long', $acctnum);
                  if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_verified_trash_folder_long('.__LINE__.'): check for L1 class var cached data: $this->_direct_access_arg_value(verified_trash_folder_long, '.$acctnum.'); returns: '.serialize($class_cached_verified_trash_folder_long).'<br>'); }
                  if ((isset($class_cached_verified_trash_folder_long))
                  && ($class_cached_verified_trash_folder_long != ''))
                  {
                        // return the cached data
                        if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_verified_trash_folder_long('.__LINE__.'): LEAVING, returned class var cached data: '.serialize($class_cached_verified_trash_folder_long).'<br>'); }
                        return $class_cached_verified_trash_folder_long;
                  }
                  // NO CACHED data, continue ...
                  
                  // does the mailserver have folders, if not then there is NO trash folder no matter what
                  if ($this->get_mailsvr_supports_folders($acctnum) == False)
                  {
                        if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: LEAVING, mailserver does NOT support folders, so storing and returning empty string<br>'); } 
                        // exit, trash folder pref is NOT TO USE ONE, so we certainly do not have a "verified" name in this case
                        $this->set_arg_value('verified_trash_folder_long', '', $acctnum);
                        return '';
                  }
                  
                  // are we even supposed to use a trash folder
                  if ( (!$this->get_isset_pref('use_trash_folder', $acctnum))
                  || (!$this->get_pref_value('use_trash_folder', $acctnum)) )
                  {
                        if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: LEAVING, user does NOT prefer to use a trash folder, so storing and returning empty string<br>'); }
                        // exit, trash folder pref is NOT TO USE ONE, so we certainly do not have a "verified" name in this case
                        $this->set_arg_value('verified_trash_folder_long', '', $acctnum);
                        return '';
                  }
                  
                  // does the trash folder actually exist ?
                  if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: humm... does the "Trash" folder actually exist :: this->get_pref_value("trash_folder_name", '.$acctnum.') = ['.htmlspecialchars($this->get_pref_value('trash_folder_name', $acctnum)).']<br>'); }
                  $verified_trash_folder_long = $this->folder_lookup('', $this->get_pref_value('trash_folder_name', $acctnum));
                  if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: did lookup on pref value for "Trash" folder, got $verified_trash_folder_long ['.htmlspecialchars($verified_trash_folder_long).']<br>'); }
                  if ((isset($verified_trash_folder_long))
                  && ($verified_trash_folder_long != ''))
                  {
                        $havefolder = True;
                  }
                  else
                  {
                        $havefolder = False;
                  }
                  
                  if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: "Trash" folder $havefolder so far is ['.serialize($havefolder).']<br>'); }
                  
                  if (!$havefolder)
                  {
                        if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: we have to create the "Trash" folder so it will exist<br>'); }
                        // create the Trash folder so it will exist (Netscape does this too)
                        //$mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
                        $namespace = $this->get_arg_value('mailsvr_namespace', $acctnum);
                        $delimiter = $this->get_arg_value('mailsvr_delimiter', $acctnum);
                        $make_this_folder_clean = $namespace.$delimiter.$this->get_pref_value('trash_folder_name', $acctnum);
                        $make_this_folder_encoded = $this->prep_folder_out($make_this_folder_clean);
                        //$this->createmailbox($mailsvr_stream,$server_str .$trash_folder_long);      
                        //$this->phpgw_createmailbox("$server_str"."$trash_folder_long");
                        $fake_fldball = array();
                        $fake_fldball['folder'] = $make_this_folder_encoded;
                        $fake_fldball['acctnum'] = $acctnum;
                        if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: calling $this->phpgw_createmailbox_ex('.serialize($fake_fldball).') <br>'); }
                        //$did_create = $this->phpgw_createmailbox($fake_fldball);
                        $did_create = $this->phpgw_createmailbox_ex($fake_fldball);
                        if (!$did_create)
                        {
                              $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: UNABLE TO CREATE ['.htmlspecialchars(serialize($fake_fldball)).']');
                              echo 'mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: UNABLE TO CREATE ['.htmlspecialchars(serialize($fake_fldball)).']';
                        }
                        if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: phpgw_createmailbox  returns $did_create ['.serialize($did_create ).'] <br>'); }
                        
                        // try again to get the real long folder name of the just created trash folder
                        $verified_trash_folder_long = $this->folder_lookup('', $this->get_pref_value('trash_folder_name', $acctnum));
                        if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: Another lookup on pref value for "Trash" folder, got $verified_trash_folder_long ['.htmlspecialchars($verified_trash_folder_long).']<br>'); }
                        // did the folder get created and do we now have the official full name of that folder?
                        if ($verified_trash_folder_long != '')
                        {
                              $havefolder = True;
                        }
                        if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: Another check of "Trash" folder $havefolder so far is ['.serialize($havefolder).']<br>'); }
                  }
                  
                  if ($havefolder == False)
                  {
                        // FAILED to find or make trash folder, return empty string
                        $verified_trash_folder_long = '';
                  }
                  else
                  {
                        // SUCCESS, put the result in L1 (page view only) cache
                        // cache the result in "level one cache" class var holder
                        if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_verified_trash_folder_long('.__LINE__.'): set "level 1 cache, class var" arg $this->set_arg_value(verified_trash_folder_long, '.$verified_trash_folder_long.', '.$acctnum.']) <br>'); } 
                        $this->set_arg_value('verified_trash_folder_long', $verified_trash_folder_long, $acctnum);
                        // LATER put it in appsession cache BUT make code to delete it from cache when submitting new prefs
                  }
                  if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg(_wrappers): get_verified_trash_folder_long('.__LINE__.'): LEAVING, returning $verified_trash_folder_long ['.serialize($verified_trash_folder_long).']<br>'); }
                  return $verified_trash_folder_long;
            }
            
            /**************************************************************************\
            * END DCOM WRAPERS                                                *
            * - - - - - - - - - - - - - - - - - - - - - - - - -                           *
            * BEGIN INPUT ARG/PARAM HANDLERS                *
            \**************************************************************************/
            
            /*!
            @function is_ball_data
            @abstract Quick test an array to see if has the elements a fldball needs to be considered a fldball, 
            or a msgball has the minumum elements to be a msgball. 
            @param $maybe_ball (array of type fldball or msgball) OR string of URI type representation of a fldball 
            or msgabll array, depends on the next param. This data is what we test this to see if it is ball data. 
            @param $expect_ball_type (known string) either "fldball" or "msgball" or "any" what we expect the 
            inpout data is supposed to be. Test above param to see if it is either a fldball or a msgball as we 
            specify here. Value of "any" means do not do strict test, just a general test meeting fldball 
            minimum criteria, but it could also be a msgball and still produce a True result. 
            @param $is_uri_type_string (boolean) default is False, set to true if the above param is not an 
            array but is known to be in URI string form. To test accurately this param is needed. 
            NOTE that the test on this param is empty is the same as False and not empty is the same as True. 
            However empty does not mean "not set", in other words an empty string is still something that 
            "is set", it does exist as a data item. 
            @result Boolean True if param tests OK for being a fldball, False otherwise. 
            @discussion Pass an ARRAY into here unless you pass the second param as True, then 
            you must pass string URI representation of a fldball. 
            BACKGROUND there are 2 ways we hold fldball (and msgball) 
            data, they are similar and can convert between one another. First is the actual associative array that 
            has the elements needed to make a fldball, at a minimum "acctnum" and "folder". Second is a 
            URI type string that is the URI representation of that array, such that is passed in a GET uri 
            which php would translate into an array. This function expects the array form. NOTE that 
            the php parse_str command will always add ot strip slashes during this conversion, als GPC magic_quotes, 
            no matter what the setting for magic quotes is set to. Remember this because that can change the 
            folder name into something with additional slashes in some cases. 
            @author Angles
            @access Public
            */
02857             function is_ball_data($maybe_ball='##NOTHING##', $expect_ball_type='any', $is_uri_type_string='')
            {
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): ENTERING<br>'); } 
                  if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): param $maybe_ball ['.htmlspecialchars(serialize($maybe_ball)).'], param $expect_ball_type ['.$expect_ball_type.'], param $is_uri_type_string ['.htmlspecialchars(serialize($is_uri_type_string)).']  <br>'); } 
                  // sanity check on input data
                  if ((!isset($maybe_ball))
                  || ($maybe_ball == $this->nothing)
                  || (!isset($expect_ball_type))
                  || (!isset($is_uri_type_string)))
                  {
                        if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): LEAVING with error, input params error, returning False <br>'); } 
                        return False;
                  }
                  elseif (($expect_ball_type != 'fldball')
                  && ($expect_ball_type != 'msgball')
                  && ($expect_ball_type != 'any'))
                  {
                        if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): LEAVING with error, param $expect_ball_type ['.htmlspecialchars(serialize($expect_ball_type)).'], is neither "fldball" nor "msgball" nor "any" , returning False <br>'); } 
                        return False;
                  }
                  // baseline data
                  $test_results = array();
                  $test_results['verified_is_fldball'] = False;
                  $test_results['verified_is_msgball'] = False;
                  $test_results['final_verdict'] = False;
                  // convert to array if necessary
                  if ($is_uri_type_string)
                  {
                        $maybe_ball_uri = $maybe_ball;
                        $maybe_ball = $this->ball_data_parse_str($maybe_ball_uri);
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): needed to converted $maybe_ball_uri ['.htmlspecialchars(serialize($maybe_ball_uri)).'] to ball data array $maybe_ball ['.htmlspecialchars(serialize($maybe_ball)).'] <br>'); } 
                  }
                  // easiest test, is it a fldball
                  if ((isset($maybe_ball['folder']))
                  && ((string)$maybe_ball['folder'] != '')
                  && (isset($maybe_ball['acctnum']))
                  && ((string)$maybe_ball['acctnum'] != ''))
                  {
                        if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): input data DOES have minimun data of a FLDBALL, so generically it is "ball data", but also do the "msgnum" test, we may need it later. <br>'); } 
                        $test_results['verified_is_fldball'] = True;
                  }
                  
                  // additional test for msgball type ball data
                  // only need to test this if the fldball criteria were met, if not it certainly can not be a msgball
                  if (($test_results['verified_is_fldball'] == True)
                  && (isset($maybe_ball['msgnum']))
                  && ((string)$maybe_ball['msgnum'] != ''))
                  {
                        $test_results['verified_is_msgball'] = True;
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): input data is a MSGBALL, so it is not a FLDBALL, but is "ball data" in the generic sence<br>'); } 
                  }
                  //make a determination
                  if (($expect_ball_type == 'any')
                  && ($test_results['verified_is_fldball'] == True))
                  {
                        // testing for "any" type of ball data is True if minimal criteria of fldball are met
                        $test_results['final_verdict'] = True;
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): testing for '.$expect_ball_type.' data and input data meets minimum criteria for FLDBALL<br>'); } 
                  }
                  elseif (($expect_ball_type == 'fldball')
                  && ($test_results['verified_is_fldball'] == True)
                  && ($test_results['verified_is_msgball'] == False))
                  {
                        // strictly speaking, data is NOT a fldball if it ALSO has a msgnum, if using a strict match, not "any" ball
                        // if the data ALSO HAS a msgnum, is is NOT a fldball, it is a msgball in the strict sence. 
                        $test_results['final_verdict'] = True;
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): expected a '.$expect_ball_type.' and input data is a FLDBALL, and dod NOT have msgball element(s) <br>'); } 
                  }
                  elseif (($expect_ball_type == 'msgball')
                  && ($test_results['verified_is_fldball'] == True)
                  && ($test_results['verified_is_msgball'] == True))
                  {
                        $test_results['final_verdict'] = True;
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): expected a '.$expect_ball_type.' and input data is a MSGBALL<br>'); } 
                  }
                  else
                  {
                        $test_results['final_verdict'] = False;
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): expected a '.$expect_ball_type.' but input data does not match that or is not ball data at all.<br>'); } 
                  }
                  // return the result
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): LEAVING, returning $test_results[final_verdict] of ['.htmlspecialchars(serialize($test_results['final_verdict'])).']<br>'); } 
                  return $test_results['final_verdict'];
            }
            
            /*!
            @function ball_data_parse_str
            @abstract used for fldball and msgball data, apply php command parse string then apply stripslashes on folder names. 
            @param $uri_ball_data (str) string in the style of a URI such as "msgball[msgnum]=2&msgball[folder]=INBOX&msgball[acctnum]=5"
            @param $do_stripslashes boolean default is True, whether to apply stripslashes to folder values, extremely rare to change this
            @result associative array of type msgball or fldball, which ever was fed as param  $uri_type_string
            @discussion php command parse_str will ALWAYS add slashes to single and double quotes, just like GPC magic quotes 
            except that even if you disable GPC magic quotes, parse_str will STILL add that escape slash to any quotes. This means 
            that folder names in URI type data that we wish to decode with parse_str will ALSO need to be stripslashed 
            or else any folder names with quotes in the, which is legal as per RFC 2060, suh folder names will not match 
            our known list of folder names because these slashes will have been added to the folder name, if it contains quotes. 
            Therefor this function is designed to decode fldball and msgball data and also stripslash the folder names that 
            this function can find. USE FOR FLDBALL AND MSGBALL DATA ONLY because this function only looks 
            for folder names where they would exist in a fldball or magball array, IT WILL NOT stripslash foldernames 
            of any other type of data because it does not look for the folder name anywhere else.
            @author Angles
            @access Public
            */
02960             function ball_data_parse_str($uri_ball_data='', $do_stripslashes=True)
            {
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: ball_data_parse_str('.__LINE__.'): ENTERING<br>'); } 
                  if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: ball_data_parse_str('.__LINE__.'): param $uri_ball_data ['.$uri_ball_data.']<br>'); } 
                  $return_struct = array();
                  if (!$uri_ball_data)
                  {
                        return $return_struct;
                  }
                  parse_str($uri_ball_data, $return_struct);
                  // RAISE UP one level, we want the return to be the msgball or fldball itself
                  if (stristr($uri_ball_data,'fldball[folder]'))
                  {
                        $return_struct = $return_struct['fldball'];
                  }
                  elseif (stristr($uri_ball_data,'msgball[folder]'))
                  {
                        $return_struct = $return_struct['msgball'];
                  }
                  else
                  {
                        if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: ball_data_parse_str('.__LINE__.'): UNHANDLED and unexpected if .. then data <br>'); }
                  }
                  // add to this data a "uri" element that is the $uri_ball_data
                  //if (isset($return_struct['uri']) == False)
                  //{
                        //$return_struct['uri'] = $uri_ball_data;
                  //}
                  if ($do_stripslashes == True)
                  {
                        // this function is DUMB it only looks here for a folder name
                        if (isset($return_struct['folder']))
                        {
                              $return_struct['folder'] = stripslashes($return_struct['folder']);
                        }
                  }
                  // we always keep folder data in urlencoded form until the last second
                  $return_struct['folder'] = $this->prep_folder_out($return_struct['folder']);
                  //rebuild the uri string
                  $return_struct['uri'] = 'msgball[msgnum]='.$return_struct['msgnum'].'&msgball[folder]='.$return_struct['folder'].'&msgball[acctnum]='.$return_struct['acctnum'];
                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: ball_data_parse_str('.__LINE__.'): final $return_struct DUMP:', $return_struct); } 
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: ball_data_parse_str('.__LINE__.'): LEAVING<br>'); } 
                  return $return_struct;
            }
            
            /*!
            @function decode_fake_uri
            @abstract decodes a URI type "query string" into an associative array
            @param $uri_type_string string in the style of a URI such as "&item=phone&action=dial"
            @result associative array where the $key and $value are exploded from the uri like [item] => "phone"
            @discussion HTML select "combobox"s can only return 1 "value" per item, to break that limitation you 
            can use that 1 item like a "fake URI", meaning you make a single string store structured data 
            by using the standard syntax of a HTTP GET URI, see the example
            @example HTTP GET URI, example
            < select name="fake_uri_data" > < option value="&item=phone&action=dial&touchtone=1" > ( ... etc ... )
            repeat with html entities for the doc parser 
            &lt; select name="fake_uri_data" &gt; &lt; option value="&item=phone&action=dial&touchtone=1" &gt; ( ... etc ... )
            In an HTTP POST event, this would appear as in the example
            $this->ref_POST["fake_uri_data"] => "&item=phone&action=dial&touchtone=1"
            Then you feed that string into this function and you get back an associave array like this
            return["item"] => "phone"
            return["action"] => "dial"
            return["touchtone"] => "1"
            NOTE: this differs from PHP's parse_str() because this function  will NOT attempt to decode the urlencoded values.
            In this way you may store many data elements in a single HTML "option" value=" " tag.
            @author Angles
            @access Public
            */
            function decode_fake_uri($uri_type_string='', $raise_up=False)
            {
                  /*
                  $fake_url_b = explode('&', $uri_type_string);
                  if ($this->debug_args_input_flow > 2) { echo 'mail_msg: decode_fake_uri: $fake_url_b = explode("&", '.$uri_type_string.') dump:<pre>'; print_r($fake_url_b); echo '</pre>'; }
                  
                  $fake_url_b_2 = array();
                  while(list($key,$value) = each($fake_url_b))
                  {
                        $explode_me = trim($fake_url_b[$key]);
                        if ((string)$explode_me != '')
                        {
                              $exploded_parts = explode('=', $explode_me);
                              $fake_url_b_2[$exploded_parts[0]] = $exploded_parts[1];
                        }
                  }
                  if ($this->debug_args_input_flow > 2) { echo 'mail_msg: decode_fake_uri: $fake_url_b_2 (sub parts exploded and made into an associative array) dump:<pre>'; print_r($fake_url_b_2); echo '</pre>'; }
                  return $fake_url_b_2;
                  */
                  
                  $embeded_data = array();
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): ENTERED $uri_type_string ['.$uri_type_string.'] <br>'); }
                  parse_str($uri_type_string, $embeded_data);
                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): parse_str('.$uri_type_string.', into $embeded_data DUMP:', $embeded_data); }
                  
                  // NOTE PARSE_STR ***WILL ADD SLASHES*** TO ESCAPE QUOTES
                  // NO MATTER WHAT YOUR MAGIC SLASHES SETTING IS
                  if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): NOTE PARSE_STR ***WILL ADD SLASHES*** TO ESCAPE QUOTES NO MATTER WHAT YOUR MAGIC SLASHES SETTING IS **stripping slashes NOW***'); } 
                  if (isset($embeded_data['folder']))
                  {
                        $embeded_data['folder'] = stripslashes($embeded_data['folder']);
                  }
                  if (isset($embeded_data['msgball']['folder']))
                  {
                        $embeded_data['msgball']['folder'] = stripslashes($embeded_data['msgball']['folder']);
                  }
                  
                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): post "stripslashes" parse_str('.$uri_type_string.', into $embeded_data DUMP:', $embeded_data); }
                  
                  // some embeded uri-faked data needs to be raised up one level from sub-elements to top level
                  if ($raise_up)
                  {
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): attempt to raise up data one level in the array <br>'); }
                        $count_embeded = count($embeded_data);
                        if ($count_embeded == 1)
                        {
                              @reset($embeded_data);
                              $new_top_level = array();
                              while(list($key,$value) = each($embeded_data))
                              {
                                    $new_top_level = $embeded_data[$key];
                                    //break;
                              }
                              // re-urlencode folder names, and make acctnum 's integers
                              /*
                              // NOT NECESSARY HERE
                              if ((is_array($new_top_level))
                              && (count($new_top_level) > 0))
                              {
                                    $loops = count($new_top_level);
                                    for($i=0;$i<$loops;$i++)
                                    {
                                          // re-urlencode folder names, because "prep_folder_in" is supposed to be where it gets urldecoded
                                          if ((isset($this_array_item[$i]['folder']))
                                          && ((string)$this_array_item[$i]['folder'] != ''))
                                          {
                                                $re_urlencoded_folder = urlencode($this_array_item[$i]['folder']);
                                                if ($this->debug_args_input_flow > 1) { echo 'mail_msg: decode_fake_uri: re-urlencode (hopefully) folder element $this_array_item['.$i.'][folder] from ['.$this_array_item[$i]['folder'].'] into ['.$re_urlencoded_folder.'] <br>'; }
                                                $this_array_item[$i]['folder'] = $re_urlencoded_folder;
                                          }
                                          if ((isset($this_array_item[$i]['acctnum']))
                                          && ((string)$this_array_item[$i]['acctnum'] != ''))
                                          {
                                                $make_int_acctnum = (int)$this_array_item[$i]['acctnum'];
                                                if ($this->debug_args_input_flow > 1) { echo 'mail_msg: decode_fake_uri: $make_int_acctnum (hopefully) acctnum element $this_array_item['.$i.'][acctnum] from ['.serialize($this_array_item[$i]['acctnum']).'] into ['.serialize($make_int_acctnum).'] <br>'; }
                                                $this_array_item[$i]['acctnum'] = $make_int_acctnum;
                                          }
                                    }
                              }
                              */
                              // replace result with $new_top_level
                              $embeded_data = $new_top_level;
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): raise embeded up to $new_top_level DUMP:', $new_top_level); }
                        }
                        else
                        {
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): original result had more than one element, can not raise <br>'); }
                        }
                  }
                  // parse_str will "urldecode" the folder string, we need to re-urlencode it, 
                  // because "prep_folder_in" is supposed to be where it gets urldecoded
                  while(list($key,$value) = each($embeded_data))
                  {
                        if ((strstr($key, 'folder'))
                        && ((string)$embeded_data[$key] != ''))
                        {
                              //$re_urlencoded_folder = urlencode($embeded_data[$key]);
                              $re_urlencoded_folder = $this->prep_folder_out($embeded_data[$key]);
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): re-urlencode (hopefully) folder element $embeded_data['.$key.'] from ['.$embeded_data[$key].'] into ['.$re_urlencoded_folder.'] <br>'); }
                              $embeded_data[$key] = $re_urlencoded_folder;
                        }
                        elseif ((strstr($key, 'acctnum'))
                        && ((string)$embeded_data[$key] != ''))
                        {
                              $make_int_acctnum = (int)$embeded_data[$key];
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): $make_int_acctnum (hopefully) acctnum element $embeded_data['.$key.'] from ['.serialize($embeded_data[$key]).'] into ['.serialize($make_int_acctnum).'] <br>'); }
                              $embeded_data[$key] = $make_int_acctnum;
                        }
                  }

                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): final $embeded_data (sub parts made into an associative array) DUMP:', $embeded_data); }
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): LEAVING <br>'); }
                  return $embeded_data;
            }
            
            /*!
            @function grab_class_args_gpc
            @abstract grab data from $this->ref_POST and $this->ref_GET, looking for var names 
            listed in the $this->known_external_args[] array, and fill various class arg variables with the available data. 
            @result none, this is an object call
            @discussion to further seperate the mail functionality from php itself, this function will perform
            the variable handling of the traditional php page view Get Post Cookie (no cookie data used here though)
            The same data could be grabbed from any source, XML-RPC for example, insttead of the php GPC vars, 
            so this function could (should) have an equivalent XML-RPC version to handle filling these class variables 
            from an alternative source. This function looks for all the var names listed in the 
            $this->known_external_args[] array. Therefor, by adding something to that array, it will be looked 
            for here. This is similar to the new phpgwapi "get_var" function, but does less syntax checking (validation), and 
            has code to handle URI type data that could be embedded in certain GPC vars that we look for. Some validation 
            is done here, but certainly more could be added using the "get_var" in the future. 
            UPDATE re RH8 php+apache2 bug. UPDATE this function 
            now handles 2 bugs associated with RH8 php with apache2. The FIRST bug is with POST data designed 
            to be a simple array when POSTed and read by php, this is the un-numbered array, not associative, the 
            type of array you get with a succession of "some_array[] = X+1" calls in php code, and with form data 
            where the key could be "some_array[]" and the value might be depenant on a check box, for example. This 
            particular type of array can have the buggy behavior of having TWO of every element. Typically, the array 
            when dumped will show the desired array, but then also have that same array again starting at the next 
            element after the real array is done. So really you get an array that is really 2 sets of itself all in one array. 
            The SECOND bug is where certain POSTed "key=value" string can get erronously added an element 
            of an array from the same form, but that is supposed to be a seperate peice of data. That erronously 
            added item will have its real "key=value" pair in the real place it should be AND also tacked on to 
            one of the POSTed array items, where these items are strings. There were a few places in this function 
            that required cleaning the POSTed data of these 2 types of bugs. 
            FURTHER UPDATE these 2 bugs in php have been fixed so we commented out those bug workarounds.
            @author Angles
            @access Public
            */
03174             function grab_class_args_gpc()
            {
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): ENTERING<br>'); }
                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): $this->ref_POST DUMP:', $this->ref_POST); }
                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): $this->ref_GET DUMP:', $this->ref_GET); }
                  
                  // ----  extract any "fake_uri" embedded data from HTTP_POST_VARS  ----
                  // note: this happens automatically for HTTP_GET_VARS 
                  // NOTE this WILL ALTER $_POST inserting processed values for later use (could this be avoided?)
                  if (is_array($this->ref_POST))
                  {
                        foreach($this->ref_POST as $key => $value)
                        {
                              if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): looking for "_fake_uri" token in HTTP_POST_VARS ['.$key.'] = '.$this->ref_POST[$key].'<br>'); }
                              /*
                              // THERE IS NO WAY THIS EARLY VERSION PHP BUG CAN STILL EXIST
                              if ($key == 'delmov_list')
                              {
                                    if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): FOUND "delmov_list_fake_uri" needs decoding HTTP_POST_VARS['.$key.'] = ['.$this->ref_POST[$key].'] <br>'); }
                                    // apache2 on test RH8.0 box submits "delmov_list" array with duplicate items in it, track this
                                    $seen_delmov_list_items=array();
                                    $sub_loops = count($this->ref_POST[$key]);
                                    for($i=0;$i<$sub_loops;$i++)
                                    {
                                          // bug2: apache2 on test RH8.0 box submits "delmov_list" array with "what=delall" tacked on to one of the array items
                                          if (strstr($this->ref_POST[$key][$i], 'what=delall'))
                                          {
                                                if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): (apache2 bug2) **fixing** "delmov_list" array has string "what=delall" added to one item ['.$this->ref_POST[$key][$i].'] <br>'); } 
                                                $this->ref_POST[$key][$i] = str_replace('what=delall', '', $this->ref_POST[$key][$i]);
                                          }
                                          
                                          // bug1: apache2: do duplicate test on the "delmov_list" array items
                                          if (in_array($this->ref_POST[$key][$i], $seen_delmov_list_items) == True)
                                          {
                                                if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): <u>unsetting</u> and *skipping* duplicate (buggy apache2) "delmov_list" array item ['.$this->ref_POST[$key][$i].'] <br>'); }
                                                $this->ref_POST[$key][$i] = '';
                                                // can I UNSET this and have the next $i index item actually be the next one
                                                // YES, a) array count calculated before loop, and b) does not squash array to unset an item
                                                unset($this->ref_POST[$key][$i]);
                                                //array_splice($this->ref_POST[$key], $i, 1);
                                                // NOTE USE OF CONTINUE COMMAND HERE!
                                                // we do not increase $ii because the next array item just fell into the current slot
                                                continue;
                                          }
                                          else
                                          {
                                                // track seen items for duplicate test
                                                if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): good (not duplicate, not buggy apache2) "delmov_list" array item ['.$this->ref_POST[$key][$i].'] <br>'); }
                                                $tmp_next_idx = count($seen_delmov_list_items);
                                                $seen_delmov_list_items[$tmp_next_idx] = $this->ref_POST[$key][$i];
                                          }
                                          // if we get here, it is not duplicate, go ahead
                                          $sub_embedded_data = array();
                                          // True = attempt to "raise up" embedded data to top level
                                          $sub_embedded_data = $this->decode_fake_uri($this->ref_POST[$key][$i], True);
                                          $this->ref_POST[$key][$i] = $sub_embedded_data;
                                    }
                                    // increment our shadow iteation count
                                    if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): decoded ARRAY "_fake_uri" data: HTTP_POST_VARS['.$key.'] data DUMP:', $this->ref_POST[$key]); }
                              }
                              else
                              */
                              if (strstr($key, '_fake_uri'))
                              {
                                    // right now these are possible "_fake_uri" items:
                                    // fldball_fake_uri
                                    // to_fldball_fake_uri
                                    // source_fldball_fake_uri
                                    if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): FOUND "_fake_uri" token in HTTP_POST_VARS['.$key.'] = ['.$this->ref_POST[$key].'] <br>'); }
                                    $embedded_data = array();
                                    $embedded_data = $this->decode_fake_uri($this->ref_POST[$key]);
                                    // Strip "_fake_uri" from $key and insert the associative array into HTTP_POST_VARS
                                    $new_key = str_replace('_fake_uri', '', $key);
                                    if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): embedded "_fake_uri" data will be inserted into POST VARS with key name: ['.$new_key.'] = ['.$this->ref_POST[$key].'] <br>'); }
                                    $this->ref_POST[$new_key] = array();
                                    $this->ref_POST[$new_key] = $embedded_data;
                                    if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): decoded "_fake_uri" data: HTTP_POST_VARS['.$new_key.'] data DUMP:', $this->ref_POST[$new_key]); }
                              }
                              // use this original "delmov_list" code without the RH8 bug check
                              elseif ($key == 'delmov_list')
                              {
                                    if ($this->debug_args_input_flow > 1) { echo 'mail_msg: grab_class_args_gpc: FOUND "delmov_list" needs decoding HTTP_POST_VARS['.$key.'] = ['.$this->ref_POST[$key].'] <br>'; }
                                    $sub_loops = count($this->ref_POST[$key]);
                                    for($i=0;$i<$sub_loops;$i++)
                                    {
                                          $sub_embedded_data = array();
                                          // True = attempt to "raise up" embedded data to top level
                                          $sub_embedded_data = $this->decode_fake_uri($this->ref_POST[$key][$i], True);
                                          $this->ref_POST[$key][$i] = $sub_embedded_data;
                                    }
                                    if ($this->debug_args_input_flow > 2) { echo 'mail_msg: grab_class_args_gpc: decoded ARRAY "_fake_uri" data: HTTP_POST_VARS['.$key.'] data dump: <pre>'; print_r($this->ref_POST[$key]); echo '</pre>'; }
                              }
                        }
                  }
                  
                  $got_args = array();
                  // insert *known* external args we find into $got_args[], then return that data
                  if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): about to loop thru $this->known_external_args<br>'); }
                  $loops = count($this->known_external_args);
                  for($i=0;$i<$loops;$i++)
                  {
                        $this_arg_name = $this->known_external_args[$i];
                        //if ($this->debug_args_input_flow > 2) { $this->dbug->out(' * * (grab pref - external) $this_arg_name: ['.$this_arg_name.']<br>'); }
                        if (isset($this->ref_POST[$this_arg_name]))
                        {
                              if ($this->debug_args_input_flow> 2) { $this->dbug->out(' * * (grab pref - external)('.__LINE__.') $this->ref_POST['.$this_arg_name.'] IS set to ['.$this->ref_POST[$this_arg_name].']<br>'); }
                              $got_args[$this_arg_name] = $this->ref_POST[$this_arg_name];
                        }
                        elseif (isset($this->ref_GET[$this_arg_name]))
                        {
                              if ($this->debug_args_input_flow > 2) { $this->dbug->out(' * * (grab pref - external)('.__LINE__.') $this->ref_GET['.$this_arg_name.'] IS set to ['.serialize($this->ref_GET[$this_arg_name]).']<br>'); }
                              $got_args[$this_arg_name] = $this->ref_GET[$this_arg_name];
                              
                              // STRIPSLASHES IF NEEDED
                              if (($this_arg_name == 'msgball')
                              || ($this_arg_name == 'fldball'))
                              {
                                    // php will automayically urldecode the folder, we don't like this
                                    // AND PHP MIGHT ADD GPC SLASHES WE DO NOT WANT!!!!! depending on your ini settings
                                    // use custom "stripslashes_gpc" function here because the slashes at this point would have been added ONLY by php itself
                                    // i.e. we have not yet used "parse_str" yet, that always addes slashes, but not using it yet means any slashes are GPC slashes
                                    $not_urlencoded_but_stripslashed_folder = $this->stripslashes_gpc($got_args[$this_arg_name]['folder']);
                                    // now we have no unwanted slashes, so put this back in the input args
                                    if ($this->debug_args_input_flow > 2) { $this->dbug->out(' * * (grab pref - external)('.__LINE__.'): msgball[folder] STRIPSLASH watch: $got_args[$this_arg_name]["folder"]: ['.htmlspecialchars($got_args[$this_arg_name]['folder']).']; $not_urlencoded_but_stripslashed_folder: ['.htmlspecialchars($not_urlencoded_but_stripslashed_folder).']<br>'); }
                                    $got_args[$this_arg_name]['folder'] = $not_urlencoded_but_stripslashed_folder;
                                    $not_urlencoded_but_stripslashed_folder = '';
                                    unset($not_urlencoded_but_stripslashed_folder);
                              }
                              // ADD "uri" element to incoming "msgball" arg
                              // so forms may pass this "msgball" on to the next page view
                              if ($this_arg_name == 'msgball')
                              {
                                    // php will automayically urldecode the folder, we don't like this
                                    // we want the folder value to be URLENCODED, so we need to re-urlencode it
                                    $re_urlencoded_folder = $this->prep_folder_out($got_args[$this_arg_name]['folder']);
                                    $got_args[$this_arg_name]['folder'] = $re_urlencoded_folder;
                                    $got_args[$this_arg_name]['uri'] = 
                                          'msgball[msgnum]='.$got_args[$this_arg_name]['msgnum']
                                          .'&msgball[folder]='.$got_args[$this_arg_name]['folder']
                                          .'&msgball[acctnum]='.$got_args[$this_arg_name]['acctnum'];
                                    if ($this->debug_args_input_flow > 2) { $this->dbug->out(' * * (grab pref - external)('.__LINE__.') made msgball URI, added it to msgball[]: DUMP:', $got_args[$this_arg_name]); } 
                              }
                        }
                        else
                        {
                              if ($this->debug_args_input_flow > 2) { $this->dbug->out(' * (grab pref - external)('.__LINE__.') neither POST nor GET vars have this item set ['.$this_arg_name.'] <br>'); }
                        }
                  }
                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): post-loop (external args) $got_args[] DUMP:', $got_args); }
                  
                  // in order to know wgat account's arg array to insert $got_args[] into, we need to determine what account 
                  // we are dealing with before we can call $this->set_arg_array or "->get_isset_arg" or "->get_arg_value", etc...
                  // so whoever called this function should obtain that before calling $this->set_arg_array() with the data we return here
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): LEAVING, returning $got_args<br>'); }
                  return $got_args;
            }
            
            /*!
            @function grab_class_args_xmlrpc
            @abstract grab data an XML-RPC call and fill various class arg variables with the available data
            @result none, this is an object call
            @discussion functional relative to function "grab_class_args_gpc()", except this function grabs the
            data from an alternative, non-php-GPC, source
            NOT YET IMPLEMENTED
            @author Angles
            @access Public
            */
03341             function grab_class_args_xmlrpc()
            {
                  // STUB, for future use
                  echo 'call to un-implemented function grab_class_args_xmlrpc';
            }
            
            
            /*!
            @function get_best_acctnum
            @abstract search a variety of vars to find a legitimate account number, fallsback to $this->get_acctnum
            @param $args_array ARRAY that was passed to ->begin_request, pass that into here if possible, it is a primary source 
            @param $got_args ARRAY of the *External* params (args) fed to this script via GPC or other methods. 
            Note: these are NOT the "internal args". 
            @param $force_feed_acctnum .DEPRECIATED. INTEGER if for some reason you want to force an account number. DEPRECIATED.
            @result integer, most legitimate account number that was obtained
            @discussion Run early in the script run to establish the most likely account that the rest 
            of the script run will be concerned with. See this function itself, or turn on debugging output, 
            to see what places this function looks to for the "best acctnum". 
            It looks in a series of places, in order of importance to this function, each step of the way if no 
            acctnum is found there, the function looks in the next place, and so on, until an acctnum is 
            found or a fallback, usually "0" for the default email account, is used. See the function 
            $this->get_acctnum for more info. If this function has not already found and stored a good 
            acctnum value, it is most likely that $this->get_acctnum will return the "fallback_default_acctnum" usually "0".  
            This "best acctnum" applies to the typical situations where you are viewing a list of 
            messages in a particular account's folder. HOWEVER, if the list of 
            messages    is composed of messages from different accounts, such as is possible with testing filter rules, 
            then there would be no "best acctnum" because no single account is being looked at. In any case 
            each message item should be in the form of a "msgball" which carries with it the acctnum the 
            message is associated with (amoung other things). Even so, the far more typical scenario is 
            viewing a list of messages from the same account and folder. Also, when viewing any one messages 
            contents, the "best acctnum" is obviously the account that message came from. When viewing 
            folder contents, the folder we want to look at is passed in the form of a "fldball" which is an associative 
            array containing "folder" and "acctnum", so this function would use that acctnum as the "best acctnum". 
            Typically we are dealing with either "fldball" or "msgball" data which carry with them the 
            acctnum they are associated with. Being able to determine the "best acctnum" that 
            applies to a particular page view is helpful since this value will be stored and available 
            using function $this->get_acctnum() which is easy to call from anyewhere.
            Before it became possible to operate on mesages from seperate folders and accounts in the same page, 
            this "best acctnum" has more importance, but still it is most common not to have a list of messages 
            from different sources, so this function is useful in a majority of the usages we will handle. Since 
            this is true, many functions in this class, like "get_arg_value", take a param for "acctnum" that is 
            OPTIONAL because if not provided any function can obtain an acctnum value by calling 
            $this->get_acctnum() which returns the value which was found here. 
            @author Angles
            @access Private
            */
03387             function get_best_acctnum($args_array='', $got_args='', $force_feed_acctnum='')
            {
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): ENTERING, param $force_feed_acctnum ['.$force_feed_acctnum.']<br>'); }
                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): parm $args_array[] DUMP:', $args_array); }
                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): parm $got_args[] DUMP:', $got_args); }
                  
                  // ---  which email account do are these args intended to apply to  ----
                  // ORDER OF PREFERENCE for determining account num: just look at the code, it has comments
                  if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): "what acctnum to use": searching...: <br>'); }
                  // initialize
                  $acctnum = '';
                  
                  if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): get acctnum from feed args if possible<br>'); }
                  $found_acctnum = False;
                  while(list($key,$value) = each($args_array))
                  {
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): (acctnum search) this loop feed arg : ['.$key.'] => ['.serialize($args_array[$key]).'] <br>'); }
                        // try to find feed acctnum value
                        if ($key == 'fldball')
                        {
                              $fldball = $args_array[$key];
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): (acctnum search) $args_array passed in $fldball[] : '.serialize($fldball).'<br>'); }
                              $acctnum = (int)$fldball['acctnum'];
                              
                              // SET OUR ACCTNUM ACCORDING TO FEED ARGS
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): (acctnum search) ACCTNUM from $args_array fldball : ['.$acctnum.']<br>'); }
                              $found_acctnum = True;
                              break;
                        }
                        elseif ($key == 'msgball')
                        {
                              $msgball = $args_array[$key];
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): (acctnum search) $args_array passed in $msgball[] : '.serialize($msgball).'<br>'); }
                              $acctnum = (int)$msgball['acctnum'];
                              // SET OUR ACCTNUM ACCORDING TO FEED ARGS
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): (acctnum search) ACCTNUM from $args_array msgball : ['.$acctnum.']<br>'); }
                              $found_acctnum = True;
                              break;
                        }
                        elseif ($key == 'acctnum')
                        {
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): (acctnum search) $args_array passed in "acctnum" : '.serialize($args_array[$key]).'<br>'); }
                              $acctnum = (int)$args_array[$key];
                              // SET OUR ACCTNUM ACCORDING TO FEED ARGS
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): (acctnum search) ACCTNUM from $args_array "acctnum" feed args : ['.$acctnum.']<br>'); }
                              $found_acctnum = True;
                              break;
                        }
                  }
                  // did the above work?
                  if ($found_acctnum == True)
                  {
                        // SET THE ACCTNUM AND RETURN IT
                        if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): (from $args_array) * * * *SETTING CLASS ACCTNUM* * * * by calling $this->set_acctnum('.serialize($acctnum).')<br>'); }
                        $this->set_acctnum($acctnum);
                        if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): LEAVING early, $args_array had the data, returning $acctnum ['.serialize($acctnum).']<br>'); }
                        return $acctnum;
                  }
                  
                  if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): "what acctnum to use": continue searching...: <br>'); }
                  
                  // ok, now we need to broaden the search for a legit account number
                  if ((isset($force_feed_acctnum))
                  && ((string)$force_feed_acctnum != ''))
                  {
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): "what acctnum to use": will use function param $force_feed_acctnum=['.serialize($force_feed_acctnum).']<br>'); }
                        $acctnum = (int)$force_feed_acctnum;
                  }
                  elseif ((isset($got_args['msgball']['acctnum']))
                  && ((string)$got_args['msgball']['acctnum'] != ''))
                  {
                        // we are requested to handle (display, move, forward, etc...) this msgball, use it's properties
                        $acctnum = (int)$got_args['msgball']['acctnum'];
                        // make sure this is an integer
                        $got_args['msgball']['acctnum'] = $acctnum;
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): "what acctnum to use": will use GPC aquired $got_args[msgball][acctnum] : ['.serialize($got_args['msgball']['acctnum']).']<br>'); }
                  }
                  elseif ((isset($got_args['fldball']['acctnum']))
                  && ((string)$got_args['fldball']['acctnum'] != ''))
                  {
                        // we are requested to handle (display, .... ) data concerning this fldball, use it's properties
                        $acctnum = (int)$got_args['fldball']['acctnum'];
                        // make sure this is an integer
                        $got_args['fldball']['acctnum'] = $acctnum;
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): "what acctnum to use": will use GPC aquired $got_args[fldball][acctnum] : ['.serialize($got_args['fldball']['acctnum']).']<br>'); }
                  }
                  elseif ((isset($got_args['source_fldball']['acctnum']))
                  && ((string)$got_args['source_fldball']['acctnum'] != ''))
                  {
                        // we are *probably* requested to delete or rename this fldball, use it's properties
                        $acctnum = (int)$got_args['source_fldball']['acctnum'];
                        // make sure this is an integer
                        $got_args['source_fldball']['acctnum'] = $acctnum;
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): "what acctnum to use": will use GPC aquired $got_args[source_fldball][acctnum] : ['.serialize($got_args['source_fldball']['acctnum']).']<br>'); }
                  }
                  elseif ((isset($got_args['delmov_list'][0]['acctnum']))
                  && ((string)$got_args['delmov_list'][0]['acctnum'] != ''))
                  {
                        // at the very least we know that we'll need to login to this account to delete or move this particular msgball
                        // also, we will need to open the particular folder where the msg is localted
                        $acctnum = (int)$got_args['delmov_list'][0]['acctnum'];
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): "what acctnum to use": will use GPC aquired $got_args[delmov_list][0][acctnum] : ['.serialize($got_args['delmov_list'][0]['acctnum']).']<br>'); }
                  }
                  elseif ((isset($got_args['target_fldball']['acctnum']))
                  && ((string)$got_args['target_fldball']['acctnum'] != ''))
                  {
                        // at the very least we know we need to login to this account to append a message to a folder there
                        // NOTE: we need not open the particular folder we are going to append to,
                        // all we need is a stream to that particular account, "opened" folder is not important
                        // therefor we can just use INBOX as the folder to log into in this case
                        $acctnum = (int)$got_args['target_fldball']['acctnum'];
                        // make sure this is an integer
                        $got_args['target_fldball']['acctnum'] = $acctnum;
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): "what acctnum to use": will use GPC aquired $got_args[target_fldball][acctnum] : ['.serialize($got_args['target_fldball']['acctnum']).']<br>'); }
                  }
                  else
                  {
                        // FALLBACK
                        // ok, we have NO acctnum in $args_array, did NOT get it from GPC got_args, nor the force fed $force_feed_acctnum
                        // so, we grab the class's current value for $this->acctnum
                        // $this->get_acctnum() will return a default value for us to use if $this->acctnum is not set
                        // note, this is identical to $this->get_acctnum(True) because True is the default arg there if one is not passed
                        // True means "return a default value, NOT boolean false, if $this->acctnum is not set
                        $acctnum = $this->get_acctnum(True);
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): "what acctnum to use": NO *incoming* acctnum specified, called $this->get_acctnum(True), got: ['.serialize($acctnum).']<br>'); }
                  }
                  
                  // SET THE ACCTNUM WITH THE "BEST VALUE" WE COULD FIND
                  // DEPRECIATED - we no longer set it here
                  //if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: get_best_acctnum: * * * *SETTING CLASS ACCTNUM* * * * by calling $this->set_acctnum('.serialize($acctnum).')<br>'); }
                  //$this->set_acctnum($acctnum);
                  
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): LEAVING, returning $acctnum ['.serialize($acctnum).']<br>'); }
                  return $acctnum;
            }
            
            /*!
            @function init_internal_args_and_set_them
            @abstract initialize Internally controlled params (args). MUST already have an acctnum
            @param $acctnum integer the current account number whose array we will fill with these initialized args
            @result none, this is an object call
            @discussion ALMOST DEPRECIATED. 
            NOTE that the most IMPORTANT thing this does now is set interal arg 
            "already_grab_class_args_gpc" to True, which serves as an ad-hoc flag indicating this class 
            has already been through its typical initialization procedures. But that was not the 
            intended purpose here, it just happened that way, partly for the simple reason that this 
            function is called after the "grab_external_args" function. This function had more meaning 
            once, back when a now depreciated and gone function used it. Now this is mostly an 
            outdated, not needed, not really used function. Even so, it turns out that keeping an 
            array called $this->known_internal_args[] is helpful from a development standpoint, just 
            to help the coder remember what args this class is passing around to itself. These internal args are 
            different from the "external" args because these internal args are NEVER filled from external sources, 
            such as GET and POST or possible XML-RPC sources, which are considered external sources of data. 
            A side effect is that this function will actually set all the args listed in that "known_internal_args" 
            array, which is bad because you can not use the set vs. not set comparison if they are set always. 
            For that reason there are currently some internal args not listed in the "known_internal_args" 
            array. This probably will be cleaned up in the future. 
            @author Angles
            @access Public
            */
03547             function init_internal_args_and_set_them($acctnum='')
            {
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: init_internal_args: ENTERING, (parm $acctnum=['.serialize($acctnum).'])<br>'); }
                  // we SHOULD have already obtained a valid acctnum before calling this function
                  if (!(isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  // INTERNALLY CONTROLLED ARGS
                  // preserve pre-existing value, for which "acctnum" must be already obtained, so we
                  // know what account to check for existing arg values when we use "get_isset_arg" or "get_arg_value"
                  $internal_args = Array();
                  if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: init_internal_args: about to loop thru $this->known_internal_args<br>'); }
                  $loops = count($this->known_internal_args);
                  for($i=0;$i<$loops;$i++)
                  {
                        $this_arg_name = $this->known_internal_args[$i];
                        //if ($this->debug_args_input_flow > 2) { $this->dbug->out(' * * (grab pref - internal) $this_arg_name: '.$this_arg_name.'<br>'); }
                        // see if there is a value we can preserve for this arg
                        if ($this->get_isset_arg($this_arg_name))
                        {
                              $preserve_this = $this->get_arg_value($this_arg_name);
                              if ($this->debug_args_input_flow> 2) { $this->dbug->out(' * * (grab pref - internal) preserving internal pre-existing arg: ['.$this_arg_name.'] = ['.$preserve_this.']<br>'); }
                              $internal_args[$this_arg_name] = $preserve_this;
                        }
                        else
                        {
                              if ($this->debug_args_input_flow > 2) { $this->dbug->out(' * (grab pref - internal) no pre-existing value for ['.$this_arg_name.'], using initialization default: <br>'); }
                              if ($this_arg_name == 'folder_status_info')
                              {
                                    $internal_args['folder_status_info'] = array();
                              }
                              elseif ($this_arg_name == 'folder_list')
                              {
                                    $internal_args['folder_list'] = array();
                              }
                              elseif ($this_arg_name == 'mailsvr_callstr')
                              {
                                    $internal_args['mailsvr_callstr'] = '';
                              }
                              elseif ($this_arg_name == 'mailsvr_namespace')
                              {
                                    $internal_args['mailsvr_namespace'] = '';
                              }
                              elseif ($this_arg_name == 'mailsvr_delimiter')
                              {
                                    $internal_args['mailsvr_delimiter'] = '';
                              }
                              elseif ($this_arg_name == 'mailsvr_stream')
                              {
                                    $internal_args['mailsvr_stream'] = '';
                              }
                              elseif ($this_arg_name == 'mailsvr_account_username')
                              {
                                    $internal_args['mailsvr_account_username'] = '';
                              }
                              // experimental (by it being *here*): this arg is handles elsewhere, but Iput it here
                              // to help remember and be consistant about accounting for all args we may use
                              // UPDATE: "expunge_folders" can NOT BE HERE because it should NOT EXIST unless set during a move or delete
                              //  putting it here will initialize it to a value of "" (empty string) which is different than unset.
                              //elseif ($this_arg_name == 'expunge_folders')
                              //{
                              //    $internal_args['expunge_folders'] = '';
                              //}
                              // experimental: Set Flag indicative we've run thru this function
                              elseif ($this_arg_name == 'already_grab_class_args_gpc')
                              {
                                    $internal_args['already_grab_class_args_gpc'] = True;
                              }
                        }
                  }
                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: init_internal_args: post-loop (internal args) $internal_args[] DUMP:', $internal_args); }
                  
                  
                  // clear old args (if any) and set the args we just obtained (or preserved)
                  //$this->unset_all_args();
                  // set new args, some may require processing (like folder will go thru prep_folder_in() automatically
                  //while(list($key,$value) = each($internal_args))
                  //{
                  //    $this->set_arg_value($key, $internal_args[$key]);
                  //}
                  
                  // use this one call to do it all
                  //$this->set_arg_array($internal_args);
                  
                  // add these items to the args array for the appropriate account
                  if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: init_internal_args: about to add $internal_args to acounts class args array<br>'); }
                  while(list($key,$value) = each($internal_args))
                  {
                        if ($this->debug_args_input_flow > 2) { $this->dbug->out(' * mail_msg: init_internal_args: (looping) setting internal arg: $this->set_arg_value('.$key.', '.$internal_args[$key].', '.$acctnum.'); <br>'); }
                        $this->set_arg_value($key, $internal_args[$key], $acctnum);
                        //$this->set_arg_value($key, $internal_args[$key]);
                  }
                  
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: grab_class_args_gpc: LEAVING, returning $internal_args<br>'); }
                  return $internal_args;
            }
            
            /*!
            @function get_best_folder_arg
            @abstract search a variety of vars to find a legitimate folder value to open on the mail server number, 
            @param $args_array ARRAY that was passed to ->begin_request, pass that into here if possible, it is a primary source
            @param $got_args ARRAY of the *External* params (args) fed to this script via GPC or other methods
            Note these are NOT the "internal args". 
            @param $acctnum INTEGER used to querey various already-set args
            @result string, most legitimate folder value that was obtained 
            @discussion The return folder string MUST *NOT* BE URLENCODED.  
            It is an artifact of this apps development that this arg exists and is not urlencoded. 
            Once upon a time, any one page view could be associated with ONLY one folder at a time. 
            This is no longer a limitation, but a VAST majority of the time it is true that 
            we are only concerned with one particular folder for a page view. A similar discussion is in the 
            comments for function "get_best_acctnum". As it happens, now this app uses things called 
            "fldball" and "msgball" because the advent of multiple accounts, and the ability to handle 
            messages from any folder from any account at any time, required that a description of a 
            folder or message carry with it enough info to bunch all those possibilities into one structure. 
            We call these "fldball" and "msgball", at the least they contain elements "folder" and "acctnum", 
            and "msgball" will additionally contain more data such as "msgnum". But I digress. In the past 
            it was only necessary to know the one folder we cared about for the script run, and mostly 
            this is still applicable. This function looks in a variety of places for that, see the function itself for 
            details, including looking for certain fldball or msgball args. SECONDLY it is an artifact 
            of this apps development that this arg value is NOT urlencoded. This is now unusual because 
            the fldball and msgball data ALWAYS keep their "folder" data URLENCODED. This STAYS 
            urlencoded up until it absolutely needs to be decoded, such as when sending a command to the 
            IMAP server. So it is a notable exception that this arg is not urlencoded. But that is because it 
            never was ever urlencoded "in the old days" so functions that look for the simple arg "folder" 
            expected a non-urlencoded value. DIGRESSION - the urlencoding of the folder names makes it 
            easier to cache the names in a database, which can be sensitive to some chars if not encoded. 
            And if you know a fldball always has urlencoded folder value then you can more 
            accurately compare two different fldballs more quickly. If one were urlencoded and the other 
            not, then identical folder names would not match in a string comparison. So SQL calls 
            expected a urlencoded folder name. If not for this expectation, we may not pull the 
            desired data from the database, and perhaps leave unneeded data there because of this. 
            MORE DIGRESSION - fldball and msgball data is in URI syntax when in string form, 
            and by using function "ball_data_parse_str" that string can be turned into an associative 
            array. Note that php function parse_str is used for this BUT see that function for more 
            discussion on that topic, the result is that we keep the folder name urlencoded in that array. 
            Usually we make the transformation into an array for ease of use inside a function, 
            but the string URI syntax takes up less memory when dealing with huge message lists, and 
            takes up less space cached in a database as compared to a serialized array with the same data.
            AND NOW we also do the VERY IMPORTANT job of VERIFYING the folder name against 
            the known folder list HENCE this function must be called either (i) after a login at least to INBOX, or 
            (ii) after obtaining from cache all data you would get from a login, such as said folder list.
            THE VERIFICATION is to (a) urldecode the folder item, so no encoded js commands slip in, 
            then (b) do the lookup so we only use actual folder names, THESE 2 things are done by a 
            call to "prep_folder_in" , then  (c) re-urlencode the result and put back into the fldball or msgball it came from, 
            because fldball and msgball folder elements must be verified too, and the want the folder name element 
            to be urlencoded so we put it back there urlencoded, then (d) urldecode it because the legacy 
            quirk requires a urldecoded return from this function, eventhough the ball data folder 
            element needs to stay urlencoded. Currently the ball data verified is fldball and msgball, 
            the other possible areas of ball data, such as folder naming, message moving, need to be 
            verified too, but not here, you must do that later.
            DO NOT FEED A FLDBALL OR A MSGBALL TO A PAGE WHERE YOU DO NOT WANT A 
            LOGIN BECAUSE THE PRESENCE OF FLDBALL OR MSGBALL FORCE THIS VERIFIFICATION 
            WHICH FORCES THE USE OF A KNOWN FOLDER LIST WHICH FORCES A LOGIN ATTEMPT 
            IF SUCH LIST IS NEEDED.
            @author Angles
            @access Private
            */
03707             function get_best_folder_arg($args_array='', $got_args='', $acctnum='')
            {
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: get_best_folder_arg: ENTERING <br>'); }
                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: get_best_folder_arg: param $acctnum ['.$acctnum.'] ; parm $args_array[] DUMP:', $args_array); }
                  if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: get_best_folder_arg: param $acctnum ['.$acctnum.'] ; parm $got_args[] DUMP:', $got_args); }
                  // initialize
                  $processed_folder_arg = '';
                  // we SHOULD have already obtained a valid acctnum before calling this function
                  if (!(isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  //  ----  Get Folder Value  ----
                  // ORDER OF PREFERENCE for pre-processed "folder" input arg
                  // (1) $args_array, IF FILLED, overrides any previous data or any other data source, look for these:
                  //    $args_array['folder']
                  // (2) GPC ['msgball']['folder']
                  // (3) GPC ['fldball']['folder']
                  // (4) GPC ['delmov_list'][0]['folder']
                  // (5) if "folder" arg it is already set, (probably during the reuse attempt, probably obtained from $args_array alreadt) then use that
                  // (6) default to blank string, which "prep_folder_in()" changes to defaultg value INBOX
                  
                  // note: it's OK to send blank string to "prep_folder_in", because it will return a default value of "INBOX"
                  if ((isset($args_array['folder']))
                  && ($args_array['folder'] != ''))
                  {
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg: $input_folder_arg chooses $args_array[folder] ('.$args_array['folder'].') over any existing "folder" arg<br>'); }
                        $input_folder_arg = $args_array['folder'];
                  }
                  elseif ($this->get_isset_arg('["msgball"]["folder"]'))
                  {
                        $input_folder_arg = $this->get_arg_value('["msgball"]["folder"]');
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg: $input_folder_arg chooses $this->get_arg_value(["msgball"]["folder"]): ['.$input_folder_arg.']<br>'); }
                        //VERIFY
                        $processed_folder_arg = $this->prep_folder_in($input_folder_arg, $this->get_arg_value('["msgball"]["acctnum"]'));
                        // when putting back into the ball data we need to urlencode it because folder element in ball data stays urlencoded until the last monent
                        $processed_folder_arg = urlencode($processed_folder_arg);
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg('.__LINE__.'): after "prep_folder_in", $processed_folder_arg : ['.$processed_folder_arg.']<br>'); }
                        // both these should be urlencoded so this is apples to apples comparison
                        if ($processed_folder_arg != $input_folder_arg)
                        {
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg('.__LINE__.'): $processed_folder_arg != $input_folder_arg so about to call $this->set_arg_value(["msgball"]["folder"], $processed_folder_arg) <br>'); }
                              $this->set_arg_value('["msgball"]["folder"]', $processed_folder_arg);
                        }
                        // now that is done, urldecode because a legacy quirk requires a urldecded return from this function
                        $processed_folder_arg = urldecode($processed_folder_arg);
                  }
                  elseif ($this->get_isset_arg('["fldball"]["folder"]'))
                  {
                        $input_folder_arg = $this->get_arg_value('["fldball"]["folder"]');
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg: $input_folder_arg chooses $this->get_arg_value(["fldball"]["folder"]): ['.$input_folder_arg.']<br>'); }
                        //VERIFY
                        $processed_folder_arg = $this->prep_folder_in($input_folder_arg, $this->get_arg_value('["fldball"]["acctnum"]'));
                        // when putting back into the ball data we need to urlencode it because folder element in ball data stays urlencoded until the last monent
                        $processed_folder_arg = urlencode($processed_folder_arg);
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg('.__LINE__.'): after "prep_folder_in", $processed_folder_arg : ['.$processed_folder_arg.']<br>'); }
                        // both these should be urlencoded so this is apples to apples comparison
                        if ($processed_folder_arg != $input_folder_arg)
                        {
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg('.__LINE__.'): $processed_folder_arg != $input_folder_arg so about to call $this->set_arg_value(["fldball"]["folder"], $processed_folder_arg) <br>'); }
                              $this->set_arg_value('["fldball"]["folder"]', $processed_folder_arg);
                        }
                        // now that is done, urldecode because a legacy quirk requires a urldecded return from this function
                        $processed_folder_arg = urldecode($processed_folder_arg);
                  }
                  elseif ($this->get_isset_arg('delmov_list'))
                  {
                        // we know we'll need to loginto this folder to get this message and move/delete it
                        // there may be other msgballs in the delmov_list array, but we know at the very list we'll need to open this folder anyway
                        $this_delmov_list = $this->get_arg_value('delmov_list');
                        $input_folder_arg = $this_delmov_list[0]['folder'];
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg: $input_folder_arg chooses $this_delmov_list[0][folder]: ['.$input_folder_arg.']<br>'); }
                  }
                  else
                  {
                        if (($this->get_isset_arg('folder'))
                        && ((string)trim($this->get_arg_value('folder')) != ''))
                        {
                              $input_folder_arg = $this->get_arg_value('folder');
                        }
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg: $input_folder_arg *might* chooses $this->get_arg_value(folder): ['.serialize($input_folder_arg).']<br>'); }
                        
                        $input_folder_arg = (string)$input_folder_arg;
                        $input_folder_arg = trim($input_folder_arg);
                        if ($input_folder_arg != '')
                        {
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg: $this->get_arg_value(folder) passes test, so $input_folder_arg chooses $this->get_arg_value(folder): ['.serialize($input_folder_arg).']<br>'); }
                        }
                        else
                        {
                              if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg: no folder value found, so $input_folder_arg takes an empty string<br>'); }
                              $input_folder_arg = '';
                        }
                  }
                  // ---- Prep the Folder Name (remove encodings, verify it's long name (with namespace)
                  // folder prepping does a lookup which requires a folder list which *usually* (unless caching) requires a login
                  if ($processed_folder_arg != '')
                  {
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg: we already obtained above a $processed_folder_arg ['.$processed_folder_arg.']<br>'); }
                  }
                  else
                  {
                        if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg: about to issue $processed_folder_arg = $this->prep_folder_in('.$input_folder_arg.')<br>'); }
                        $processed_folder_arg = $this->prep_folder_in($input_folder_arg);
                  }
                  if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_folder_arg: remember legacy quirk says return value from here should be urdecoded, eventhough msgball and fldball keep folder element urlencoded usually<br>'); }
                  if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: get_best_folder_arg: LEAVING, returning $processed_folder_arg value: ['.$processed_folder_arg.'] remember legacy quirk says return value from here should be urdecoded, eventhough msgball and fldball keep folder element urlencoded usually<br>'); }
                  return $processed_folder_arg;
            }     
            
            
            /**************************************************************************\
            * END INPUT ARG/PARAM HANDLERS                                                *
            * - - - - - - - - - - - - - - - - - - - - - - - - -                                                   *
            * BEGIN APPSESSION TEMPORARY CACHING HANDLERS         *
            \**************************************************************************/
            
            // ACTUAL DATA ACCESS FUNCTIONS MOVED TO SO CLASS
            // HIGH LEVEL CACHING STUFF KEPT HERE
            /*!
            @function save_session_cache_item BEING OVERHAULED preliminary complete. 
            @abstract TEMPORARY DATA CACHING server-side in the phpgw appsession cache.
            @param $data_name (string) 
            @param $data (mixed) usually an array 
            @param $acctnum (int) 
            @param $extra_keys (string) optional info used to make the appsession "location" string for data that is 
            part of a larger group of elements, this param will let us refer to this single element without using sub arrays
            @author Angles
            @discussion Server-side caching of limited, ephermal data, such as a list of messages from 
            an imap search, saved to phpgw appsession. All appsession data gets deleted when the user logs out, which is 
            why this is a temporary cache. NOTE: to cache an item you must add it to list of data items that 
            has a handler here, otherwise we skip it. Handler need may be lifted in the future, but for now anything 
            stored in the appsession cache requires a handler here because different types of data arrive here in different 
            forms, and must be stored using that extra information to know where to put the data in the appsession 
            cache array of data. For example, *some* cached items get meta_data saved with it, right now only the 
            "msgball_list" is saved with other, related data. In the case of the "msgball_list", $meta_data["msgball_list"] 
            and $meta_data["validity"]  is what we should get fed into this function, where that meta_data "validity" data 
            is used to verify if the cached "msgball_list" is valid, not-stale data, when restoring a "msgball_list" from the 
            cache. Also. data is saved in the appsession data array starting at element  
            "[_SESSION][phpgw_session][phpgw_app_sessions][email]" and from that we add the single element 
            "dat" and from that all the information we cache is attached to that element of the array. All data REQUIRES 
            an acctnum, as an integer, because that is the first thing we use to determine a location in the appsession 
            array for the data. Then we use the data_name, the combination of [acctnum][data_name]  is the least 
            amount of info we need to put the more simple data in the appsession array, such as the cached "mailsvr_callstr". 
            It would be saved here "[_SESSION][phpgw_session][phpgw_app_sessions][email][dat][$acctnum][mailsvr_callstr]" 
            as the key and the actual mailsvr_callstr data is the value. The next step up in data requirement is the addition 
            of the folder name to the array, such as for "folder_status_info" data, is gets saved at this key 
            "[_SESSION][phpgw_session][phpgw_app_sessions][email][dat][$acctnum][folder_status_info][folder]" as its key. 
            Them the most info we need to store cached data is for, currently, the "msg_structure" and "phpgw_header" 
            which reauire the additional information that is the msgnum they apply to, so they are saved with this 
            as their key, as in the case of "msg_structure", 
            "[_SESSION][phpgw_session][phpgw_app_sessions][email][dat][$acctnum][msg_structure][folder][msgnum]". 
            This allows us to directly access this appsession array if we want, and to quickly access or expire individual 
            parts of the data without having to loop through a lot of array to find what we want. SUBJECT TO CHANGE. 
            @example This is not really an example but it does explain where this data is kept in memory. 
            [HTTP_SESSION_VARS][phpgw_session][phpgw_app_sessions][email]
            [_SESSION][phpgw_session][phpgw_app_sessions][email]
            This is also accessable thru this
            GLOBALS[phpgw_session][phpgw_app_sessions][email]
            @access private
            */
03869             function save_session_cache_item($data_name='misc',$data,$acctnum='',$extra_keys='')
            {
                  if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): ENTERED, $this->session_cache_enabled='.serialize($this->session_cache_enabled).', $data_name: ['.$data_name.'], $acctnum (optional): ['.$acctnum.'], $extra_keys: ['.$extra_keys.']<br>'); }
                  $has_handler = False;
                  
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  if ($this->session_cache_enabled == False)
                  {
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING, because $this->session_cache_enabled='.serialize($this->session_cache_enabled).', so not this basic stuff, and not the extra "extreme cache" stuff, nothing can use the appsession cache.<br>'); }
                        // LEAVING
                        return False;
                  }
                  // IF EMPTY DATA - LEAVE
                  if (!$data)
                  {
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING with ERROR, no $data param was provided for $data_name ['.$data_name.'] $acctnum ['.$acctnum.'], we gotta return false because no data was saved<br>'); } 
                        // LEAVING
                        return False;
                  }
                  
                  if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): extra param (if provided) $extra_keys: ['.serialize($extra_keys).'] <br>'); } 
                  
                  // 1st -- session_cache_enabled stuff 
                  // this stuff is cached REGARDLESS of extreme mode, extreme mode is *extra* caching in addition to the "session_cache_enabled" stuff
                  // process the data according to what it is
                  if ($data_name == 'msgball_list')
                  {
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): session_cache_enabled and data exists AND $data_name ['.$data_name.'] has a handler<br>'); }
                        
                        // we damn well better have a msgball_list or else we have nothing to save
                        if ((isset($data['msgball_list']) == False)
                        || (!$data['msgball_list']))
                        {
                              $this->dbug->out('mail_msg: save_session_cache_item: LEAVING on ERROR, FIXME line '.__LINE__.' we have no msgball_list, sometimes caused when you ask call get_msgball_list on an empty folder, or if trying to use imap sockets<br>');
                              echo 'mail_msg: save_session_cache_item: LEAVING on ERROR, FIXME line '.__LINE__.' we have no msgball_list, check: is this an empty folder, is php_imap installed <br>';
                              // LEAVING ERROR
                              return False;
                        }
                        
                        // FIRST do a CACHE SANITY CHECK
                        // anglemail table is good for about 8,000 to 10,000 item msgball list
                        // while php4 sessions and sessions_db are good for much less than that
                        // if we put too much data into a store that can not handle it, we get an error on retieving that data
                        // which error will stop page view and because php has no excepion handler, we must avoid the error before it happens
                        $msgball_list_count = count($data['msgball_list']);
                        if 
                        (
                          (
                              (($this->use_private_table == True)
                              && ($msgball_list_count > 9000))
                          )
                         ||
                          (
                              (($this->use_private_table == False)
                              && ($msgball_list_count > 2000))
                          )
                        )
                        {
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item: LEAVING on $msgball_list_count ['.$msgball_list_count.'] TOO LARGE, line '.__LINE__.' test indicates msgball_list too big for data store <br>'); }
                              // LEAVING ERROR
                              return False;
                        }
                        
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): $data_name ['.$data_name.'] is saved with validity data from "get_folder_status_info" for later staleness testing<br>'); }
                        
                        // NOTE 1: if you provide this 1 thing:
                        //    data['msgball_list'] 
                        // the rest will be filled in for you based on current folder and account (as determined above via acctnum param or the current acctnum arg)
                        
                        // NOTE 2: if you provide these 2 things
                        //    data['msgball_list'] 
                        //    data['validity']['fldball']
                        // the "folder_status_info" will be filled in for you SPECIFICALLY for that fldball EVEN IF it is not the current folder or account
                        
                        // NOTE 3: if you provide these 2 things
                        //    data['msgball_list'] 
                        //    data['validity']['folder_status_info']
                        // the "fldball" will be filled from your "folder_status_info" which has a fldball data in it
                        // a FOLDER_STATUS_INFO fldball PREVAILS, it overrules data['validity']['fldball'] 
                        
                        // NOTE 4: if you provide these 3 things
                        //    data['msgball_list'] 
                        //    data['validity']['fldball']
                        //    data['validity']['folder_status_info']
                        // then you have provided EVERYTHING we need
                        
                        // NOTE 5: NOW we also use folder as a data key, so each folder has its own msgball_list
                        // so later we will use this folder data for what is called $extra_keys, but it is just the folder name here
                        // update to make it easier to understand I call this $folder_name now
                        
                        // easy way is "note 1", at this point providing the other stuff is not needed for normal operation
                        // perhaps if you already have the other stuff and you know it has not changed ("folder_status_info" still good) 
                        // then *maybe* it might save some time passing "folder_status_info" you already have 
                        // if you have many accounts open and the "current account" is hard to discerrn, pass a fldball make sure everything will match
                        
                        // we need an acctnum, either 
                        //  (a) from the fldball, or 
                        //  (b) from the fldball in the "folder_status_info", or
                        // (c) what was passed as the acctnum (which itself is optional, then it will be the "current acctnum"
                        
                        // we use folder_info for validity testing of data "stale" or not when we retrieve the cached data later
                        // either data has INCLUDED with it a fldball or we make one based on current account info
                        if ((isset($data['validity']['folder_status_info']['fldball']['folder']) == True)
                        && ((string)$data['validity']['folder_status_info']['fldball']['folder'] != '')
                        && ((string)$data['validity']['folder_status_info']['fldball']['acctnum'] != ''))
                        {
                              // folder_status_info IF PROVIDED has PRIORITY of fldball and therefor PRIORITY of acctnum
                              $acctnum = $data['validity']['folder_status_info']['fldball']['acctnum'];
                              // make a fldball, ADD IT TO DATA
                              $data['validity']['fldball'] = array();
                              $data['validity']['fldball']['acctnum'] = $acctnum;
                              $data['validity']['fldball']['folder'] = $data['validity']['folder_status_info']['fldball']['folder'];
                        }
                        elseif ((isset($data['validity']['fldball']['folder']) == True)
                        && ((string)$data['validity']['fldball']['folder'] != '')
                        && ((string)$data['validity']['fldball']['acctnum'] != ''))
                        {
                              // the next place to look for acctnum is here
                              $acctnum = $data['validity']['fldball']['acctnum'];
                              // in this case WE CAN NOT RELY on folder_status_info if provided because it is not complete
                              if (isset($data['validity']['folder_status_info']) == True)
                              {
                                    $data['validity']['folder_status_info'] == array();
                                    unset($data['validity']['folder_status_info']);
                              }
                        }
                        else
                        {
                              // fallback: using genericly obtained acctnum, make a fldball and add it to DATA
                              $data['validity']['fldball'] = array();
                              $data['validity']['fldball']['acctnum'] = $acctnum;
                              $data['validity']['fldball']['folder'] = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
                              // just in case incomplete "folder_status_info" was passed, unset it
                              if (isset($data['validity']['folder_status_info']) == True)
                              {
                                    $data['validity']['folder_status_info'] == array();
                                    unset($data['validity']['folder_status_info']);
                              }
                        }
                        
                        // this is really for backwards compat right now
                        if (isset($data['validity']['folder_long']) == False)
                        {
                              $data['validity']['folder_long'] = $data['validity']['fldball']['folder'];
                        }
                        
                        // OK now we know we have a fldball, SO we know we have an ACCTNUM, from now on USE THAT ACCTNUM if calling other functions b4 we save this data
                        // $extra_keys if provided is enabling new stuff, it was not used before for msgball_list data
                        // IF it is provided (not yet used) it will probably be a folder name in urlencoded form, but NOT SURE YET
                        // we know what to do here, so this data "has a handler"
                        $has_handler = True;
                        
                        // either data has INCLUDED with it "folder_status_info" or we obtain it based on that fldball
                        if ((isset($data['validity']['folder_status_info']) == False)
                        || (!$data['validity']['folder_status_info']))
                        {
                              // obtain folder stats, ADD IT TO DATA
                              $data['validity']['folder_status_info'] = array();
                              $data['validity']['folder_status_info'] = $this->get_folder_status_info($data['validity']['fldball']);
                        }
                        // ADDITIONAL info that we need for validity testing when we later want to read this info
                        // ADD IT IF NOT PROVIDED
                        if (isset($data['validity']['sort']) == False)
                        {
                              $data['validity']['sort'] = $this->get_arg_value('sort', $data['validity']['fldball']['acctnum']);
                        }
                        if (isset($data['validity']['order']) == False)
                        {
                              $data['validity']['order'] = $this->get_arg_value('order', $data['validity']['fldball']['acctnum']);
                        }
                        if ((isset($data['validity']['mailsvr_callstr']) == False)
                        || (isset($data['validity']['mailsvr_account_username']) == False))
                        {
                              // these two things are REALLY linked together, the better be obtained as a pair
                              $data['validity']['mailsvr_callstr'] = $this->get_arg_value('mailsvr_callstr', $data['validity']['fldball']['acctnum']);
                              $data['validity']['mailsvr_account_username'] = $this->get_arg_value('mailsvr_account_username', $data['validity']['fldball']['acctnum']);
                        }
                        
                        // only is admin is using DB as session store, if "php4" session is used instead, this is not necessary
                        //if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                        //&& (strstr($data['validity']['mailsvr_callstr'] ,'{')) )
                        //{
                        //    // NOTE: YOU BETTER BASE64 ENCODE "DEFANG" the "mailsvr_callstr" OR RISK b0rking your database!!!
                        //    // applies to DBs where ' " / \ and other offensive chars are not handled at the api level in phpgw
                        //    $data['validity']['mailsvr_callstr'] = base64_encode($data['validity']['mailsvr_callstr']);
                        //    // COMMENT THAT OUT if you can prove that DBs are immune to this b0rking
                        //}
                        
                        // we have ALL the data we need now                   
                        // RECAST DATA ready for saving in appsession
                        //$data = serialize($data);
                        
                        // NOW WE USE FOLDER INFO TOO SO EVERY FOLDER GETS ITS OWN MSGBALL_LIST
                        $folder_name = $data['validity']['fldball']['folder'];
                        if (($this->debug_session_caching > 2) && ($this->debug_allow_magball_list_dumps)) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.']['.$folder_name.'] DUMP:', $data); } 
                        // SET DATA
                        //$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name] = $data;
                        // SET DATA USING FOLDER ALSO
                        $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$folder_name] = $data;
                        
                        // for DB sessions_db ONLY
                        if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                        || ($this->use_private_table == True))
                        {
                              //$my_location = (string)$acctnum.';'.$data_name;
                              // SET DATA USING FOLDER ALSO
                              $my_location = (string)$acctnum.';'.$data_name.';'.$folder_name;
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to put data into database anglemail table, $my_location ['.$my_location.']<br>'); } 
                              if ($this->use_private_table == True)
                              {
                                    //$this->so->so_set_data($my_location, $data);
                                    // TRY USING COMPRESSION
                                    $this->so->so_set_data($my_location, $data, True);
                              }
                              else
                              {
                                    // NOTE compression not available for appsession table
                                    $GLOBALS['phpgw']->session->appsession($my_location, 'email', $data);
                              }
                        }
                        
                        $data = array();
                        unset($data);
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING, did SET data for location: [email][dat][$acctnum][$data_name][$extra_keys] :: [email][dat]['.$acctnum.']['.$data_name.']['.$folder_name.'] <br>'); }
                        return True;
                  }
                  // DEFANG special handler for data that might b0rk a database, stuff like ' " / \ and maybe some others, only "mailsvr_callstr" needs this right now
                  elseif ($data_name == 'mailsvr_callstr_OLD_HANDLER')
                  //elseif ($data_name == 'mailsvr_callstr')
                  {
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): session_cache_enabled and DEFANG because $GLOBALS[phpgw_info][server][sessions_type] is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] and data exists for "'.$data_name.'" AND has a handler *here* because phpgw db code may be sensitive to the chars in this data.<br>'); } 
                        // we know what to do here, so this data "has a handler"
                        $has_handler = True;
                        //$location = 'acctnum='.(string)$acctnum.';data_name='.$data_name.$extra_keys;
                        //$app = 'email';
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): "'.$data_name.'" before encoding: '.serialize($data).'<br>'); }
                        // DATABASE DEFANG, this item has "database unfriendly" chars in it so we encode it before it goes to appsession cache
                        $data = base64_encode($data);
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): "'.$data_name.'" AFTER encoding: '.serialize($data).'<br>'); }
                        
                        if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.'] DUMP:', $data); } 
                        // SET DATA
                        $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name] = $data;
                        $data = array();
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING, did SET data for location: [email][dat][$acctnum][$data_name] :: [email][dat]['.$acctnum.']['.$data_name.'] <br>'); }
                        unset($data);
                        return True;
                  }
                  // ANYTHING that is non extreme-mode cachable that requires NO special treatment, add it to this block here
                  elseif (($data_name == 'mailsvr_namespace')
                  || ($data_name == 'folder_list')
                  || ($data_name == 'mailsvr_callstr'))
                  {
                        // note "mailsvr_callstr" is handled here is php4 session is used, because no "defanging" is necessary since data does not go into a database via the phpgw api
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): session_cache_enabled and data exists for "'.$data_name.'" AND has a handler<br>'); }
                        // we know what to do here, so this data "has a handler"
                        $has_handler = True;
                        //$location = 'acctnum='.(string)$acctnum.';data_name='.$data_name.$extra_keys;
                        //$app = 'email';
                        // NO CHANGE TO INPUT DATA NECESSARY because the data is in ready to save form
                        
                        if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.'] DUMP:', $data); } 
                        // SET DATA
                        $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name] = $data;
                        // session_db backwards_compat
                        if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                        || ($this->use_private_table == True))
                        {
                              $my_location = (string)$acctnum.';'.$data_name;
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to put data into phpgw_app_sessions table, $my_location ['.$my_location.']<br>'); } 
                              if ($this->use_private_table == True)
                              {
                                    $this->so->so_set_data($my_location, $data);
                              }
                              else
                              {
                                    $GLOBALS['phpgw']->session->appsession($my_location, 'email', $data);
                              }
                        }
                        
                        $data = array();
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING, did SET data for location: [email][dat][$acctnum][$data_name] :: [email][dat]['.$acctnum.']['.$data_name.'] <br>'); }
                        unset($data);
                        return True;
                  }
                  else
                  {
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): in non-extreme mode block, $data_name ['.$data_name.'] has no handler, so Your data better be handled in the extreme mode block or you are SOL!<br>'); }
                  }
                  
                  // 2nd -- "session_cache_extreme" stuff, ADDITIONAL EXTRA CACHED ITEMS
                  if ($this->session_cache_extreme == False)
                  {
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING, return False, because $this->session_cache_extreme='.serialize($this->session_cache_extreme).', so the extra "extreme cache" stuff can NOT use the appsession cache.<br>'); }
                        return False;
                  }
                  elseif ($data_name == 'folder_status_info')
                  {
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): (extreme-mode) data exists for "'.$data_name.'" AND has a handler<br>'); }
                        if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): "'.$data_name.'" ARRIVING param $data as it is passed into this function DUMP:', $data); } 
                        // DATA REQUIRING "extra_keys" because it is probably one element of a larger group of related data
                        // we will not waste time making a sub array, instead we add the "extra_keys" to the "location"
                        // that way we can directly access this individual datUM without the hastle of a sub array
                        
                        /*!
                        @capability folder_status_info appsession cached when using extreme-mode
                        @syntax Data Param ARRIVES to this function like this
                        folder_status_info $data param arrives into this function like this:
                              $folder_status_info = $data;
                        because folder_status_info has in it a fldball item we can use
                              $folder_status_info[foldball] 
                        has the fldball data we can use that already provided
                        
                        @discussion The "extra_keys" string is the extra KEY which is "FOLDER" 
                        where "FOLDER" is the "long" foldername, meaning it has the Namespace_Delimiter prefixing it.
                        NOTE this is less info than some other data items use in their "extra_keys", other data 
                        may require extra_keys that is "FOLDER_UID" where UID is the message UID, but this 
                        data is for "folder_status_info" so no message UID is necessary, nor applicable, to this.
                        That "extra_keys" string is used in the "location" param of the appsession to access this specific data 
                        which is part of a larger set of data, BUT we do not need to use sub array because that single KEY 
                        will let us directly access this data.
                        */
                        
                        if (!$extra_keys)
                        {
                              // (a) make the key, value pair that we will add to the cache
                              // EXTRA_KEYS = FOLDERNAME_MSGNUM
                              // we know the acctnum, so we already know the username and server name is correct
                              // but within that account there are many folders, all data for those folders is indexed by a unique KEY
                              // by using KEY of msgnum and foldername, we know everything we need to identify the exact data we need
                              
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item: ('.__LINE__.') ['.$data_name.'] needs folder stats to have a PLAIN (not urlencoded) "folder" value ['.$data[$data_name]['folder'].'] so we can urlencode it to make the $specific_key<br>'); } 
                              // $data has info to generate out specific key
                              // KEY MUST BE FOLDER IN URLENCODED FORM
                              //$specific_key = $this->prep_folder_out($data[$data_name]['folder']);
                              // ****************
                              // fix this data structure, i.e. it should arrive here as one thing and we get the folder from IT itself
                              // just a temp hack while fixing other stuff
                              // IS THIS FOLDER NAME ALREADY URLENCODED? IT SHOULD BE.
                              // but we are only using it as a "extra_keys" so maybe just be consistant with the other data?
                              //$extra_keys = urlencode($data['folder_status_info']['fldball']['folder']);
                              //$extra_keys = $data['folder_status_info']['fldball']['folder'];
                              $extra_keys = $data['fldball']['folder'];
                              
                        }
                        // we know what to do here, so this data "has a handler"
                        $has_handler = True;
                        
                        // RECAST DATA ready for saving in appsession
                        //$data = serialize($data);
                        
                        if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.']['.$extra_keys.'] DUMP:', $data); } 
                        // SET DATA
                        $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$extra_keys] = $data;
                        // for DB sessions_db ONLY
                        if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                        || ($this->use_private_table == True))
                        {
                              $my_location = (string)$acctnum.';'.$data_name.';'.$extra_keys;
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to put data into phpgw_app_sessions table, $my_location ['.$my_location.']<br>'); } 
                              if ($this->use_private_table == True)
                              {
                                    $this->so->so_set_data($my_location, $data);
                              }
                              else
                              {
                                    $GLOBALS['phpgw']->session->appsession($my_location, 'email', $data);
                              }
                        }
                        $data = array();
                        unset($data);
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING, did SET data for location: [email][dat][$acctnum][$data_name][$extra_keys] :: [email][dat]['.$acctnum.']['.$data_name.']['.$extra_keys.'] <br>'); }
                        return True;
                  }
                  elseif (($data_name == 'msg_structure')
                  || ($data_name == 'phpgw_header'))
                  {
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): (extreme-mode) data exists for "'.$data_name.'" AND has a handler<br>'); }
                        if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): "'.$data_name.'" ARRIVING param $data as it is passed into this function DUMP:', $data); } 
                        // DATA REQUIRING "extra_keys" because it is probably one element of a larger group of related data
                        // we will not waste time making a sub array, instead we add the "extra_keys" to the "location"
                        // that way we can directly access this individual datUM without the hastle of a sub array
                        
                        // "flat_pgw_struct" also known as "part_nice"
                        
                        /*!
                        @capability msg_structure and phpgw_header unique handling
                        @abstract The theory is correct and still applied, but the sub-array style is OLD OLD OLD 
                        and has been changed to "extra_keys" in the "location"
                        @discussion These 2 data items are cached ONLY IF this->session_cache_extreme IS TRUE. 
                        Notes on this data caching (see 4 notes, 2 in discussion and 2 in syntax): 
                        (1) Fetchstructure data can ONLY BE OBTAINED for the currently selected folder, 
                        so we automatically know which folder this data is applies to by calling get_arg_value("folder") 
                        HOWEVER in order to break free from this limitation in grabbing the data we are about to cache, 
                        ALWAYS pass a msgball WITH a folder value, even though it is obvious now, it may not be later. 
                        Note: folder name must be in urlencoded form, because it may contain "database unfriendly" chars 
                        This applies to "phpgw_header" data also. 
                        (Note: a msgball without a folder is *extremely* rare, and probably never occurs.) 
                        (2) "msg_structure" and "phpgw_header" caching scheme is UNUSUAL because 
                        it requires "extra_keys" param with BOTH folder AND message UID. 
                        You can provide such an  "extra_keys" param BUT that is NOT necessary because we will make 
                        the "extra_keys" string here, using the data from the "msgball" element of the data structure, 
                        see "syntax" below for how you MUST pass data into this function to save this data type. 
                        @syntax (3) The "extra_keys" string is the extra KEY which is "FOLDER_UID" 
                        where "FOLDER" is the "long" foldername, meaning it has the Namespace_Delimiter prefixing it, and 
                        where "_" is an underscore char, and
                        where "UID" is the msgnum UID that the mailserver has given the message. 
                        That "extra_keys" string is used in the "location" param of the appsession to access this specific data 
                        which is part of a larger set of data, BUT we do not need to use sub array because that single KEY 
                        will let us directly access this data.
                        
                        (4) Data Param ARRIVES to this function like this
                        msg_structure $data param arrives into this function like this:
                              $data['msgball'] = $msgball;
                              $data['msg_structure'] = $data;
                         
                        phpgw_header $data param arrives into this function like this:
                              $data['msgball'] = $msgball;
                              $data['phpgw_header'] = $data;
                        @author Angles
                        */
                        
                        if (!$extra_keys)
                        {
                              // (a) make the key, value pair that we will add to the cache
                              // EXTRA_KEYS = FOLDERNAME_MSGNUM
                              // we know the acctnum, so we already know the username and server name is correct
                              // but within that account there are many folders, all data for those folders is indexed by a unique KEY
                              // by using KEY of msgnum and foldername, we know everything we need to identify the exact data we need
                              
                              //$extra_keys = (string)$data['msgball']['msgnum'].'_'.$data['msgball']['folder'];
                              // OLD WAY -- DEPRECIATED
                              $extra_keys = $data['msgball']['folder'].'_'.(string)$data['msgball']['msgnum'];
                        }
                        // NEW METHOD -- USE THIS
                        $ex_folder = $data['msgball']['folder'];
                        $ex_msgnum = $data['msgball']['msgnum'];
                        // we know what to do here, so this data "has a handler"
                        $has_handler = True;
                        
                        // RECAST DATA ready for saving in appsession
                        //$data = serialize($data[$data_name]);
                        $data = $data[$data_name];
                        if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.']['.$ex_msgnum.'] DUMP:', $data); } 
                        // SET DATA
                        $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum] = $data;
                        // for DB sessions_db ONLY
                        if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                        || ($this->use_private_table == True))
                        {
                              $my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum;
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to put data into phpgw_app_sessions table, $my_location ['.$my_location.']<br>'); } 
                              if ($this->use_private_table == True)
                              {
                                    $this->so->so_set_data($my_location, $data);
                              }
                              else
                              {
                                    $GLOBALS['phpgw']->session->appsession($my_location, 'email', $data);
                              }
                        }
                        $data = array();
                        unset($data);
                        //if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING, did SET data for location: [email][dat][$acctnum][$data_name][$ex_folder][$ex_msgnum] :: [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.']['.$ex_msgnum.'] <br>'); }
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING, did SET data for [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.']['.$ex_msgnum.'] <br>'); }
                        return True;
                  }
                  elseif ($data_name == 'phpgw_fetchbody')
                  {
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): (extreme-mode) data exists for "'.$data_name.'" AND has a handler<br>'); }
                        if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): "'.$data_name.'" ARRIVING param $data as it is passed into this function DUMP:', $data); } 
                        
                        //(1) Data Param ARRIVES to this function like this
                        //msg_structure $data param arrives into this function like this:
                        //    $data['msgball'] = $msgball;
                        //    $data['phpgw_fetchbody'] = $data;
                        //
                        //(2) Data Param $data['msgball'] SHOULD HAVE ELEMENT "part_no" !!!!
                        //    $data['msgball']['acctnum']
                        //    $data['msgball']['folder']
                        //    $data['msgball']['msgnum']
                        //    $data['msgball']['part_no']
                        
                        if ((!isset($data['msgball']['part_no']))
                        || (!$data['msgball']['part_no']))
                        {
                              // LEAVING
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING with ERROR, returning FALSE, insufficient data for ['.$data_name.'] because NOT GIVEN $data[msgball][part_no];  $data[msgball] was ['.serialize($data['msgball']).'] <br>'); }
                              return False;
                        }
                        $strlen_phpgw_fetchbody = strlen($data['phpgw_fetchbody']);
                        if ($strlen_phpgw_fetchbody > 60000)
                        {
                              // LEAVING
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING with ERROR, returning FALSE, data TOO LARGE for ['.$data_name.']; $strlen_phpgw_fetchbody;  $data[msgball] was ['.$strlen_phpgw_fetchbody.'] <br>'); }
                              return False;
                        }
                        
                        $ex_folder = $data['msgball']['folder'];
                        $ex_msgnum = $data['msgball']['msgnum'];
                        $ex_part_no = $data['msgball']['part_no'];
                        // we know what to do here, so this data "has a handler"
                        $has_handler = True;
                        
                        // RECAST DATA ready for saving in appsession
                        //$data = serialize($data[$data_name]);
                        $data = $data[$data_name];
                        if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.']['.$ex_msgnum.']['.$ex_part_no.'] DUMP:', $data); } 
                        // SET DATA
                        $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no] = $data;
                        // for DB sessions_db ONLY
                        if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                        || ($this->use_private_table == True))
                        {
                              $my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum.';'.$ex_part_no;
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): AM Table in use or sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to put data into a table table, $my_location ['.$my_location.'], this is always needed for AM table<br>'); } 
                              if ($this->use_private_table == True)
                              {
                                    $this->so->so_set_data($my_location, $data);
                              }
                              else
                              {
                                    $GLOBALS['phpgw']->session->appsession($my_location, 'email', $data);
                              }
                        }
                        $data = array();
                        unset($data);
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING, did SET data for [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.']['.$ex_msgnum.']['.$ex_part_no.'] <br>'); }
                        return True;
                  }
                  else
                  {
                        // this data_name has no specific handler
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): NOTE: no handler for data_name='.$data_name.' in extreme-mode block<br>'); }
                        //echo 'mail_msg: save_session_cache_item('.__LINE__.'): LEAVING with ERROR: NO HANDLER for data_name='.$data_name.' in extreme-mode block, DATA WILL NOT BE SAVED, returning False<br>';
                        // LEAVING
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): LEAVING with ERROR, no data handler for: [email][dat][$acctnum][$data_name][$extra_keys] :: [email][dat]['.$acctnum.']['.$data_name.']['.$extra_keys.'] <br>'); }
                        return False;
                  }
                  
                  /*
                  // save data, assuming we've "handled" it
                  // perhaps we already saved and exited above, or even exited on error above
                  if ((isset($data) == True)
                  && ($has_handler == True))
                  {
                        if ($this->debug_session_caching > 1) { echo 'mail_msg: save_session_cache_item('.__LINE__.'): location: ['.$location.'] $app='.$app.'; $meta dump:<pre>'; print_r($data); echo '</pre>'; }
                        if ($this->session_cache_debug_nosave == False)
                        {
                              $GLOBALS['phpgw']->session->appsession($location,$app,$data);
                        }
                        else
                        {
                              echo 'mail_msg: save_session_cache_item('.__LINE__.'): session_cache_debug_nosave disallows actual saving of data<br>';
                        }
                        if ($this->debug_session_caching > 0) { echo 'mail_msg: save_session_cache_item('.__LINE__.'): LEAVING, did set data for $data_name ['.$data_name.'] $acctnum ['.$acctnum.'], returning True<br>'; }
                        return True;
                  }
                  else
                  {
                        if ($this->debug_session_caching > 0) { echo 'mail_msg: save_session_cache_item('.__LINE__.'): LEAVING on ERROR, no handler for $data_name ['.$data_name.'] $acctnum ['.$acctnum.'], OR data was empty AFTER we messed with, we screwed it up? returning False<br>'; } 
                        return False;
                  }
                  */
            }
            
            
            /*!
            @function read_session_cache_item  BEING OVERHAULED preliminary complete. 
            @abstract get data from the appsession cache, requires a "handler" to be in this function to return the desired data. 
            @param $data_name (string) The name of the data you want from the appsession cache, needs a handler in this function to return it. 
            @param $acctnum (int) optional, can pass as an empty string the this function will use the currently active account. 
            However, if you are not doiung the usual "show X msgs in a folder", such as showing search results from multiple 
            sources, then there really is no "currently active" acctnum so you SHOULD pass it in that case.
            @param $ex_folder (string) folder name, in "preped_out" urlencoded form, the data applies to, such as for restoring "folder_status_info" 
            @param $ex_msgnum (int) msgnum data applies to, currently only needed for restoring "msg_structure" and "phpgw_header" data 
            which of course need the $ex_folder param too, any data requiring this $ex_msgnum param also requires the $ex_folder param. 
            @author Angles
            @discussion used with appsession TEMPORARY DATA CACHING server-side caching of limited, 
            ephermal data, such as a list of messages from an imap search, via appsession. 
            NOTE: currently only ONE "msgball_list" is saved per account, and when user changes folders 
            within an account, we must request a new "msgball_list" for that folder from the mail server. 
            ISSUES (pro) this saves appsession from building up massive lists of msgballs, and (con) this 
            results in needless requesting of info that we may have already asked for. NOTE the msgball_list 
            does not care about number new, and only looks at number all to know when to refresh itself. 
            The primary handler of that status info is function "get_folder_status_info".  THEREFOR 
            msgball_list most important freshness meta_data is "number_all" for what we need to do. 
            We need to use an event to directly make fresh the cached data and save it back to cache, 
            so in order for the data not to look "stale" we need to do some math on the "number_all" in 
            that msgball_list meta_data also. 
            FURTHER DISCUSSION is that this is a PRIVATE function, generally you ask for a 
            peice if data with the "get_arg_value" function, or in some cases by calling a specific function 
            such as "get_msgball_list", then those functions determine HOW you get the data, including 
            IF the data should be returned from the appsession cache. Therefor, for whatever data you 
            want, YOU SHOULD NOT DIRECTLY call this function UNLESS YOU REALLY KNOW 
            what you are doing and you are doing some exotic stuff. 
            NOTE: ALL INFO SUBJECT TO CHANGE. 
            @access private
            */
            function read_session_cache_item($data_name='misc', $acctnum='', $ex_folder='', $ex_msgnum='', $ex_part_no='')
            {
                  if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): ENTERED, $data_name: ['.$data_name.']; optional: $acctnum: ['.$acctnum.'], $ex_folder: ['.$ex_folder.'], $ex_msgnum: ['.$ex_msgnum.'], $ex_part_no: ['.$ex_part_no.'] '.'<br>'); }
                  if ($this->debug_session_caching > 1) { $this->dbug->out('AND $this->session_cache_enabled='.serialize($this->session_cache_enabled).'; $this->session_cache_extreme='.serialize($this->session_cache_extreme).'<br>'); } 
                  if ($this->debug_session_caching > 0) { $this->dbug->out('<br>'); } 
                  $font_start = '<font color="purple">';
                  $font_end = '</font>';
                  
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  if ($this->session_cache_enabled == False)
                  {
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING, nothing to do since $this->session_cache_enabled='.serialize($this->session_cache_enabled).' returning False I guess<br>'); } 
                        // EXITING NOW!~!!!!!
                        return False;
                  }
                  
                  // we are here so at least some caching is allowed, get the data from appsession
                  // handle it accordingly
                  if (($data_name == 'folder_status_info')
                  && ($this->session_cache_extreme == True))
                  {
                        // GET DATA
                        // for DB sessions_db ONLY
                        if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                        || ($this->use_private_table == True))
                        {
                              $my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder;
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to read data from phpgw_app_sessions table, $my_location ['.$my_location.']<br>'); } 
                              if ($this->use_private_table == True)
                              {
                                    $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]
                                          = $this->so->so_get_data($my_location);
                              }
                              else
                              {
                                    $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]
                                          = $GLOBALS['phpgw']->session->appsession($my_location, 'email');
                              }
                              //if ($this->debug_session_caching > 2) { echo 'mail_msg: read_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.'] DUMP:<pre>'; print_r($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]); echo '</pre>'; }
                        }
                        
                        // GET DATA
                        // "folder_status_info" DATA IS ONLY appsession CACHED IF $this->session_cache_extreme IS TRUE
                        $folder_status_info = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder];
                        
                        if ($folder_status_info)
                        {
                              // HAVE WE ALREADY DONE THE TIMESTAMP CHECK ?
                              // we only need the timestamp text once
                              $already_checked_ok = $this->get_isset_arg('["folder_status_info_already_checked_ok"]["'.$ex_folder.'"]', $acctnum); 
                              //$this->get_arg_value('["msgball"]["msgnum"]')
                              
                              if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: read_session_cache_item: ('.__LINE__.') location: ['.$location.'] $app='.$app.'; $got_data DUMP:', $got_data); }
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item: ('.__LINE__.') $data_name ['.$data_name.'], is only cached is $this->session_cache_extreme is TRUE<br>'); } 
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item: ('.__LINE__.') $data_name ['.$data_name.'], timestamp check is SKIPPED if $already_checked_ok is set, $already_checked_ok: ['.serialize($already_checked_ok).']<br>'); } 
                              // this is set as a class param in file mail_msg_base
                              $timestamp_age_limit = $this->timestamp_age_limit;
                              
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item: ('.__LINE__.')  handler exists for $data_name ['.$data_name.'], this item requires param $extra_keys ['.serialize($extra_keys).']<br>'); }
                              // this special handler uses timestamp info to determine "freshness"
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item: ('.__LINE__.')  KEY ($special_extra_stuff) MUST BE FED INTO HERE AS A FOLDER IN URLENCODED FORM<br>'); } 
                              //if ($this->debug_session_caching > 1) { echo 'mail_msg: read_session_cache_item: ('.__LINE__.') $data_name ['.$data_name.'], param $special_extra_stuff gives us $specific_key ['.$specific_key.']<br>'; }
                              //$folder_status_info = unserialize($folder_status_info);
                              $timestamp_age = (time() - $folder_status_info['timestamp']);
                              if ($this->debug_session_caching > 1) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') got cached data, $timestamp_age ['.$timestamp_age.'] ; $timestamp_age_limit ['.$timestamp_age_limit.']<br>'); }
                              if ($this->debug_session_caching > 2) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') $folder_status_info DUMP:', $folder_status_info); }
                              if ($already_checked_ok == True)
                              {
                                    if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item: ('.__LINE__.') $data_name ['.$data_name.'], timestamp check will be '.'<font color="red">'.'SKIPPED'.'</font>'.' because we already checked it this page view, $already_checked_ok: ['.serialize($already_checked_ok).']<br>'); } 
                              }
                              elseif ($timestamp_age > $timestamp_age_limit) 
                              {
                                    if ($this->debug_session_caching > 1) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') LEAVING, $timestamp_age ['.$timestamp_age.'] EXCEEDS $timestamp_age_limit ['.$timestamp_age_limit.'], this data NEEDS REFRESHING, expire this element<br>'); }
                                    $got_data = '';
                                    unset($got_data);
                                    //$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder] = array();
                                    //unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]);
                                    // this does the same thing, so use this instead
                                    $this->expire_session_cache_item('folder_status_info', $acctnum, $ex_folder);
                                    
                                    if ($this->debug_session_caching > 0) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') LEAVING,  $data_name ['.$data_name.'] $specific_key ['.$specific_key.'], $timestamp_age ['.$timestamp_age.'] EXCEEDS $timestamp_age_limit ['.$timestamp_age_limit.'], this data NEEDS REFRESHING, returning False<br>'); }
                                    return False;
                                    
                              }
                              // set flag "folder_status_info_already_checked_ok" for this ex_folder so we do NOT timestamp check again the rest of THIS PAGEVIEW 
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item: ('.__LINE__.') $data_name ['.$data_name.'], set arg ["folder_status_info_already_checked_ok"]['.$ex_folder.'] so we DO NOT timestamp check this data again for the rest of this script run<br>'); } 
                              $this->set_arg_value('["folder_status_info_already_checked_ok"]["'.$ex_folder.'"]', 'timestamp_ok', $acctnum); 
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item: ('.__LINE__.') LEAVING, '.$font_start.'successfully restored ['.$data_name.']'.$font_end.' session data, $acctnum: ['.$acctnum.'],  $ex_folder ['.htmlspecialchars($ex_folder).'], data passed timestamp test<br>'); }
                              return $folder_status_info;
                        }
                        else
                        {
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item: ('.__LINE__.') LEAVING, returning False, $data_name ['.$data_name.'] had NO data stored, $acctnum: ['.$acctnum.'],  $ex_folder ['.htmlspecialchars($ex_folder).']<br>'); }
                              return False;
                        }
                              
                  }
                  elseif ($data_name == 'msgball_list')
                  {
                        
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handler exists for $data_name ['.$data_name.']<br>'); }
                        
                        /*!
                        @capability VERIFY appsession cached "msgball_list" data is still valid (in read_session_cache_item)
                        @discussion The "msgball_list" is a list of all messages in a folder sorted according to user prefs and 
                        containing additional information to make each message UID into a msgball by adding the acctnum 
                        and folder data to the msgnum. This data is put into a numbered array we call the "msgball_list". 
                        This "msgball_list" is cached in the appsession along with some meta_data that we use verify the 
                        cached msgball_list as (a) applicable to the current mailserver and user, and (b) for "freshness". 
                        For (b) "freshness", we compare the meta_data items for folder status against the 
                        current "folder_status_info" data to determine if this cached msgball_list is "fresh" or "stale". 
                        See the example for the exact data structure of the cached data. Additionally, for (a) "applicablility" 
                        just to be super safe, the cached msgball_list meta_data is tested against the current "mailsvr_callstr" and 
                        the current "mailsvr_account_username" args, not really needed but is does make us confident 
                        that the cached msgball_list applies to the same account as existed when the cache was set. It is not 
                        really known how they could not match, but this additional test can not hurt. 
                        This test is the SAME for session_cache_extreme True or False, 
                        the difference is that for session_cache_extreme TRUE, the "number_all" element 
                        of this data and the "number_all" element of the "folder_status_info" appsession cached 
                        data is manually updated so (1) they are "fresh" with respect to changes the user makes 
                        such as moving or deleting mail from a folder, and (2) so the following test which matches 
                        "number_all" elements of both data sets remain the same, so the "msgball_list" is considered 
                        "fresh" by this test.
            @example This is the meta data that is saved and tested on reading the data from cache. 
            $validity_test['fldball'] = array();
            $validity_test['fldball']['acctnum'] = $acctnum;
            $validity_test['fldball']['folder'] = $this->get_arg_value('folder', $acctnum);
            $validity_test['folder_status_info']['uidnext'] = $folder_info['uidnext'];
            $validity_test['folder_status_info']['uidvalidity'] = $folder_info['uidvalidity'];
            $validity_test['folder_status_info']['number_all']  = $folder_info['number_all'];
            $validity_test['sort'] = $this->get_arg_value('sort', $acctnum);
            $validity_test['order'] = $this->get_arg_value('order', $acctnum);
            $validity_test['mailsvr_callstr'] = $this->get_arg_value('mailsvr_callstr', $acctnum);
            $validity_test['mailsvr_account_username'] = $this->get_arg_value('mailsvr_account_username', $acctnum);
                        @author Angles
                        */
                        
                        // NEW: NOW WE NEED A FOLDER NAME TO COMPLETE THE KEY
                        // if one is not provided as a param,
                        // (a) WE MUST GET ONE, and 
                        // (b) it must be URLENCODED, or preped_out
                        if ($ex_folder != '')
                        {
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handling ['.$data_name.'] we DID get passed param $ex_folder ['.$ex_folder.']<br>'); } 
                        }
                        else
                        {
                              $ex_folder = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handling ['.$data_name.'] we did NOT get param $ex_folder so we just obtained and set it to this: ['.$ex_folder.']<br>'); } 
                        }
                        
                        // GET DATA
                        // for DB sessions_db ONLY
                        if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                        || ($this->use_private_table == True))
                        {
                              //$my_location = (string)$acctnum.';'.$data_name;
                              // WE N OW USE FOLDER DATA TOO
                              $my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder;
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to read data from a database table, $my_location ['.$my_location.']<br>'); } 
                              if ($this->use_private_table == True)
                              {
                                    //$got_data = array();
                                    //$got_data = $this->so->so_get_data($my_location);
                                    //$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]
                                    //    = $got_data;
                                    
                                    //$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]
                                    //    = $this->so->so_get_data($my_location);
                                    
                                    // TRY USING COMPRESSION
                                    //$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]
                                    //    = $this->so->so_get_data($my_location, True);
                                    
                                    // TRY USING COMPRESSION
                                    $got_data = array();
                                    $got_data = $this->so->so_get_data($my_location, True);
                                    $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]
                                          = $got_data;
                              }
                              else
                              {
                                    // NOTE: no compression available for appsession
                                    $got_data = array();
                                    $got_data = $GLOBALS['phpgw']->session->appsession($my_location, 'email');
                                    $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]
                                          = $got_data;
                                    
                                    // NOTE: no compression available for appsession
                                    //$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]
                                    //    = $GLOBALS['phpgw']->session->appsession($my_location, 'email');
                              }
                              if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.'] DUMP:', $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]); }
                        }
                        //$got_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name];
                        
                        if (($this->debug_session_caching > 2) && ($this->debug_allow_magball_list_dumps)) { $this->dbug->out('mail_msg: read_session_cache_item: $data_name ['.$data_name.'] verified NOT Stale, restored data DUMP:', $got_data); } 
                        if (!$got_data)
                        {
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING, returning False, <font color="red">$data_name ['.$data_name.'] had NO data stored</font>, $acctnum: ['.$acctnum.'], $ex_folder ['.$ex_folder.']<br>'); }
                              return False;
                        }
                        else
                        {
                              // folder_info used to test validity (stale or not) of the cached msgball_list data
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handling $data_name ['.$data_name.'] session validity and/or relevance, check against "get_folder_status_info" data<br>'); }
                              // UPDATE THIS CRAP TO HANDLE ANY FOLDER WITHIN A SINGLE ACCOUNT, not just the current one
                              $fldball = array();
                              $fldball['acctnum'] = $acctnum;
                              $fldball['folder'] = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handling ['.$data_name.'] requires obtaining "folder_status_info" we get by calling $this->get_folder_status_info('.serialize($fldball).')<br>'); } 
                              $folder_info = $this->get_folder_status_info($fldball);
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): ['.$data_name.'] will be verified as representing this folder and acctnum, then freshness checked <i>details: with the stored "folder_status_info" compared to the "folder_status_info" we just obtained a few lines up, which "folder_status_info" was itself validated in the function we got it from. The "msgball_list" is appsession cached whether in "extreme-mode" or not, as long as "enable_session_cache" is true </i><br>'); }
                              
                              // "validity_test" is what the test is like, but it's only really used for the debug data dump
                              // because the real test below gets all the same info all over again as exists in this "match_to_be_fresh" array
                              $validity_test = array();
                              $validity_test['fldball'] = array();
                              $validity_test['fldball']['acctnum'] = $acctnum;
                              $validity_test['fldball']['folder'] = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
                              $validity_test['folder_status_info']['uidnext'] = $folder_info['uidnext'];
                              $validity_test['folder_status_info']['uidvalidity'] = $folder_info['uidvalidity'];
                              $validity_test['folder_status_info']['number_all']  = $folder_info['number_all'];
                              $validity_test['sort'] = $this->get_arg_value('sort', $acctnum);
                              $validity_test['order'] = $this->get_arg_value('order', $acctnum);
                              $validity_test['mailsvr_callstr'] = $this->get_arg_value('mailsvr_callstr', $acctnum);
                              $validity_test['mailsvr_account_username'] = $this->get_arg_value('mailsvr_account_username', $acctnum);
                              
                              if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): $data_name ['.$data_name.'] validity check, freshness litmus test match this $validity_test DUMP:', $validity_test); }
                              if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): $data_name ['.$data_name.'] validity check, restored data validity $got_data[validity] DUMP:', $got_data['validity']); }
                              
                              // only is admin is using DB as session store, if "php4" session is used instead, this is not necessary
                              //if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                              //&& (!strstr($got_data['validity']['mailsvr_callstr'] ,'{')) )
                              //{
                              //    // NOTE: YOU BETTER BASE64 ENCODE "DEFANG" the "mailsvr_callstr" OR RISK b0rking your database!!!
                              //    // applies to DBs where ' " / \ and other offensive chars are not handled at the api level in phpgw
                              //    $got_data['validity']['mailsvr_callstr'] = base64_decode($got_data['validity']['mailsvr_callstr']);
                              //    // COMMENT THAT OUT if you can prove that DBs are immune to this b0rking
                              //}
                              
                              if (($got_data['validity']['fldball']['folder'] == $this->prep_folder_out($this->get_arg_value('folder', $acctnum)))
                              && ($got_data['validity']['fldball']['acctnum'] == $acctnum)
                              && ($got_data['validity']['folder_status_info']['uidnext'] == $folder_info['uidnext'])
                              && ($got_data['validity']['folder_status_info']['uidvalidity'] == $folder_info['uidvalidity'])
                              && ($got_data['validity']['folder_status_info']['number_all']  == $folder_info['number_all'])
                              && ($got_data['validity']['sort'] == $this->get_arg_value('sort', $acctnum))
                              && ($got_data['validity']['order'] == $this->get_arg_value('order', $acctnum))
                              && ($got_data['validity']['mailsvr_callstr'] == $this->get_arg_value('mailsvr_callstr', $acctnum))
                              && ($got_data['validity']['mailsvr_account_username'] == $this->get_arg_value('mailsvr_account_username', $acctnum)))
                              {
                                    if (($this->debug_session_caching > 2) && ($this->debug_allow_magball_list_dumps)) { $this->dbug->out('mail_msg: read_session_cache_item: $data_name ['.$data_name.'] verified NOT Stale, restored data DUMP:', $got_data); } 
                                    if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING, '.$font_start.'successfully restored ['.$data_name.']'.$font_end.' VALID and NOT Stale session data, $acctnum: ['.$acctnum.'] param (or obtained) $ex_folder ['.$ex_folder.']<br>'); }
                                    return $got_data['msgball_list'];
                              }
                              else
                              {
                                    if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): EXPIRE STALE ['.$data_name.'] session data for $acctnum: ['.$acctnum.'], $ex_folder ['.$ex_folder.']<br>'); }
                                    //$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name] = array();
                                    //unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]);
                                    // this is better to use
                                    //$this->expire_session_cache_item($data_name, $acctnum);
                                    // NOW WE USE FOLDER IN DATA KEY ALSO
                                    $this->expire_session_cache_item($data_name, $acctnum, $ex_folder);
                                    if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING, returning False, $data_name ['.$data_name.'] session was STALE, $acctnum: ['.$acctnum.'], $ex_folder ['.$ex_folder.']<br>'); }
                                    return False;
                              }
                        }
                  }
                  elseif ($data_name == 'mailsvr_callstr_OLD_HANDLER')
                  //elseif ($data_name == 'mailsvr_callstr')
                  {
                        // GET DATA
                        $got_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name];
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): DEFANG because session cache is "db", this handler exists for $data_name ['.$data_name.']<br>'); }
                        // this special handler decodes the database defanging
                        if ($got_data)
                        {
                              // DATABASE DEFANG, this item has "database unfriendly" chars in it so we encode it before it goes to appsession cache
                              // now we mode DECODE it coming out of the appsession cache
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): restored $data_name ['.$data_name.'] (pre-decoded) data DUMP:', $got_data); }
                              $got_data = base64_decode($got_data);
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): restored $data_name ['.$data_name.'] (decoded) data DUMP:', $got_data); }
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING, '.$font_start.'successfully restored ['.$data_name.']'.$font_end.' session data, $acctnum: ['.$acctnum.'], $extra_keys: ['.$extra_keys.']<br>'); }
                              return $got_data;
                        }
                        else
                        {
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING, returning False, $data_name='.$data_name.' had NO data stored, $acctnum: ['.$acctnum.'], $extra_keys: ['.$extra_keys.']<br>'); }
                              return False;
                        }
                  }
                  elseif (($data_name == 'mailsvr_namespace')
                  || ($data_name == 'folder_list')
                  || ($data_name == 'mailsvr_callstr'))
                  {
                        // GET DATA
                        // for DB sessions_db ONLY
                        if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                        || ($this->use_private_table == True))
                        {
                              $my_location = (string)$acctnum.';'.$data_name;
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to read data from phpgw_app_sessions table, $my_location ['.$my_location.']<br>'); } 
                              if ($this->use_private_table == True)
                              {
                                    $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]
                                          = $this->so->so_get_data($my_location);
                              }
                              else
                              {
                                    $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]
                                          = $GLOBALS['phpgw']->session->appsession($my_location, 'email');
                              }
                              if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.'] DUMP:', $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]); }
                        }
                        $got_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name];
                        
                        // "mailsvr_callstr" if session is NOT "db" is handled here because no database defang if necessary
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handler exists for $data_name ['.$data_name.']<br>'); }
                        // this is not really a special handler
                        if ($got_data)
                        {
                              if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): restored $data_name ['.$data_name.'] data DUMP:', $got_data); }
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING, '.$font_start.'successfully restored ['.$data_name.']'.$font_end.' session data, $acctnum: ['.$acctnum.'], $extra_keys: ['.$extra_keys.']<br>'); }
                              return $got_data;
                        }
                        else
                        {
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING, returning False, $data_name ['.$data_name.'] had NO data stored, $acctnum: ['.$acctnum.'], $extra_keys: ['.$extra_keys.']<br>'); }
                              return False;
                        }
                        
                  }
                  elseif (($this->session_cache_extreme == True)
                  && (
                              ($data_name == 'msg_structure')
                        ||  ($data_name == 'phpgw_header'))
                  )
                  {
                        // THIS DATA IS NEVER CACHED IF $this->session_cache_extreme IS FALSE
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handler exists for $data_name ['.$data_name.']<br>'); }
                        // this IS a special handler because we do not return the entire cached array of "msg_structure" elements, 
                        // we only return ONE SINGLE "msg_structure" (or "phpgw_header" ) value from that array, 
                        // if it exists in that array. 
                        // the array can exist but not have the specific element we are looking for
                        // NOTE: we need param $special_extra_stuff because other cached items are returned whole
                        // but "msg_structure" (or "phpgw_header" ) need that EXTRA peice of information 
                        // to return only a portion of cached data. 
                        
                        // GET DATA
                        // for DB sessions_db ONLY
                        if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                        || ($this->use_private_table == True))
                        {
                              $my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum;
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to read data from phpgw_app_sessions table, $my_location ['.$my_location.']<br>'); } 
                              if ($this->use_private_table == True)
                              {
                                    $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum]
                                          = $this->so->so_get_data($my_location);
                              }
                              else
                              {
                                    $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum]
                                          = $GLOBALS['phpgw']->session->appsession($my_location, 'email');
                              }
                              //if ($this->debug_session_caching > 2) { echo 'mail_msg: read_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.'] ['.$ex_msgnum.']DUMP:<pre>'; print_r($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum]); echo '</pre>'; }
                        }
                        $got_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum];
                        
                        if ($got_data)
                        {
                              if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): found an existing array of items for $data_name ['.$data_name.'] data DUMP:', $got_data); }
                              // we need more info than the usual cached item to get what we want, because we want a SINGLE element only
                              // the array KEY in the key,value pair is passed in param $special_extra_stuff
                              
                              // SUCCESS - desired single element within that array does exist
                              // also unserialize it back into an object
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING, '.$font_start.'successfully restored ['.$data_name.']'.$font_end.' session data for [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.']['.$ex_msgnum.']<br>'); } 
                              // NOTE unserializing an already inserialized thing gives you NOTHING, so damn well better serialize this on saving to the appsession cache.
                              return $got_data;
                              
                        }
                  }
                  elseif (($this->session_cache_extreme == True)
                  && ($data_name == 'phpgw_fetchbody'))
                  {
                        // THIS DATA IS NEVER CACHED IF $this->session_cache_extreme IS FALSE
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handler exists for $data_name ['.$data_name.']<br>'); }
                        
                        // GET DATA
                        // for DB sessions_db ONLY
                        if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                        || ($this->use_private_table == True))
                        {
                              $my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum.';'.$ex_part_no;
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): AM table in use or sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to read data from a table table, $my_location ['.$my_location.']<br>'); } 
                              if ($this->use_private_table == True)
                              {
                                    $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no]
                                          = $this->so->so_get_data($my_location);
                              }
                              else
                              {
                                    $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no]
                                          = $GLOBALS['phpgw']->session->appsession($my_location, 'email');
                              }
                              //if ($this->debug_session_caching > 2) { echo 'mail_msg: read_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.'] ['.$ex_msgnum.']DUMP:<pre>'; print_r($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no]); echo '</pre>'; }
                        }
                        $got_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no];
                        
                        if ($got_data)
                        {
                              if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): found an existing array of items for $data_name ['.$data_name.'] data DUMP:', $got_data); }
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING, '.$font_start.'successfully restored ['.$data_name.']'.$font_end.' session data for [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.']['.$ex_msgnum.']['.$ex_part_no.']<br>'); } 
                              return $got_data;
                              
                        }
                        else
                        {
                              if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING, returning False, $data_name ['.$data_name.'] had NO data stored, $acctnum: ['.$acctnum.'], $extra_keys: ['.$extra_keys.']<br>'); }
                              return False;
                        }
                  }
                  if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): LEAVING with ERROR, NO SPECIAL HANDLER exists for $data_name ['.$data_name.']<br>'); } 
                  //return $got_data;
                  return False;
            }
            
            /*!
            @function expire_session_cache_item  BEING OVERHAULED  preliminary complete. 
            @abstract ?
            @discussion used with appsession TEMPORARY DATA CACHING server-side caching of limited, 
            ephermal data, such as a list of messages from an imap search, via appsession
            NOTE we get the old data, if any, to see whether it was an array or string, HOWEVER using get_arg_value 
            for "folder_list" WILL ATTEMPT A LOGIN so to avoid that we use "_direct_access_arg_value" instead. 
            @author Angles
            @access private
            */
            // ---- session-only data cached to appsession  ----
04918             function expire_session_cache_item($data_name='misc',$acctnum='', $ex_folder='', $ex_msgnum='', $ex_part_no='')
            {           
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): ENTERED, $this->session_cache_enabled='.serialize($this->session_cache_enabled).', $data_name: ['.$data_name.'], $acctnum (optional): ['.$acctnum.'], $extra_keys: ['.$extra_keys.']<br>'); }
                  // process $extra_keys input
                  
                  // BEFORE we do anything else, if extreme-mode is OFF then we MANDATORY, ALWAYS, NO-MATTER-WHAT expire "folder_status_info"
                  // NOTE: in non-extreme cache mode, "folder_status_info" is exipred ALLWAYS whenever we expire ANYTHING
                  // why? because I'm assuming (correctly?) that if we are expiring something then it *probably* effects "folder_status_info" too
                  // NOTE also that in extreme mode, "folder_status_info" is NOT put in L1 cache EVER, we directly use appsession.
                  if ($this->session_cache_extreme == False)
                  {
                        // ---  get rid of any L1 cache for "folder_status_info"  ---
                        if ($this->debug_session_caching > 1) { $this->dbug->out('class_msg: expire_session_cache_item('.__LINE__.'): non-extreme mode ALWAYS EXPIRES "folder_status_info" with ANYTHING ELSE, because non-extreme mode uses "folder_status_info" L1/class var cache only, NO appsession cache used in non-extreme <br>'); }
                        if ($this->debug_session_caching > 1) { $this->dbug->out('class_msg: expire_session_cache_item('.__LINE__.'): NOTE non-extreme mode uses "folder_status_info" L1/class var cache only, NO appsession cache used in non-extreme <br>'); }
                        // cache data in a class var (L1 Cache)
                        // ALWAYS expire "folder_status_info" because many time this expire function is called because of a message move or delete
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): (non-extreme mode) Mandatory clearing of L1 cache/class data "folder_status_info" <br>'); }
                        $empty_array = array();
                        //$this->set_arg_value('folder_status_info', $empty_array, $acctnum);
                        $this->set_arg_value('["folder_status_info"]["'.$ex_folder.'"]', $empty_array, $acctnum);

                  }
                  
                  // now eliminate the EXPIRED data, 1st get rid of any L1 cache it it exists for this item
                  if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): checking for L1 cache/class var for $data_name = ['.$data_name.']<br>'); }
                  if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): NOTE when session_class_extreme is True, "folder_status_info" is NOT cached in L1 cache/class var, only in appsession<br>'); }
                  
                  // FIRST -- EXPIRE ANY L1 CACHE for this data_name
                  if (($this->get_isset_arg($data_name, $acctnum))
                  && (strstr($data_name, 'folder_status_info') == False))
                  {
                        // NOTE L1 cached "folder_status_info" was (a) expired above for non-extreme mode and (b) never uses L1 cached in extreme mode
                        //$old_content = $this->get_arg_value($data_name, $acctnum);
                        if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): does L1 cache/class for ['.$data_name.'], to find out we call "_direct_access_arg_value" <b>THUS FOLDER_LIST WILL NOT CAUSE A LOGIN</b> using "get_arg_value" for folder_list WILL ATTEMPT A LOGIN!<br>'); }
                        $old_content = $this->_direct_access_arg_value($data_name, $acctnum);
                        if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): found and clearing L1 cache/class for ['.$data_name.'] OLD value DUMP:', $old_content); }
                        if (gettype($old_content) == 'array')
                        {
                              $empty_data = array();
                        }
                        else
                        {
                              $empty_data = '';
                        }
                        // set the arg item to this blank value, effectively clearing/expiring it
                        $this->set_arg_value($data_name, $empty_data, $acctnum);
                  }
                  
                  // SECOND -- EXPIRE ANY APPSESSION CACHE for this data_name
                  // ---  now get rid of any "$data_name" value saved in the session cache  ---
                  // also note that in extreme mode we expire "folder_status_info" here because 
                  //    (a) it would be handled above for non-extreme mode and 
                  //    (b) it uses subitems so we need to handle it here so we expire single sub item and not the whole thing
                  
                  // DELETE DATA
                  // for DB sessions_db ONLY
                  if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
                  || ($this->use_private_table == True))
                  {
                        // generic fallback value
                        $my_location = (string)$acctnum.';'.$data_name;
                        if (((string)$ex_folder != '')
                        && ((string)$ex_msgnum != '')
                        && ((string)$ex_part_no != ''))
                        {
                              $my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum.';'.$ex_part_no;
                        }
                        elseif (((string)$ex_folder != '')
                        && ((string)$ex_msgnum != ''))
                        {
                              $my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum;
                        }
                        elseif ((string)$ex_folder != '')
                        {
                              $my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder;
                        }
                        
                        if ($this->use_private_table == True)
                        {
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): we are using private table, SO we have this additional step to blank data in the "phpgw_anglemail" table, $my_location ['.$my_location.']<br>'); } 
                              $this->so->so_delete_data($my_location);
                        }
                        else
                        {
                              if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to blank data in the "phpgw_app_sessions" table, $my_location ['.$my_location.']<br>'); } 
                              $GLOBALS['phpgw']->session->appsession($my_location, 'email', '');
                        }
                  }
                  
                  //if ((!$ex_folder)
                  //&& (!$ex_msgnum)
                  if (((string)$ex_folder == '')
                  && ((string)$ex_msgnum == '')
                  && (isset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name])))
                  {
                        $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name] = '';
                        unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]);
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): LEAVING return True, did expire existing data for [email][dat]['.$acctnum.']['.$data_name.']<br>'); }
                        return True;
                  }
                  //elseif (($ex_folder)
                  //&& (!$ex_msgnum)
                  elseif (((string)$ex_folder != '')
                  && ((string)$ex_msgnum == '')
                  && (isset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder])))
                  {
                        $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder] = '';
                        unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]);
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): LEAVING return True, did expire existing data for [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.']<br>'); }
                        return True;
                  }
                  //elseif (($ex_folder)
                  //&& ($ex_msgnum)
                  elseif (((string)$ex_folder != '')
                  && ((string)$ex_msgnum != '')
                  && (isset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum])))
                  {
                        $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum] = '';
                        unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum]);
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): LEAVING return True, did expire existing data for [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.']['.$ex_msgnum.']<br>'); }
                        return True;
                  }
                  elseif (((string)$ex_folder != '')
                  && ((string)$ex_msgnum != '')
                  && ((string)$ex_part_no != '')
                  && (isset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no])))
                  {
                        $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no] = '';
                        unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no]);
                        if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): LEAVING return True, did expire existing data for [email][dat]['.$acctnum.']['.$data_name.']['.$ex_folder.']['.$ex_msgnum.']['.$ex_part_no.']<br>'); }
                        return True;
                  }
                  if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg: expire_session_cache_item('.__LINE__.'): LEAVING, return False, data did not exist for [email][dat]['.$acctnum.']['.$data_name.']['.serialize($ex_folder).']['.serialize($ex_msgnum).']['.serialize($ex_part_no).']<br>'); } 
                  return False;
            }

            /*!
            @function clearcache_all
            @abstract will remove all cached data for this user, all cahced data for all email accounts
            @param (string) $called_by optional for debug information
            @author Angles
            @discussion This function is a passthru directly to the so class, calls a low level function that 
            simply wipes ANY and ALL email cached data for this user, all accounts, all data. 
            Not to be confused with "batch_expire_cached_items", which is used with the extreme caching events system. 
            This function wipes it all.
            @access Public
            */
05070             function clearcache_all($called_by='not_specified')
            {
                  if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg_wrappers: clearcache_all: ('.__LINE__.') ENTERING, called by ['.$called_by.'],  $this->session_cache_extreme is ['.serialize($this->session_cache_extreme).']<br>'); } 
                  if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg_wrappers: clearcache_all: ('.__LINE__.') about to call $this->so->expire_db_session_bulk_data("clearcache_all", True); <br>'); } 
                  // True means $wipe_absolutely_everything = True
                  $this->so->expire_db_session_bulk_data('mail_msg_wrappers.clearcache_all line('.__LINE__.') which was called by '.$called_by, True);
                  if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg_wrappers: clearcache_all: ('.__LINE__.') unset $GLOBALS["email_dbsession_compat"] is it exists, may not be necessary? <br>'); } 
                  // is this necessary
                  if (isset($GLOBALS['email_dbsession_compat']))
                  {
                        $GLOBALS['email_dbsession_compat'] = array();
                        unset($GLOBALS['email_dbsession_compat']);
                  }
                  if ($this->debug_session_caching > 0) { $this->dbug->out('mail_msg_wrappers: clearcache_all: ('.__LINE__.') LEAVING, called by ['.$called_by.'],  $this->session_cache_extreme is ['.serialize($this->session_cache_extreme).']<br>'); } 
            }

            
            /**************************************************************************\
            * END  CACHING HANDLERS                                           *
            * - - - - - - - - - - - - - - - - - - - - - - - - -                                                   *
            * BEGIN PARAM / ARGS / PREFS  ACCESS FUNCTIONS              *
            \**************************************************************************/
            
            /*!
            @capability OOP-Style Access Methods to Private Object Properties
            @abstract  simple access methods to read and set data, with transparent account number handling
            @discussion When multiple email accounts are enables, they may even be active at the same time,
            thus the properties and preferences for any individual email account must be distinctly accessable 
            for each email account with as little brain damage to the developer as possible. These access methods 
            server two purposes:
            (1) centralize access to all params and oprefs into a common, standardized methodology, and
            (2) these access functions also transparently handly the dirty work of tracking which email account
            the data applies to, takes care of any special handling a param may require, and it's classic OOP style.
            With the exception of a few paramaters/arguments that are not specific to any individual email acount,
            such as for private, internal object core properties, the developer need only use these functions to 
            access object params, arguments, and preferences.
            @author Angles
            */
            
            /*!
            @function get_acctnum
            @abstract  read which account number the object is currently activated on
            @param $unset_returns_default  boolean  default True. If no acctnum is currently set,
            should this function return a boolean False or a hardcoded "fallback default" account number,
            typically integer 0. Default is to return a fallback default account number.
            @returns (most typically) the internal account number of the currently active email account, 
            but can be set, via the $unset_returns_default param, 
            @discussion When multiple email accounts are enabled, all arg/param and preference access 
            functions "pivot" off of this "object->acctnum" property, it serves essentially as the array key 
            which maps the various access functions to the data of the intended account number.
            DEVELOPERS NOTE: The integer zero returned by this function can sometimes be mistaken
            as "empty" of "false", when using conditionals such as
            if ($my_acctnum) { then do this };
            may incorrectly interper integer 0 as a "false" and this example conditional would not behave 
            as expected, since there is infact a valid acount number of 0 in the variable. The preferred test 
            for that type of condition is:
            if ((string)$my_acctnum != '') { then do this };
            which produces a more desirable result.
            @author Angles
            */
05130             function get_acctnum($unset_returns_default=True)
            {
                  if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: get_acctnum: ENTERING, (parm $unset_returns_default=['.serialize($unset_returns_default).'])<br>'); }
                  
                  if ((isset($this->acctnum))
                  && ((string)$this->acctnum != ''))
                  {
                        if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: get_acctnum: LEAVING, $this->acctnum exists, returning it: '.serialize($this->acctnum).'<br>'); } 
                        return $this->acctnum;
                  }
                  // ok, no useful acctnumber exists, what should we do
                  elseif ($unset_returns_default == True)
                  {
                        
                        if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: get_acctnum: LEAVING, NO $this->acctnum exists, returning $this->fallback_default_acctnum : '.serialize($this->fallback_default_acctnum).'<br>'); } 
                        return $this->fallback_default_acctnum;
                  }
                  else
                  {
                        if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: get_acctnum: LEAVING, NO $this->acctnum exists, returning FALSE<br>'); } 
                        return False;
                  }
            }
            
            /*!
            @function set_acctnum
            @abstract  instruct the object which email account is the desired active account for all params,
            args, and preferences should refer to.
            @param $acctnum  integer  
            @result True if a valid param $acctnum is given and the object->acctnum value is set, False if 
            invalid data is passed in the param.
            @author Angles
            @discussion ?
            @access public
            */
05165             function set_acctnum($acctnum='')
            {
                  if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: set_acctnum: ENTERING, (parm $acctnum=['.serialize($acctnum).'])<br>'); }
                  if ((isset($acctnum))
                  && ((string)$acctnum != ''))
                  {
                        $this->acctnum = $acctnum;
                        if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: set_acctnum: LEAVING, returning True, made $this->acctnum = $acctnum ('.serialize($acctnum).')<br>'); }
                        return True;
                  }
                  else
                  {
                        if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: set_acctnum: LEAVING, returning False, value $acctnum not sufficient to set $this->acctnum<br>'); }
                        return False;
                  }
            }
            
            
            /* * * * * * * * * * * * * * * * * *
            * OOP-Style Access Methods for Preference Values
            * * * * * * * * * * * * * * * * * */
            /*!
            @function get_pref_value
            @abstract OOP access of pref items for any given email account. 
            @param $pref_name (string) id if the desired pref item 
            @param $acctnum (int) OPTIONAL acctnum this applies to, "current acctnum" will be supplied if this arg is not given. 
            @result value of the desired pref IF it exists, if no you are returned nothing (null). 
            @discussion ?
            @access public
            @author Angles
            */
05196             function get_pref_value($pref_name='',$acctnum='')
            {
                  if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_pref_value: ENTERING, $pref_name: ['.$pref_name.'] $acctnum: ['.$acctnum.']'.'<br>'); }
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                        if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers): get_pref_value: obtained acctnum from "$this->get_acctnum()", got $acctnum: ['.$acctnum.']'.'<br>'); }
                  }
                  
                  if ((isset($pref_name))
                  && ((string)$pref_name != '')
                  && (isset($this->a[$acctnum]['prefs'][$pref_name])))
                  {
                        if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_pref_value: LEAVING, returning $this->a['.$acctnum.'][prefs]['.$pref_name.'] : ['.$this->a[$acctnum]['prefs'][$pref_name].'] <br>'); }
                        return $this->a[$acctnum]['prefs'][$pref_name];
                  }
                  else
                  {
                        // arg not set, or invalid input $arg_name
                        if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_pref_value: LEAVING with ERRROR, pref item was not found<br>'); }
                        return;
                  }
            }
            
            /*!
            @function set_pref_value
            @abstract set pref value for a pref items for any given email account, TEMPORARY to this page view. 
            @param $pref_name  (string) 
            @param $this_value (string, int, or array)
            @param $acctnum  (int) OPTIONAL 
            @result True if pref value was set, False on failure to set the item, such as invalid data is passed in the param.
            @author Angles
            @discussion Used when certain prefs are derived from other prefs, they may not actually exist in the 
            prefs DB, so we figure them out in the code somewhere and set the value here, Lasts only as long a 
            a single script run. 
            @access public
            */
05234             function set_pref_value($pref_name='', $this_value='', $acctnum='')
            {
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  if ((isset($pref_name))
                  && ((string)$pref_name != ''))
                  {
                        $this->a[$acctnum]['prefs'][$pref_name] = $this_value;
                        // return True to indicate success
                        return True;
                  }
                  else
                  {
                        // return False to indicate invalid input $arg_name
                        return False;
                  }
            }
            
            /*!
            @function get_isset_pref
            @abstract Check if a given preference is set
            @param $pref_name  (string)
            @param $acctnum  (int) OPTIONAL 
            @result True if preference $pref_name value exists, False if not
            @author Angles
            @discussion It is common the boolean preference items are simply not set if their value 
            is supposed to be false. This function can be used for this discovery. Note that some 
            string preferences items, such as the email sig, can be set yet have a value of an empty string, 
            in this case this function follows strict logic and returns True because the preference exists. 
            Similarly, uwash mail location is another example of a preference item where an empty string 
            is a valid value.
            @access public
            */
05271             function get_isset_pref($pref_name='',$acctnum='')
            {
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  // error check
                  if ((isset($pref_name))
                  && ((string)$pref_name != '')
                  && (isset($this->a[$acctnum]['prefs'][$pref_name])))
                  {
                        return True;
                  }
                  else
                  {
                        // arg not set, or invalid input $arg_name
                        return False;
                  }
            }
            
            /*!
            @function unset_pref
            @abstract unset a preference item.
            @param $pref_name  (string)
            @param $acctnum  (int) OPTIONAL 
            @result True if $pref_name existed and was made unset, False on failure, such as $pref_name not 
            being set in the first place. This function can not unset something that does not exist to begin with.
            @author Angles
            @discussion ?
            @access public
            */
05303             function unset_pref($pref_name='', $acctnum='')
            {
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  if ((isset($pref_name))
                  && ((string)$pref_name != ''))
                  {
                        $this->a[$acctnum]['prefs'][$pref_name] = '';
                        unset($this->a[$acctnum]['prefs'][$pref_name]);
                        // return True to indicate success
                        return True;
                  }
                  else
                  {
                        // return False to indicate invalid input $pref_name
                        return False;
                  }
            }
            
            /*!
            @function get_all_prefs
            @abstract get the entire preference data array FOR ONE ACCOUNT
            @param $acctnum  (int) OPTIONAL 
            @result none
            @author Angles
            @discussion The result are not the raw preferences directly from the database, this function returns 
            the preference array for an email account as explosed to the email app, that is these are preferences that 
            have passed through some logic to process and normalize them,
            @access public
            */
05337             function get_all_prefs($acctnum='')
            {
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  if (isset($this->a[$acctnum]['prefs']))
                  {
                        return $this->a[$acctnum]['prefs'];
                  }
                  else
                  {
                        // arg not set, or invalid input $arg_name
                        return;
                  }
            }
            
            /*!
            @function set_pref_array
            @abstract set the entire preference data array FOR ONE ACCOUNT
            @param $pref_array_data  (array) either (a) correctly formed email pref array data, or (b) an empty array
            @param $acctnum  (int) OPTIONAL 
            @result boolean True is successfully sets $pref_array_data, False to indicate all we did was clear the args, no data was fed
            @author Angles
            @discussion NOTE  the first thing this function does is clear the existing preference array for the 
            emal account. This happens no matter what. This effectively is a way to clear an accounts email preference 
            array by passing an empty array, which can be useful in certain situations. More commonly this function 
            is used to set the entire preference array for an account in one operation. In that case you better know 
            what youre doing, $pref_array_data must be correctly formed emai pref array data. By clearing the 
            existing preference array no matter what, this is why a return value of False indicates that, while no 
            new preference data was set, still something did occur and that was the clearing of any pre-existing 
            preference array.
            @access private
            */
05373             function set_pref_array($pref_array_data='', $acctnum='')
            {
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  $this->a[$acctnum]['prefs'] = array();
                  
                  if ((isset($pref_array_data))
                  && (count($pref_array_data > 0)))
                  {
                        $this->a[$acctnum]['prefs'] = $pref_array_data;
                        // return True to indicate we filled, not just cleared
                        return True;
                  }
                  else
                  {
                        // return False to indicate all we did was clear the args, no data was fed
                        return False;
                  }
            }
            
            
            /* * * * * * * * * * * * * * * * * *
            * OOP-Style Access Methods for Class Params/Args Values
            * * * * * * * * * * * * * * * * * */
            /*!
            @function _get_arg_is_known
            @abstract utility function for private use, tests if a given  $arg_name is in the $this->known_external_args[] array.
            @param $arg_name  (string) 
            @param $calling_function_name  (string) used for debug output
            @result boolean
            @author Angles
            @discussion ?
            @access private
            */
05411             function _get_arg_is_known($arg_name='', $calling_function_name='')
            {
                  // skip this unless debug level 4
                  if ($this->debug_args_oop_access < 4)
                  {
                        return False;
                  }
                  
                  if ($arg_name == '')
                  {
                        return False;
                  }
                  if ($calling_function_name == '')
                  {
                        $calling_function_name == 'UNSPECIFIED';
                  }
                  // loop thru known externally controlled args
                  $finding = False;
                  $report = '';
                  for($i=0; $i < count($this->known_external_args); $i++)
                  {
                        if ($arg_name == $this->known_external_args[$i])
                        {
                              $finding = True;
                              $report = '*is* known (external)';
                              break;
                        }
                  }
                  // check internal args
                  for($i=0; $i < count($this->known_internal_args); $i++)
                  {
                        if ($arg_name == $this->known_internal_args[$i])
                        {
                              $finding = True;
                              $report = '*is* known (internal)';
                              break;
                        }
                  }
                  if (!$finding)
                  {
                        $report = '*NOT* KNOWN *NOT* KNOWN *NOT* KNOWN *NOT* KNOWN';
                  }
                  if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): _arg_is_known: TEST: '.$report.' ; $arg_name: ['.$arg_name.'] called by $calling_function_name: ['.$calling_function_name.'] '.'<br>'); }
                  return $finding;
            }
            
            /*!
            @function get_isset_arg
            @abstract Check if a given variable is set
            @param $arg_name  (string)
            @param $acctnum  (int) OPTIONAL 
            @param $extra_keys (string) for SPECIAL INTERNAL use only. Rarely used. DEPRECIATED use sublevels instead. 
            @result boolean
            @author Angles
            @discussion ?
            @access public
            */
05468             function get_isset_arg($arg_name='',$acctnum='', $extra_keys='')
            {
                  if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: ENTERING, $arg_name: ['.$arg_name.'] $acctnum: ['.$acctnum.']'.'<br>'); }
                  if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'get_isset_arg'); }
                  
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                        if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: obtained $acctnum from $this->get_acctnum(): ['.$acctnum.']'.'<br>'); }
                  }
                  
                  /*
                  // OOP VERSION if PROBLEMATIC
                  // but it may not give intended answer because
                  // "get_arg_value" will handoff processing to specialized functions that WILL fill the value
                  // sometimes simply with default values, which would cause this function to return unexpected results
                  $test_this = $this->get_arg_value($arg_name, $acctnum);
                  if (isset($test_this))
                  {
                        return True;
                  }
                  */
                  
                  // Best Version at this time, if something is not set, DO NOT handoff to a support function to fill it
                  // that way we can return false if something is indeed NOT set
                  
                  // $arg_name has sub-levels
                  if ((isset($arg_name))
                  && ((string)$arg_name != '')
                  && (strstr($arg_name, '][')))
                  {
                        if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_isset_arg: $arg_name is requesting <font color="green"> sub-levels </font> array element(s), $arg_name: '.serialize($arg_name).'<br>'); }
                        // ["msgball"]["acctnum"]
                        $exploded = array();
                        $exploded = explode('][', $arg_name);
                        // ["msgball"
                        //  "acctnum"]
                        if ((!is_array($exploded))
                        || (count($exploded) < 2)
                        || (count($exploded) > 3))
                        {
                              if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_isset_arg: ERROR: exploded has wrong number of elements<br>'); }
                              echo 'error line '.__LINE__.'<br>';
                        }
                        else
                        {
                              // only 2 or 3 levels deep, we can do this making an array
                              for ($i=0;$i<count($exploded);$i++)
                              {
                                    // is there actual data in this array element
                                    if ((isset($exploded[$i])) && ($exploded[$i] != ''))
                                    {
                                          $exploded[$i] = str_replace('[', '', $exploded[$i]);
                                          $exploded[$i] = str_replace(']', '', $exploded[$i]);
                                          $exploded[$i] = str_replace('"', '', $exploded[$i]);
                                    }
                                    else
                                    {
                                          if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_isset_arg: ERROR: some of exploded was not set<br>'); }
                                          echo 'error line '.__LINE__.'<br>';
                                    }
                              }
                              if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_isset_arg: for this test cleaned $exploded args are ['.htmlspecialchars(serialize($exploded)).']<br>'); }
                              // now we have the 2 or 3 things we need
                              if (count($exploded) == 2)
                              {
                                    $thing1 = $exploded[0];
                                    $thing2 = $exploded[1];
                                    if (isset($this->a[$acctnum]['args'][$thing1][$thing2]))
                                    {
                                          if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: LEAVING returning TRUE for isset($this->a['.$acctnum.'][args]['.$thing1.']['.$thing2.'])<br>'); }
                                          return True;
                                    }
                              }
                              elseif (count($exploded) == 3)
                              {
                                    $thing1 = $exploded[0];
                                    $thing2 = $exploded[1];
                                    $thing3 = $exploded[2];
                                    if (isset($this->a[$acctnum]['args'][$thing1][$thing2][$thing3]))
                                    {
                                          if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: LEAVING returning TRUE for isset($this->a['.$acctnum.'][args]['.$thing1.']['.$thing2.']['.$thing3.'])<br>'); }
                                          return True;
                                    }
                              }
                        }
                  }
                  // $arg_name has NO sub-levels
                  elseif ((isset($this->a[$acctnum]['args'][$arg_name]))
                  && (!$extra_keys))
                  {
                        if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: LEAVING returning $this->a[$acctnum('.$acctnum.')][args][$arg_name]: '.$this->a[$acctnum]['args'][$arg_name].'<br>'); }
                        return True;
                  }
                  elseif ((isset($this->a[$acctnum]['args'][$arg_name][$extra_keys]))
                  && ($extra_keys))
                  {
                        if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: LEAVING <font color="red">(using DEPRECIATED arg $EXTRA_KEYS) </font> returning $this->a[$acctnum('.$acctnum.')][args][$arg_name][$extra_keys]: '.$this->a[$acctnum]['args'][$arg_name][$extra_keys].'<br>'); }
                        return True;
                  }
                  // if we get here, it was not set
                  if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: LEAVING returning False<br>'); }
                  return False;
            }
            
            /*!
            @function unset_arg
            @abstract unset a class variable
            @param $arg_name  (string)
            @param $acctnum  (int) OPTIONAL 
            @result boolean True if $arg_name existed and was made unset, False on failure, such as $arg_name not 
            being set in the first place. This function can not unset something that does not exist to begin with.
            NOTE right now ther is NO call to this that requires more than 1 level array, ie it is never called needing to 
            unset sub-level array data, although the code for it is here.
            @author Angles
            @discussion ?
            @access public
            */
05587             function unset_arg($arg_name='', $acctnum='')
            {
                  if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'unset_arg'); }
                  
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  // $arg_name has sub-levels
                  if ((isset($arg_name))
                  && ((string)$arg_name != '')
                  && (strstr($arg_name, '][')))
                  {
                        $exploded = array();
                        $exploded = explode('][', $arg_name);
                        // ["msgball"
                        //  "acctnum"]
                        if ((!is_array($exploded))
                        || (count($exploded) < 2)
                        || (count($exploded) > 3))
                        {
                              if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg: ERROR: exploded has wrong number of elements<br>'); }
                              echo 'error line '.__LINE__.'<br>';
                        }
                        else
                        {
                              /*
                              // NO NEED FOR REFERENCE USE HERE - what does unset(reference) really do anyway?
                              // only 2 or 3 levels deep, we can do this making an array
                              if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg: $arg_name has <font color="green"> sub-levels </font> <br>'); }
                              for ($i=0;$i<count($exploded);$i++)
                              {
                                    // is there actual data in this array element
                                    if ((isset($exploded[$i])) && ($exploded[$i] != ''))
                                    {
                                          $exploded[$i] = str_replace('[', '', $exploded[$i]);
                                          $exploded[$i] = str_replace(']', '', $exploded[$i]);
                                          $exploded[$i] = str_replace('"', '', $exploded[$i]);
                                    }
                                    else
                                    {
                                          if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg: ERROR: some of exploded was not set<br>'); }
                                          echo 'error line '.__LINE__.'<br>';
                                    }
                              }
                              // try using REFERENCE
                              if (count($exploded) == 2)
                              {
                                    $thing1 = $exploded[0];
                                    $thing2 = $exploded[1];
                                    $ref_unset_me =& $this->a[$acctnum]['args'][$thing1][$thing2];
                                    
                              }
                              elseif (count($exploded) == 3)
                              {
                                    $thing1 = $exploded[0];
                                    $thing2 = $exploded[1];
                                    $thing3 = $exploded[2];
                                    $ref_unset_me =& $this->a[$acctnum]['args'][$thing1][$thing2][$thing3];
                              }
                              else
                              {
                                    if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg: ERROR: some of exploded was not set<br>'); }
                                    echo 'error line '.__LINE__.'<br>';
                              }
                              // make it equal a blank string
                              $ref_unset_me = '';
                              // unset it (aka UNLINK)
                              unset($ref_unset_me);
                              // now were we successful?
                              if (isset($ref_unset_me))
                              {
                                    if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg (sublevels): LEAVING returning False, FAILED to unset arg, because VERIFY showed what $ref_unset_me points to is still set<br>'); } 
                                    return False;
                              }
                              else
                              {
                                    if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg (sublevels): LEAVING returning True, because VERIFY showed what $ref_unset_me points to is unset<br>'); } 
                                    return True;
                              }
                              */
                              // not using reference
                              // now we have the 2 or 3 things we need
                              if (count($exploded) == 2)
                              {
                                    $thing1 = $exploded[0];
                                    $thing2 = $exploded[1];
                                    // make it equal a blank string
                                    $this->a[$acctnum]['args'][$thing1][$thing2] = '';
                                    // unset it
                                    unset($this->a[$acctnum]['args'][$thing1][$thing2]);
                                    // now were we successful?
                                    if (isset($this->a[$acctnum]['args'][$thing1][$thing2]))
                                    {
                                          if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg (sublevels): LEAVING returning False, FAILED to unset arg, because VERIFY showed $this->a[$acctnum][args][$thing1][$thing2] is still set<br>'); } 
                                          return False;
                                    }
                                    else
                                    {
                                          if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg (sublevels): LEAVING returning True, because VERIFY showed $this->a[$acctnum][args][$thing1][$thing2] is unset<br>'); } 
                                          return True;
                                    }
                              }
                              elseif (count($exploded) == 3)
                              {
                                    $thing1 = $exploded[0];
                                    $thing2 = $exploded[1];
                                    $thing3 = $exploded[2];
                                    // make it equal a blank string
                                    $this->a[$acctnum]['args'][$thing1][$thing2][$thing3] = '';
                                    // unset it
                                    unset($this->a[$acctnum]['args'][$thing1][$thing2][$thing3]);
                                    // now were we successful?
                                    if (isset($this->a[$acctnum]['args'][$thing1][$thing2][$thing3]))
                                    {
                                          if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg (sublevels): LEAVING returning False, FAILED to unset arg, because VERIFY showed $this->a[$acctnum][args][$thing1][$thing2][$thing3] is still set<br>'); } 
                                          return False;
                                    }
                                    else
                                    {
                                          if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg (sublevels): LEAVING returning True, because VERIFY showed $this->a[$acctnum][args][$thing1][$thing2][$thing3] is unset<br>'); } 
                                          return True;
                                    }
                              }
                        }
                  }
                  elseif ((isset($arg_name))
                  && ((string)$arg_name != ''))
                  {
                        $this->a[$acctnum]['args'][$arg_name] = '';
                        unset($this->a[$acctnum]['args'][$arg_name]);
                        // return True to indicate success
                        return True;
                  }
                  else
                  {
                        // return False to indicate invalid input $arg_name
                        return False;
                  }
            }
            
            /*!
            @function get_arg_value
            @abstract Obtain the value of a given class variable, will handoff to helper functions if necessary.
            @param $arg_name (string) 
            @param $acctnum (int) OPTIONAL 
            @param $extra_keys (string) for SPECIAL INTERNAL use only. Rarely used. If arg uses a "special handler", that handler BETTER support this too, if used.  
            Currently no arg that requires a "special handler" uses an $extra_keys param anyway. DEPRECIATED use sublevels instead. 
            @result (string, int, or array)
            @author Angles
            @discussion Some class variables, such as "mailsvr_namespace", have functions dedicated only to determining their value. 
            In these cases this function will hand off the request directly to that specialized function. In other cases the 
            class variable desired is a simple variable and its value is returned.
            @access public
            */
05744             function get_arg_value($arg_name='',$acctnum='', $extra_keys='')
            {
                  if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: ENTERING ($arg_name: ['.$arg_name.'], $acctnum: ['.$acctnum.'] )<br>'); }
                  if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'get_arg_value'); }
                  
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  if ((isset($arg_name))
                  && ((string)$arg_name != ''))
                  {
                        // ----  SPECIAL HANDLERS  ----
                        if ($arg_name == 'mailsvr_callstr')
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: LEAVING with HANDOFF to get_mailsvr_callstr('.$acctnum.')<br>'); }
                              return $this->get_mailsvr_callstr($acctnum);
                        }
                        elseif ($arg_name == 'mailsvr_namespace')
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: LEAVING with HANDOFF to get_mailsvr_namespace('.$acctnum.')<br>'); }
                              return $this->get_mailsvr_namespace($acctnum);
                        }
                        elseif ($arg_name == 'mailsvr_delimiter')
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: LEAVING with HANDOFF to get_mailsvr_delimiter('.$acctnum.')<br>'); }
                              return $this->get_mailsvr_delimiter($acctnum);
                        }
                        elseif ($arg_name == 'folder_list')
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: LEAVING with HANDOFF to get_folder_list()<br>'); }
                              return $this->get_folder_list($acctnum);
                        }
                        elseif ($arg_name == 'verified_trash_folder_long')
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: LEAVING with HANDOFF to get_verified_trash_folder_long()<br>'); }
                              return $this->get_verified_trash_folder_long($acctnum);
                        }
                        // ----  DEPRECIATED HANDLER (arg_name has sub-levels + uses extrakeys) ----
                        elseif ((strstr($arg_name, ']['))
                        && ($extra_keys))
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value('.__LINE__.'): <font color="red">ERROR using DEPRECIATED arg</font> $extra_keys: '.serialize($extra_keys).'<br>'); }
                              echo 'error line '.__LINE__.'<br>';
                        }
                        // ----  STANDARD HANDLER (arg_name has sub-levels) ----
                        elseif (strstr($arg_name, ']['))
                        {
                              if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_arg_value: $arg_name is requesting <font color="green"> sub-levels </font> array element(s), $arg_name: '.serialize($arg_name).'<br>'); }
                              // ["msgball"]["acctnum"]
                              $exploded = array();
                              $exploded = explode('][', $arg_name);
                              // ["msgball"
                              //  "acctnum"]
                              if ((!is_array($exploded))
                              || (count($exploded) < 2)
                              || (count($exploded) > 3))
                              {
                                    if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_arg_value: ERROR: exploded has wrong number of elements<br>'); }
                                    echo 'error line '.__LINE__.'<br>';
                              }
                              else
                              {
                                    // only 2 or 3 levels deep, we can do this making an array
                                    for ($i=0;$i<count($exploded);$i++)
                                    {
                                          // is there actual data in this array element
                                          if ((isset($exploded[$i])) && ($exploded[$i] != ''))
                                          {
                                                $exploded[$i] = str_replace('[', '', $exploded[$i]);
                                                $exploded[$i] = str_replace(']', '', $exploded[$i]);
                                                $exploded[$i] = str_replace('"', '', $exploded[$i]);
                                          }
                                          else
                                          {
                                                if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_arg_value: ERROR: some of exploded was not set<br>'); }
                                                echo 'error line '.__LINE__.'<br>';
                                          }
                                    }
                                    // now we have the 2 or 3 things we need
                                    if (count($exploded) == 2)
                                    {
                                          $thing1 = $exploded[0];
                                          $thing2 = $exploded[1];
                                          $got_value = $this->a[$acctnum]['args'][$thing1][$thing2];
                                          if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_arg_value: $this->a[$acctnum][args][$thing1][$thing2] is '.serialize($this->a[$acctnum]['args'][$thing1][$thing2]).'<br>'); }
                                    }
                                    elseif (count($exploded) == 3)
                                    {
                                          $thing1 = $exploded[0];
                                          $thing2 = $exploded[1];
                                          $thing3 = $exploded[2];
                                          $got_value = $this->a[$acctnum]['args'][$thing1][$thing2][$thing3];
                                          if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_arg_value: $this->a[$acctnum][args][$thing1][$thing2][$thing3] is '.serialize($this->a[$acctnum]['args'][$thing1][$thing2][$thing3]).'<br>'); }
                                    }
                                    else
                                    {
                                          if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_arg_value: ERROR: some of exploded was not set<br>'); }
                                          echo 'error line '.__LINE__.'<br>';
                                    }
                                    if ((isset($got_value))
                                    && (strstr($arg_name, 'folder_status_info')))
                                    {
                                          if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_arg_value: sublevels with folder_status_info NEED values to be serialized, so <font color="green">unserialize(base64_decode($got_value)) </font> now: <br>'); }
                                          $got_value = unserialize(base64_decode($got_value));
                                    }
                                    if (isset($got_value))
                                    {
                                          if ($this->debug_args_oop_access > 0 && is_array($got_value)) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_arg_value: LEAVING returning $got_value array DUMP:',$got_value); }
                                          if ($this->debug_args_oop_access > 0 && !is_array($got_value)) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_arg_value: LEAVING returning $got_value: ['.$got_value.'] <br>'); }
                                          return $got_value;
                                    }
                              }
                        }
                        // ----  STANDARD HANDLER (arg_name has NO sub-levels) ----
                        elseif (($extra_keys)
                        && (isset($this->a[$acctnum]['args'][$arg_name][$extra_keys])))
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: LEAVING returning $this->a[$acctnum('.$acctnum.')][args][$arg_name]: '.$this->a[$acctnum]['args'][$arg_name].'<br>'); }
                              return $this->a[$acctnum]['args'][$arg_name];
                        }
                        elseif ((!$extra_keys)
                        && (isset($this->a[$acctnum]['args'][$arg_name])))
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: LEAVING returning $this->a[$acctnum('.$acctnum.')][args][$arg_name]: '.$this->a[$acctnum]['args'][$arg_name].'<br>'); }
                              return $this->a[$acctnum]['args'][$arg_name];
                        }
                  }
                  
                  // we ONLY get here if there's no data to return,
                  // arg not set, or invalid input $arg_name
                  // otherwise, anything that is sucessful returns and exist at that point, never gets to here
                  if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: LEAVING, returning *nothing*, arg not set of input arg invalid, using naked "return" call<br>'); }
                  return;
            }
            
            /*!
            @function _direct_access_arg_value
            @abstract utility function for private use, used to bypass any special handlers, to directly access the "args" array.
            @param $arg_name  (string) 
            @param $acctnum  (int) optional
            @result (mixed)
            @author Angles
            @discussion Esoteric utility function for specialized private use.
            @access private
            */
05892             function _direct_access_arg_value($arg_name='',$acctnum='')
            {
                  if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, '_direct_access_arg_value'); }
                  
                  // PRIVATE - for use by internal functions
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  //    $this->set_arg_value('["folder_status_info"]["'.$ex_folder.'"]', $empty_array, $acctnum);

                  if (isset($this->a[$acctnum]['args'][$arg_name]))
                  {
                        return $this->a[$acctnum]['args'][$arg_name];
                  }
                  else
                  {
                        // arg not set, or invalid input $arg_name
                        return;
                  }
            }

            /*!
            @function _get_arg_ref
            @abstract utility function for private use, used to bypass any special handlers, get a reference to something in 
            the args array.
            @param $arg_name  (string) 
            @param $acctnum  (int) optional
            @result (mixed) direct refernce to an arg value in memory, or a reference to a constant "##NOTHING##" on failure.
            @author Angles
            @discussion Esoteric utility function for specialized private use. Primary for use where speed is an issue.
            NOTE: Returning References requires the ampersand in BOTH the call to the function AND the function 
            declaration here.
            @example 
                  function &find_var ($param)
                  {
                        ...code...
                        return $found_var;
                  }
                  $foo =& find_var ($bar);
                  // that was straing from the phpmanual
            @access private
            */
            function &_get_arg_ref($arg_name='',$acctnum='')
            {
                  if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'get_arg_ref'); }
                  
                  // PRIVATE - for use by internal functions
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  if (isset($this->a[$acctnum]['args'][$arg_name]))
                  {
                        return $this->a[$acctnum]['args'][$arg_name];
                  }
                  else
                  {
                        // arg not set, or invalid input $arg_name
                        //return '##NOTHING##';
                        return $this->nothing;
                  }
            }
            
            /*!
            @function get_arg_value_ref
            @abstract return reference to the value, but smart enough to make it if possible, then return reference. 
            @param (string) $arg_name 
            @param (int) $acctnum (optional)
            @result REFERENCE 
            @discussion get a ref instead of a copy of a value, Try to use "special handler" to generate the 
            value if it does not already exist, then again try to return the reference. Returns ref to 
            $this->nothing on failure.
            @author Angles
            */
            /*
            function &get_arg_value_ref($arg_name='',$acctnum='')
            {
                  if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value_ref: ENTERING ($arg_name: ['.$arg_name.'], $acctnum: ['.$acctnum.'] )<br>'); }
                  if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'get_arg_value_ref'); }
                  
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  if (isset($this->a[$acctnum]['args'][$arg_name]))
                  {
                        if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value_ref: LEAVING found and returning ref for ['.$arg_name.'] for ['.$acctnum.']<br>'); }
                        return $this->a[$acctnum]['args'][$arg_name];
                  }
                  else
                  {
                        // try to geberate the arg value, then again try to return the ref
                        if ($arg_name == 'mailsvr_callstr')
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value_ref: generate arg value using get_mailsvr_callstr('.$acctnum.')<br>'); }
                              $this->get_mailsvr_callstr($acctnum);
                              
                        }
                        elseif ($arg_name == 'mailsvr_namespace')
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value_ref: generate arg value using get_mailsvr_namespace('.$acctnum.')<br>'); }
                              $this->get_mailsvr_namespace($acctnum);
                        }
                        elseif ($arg_name == 'mailsvr_delimiter')
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value_ref: generate arg value using get_mailsvr_delimiter('.$acctnum.')<br>'); }
                              $this->get_mailsvr_delimiter($acctnum);
                        }
                        elseif ($arg_name == 'folder_list')
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value_ref: generate arg value using get_folder_list()<br>'); }
                              $this->get_folder_list($acctnum);
                        }
                        elseif ($arg_name == 'verified_trash_folder_long')
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value_ref: generate arg value using get_verified_trash_folder_long()<br>'); }
                              $this->get_verified_trash_folder_long($acctnum);
                        }
                        else
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value_ref: could not find "special handler" to generate an arg value, returning ref to $this->nothing ['.$this->nothing.']<br>'); }
                              return $this->nothing;
                        }
                  }
                  // ok, we tried to generate the arg value, were we successful?
                  if (isset($this->a[$acctnum]['args'][$arg_name]))
                  {
                        if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value_ref: LEAVING was able to generate arg value, found and returning ref for ['.$arg_name.'] for ['.$acctnum.']<br>'); }
                        return $this->a[$acctnum]['args'][$arg_name];
                  }
                  // fallback, we must have failed to find or make then find an arg value, so no reference to something we can not find
                  if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value_ref: if we get here we probably tried but failed to generate a arg value (tried a "special handler"), so returning ref to $this->nothing ['.$this->nothing.']<br>'); }
                  return $this->nothing;
            }
            */

            /*!
            @function set_arg_value
            @abstract Sets a variable in the "args" array.  Should only be used for args that do not require specialized functions.
            @param $arg_name  (string) 
            @param $this_value  (mixed) 
            @param $acctnum  (int) optional
            @result (mixed)
            @author Angles
            @discussion ?
            @access public
            */
06045             function set_arg_value($arg_name='', $this_value='', $acctnum='', $extra_keys='')
            {
                  if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'set_arg_value'); }
                  if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value: ENTERING, $arg_name: ['.$arg_name.'] ; $this_value: ['.$this_value.'] ; $acctnum: ['.$acctnum.']<br>'); }
                  
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  // NOTE we have NO special handlers in this set code
                  // ----  STANDARD HANDLER (arg_name has sub-levels) ----
                  if ((isset($arg_name))
                  && ((string)$arg_name != '')
                  && (strstr($arg_name, '][')))
                  {
                        // only 2 or 3 levels deep, we can do this making an array
                        if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value: $arg_name has <font color="green"> sub-levels </font> <br>'); }
                        $exploded = array();
                        $exploded = explode('][', $arg_name);
                        // ["msgball"
                        //  "acctnum"]
                        if ((!is_array($exploded))
                        || (count($exploded) < 2)
                        || (count($exploded) > 3))
                        {
                              if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value: ERROR: exploded has wrong number of elements<br>'); }
                              echo 'error line '.__LINE__.'<br>';
                        }
                        else
                        {
                              for ($i=0;$i<count($exploded);$i++)
                              {
                                    // is there actual data in this array element
                                    if ((isset($exploded[$i])) && ($exploded[$i] != ''))
                                    {
                                          $exploded[$i] = str_replace('[', '', $exploded[$i]);
                                          $exploded[$i] = str_replace(']', '', $exploded[$i]);
                                          $exploded[$i] = str_replace('"', '', $exploded[$i]);
                                    }
                                    else
                                    {
                                          if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value: ERROR: some of exploded was not set<br>'); }
                                          echo 'error line '.__LINE__.'<br>';
                                    }
                              }
                        }
                        // try using REFERENCE, it might be slow but it reduces code replication here
                        if (count($exploded) == 2)
                        {
                              $thing1 = $exploded[0];
                              $thing2 = $exploded[1];
                              $ref_set_me =& $this->a[$acctnum]['args'][$thing1][$thing2];
                              if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value: $ref_set_me =& $this->a['.$acctnum.'][args]['.$thing1.']['.$thing2.']  <br>'); }
                        }
                        elseif (count($exploded) == 3)
                        {
                              $thing1 = $exploded[0];
                              $thing2 = $exploded[1];
                              $thing3 = $exploded[2];
                              $ref_set_me =& $this->a[$acctnum]['args'][$thing1][$thing2][$thing3];
                              if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value: $ref_set_me =& $this->a['.$acctnum.'][args]['.$thing1.']['.$thing2.']['.$thing3.']  <br>'); }
                        }
                        else
                        {
                              if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value: ERROR: some of exploded was not set, $exploded DUMP', $exploded); }
                              echo 'error line '.__LINE__.'<br>';
                        }
                        // NOTE $this_value BETTER BE SIMPLE string or int if you are doing it like this
                        if (strstr($arg_name, 'folder_status_info'))
                        {
                              $ref_set_me = base64_encode(serialize($this_value));
                        }
                        elseif (is_string($this_value))
                        {
                              $ref_set_me = $this_value;
                        }
                        else
                        {
                              $ref_set_me = $this_value;
                        }
                        if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value (sublevels): $arg_name is requesting sub-level array element(s), $arg_name: '.serialize($arg_name).'<br>'); }
                        // now were we successful?
                        if ($this->debug_args_oop_access > 1) { $this->dbug->out(' set_arg_value(sublevels): (VERIFY) * $ref_set_me (serialized for this display): '.serialize($ref_set_me).'<br>'); }
                        if (isset($ref_set_me))
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value (sublevels): LEAVING returning True, VERIFY indicates $ref_set_me was indeded set<br>'); } 
                              return True;
                        }
                        else
                        {
                              if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value (sublevels): LEAVING returning False, FAILED to set arg value, because VERIFY showed $ref_set_me was unset<br>'); } 
                              return False;
                        }
                  }
                  // ----  STANDARD HANDLER (arg_name has NO sub-levels) ----
                  elseif ((isset($arg_name))
                  && ((string)$arg_name != ''))
                  {
                        /*
                        // can not do prep_folder_in because it calls "folder_lookup" which requires an active mailsvr stream login
                        // ----  SPECIAL HANDLERS  ----
                        //if ($arg_name == 'folder')
                        //{
                        //    $processed_value = $this->prep_folder_in($this_value);
                        //    $this_value = $processed_value;
                        //}
                        */
                        // SET it, any special processing should be taken care just above here
                        $this->a[$acctnum]['args'][$arg_name] = $this_value;
                        if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value: LEAVING, returning TRUE, set data $this->a['.$acctnum.'][args]['.$arg_name.']: ['.$this->a[$acctnum]['args'][$arg_name].']<br>'); }
                        // return True to indicate success
                        return True;
                  }
                  else
                  {
                        // return False to indicate invalid input $arg_name
                        if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value: LEAVING, returning FALSE, invalid $arg_name: ['.$arg_name.']<br>'); }
                        return False;
                  }
            }
            
            /*!
            @function set_arg_array
            @abstract ?
            @param $arg_array_data  (array) 
            @param $acctnum  (int) optional
            @result boolean
            @author Angles
            @discussion ?
            @access private
            */
06178             function set_arg_array($arg_array_data='', $acctnum='')
            {
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  $this->a[$acctnum]['args'] = array();
                  
                  if ((isset($arg_array_data))
                  && (count($arg_array_data > 0)))
                  {
                        /*
                        while(list($key,$value) = each($arg_array_data))
                        {
                              $this->set_arg_value($key, $arg_array_data[$key]);
                        }
                        */
                        $this->a[$acctnum]['args'] = $arg_array_data;
                        // return True to indicate we filled, not just cleared
                        return True;
                  }
                  else
                  {
                        // return False to indicate all we did was clear the args, no data was fed
                        return False;
                  }
            }
            
            /*!
            @function get_all_args
            @abstract ?
            @param $acctnum  (int) optional
            @result (mixed)
            @author Angles
            @discussion ?
            @access private
            */
06217             function get_all_args($acctnum='')
            {
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  if (isset($this->a[$acctnum]['args']))
                  {
                        return $this->a[$acctnum]['args'];
                  }
                  else
                  {
                        // arg not set, or invalid input $arg_name
                        return;
                  }
            }
            
            /*!
            @function unset_all_args
            @abstract ?
            @param $acctnum  (int) optional
            @result none
            @author Angles
            @discussion ?
            @access private
            */
06245             function unset_all_args($acctnum='')
            {
                  if ((!isset($acctnum))
                  || ((string)$acctnum == ''))
                  {
                        $acctnum = $this->get_acctnum();
                  }
                  
                  $this->a[$acctnum]['args'] = array();
            }
            
            
            // depreciated
            //function get_folder($acctnum='')
            //{
            //    return $this->get_arg_value('folder');
            //}
            
            // depreciated
            //function get_msgnum($acctnum='')
            //{
            //    return $this->get_arg_value('["msgball"]["msgnum"]');
            //}
            
            //function get_pref_layout($acctnum='')
            //{
            //    return $this->get_pref_value('layout', $acctnum);
            //}
            
            
      }  // end class mail_msg_wrappers
?>

Generated by  Doxygen 1.6.0   Back to index