1<?php
2	/**************************************************************************\
3	* AngleMail - E-Mail Message Processing Functions				*
4	* http://www.anglemail.org									*
5	* http://www.phpgroupware.org									*
6	*/
7	/**************************************************************************\
8	* AngleMail - E-Mail Message Processing Functions					*
9	* This file written by Angelo Puglisi (Angles) <angles@aminvestments.com>	*
10	* Handles specific operations in manipulating email messages			*
11	* Copyright (C) 2001, 2002 Angelo Tony Puglisi (Angles)				*
12	* ------------------------------------------------------------------------ 		*
13	* This library is free software; you can redistribute it and/or modify it		*
14	* under the terms of the GNU Lesser General Public License as published by 	*
15	* the Free Software Foundation; either version 2.1 of the License,			*
16	* or any later version.								*
17	* This library is distributed in the hope that it will be useful, but			*
18	* WITHOUT ANY WARRANTY; without even the implied warranty of	*
19	* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	*
20	* See the GNU Lesser General Public License for more details.			*
21	* You should have received a copy of the GNU Lesser General Public License 	*
22	* along with this library; if not, write to the Free Software Foundation, 		*
23	* Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA			*
24	\**************************************************************************/
25
26	/* $Id: class.mail_msg_wrappers.inc.php 15464 2004-11-06 16:13:49Z powerstat $ */
27
28	// =====  INTERFACE FUNCTIONS AND/OR  WRAPPER FUNCTIONS =====
29
30	/*!
31	@class mail_msg_wrappers
32	@abstract  Wrapper functions to be called as "public" functions
33	@discussion  Hides the implementation details from the calling process
34	Provides most args to the dcom class from variables which class msg processed and set
35	Sometimes returns processed data ready to be used for display or information
36	MORE DISCUSSION - Why Wrap Here?
37	Answer: because once the msg class opens a mailsvr_stream, that will be the only stream
38	that instance of the class will have, so WHY keep supplying it as an arg EVERY time?
39	Also, same for the "msgnum", unless you are looping thru a message list, you are
40	most likely concerned with only ONE message, and the variable would be the MIME part therein
41	*/
42	class mail_msg_wrappers extends mail_msg_base
43	{
44
45		/*!
46		@function mail_msg_wrappers
47		@abstract CONSTRUCTOR place holder, does nothing
48		*/
49		function mail_msg_wrappers()
50		{
51			return;
52		}
53
54		// ====  Functions For Getting Information About A Message  ====
55
56		/*!
57		@function phpgw_fetchstructure
58		@abstract wrapper for IMAP_FETSCSTRUCTURE, phpgw supplies the nedessary stream arg
59		@param $msgnum   integer
60		@result returns the IMAP_FETSCSTRUCTURE data
61		@discussion Wrapper supplies the needed mailsvr_stream arg to IMAP_FETSCSTRUCTURE
62		The data communications object (class mail_dcom) is supplied by the class. NOTE: this data
63		CAN ONLY BE OBTAINED FOR A MSG IN THE CURRENTLY SELECTED FOLDER.
64		This means we automatically know which folder this data applies to because it can ONLY be
65		the currently selected folder, and only one folder can be selected at any one time.
66		CACHE NOTE if $this->session_cache_extreme is True, then this data is cached and
67		manipulated by the "extreme" caching code, which will pop a cached "msg_structure" out
68		of cache if the message is moved to another folder. If $this->session_cache_extreme is False,
69		then caching is NOT used on this data.
70		*/
71		function phpgw_fetchstructure($msgball='')
72		{
73			if (!(isset($msgball))
74			|| ((string)$msgball == ''))
75			{
76				$msgball = $this->get_arg_value('msgball');
77			}
78			$acctnum = $msgball['acctnum'];
79			if (!(isset($acctnum))
80			|| ((string)$acctnum == ''))
81			{
82				$acctnum = $this->get_acctnum();
83			}
84
85			// CHECK FOR CACHED ITEM
86			// if "session_cache_extreme" is not enabled, do not use caching for this data
87
88			if ($this->session_cache_extreme == True)
89			{
90				// function read_session_cache_item($data_name='misc', $acctnum='', $extra_keys='')
91				// this key, if it exists in the cached array of msg_structures, will hold the data we want as its value
92				// this msgball *may* not have a "folder" element because fetchstructure can only be for the current folder anyway
93				// so sometimes we feed the msgball with no folder into here because it is obvious anyway.
94				if ((isset($msgball['folder']))
95				&& (trim($msgball['folder']) != ''))
96				{
97					//$extra_keys = $msgball['folder'].'_'.(string)$msgball['msgnum'];
98					$ex_folder = $msgball['folder'];
99					$ex_msgnum = $msgball['msgnum'];
100				}
101				else
102				{
103					//$extra_keys = $this->prep_folder_out().'_'.(string)$msgball['msgnum'];
104					$ex_folder = $this->prep_folder_out();
105					$ex_msgnum = $msgball['msgnum'];
106				}
107				// the cached data is returned as a ready to use object if it exists, or False if not existing
108				//$cache_msg_structure = $this->read_session_cache_item('msg_structure', $acctnum, $extra_keys);
109				$cache_msg_structure = $this->read_session_cache_item('msg_structure', $acctnum, $ex_folder, $ex_msgnum);
110				//echo '** phpgw_fetchstructure: $specific_key ['.$specific_key.'] :: $cache_msg_structure DUMP<pre>'; print_r($cache_msg_structure); echo '</pre>';
111			}
112			else
113			{
114				// provide an empty var so the following if .. then does not complain about "undefined var"
115				// because this var is tested along with the "cache_phpgw_header" flag, it should at least
116				// exist even if caching is not turned on just so the following test is "cool" with it
117				$cache_msg_structure = '';
118			}
119
120			if (($cache_msg_structure)
121			&& ($this->session_cache_extreme == True))
122			{
123				//echo '** phpgw_fetchstructure: $specific_key ['.$specific_key.'] :: $cache_msg_structure DUMP<pre>'; print_r($cache_msg_structure); echo '</pre>';
124				return $cache_msg_structure;
125			}
126			else
127			{
128				// NO CACHED ITEM or CACHING NOT ENABLED
129				// get  the data from the mail server
130				$this->ensure_stream_and_folder($msgball, 'phpgw_fetchstructure'.' LINE '.__LINE__);
131				$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
132				$data = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->fetchstructure($mailsvr_stream, $msgball['msgnum']);
133				// PUT THIS IN CACHE
134				//  if "session_cache_extreme" is True
135				if ($this->session_cache_extreme == True)
136				{
137					// this msgball *may* not have a "folder" element because fetchstructure can only be for the current folder anyway
138					// so sometimes we feed the msgball with no folder into here because it is obvious anyway.
139					// But for caching purposes we will MAKE SURE it has folder so we can check the cache for other than the selected folder
140					// at a later date
141					if (!(isset($msgball['folder']))
142					|| ($msgball['folder'] == ''))
143					{
144						//$msgball['folder'] = $this->get_arg_value('folder');
145						$msgball['folder'] = $this->prep_folder_out($this->get_arg_value('folder'));
146					}
147					// this is the way we pass msg_structure data to the caching function
148					// NOTE that $extra_keys is generated FOR US in the function that saves this to appsesion cache
149					$meta_data = array();
150					$meta_data['msgball'] = array();
151					$meta_data['msgball'] = $msgball;
152					$meta_data['msg_structure'] = $data;
153
154					// SET_CACHE_ITEM
155					//echo 'saving msg_structure to cache<br />';
156					$this->save_session_cache_item('msg_structure', $meta_data, $acctnum);
157				}
158				return $data;
159			}
160		}
161
162		/*!
163		@function phpgw_header
164		@abstract wrapper for IMAP_HEADER, phpgw supplies the necessary stream arg and mail_dcom reference
165		@param $msgball (typed array)
166		@result returns the php IMAP_HEADER data
167		@discussion Wrapper supplies the needed mailsvr_stream arg to IMAP_HEADER.
168		Message Information: THE MESSAGE'S HEADERS RETURNED AS A STRUCTURE.
169		The data communications object (class mail_dcom) is supplied by the class.
170		CACHE NOTE if $this->session_cache_extreme is True, then this data is cached and
171		manipulated by the "extreme" caching code, which will pop a cached "phpgw_header" out
172		of cache if the message is moved to another folder, and manually clear a cached "phpgw_header"
173		items flag from "unseen" or "recent", if necessary, if the message is read, and put the updated
174		"phpgw_header" item back in cache, with no need to contact the mailserver about this. Eventhough
175		we still need to contact the mail server to get the body, by manually clearing the flag, if necssary, as
176		described above, then when the user goes back to the message list after reading the message,
177		it is possible that ALL information required to make that index page is "fresh" in local cache,
178		and NO login to the mailserver is done in that case. Situations where ALL the necessary data
179		is not in the cache are as follows, if the user deleted or moved ONE message, for example, it
180		may be possible that the index page needs to contact the mailserver to get one additional "phpgw_header"
181		(and also one additional "msg_structure") item to fill out the message list page. If the user had already
182		viewed the index page that had that message, such as paging forward and then backwards thru the
183		message list, the the single message that *was* on the next message list page that is now on the *current*
184		message list page, would already be in the cache. If $this->session_cache_extreme is False,
185		then caching is NOT used on this data.
186		*/
187		function phpgw_header($msgball='')
188		{
189			if (!(isset($msgball))
190			|| ((string)$msgball == ''))
191			{
192				$msgball = $this->get_arg_value('msgball');
193			}
194			//$acctnum = $msgball['acctnum'];
195			//if (!(isset($acctnum))
196			//|| ((string)$acctnum == ''))
197			//{
198			//	$acctnum = $this->get_acctnum();
199			//}
200			if ((isset($msgball['acctnum']))
201			|| ((string)$msgball['acctnum'] != ''))
202			{
203				$acctnum = $msgball['acctnum'];
204			}
205			else
206			{
207				$acctnum = $this->get_acctnum();
208				$msgball['acctnum'] = $acctnum;
209			}
210
211			// CHECK FOR CACHED ITEM
212			// if "session_cache_extreme" is not enabled, do not use caching for this data
213
214			if ($this->session_cache_extreme == True)
215			{
216				// function read_session_cache_item($data_name='misc', $acctnum='', $extra_keys='')
217				// this key, if it exists in the cached array of msg_structures, will hold the data we want as its value
218				// this msgball *may* not have a "folder" element because header can only be for the current folder anyway
219				// so sometimes we feed the msgball with no folder into here because it is obvious anyway.
220				if ((isset($msgball['folder']))
221				&& (trim($msgball['folder']) != ''))
222				{
223					//$extra_keys = $msgball['folder'].'_'.(string)$msgball['msgnum'];
224					$ex_folder = $msgball['folder'];
225					$ex_msgnum = $msgball['msgnum'];
226				}
227				else
228				{
229					//$extra_keys = $this->prep_folder_out().'_'.(string)$msgball['msgnum'];
230					$ex_folder = $this->prep_folder_out();
231					$ex_msgnum = $msgball['msgnum'];
232				}
233				// the cached data is returned as a ready to use object if it exists, or False if not existing
234				//$cache_phpgw_header = $this->read_session_cache_item('phpgw_header', $acctnum, $extra_keys);
235				$cache_phpgw_header = $this->read_session_cache_item('phpgw_header', $acctnum, $ex_folder, $ex_msgnum);
236				//echo '** phpgw_header: $specific_key ['.$specific_key.'] :: $cache_phpgw_header DUMP<pre>'; print_r($cache_phpgw_header); echo '</pre>';
237			}
238			else
239			{
240				// provide an empty var so the following if .. then does not complain about "undefined var"
241				// because this var is tested along with the "cache_phpgw_header" flag, it should at least
242				// exist even if caching is not turned on just so the following test is "cool" with it
243				$cache_phpgw_header = '';
244			}
245
246			if (($cache_phpgw_header)
247			&& ($this->session_cache_extreme == True))
248			{
249				//echo '** phpgw_header: $specific_key ['.$specific_key.'] :: $cache_phpgw_header DUMP<pre>'; print_r($cache_phpgw_header); echo '</pre>';
250				return $cache_phpgw_header;
251			}
252			else
253			{
254				// NO CACHED ITEM or CACHING NOT ENABLED
255				// get  the data from the mail server
256				$this->ensure_stream_and_folder($msgball, 'phpgw_header'.' LINE '.__LINE__);
257				$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
258				$data = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->header($mailsvr_stream, $msgball['msgnum']);
259
260					 // fix escape problem FORGET THIS FOR NOW
261					//$this->cache_clean_phpgw_header($data);
262
263				// PUT THIS IN CACHE
264				//  if "session_cache_extreme" is True
265				if (($data)
266				&& ($this->session_cache_extreme == True))
267				{
268					// this msgball *may* not have a "folder" element because header can only be for the current folder anyway
269					// so sometimes we feed the msgball with no folder into here because it is obvious anyway.
270					// But for caching purposes we will MAKE SURE it has folder so we can check the cache for other than the selected folder
271					// at a later date
272					if (!(isset($msgball['folder']))
273					|| ($msgball['folder'] == ''))
274					{
275						//$msgball['folder'] = $this->get_arg_value('folder');
276						$msgball['folder'] = $this->prep_folder_out($this->get_arg_value('folder'));
277					}
278
279					 // fix escape problem
280					//$this->cache_clean_phpgw_header($data);
281
282					// this is the way we pass phpgw_header data to the caching function
283					$meta_data = array();
284					$meta_data['msgball'] = array();
285					$meta_data['msgball'] = $msgball;
286					$meta_data['phpgw_header'] = $data;
287
288					// SET_CACHE_ITEM
289					// NOTE that $extra_keys is generated FOR US in the function that saves this to appsesion cache
290					//echo 'saving phpgw_header to cache<br />';
291					$this->save_session_cache_item('phpgw_header', $meta_data, $acctnum);
292				}
293				if (!$data)
294				{
295					return False;
296				}
297				else
298				{
299					return $data;
300				}
301			}
302		}
303
304		/*!
305		@function cache_clean_phpgw_header
306		@abstract ?
307		@param $msg_headers (php struct from header)  btw it is a refrence OOP directly on param
308		@author Angles
309		@discussion ?
310
311		function cache_clean_phpgw_header(&$msg_headers)
312		{
313			//$debug = 0;
314			$debug = 3;
315
316			if (
317			  (!isset($msg_headers))
318			|| (!$msg_headers )
319			//|| ($this->session_cache_extreme == False)
320			)
321			{
322				return '';
323			}
324			// toaddress
325			// to[]
326			// fromaddress
327			// from[]
328			// reply_toaddress
329			// reply_to[]
330			// senderaddress
331			// sender[]
332			// cc[]
333
334			if ($debug > 2) { $this->dbug->out('wrappers.cache_clean_phpgw_header('.__LINE__.'): BEFORE $msg_headers DUMP:', $msg_headers);  }
335			// toaddress
336			if ($msg_headers->toaddress)
337			{
338				$msg_headers->toaddress = addslashes($msg_headers->toaddress);
339			}
340
341			// to[]
342			if ($msg_headers->to)
343			{
344				$loops = count($msg_headers->to);
345				for($i=0;$i<$loops;$i++)
346				{
347					$thingy =& $msg_headers->to[$i];
348					$thingy->personal = addslashes($thingy->personal);
349				}
350			}
351			// fromaddress
352			if ($msg_headers->fromaddress)
353			{
354				$msg_headers->fromaddress = addslashes($msg_headers->fromaddress);
355			}
356			// from[]
357			if ($msg_headers->from)
358			{
359				$loops = count($msg_headers->from);
360				for($i=0;$i<$loops;$i++)
361				{
362					$thingy =& $msg_headers->from[$i];
363					$thingy->personal = addslashes($thingy->personal);
364				}
365			}
366
367			// reply_toaddress
368			if ($msg_headers->reply_toaddress)
369			{
370				$msg_headers->reply_toaddress = addslashes($msg_headers->reply_toaddress);
371			}
372			// reply_to[]
373			if ($msg_headers->reply_to)
374			{
375				$loops = count($msg_headers->reply_to);
376				for($i=0;$i<$loops;$i++)
377				{
378					$thingy =& $msg_headers->reply_to[$i];
379					$thingy->personal = addslashes($thingy->personal);
380				}
381			}
382
383			// senderaddress
384			if ($msg_headers->senderaddress)
385			{
386				$msg_headers->senderaddress = addslashes($msg_headers->senderaddress);
387			}
388			// sender[]
389			if ($msg_headers->sender)
390			{
391				$loops = count($msg_headers->sender);
392				for($i=0;$i<$loops;$i++)
393				{
394					$thingy =& $msg_headers->sender[$i];
395					$thingy->personal = addslashes($thingy->personal);
396				}
397			}
398
399			// cc[]
400
401			if ($debug > 2) { $this->dbug->out('wrappers.cache_clean_phpgw_header('.__LINE__.'): AFTER $msg_headers DUMP:', $msg_headers);  }
402		}
403		*/
404
405		/*!
406		@function phpgw_fetchheader
407		@abstract returns the message RAW headers as a blob, or long string.
408		@param $msgball (typed array)
409		@author Angles
410		@discussion Used by filtering, and in other cases where testing or checking the
411		actual message headers as a text item, is necessary.
412		*/
413		function phpgw_fetchheader($msgball='')
414		{
415			if (!(isset($msgball))
416			|| ((string)$msgball == ''))
417			{
418				$msgball = $this->get_arg_value('msgball');
419			}
420			$acctnum = $msgball['acctnum'];
421			if (!(isset($acctnum))
422			|| ((string)$acctnum == ''))
423			{
424				$acctnum = $this->get_acctnum();
425			}
426
427			$this->ensure_stream_and_folder($msgball, 'phpgw_fetchheader'.' LINE '.__LINE__);
428
429			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
430			// Message Information: THE MESSAGE'S HEADERS RETURNED RAW (no processing)
431			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->fetchheader($mailsvr_stream, $msgball['msgnum']);
432		}
433
434		/*!
435		@function all_headers_in_folder
436		@abstract wrapper for IMAP_HEADERS, phpgw supplies the nedessary stream arg and mail_dcom reference
437		@param $fldball   array[folder]   string ; array[acctnum]   int
438		@result returns the php IMAP_HEADERS data, php manual says
439		function.imap-headers.php
440		Returns headers for all messages in a mailbox
441		Returns an array of string formatted with header info. One element per mail message
442		@discussion = = = = USELESS FUNCTION = = = =
443		returns array of strings, each string is extremely truncated
444		partial contents of date, from, and subject, also includes the msg size in chars
445		*/
446		function all_headers_in_folder($fldball='')
447		{
448			if (!(isset($fldball))
449			|| ((string)$fldball == ''))
450			{
451				$msgball = $this->get_arg_value('fldball');
452			}
453			$acctnum = $fldball['acctnum'];
454			if (!(isset($acctnum))
455			|| ((string)$acctnum == ''))
456			{
457				$acctnum = $this->get_acctnum();
458			}
459			$this->ensure_stream_and_folder($fldball, 'all_headers_in_folder');
460			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
461
462			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->headers($mailsvr_stream);
463		}
464
465		/*!
466		@function phpgw_get_flag
467		@abstract ?
468		*/
469		function phpgw_get_flag($flag='')
470		{
471			// sanity check
472			if ($flag == '')
473			{
474				return '';
475			}
476			else
477			{
478				$msgball = $this->get_arg_value('msgball');
479				$this->ensure_stream_and_folder($msgball , 'phpgw_get_flag'.' LINE '.__LINE__);
480				return $GLOBALS['phpgw_dcom_'.$this->acctnum]->dcom->get_flag($this->get_arg_value('mailsvr_stream'),$this->get_arg_value('["msgball"]["msgnum"]'),$flag);
481			}
482		}
483
484		// ====  Functions For Getting A Message Or A Part (MIME Part) Of A Message  ====
485
486		/*!
487		@function phpgw_body
488		@abstract get the entire body for a message.
489		@param $msgball (typed array)
490		@author Angles
491		@discussion If only a part of the message body is desired, use "phpgw_fetchbody" instead.
492		*/
493		function phpgw_body($msgball='')
494		{
495			if (!(isset($msgball))
496			|| ((string)$msgball == ''))
497			{
498				$msgball = $this->get_arg_value('msgball');
499			}
500			$acctnum = $msgball['acctnum'];
501			if (!(isset($acctnum))
502			|| ((string)$acctnum == ''))
503			{
504				$acctnum = $this->get_acctnum();
505			}
506			$this->ensure_stream_and_folder($msgball, 'phpgw_body'.' LINE '.__LINE__);
507			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
508			// notice of event
509			$this->event_msg_seen($msgball, 'phpgw_body');
510			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->get_body($mailsvr_stream, $msgball['msgnum']);
511		}
512
513		/*!
514		@function phpgw_fetchbody
515		@abstract FETCHBODY get a portion, via MIME part number, of a message body, not the entire body.
516		@param $msgball (typed array)
517		@param $flags (defined int) options passed to the mailserver with the php FETCHBODY command.
518		(Not related to a message flag like "unseen", this is an optional argument for the mail server.)
519		@author Angles
520		*/
521		function phpgw_fetchbody($msgball='', $flags='')
522		{
523			//echo 'mail_msg(_wrappers): phpgw_fetchbody: ENTERING, $msgball dump<pre>'; print_r($msgball); echo '</pre>';
524			if ( (!isset($msgball))
525			|| ($msgball == '') )
526			{
527				$msgball = $this->get_arg_value('msgball');
528			}
529			$acctnum = $msgball['acctnum'];
530			// why is this next check needed?
531			if ((!isset($acctnum))
532			|| ((string)$acctnum == ''))
533			{
534				$acctnum = $this->get_acctnum();
535			}
536
537			// TRY CACHED DATA
538			$cached_phpgw_fetchbody = $this->read_session_cache_item('phpgw_fetchbody', $msgball['acctnum'], $msgball['folder'], $msgball['msgnum'], $msgball['part_no']);
539			if ($cached_phpgw_fetchbody)
540			{
541				// notice of event
542				$this->event_msg_seen($msgball, 'phpgw_fetchbody');
543				return $cached_phpgw_fetchbody;
544			}
545			// if we get here we need to contact mailserver
546			$this->ensure_stream_and_folder($msgball, 'phpgw_fetchbody'.' LINE '.__LINE__);
547			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
548			$msgnum = $msgball['msgnum'];
549			$part_no = $msgball['part_no'];
550			//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>';
551
552			$data = '';
553			$data = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->fetchbody($mailsvr_stream, $msgnum, $part_no, $flags);
554
555			if ($data)
556			{
557				// notice of event
558				$this->event_msg_seen($msgball, 'phpgw_fetchbody');
559				// SET_CACHE_ITEM
560				$meta_data = array();
561				$meta_data['msgball'] = $msgball;
562				$meta_data['phpgw_fetchbody'] = $data;
563				$this->save_session_cache_item('phpgw_fetchbody', $meta_data, $acctnum);
564				$meta_data = array();
565				return $data;
566			}
567			else
568			{
569				return False;
570			}
571		}
572
573		/*
574		// OLD FUNCTION
575		function phpgw_fetchbody($msgball='', $flags='')
576		{
577			//echo 'mail_msg(_wrappers): phpgw_fetchbody: ENTERING, $msgball dump<pre>'; print_r($msgball); echo '</pre>';
578			if ( (!isset($msgball))
579			|| ($msgball == '') )
580			{
581				$msgball = $this->get_arg_value('msgball');
582			}
583			$acctnum = $msgball['acctnum'];
584			if ((!isset($acctnum))
585			|| ((string)$acctnum == ''))
586			{
587				$acctnum = $this->get_acctnum();
588			}
589			$this->ensure_stream_and_folder($msgball, 'phpgw_fetchbody'.' LINE '.__LINE__);
590			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
591			$msgnum = $msgball['msgnum'];
592			$part_no = $msgball['part_no'];
593			//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>';
594
595			// notice of event
596			$this->event_msg_seen($msgball, 'phpgw_fetchbody');
597
598			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->fetchbody($mailsvr_stream, $msgnum, $part_no, $flags);
599		}
600		*/
601
602	// =====  Functions For Getting Information About A Folder  =====
603		// returns an array of integers which are refer to all the messages in a folder ("INBOX") sorted and ordered
604		// any integer in this array can be used to request that specific message from the server
605		/*!
606		@function get_msgball_list
607		@abstract wrapper for IMAP_SORT, sorts a folder in the desired way, then get a list of all message, as integer message numbers
608		@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
609		@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
610		@param $only_fill_cache (boolean) SPECIAL USE ONLY when we only want to make sure the cache has data, so we can reference
611		it after we call this function, so we do not actually want data returned here (EXPERIMENTAL, MAY NOT WORK)
612		@author Angles
613		@access public
614		@result returns an array of of type "msgball" , so it contains acctnum, foldername, message UID, and some other info, such as a
615		pre-prepared "fake URI" a.k.a. a GET URI string of type magball. Important data is the message UID integers which
616		are message numbers referring to messages in the current folder. Because multiple accounts may be in use, the msgball array
617		structure is necessary so the correct acctnum and foldername accompanies each message UID. Therefor you have enough information
618		to take all sorts of action on any particular message in the list, see discussion below.
619		@discussion Folder and Account Number SHOULD be obtained from the class vars which were set during begin_request(),
620		where folder and acctnum were determined from GET POST data or data supplied to begin_request() in its arg array. This way
621		the desired folder is known to be correctly named (it exists, not a bogus foldername) and associated with the correct acctnum.
622		However, some of the filter functions do use these params, but using them is discouraged.
623		The return is an array of "msgball" data, which contains acctnum, foldername, message UID, and some other info, such as a
624		pre-prepared "fake URI" a.k.a. a GET URI string of type magball. Use this data and specifically these message numbers
625		to request more detailed information about a message (headers, subject), or the request message itself from the server.
626		Sort and Order is applied by the class, so the calling process does not need to specify sorting here
627		The data communications object (class mail_dcom) is supplied by the class
628		*/
629		function get_msgball_list($acctnum='', $folder='', $only_fill_cache=False)
630		{
631			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).get_msgball_list:  ENTERING $acctnum ['.$acctnum.'] ; $folder ['.$folder.'] <br />'); }
632			// IF specifying a folder, as a filter search may do, we need to ensure stream and folder
633			if ((isset($acctnum))
634			&& ((string)$acctnum != '')
635			&& (isset($folder))
636			&& ((string)$folder != ''))
637			{
638				// SPECIAL HANDLING, typical message viewing would not need to specify folder
639				// DO NOT SPECIFY FOLDER unless you *really* know what you are doing
640				// typically "best" folder and acctnum are obtained during begin request
641				// right now only specialized filter searching requires tp specify a folder
642
643				// UPDATE WE NOW USE A FOLDER ARG IN THE DATA KEY
644				// not sure how that changes any of this, if at all
645				// in this case, not even an acctnum was passed, so obviously a folder is out of the question here
646
647				$fake_fldball = array();
648				$fake_fldball['acctnum'] = $acctnum;
649				$fake_fldball['folder'] = $folder;
650				// WHY DO THIS HERE?
651				//$this->ensure_stream_and_folder($fake_fldball, 'get_msgball_list'.' LINE '.__LINE__);
652				// ok, so now we KNOW the stream exists and folder value is what we need for this desired account
653			}
654			elseif ((!isset($acctnum))
655			|| ((string)$acctnum == ''))
656			{
657				$acctnum = $this->get_acctnum();
658			}
659			// as I said above, rare to specify folder, if it wasn;t handled above, forget about it
660
661			// try to restore "msgball_list" from saved session data store
662			// in appsession this data is saved like this:
663			//	$cached_msgball_data[msgball_list]
664			//	$cached_msgball_data[validity]
665			// BUT IT RETURNS TO US THE ACTUAL "msgball_list" part, *NOT* WITH THE OTHER STUFF TOO
666			//$cached_msgball_list = $this->read_session_cache_item('msgball_list', $acctnum);
667			// NOW WE USE FOLDER NAME AS A DATA KEY TOO
668			if ((!isset($folder))
669			|| ((string)$folder == ''))
670			{
671				$folder = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
672			}
673			$cached_msgball_list = $this->read_session_cache_item('msgball_list', $acctnum, $folder);
674			if ($cached_msgball_list)
675			{
676				if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers).get_msgball_list: ('.__LINE__.') LEAVING, returning appsession cached "msgball_list"<br />'); }
677				//
678				//return $cached_msgball_data['msgball_list'];
679				return $cached_msgball_list;
680			}
681			else
682			{
683				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 />'); }
684				// right now only specialized filter searching requires tp specify a folder
685				$fake_fldball = array();
686				$fake_fldball['acctnum'] = $acctnum;
687				//$fake_fldball['folder'] = $this->get_arg_value('folder');
688				$fake_fldball['folder'] = $this->prep_folder_out($this->get_arg_value('folder'));
689				$this->ensure_stream_and_folder($fake_fldball, 'get_msgball_list'.' LINE '.__LINE__);
690
691				$server_msgnum_list = array();
692
693				//if (is_object($GLOBALS['phpgw_dcom_'.$acctnum]))
694				//{
695				//	$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));
696				//}
697				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 />'); }
698				$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));
699
700				// put more information about these particular messages into the msgball_list[] structure
701				/*
702				$msgball_list = array();
703				$loops = count($server_msgnum_list);
704				// folder empty (or an error?), msg_nums_list[] count will be 0, so msgball_list[] will be empty as well
705				// because we'll never fill it with anything
706				if ($loops > 0)
707				{
708					// we store folder in URLENCODED form in the msgball and therefor the msgball_list
709					$msg_folder = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
710					for($i=0;$i<$loops;$i++)
711					{
712						$msgball_list[$i]['msgnum'] = $server_msgnum_list[$i];
713						$msgball_list[$i]['folder'] = $msg_folder;
714						$msgball_list[$i]['acctnum'] = $acctnum;
715						// see php manual page "function.parse-str.html" for explanation of the array'ing of the URI data
716						// NOTE: this uri NEVER begins with a "&" here
717						// YOU must add the prefix "&" if it's needed
718						$msgball_list[$i]['uri'] =
719							 'msgball[msgnum]='.$msgball_list[$i]['msgnum']
720							.'&msgball[folder]='.$msgball_list[$i]['folder']
721							.'&msgball[acctnum]='.$msgball_list[$i]['acctnum'];
722						// NEW - try making the msgball_list URI DATA ONLY
723						$msgball_list_uri_only[$i] = $msgball_list[$i]['uri'];
724					}
725				}
726				*/
727				$msgball_list = array();
728				$loops = count($server_msgnum_list);
729				// folder empty (or an error?), msg_nums_list[] count will be 0, so msgball_list[] will be empty as well
730				// because we'll never fill it with anything
731				if ($loops > 0)
732				{
733					// we store folder in URLENCODED form in the msgball and therefor the msgball_list
734					$msg_folder = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
735					for($i=0;$i<$loops;$i++)
736					{
737						// NEW - try making the msgball_list URI DATA ONLY
738						// see php manual page "function.parse-str.html" for explanation of the array'ing of the URI data
739						// NOTE: this uri NEVER begins with a "&" here
740						// YOU must add the prefix "&" if it's needed
741						$msgball_list[$i] =
742							 'msgball[msgnum]='.$server_msgnum_list[$i]
743							.'&msgball[folder]='.$msg_folder
744							.'&msgball[acctnum]='.$acctnum;
745					}
746				}
747
748				// save "msgball_list" to session data store
749				// prepare the data for storage, save it with info to aid in "freshness" testing on later reading of the cache
750				// NOTE right now we do not use the $extra_keys param for this msgball_list data caching
751				$meta_data = array();
752				$meta_data['msgball_list'] = $msgball_list;
753				$meta_data['validity'] = array();
754				$meta_data['validity']['fldball'] = $fake_fldball;
755				//$this->save_session_cache_item('msgball_list', $meta_data, $acctnum);
756				// NOW WE USE FOLDER NAME ALSO IN THE DATA KEY
757				// use the folder name that was fed as a param, since this most likely represents a good key to use
758				// just in case the msgball_list, in the future, is a virtual one composed of msg from many folders
759				$this->save_session_cache_item('msgball_list', $meta_data, $acctnum, $folder);
760				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 />'); }
761				return $msgball_list;
762			}
763		}
764
765		/*!
766		@function get_msgball_list_oldschool
767		@abstract for backward compat with functions requiring a fully expanded array msgball_list instead of only URI type data.
768		@discussion The same as "get_msgball_list" EXCEPT the uri data is looped on and expanded into full array data, for
769		backwards compat with functions expected such data instead of the new style numbered list of uri string data.
770		HEAVILY DEPRECIATED.
771		@author Angles
772		@access public
773		*/
774		function get_msgball_list_oldschool($acctnum='', $folder='', $only_fill_cache=False)
775		{
776			$msgball_list = $this->get_msgball_list($acctnum, $folder, $only_fill_cache);
777			$loops = count($msgball_list);
778			for($i=0;$i<$loops;$i++)
779			{
780				// so we do not have 2 arrays around at the same time, just use the same one for this
781				$msgball_list[$i] = $this->ball_data_parse_str($msgball_list[$i]);
782			}
783			return $msgball_list;
784		}
785
786		/*!
787		@function get_folder_size
788		@abstract uses IMAP_MAILBOXMSGINFO but returns only the size element
789		@result integer returns the SIZE element of the php IMAP_MAILBOXMSGINFO data
790		@discussion used only if the total size of a folder is desired, which takes time for the server to return
791		The other data IMAP_MAILBOXMSGINFO returns (if size is NOT needed) is obtainable
792		from "get_folder_status_info" more quickly and wth less load to the IMAP server
793		The data communications object (class mail_dcom) and mailsvr_stream are supplied by the class.
794		CACHE NOTE - USE THE WRAPPER FUNCTION "report_total_foldersize_conditional" which wraps this
795		function with logic to only actually call this function if various conditional allow us to use this
796		function, since getting size data is time consuming.  STREAM NOTE - this function DOES
797		require changing folders to the onr we want size data on. This may cause problems, since normal
798		status info without size data is obtained without the need to actually switch to the subject folder.
799		For example, using this in a combobox, of in a folder list, looping thru every folder getting this data
800		requires REOPENING aka switching to the desired folder, which can confuse which folder is the
801		true desired folder. Conrast to the other stats data, which can loop thru a folder list but never require
802		actually switching folders, so this keep the true subject folder in the "folder" arg because the folder
803		is never switched away from that.
804		@author Angles
805		@access public
806		*/
807		function get_folder_size()
808		{
809			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): get_folder_size: ('.__LINE__.') ENTERING<br />'); }
810			$fldball = array();
811			$fldball['acctnum'] = $this->get_acctnum();
812			$fldball['folder'] = $this->prep_folder_out($this->get_arg_value('folder', $fldball['acctnum']));
813			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 />'); }
814			// make sure a stream is open
815			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 />'); }
816			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 />'); }
817			$this->ensure_stream_and_folder($fldball, 'get_folder_size'.' LINE '.__LINE__);
818
819			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 />'); }
820			$mailbox_detail = $GLOBALS['phpgw_dcom_'.$fldball['acctnum']]->dcom->mailboxmsginfo($this->get_arg_value('mailsvr_stream'), $fldball['acctnum']);
821			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): get_folder_size: ('.__LINE__.') LEAVING, returning ['.(serialize($mailbox_detail)).']<br />'); }
822			return $mailbox_detail->Size;
823		}
824
825		// ALIAS for get_folder_status_info() , for backward compatibility
826		/*!
827		@function new_message_check
828		@abstract PUBLIC function to get status info for a folder, with emphesis on reporting to user about new messages
829		@param $fldball (array of type fldball) OPTIONAL the folder you want info about. LEGACY CODE exists for older code
830		which does not use array type fldball but instead passes a string only. In not provided, the currently selected folder is inspected.
831		@discussion THIS IS A PUBLIC FUNCTION, use this one instead of "get_folder_status_info" because this function has legacy
832		code for string only param and also has extra checks for the integrity of the param data, such as with external code
833		calling this function and providing incomplete information. These param data checks do not exist in "get_folder_status_info".
834		BUT THE DATA RETURNED IS THE SAME. After these extra param checks the function calls "get_folder_status_info" to do the
835		reast of the job. So see the discussion there about what gets returned.
836		THIS IS FOR BACKWARD COMPAT ONLY, has a lot of checks verifying the input param before passing handling
837		off to the real function "get_folder_status_info", if you pass a real fldball you can just use that function. If you
838		simply want status info on the inbox of the current account, then use this function with no params, it will do the
839		rest for you.
840		@author Angles
841		@access Public
842		*/
843		function new_message_check($fldball='')
844		{
845			// detect OLD param which was string only
846			if ((isset($fldball))
847			&& (is_string($fldball))
848			&& ($fldball != ''))
849			{
850				$legacy_param = $fldball;
851				$fldball = array();
852				$fldball['folder'] = $legacy_param;
853				// legacy param ALWAYS was applicable to acct 0 only, multi-accounts did not exist then
854				$fldball['acctnum'] = 0;
855			}
856			elseif ( (!isset($fldball))
857			|| (!$fldball))
858			{
859				// we have NO instructions on a folder nor acctnum, so make a INBOX acct 0 fldball legacy support
860				$fldball = array();
861				$fldball['acctnum'] = 0;
862				$fldball['folder'] = 'INBOX';
863			}
864
865			// now we know we have a fldball structure to work with, either we made or provoded as a param
866			// see if it needs anything else we did not handle yet
867			if ((!isset($fldball['acctnum']))
868			|| ((string)$fldball['acctnum'] == ''))
869			{
870				$fldball['acctnum'] = $this->get_acctnum();
871
872			}
873
874			// we need a folder value, this code only does allow
875			// NOTE INTERNAL CODE WILL PASS ALREADY URLENCODED FOLDER NAME
876			// This legacy code and public code EXPECTS THE PARAM WILL BE UN-ENCODED
877			// DAMN that is confusing, hummmmmmm...
878			if ((isset($fldball['folder']))
879			&& (is_string($fldball['folder']))
880			&& ($fldball['folder'] != ''))
881			{
882				$fldball['folder'] = $this->prep_folder_out($fldball['folder']);
883			}
884			else
885			{
886				$fldball['folder'] = $this->prep_folder_out('INBOX');
887			}
888			return $this->get_folder_status_info($fldball);
889		}
890
891		/*!
892		@function get_folder_status_info
893		@abstract wrapper for IMAP_STATUS, get status info for the current folder, with emphesis on reporting to user about new messages
894		@param $fldball  typed array  OPTIONAL  as with many functions in this class, the folder you are interested in is usually the currently
895		"selected" folder, in IMAP terms, which is selected during begin_request(), in which case it is not necessary to supply this information
896		again in this param, instead this function will use the class vars about foldername and acctnum established during begin_request(). However,
897		since there are multiple accounts, and since IMAP accounts themselves can contain many folders, it is understood that you may want
898		information about a folder other than the currently selected folder, or about an  account that you may want to move messges to.  In these
899		cases you may supply this param of type fldball, like this: parmarray[acctnum] = 1,  parmarray[folder]  = "INBOX", for example. The fldball
900		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
901		contain quite detailed information.
902		@param $force_refresh boolean  DEPRECIATED - PHASED OUT - To speed email functionality, much data collected
903		from the IMAP server is cached in some capacity, in fact the RFC on IMAP strongly encourages this. This function
904		is used by many other functions and may be called sveral times during any single operation, so the return array data
905		is cached in memory and will be returned if it is available. This is desirable in many occasions, but if for some reason
906		you need to be sure the returned information is not from this cache, set this param to TRUE.
907		=UPDATE= now this data is cached in the appsession cache IF $this->session_cache_extreme is True, and
908		assumed to be fresh for X period of time, as defined in $this->timestamp_age_limit. This param is
909		NO LONGER USED buy *MAY* be reimplemented later.
910		@result returns an associative array  with 5 named elements see the example
911		@example this is the return structure
912		result['is_imap'] boolean - pop3 server do not know what is "new" or not, IMAP servers do
913		result['folder_checked'] string - the folder checked, as processed by the msg class, which may have done a lookup on the folder name
914		result['alert_string'] string - lang'd string to show the user about status of new messages in this folder
915		result['number_new'] integer - for IMAP: the number "recent" and/or "unseen"messages; for POP3: the total number of messages
916		result['number_all'] integer - for IMAP and POP3: the total number messages in the folder
917		@discussion gives user friendly "alert_string" element to show the user, info is for what ever folder the msg
918		class is currently logged into, you may want to apply PHP function "number_format()" to
919		the integers after you have done any math code and befor eyou display them to the user, it adds the thousands comma.
920		CACHE NOTE: If $this->session_cache_extreme is True, the data this function gets is cached in the appsession
921		cache and is assumed to be "fresh" for X period of time, as defined in $this->timestamp_age_limit
922		(currently hardcoded at 4 minutes). Any changes to cached elements number_new and number_all (part of this functions
923		data array) during that time are manually changed by the "extreme" caching code, we do not re-fetch this data from
924		the mailserver for changes tht we can make ourselves.
925		If $this->session_cache_extreme is False, this data is NOT put in the appsession cache, instead it is stored in a class
926		variable (L1 cache) that lasts only as long as the page view.
927		MORE CACHE NOTE: The "msgball_list" cached in the appsession cache is verified for "freshness" by comparing
928		against the "number_all" element in this functions data array. If the "number_all" of the cached "msgball_list" is
929		different from the "nunber_all" from this function,  the "msgball_list" is deemed "stale" and we request a new
930		msgball list from the server, which means calling the php SORT command and adding some data to that to make
931		the "msgball_list". If $this->session_cache_extreme is True, the "extreme" caching code manually updates the
932		"number_all" cached data for this function for X minutes, as defined in $this->timestamp_age_limit, and also manually
933		updates that "number_all" that is stored with the "msgball_list" data, so that, during that X period of time,
934		as defined in $this->timestamp_age_limit, the "msgball_list" is deemed "fresh"
935		because its "number_all" element matches the "number_all" element from this functions data array.
936		If $this->session_cache_extreme is False, the same "number_all" test is done, but the data from this
937		function is ALWAYS the latest data obtained from the server because if $this->session_cache_extreme is False,
938		this function ALWAYS gets fresh folder stats data at the start of every pageview, so the "msgball_list" will be deemed
939		"stale" as soon as a change occurs on the mailserver, such as when new mail arrives or when messages are moved or
940		deleted, in which case the "msgball_list" is expired and re-fetched as described above.
941		@author Angles
942		@access public
943		*/
944		function get_folder_status_info($fldball='', $force_refresh=False)
945		{
946			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 />'); }
947
948			// note: param $fldball WAS just folder previously, watch out for old code still passing that string instead of the fldball
949			if ( (!isset($fldball))
950			|| (!$fldball))
951			{
952				// we have NO instructions on a folder nor acctnum, so make a blank fldball
953				$fldball = array();
954				$fldball['acctnum'] = '';
955				$fldball['folder'] = '';
956			}
957			// now we know we have a fldball structure to work with, analyse it
958			if ((!isset($fldball['acctnum']))
959			|| ((string)$fldball['acctnum'] == ''))
960			{
961				$fldball['acctnum'] = $this->get_acctnum();
962			}
963			if ((!isset($fldball['folder']))
964			|| ((string)$fldball['folder'] == ''))
965			{
966				//$fldball['folder'] = $this->get_arg_value('folder', $fldball['acctnum']);
967				// we keep this folder name in its urlencoded form until the last second,
968				// partly so we can use it as an index member in the cache, also because it is a consistant prectice to do that
969				$fldball['folder'] = $this->prep_folder_out($this->get_arg_value('folder', $fldball['acctnum']));
970			}
971
972			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 />'); }
973
974			if ($this->session_cache_extreme == False)
975			{
976				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 />'); }
977				// do we have cached data in L1 cache / class object var, that we can use
978				// ONLY L1 CACHE OF THIS INFO IF IN NON-EXTREME MODE
979				//$folder_status_info = $this->get_arg_value('folder_status_info', $fldball['acctnum']);
980				$folder_status_info = $this->get_arg_value('["folder_status_info"]["'.$fldball['folder'].'"]', $fldball['acctnum']);
981				//$folder_status_info = $this->get_arg_value('folder_status_info', $fldball['acctnum'], $fldball['folder']);
982				//$folder_status_info = $this->get_arg_value_ex('folder_status_info', $fldball['acctnum'], $fldball['folder']);
983				if ((!$force_refresh)
984				&& ($folder_status_info)
985				&& (count($folder_status_info) > 0)
986				&& ($folder_status_info['folder_checked'] == $this->prep_folder_in($fldball['folder'])))
987				{
988					// this data is cached, L1 cache, temp cache, so it should still be "fresh"
989					// add this "timestamp" array element only to imitate what exists in data structure if extreme-mode were on (which it is not here).
990					$timestamp_age = (time() - $folder_status_info['timestamp']);
991					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); }
992					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 />'); }
993					return $folder_status_info;
994				}
995				else
996				{
997					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 />'); }
998				}
999			}
1000			else
1001			{
1002				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); }
1003				// ONLY USE APPSESSION CACHE IF IN EXTREME MODE
1004				// non-extreme mose, which was handled above, does not use appsession cache for stats data, only "L1 cache" temp class var
1005				// below is for extreme-mode, which only uses appsession cache for this data, does NOT use "L1 cache"
1006
1007				// try to restore from saved session data store
1008				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 />'); }
1009				$cached_folder_status_info = $this->read_session_cache_item('folder_status_info', $fldball['acctnum'], $fldball['folder']);
1010				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); }
1011				if ($cached_folder_status_info)
1012				{
1013					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 />'); }
1014					return $cached_folder_status_info;
1015				}
1016				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 />'); }
1017			}
1018
1019			// Make Sure Stream Exists
1020			// multiple accounts means one stream may be open but another may not
1021			// "ensure_stream_and_folder" will verify for us,
1022			/*!
1023			@capability ensure_stream_and_folder WITHOUT changing folders
1024			@discussion folder STATUS data does not require opening the folder we want information about,
1025			THEREFOR we use a "$special_fldball" which has a$special_fldball["no_switch_away"] element of
1026			which is understood by function "ensure_stream_and_folder" to NOT CHANGE
1027			the selected folder, to make sure only that the stream exists and we are logged in to the mailserver,
1028			but the actual selected folder IN THIS SPECIAL CASE is not necessary to set. If we were to change the
1029			currently selected folder just to get stats, this would add additional complication to the concept of which folder
1030			we actually wanted to be selected, because "ensure_stream_and_folder" would set the "arg_value" for "folder"
1031			IF IT DID change the selected folder, which is NOT wanted nor needed in this case.
1032			@author Angles
1033			*/
1034			$special_fldball = array();
1035			$special_fldball['acctnum'] = $fldball['acctnum'];
1036			$special_fldball['folder'] = $fldball['folder'];
1037			// STATUS does not require opening the folder we want information about
1038			$special_fldball['no_switch_away'] = True;
1039			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 />'); }
1040			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 />'); }
1041			$this->ensure_stream_and_folder($special_fldball, 'get_folder_status_info'.' LINE '.__LINE__);
1042
1043			//$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
1044			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $fldball['acctnum']);
1045			$server_str = $this->get_arg_value('mailsvr_callstr', $fldball['acctnum']);
1046			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 />'); }
1047
1048			$clean_folder_name = $this->prep_folder_in($fldball['folder']);
1049			$urlencoded_folder = $this->prep_folder_out($clean_folder_name);
1050			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 />'); }
1051
1052			// initialize return structure
1053			$return_data = Array();
1054			$return_data['is_imap'] = False;
1055			$return_data['fldball'] = $fldball;
1056			$return_data['folder_checked'] = $clean_folder_name;
1057			$return_data['folder'] = $clean_folder_name;
1058			$return_data['alert_string'] = '';
1059			$return_data['number_new'] = 0;
1060			$return_data['number_all'] = 0;
1061			// these are used to verify cached msg_list_array data, i.e. is it still any good, or is it stale
1062			$return_data['uidnext'] = 0;
1063			$return_data['uidvalidity'] = 0;
1064			$return_data['timestamp'] = time();
1065			// FIXME: make this a "ensure_stream_and_folder" call, to make a login if needed
1066			//if (is_object($GLOBALS['phpgw_dcom_'.$fldball['acctnum']]))
1067			//{
1068			//	$mailbox_status = $GLOBALS['phpgw_dcom_'.$fldball['acctnum']]->dcom->status($mailsvr_stream,$server_str.$fldball['folder'],SA_ALL);
1069			//}
1070			// earlier we called $this->ensure_stream_and_folder, so stream *should* exist
1071			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 />'); }
1072			$mailbox_status = $GLOBALS['phpgw_dcom_'.$fldball['acctnum']]->dcom->status($mailsvr_stream,$server_str.$clean_folder_name,SA_ALL);
1073			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); }
1074
1075			// cache validity data - will be used to cache msg_list_array data, which is good until UID_NEXT changes
1076			$return_data['uidnext'] = $mailbox_status->uidnext;
1077			$return_data['uidvalidity'] = $mailbox_status->uidvalidity;
1078
1079			$mail_server_type = $this->get_pref_value('mail_server_type', $fldball['acctnum']);
1080			if (($mail_server_type == 'imap')
1081			|| ($mail_server_type == 'imaps'))
1082			{
1083				$return_data['is_imap'] = True;
1084				$return_data['number_new'] = $mailbox_status->unseen;
1085				$return_data['number_all'] = $mailbox_status->messages;
1086				if ($mailbox_status->unseen == 1)
1087				{
1088					$return_data['alert_string'] .= lang('You have 1 new message!');
1089				}
1090				if ($mailbox_status->unseen > 1)
1091				{
1092					$return_data['alert_string'] .= lang('You have %1 new messages!',$mailbox_status->unseen);
1093				}
1094				if ($mailbox_status->unseen == 0)
1095				{
1096					$return_data['alert_string'] .= lang('You have no new messages');
1097				}
1098			}
1099			else
1100			{
1101				$return_data['is_imap'] = False;
1102				// pop3 does not know what is "new" or not
1103				$return_data['number_new'] = $mailbox_status->messages;
1104				$return_data['number_all'] = $mailbox_status->messages;
1105				if ($mailbox_status->messages > 0)
1106				{
1107					$return_data['alert_string'] .= lang('You have messages!');
1108				}
1109				elseif ($mailbox_status->messages == 0)
1110				{
1111					$return_data['alert_string'] .= lang('You have no new messages');
1112				}
1113				else
1114				{
1115					$return_data['alert_string'] .= lang('error');
1116				}
1117			}
1118
1119			if ($this->session_cache_extreme == False)
1120			{
1121				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 />'); }
1122				// cache data in a class var (L1 Cache)
1123				// USE L1 CACHE ONLY IN NON-EXTREME MODE
1124				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); }
1125				//$this->set_arg_value('folder_status_info', $return_data, $fldball['acctnum']);
1126				$this->set_arg_value('["folder_status_info"]["'.$fldball['folder'].'"]', $return_data, $fldball['acctnum']);
1127			}
1128			else
1129			{
1130				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 />'); }
1131				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 />'); }
1132				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); }
1133				// NOTE that the $extra_keys param is generated FOR US in the function that saves this to appsession cache, we are doing that now
1134				$this->save_session_cache_item('folder_status_info', $return_data, $fldball['acctnum'], $fldball['folder']);
1135			}
1136			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 />'); }
1137			return $return_data;
1138		}
1139
1140		// FIXME: change arg to fldball
1141		/*!
1142		@function phpgw_status
1143		@abstract ?
1144		@author Angles
1145		@discussion Debug with flag "debug_wrapper_dcom_calls" . FIXME change param to fldball.
1146		@access public
1147		*/
1148		function phpgw_status($feed_folder_long='')
1149		{
1150			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers): phpgw_status ('.__LINE__.'): ENTERING, $feed_folder_long ['.($feed_folder_long).']<br />'); }
1151			$fake_fldball = array();
1152			$fake_fldball['acctnum'] = $this->get_acctnum();
1153			$fake_fldball['folder'] = $feed_folder_long;
1154			$this->ensure_stream_and_folder($fake_fldball, 'phpgw_status'.' LINE '.__LINE__);
1155			$server_str = $this->get_arg_value('mailsvr_callstr');
1156			$mailsvr_stream = $this->get_arg_value('mailsvr_stream');
1157			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 />'); }
1158			$retval = $GLOBALS['phpgw_dcom_'.$fake_fldball['acctnum']]->dcom->status($mailsvr_stream,"$server_str"."$feed_folder_long",SA_ALL);
1159			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(wrappers): phpgw_status ('.__LINE__.'): LEAVING, returning $retval ['.serialize($retval).'] <br />'); }
1160			return $retval;
1161		}
1162
1163		/*!
1164		@function phpgw_server_last_error
1165		@abstract ?
1166		@author Angles
1167		@discussion Debug with flag "debug_wrapper_dcom_calls"
1168		@access public
1169		*/
1170		function phpgw_server_last_error($acctnum='')
1171		{
1172			if ((!isset($acctnum))
1173			|| ((string)$acctnum == ''))
1174			{
1175				$acctnum = $this->get_acctnum();
1176			}
1177			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 />'); }
1178			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->server_last_error();
1179		}
1180
1181		/*!
1182		@function phpgw_ping
1183		@abstract ?
1184		@author Angles
1185		@discussion Debug with flag "debug_wrapper_dcom_calls"
1186		@access public
1187		*/
1188		function phpgw_ping($acctnum='')
1189		{
1190			if ((!isset($acctnum))
1191			|| ((string)$acctnum == ''))
1192			{
1193				$acctnum = $this->get_acctnum();
1194			}
1195			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 />'); }
1196			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
1197			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 />'); }
1198			$retval = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->noop_ping_test($mailsvr_stream);
1199			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_ping ('.__LINE__.'): LEAVING, returing $retval ['.serialize($retval).']<br />'); }
1200			return $retval;
1201		}
1202
1203		/*!
1204		@function phpgw_search
1205		@abstract ?
1206		@author Angles
1207		@discussion Debug with flag "debug_wrapper_dcom_calls"
1208		@access public
1209		*/
1210		function phpgw_search($fldball='', $criteria='', $flags='')
1211		{
1212			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 />'); }
1213			$acctnum = (int)$fldball['acctnum'];
1214			if ((!isset($acctnum))
1215			|| ((string)$acctnum == ''))
1216			{
1217				$acctnum = $this->get_acctnum();
1218			}
1219			$folder = $fldball['folder'];
1220			// if folder is blank, we *should* assume INBOX because filters always search the INBOX
1221			if ((!isset($folder))
1222			|| ((string)$folder == ''))
1223			{
1224				$folder = 'INBOX';
1225			}
1226			// Make Sure Stream Exists
1227			// multiple accounts means one stream may be open but another may not
1228			// "ensure_stream_and_folder" will verify for us,
1229			$fake_fldball = array();
1230			$fake_fldball['acctnum'] = $acctnum;
1231			$fake_fldball['folder'] = $folder;
1232			$this->ensure_stream_and_folder($fake_fldball, 'phpgw_search LINE '.__LINE__);
1233			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
1234
1235			// now we have the stream and the desired folder open
1236			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 />'); }
1237			$retval = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->i_search($mailsvr_stream,$criteria,$flags);
1238			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_search ('.__LINE__.'): LEAVING, returing $retval ['.serialize($retval).']<br />'); }
1239			return $retval;
1240		}
1241
1242		/*!
1243		@function phpgw_createmailbox
1244		@abstract ?
1245		@param $target_fldball (array or type "fldball") NOTE: folder element SHOULD HAVE SERVER CALLSTR.
1246		@author Angles
1247		@access public
1248		*/
1249		function phpgw_createmailbox($target_fldball)
1250		{
1251			$acctnum = (int)$target_fldball['acctnum'];
1252			if ((!isset($acctnum))
1253			|| ((string)$acctnum == ''))
1254			{
1255				$acctnum = $this->get_acctnum();
1256			}
1257			$folder = $target_fldball['folder'];
1258			// if folder is blank, we *should* assume INBOX because BUT mailsvr will give an error INBOX already exists
1259			if ((!isset($folder))
1260			|| ((string)$folder == ''))
1261			{
1262				$folder = 'INBOX';
1263			}
1264			// Make Sure Stream Exists
1265			// multiple accounts means one stream may be open but another may not
1266			// "ensure_stream_and_folder" will verify for us,
1267			$fake_fldball = array();
1268			$fake_fldball['acctnum'] = $acctnum;
1269			$fake_fldball['folder'] = $folder;
1270			// tell "ensure_stream_and_folder" that its NOT NECESSARY to switch TO this folder
1271			$fake_fldball['no_switch_away'] = True;
1272			$this->ensure_stream_and_folder($fake_fldball, 'phpgw_createmailbox LINE ('.__LINE__.')');
1273			// if $folder dies not have the {SERVERNAME}  then add it
1274			if (!strstr($folder, '}'))
1275			{
1276				$mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
1277				$folder = $mailsvr_callstr.$folder;
1278			}
1279			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
1280			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->createmailbox($mailsvr_stream, $folder);
1281		}
1282
1283		/*!
1284		@function phpgw_createmailbox_ex
1285		@abstract ?
1286		@param $target_fldball (array or type "fldball") NOTE: folder element DOES NOT NEED THE SERVER CALLSTR.
1287		@discussion This function handles the traditional fldball better, a fldball["folder"] is NOT ever supposed to have
1288		a mailsvr_callstr, and IS ALWAYS urlencoded until just before we call the dcom class, when we urldecode it only
1289		at that point. This function assumes this behavior, so the typical fldball is supported by this function.
1290		This will be the future version of this function, code is migrating to this version.
1291		@author Angles
1292		@access public
1293		*/
1294		function phpgw_createmailbox_ex($target_fldball)
1295		{
1296			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('phpgw_createmailbox_ex('.__LINE__.'): ENTERING: raw $target_fldball arg ['.htmlspecialchars($target_fldball).']<br />'); }
1297			if ((isset($target_fldball['acctnum']))
1298			&& ((string)$target_fldball['acctnum'] != ''))
1299			{
1300				$target_fldball['acctnum'] = (int)$target_fldball['acctnum'];
1301			}
1302			else
1303			{
1304				$target_fldball['acctnum'] = $this->get_acctnum();
1305			}
1306			$acctnum = $target_fldball['acctnum'];
1307			// if folder is blank, we *should* assume INBOX because BUT mailsvr will give an error INBOX already exists
1308			if ((isset($target_fldball['folder']))
1309			|| ((string)$target_fldball['folder'] != ''))
1310			{
1311				// good folder name, no change needed
1312			}
1313			else
1314			{
1315				$target_fldball['folder'] = 'INBOX';
1316			}
1317			// Make Sure Stream Exists
1318			// multiple accounts means one stream may be open but another may not
1319			// "ensure_stream_and_folder" will verify for us,
1320			// tell "ensure_stream_and_folder" that its NOT NECESSARY to switch TO this folder
1321			$target_fldball['no_switch_away'] = True;
1322			$this->ensure_stream_and_folder($target_fldball, 'phpgw_createmailbox_ex LINE ('.__LINE__.')');
1323			// if $folder dies not have the {SERVERNAME}  then add it
1324			//$target_folder_clean = $this->prep_folder_in($target_fldball['folder']);
1325			$target_folder_clean = urldecode($target_fldball['folder']);
1326			if (!strstr($target_folder_clean, '}'))
1327			{
1328				$mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
1329				$target_folder_clean = $mailsvr_callstr.$target_folder_clean;
1330			}
1331			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
1332			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 />'); }
1333			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->createmailbox($mailsvr_stream, $target_folder_clean);
1334		}
1335
1336		/*!
1337		@function phpgw_deletemailbox
1338		@abstract ?
1339		@author Angles
1340		@access public
1341		*/
1342		function phpgw_deletemailbox($target_fldball)
1343		{
1344			$this->ensure_stream_and_folder($target_fldball, 'phpgw_deletemailbox'.' LINE '.__LINE__);
1345			$acctnum = $target_fldball['acctnum'];
1346			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
1347			$folder = $target_fldball['folder'];
1348			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->deletemailbox($mailsvr_stream, $folder);
1349		}
1350
1351		/*!
1352		@function phpgw_deletemailbox_ex
1353		@abstract ?
1354		@param $target_fldball (array or type "fldball") NOTE: folder element DOES NOT NEED THE SERVER CALLSTR.
1355		@discussion This function handles the traditional fldball better, a fldball["folder"] is NOT ever supposed to have
1356		a mailsvr_callstr, and IS ALWAYS urlencoded until just before we call the dcom class, when we urldecode it only
1357		at that point. This function assumes this behavior, so the typical fldball is supported by this function.
1358		This will be the future version of this function, code is migrating to this version.
1359		@author Angles
1360		@access public
1361		*/
1362		function phpgw_deletemailbox_ex($target_fldball)
1363		{
1364			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('phpgw_deletemailbox_ex('.__LINE__.'): ENTERING: raw $target_fldball arg ['.htmlspecialchars($target_fldball).']<br />'); }
1365			if ((isset($target_fldball['acctnum']))
1366			&& ((string)$target_fldball['acctnum'] != ''))
1367			{
1368				$target_fldball['acctnum'] = (int)$target_fldball['acctnum'];
1369			}
1370			else
1371			{
1372				$target_fldball['acctnum'] = $this->get_acctnum();
1373			}
1374			$acctnum = $target_fldball['acctnum'];
1375			// if folder is blank, we *should* assume INBOX because BUT mailsvr will give an error INBOX already exists
1376			if ((isset($target_fldball['folder']))
1377			|| ((string)$target_fldball['folder'] != ''))
1378			{
1379				// good folder name, no change needed
1380			}
1381			else
1382			{
1383				$target_fldball['folder'] = 'INBOX';
1384			}
1385			// Make Sure Stream Exists
1386			// multiple accounts means one stream may be open but another may not
1387			// "ensure_stream_and_folder" will verify for us,
1388			// tell "ensure_stream_and_folder" that its NOT NECESSARY to switch TO this folder
1389			$target_fldball['no_switch_away'] = True;
1390			$this->ensure_stream_and_folder($target_fldball, 'phpgw_deletemailbox_ex LINE ('.__LINE__.')');
1391			// if $folder dies not have the {SERVERNAME}  then add it
1392			//$target_folder_clean = $this->prep_folder_in($target_fldball['folder']);
1393			$target_folder_clean = urldecode($target_fldball['folder']);
1394			if (!strstr($target_folder_clean, '}'))
1395			{
1396				$mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
1397				$target_folder_clean = $mailsvr_callstr.$target_folder_clean;
1398			}
1399			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
1400			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 />'); }
1401			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->deletemailbox($mailsvr_stream, $target_folder_clean);
1402		}
1403
1404		/*!
1405		@function phpgw_renamemailbox
1406		@abstract ?
1407		@author Angles
1408		@access public
1409		*/
1410		function phpgw_renamemailbox($source_fldball,$target_fldball)
1411		{
1412			$this->ensure_stream_and_folder($source_fldball, 'phpgw_renamemailbox'.' LINE '.__LINE__);
1413			$acctnum = (int)$source_fldball['acctnum'];
1414			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
1415			$folder_old = $source_fldball['folder'];
1416			$folder_new = $target_fldball['folder'];
1417			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->renamemailbox($mailsvr_stream, $folder_old, $folder_new);
1418		}
1419
1420		/*!
1421		@function phpgw_renamemailbox_ex
1422		@abstract ?
1423		@param $target_fldball (array or type "fldball") NOTE: folder element DOES NOT NEED THE SERVER CALLSTR.
1424		@discussion This function handles the traditional fldball better, a fldball["folder"] is NOT ever supposed to have
1425		a mailsvr_callstr, and IS ALWAYS urlencoded until just before we call the dcom class, when we urldecode it only
1426		at that point. This function assumes this behavior, so the typical fldball is supported by this function.
1427		This will be the future version of this function, code is migrating to this version.
1428		@author Angles
1429		@access public
1430		*/
1431		function phpgw_renamemailbox_ex($source_fldball,$target_fldball)
1432		{
1433			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('phpgw_renamemailbox_ex('.__LINE__.'): ENTERING<br />'); }
1434			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 />'); }
1435			if ((isset($target_fldball['acctnum']))
1436			&& ((string)$target_fldball['acctnum'] != ''))
1437			{
1438				$target_fldball['acctnum'] = (int)$target_fldball['acctnum'];
1439			}
1440			else
1441			{
1442				$target_fldball['acctnum'] = $this->get_acctnum();
1443			}
1444			$acctnum = $target_fldball['acctnum'];
1445			// if folder is blank, we *should* assume INBOX because BUT mailsvr will give an error INBOX already exists
1446			if ((isset($target_fldball['folder']))
1447			|| ((string)$target_fldball['folder'] != ''))
1448			{
1449				// good folder name, no change needed
1450			}
1451			else
1452			{
1453				$target_fldball['folder'] = 'INBOX';
1454			}
1455			// Make Sure Stream Exists
1456			// multiple accounts means one stream may be open but another may not
1457			// "ensure_stream_and_folder" will verify for us,
1458			// tell "ensure_stream_and_folder" that its NOT NECESSARY to switch TO this folder
1459			$target_fldball['no_switch_away'] = True;
1460			$this->ensure_stream_and_folder($target_fldball, 'phpgw_renamemailbox_ex LINE ('.__LINE__.')');
1461			// if $folder dies not have the {SERVERNAME}  then add it
1462			//$target_folder_clean = $this->prep_folder_in($target_fldball['folder']);
1463			$target_folder_clean = urldecode($target_fldball['folder']);
1464			if (!strstr($target_folder_clean, '}'))
1465			{
1466				$mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
1467				$target_folder_clean = $mailsvr_callstr.$target_folder_clean;
1468			}
1469
1470			// SOURCE FOLDER NAME
1471			//the folder we are renaming MUST BE from the same account
1472			// so we only need to clean up its name
1473			if ((isset($source_fldball['folder']))
1474			|| ((string)$source_fldball['folder'] != ''))
1475			{
1476				// good folder name, no change needed
1477			}
1478			else
1479			{
1480				$source_fldball['folder'] = 'INBOX';
1481			}
1482			$source_folder_clean = urldecode($source_fldball['folder']);
1483			// if $folder dies not have the {SERVERNAME}  then add it
1484			if (!strstr($source_folder_clean, '}'))
1485			{
1486				$mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
1487				$source_folder_clean = $mailsvr_callstr.$source_folder_clean;
1488			}
1489
1490			// OK WE are ready to do it!
1491			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
1492			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 />'); }
1493			//return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->createmailbox($mailsvr_stream, $target_folder_clean);
1494			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->renamemailbox($mailsvr_stream, $source_folder_clean, $target_folder_clean);
1495		}
1496
1497		/*!
1498		@function phpgw_listmailbox
1499		@abstract ?
1500		@author Angles
1501		@discussion Debug with flag "debug_wrapper_dcom_calls"
1502		@access public
1503		*/
1504		function phpgw_listmailbox($ref,$pattern,$acctnum)
1505		{
1506			if (!(isset($acctnum))
1507			|| ((string)$acctnum == ''))
1508			{
1509				$acctnum = $this->get_acctnum();
1510			}
1511			// Make Sure Stream Exists
1512			// multiple accounts means one stream may be open but another may not
1513			// "ensure_stream_and_folder" will verify for us,
1514			// folder logged into does not matter for listmailbox, so pass $fake_fldball['no_switch_away'] = True
1515			$fake_fldball = array();
1516			$fake_fldball['acctnum'] = $acctnum;
1517			$fake_fldball['folder'] = '';
1518			$fake_fldball['no_switch_away'] = True;
1519			$this->ensure_stream_and_folder($fake_fldball, 'phpgw_listmailbox');
1520			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
1521
1522			// ... so stream exists, do the transaction ...
1523			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 />'); }
1524			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->listmailbox($mailsvr_stream,$ref,$pattern);
1525		}
1526
1527		/*!
1528		@function phpgw_append
1529		@abstract ?
1530		@author Angles
1531		@discussion Debug with flag "debug_wrapper_dcom_calls"
1532		@access public
1533		*/
1534		function phpgw_append($folder="Sent", $message, $flags=0)
1535		{
1536			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: ('.__LINE__.') ENTERING, folder: '.$folder.'<br />'); }
1537
1538			$server_str = $this->get_arg_value('mailsvr_callstr');
1539
1540			// ---  does the target folder actually exist ?  ---
1541			// strip {server_str} string if it's there
1542			$folder = $this->ensure_no_brackets($folder);
1543			// attempt to find a folder match in the lookup list
1544			$official_folder_long = $this->folder_lookup('', $folder);
1545			if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: ('.__LINE__.') $official_folder_long: '.$official_folder_long.'<br />'); }
1546			if ($official_folder_long != '')
1547			{
1548				$havefolder = True;
1549			}
1550			else
1551			{
1552				$havefolder = False;
1553			}
1554
1555			if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: ('.__LINE__.') $havefolder ['.serialize($havefolder).']<br />'); }
1556			if ($havefolder == False)
1557			{
1558				// add whatever namespace we believe should exist
1559				// (remember the lookup failed, so we have to guess here)
1560				$folder_long = $this->get_folder_long($folder);
1561				// create the specified target folder so it will exist
1562				//$this->createmailbox($mailsvr_stream,"$server_str"."$folder_long");
1563				//$this->phpgw_createmailbox("$server_str"."$folder_long");
1564				$fake_fldball = array();
1565				$fake_fldball['folder'] = $server_str.$folder_long;
1566				$fake_fldball['acctnum'] = $this->get_acctnum();
1567				if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: ('.__LINE__.') calling $this->phpgw_createmailbox('.serialize($fake_fldball).')<br />'); }
1568				$this->phpgw_createmailbox($fake_fldball);
1569
1570				// try again to get the real long folder name of the just created trash folder
1571				$official_folder_long = $this->folder_lookup('', $folder);
1572				if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_append: ('.__LINE__.') $official_folder_long: '.$official_folder_long.'<br />'); }
1573				// did the folder get created and do we now have the official full name of that folder?
1574				if ($official_folder_long != '')
1575				{
1576					$havefolder = True;
1577				}
1578			}
1579
1580			// at this point we've tries 2 time to obtain the "server approved" long name for the target folder
1581			// even tries creating it if necessary
1582			// if we have the name, append the message to that folder
1583			if (($havefolder == True)
1584			&& ($official_folder_long != ''))
1585			{
1586				// delete appsession msg array data thAt is now stale
1587				// WE DO NOT GUESS ABOUT APPENDS, WE EXPIRE THE DATA AND GET FRESH
1588				//$this->expire_session_cache_item('msgball_list');
1589				$target_fldball = array();
1590				$target_fldball['folder'] = $official_folder_long;
1591				$target_fldball['acctnum'] = $this->get_acctnum();
1592				$this->event_msg_append($target_fldball, 'phpgw_append'.' LINE '.__LINE__);
1593
1594				$this->ensure_stream_and_folder($target_fldball, 'phpgw_append'.' LINE '.__LINE__);
1595				$mailsvr_stream = $this->get_arg_value('mailsvr_stream');
1596				// do the append
1597				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, '.$flags.') '); }
1598				//$acctnum: ['.$acctnum.'] $mailsvr_stream: ['.$mailsvr_stream.'] $msgnum: ['.$msgnum.'] $mailbox: ['.htmlspecialchars($mailbox).']<br />'; }
1599				$retval = $GLOBALS['phpgw_dcom_'.$target_fldball['acctnum']]->dcom->append($mailsvr_stream, "$server_str"."$official_folder_long", $message, $flags);
1600				if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_append ('.__LINE__.'): LEAVING, returning $retval ['.serialize($retval).']<br />'); }
1601				return $retval;
1602			}
1603			else
1604			{
1605				// we do not have the official long folder name for the target folder
1606				// we can NOT append the message to a folder name we are not SURE is corrent
1607				// it will fail  HANG the browser for a while
1608				// so just SKIP IT
1609				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 />'); }
1610				return False;
1611			}
1612		}
1613
1614		/*!
1615		@function phpgw_mail_move
1616		@abstract DEPRECIATED - NO LONGER USED. Use "industrial_interacct_mail_move" instead.
1617		@author Angles
1618		@access public
1619		*/
1620		function phpgw_mail_move($msg_list,$mailbox)
1621		{
1622			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_mail_move: (DEPRECIATED) ENTERING<br />'); }
1623			// OLD FUNCTION does not provide enough information, all we can do is expire
1624			$this->event_msg_move_or_delete(array(), 'phpgw_mail_move');
1625			// delete session msg array data thAt is now stale
1626			//$this->expire_session_cache_item('msgball_list');
1627
1628			$retval = $GLOBALS['phpgw_dcom_'.$this->acctnum]->dcom->mail_move($this->get_arg_value('mailsvr_stream'), $msg_list, $mailbox);
1629			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_mail_move: (DEPRECIATED) LEAVING, $retval ['.serialize($retval).'] <br />'); }
1630			return $retval;
1631		}
1632
1633		/*!
1634		@function interacct_mail_move
1635		@abstract DEPRECIATED - BEING PHASED OUT. Use "industrial_interacct_mail_move" instead.
1636		@author Angles
1637		@access public
1638		*/
1639		function interacct_mail_move($mov_msgball='', $to_fldball='')
1640		{
1641			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): interacct_mail_move: ENTERING<br />'); }
1642			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 />'); }
1643			// this needs A LOT of work!!! do not rely on this yet
1644
1645			// delete session msg array data thAt is now stale
1646			$this->event_msg_move_or_delete($mov_msgball, 'interacct_mail_move'.' LINE '.__LINE__, $to_fldball);
1647			//$this->expire_session_cache_item('msgball_list');
1648
1649			// Note: Only call this function with ONE msgball at a time, i.e. NOT a list of msgballs
1650			$acctnum = (int)$mov_msgball['acctnum'];
1651			if (!(isset($acctnum))
1652			|| ((string)$acctnum == ''))
1653			{
1654				$acctnum = $this->get_acctnum();
1655			}
1656			$this->ensure_stream_and_folder($mov_msgball, 'interacct_mail_move'.' LINE '.__LINE__);
1657
1658			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 />'); }
1659
1660			// NO - this function only works with folders within the same account
1661			//$this->ensure_stream_and_folder($to_fldball, 'interacct_mail_move'.' LINE '.__LINE__);
1662
1663			//$mailsvr_stream = (int)$this->get_arg_value('mailsvr_stream', $acctnum);
1664			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
1665			$msgnum = (string)$mov_msgball['msgnum'];
1666			$mailbox = $to_fldball['folder'];
1667			// the acctnum we are moving FROM *may* be different from the acctnum we are moving TO
1668			// that requires a fetch then an append - FIXME!!!
1669
1670			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 />'); }
1671			$retval = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->mail_move($mailsvr_stream ,$msgnum, $mailbox);
1672			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): interacct_mail_move: LEAVING, $retval ['.serialize($retval).'] <br />'); }
1673			return $retval;
1674		}
1675
1676		/*!
1677		@function industrial_interacct_mail_move
1678		@abstract ?
1679		@param $mov_msgball (array of type msgball) the message the will be moved.
1680		@param $to_fldball (array of type fldball) the target of the move.
1681		@author Angles
1682		@discussion ?
1683		@access public
1684		*/
1685		function industrial_interacct_mail_move($mov_msgball='', $to_fldball='')
1686		{
1687			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 />'); }
1688			// Note: Only call this function with ONE msgball at a time, i.e. NOT a list of msgballs
1689			// then we buffer each command with this function
1690			$this->buffer_move_commands($mov_msgball, $to_fldball);
1691
1692			//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 />'); }
1693			// do this during actual moves
1694			$this->track_expungable_folders($mov_msgball);
1695
1696			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 />'); }
1697			return True;
1698		}
1699
1700		/*!
1701		@function buffer_mail_move_commands
1702		@abstract ?
1703		@param $mov_msgball (array of type msgball) the message the will be moved.
1704		@param $to_fldball (array of type fldball) the target of the move.
1705		@author Angles
1706		@discussion ?
1707		@access public
1708		*/
1709		function buffer_move_commands($mov_msgball='', $to_fldball='')
1710		{
1711			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): buffer_move_commands ('.__LINE__.'): ENTERING<br />'); }
1712			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 />'); }
1713
1714			// assemble the URI like string that will hold the command move request instructions
1715			$this_move_data = '';
1716			$this_move_data =
1717				 'mov_msgball[acctnum]='.$mov_msgball['acctnum']
1718				.'&mov_msgball[folder]='.$mov_msgball['folder']
1719				.'&to_fldball[acctnum]='.$to_fldball['acctnum']
1720				.'&to_fldball[folder]='.$to_fldball['folder']
1721				.'&mov_msgball[msgnum]='.$mov_msgball['msgnum'];
1722
1723			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 />'); }
1724			if ($this->debug_wrapper_dcom_calls > 2)
1725			{
1726				$this_move_balls = array();
1727				parse_str($this_move_data, $this_move_balls);
1728				//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>';
1729				$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);
1730			}
1731
1732			// add this to the array
1733			$this->buffered_move_commmands[$this->buffered_move_commmands_count] = $this_move_data;
1734			// increase the count, avoids calling count() every trip thru this loop
1735			$this->buffered_move_commmands_count++;
1736			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); }
1737
1738			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 />'); }
1739			return;
1740		}
1741
1742		/*!
1743		@function buffer_delete_commands
1744		@abstract ?
1745		@param $mov_msgball (array of type msgball) the message the will be moved.
1746		@param $to_fldball (array of type fldball) the target of the move.
1747		@author Angles
1748		@discussion ?
1749		@access public
1750		*/
1751		function buffer_delete_commands($mov_msgball='', $to_fldball='')
1752		{
1753			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): buffer_move_commands ('.__LINE__.'): ENTERING<br />'); }
1754			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 />'); }
1755
1756			// assemble the URI like string that will hold the command move request instructions
1757			$this_move_data = '';
1758			$this_move_data =
1759				 'mov_msgball[acctnum]='.$mov_msgball['acctnum']
1760				.'&mov_msgball[folder]='.$mov_msgball['folder']
1761				.'&to_fldball[acctnum]='.$to_fldball['acctnum']
1762				.'&to_fldball[folder]='.$to_fldball['folder']
1763				.'&mov_msgball[msgnum]='.$mov_msgball['msgnum'];
1764
1765			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 />'); }
1766			if ($this->debug_wrapper_dcom_calls > 2)
1767			{
1768				$this_move_balls = array();
1769				parse_str($this_move_data, $this_move_balls);
1770				//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>';
1771				$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);
1772			}
1773
1774			// add this to the array
1775			$this->buffered_move_commmands[$this->buffered_move_commmands_count] = $this_move_data;
1776			// increase the count, avoids calling count() every trip thru this loop
1777			$this->buffered_move_commmands_count++;
1778			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); }
1779
1780			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 />'); }
1781			return;
1782		}
1783
1784		/*
1785Array
1786(
1787    [0] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=38
1788    [1] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=66
1789    [2] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=28
1790    [3] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=29
1791    [4] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=31
1792    [5] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=32
1793    [6] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=33
1794    [7] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=34
1795    [8] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=35
1796    [9] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=24
1797    [10] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=26
1798    [11] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=27
1799    [12] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=23
1800    [13] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=13
1801    [14] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=14
1802    [15] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=15
1803    [16] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=16
1804    [17] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=17
1805    [18] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=18
1806    [19] => mov_msgball[acctnum]=5&mov_msgball[folder]=INBOX&to_fldball[acctnum]=5&to_fldball[folder]=mail%2FPostmaster&mov_msgball[msgnum]=19
1807)
1808
1809Array
1810(
1811    [mov_msgball] => Array
1812        (
1813            [acctnum] => 5
1814            [folder] => INBOX
1815            [msgnum] => 19
1816        )
1817
1818    [to_fldball] => Array
1819        (
1820            [acctnum] => 5
1821            [folder] => mail/Postmaster
1822        )
1823
1824)
1825		*/
1826
1827
1828		/*!
1829		@function flush_buffered_move_commmands
1830		@abstract ?
1831		@author Angles
1832		@discussion ?
1833		@access public
1834		*/
1835		function flush_buffered_move_commmands($called_by='not_specified')
1836		{
1837			$do_it_for_real = True;
1838			//$do_it_for_real = False;
1839
1840			// 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.
1841			$did_give_big_move_notice = False;
1842
1843			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): flush_buffered_move_commmands ('.__LINE__.'): ENTERING, called by ['.$called_by.'], <br />'); }
1844			// leave now if nothing is in the buffered command array
1845			if ($this->buffered_move_commmands_count == 0)
1846			{
1847				if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): flush_buffered_move_commmands ('.__LINE__.'): LEAVING, nothing to do, return False, $this->buffered_move_commmands_count: ['.$this->buffered_move_commmands_count.']<br />'); }
1848				return False;
1849			}
1850
1851			// is this a "big move"
1852			//$big_move_thresh = 2;
1853			$big_move_thresh = 10;
1854			$is_big_move = False;
1855			if ($this->buffered_move_commmands_count > $big_move_thresh)
1856			{
1857				if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): 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 />'); }
1858				$this->event_begin_big_move(array(), 'mail_msg(_wrappers): buffered_move_commmands: LINE '.__LINE__);
1859				$is_big_move = True;
1860			}
1861			// Sort will GROUP THE MOVES AS MUCH AS POSSIBLE RIGHT NOW
1862			// the way we put the strings in the $this->buffered_move_commmands is designed to be
1863			// used by sort to end up grouping similar moves for us inside the array,
1864			// grouping by _from_acctnum__from_folder__to_acctnum__to_folder__msgnum
1865			// so similar moves are grouped as much as possible, simply, by calling sort.
1866			reset($this->buffered_move_commmands);
1867			sort($this->buffered_move_commmands);
1868			// trying to get single digit integers grouped with each other, but I do not think this next function is any better at that
1869			//sort($this->buffered_move_commmands, SORT_NUMERIC & SORT_STRING);
1870			// we know the FROM acct num is the same for all commands
1871			// we know the list is sorted so all FROM folders are together, and then the TO_FOLDERS
1872			// note the the "del_pseudo_folder" also will be grouped together, later we determing what command to call whether move or straight delete
1873			if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): flush_buffered_move_commmands ('.__LINE__.'): we have delete instructions(s) to be processed, (sorted) $this->buffered_move_commmands DUMP:', $this->buffered_move_commmands); }
1874
1875			$grouped_move_balls = array();
1876			// group the commands
1877			for ($x=0; $x < $this->buffered_move_commmands_count; $x++)
1878			{
1879				$this_move_balls = array();
1880				parse_str($this->buffered_move_commmands[$x], $this_move_balls);
1881				if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers): flush_buffered_move_commmands: loop ['.$x.']: $this_move_balls: ['.htmlspecialchars(serialize($this_move_balls)).']<br />'); }
1882				// NOTE PARSE_STR ***WILL ADD SLASHES*** TO ESCAPE QUOTES
1883				// NO MATTER WHAT YOUR MAGIC SLASHES SETTING IS
1884				if ($this->debug_args_input_flow > 1) { $this->dbug->out(' * mail_msg(_wrappers): 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***'); }
1885				if (isset($this_move_balls['mov_msgball']['folder']))
1886				{
1887					$this_move_balls['mov_msgball']['folder'] = stripslashes($this_move_balls['mov_msgball']['folder']);
1888				}
1889				if (isset($this_move_balls['to_fldball']['folder']))
1890				{
1891					$this_move_balls['to_fldball']['folder'] = stripslashes($this_move_balls['to_fldball']['folder']);
1892				}
1893
1894				// no matter what, we know we are going to move this message, so notify cache if needed
1895				// IF WE ISSUED A BIG MOVE NOTICE THEN THE CACHE IS FLUSHED ALREADY
1896				if ($is_big_move == False)
1897				{
1898					if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers): flush_buffered_move_commmands: loop ['.$x.'] $is_big_move: ['.serialize($is_big_move).'] so calling $this->event_msg_move_or_delete()<br />'); }
1899					$this->event_msg_move_or_delete($this_move_balls['mov_msgball'], 'flush_buffered_move_commmands'.' LINE: '.__LINE__, $this_move_balls['to_fldball']);
1900				}
1901
1902				// --- does the FROM folder match the previous one in the list? ---
1903				$count_grouped = count($grouped_move_balls);
1904				// 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
1905				if ($count_grouped  == 0)
1906				{
1907					// add it to the array to get it started
1908					//array_push($grouped_move_balls, $this_move_balls);
1909					$grouped_move_balls[0] = $this_move_balls;
1910					if ($this->buffered_move_commmands_count > 1)
1911					{
1912						// SKIP TO NEXT LOOP, we need to compare (try to group) b4 we know to issue the actual move command or not
1913						// NOTE: CONTINUE
1914						if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: added item to array, skip to next iteration<br />'); }
1915						continue;
1916					}
1917					else
1918					{
1919						if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers): 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 />'); }
1920					}
1921				}
1922				//elseif (($count_grouped > 0)
1923				//&& ($grouped_move_balls[$count_grouped-1]['mov_msgball']['folder'] == $this_move_balls['mov_msgball']['folder'])
1924				//&& ($grouped_move_balls[$count_grouped-1]['to_fldball']['folder'] == $this_move_balls['to_fldball']['folder'])
1925				//)
1926				elseif (($count_grouped > 0)
1927				&& ($x != $this->buffered_move_commmands_count-1)
1928				&& ($grouped_move_balls[$count_grouped-1]['mov_msgball']['acctnum'] == $this_move_balls['mov_msgball']['acctnum'])
1929				&& ($grouped_move_balls[$count_grouped-1]['mov_msgball']['folder'] == $this_move_balls['mov_msgball']['folder'])
1930				&& ($grouped_move_balls[$count_grouped-1]['to_fldball']['folder'] == $this_move_balls['to_fldball']['folder'])
1931				)
1932				{
1933					// PASSES the "is grouped" test, add to the "grouped array"
1934					// AND this is NOT the last item in buffered_move_commmands (that would require action, not another loop)
1935					array_push($grouped_move_balls, $this_move_balls);
1936					// NOTE: CONTINUE
1937					if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: added item to array, skip to next iteration<br />'); }
1938					continue;
1939				}
1940				elseif (($count_grouped > 0)
1941				&& ($x == $this->buffered_move_commmands_count-1)
1942				&& ($grouped_move_balls[$count_grouped-1]['mov_msgball']['acctnum'] == $this_move_balls['mov_msgball']['acctnum'])
1943				&& ($grouped_move_balls[$count_grouped-1]['mov_msgball']['folder'] == $this_move_balls['mov_msgball']['folder'])
1944				&& ($grouped_move_balls[$count_grouped-1]['to_fldball']['folder'] == $this_move_balls['to_fldball']['folder'])
1945				)
1946				{
1947					// PASSES the "is grouped" test, add to the "grouped array"
1948					// AND this is the FINAL ITEM, so KEEP GOING down to the code to issue the actual move command
1949					array_push($grouped_move_balls, $this_move_balls);
1950					if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: added item to array, but NOT skipping to next iteration<br />'); }
1951					// DO NOT issue "CONTINUE" here
1952				}
1953				else
1954				{
1955					//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>' ; }
1956					if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: UNHANDLED if .. then, $$grouped_move_balls[$count_grouped-1] DUMP', $grouped_move_balls[$count_grouped-1]); }
1957					if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * mail_msg(_wrappers): flush_buffered_move_commmands('.__LINE__.'): loop ['.$x.']: UNHANDLED if .. then, $$this_move_balls DUMP', $this_move_balls); }
1958				}
1959
1960				if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): flush_buffered_move_commmands: if we get here, we can not group anymore, or the series just ended, so issue the command now<br />'); }
1961				// OK if we are here then we know this
1962				// * "grouped_move_balls" has at least one command in it
1963				// ** the current command does not match the preious one in terms or grouping them together
1964				// ** OR the urrent command is the final in the $this->buffered_move_commmands array
1965				// THEREFOR:
1966				// 1) we need now make a IMAP command that has all the grouped msgnums from grouped_move_balls
1967				// 2) if NOT the final item in $this->buffered_move_commmands we need to
1968				//     2a) then we need to clear grouped_move_balls and ADD this_move_balls to it to start a new grouping array
1969				//     2b) then run again thru the loop after that
1970
1971				// update this, this loop may have added to it since we last checked this
1972				$count_grouped = count($grouped_move_balls);
1973
1974				// IF THIS MOVE IS TO ANOTHER ACCOUNT, HAND IT OFF RIGHT NOW
1975				if ( ($count_grouped = 1)
1976				&& ((int)$grouped_move_balls[0]['mov_msgball']['acctnum'] != (int)$grouped_move_balls[0]['to_fldball']['acctnum']) )
1977				{
1978					if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): 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 />'); }
1979					if ($do_it_for_real == True)
1980					{
1981						$this->single_interacct_mail_move($grouped_move_balls[$count_grouped-1]['mov_msgball'], $grouped_move_balls[$count_grouped-1]['to_fldball']);
1982					}
1983				}
1984				elseif ( ($count_grouped > 1)
1985				&& ((int)$grouped_move_balls[$count_grouped-1]['mov_msgball']['acctnum'] != (int)$grouped_move_balls[$count_grouped-1]['to_fldball']['acctnum']) )
1986				{
1987					if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): 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 />'); }
1988					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 />';
1989					return False;
1990				}
1991				else
1992				{
1993					// FIXME: some logic below relies on strlen of $collected_msg_num_string to determine if it has ONE DIGIT or not
1994					// but a single integer can be 1 char or 5 chars, for example, this causes *very* rare errors in the string groupings
1995					// 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
1996					// whih can happen if 2 digits are put together without a comman or colon inbetween, makes a number unrelated to the grouping
1997
1998					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); }
1999					// update this, this loop may have added to it since we last checked this
2000					$count_grouped = count($grouped_move_balls);
2001					$collected_msg_num_string = '';
2002					// super dumb but simple way, just put a comma between all msgnum's
2003					//for ($group_loops=0; $group_loops < $count_grouped; $group_loops++)
2004					//{
2005					//	if ($group_loops > 0)
2006					//	{
2007					//		$collected_msg_num_string .= ',';
2008					//	}
2009					//	$collected_msg_num_string .= $grouped_move_balls[$group_loops]['mov_msgball']['msgnum'];
2010					//}
2011					// BETTER way, use rfv2060 specs to put range of msgnums together seperated by a colon
2012					for ($group_loops=0; $group_loops < $count_grouped; $group_loops++)
2013					{
2014						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 />'); }
2015						if ( ($group_loops > 0)
2016						//&& (($grouped_move_balls[$group_loops-1]['mov_msgball']['msgnum']+1) == $grouped_move_balls[$group_loops]['mov_msgball']['msgnum']) )
2017						&& ($grouped_move_balls[$group_loops-1]['mov_msgball']['msgnum']+1 == $grouped_move_balls[$group_loops]['mov_msgball']['msgnum']) )
2018						{
2019							// we have a contiguous series, handle string specially
2020							if (($count_grouped == 2)
2021							&& ($group_loops == 1))
2022							{
2023								// two items will never make a series
2024								$collected_msg_num_string .= ','.(string)$grouped_move_balls[$group_loops]['mov_msgball']['msgnum'];
2025							}
2026							elseif ($group_loops == $count_grouped-1)
2027							{
2028								// the contiguous series of numbers just ended because the list is done
2029								// if there is not a comma nor a colon after the last number, put one there
2030								$last_char_idx = strlen((string)$collected_msg_num_string)-1;
2031								$last_char = $collected_msg_num_string[$last_char_idx];
2032								// situation is that two contiguos numbers at the end of a list like this need a comma
2033								if (($last_char != ',')
2034								&& ($last_char != ':'))
2035								{
2036									// COLON OR A COMMAN NEEDED
2037									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 />'); }
2038									if (($count_grouped > 2)
2039									//&& (strlen((string)$collected_msg_num_string) == 1))
2040									&& (!stristr($collected_msg_num_string, ':'))
2041									&& (!stristr($collected_msg_num_string, ',')))
2042									{
2043										// ADD A COLON IF
2044										// (a) if the total things the move are > 2 AND
2045										// (b) there is so far only ONE number in our $collected_msg_num_string
2046										// then this is a series that had been uninterupted since it began looping here
2047										// situation is nums are 1, 2, 3, 4, 5, so in the last loop $collected_msg_num_string = "1:5"
2048										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 />'); }
2049										$collected_msg_num_string .= ':';
2050									}
2051									else
2052									{
2053										// ADD A COMMA, these are
2054										// situation is nums are 3, 37, 38, so in the last loop $collected_msg_num_string = "3,37"
2055										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 />'); }
2056										$collected_msg_num_string .= ',';
2057									}
2058								}
2059								$collected_msg_num_string .= (string)$grouped_move_balls[$group_loops]['mov_msgball']['msgnum'];
2060							}
2061							elseif ( (strlen($collected_msg_num_string) > 1)
2062							&& ($collected_msg_num_string[strlen($collected_msg_num_string)-1] != ':') )
2063							{
2064								// this is a contiguous series just starting, needs a colon
2065								$collected_msg_num_string .= ':';
2066							}
2067							else
2068							{
2069								// DO NOTHING we are in the middle of this contiguous series of numbers
2070							}
2071						}
2072						// did a series just end?
2073						elseif ( ($group_loops > 1)
2074						&& (($grouped_move_balls[$group_loops-2]['mov_msgball']['msgnum']+1) == $grouped_move_balls[$group_loops-1]['mov_msgball']['msgnum']) )
2075						{
2076							// NOTE: ADD A COLON
2077							// if the previous existing $collected_msg_num_string does NOT have a colon or comma as its last crag
2078							$last_char_idx = strlen((string)$collected_msg_num_string)-1;
2079							$last_char = $collected_msg_num_string[$last_char_idx];
2080							// situation is that the current end of a list needs something before we can add another number, (a comma or a colon)
2081							if (($last_char != ',')
2082							&& ($last_char != ':'))
2083							{
2084								// so why colon and no check for a needed comma?
2085								// SINCE WE KNOW A SERIES JUST ENDED, whether just 2 contiguous numbers or 20 contiguous numbers
2086								// WE KNOW that a colon would be valid because we KNOW the previous existing number
2087								//   AND the number we are about to add are IN A SERIES
2088								// so a colon would not create an unwanted series becuase we KNOW we have a series at this point
2089								// so ... a colon can not hurt, a colon between contiguous numbers is still valid syntax (i.e. "3:4"
2090								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 />'); }
2091								$collected_msg_num_string .= ':';
2092							}
2093							//  inset the number of the end of the series, a comman, and the current non-contiguous number
2094							$collected_msg_num_string .=
2095								 (string)$grouped_move_balls[$group_loops-1]['mov_msgball']['msgnum']
2096								.','
2097								.(string)$grouped_move_balls[$group_loops]['mov_msgball']['msgnum'];
2098						}
2099						else
2100						{
2101							// we are NOT in a contiguous series, inset  a comma, and the current number
2102							if (strlen((string)$collected_msg_num_string) > 0)
2103							{
2104								$collected_msg_num_string .= ',';
2105							}
2106							$collected_msg_num_string .= (string)$grouped_move_balls[$group_loops]['mov_msgball']['msgnum'];
2107						}
2108						if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out(' * flush_buffered_move_commmands ('.__LINE__.'): $collected_msg_num_string: ['.$collected_msg_num_string.']<br />'); }
2109					}
2110
2111					if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): flush_buffered_move_commmands: final $collected_msg_num_string: ['.$collected_msg_num_string.']<br />'); }
2112					// 1b) issue the delete COMMAND finally now
2113					$mov_msgball = array();
2114					$mov_msgball = $grouped_move_balls[$count_grouped-1]['mov_msgball'];
2115					$to_fldball = array();
2116					$to_fldball = $grouped_move_balls[$count_grouped-1]['to_fldball'];
2117					// the FROM acctnum we'll use as "this_acctnum"
2118					$this_acctnum = $mov_msgball['acctnum'];
2119					// EXPIRE MSGBALL ???? wasn't this done with the notice of big move?
2120					// note since we ALWAYS turn off extreme caching when weuse this function, we *could* DIRECTLY expire it
2121					//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 />'; }
2122					//$this->event_msg_move_or_delete($mov_msgball, 'flush_buffered_move_commmands'.' LINE: '.__LINE__.' and CACHE SHOULD BE OFF NOW', $to_fldball);
2123					//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 />'; }
2124					//$this->expire_session_cache_item('msgball_list', $this_acctnum);
2125					if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): 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 />'); }
2126					if ($do_it_for_real == True)
2127					{
2128						$this->ensure_stream_and_folder($mov_msgball, 'flush_buffered_move_commmands'.' LINE: '.__LINE__);
2129					}
2130					$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $this_acctnum);
2131					// IS THIS A MOVE OR A DELETE?
2132					if ($to_fldball['folder'] == $this->del_pseudo_folder || $to_fldball['folder'] == '##NOTHING##')
2133					{
2134						// STRAIGHT DELETE
2135						if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): 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 />'); }
2136						if ($do_it_for_real == True)
2137						{
2138							$did_delete = $GLOBALS['phpgw_dcom_'.$this_acctnum]->dcom->delete($mailsvr_stream , $collected_msg_num_string);
2139							if (!$did_delete)
2140							{
2141								$imap_err = $GLOBALS['phpgw_dcom_'.$this_acctnum]->dcom->server_last_error();
2142								if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): flush_buffered_move_commmands: STRAIGHT DELETE: LEAVING on ERROR, $imap_err: ['.$imap_err.'] return False'.' LINE '.__LINE__.'<br />'); }
2143								echo 'mail_msg(_wrappers): flush_buffered_move_commmands: LEAVING on ERROR, $imap_err: ['.$imap_err.'] return False'.' LINE '.__LINE__.'<br />';
2144								echo '&nbsp; command was: $GLOBALS[phpgw_dcom_'.$this_acctnum.']->dcom->delete('.serialize($mailsvr_stream).' ,'.$collected_msg_num_string.')<br />';
2145								return False;
2146							}
2147						}
2148					}
2149					else
2150					{
2151						// MOVE
2152						if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): 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 />'); }
2153						if ($do_it_for_real == True)
2154						{
2155							$did_move = $GLOBALS['phpgw_dcom_'.$this_acctnum]->dcom->mail_move($mailsvr_stream , $collected_msg_num_string, $to_fldball['folder']);
2156							if (!$did_move)
2157							{
2158								$imap_err = $GLOBALS['phpgw_dcom_'.$this_acctnum]->dcom->server_last_error();
2159								if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): flush_buffered_move_commmands: LEAVING on ERROR, $imap_err: ['.$imap_err.'] return False'.' LINE '.__LINE__.'<br />'); }
2160								echo 'mail_msg(_wrappers): flush_buffered_move_commmands: LEAVING on ERROR, $imap_err: ['.$imap_err.'] return False'.' LINE '.__LINE__.'<br />';
2161								echo '&nbsp; command was: $GLOBALS[phpgw_dcom_'.$this_acctnum.']->dcom->mail_move('.serialize($mailsvr_stream).' ,'.$collected_msg_num_string.', '.serialize($to_fldball['folder']).')<br />';
2162								return False;
2163							}
2164						}
2165					}
2166					if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): 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 />'); }
2167				}
2168
2169				// 2) if NOT the final item in $this->buffered_move_commmands we need to
2170				//     2a) then we need to clear grouped_move_balls and ADD this_move_balls to it to start a new grouping array
2171				//     2b) then run again thru the loop after that
2172				if ($x != $this->buffered_move_commmands_count-1)
2173				{
2174					$grouped_move_balls = array();
2175					array_push($grouped_move_balls, $this_move_balls);
2176					// 3) then run again thru the loop after that
2177					if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): flush_buffered_move_commmands: continue; ... to look for groupable move commands for acctnum ['.$mailsvr_stream.']<br />'); }
2178					// doesn't this happen anyway here?
2179					continue;
2180				}
2181				if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): 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 />'); }
2182			}
2183
2184			if ($is_big_move == True)
2185			{
2186				if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): 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 />'); }
2187				$this->event_begin_big_end('flush_buffered_move_commmands '.__LINE__);
2188			}
2189			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): flush_buffered_move_commmands ('.__LINE__.'): LEAVING<br />'); }
2190			// FIXME return something more useful
2191			return True;
2192		}
2193
2194
2195		/*!
2196		@function single_interacct_mail_move
2197		@abstract Primary mail move function for DIFFERENT Accounts. Moves single mails, use a loop if moving more than one mail.
2198		@param $mov_msgball (array of type msgball) the message the will be moved.
2199		@param $to_fldball (array of type fldball) the target of the move.
2200		@author Angles
2201		@discussion Can handle any kind of move, same account, different account, different server.  Now
2202		used mostly for different account moves, because we attempt to group single account moves elsewhere.
2203		Fills arg "expunge_folders" for any account that has folders needing to be expunged.
2204		@access public
2205		*/
2206		function single_interacct_mail_move($mov_msgball='', $to_fldball='')
2207		{
2208			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 />'); }
2209			// Note: Only call this function with ONE msgball at a time, i.e. NOT a list of msgballs
2210			// INTERACCOUNT -OR- SAME ACCOUNT ?
2211			if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: $mov_msgball DUMP:', $mov_msgball); }
2212			if ($this->debug_wrapper_dcom_calls > 2) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: $to_fldball DUMP:', $to_fldball); }
2213			// --- Establist account numbers ----
2214			$mov_msgball['acctnum'] = (int)$mov_msgball['acctnum'];
2215			if (!(isset($mov_msgball['acctnum']))
2216			|| ((string)$mov_msgball['acctnum'] == ''))
2217			{
2218				$mov_msgball['acctnum'] = $this->get_acctnum();
2219			}
2220			$to_fldball['acctnum'] = (int)$to_fldball['acctnum'];
2221			if (!(isset($to_fldball['acctnum']))
2222			|| ((string)$to_fldball['acctnum'] == ''))
2223			{
2224				$to_fldball['acctnum'] = $this->get_acctnum();
2225			}
2226
2227			// Are the acctnums the same?
2228			if ((string)$mov_msgball['acctnum'] == (string)$to_fldball['acctnum'])
2229			{
2230				// SAME ACCOUNT MAIL MOVE
2231
2232				$common_acctnum = $mov_msgball['acctnum'];
2233				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 />'); }
2234				$this->event_msg_move_or_delete($mov_msgball, 'single_interacct_mail_move'.' LINE: '.__LINE__, $to_fldball);
2235				//$this->expire_session_cache_item('msgball_list', $common_acctnum);
2236				// we need to SELECT the folder the message is being moved FROM
2237				$mov_msgball['folder'] = urldecode($mov_msgball['folder']);
2238
2239				$this->ensure_stream_and_folder($mov_msgball, 'single_interacct_mail_move'.' LINE: '.__LINE__);
2240				$mov_msgball['msgnum'] = (string)$mov_msgball['msgnum'];
2241				$to_fldball['folder'] = urldecode($to_fldball['folder']);
2242				$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $common_acctnum);
2243				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 />'); }
2244				$did_move = $GLOBALS['phpgw_dcom_'.$common_acctnum]->dcom->mail_move($mailsvr_stream ,$mov_msgball['msgnum'], $to_fldball['folder']);
2245				if (!$did_move)
2246				{
2247					if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return False'.' LINE '.__LINE__.'<br />'); }
2248					return False;
2249				}
2250				else
2251				{
2252					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 />'); }
2253					$this->track_expungable_folders($mov_msgball);
2254					//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 />'; }
2255					//return $this->phpgw_expunge($mov_msgball['acctnum']);
2256					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 />'); }
2257					return True;
2258				}
2259			}
2260			else
2261			{
2262				// DIFFERENT ACCOUNT MAIL MOVE
2263
2264				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 />'); }
2265				$good_to_go = False;
2266				// delete session msg array data thAt is now stale
2267				$this->event_msg_move_or_delete($mov_msgball, 'single_interacct_mail_move'.' LINE: '.__LINE__, $to_fldball);
2268				//$this->expire_session_cache_item('msgball_list', $mov_msgball['acctnum']);
2269				$mov_msgball['folder'] = urldecode($mov_msgball['folder']);
2270				// Make Sure Stream Exists
2271				// multiple accounts means one stream may be open but another may not
2272				// "ensure_stream_and_folder" will verify for us,
2273				$this->ensure_stream_and_folder($mov_msgball, 'single_interacct_mail_move');
2274				// GET MESSAGE FLAGS (before you get the mgs, so unseen/seen is not tainted by our grab)
2275				$hdr_envelope = $this->phpgw_header($mov_msgball);
2276				$mov_msgball['flags'] = $this->make_flags_str($hdr_envelope);
2277				// GET THE MESSAGE
2278				// part_no 0 only used to get the headers
2279				$mov_msgball['part_no'] = 0;
2280				// (a)  the headers, specify part_no 0
2281				//$moving_message = $GLOBALS['phpgw']->msg->phpgw_fetchbody($mov_msgball);
2282				$moving_message = $this->phpgw_fetchbody($mov_msgball);
2283				// (b) the body, plus a CRLF, reuse headers_msgball b/c "phpgw_body" cares not about part_no
2284				//$moving_message .= $GLOBALS['phpgw']->msg->phpgw_body($mov_msgball)."\r\n";
2285				$moving_message .= $this->phpgw_body($mov_msgball)."\r\n";
2286				$good_to_go = (strlen($moving_message) > 3);
2287				if (!$good_to_go)
2288				{
2289					if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return False'.' LINE '.__LINE__.'<br />'); }
2290					return False;
2291				}
2292
2293				// APPEND TO TARGET FOLDER
2294				// delete session msg array data thAt is now stale
2295				// WE DO NOT GUESS ABOUT APPENDS, WE EXPIRE THE DATA AND GET FRESH
2296				//$this->expire_session_cache_item('msgball_list', $to_fldball['acctnum']);
2297				$this->event_msg_append($to_fldball, 'single_interacct_mail_move  Line '.__LINE__);
2298
2299
2300				$to_fldball['folder'] = urldecode($to_fldball['folder']);
2301				// TEMP (MUST add this back!!!) append does NOT require we open the target folder, only requires a stream
2302				//$remember_to_fldball = $to_fldball['folder'];
2303				//$to_fldball['folder'] = '';
2304				// PASS "no_switch_away" to indicate we should NOT CHANGE FOLDERS
2305				$to_fldball['no_switch_away'] = True;
2306				$this->ensure_stream_and_folder($to_fldball, 'single_interacct_mail_move');
2307				$mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $to_fldball['acctnum']);
2308				$to_mailsvr_stream = $this->get_arg_value('mailsvr_stream', $to_fldball['acctnum']);
2309				//$to_fldball['folder'] = $remember_to_fldball;
2310				// $tol_folder_utldecoded =
2311				$good_to_go = $GLOBALS['phpgw_dcom_'.$to_fldball['acctnum']]->dcom->append($to_mailsvr_stream, $mailsvr_callstr.$to_fldball['folder'], $moving_message, $mov_msgball['flags']);
2312				if (!$good_to_go)
2313				{
2314					if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return False'.' LINE '.__LINE__.'<br />'); }
2315					return False;
2316				}
2317				// DELETE and EXPUNGE from FROM FOLDER
2318				$from_mailsvr_stream = $this->get_arg_value('mailsvr_stream', $mov_msgball['acctnum']);
2319				$good_to_go = $GLOBALS['phpgw_dcom_'.$mov_msgball['acctnum']]->dcom->delete($from_mailsvr_stream, $mov_msgball['msgnum']);
2320				if (!$good_to_go)
2321				{
2322					if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return False'.' LINE '.__LINE__.'<br />'); }
2323					return False;
2324				}
2325				//$good_to_go = $GLOBALS['phpgw']->msg->phpgw_expunge($mov_msgball['acctnum']);
2326				//$good_to_go = $this->phpgw_expunge($mov_msgball['acctnum']);
2327				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 />'); }
2328				$this->track_expungable_folders($mov_msgball);
2329
2330				if (!$good_to_go)
2331				{
2332					if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return False'.' LINE '.__LINE__.'<br />'); }
2333					return False;
2334				}
2335				if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): single_interacct_mail_move: LEAVING, return TRUE'.' LINE '.__LINE__.'<br />'); }
2336				return True;
2337			}
2338		}
2339
2340		/*!
2341		@function track_expungable_folders
2342		@abstract Keeps track of what accounts folders will need to be expunged.
2343		@author Angles
2344		@discussion Used by "industrial_interacct_mail_move" and "phpgw_delete" to keep track
2345		of which folders for any account will need to be expunged. NOTE this tracking occurs
2346		automatically in those functions, HOWEVER the calling function is responsible to
2347		call "expunge_expungable_folders" when all the moves or deletes are done.
2348		@access private
2349		*/
2350		function track_expungable_folders($fldball='')
2351		{
2352			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): track_expungable_folders ('.__LINE__.'): ENTERING, $fldball ['.serialize($fldball).']<br />'); }
2353			if (!(isset($fldball['acctnum']))
2354			|| ((string)$fldball['acctnum'] == ''))
2355			{
2356				$acctnum = $this->get_acctnum();
2357			}
2358			else
2359			{
2360				$acctnum = $fldball['acctnum'];
2361			}
2362			if (!(isset($fldball['folder']))
2363			|| ((string)$fldball['folder'] == ''))
2364			{
2365				$folder = $this->get_arg_value('folder', $acctnum);
2366			}
2367			else
2368			{
2369				$folder = $fldball['folder'];
2370			}
2371			$my_fldball = array();
2372			$my_fldball['folder'] = $folder;
2373			$my_fldball['acctnum'] = $acctnum;
2374			if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): track_expungable_folders ('.__LINE__.'): $my_fldball ['.serialize($my_fldball).']<br />'); }
2375
2376			$first_addition_to_array = False;
2377			// get an array of folders that need expunging that we know of
2378			if ($this->get_isset_arg('expunge_folders', $my_fldball['acctnum']) == False)
2379			{
2380				$expunge_folders = array();
2381				$first_addition_to_array = True;
2382			}
2383			else
2384			{
2385				//$expunge_folders = $this->get_arg_value('expunge_folders', $my_fldball['acctnum']);
2386				$expunge_folders =& $this->_get_arg_ref('expunge_folders', $my_fldball['acctnum']);
2387			}
2388			if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): track_expungable_folders ('.__LINE__.'): $expunge_folders DUMP:', $expunge_folders); }
2389			// if this particular folder already in the array
2390			$loops = count($expunge_folders);
2391			$already_listed = False;
2392			for ($i=0; $i<$loops;$i++)
2393			{
2394				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 />'); }
2395				if ($expunge_folders[$i] == $my_fldball['folder'])
2396				{
2397					$already_listed = True;
2398					break;
2399				}
2400			}
2401			// if this folder was NOT already in the array, put it there and save the arg  value
2402			if ($already_listed == False)
2403			{
2404				$new_idx = count($expunge_folders);
2405				$expunge_folders[$new_idx] = $my_fldball['folder'];
2406				if ($first_addition_to_array == True)
2407				{
2408					$this->set_arg_value('expunge_folders', $expunge_folders, $my_fldball['acctnum']);
2409					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 />'); }
2410				}
2411				else
2412				{
2413					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 />'); }
2414				}
2415				return True;
2416			}
2417			else
2418			{
2419				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 />'); }
2420				return False;
2421			}
2422		}
2423
2424		/*!
2425		@function expunge_expungable_folders
2426		@abstract loops thru ALL accounts, expunges any account that has folder names in its arg "expunge_folders"
2427		@author Angles
2428		@discussion This function uses the folder tracking from "track_expungable_folders" to know what
2429		to expunge. Call this function after all your moves or deletes are done.
2430		@access public
2431		*/
2432		function expunge_expungable_folders($called_by='not_specified')
2433		{
2434			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): expunge_expungable_folders ('.__LINE__.'): ENTERING, called by ['.$called_by.'], <br />'); }
2435
2436			$this->flush_buffered_move_commmands('expunge_expungable_folders');
2437
2438			$expunge_folders = array();
2439			for ($i=0; $i < count($this->extra_and_default_acounts); $i++)
2440			{
2441				if ($this->extra_and_default_acounts[$i]['status'] == 'enabled')
2442				{
2443					$this_acctnum = $this->extra_and_default_acounts[$i]['acctnum'];
2444					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 />'); }
2445					if ($this->get_isset_arg('expunge_folders', $this_acctnum) == True)
2446					{
2447						$expunge_folders = array();
2448						$expunge_folders = $this->get_arg_value('expunge_folders', $this_acctnum);
2449						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); }
2450
2451						for ($x=0; $x < count($expunge_folders); $x++)
2452						{
2453							$success = False;
2454							$fake_fldball = array();
2455							$fake_fldball['acctnum'] = $i;
2456							$fake_fldball['folder'] = $expunge_folders[$x];
2457							$success = $this->phpgw_expunge('', $fake_fldball);
2458							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 />'); }
2459						}
2460						// we are done with this account, we expunged all expungable folders, not UNSET that arg so it is not left hanging around
2461						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 />'); }
2462						$this->unset_arg('expunge_folders', $this_acctnum);
2463					}
2464					else
2465					{
2466						$expunge_folders = array();
2467						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 />'); }
2468					}
2469				}
2470			}
2471			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): expunge_expungable_folders ('.__LINE__.'): LEAVING<br />'); }
2472			// FIXME return something more useful
2473			return True;
2474		}
2475
2476
2477		/*!
2478		@function phpgw_expunge
2479		@abstract Expunge a folder.
2480		@author Angles
2481		@discussion Brainless function, used by "expunge_expungable_folders" which is a "smart"
2482		function. This may be called directly, but it is preferable to use "expunge_expungable_folders"
2483		assuming the move or delete function you used calls "track_expungable_folders".
2484		@access public
2485		*/
2486		function phpgw_expunge($acctnum='', $fldball='')
2487		{
2488			if (!(isset($acctnum))
2489			|| ((string)$acctnum == ''))
2490			{
2491				if (!(isset($fldball['acctnum']))
2492				|| ((string)$fldball['acctnum'] == ''))
2493				{
2494					$acctnum = $this->get_acctnum();
2495				}
2496				else
2497				{
2498					$acctnum = $fldball['acctnum'];
2499				}
2500			}
2501
2502			//$fake_fldball = array();
2503			//$fake_fldball['folder'] = $this->get_arg_value('folder', $acctnum);
2504
2505			// NOTE: it is OK to pass blank folder to "ensure_stream_and_folder" when FOLDER DOE NOT MATTER
2506			// for expunge, all we need is a stream,
2507			//$fake_fldball['acctnum'] = $acctnum;
2508			//$this->ensure_stream_and_folder($fake_fldball, 'phpgw_expunge'.' LINE '.__LINE__);
2509			// NOTE THAT CAUSED MAILSERVER TO REOPEN *AWAY* FROM FOLDER NEEDING EXPUNGE
2510			// and re-open to INBOX because a blank folder arg was passed.
2511			if ((isset($fldball['acctnum']))
2512			&& ((string)$fldball['acctnum'] != '')
2513			&& (isset($fldball['folder']))
2514			&& ((string)$fldball['folder'] != ''))
2515			{
2516				$this->ensure_stream_and_folder($fldball, 'phpgw_expunge'.' LINE '.__LINE__);
2517			}
2518
2519			$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
2520			$note_folder = $this->get_arg_value('folder', $acctnum);
2521			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): dcom_call: phpgw_expunge: $acctnum: '.serialize($acctnum).' NOTE current "folder" arg set for that acct is ['.$note_folder.']<br />'); }
2522
2523			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 />'); }
2524			return $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->expunge($mailsvr_stream);
2525		}
2526
2527		/*!
2528		@function phpgw_delete
2529		@abstract Delete a message, will move to "Trash" folder is necessary.
2530		@author Angles
2531		@param $msg_num (int) single msgnum of msg to "delete" (or move to trash folder")
2532		@param $currentfolder (string) full name (as in folder_long) and urlencoded name of the
2533		folder from which we are deleting from.
2534		@param $acctnum (int) (optional) acctnum this applies to
2535		@param $known_single_delete (boolean) BEING PHASED OUT was used to take abreviated action
2536		if we know this is only a single delete, not just one in a series, this logic being moved elsewhere.
2537		@discussion If the user pref wants to use the Trash folder, this function will auto-create
2538		that folder if it does not already exist, and move the mail to that trash folder. If
2539		the user pref is to not use a trash folder, or if deleting mail that is IN the trash folder,
2540		then a straight delete is done. Keeps track of folders needing expunging via calls
2541		to "track_expungable_folders", but the calling process is responsible to
2542		call "expunge_expungable_folders" after all deletes have been done.
2543		@access public
2544		*/
2545		function phpgw_delete($msg_num,$flags=0, $currentfolder="", $acctnum='', $known_single_delete=False)
2546		{
2547			if ($this->debug_wrapper_dcom_calls > 0) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete: ENTERING <br />'); }
2548
2549			if (!(isset($acctnum))
2550			|| ((string)$acctnum == ''))
2551			{
2552				$acctnum = $this->get_acctnum();
2553			}
2554			// everything from now on MUST specify this $acctnum
2555
2556			// now get the stream that applies to that acctnum
2557			if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete: $acctnum: ['.$acctnum.'], $msg_num: ['.$msg_num.']<br />'); }
2558
2559			// this get arg value checks the pref for enabled or not enabled, no need to do it again
2560			if ($this->get_isset_arg('verified_trash_folder_long', $acctnum) == False)
2561			{
2562				$trash_folder_primer = $this->get_arg_value('verified_trash_folder_long', $acctnum);
2563				$trash_folder_primer = '';
2564				unset($trash_folder_primer);
2565			}
2566
2567			// -- determine if we are moving to the trash folder or actually deleting the message
2568			$trash_folder_long =& $this->_get_arg_ref('verified_trash_folder_long', $acctnum);
2569			// if $trash_folder_long is not an ampty string, we need to try to move msgs to it
2570			if ($trash_folder_long != '')
2571			{
2572				// get a clean version
2573				$currentfolder_encoded = $currentfolder;
2574				$currentfolder_clean = urldecode($currentfolder);
2575
2576				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 />'); }
2577				//echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): "Trash" folder pref is enabled, does ['.$currentfolder.'] == ['.$trash_folder_long.']<br />';
2578				if ( ($currentfolder_clean != '')
2579				&& ($currentfolder_clean == $trash_folder_long) )
2580				{
2581					$straight_delete = True;
2582					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 />'); }
2583					//echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): "Trash" folder pref is enabled, shortcut good<br />';
2584				}
2585				else
2586				{
2587					$straight_delete = False;
2588					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 />'); }
2589				}
2590				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 />'); }
2591			}
2592			else
2593			{
2594				$straight_delete = True;
2595				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 />'); }
2596			}
2597
2598			// now that we know if this is a straight delete or not
2599			// TAKE ACTION
2600			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 />'); }
2601			if ($straight_delete == True)
2602			{
2603				if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): begin code for STRAIGHT DELETE<br />'); }
2604				$mov_msgball = array();
2605				if ((isset($currentfolder_encoded))
2606				&& ((string)$currentfolder_encoded != ''))
2607				{
2608					// lets trust that current folder is in long form
2609					$mov_msgball['folder'] = $currentfolder_encoded;
2610				}
2611				else
2612				{
2613					$mov_msgball['folder'] = $this->prep_folder_out();
2614				}
2615				$mov_msgball['acctnum'] = $acctnum;
2616				$mov_msgball['msgnum'] = $msg_num;
2617
2618				// STRAIGHT DELETE has a "PSUEDO FOLDER" called "##DELETE##"
2619				// that we use in the "flush_buffered_move_commmands" to indicate a delete instead of a move
2620				// AND we'll use the same "acctnum" as the delete from acctnum because this will group them together during a "sort" of the array
2621				// so we can use the same function  for both
2622				$to_fldball = array();
2623				$to_fldball['acctnum'] = $mov_msgball['acctnum'];
2624				$to_fldball['folder'] = $this->del_pseudo_folder;
2625
2626				// PUT THIS COMMAND IN THE BUFFERED MOVE (OR DELETE) ARRAY
2627				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 />'); }
2628				$did_take_action = $this->industrial_interacct_mail_move($mov_msgball, $to_fldball);
2629				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 />'); }
2630				// LEAVING
2631				return $did_take_action;
2632
2633				/*
2634				// BELOW HERE SHOULD GO INTO THE NEW STRAIGHT DELETE BUFFER
2635				if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): calling $this->event_msg_move_or_delete()<br />'; }
2636				$this->event_msg_move_or_delete($mov_msgball, 'phpgw_delete'.' LINE: '.__LINE__);
2637				//$this->expire_session_cache_item('msgball_list', $acctnum);
2638				// delete this when we start buffering straight deletes
2639				if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): calling $this->ensure_stream_and_folder()<br />'; }
2640				$this->ensure_stream_and_folder($mov_msgball, 'phpgw_delete'.' LINE '.__LINE__);
2641
2642				if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): getting "mailsvr_stream"<br />'; }
2643				$mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum);
2644				//return imap_delete($mailsvr_stream,$msg_num);
2645				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 />'; }
2646				$retval = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->delete($mailsvr_stream, $msg_num);
2647				if ($retval)
2648				{
2649					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 />'; }
2650					$this->track_expungable_folders($mov_msgball);
2651				}
2652				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 />'; }
2653				// LEAVING
2654				return $retval;
2655				*/
2656			}
2657			else
2658			{
2659				if ($this->debug_wrapper_dcom_calls > 1) { $this->dbug->out('mail_msg(_wrappers): phpgw_delete('.__LINE__.'): begin do move to trash folder<br />'); }
2660				$mov_msgball = array();
2661				if ((isset($currentfolder_encoded))
2662				&& ((string)$currentfolder_encoded != ''))
2663				{
2664					// lets trust that current folder is in long form (and encoded - I guess we like it that way? it came in here that way)
2665					$mov_msgball['folder'] = $currentfolder_encoded;
2666				}
2667				else
2668				{
2669					$mov_msgball['folder'] = $this->prep_folder_out();
2670				}
2671				$mov_msgball['acctnum'] = $acctnum;
2672				$mov_msgball['msgnum'] = $msg_num;
2673				// destination Trash Folder
2674				$to_fldball = array();
2675				$to_fldball['folder'] = $trash_folder_long;
2676				$to_fldball['acctnum'] = $acctnum;
2677				// this event MOVED to flush command
2678				//if ($this->debug_wrapper_dcom_calls > 1) { echo 'mail_msg(_wrappers): phpgw_delete('.__LINE__.'): calling $this->event_msg_move_or_delete<br />'; }
2679				//$this->event_msg_move_or_delete($mov_msgball, 'phpgw_delete'.' LINE: '.__LINE__, $to_fldball);
2680				//$this->expire_session_cache_item('msgball_list', $acctnum);
2681
2682				//if ($known_single_delete == True)
2683				//{
2684				//	// we were told this is just a SINGLE delete call, NOT multiple deletes involved
2685				//	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 />'; }
2686				//	$did_move = $this->single_interacct_mail_move($mov_msgball, $to_fldball);
2687				//}
2688				//else
2689				//{
2690					// most (WAS) likely multiple deletes, so use the command that buffers the moves
2691					// this logic concerning single or not has been moved elsewhere
2692					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 />'); }
2693					$did_move = $this->industrial_interacct_mail_move($mov_msgball, $to_fldball);
2694				//}
2695				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 />'); }
2696
2697				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 />'); }
2698				return $did_move;
2699			}
2700		}
2701
2702
2703		/*!
2704		@function get_verified_trash_folder_long
2705		@abstract SPECIAL HANDLER for use by "get_arg_value" for "verified_trash_folder_long"
2706		@param (int) the account number OPTIONAL if not supplied current acctnum is used
2707		@result (string) folder long name of trash folder, or empty string on failure
2708		@discussion First there is a pref called "trash_folder_name" that needs some processing
2709		because .1. the name is stored in user friendly form, is not known to be authentic server
2710		string folder long style, and .2. if the folder does not yet exist we must make it. This function
2711		does that processing, the result of this is put into arg value "verified_trash_folder_long". So
2712		if you need to know about the trash folder, use get_arg_value("verified_trash_folder_long") and
2713		this is all transparent to you. **CACHE NOTE** this value is stored in LEVEL 1 temporary cache
2714		for the duration of the script run, perhaps later we could put in appsession cache but if so then it
2715		must be expired if new prefs are submited.
2716		@author Angles
2717		*/
2718		function get_verified_trash_folder_long($acctnum='')
2719		{
2720			if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: ENTERING<br />'); }
2721
2722			if ((!isset($acctnum))
2723			|| ((string)$acctnum == ''))
2724			{
2725				$acctnum = $this->get_acctnum();
2726			}
2727			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 />'); }
2728
2729			// L1 (temporary) CACHED data available ?
2730			$class_cached_verified_trash_folder_long = $this->_direct_access_arg_value('verified_trash_folder_long', $acctnum);
2731			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 />'); }
2732			if ((isset($class_cached_verified_trash_folder_long))
2733			&& ($class_cached_verified_trash_folder_long != ''))
2734			{
2735				// return the cached data
2736				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 />'); }
2737				return $class_cached_verified_trash_folder_long;
2738			}
2739			// NO CACHED data, continue ...
2740
2741			// does the mailserver have folders, if not then there is NO trash folder no matter what
2742			if ($this->get_mailsvr_supports_folders($acctnum) == False)
2743			{
2744				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 />'); }
2745				// exit, trash folder pref is NOT TO USE ONE, so we certainly do not have a "verified" name in this case
2746				$this->set_arg_value('verified_trash_folder_long', '', $acctnum);
2747				return '';
2748			}
2749
2750			// are we even supposed to use a trash folder
2751			if ( (!$this->get_isset_pref('use_trash_folder', $acctnum))
2752			|| (!$this->get_pref_value('use_trash_folder', $acctnum)) )
2753			{
2754				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 />'); }
2755				// exit, trash folder pref is NOT TO USE ONE, so we certainly do not have a "verified" name in this case
2756				$this->set_arg_value('verified_trash_folder_long', '', $acctnum);
2757				return '';
2758			}
2759
2760			// does the trash folder actually exist ?
2761			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 />'); }
2762			$verified_trash_folder_long = $this->folder_lookup('', $this->get_pref_value('trash_folder_name', $acctnum));
2763			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 />'); }
2764			if ((isset($verified_trash_folder_long))
2765			&& ($verified_trash_folder_long != ''))
2766			{
2767				$havefolder = True;
2768			}
2769			else
2770			{
2771				$havefolder = False;
2772			}
2773
2774			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 />'); }
2775
2776			if (!$havefolder)
2777			{
2778				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 />'); }
2779				// create the Trash folder so it will exist (Netscape does this too)
2780				//$mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum);
2781				$namespace = $this->get_arg_value('mailsvr_namespace', $acctnum);
2782				$delimiter = $this->get_arg_value('mailsvr_delimiter', $acctnum);
2783				$make_this_folder_clean = $namespace.$delimiter.$this->get_pref_value('trash_folder_name', $acctnum);
2784				$make_this_folder_encoded = $this->prep_folder_out($make_this_folder_clean);
2785				//$this->createmailbox($mailsvr_stream,$server_str .$trash_folder_long);
2786				//$this->phpgw_createmailbox("$server_str"."$trash_folder_long");
2787				$fake_fldball = array();
2788				$fake_fldball['folder'] = $make_this_folder_encoded;
2789				$fake_fldball['acctnum'] = $acctnum;
2790				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 />'); }
2791				//$did_create = $this->phpgw_createmailbox($fake_fldball);
2792				$did_create = $this->phpgw_createmailbox_ex($fake_fldball);
2793				if (!$did_create)
2794				{
2795					$this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: UNABLE TO CREATE ['.htmlspecialchars(serialize($fake_fldball)).']');
2796					echo 'mail_msg(_wrappers)('.__LINE__.'): get_verified_trash_folder_long: UNABLE TO CREATE ['.htmlspecialchars(serialize($fake_fldball)).']';
2797				}
2798				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 />'); }
2799
2800				// try again to get the real long folder name of the just created trash folder
2801				$verified_trash_folder_long = $this->folder_lookup('', $this->get_pref_value('trash_folder_name', $acctnum));
2802				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 />'); }
2803				// did the folder get created and do we now have the official full name of that folder?
2804				if ($verified_trash_folder_long != '')
2805				{
2806					$havefolder = True;
2807				}
2808				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 />'); }
2809			}
2810
2811			if ($havefolder == False)
2812			{
2813				// FAILED to find or make trash folder, return empty string
2814				$verified_trash_folder_long = '';
2815			}
2816			else
2817			{
2818				// SUCCESS, put the result in L1 (page view only) cache
2819				// cache the result in "level one cache" class var holder
2820				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 />'); }
2821				$this->set_arg_value('verified_trash_folder_long', $verified_trash_folder_long, $acctnum);
2822				// LATER put it in appsession cache BUT make code to delete it from cache when submitting new prefs
2823			}
2824			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 />'); }
2825			return $verified_trash_folder_long;
2826		}
2827
2828		/**************************************************************************\
2829		* END DCOM WRAPERS								*
2830		* - - - - - - - - - - - - - - - - - - - - - - - - -					*
2831		* BEGIN INPUT ARG/PARAM HANDLERS			*
2832		\**************************************************************************/
2833
2834		/*!
2835		@function is_ball_data
2836		@abstract Quick test an array to see if has the elements a fldball needs to be considered a fldball,
2837		or a msgball has the minumum elements to be a msgball.
2838		@param $maybe_ball (array of type fldball or msgball) OR string of URI type representation of a fldball
2839		or msgabll array, depends on the next param. This data is what we test this to see if it is ball data.
2840		@param $expect_ball_type (known string) either "fldball" or "msgball" or "any" what we expect the
2841		inpout data is supposed to be. Test above param to see if it is either a fldball or a msgball as we
2842		specify here. Value of "any" means do not do strict test, just a general test meeting fldball
2843		minimum criteria, but it could also be a msgball and still produce a True result.
2844		@param $is_uri_type_string (boolean) default is False, set to true if the above param is not an
2845		array but is known to be in URI string form. To test accurately this param is needed.
2846		NOTE that the test on this param is empty is the same as False and not empty is the same as True.
2847		However empty does not mean "not set", in other words an empty string is still something that
2848		"is set", it does exist as a data item.
2849		@result Boolean True if param tests OK for being a fldball, False otherwise.
2850		@discussion Pass an ARRAY into here unless you pass the second param as True, then
2851		you must pass string URI representation of a fldball.
2852		BACKGROUND there are 2 ways we hold fldball (and msgball)
2853		data, they are similar and can convert between one another. First is the actual associative array that
2854		has the elements needed to make a fldball, at a minimum "acctnum" and "folder". Second is a
2855		URI type string that is the URI representation of that array, such that is passed in a GET uri
2856		which php would translate into an array. This function expects the array form. NOTE that
2857		the php parse_str command will always add ot strip slashes during this conversion, als GPC magic_quotes,
2858		no matter what the setting for magic quotes is set to. Remember this because that can change the
2859		folder name into something with additional slashes in some cases.
2860		@author Angles
2861		@access Public
2862		*/
2863		function is_ball_data($maybe_ball='##NOTHING##', $expect_ball_type='any', $is_uri_type_string='')
2864		{
2865			if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: is_ball_data('.__LINE__.'): ENTERING<br />'); }
2866			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 />'); }
2867			// sanity check on input data
2868			if ((!isset($maybe_ball))
2869			|| ($maybe_ball == $this->nothing)
2870			|| (!isset($expect_ball_type))
2871			|| (!isset($is_uri_type_string)))
2872			{
2873				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 />'); }
2874				return False;
2875			}
2876			elseif (($expect_ball_type != 'fldball')
2877			&& ($expect_ball_type != 'msgball')
2878			&& ($expect_ball_type != 'any'))
2879			{
2880				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 />'); }
2881				return False;
2882			}
2883			// baseline data
2884			$test_results = array();
2885			$test_results['verified_is_fldball'] = False;
2886			$test_results['verified_is_msgball'] = False;
2887			$test_results['final_verdict'] = False;
2888			// convert to array if necessary
2889			if ($is_uri_type_string)
2890			{
2891				$maybe_ball_uri = $maybe_ball;
2892				$maybe_ball = $this->ball_data_parse_str($maybe_ball_uri);
2893				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 />'); }
2894			}
2895			// easiest test, is it a fldball
2896			if ((isset($maybe_ball['folder']))
2897			&& ((string)$maybe_ball['folder'] != '')
2898			&& (isset($maybe_ball['acctnum']))
2899			&& ((string)$maybe_ball['acctnum'] != ''))
2900			{
2901				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 />'); }
2902				$test_results['verified_is_fldball'] = True;
2903			}
2904
2905			// additional test for msgball type ball data
2906			// only need to test this if the fldball criteria were met, if not it certainly can not be a msgball
2907			if (($test_results['verified_is_fldball'] == True)
2908			&& (isset($maybe_ball['msgnum']))
2909			&& ((string)$maybe_ball['msgnum'] != ''))
2910			{
2911				$test_results['verified_is_msgball'] = True;
2912				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 />'); }
2913			}
2914			//make a determination
2915			if (($expect_ball_type == 'any')
2916			&& ($test_results['verified_is_fldball'] == True))
2917			{
2918				// testing for "any" type of ball data is True if minimal criteria of fldball are met
2919				$test_results['final_verdict'] = True;
2920				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 />'); }
2921			}
2922			elseif (($expect_ball_type == 'fldball')
2923			&& ($test_results['verified_is_fldball'] == True)
2924			&& ($test_results['verified_is_msgball'] == False))
2925			{
2926				// strictly speaking, data is NOT a fldball if it ALSO has a msgnum, if using a strict match, not "any" ball
2927				// if the data ALSO HAS a msgnum, is is NOT a fldball, it is a msgball in the strict sence.
2928				$test_results['final_verdict'] = True;
2929				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 />'); }
2930			}
2931			elseif (($expect_ball_type == 'msgball')
2932			&& ($test_results['verified_is_fldball'] == True)
2933			&& ($test_results['verified_is_msgball'] == True))
2934			{
2935				$test_results['final_verdict'] = True;
2936				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 />'); }
2937			}
2938			else
2939			{
2940				$test_results['final_verdict'] = False;
2941				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 />'); }
2942			}
2943			// return the result
2944			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 />'); }
2945			return $test_results['final_verdict'];
2946		}
2947
2948		/*!
2949		@function ball_data_parse_str
2950		@abstract used for fldball and msgball data, apply php command parse string then apply stripslashes on folder names.
2951		@param $uri_ball_data (str) string in the style of a URI such as "msgball[msgnum]=2&msgball[folder]=INBOX&msgball[acctnum]=5"
2952		@param $do_stripslashes boolean default is True, whether to apply stripslashes to folder values, extremely rare to change this
2953		@result associative array of type msgball or fldball, which ever was fed as param  $uri_type_string
2954		@discussion php command parse_str will ALWAYS add slashes to single and double quotes, just like GPC magic quotes
2955		except that even if you disable GPC magic quotes, parse_str will STILL add that escape slash to any quotes. This means
2956		that folder names in URI type data that we wish to decode with parse_str will ALSO need to be stripslashed
2957		or else any folder names with quotes in the, which is legal as per RFC 2060, suh folder names will not match
2958		our known list of folder names because these slashes will have been added to the folder name, if it contains quotes.
2959		Therefor this function is designed to decode fldball and msgball data and also stripslash the folder names that
2960		this function can find. USE FOR FLDBALL AND MSGBALL DATA ONLY because this function only looks
2961		for folder names where they would exist in a fldball or magball array, IT WILL NOT stripslash foldernames
2962		of any other type of data because it does not look for the folder name anywhere else.
2963		@author Angles
2964		@access Public
2965		*/
2966		function ball_data_parse_str($uri_ball_data='', $do_stripslashes=True)
2967		{
2968			if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: ball_data_parse_str('.__LINE__.'): ENTERING<br />'); }
2969			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 />'); }
2970			$return_struct = array();
2971			if (!$uri_ball_data)
2972			{
2973				return $return_struct;
2974			}
2975			parse_str($uri_ball_data, $return_struct);
2976			// RAISE UP one level, we want the return to be the msgball or fldball itself
2977			if (stristr($uri_ball_data,'fldball[folder]'))
2978			{
2979				$return_struct = $return_struct['fldball'];
2980			}
2981			elseif (stristr($uri_ball_data,'msgball[folder]'))
2982			{
2983				$return_struct = $return_struct['msgball'];
2984			}
2985			else
2986			{
2987				if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: ball_data_parse_str('.__LINE__.'): UNHANDLED and unexpected if .. then data <br />'); }
2988			}
2989			// add to this data a "uri" element that is the $uri_ball_data
2990			//if (isset($return_struct['uri']) == False)
2991			//{
2992				//$return_struct['uri'] = $uri_ball_data;
2993			//}
2994			if ($do_stripslashes == True)
2995			{
2996				// this function is DUMB it only looks here for a folder name
2997				if (isset($return_struct['folder']))
2998				{
2999					$return_struct['folder'] = stripslashes($return_struct['folder']);
3000				}
3001			}
3002			// we always keep folder data in urlencoded form until the last second
3003			$return_struct['folder'] = $this->prep_folder_out($return_struct['folder']);
3004			//rebuild the uri string
3005			$return_struct['uri'] = 'msgball[msgnum]='.$return_struct['msgnum'].'&msgball[folder]='.$return_struct['folder'].'&msgball[acctnum]='.$return_struct['acctnum'];
3006			if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: ball_data_parse_str('.__LINE__.'): final $return_struct DUMP:', $return_struct); }
3007			if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: ball_data_parse_str('.__LINE__.'): LEAVING<br />'); }
3008			return $return_struct;
3009		}
3010
3011		/*!
3012		@function decode_fake_uri
3013		@abstract decodes a URI type "query string" into an associative array
3014		@param $uri_type_string string in the style of a URI such as "&item=phone&action=dial"
3015		@result associative array where the $key and $value are exploded from the uri like [item] => "phone"
3016		@discussion HTML select "combobox"s can only return 1 "value" per item, to break that limitation you
3017		can use that 1 item like a "fake URI", meaning you make a single string store structured data
3018		by using the standard syntax of a HTTP GET URI, see the example
3019		@example HTTP GET URI, example
3020		< select name="fake_uri_data" > < option value="&item=phone&action=dial&touchtone=1" > ( ... etc ... )
3021		repeat with html entities for the doc parser
3022		&lt; select name="fake_uri_data" &gt; &lt; option value="&item=phone&action=dial&touchtone=1" &gt; ( ... etc ... )
3023		In an HTTP POST event, this would appear as in the example
3024		$this->ref_POST["fake_uri_data"] => "&item=phone&action=dial&touchtone=1"
3025		Then you feed that string into this function and you get back an associave array like this
3026		return["item"] => "phone"
3027		return["action"] => "dial"
3028		return["touchtone"] => "1"
3029		NOTE: this differs from PHP's parse_str() because this function  will NOT attempt to decode the urlencoded values.
3030		In this way you may store many data elements in a single HTML "option" value=" " tag.
3031		@author Angles
3032		@access Public
3033		*/
3034		function decode_fake_uri($uri_type_string='', $raise_up=False)
3035		{
3036			/*
3037			$fake_url_b = explode('&', $uri_type_string);
3038			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>'; }
3039
3040			$fake_url_b_2 = array();
3041			while(list($key,$value) = each($fake_url_b))
3042			{
3043				$explode_me = trim($fake_url_b[$key]);
3044				if ((string)$explode_me != '')
3045				{
3046					$exploded_parts = explode('=', $explode_me);
3047					$fake_url_b_2[$exploded_parts[0]] = $exploded_parts[1];
3048				}
3049			}
3050			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>'; }
3051			return $fake_url_b_2;
3052			*/
3053
3054			$embeded_data = array();
3055			if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): ENTERED $uri_type_string ['.$uri_type_string.'] <br />'); }
3056			parse_str($uri_type_string, $embeded_data);
3057			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); }
3058
3059			// NOTE PARSE_STR ***WILL ADD SLASHES*** TO ESCAPE QUOTES
3060			// NO MATTER WHAT YOUR MAGIC SLASHES SETTING IS
3061			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***'); }
3062			if (isset($embeded_data['folder']))
3063			{
3064				$embeded_data['folder'] = stripslashes($embeded_data['folder']);
3065			}
3066			if (isset($embeded_data['msgball']['folder']))
3067			{
3068				$embeded_data['msgball']['folder'] = stripslashes($embeded_data['msgball']['folder']);
3069			}
3070
3071			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); }
3072
3073			// some embeded uri-faked data needs to be raised up one level from sub-elements to top level
3074			if ($raise_up)
3075			{
3076				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 />'); }
3077				$count_embeded = count($embeded_data);
3078				if ($count_embeded == 1)
3079				{
3080					@reset($embeded_data);
3081					$new_top_level = array();
3082					while(list($key,$value) = each($embeded_data))
3083					{
3084						$new_top_level = $embeded_data[$key];
3085						//break;
3086					}
3087					// re-urlencode folder names, and make acctnum 's integers
3088					/*
3089					// NOT NECESSARY HERE
3090					if ((is_array($new_top_level))
3091					&& (count($new_top_level) > 0))
3092					{
3093						$loops = count($new_top_level);
3094						for($i=0;$i<$loops;$i++)
3095						{
3096							// re-urlencode folder names, because "prep_folder_in" is supposed to be where it gets urldecoded
3097							if ((isset($this_array_item[$i]['folder']))
3098							&& ((string)$this_array_item[$i]['folder'] != ''))
3099							{
3100								$re_urlencoded_folder = urlencode($this_array_item[$i]['folder']);
3101								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 />'; }
3102								$this_array_item[$i]['folder'] = $re_urlencoded_folder;
3103							}
3104							if ((isset($this_array_item[$i]['acctnum']))
3105							&& ((string)$this_array_item[$i]['acctnum'] != ''))
3106							{
3107								$make_int_acctnum = (int)$this_array_item[$i]['acctnum'];
3108								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 />'; }
3109								$this_array_item[$i]['acctnum'] = $make_int_acctnum;
3110							}
3111						}
3112					}
3113					*/
3114					// replace result with $new_top_level
3115					$embeded_data = $new_top_level;
3116					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); }
3117				}
3118				else
3119				{
3120					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 />'); }
3121				}
3122			}
3123			// parse_str will "urldecode" the folder string, we need to re-urlencode it,
3124			// because "prep_folder_in" is supposed to be where it gets urldecoded
3125			while(list($key,$value) = each($embeded_data))
3126			{
3127				if ((strstr($key, 'folder'))
3128				&& ((string)$embeded_data[$key] != ''))
3129				{
3130					//$re_urlencoded_folder = urlencode($embeded_data[$key]);
3131					$re_urlencoded_folder = $this->prep_folder_out($embeded_data[$key]);
3132					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 />'); }
3133					$embeded_data[$key] = $re_urlencoded_folder;
3134				}
3135				elseif ((strstr($key, 'acctnum'))
3136				&& ((string)$embeded_data[$key] != ''))
3137				{
3138					$make_int_acctnum = (int)$embeded_data[$key];
3139					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 />'); }
3140					$embeded_data[$key] = $make_int_acctnum;
3141				}
3142			}
3143
3144			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); }
3145			if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: decode_fake_uri('.__LINE__.'): LEAVING <br />'); }
3146			return $embeded_data;
3147		}
3148
3149		/*!
3150		@function grab_class_args_gpc
3151		@abstract grab data from $this->ref_POST and $this->ref_GET, looking for var names
3152		listed in the $this->known_external_args[] array, and fill various class arg variables with the available data.
3153		@result none, this is an object call
3154		@discussion to further seperate the mail functionality from php itself, this function will perform
3155		the variable handling of the traditional php page view Get Post Cookie (no cookie data used here though)
3156		The same data could be grabbed from any source, XML-RPC for example, insttead of the php GPC vars,
3157		so this function could (should) have an equivalent XML-RPC version to handle filling these class variables
3158		from an alternative source. This function looks for all the var names listed in the
3159		$this->known_external_args[] array. Therefor, by adding something to that array, it will be looked
3160		for here. This is similar to the new phpgwapi "get_var" function, but does less syntax checking (validation), and
3161		has code to handle URI type data that could be embedded in certain GPC vars that we look for. Some validation
3162		is done here, but certainly more could be added using the "get_var" in the future.
3163		UPDATE re RH8 php+apache2 bug. UPDATE this function
3164		now handles 2 bugs associated with RH8 php with apache2. The FIRST bug is with POST data designed
3165		to be a simple array when POSTed and read by php, this is the un-numbered array, not associative, the
3166		type of array you get with a succession of "some_array[] = X+1" calls in php code, and with form data
3167		where the key could be "some_array[]" and the value might be depenant on a check box, for example. This
3168		particular type of array can have the buggy behavior of having TWO of every element. Typically, the array
3169		when dumped will show the desired array, but then also have that same array again starting at the next
3170		element after the real array is done. So really you get an array that is really 2 sets of itself all in one array.
3171		The SECOND bug is where certain POSTed "key=value" string can get erronously added an element
3172		of an array from the same form, but that is supposed to be a seperate peice of data. That erronously
3173		added item will have its real "key=value" pair in the real place it should be AND also tacked on to
3174		one of the POSTed array items, where these items are strings. There were a few places in this function
3175		that required cleaning the POSTed data of these 2 types of bugs.
3176		@author Angles
3177		@access Public
3178		*/
3179		function grab_class_args_gpc()
3180		{
3181			if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): ENTERING<br />'); }
3182			if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): $this->ref_POST DUMP:', $this->ref_POST); }
3183			if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): $this->ref_GET DUMP:', $this->ref_GET); }
3184
3185			// ----  extract any "fake_uri" embedded data from HTTP_POST_VARS  ----
3186			// note: this happens automatically for HTTP_GET_VARS
3187			// NOTE this WILL ALTER $_POST inserting processed values for later use (could this be avoided?)
3188			if (is_array($this->ref_POST))
3189			{
3190				while(list($key,$value) = each($this->ref_POST))
3191				{
3192					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 />'); }
3193					if ($key == 'delmov_list')
3194					{
3195						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 />'); }
3196						// apache2 on test RH8.0 box submits "delmov_list" array with duplicate items in it, track this
3197						$seen_delmov_list_items=array();
3198						$sub_loops = count($this->ref_POST[$key]);
3199						for($i=0;$i<$sub_loops;$i++)
3200						{
3201							// bug2: apache2 on test RH8.0 box submits "delmov_list" array with "what=delall" tacked on to one of the array items
3202							if (strstr($this->ref_POST[$key][$i], 'what=delall'))
3203							{
3204								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 />'); }
3205								$this->ref_POST[$key][$i] = str_replace('what=delall', '', $this->ref_POST[$key][$i]);
3206							}
3207
3208							// bug1: apache2: do duplicate test on the "delmov_list" array items
3209							if (in_array($this->ref_POST[$key][$i], $seen_delmov_list_items) == True)
3210							{
3211								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 />'); }
3212								$this->ref_POST[$key][$i] = '';
3213								// can I UNSET this and have the next $i index item actually be the next one
3214								// YES, a) array count calculated before loop, and b) does not squash array to unset an item
3215								unset($this->ref_POST[$key][$i]);
3216								//array_splice($this->ref_POST[$key], $i, 1);
3217								// NOTE USE OF CONTINUE COMMAND HERE!
3218								// we do not increase $ii because the next array item just fell into the current slot
3219								continue;
3220							}
3221							else
3222							{
3223								// track seen items for duplicate test
3224								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 />'); }
3225								$tmp_next_idx = count($seen_delmov_list_items);
3226								$seen_delmov_list_items[$tmp_next_idx] = $this->ref_POST[$key][$i];
3227							}
3228							// if we get here, it is not duplicate, go ahead
3229							$sub_embedded_data = array();
3230							// True = attempt to "raise up" embedded data to top level
3231							$sub_embedded_data = $this->decode_fake_uri($this->ref_POST[$key][$i], True);
3232							$this->ref_POST[$key][$i] = $sub_embedded_data;
3233						}
3234						// increment our shadow iteation count
3235						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]); }
3236					}
3237					elseif (strstr($key, '_fake_uri'))
3238					{
3239						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 />'); }
3240						$embedded_data = array();
3241						$embedded_data = $this->decode_fake_uri($this->ref_POST[$key]);
3242						// Strip "_fake_uri" from $key and insert the associative array into HTTP_POST_VARS
3243						$new_key = str_replace('_fake_uri', '', $key);
3244						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 />'); }
3245						$this->ref_POST[$new_key] = array();
3246						$this->ref_POST[$new_key] = $embedded_data;
3247						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]); }
3248					}
3249					/*
3250					elseif ($key == 'delmov_list')
3251					{
3252						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 />'; }
3253						$sub_loops = count($this->ref_POST[$key]);
3254						for($i=0;$i<$sub_loops;$i++)
3255						{
3256							$sub_embedded_data = array();
3257							$sub_embedded_data = $this->decode_fake_uri($this->ref_POST[$key][$i]);
3258							$this->ref_POST[$key][$i] = $sub_embedded_data;
3259						}
3260						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>'; }
3261					}
3262					*/
3263				}
3264			}
3265
3266			$got_args = array();
3267			// insert *known* external args we find into $got_args[], then return that data
3268			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 />'); }
3269			$loops = count($this->known_external_args);
3270			for($i=0;$i<$loops;$i++)
3271			{
3272				$this_arg_name = $this->known_external_args[$i];
3273				//if ($this->debug_args_input_flow > 2) { $this->dbug->out(' * * (grab pref - external) $this_arg_name: ['.$this_arg_name.']<br />'); }
3274				if (isset($this->ref_POST[$this_arg_name]))
3275				{
3276					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 />'); }
3277					$got_args[$this_arg_name] = $this->ref_POST[$this_arg_name];
3278				}
3279				elseif (isset($this->ref_GET[$this_arg_name]))
3280				{
3281					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 />'); }
3282					$got_args[$this_arg_name] = $this->ref_GET[$this_arg_name];
3283
3284					// STRIPSLASHES IF NEEDED
3285					if (($this_arg_name == 'msgball')
3286					|| ($this_arg_name == 'fldball'))
3287					{
3288						// php will automayically urldecode the folder, we don't like this
3289						// AND PHP MIGHT ADD GPC SLASHES WE DO NOT WANT!!!!! depending on your ini settings
3290						// use custom "stripslashes_gpc" function here because the slashes at this point would have been added ONLY by php itself
3291						// 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
3292						$not_urlencoded_but_stripslashed_folder = $this->stripslashes_gpc($got_args[$this_arg_name]['folder']);
3293						// now we have no unwanted slashes, so put this back in the input args
3294						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 />'); }
3295						$got_args[$this_arg_name]['folder'] = $not_urlencoded_but_stripslashed_folder;
3296						$not_urlencoded_but_stripslashed_folder = '';
3297						unset($not_urlencoded_but_stripslashed_folder);
3298					}
3299					// ADD "uri" element to incoming "msgball" arg
3300					// so forms may pass this "msgball" on to the next page view
3301					if ($this_arg_name == 'msgball')
3302					{
3303						// php will automayically urldecode the folder, we don't like this
3304						// we want the folder value to be URLENCODED, so we need to re-urlencode it
3305						$re_urlencoded_folder = $this->prep_folder_out($got_args[$this_arg_name]['folder']);
3306						$got_args[$this_arg_name]['folder'] = $re_urlencoded_folder;
3307						$got_args[$this_arg_name]['uri'] =
3308							'msgball[msgnum]='.$got_args[$this_arg_name]['msgnum']
3309							.'&msgball[folder]='.$got_args[$this_arg_name]['folder']
3310							.'&msgball[acctnum]='.$got_args[$this_arg_name]['acctnum'];
3311						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]); }
3312					}
3313				}
3314				else
3315				{
3316					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 />'); }
3317				}
3318			}
3319			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); }
3320
3321			// in order to know wgat account's arg array to insert $got_args[] into, we need to determine what account
3322			// we are dealing with before we can call $this->set_arg_array or "->get_isset_arg" or "->get_arg_value", etc...
3323			// so whoever called this function should obtain that before calling $this->set_arg_array() with the data we return here
3324			if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: grab_class_args_gpc('.__LINE__.'): LEAVING, returning $got_args<br />'); }
3325			return $got_args;
3326		}
3327
3328		/*!
3329		@function grab_class_args_xmlrpc
3330		@abstract grab data an XML-RPC call and fill various class arg variables with the available data
3331		@result none, this is an object call
3332		@discussion functional relative to function "grab_class_args_gpc()", except this function grabs the
3333		data from an alternative, non-php-GPC, source
3334		NOT YET IMPLEMENTED
3335		@author Angles
3336		@access Public
3337		*/
3338		function grab_class_args_xmlrpc()
3339		{
3340			// STUB, for future use
3341			echo 'call to un-implemented function grab_class_args_xmlrpc';
3342		}
3343
3344
3345		/*!
3346		@function get_best_acctnum
3347		@abstract search a variety of vars to find a legitimate account number, fallsback to $this->get_acctnum
3348		@param $args_array ARRAY that was passed to ->begin_request, pass that into here if possible, it is a primary source
3349		@param $got_args ARRAY of the *External* params (args) fed to this script via GPC or other methods.
3350		Note: these are NOT the "internal args".
3351		@param $force_feed_acctnum .DEPRECIATED. INTEGER if for some reason you want to force an account number. DEPRECIATED.
3352		@result integer, most legitimate account number that was obtained
3353		@discussion Run early in the script run to establish the most likely account that the rest
3354		of the script run will be concerned with. See this function itself, or turn on debugging output,
3355		to see what places this function looks to for the "best acctnum".
3356		It looks in a series of places, in order of importance to this function, each step of the way if no
3357		acctnum is found there, the function looks in the next place, and so on, until an acctnum is
3358		found or a fallback, usually "0" for the default email account, is used. See the function
3359		$this->get_acctnum for more info. If this function has not already found and stored a good
3360		acctnum value, it is most likely that $this->get_acctnum will return the "fallback_default_acctnum" usually "0".
3361		This "best acctnum" applies to the typical situations where you are viewing a list of
3362		messages in a particular account's folder. HOWEVER, if the list of
3363		messages 	is composed of messages from different accounts, such as is possible with testing filter rules,
3364		then there would be no "best acctnum" because no single account is being looked at. In any case
3365		each message item should be in the form of a "msgball" which carries with it the acctnum the
3366		message is associated with (amoung other things). Even so, the far more typical scenario is
3367		viewing a list of messages from the same account and folder. Also, when viewing any one messages
3368		contents, the "best acctnum" is obviously the account that message came from. When viewing
3369		folder contents, the folder we want to look at is passed in the form of a "fldball" which is an associative
3370		array containing "folder" and "acctnum", so this function would use that acctnum as the "best acctnum".
3371		Typically we are dealing with either "fldball" or "msgball" data which carry with them the
3372		acctnum they are associated with. Being able to determine the "best acctnum" that
3373		applies to a particular page view is helpful since this value will be stored and available
3374		using function $this->get_acctnum() which is easy to call from anyewhere.
3375		Before it became possible to operate on mesages from seperate folders and accounts in the same page,
3376		this "best acctnum" has more importance, but still it is most common not to have a list of messages
3377		from different sources, so this function is useful in a majority of the usages we will handle. Since
3378		this is true, many functions in this class, like "get_arg_value", take a param for "acctnum" that is
3379		OPTIONAL because if not provided any function can obtain an acctnum value by calling
3380		$this->get_acctnum() which returns the value which was found here.
3381		@author Angles
3382		@access Private
3383		*/
3384		function get_best_acctnum($args_array='', $got_args='', $force_feed_acctnum='')
3385		{
3386			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 />'); }
3387			if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): parm $args_array[] DUMP:', $args_array); }
3388			if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): parm $got_args[] DUMP:', $got_args); }
3389
3390			// ---  which email account do are these args intended to apply to  ----
3391			// ORDER OF PREFERENCE for determining account num: just look at the code, it has comments
3392			if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): "what acctnum to use": searching...: <br />'); }
3393			// initialize
3394			$acctnum = '';
3395
3396			if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): get acctnum from feed args if possible<br />'); }
3397			$found_acctnum = False;
3398			while(list($key,$value) = each($args_array))
3399			{
3400				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 />'); }
3401				// try to find feed acctnum value
3402				if ($key == 'fldball')
3403				{
3404					$fldball = $args_array[$key];
3405					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 />'); }
3406					$acctnum = (int)$fldball['acctnum'];
3407
3408					// SET OUR ACCTNUM ACCORDING TO FEED ARGS
3409					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 />'); }
3410					$found_acctnum = True;
3411					break;
3412				}
3413				elseif ($key == 'msgball')
3414				{
3415					$msgball = $args_array[$key];
3416					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 />'); }
3417					$acctnum = (int)$msgball['acctnum'];
3418					// SET OUR ACCTNUM ACCORDING TO FEED ARGS
3419					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 />'); }
3420					$found_acctnum = True;
3421					break;
3422				}
3423				elseif ($key == 'acctnum')
3424				{
3425					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 />'); }
3426					$acctnum = (int)$args_array[$key];
3427					// SET OUR ACCTNUM ACCORDING TO FEED ARGS
3428					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 />'); }
3429					$found_acctnum = True;
3430					break;
3431				}
3432			}
3433			// did the above work?
3434			if ($found_acctnum == True)
3435			{
3436				// SET THE ACCTNUM AND RETURN IT
3437				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 />'); }
3438				$this->set_acctnum($acctnum);
3439				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 />'); }
3440				return $acctnum;
3441			}
3442
3443			if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): "what acctnum to use": continue searching...: <br />'); }
3444
3445			// ok, now we need to broaden the search for a legit account number
3446			if ((isset($force_feed_acctnum))
3447			&& ((string)$force_feed_acctnum != ''))
3448			{
3449				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 />'); }
3450				$acctnum = (int)$force_feed_acctnum;
3451			}
3452			elseif ((isset($got_args['msgball']['acctnum']))
3453			&& ((string)$got_args['msgball']['acctnum'] != ''))
3454			{
3455				// we are requested to handle (display, move, forward, etc...) this msgball, use it's properties
3456				$acctnum = (int)$got_args['msgball']['acctnum'];
3457				// make sure this is an integer
3458				$got_args['msgball']['acctnum'] = $acctnum;
3459				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 />'); }
3460			}
3461			elseif ((isset($got_args['fldball']['acctnum']))
3462			&& ((string)$got_args['fldball']['acctnum'] != ''))
3463			{
3464				// we are requested to handle (display, .... ) data concerning this fldball, use it's properties
3465				$acctnum = (int)$got_args['fldball']['acctnum'];
3466				// make sure this is an integer
3467				$got_args['fldball']['acctnum'] = $acctnum;
3468				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 />'); }
3469			}
3470			elseif ((isset($got_args['source_fldball']['acctnum']))
3471			&& ((string)$got_args['source_fldball']['acctnum'] != ''))
3472			{
3473				// we are *probably* requested to delete or rename this fldball, use it's properties
3474				$acctnum = (int)$got_args['source_fldball']['acctnum'];
3475				// make sure this is an integer
3476				$got_args['source_fldball']['acctnum'] = $acctnum;
3477				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 />'); }
3478			}
3479			elseif ((isset($got_args['delmov_list'][0]['acctnum']))
3480			&& ((string)$got_args['delmov_list'][0]['acctnum'] != ''))
3481			{
3482				// at the very least we know that we'll need to login to this account to delete or move this particular msgball
3483				// also, we will need to open the particular folder where the msg is localted
3484				$acctnum = (int)$got_args['delmov_list'][0]['acctnum'];
3485				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 />'); }
3486			}
3487			elseif ((isset($got_args['target_fldball']['acctnum']))
3488			&& ((string)$got_args['target_fldball']['acctnum'] != ''))
3489			{
3490				// at the very least we know we need to login to this account to append a message to a folder there
3491				// NOTE: we need not open the particular folder we are going to append to,
3492				// all we need is a stream to that particular account, "opened" folder is not important
3493				// therefor we can just use INBOX as the folder to log into in this case
3494				$acctnum = (int)$got_args['target_fldball']['acctnum'];
3495				// make sure this is an integer
3496				$got_args['target_fldball']['acctnum'] = $acctnum;
3497				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 />'); }
3498			}
3499			else
3500			{
3501				// FALLBACK
3502				// ok, we have NO acctnum in $args_array, did NOT get it from GPC got_args, nor the force fed $force_feed_acctnum
3503				// so, we grab the class's current value for $this->acctnum
3504				// $this->get_acctnum() will return a default value for us to use if $this->acctnum is not set
3505				// note, this is identical to $this->get_acctnum(True) because True is the default arg there if one is not passed
3506				// True means "return a default value, NOT boolean false, if $this->acctnum is not set
3507				$acctnum = $this->get_acctnum(True);
3508				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 />'); }
3509			}
3510
3511			// SET THE ACCTNUM WITH THE "BEST VALUE" WE COULD FIND
3512			// DEPRECIATED - we no longer set it here
3513			//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 />'); }
3514			//$this->set_acctnum($acctnum);
3515
3516			if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: get_best_acctnum('.__LINE__.'): LEAVING, returning $acctnum ['.serialize($acctnum).']<br />'); }
3517			return $acctnum;
3518		}
3519
3520		/*!
3521		@function init_internal_args_and_set_them
3522		@abstract initialize Internally controlled params (args). MUST already have an acctnum
3523		@param $acctnum integer the current account number whose array we will fill with these initialized args
3524		@result none, this is an object call
3525		@discussion ALMOST DEPRECIATED.
3526		NOTE that the most IMPORTANT thing this does now is set interal arg
3527		"already_grab_class_args_gpc" to True, which serves as an ad-hoc flag indicating this class
3528		has already been through its typical initialization procedures. But that was not the
3529		intended purpose here, it just happened that way, partly for the simple reason that this
3530		function is called after the "grab_external_args" function. This function had more meaning
3531		once, back when a now depreciated and gone function used it. Now this is mostly an
3532		outdated, not needed, not really used function. Even so, it turns out that keeping an
3533		array called $this->known_internal_args[] is helpful from a development standpoint, just
3534		to help the coder remember what args this class is passing around to itself. These internal args are
3535		different from the "external" args because these internal args are NEVER filled from external sources,
3536		such as GET and POST or possible XML-RPC sources, which are considered external sources of data.
3537		A side effect is that this function will actually set all the args listed in that "known_internal_args"
3538		array, which is bad because you can not use the set vs. not set comparison if they are set always.
3539		For that reason there are currently some internal args not listed in the "known_internal_args"
3540		array. This probably will be cleaned up in the future.
3541		@author Angles
3542		@access Public
3543		*/
3544		function init_internal_args_and_set_them($acctnum='')
3545		{
3546			if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: init_internal_args: ENTERING, (parm $acctnum=['.serialize($acctnum).'])<br />'); }
3547			// we SHOULD have already obtained a valid acctnum before calling this function
3548			if (!(isset($acctnum))
3549			|| ((string)$acctnum == ''))
3550			{
3551				$acctnum = $this->get_acctnum();
3552			}
3553
3554			// INTERNALLY CONTROLLED ARGS
3555			// preserve pre-existing value, for which "acctnum" must be already obtained, so we
3556			// know what account to check for existing arg values when we use "get_isset_arg" or "get_arg_value"
3557			$internal_args = Array();
3558			if ($this->debug_args_input_flow > 1) { $this->dbug->out('mail_msg: init_internal_args: about to loop thru $this->known_internal_args<br />'); }
3559			$loops = count($this->known_internal_args);
3560			for($i=0;$i<$loops;$i++)
3561			{
3562				$this_arg_name = $this->known_internal_args[$i];
3563				//if ($this->debug_args_input_flow > 2) { $this->dbug->out(' * * (grab pref - internal) $this_arg_name: '.$this_arg_name.'<br />'); }
3564				// see if there is a value we can preserve for this arg
3565				if ($this->get_isset_arg($this_arg_name))
3566				{
3567					$preserve_this = $this->get_arg_value($this_arg_name);
3568					if ($this->debug_args_input_flow> 2) { $this->dbug->out(' * * (grab pref - internal) preserving internal pre-existing arg: ['.$this_arg_name.'] = ['.$preserve_this.']<br />'); }
3569					$internal_args[$this_arg_name] = $preserve_this;
3570				}
3571				else
3572				{
3573					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 />'); }
3574					if ($this_arg_name == 'folder_status_info')
3575					{
3576						$internal_args['folder_status_info'] = array();
3577					}
3578					elseif ($this_arg_name == 'folder_list')
3579					{
3580						$internal_args['folder_list'] = array();
3581					}
3582					elseif ($this_arg_name == 'mailsvr_callstr')
3583					{
3584						$internal_args['mailsvr_callstr'] = '';
3585					}
3586					elseif ($this_arg_name == 'mailsvr_namespace')
3587					{
3588						$internal_args['mailsvr_namespace'] = '';
3589					}
3590					elseif ($this_arg_name == 'mailsvr_delimiter')
3591					{
3592						$internal_args['mailsvr_delimiter'] = '';
3593					}
3594					elseif ($this_arg_name == 'mailsvr_stream')
3595					{
3596						$internal_args['mailsvr_stream'] = '';
3597					}
3598					elseif ($this_arg_name == 'mailsvr_account_username')
3599					{
3600						$internal_args['mailsvr_account_username'] = '';
3601					}
3602					// experimental (by it being *here*): this arg is handles elsewhere, but Iput it here
3603					// to help remember and be consistant about accounting for all args we may use
3604					// UPDATE: "expunge_folders" can NOT BE HERE because it should NOT EXIST unless set during a move or delete
3605					//  putting it here will initialize it to a value of "" (empty string) which is different than unset.
3606					//elseif ($this_arg_name == 'expunge_folders')
3607					//{
3608					//	$internal_args['expunge_folders'] = '';
3609					//}
3610					// experimental: Set Flag indicative we've run thru this function
3611					elseif ($this_arg_name == 'already_grab_class_args_gpc')
3612					{
3613						$internal_args['already_grab_class_args_gpc'] = True;
3614					}
3615				}
3616			}
3617			if ($this->debug_args_input_flow > 2) { $this->dbug->out('mail_msg: init_internal_args: post-loop (internal args) $internal_args[] DUMP:', $internal_args); }
3618
3619
3620			// clear old args (if any) and set the args we just obtained (or preserved)
3621			//$this->unset_all_args();
3622			// set new args, some may require processing (like folder will go thru prep_folder_in() automatically
3623			//while(list($key,$value) = each($internal_args))
3624			//{
3625			//	$this->set_arg_value($key, $internal_args[$key]);
3626			//}
3627
3628			// use this one call to do it all
3629			//$this->set_arg_array($internal_args);
3630
3631			// add these items to the args array for the appropriate account
3632			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 />'); }
3633			while(list($key,$value) = each($internal_args))
3634			{
3635				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 />'); }
3636				$this->set_arg_value($key, $internal_args[$key], $acctnum);
3637				//$this->set_arg_value($key, $internal_args[$key]);
3638			}
3639
3640			if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: grab_class_args_gpc: LEAVING, returning $internal_args<br />'); }
3641			return $internal_args;
3642		}
3643
3644		/*!
3645		@function get_best_folder_arg
3646		@abstract search a variety of vars to find a legitimate folder value to open on the mail server number,
3647		@param $args_array ARRAY that was passed to ->begin_request, pass that into here if possible, it is a primary source
3648		@param $got_args ARRAY of the *External* params (args) fed to this script via GPC or other methods
3649		Note these are NOT the "internal args".
3650		@param $acctnum INTEGER used to querey various already-set args
3651		@result string, most legitimate folder value that was obtained
3652		@discussion The return folder string MUST *NOT* BE URLENCODED.
3653		It is an artifact of this apps development that this arg exists and is not urlencoded.
3654		Once upon a time, any one page view could be associated with ONLY one folder at a time.
3655		This is no longer a limitation, but a VAST majority of the time it is true that
3656		we are only concerned with one particular folder for a page view. A similar discussion is in the
3657		comments for function "get_best_acctnum". As it happens, now this app uses things called
3658		"fldball" and "msgball" because the advent of multiple accounts, and the ability to handle
3659		messages from any folder from any account at any time, required that a description of a
3660		folder or message carry with it enough info to bunch all those possibilities into one structure.
3661		We call these "fldball" and "msgball", at the least they contain elements "folder" and "acctnum",
3662		and "msgball" will additionally contain more data such as "msgnum". But I digress. In the past
3663		it was only necessary to know the one folder we cared about for the script run, and mostly
3664		this is still applicable. This function looks in a variety of places for that, see the function itself for
3665		details, including looking for certain fldball or msgball args. SECONDLY it is an artifact
3666		of this apps development that this arg value is NOT urlencoded. This is now unusual because
3667		the fldball and msgball data ALWAYS keep their "folder" data URLENCODED. This STAYS
3668		urlencoded up until it absolutely needs to be decoded, such as when sending a command to the
3669		IMAP server. So it is a notable exception that this arg is not urlencoded. But that is because it
3670		never was ever urlencoded "in the old days" so functions that look for the simple arg "folder"
3671		expected a non-urlencoded value. DIGRESSION - the urlencoding of the folder names makes it
3672		easier to cache the names in a database, which can be sensitive to some chars if not encoded.
3673		And if you know a fldball always has urlencoded folder value then you can more
3674		accurately compare two different fldballs more quickly. If one were urlencoded and the other
3675		not, then identical folder names would not match in a string comparison. So SQL calls
3676		expected a urlencoded folder name. If not for this expectation, we may not pull the
3677		desired data from the database, and perhaps leave unneeded data there because of this.
3678		MORE DIGRESSION - fldball and msgball data is in URI syntax when in string form,
3679		and by using function "ball_data_parse_str" that string can be turned into an associative
3680		array. Note that php function parse_str is used for this BUT see that function for more
3681		discussion on that topic, the result is that we keep the folder name urlencoded in that array.
3682		Usually we make the transformation into an array for ease of use inside a function,
3683		but the string URI syntax takes up less memory when dealing with huge message lists, and
3684		takes up less space cached in a database as compared to a serialized array with the same data.
3685		@author Angles
3686		@access Private
3687		*/
3688		function get_best_folder_arg($args_array='', $got_args='', $acctnum='')
3689		{
3690			if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: get_best_folder_arg: ENTERING <br />'); }
3691			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); }
3692			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); }
3693			// initialize
3694			$processed_folder_arg = '';
3695			// we SHOULD have already obtained a valid acctnum before calling this function
3696			if (!(isset($acctnum))
3697			|| ((string)$acctnum == ''))
3698			{
3699				$acctnum = $this->get_acctnum();
3700			}
3701			//  ----  Get Folder Value  ----
3702			// ORDER OF PREFERENCE for pre-processed "folder" input arg
3703			// (1) $args_array, IF FILLED, overrides any previous data or any other data source, look for these:
3704			//	$args_array['folder']
3705			// (2) GPC ['msgball']['folder']
3706			// (3) GPC ['fldball']['folder']
3707			// (4) GPC ['delmov_list'][0]['folder']
3708			// (5) if "folder" arg it is already set, (probably during the reuse attempt, probably obtained from $args_array alreadt) then use that
3709			// (6) default to blank string, which "prep_folder_in()" changes to defaultg value INBOX
3710
3711			// note: it's OK to send blank string to "prep_folder_in", because it will return a default value of "INBOX"
3712			if ((isset($args_array['folder']))
3713			&& ($args_array['folder'] != ''))
3714			{
3715				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 />'); }
3716				$input_folder_arg = $args_array['folder'];
3717			}
3718			elseif ($this->get_isset_arg('["msgball"]["folder"]'))
3719			{
3720				$input_folder_arg = $this->get_arg_value('["msgball"]["folder"]');
3721				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 />'); }
3722				//VERIFY
3723				$processed_folder_arg = $this->prep_folder_in($input_folder_arg, $this->get_arg_value('["msgball"]["acctnum"]'));
3724				// when putting back into the ball data we need to urlencode it because folder element in ball data stays urlencoded until the last monent
3725				$processed_folder_arg = urlencode($processed_folder_arg);
3726				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 />'); }
3727				// both these should be urlencoded so this is apples to apples comparison
3728				if ($processed_folder_arg != $input_folder_arg)
3729				{
3730					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 />'); }
3731					$this->set_arg_value('["msgball"]["folder"]', $processed_folder_arg);
3732				}
3733				// now that is done, urldecode because a legacy quirk requires a urldecded return from this function
3734				$processed_folder_arg = urldecode($processed_folder_arg);
3735			}
3736			elseif ($this->get_isset_arg('["fldball"]["folder"]'))
3737			{
3738				$input_folder_arg = $this->get_arg_value('["fldball"]["folder"]');
3739				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 />'); }
3740				//VERIFY
3741				$processed_folder_arg = $this->prep_folder_in($input_folder_arg, $this->get_arg_value('["fldball"]["acctnum"]'));
3742				// when putting back into the ball data we need to urlencode it because folder element in ball data stays urlencoded until the last monent
3743				$processed_folder_arg = urlencode($processed_folder_arg);
3744				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 />'); }
3745				// both these should be urlencoded so this is apples to apples comparison
3746				if ($processed_folder_arg != $input_folder_arg)
3747				{
3748					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 />'); }
3749					$this->set_arg_value('["fldball"]["folder"]', $processed_folder_arg);
3750				}
3751				// now that is done, urldecode because a legacy quirk requires a urldecded return from this function
3752				$processed_folder_arg = urldecode($processed_folder_arg);
3753			}
3754			elseif ($this->get_isset_arg('delmov_list'))
3755			{
3756				// we know we'll need to loginto this folder to get this message and move/delete it
3757				// 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
3758				$this_delmov_list = $this->get_arg_value('delmov_list');
3759				$input_folder_arg = $this_delmov_list[0]['folder'];
3760				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 />'); }
3761			}
3762			else
3763			{
3764				if (($this->get_isset_arg('folder'))
3765				&& ((string)trim($this->get_arg_value('folder')) != ''))
3766				{
3767					$input_folder_arg = $this->get_arg_value('folder');
3768				}
3769				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 />'); }
3770
3771				$input_folder_arg = (string)$input_folder_arg;
3772				$input_folder_arg = trim($input_folder_arg);
3773				if ($input_folder_arg != '')
3774				{
3775					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 />'); }
3776				}
3777				else
3778				{
3779					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 />'); }
3780					$input_folder_arg = '';
3781				}
3782			}
3783			// ---- Prep the Folder Name (remove encodings, verify it's long name (with namespace)
3784			// folder prepping does a lookup which requires a folder list which *usually* (unless caching) requires a login
3785			if ($processed_folder_arg != '')
3786			{
3787				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 />'); }
3788			}
3789			else
3790			{
3791				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 />'); }
3792				$processed_folder_arg = $this->prep_folder_in($input_folder_arg);
3793			}
3794			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 />'); }
3795			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 />'); }
3796			return $processed_folder_arg;
3797		}
3798
3799
3800		/**************************************************************************\
3801		* END INPUT ARG/PARAM HANDLERS								*
3802		* - - - - - - - - - - - - - - - - - - - - - - - - -									*
3803		* BEGIN APPSESSION TEMPORARY CACHING HANDLERS		*
3804		\**************************************************************************/
3805
3806		// ACTUAL DATA ACCESS FUNCTIONS MOVED TO SO CLASS
3807		// HIGH LEVEL CACHING STUFF KEPT HERE
3808		/*!
3809		@function save_session_cache_item BEING OVERHAULED preliminary complete.
3810		@abstract TEMPORARY DATA CACHING server-side in the phpgw appsession cache.
3811		@param $data_name (string)
3812		@param $data (mixed) usually an array
3813		@param $acctnum (int)
3814		@param $extra_keys (string) optional info used to make the appsession "location" string for data that is
3815		part of a larger group of elements, this param will let us refer to this single element without using sub arrays
3816		@author Angles
3817		@discussion Server-side caching of limited, ephermal data, such as a list of messages from
3818		an imap search, saved to phpgw appsession. All appsession data gets deleted when the user logs out, which is
3819		why this is a temporary cache. NOTE: to cache an item you must add it to list of data items that
3820		has a handler here, otherwise we skip it. Handler need may be lifted in the future, but for now anything
3821		stored in the appsession cache requires a handler here because different types of data arrive here in different
3822		forms, and must be stored using that extra information to know where to put the data in the appsession
3823		cache array of data. For example, *some* cached items get meta_data saved with it, right now only the
3824		"msgball_list" is saved with other, related data. In the case of the "msgball_list", $meta_data["msgball_list"]
3825		and $meta_data["validity"]  is what we should get fed into this function, where that meta_data "validity" data
3826		is used to verify if the cached "msgball_list" is valid, not-stale data, when restoring a "msgball_list" from the
3827		cache. Also. data is saved in the appsession data array starting at element
3828		"[_SESSION][phpgw_session][phpgw_app_sessions][email]" and from that we add the single element
3829		"dat" and from that all the information we cache is attached to that element of the array. All data REQUIRES
3830		an acctnum, as an integer, because that is the first thing we use to determine a location in the appsession
3831		array for the data. Then we use the data_name, the combination of [acctnum][data_name]  is the least
3832		amount of info we need to put the more simple data in the appsession array, such as the cached "mailsvr_callstr".
3833		It would be saved here "[_SESSION][phpgw_session][phpgw_app_sessions][email][dat][$acctnum][mailsvr_callstr]"
3834		as the key and the actual mailsvr_callstr data is the value. The next step up in data requirement is the addition
3835		of the folder name to the array, such as for "folder_status_info" data, is gets saved at this key
3836		"[_SESSION][phpgw_session][phpgw_app_sessions][email][dat][$acctnum][folder_status_info][folder]" as its key.
3837		Them the most info we need to store cached data is for, currently, the "msg_structure" and "phpgw_header"
3838		which reauire the additional information that is the msgnum they apply to, so they are saved with this
3839		as their key, as in the case of "msg_structure",
3840		"[_SESSION][phpgw_session][phpgw_app_sessions][email][dat][$acctnum][msg_structure][folder][msgnum]".
3841		This allows us to directly access this appsession array if we want, and to quickly access or expire individual
3842		parts of the data without having to loop through a lot of array to find what we want. SUBJECT TO CHANGE.
3843		@example This is not really an example but it does explain where this data is kept in memory.
3844		[HTTP_SESSION_VARS][phpgw_session][phpgw_app_sessions][email]
3845		[_SESSION][phpgw_session][phpgw_app_sessions][email]
3846		This is also accessable thru this
3847		GLOBALS[phpgw_session][phpgw_app_sessions][email]
3848		@access private
3849		*/
3850		function save_session_cache_item($data_name='misc',$data,$acctnum='',$extra_keys='')
3851		{
3852			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 />'); }
3853			$has_handler = False;
3854
3855			if ((!isset($acctnum))
3856			|| ((string)$acctnum == ''))
3857			{
3858				$acctnum = $this->get_acctnum();
3859			}
3860
3861			if ($this->session_cache_enabled == False)
3862			{
3863				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 />'); }
3864				// LEAVING
3865				return False;
3866			}
3867			// IF EMPTY DATA - LEAVE
3868			if (!$data)
3869			{
3870				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 />'); }
3871				// LEAVING
3872				return False;
3873			}
3874
3875			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 />'); }
3876
3877			// 1st -- session_cache_enabled stuff
3878			// this stuff is cached REGARDLESS of extreme mode, extreme mode is *extra* caching in addition to the "session_cache_enabled" stuff
3879			// process the data according to what it is
3880			if ($data_name == 'msgball_list')
3881			{
3882				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 />'); }
3883
3884				// we damn well better have a msgball_list or else we have nothing to save
3885				if ((isset($data['msgball_list']) == False)
3886				|| (!$data['msgball_list']))
3887				{
3888					$this->dbug->out('mail_msg: save_session_cache_item: LEAVING on ERROR, FIXME line '.__LINE__.' we have no msgball_list<br />');
3889					echo 'mail_msg: save_session_cache_item: LEAVING on ERROR, FIXME line '.__LINE__.' we have no msgball_list<br />';
3890					// LEAVING ERROR
3891					return False;
3892				}
3893
3894				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 />'); }
3895
3896				// NOTE 1: if you provide this 1 thing:
3897				//	data['msgball_list']
3898				// 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)
3899
3900				// NOTE 2: if you provide these 2 things
3901				//	data['msgball_list']
3902				//	data['validity']['fldball']
3903				// the "folder_status_info" will be filled in for you SPECIFICALLY for that fldball EVEN IF it is not the current folder or account
3904
3905				// NOTE 3: if you provide these 2 things
3906				//	data['msgball_list']
3907				//	data['validity']['folder_status_info']
3908				// the "fldball" will be filled from your "folder_status_info" which has a fldball data in it
3909				// a FOLDER_STATUS_INFO fldball PREVAILS, it overrules data['validity']['fldball']
3910
3911				// NOTE 4: if you provide these 3 things
3912				//	data['msgball_list']
3913				//	data['validity']['fldball']
3914				//	data['validity']['folder_status_info']
3915				// then you have provided EVERYTHING we need
3916
3917				// NOTE 5: NOW we also use folder as a data key, so each folder has its own msgball_list
3918				// so later we will use this folder data for what is called $extra_keys, but it is just the folder name here
3919				// update to make it easier to understand I call this $folder_name now
3920
3921				// easy way is "note 1", at this point providing the other stuff is not needed for normal operation
3922				// perhaps if you already have the other stuff and you know it has not changed ("folder_status_info" still good)
3923				// then *maybe* it might save some time passing "folder_status_info" you already have
3924				// if you have many accounts open and the "current account" is hard to discerrn, pass a fldball make sure everything will match
3925
3926				// we need an acctnum, either
3927				//  (a) from the fldball, or
3928				//  (b) from the fldball in the "folder_status_info", or
3929				// (c) what was passed as the acctnum (which itself is optional, then it will be the "current acctnum"
3930
3931				// we use folder_info for validity testing of data "stale" or not when we retrieve the cached data later
3932				// either data has INCLUDED with it a fldball or we make one based on current account info
3933				if ((isset($data['validity']['folder_status_info']['fldball']['folder']) == True)
3934				&& ((string)$data['validity']['folder_status_info']['fldball']['folder'] != '')
3935				&& ((string)$data['validity']['folder_status_info']['fldball']['acctnum'] != ''))
3936				{
3937					// folder_status_info IF PROVIDED has PRIORITY of fldball and therefor PRIORITY of acctnum
3938					$acctnum = $data['validity']['folder_status_info']['fldball']['acctnum'];
3939					// make a fldball, ADD IT TO DATA
3940					$data['validity']['fldball'] = array();
3941					$data['validity']['fldball']['acctnum'] = $acctnum;
3942					$data['validity']['fldball']['folder'] = $data['validity']['folder_status_info']['fldball']['folder'];
3943				}
3944				elseif ((isset($data['validity']['fldball']['folder']) == True)
3945				&& ((string)$data['validity']['fldball']['folder'] != '')
3946				&& ((string)$data['validity']['fldball']['acctnum'] != ''))
3947				{
3948					// the next place to look for acctnum is here
3949					$acctnum = $data['validity']['fldball']['acctnum'];
3950					// in this case WE CAN NOT RELY on folder_status_info if provided because it is not complete
3951					if (isset($data['validity']['folder_status_info']) == True)
3952					{
3953						$data['validity']['folder_status_info'] == array();
3954						unset($data['validity']['folder_status_info']);
3955					}
3956				}
3957				else
3958				{
3959					// fallback: using genericly obtained acctnum, make a fldball and add it to DATA
3960					$data['validity']['fldball'] = array();
3961					$data['validity']['fldball']['acctnum'] = $acctnum;
3962					$data['validity']['fldball']['folder'] = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
3963					// just in case incomplete "folder_status_info" was passed, unset it
3964					if (isset($data['validity']['folder_status_info']) == True)
3965					{
3966						$data['validity']['folder_status_info'] == array();
3967						unset($data['validity']['folder_status_info']);
3968					}
3969				}
3970
3971				// this is really for backwards compat right now
3972				if (isset($data['validity']['folder_long']) == False)
3973				{
3974					$data['validity']['folder_long'] = $data['validity']['fldball']['folder'];
3975				}
3976
3977				// 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
3978				// $extra_keys if provided is enabling new stuff, it was not used before for msgball_list data
3979				// IF it is provided (not yet used) it will probably be a folder name in urlencoded form, but NOT SURE YET
3980				// we know what to do here, so this data "has a handler"
3981				$has_handler = True;
3982
3983				// either data has INCLUDED with it "folder_status_info" or we obtain it based on that fldball
3984				if ((isset($data['validity']['folder_status_info']) == False)
3985				|| (!$data['validity']['folder_status_info']))
3986				{
3987					// obtain folder stats, ADD IT TO DATA
3988					$data['validity']['folder_status_info'] = array();
3989					$data['validity']['folder_status_info'] = $this->get_folder_status_info($data['validity']['fldball']);
3990				}
3991				// ADDITIONAL info that we need for validity testing when we later want to read this info
3992				// ADD IT IF NOT PROVIDED
3993				if (isset($data['validity']['sort']) == False)
3994				{
3995					$data['validity']['sort'] = $this->get_arg_value('sort', $data['validity']['fldball']['acctnum']);
3996				}
3997				if (isset($data['validity']['order']) == False)
3998				{
3999					$data['validity']['order'] = $this->get_arg_value('order', $data['validity']['fldball']['acctnum']);
4000				}
4001				if ((isset($data['validity']['mailsvr_callstr']) == False)
4002				|| (isset($data['validity']['mailsvr_account_username']) == False))
4003				{
4004					// these two things are REALLY linked together, the better be obtained as a pair
4005					$data['validity']['mailsvr_callstr'] = $this->get_arg_value('mailsvr_callstr', $data['validity']['fldball']['acctnum']);
4006					$data['validity']['mailsvr_account_username'] = $this->get_arg_value('mailsvr_account_username', $data['validity']['fldball']['acctnum']);
4007				}
4008
4009				// only is admin is using DB as session store, if "php4" session is used instead, this is not necessary
4010				//if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4011				//&& (strstr($data['validity']['mailsvr_callstr'] ,'{')) )
4012				//{
4013				//	// NOTE: YOU BETTER BASE64 ENCODE "DEFANG" the "mailsvr_callstr" OR RISK b0rking your database!!!
4014				//	// applies to DBs where ' " / \ and other offensive chars are not handled at the api level in phpgw
4015				//	$data['validity']['mailsvr_callstr'] = base64_encode($data['validity']['mailsvr_callstr']);
4016				//	// COMMENT THAT OUT if you can prove that DBs are immune to this b0rking
4017				//}
4018
4019				// we have ALL the data we need now
4020				// RECAST DATA ready for saving in appsession
4021				//$data = serialize($data);
4022
4023				// NOW WE USE FOLDER INFO TOO SO EVERY FOLDER GETS ITS OWN MSGBALL_LIST
4024				$folder_name = $data['validity']['fldball']['folder'];
4025				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); }
4026				// SET DATA
4027				//$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name] = $data;
4028				// SET DATA USING FOLDER ALSO
4029				$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$folder_name] = $data;
4030
4031				// for DB sessions_db ONLY
4032				if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4033				|| ($this->use_private_table == True))
4034				{
4035					//$my_location = (string)$acctnum.';'.$data_name;
4036					// SET DATA USING FOLDER ALSO
4037					$my_location = (string)$acctnum.';'.$data_name.';'.$folder_name;
4038					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 />'); }
4039					if ($this->use_private_table == True)
4040					{
4041						//$this->so->so_set_data($my_location, $data);
4042						// TRY USING COMPRESSION
4043						$this->so->so_set_data($my_location, $data, True);
4044					}
4045					else
4046					{
4047						// NOTE compression not available for appsession table
4048						$GLOBALS['phpgw']->session->appsession($my_location, 'email', $data);
4049					}
4050				}
4051
4052				$data = array();
4053				unset($data);
4054				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 />'); }
4055				return True;
4056			}
4057			// DEFANG special handler for data that might b0rk a database, stuff like ' " / \ and maybe some others, only "mailsvr_callstr" needs this right now
4058			elseif ($data_name == 'mailsvr_callstr_OLD_HANDLER')
4059			//elseif ($data_name == 'mailsvr_callstr')
4060			{
4061				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 />'); }
4062				// we know what to do here, so this data "has a handler"
4063				$has_handler = True;
4064				//$location = 'acctnum='.(string)$acctnum.';data_name='.$data_name.$extra_keys;
4065				//$app = 'email';
4066				if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): "'.$data_name.'" before encoding: '.serialize($data).'<br />'); }
4067				// DATABASE DEFANG, this item has "database unfriendly" chars in it so we encode it before it goes to appsession cache
4068				$data = base64_encode($data);
4069				if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): "'.$data_name.'" AFTER encoding: '.serialize($data).'<br />'); }
4070
4071				if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.'] DUMP:', $data); }
4072				// SET DATA
4073				$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name] = $data;
4074				$data = array();
4075				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 />'); }
4076				unset($data);
4077				return True;
4078			}
4079			// ANYTHING that is non extreme-mode cachable that requires NO special treatment, add it to this block here
4080			elseif (($data_name == 'mailsvr_namespace')
4081			|| ($data_name == 'folder_list')
4082			|| ($data_name == 'mailsvr_callstr'))
4083			{
4084				// 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
4085				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 />'); }
4086				// we know what to do here, so this data "has a handler"
4087				$has_handler = True;
4088				//$location = 'acctnum='.(string)$acctnum.';data_name='.$data_name.$extra_keys;
4089				//$app = 'email';
4090				// NO CHANGE TO INPUT DATA NECESSARY because the data is in ready to save form
4091
4092				if ($this->debug_session_caching > 2) { $this->dbug->out('mail_msg: save_session_cache_item('.__LINE__.'): [email][dat]['.$acctnum.']['.$data_name.'] DUMP:', $data); }
4093				// SET DATA
4094				$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name] = $data;
4095				// session_db backwards_compat
4096				if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4097				|| ($this->use_private_table == True))
4098				{
4099					$my_location = (string)$acctnum.';'.$data_name;
4100					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 />'); }
4101					if ($this->use_private_table == True)
4102					{
4103						$this->so->so_set_data($my_location, $data);
4104					}
4105					else
4106					{
4107						$GLOBALS['phpgw']->session->appsession($my_location, 'email', $data);
4108					}
4109				}
4110
4111				$data = array();
4112				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 />'); }
4113				unset($data);
4114				return True;
4115			}
4116			else
4117			{
4118				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 />'); }
4119			}
4120
4121			// 2nd -- "session_cache_extreme" stuff, ADDITIONAL EXTRA CACHED ITEMS
4122			if ($this->session_cache_extreme == False)
4123			{
4124				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 />'); }
4125				return False;
4126			}
4127			elseif ($data_name == 'folder_status_info')
4128			{
4129				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 />'); }
4130				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); }
4131				// DATA REQUIRING "extra_keys" because it is probably one element of a larger group of related data
4132				// we will not waste time making a sub array, instead we add the "extra_keys" to the "location"
4133				// that way we can directly access this individual datUM without the hastle of a sub array
4134
4135				/*!
4136				@capability folder_status_info appsession cached when using extreme-mode
4137				@syntax Data Param ARRIVES to this function like this
4138				folder_status_info $data param arrives into this function like this:
4139					$folder_status_info = $data;
4140				because folder_status_info has in it a fldball item we can use
4141					$folder_status_info[foldball]
4142				has the fldball data we can use that already provided
4143
4144				@discussion The "extra_keys" string is the extra KEY which is "FOLDER"
4145				where "FOLDER" is the "long" foldername, meaning it has the Namespace_Delimiter prefixing it.
4146				NOTE this is less info than some other data items use in their "extra_keys", other data
4147				may require extra_keys that is "FOLDER_UID" where UID is the message UID, but this
4148				data is for "folder_status_info" so no message UID is necessary, nor applicable, to this.
4149				That "extra_keys" string is used in the "location" param of the appsession to access this specific data
4150				which is part of a larger set of data, BUT we do not need to use sub array because that single KEY
4151				will let us directly access this data.
4152				*/
4153
4154				if (!$extra_keys)
4155				{
4156					// (a) make the key, value pair that we will add to the cache
4157					// EXTRA_KEYS = FOLDERNAME_MSGNUM
4158					// we know the acctnum, so we already know the username and server name is correct
4159					// but within that account there are many folders, all data for those folders is indexed by a unique KEY
4160					// by using KEY of msgnum and foldername, we know everything we need to identify the exact data we need
4161
4162					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 />'); }
4163					// $data has info to generate out specific key
4164					// KEY MUST BE FOLDER IN URLENCODED FORM
4165					//$specific_key = $this->prep_folder_out($data[$data_name]['folder']);
4166					// ****************
4167					// fix this data structure, i.e. it should arrive here as one thing and we get the folder from IT itself
4168					// just a temp hack while fixing other stuff
4169					// IS THIS FOLDER NAME ALREADY URLENCODED? IT SHOULD BE.
4170					// but we are only using it as a "extra_keys" so maybe just be consistant with the other data?
4171					//$extra_keys = urlencode($data['folder_status_info']['fldball']['folder']);
4172					//$extra_keys = $data['folder_status_info']['fldball']['folder'];
4173					$extra_keys = $data['fldball']['folder'];
4174
4175				}
4176				// we know what to do here, so this data "has a handler"
4177				$has_handler = True;
4178
4179				// RECAST DATA ready for saving in appsession
4180				//$data = serialize($data);
4181
4182				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); }
4183				// SET DATA
4184				$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$extra_keys] = $data;
4185				// for DB sessions_db ONLY
4186				if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4187				|| ($this->use_private_table == True))
4188				{
4189					$my_location = (string)$acctnum.';'.$data_name.';'.$extra_keys;
4190					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 />'); }
4191					if ($this->use_private_table == True)
4192					{
4193						$this->so->so_set_data($my_location, $data);
4194					}
4195					else
4196					{
4197						$GLOBALS['phpgw']->session->appsession($my_location, 'email', $data);
4198					}
4199				}
4200				$data = array();
4201				unset($data);
4202				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 />'); }
4203				return True;
4204			}
4205			elseif (($data_name == 'msg_structure')
4206			|| ($data_name == 'phpgw_header'))
4207			{
4208				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 />'); }
4209				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); }
4210				// DATA REQUIRING "extra_keys" because it is probably one element of a larger group of related data
4211				// we will not waste time making a sub array, instead we add the "extra_keys" to the "location"
4212				// that way we can directly access this individual datUM without the hastle of a sub array
4213
4214				// "flat_pgw_struct" also known as "part_nice"
4215
4216				/*!
4217				@capability msg_structure and phpgw_header unique handling
4218				@abstract The theory is correct and still applied, but the sub-array style is OLD OLD OLD
4219				and has been changed to "extra_keys" in the "location"
4220				@discussion These 2 data items are cached ONLY IF this->session_cache_extreme IS TRUE.
4221				Notes on this data caching (see 4 notes, 2 in discussion and 2 in syntax):
4222				(1) Fetchstructure data can ONLY BE OBTAINED for the currently selected folder,
4223				so we automatically know which folder this data is applies to by calling get_arg_value("folder")
4224				HOWEVER in order to break free from this limitation in grabbing the data we are about to cache,
4225				ALWAYS pass a msgball WITH a folder value, even though it is obvious now, it may not be later.
4226				Note: folder name must be in urlencoded form, because it may contain "database unfriendly" chars
4227				This applies to "phpgw_header" data also.
4228				(Note: a msgball without a folder is *extremely* rare, and probably never occurs.)
4229				(2) "msg_structure" and "phpgw_header" caching scheme is UNUSUAL because
4230				it requires "extra_keys" param with BOTH folder AND message UID.
4231				You can provide such an  "extra_keys" param BUT that is NOT necessary because we will make
4232				the "extra_keys" string here, using the data from the "msgball" element of the data structure,
4233				see "syntax" below for how you MUST pass data into this function to save this data type.
4234				@syntax (3) The "extra_keys" string is the extra KEY which is "FOLDER_UID"
4235				where "FOLDER" is the "long" foldername, meaning it has the Namespace_Delimiter prefixing it, and
4236				where "_" is an underscore char, and
4237				where "UID" is the msgnum UID that the mailserver has given the message.
4238				That "extra_keys" string is used in the "location" param of the appsession to access this specific data
4239				which is part of a larger set of data, BUT we do not need to use sub array because that single KEY
4240				will let us directly access this data.
4241
4242				(4) Data Param ARRIVES to this function like this
4243				msg_structure $data param arrives into this function like this:
4244					$data['msgball'] = $msgball;
4245					$data['msg_structure'] = $data;
4246
4247				phpgw_header $data param arrives into this function like this:
4248					$data['msgball'] = $msgball;
4249					$data['phpgw_header'] = $data;
4250				@author Angles
4251				*/
4252
4253				if (!$extra_keys)
4254				{
4255					// (a) make the key, value pair that we will add to the cache
4256					// EXTRA_KEYS = FOLDERNAME_MSGNUM
4257					// we know the acctnum, so we already know the username and server name is correct
4258					// but within that account there are many folders, all data for those folders is indexed by a unique KEY
4259					// by using KEY of msgnum and foldername, we know everything we need to identify the exact data we need
4260
4261					//$extra_keys = (string)$data['msgball']['msgnum'].'_'.$data['msgball']['folder'];
4262					// OLD WAY -- DEPRECIATED
4263					$extra_keys = $data['msgball']['folder'].'_'.(string)$data['msgball']['msgnum'];
4264				}
4265				// NEW METHOD -- USE THIS
4266				$ex_folder = $data['msgball']['folder'];
4267				$ex_msgnum = $data['msgball']['msgnum'];
4268				// we know what to do here, so this data "has a handler"
4269				$has_handler = True;
4270
4271				// RECAST DATA ready for saving in appsession
4272				//$data = serialize($data[$data_name]);
4273				$data = $data[$data_name];
4274				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); }
4275				// SET DATA
4276				$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum] = $data;
4277				// for DB sessions_db ONLY
4278				if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4279				|| ($this->use_private_table == True))
4280				{
4281					$my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum;
4282					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 />'); }
4283					if ($this->use_private_table == True)
4284					{
4285						$this->so->so_set_data($my_location, $data);
4286					}
4287					else
4288					{
4289						$GLOBALS['phpgw']->session->appsession($my_location, 'email', $data);
4290					}
4291				}
4292				$data = array();
4293				unset($data);
4294				//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 />'); }
4295				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 />'); }
4296				return True;
4297			}
4298			elseif ($data_name == 'phpgw_fetchbody')
4299			{
4300				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 />'); }
4301				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); }
4302
4303				//(1) Data Param ARRIVES to this function like this
4304				//msg_structure $data param arrives into this function like this:
4305				//	$data['msgball'] = $msgball;
4306				//	$data['phpgw_fetchbody'] = $data;
4307				//
4308				//(2) Data Param $data['msgball'] SHOULD HAVE ELEMENT "part_no" !!!!
4309				//	$data['msgball']['acctnum']
4310				//	$data['msgball']['folder']
4311				//	$data['msgball']['msgnum']
4312				//	$data['msgball']['part_no']
4313
4314				if ((!isset($data['msgball']['part_no']))
4315				|| (!$data['msgball']['part_no']))
4316				{
4317					// LEAVING
4318					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 />'); }
4319					return False;
4320				}
4321				$strlen_phpgw_fetchbody = strlen($data['phpgw_fetchbody']);
4322				if ($strlen_phpgw_fetchbody > 60000)
4323				{
4324					// LEAVING
4325					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 />'); }
4326					return False;
4327				}
4328
4329				$ex_folder = $data['msgball']['folder'];
4330				$ex_msgnum = $data['msgball']['msgnum'];
4331				$ex_part_no = $data['msgball']['part_no'];
4332				// we know what to do here, so this data "has a handler"
4333				$has_handler = True;
4334
4335				// RECAST DATA ready for saving in appsession
4336				//$data = serialize($data[$data_name]);
4337				$data = $data[$data_name];
4338				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); }
4339				// SET DATA
4340				$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no] = $data;
4341				// for DB sessions_db ONLY
4342				if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4343				|| ($this->use_private_table == True))
4344				{
4345					$my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum.';'.$ex_part_no;
4346					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 />'); }
4347					if ($this->use_private_table == True)
4348					{
4349						$this->so->so_set_data($my_location, $data);
4350					}
4351					else
4352					{
4353						$GLOBALS['phpgw']->session->appsession($my_location, 'email', $data);
4354					}
4355				}
4356				$data = array();
4357				unset($data);
4358				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 />'); }
4359				return True;
4360			}
4361			else
4362			{
4363				// this data_name has no specific handler
4364				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 />'); }
4365				//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 />';
4366				// LEAVING
4367				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 />'); }
4368				return False;
4369			}
4370
4371			/*
4372			// save data, assuming we've "handled" it
4373			// perhaps we already saved and exited above, or even exited on error above
4374			if ((isset($data) == True)
4375			&& ($has_handler == True))
4376			{
4377				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>'; }
4378				if ($this->session_cache_debug_nosave == False)
4379				{
4380					$GLOBALS['phpgw']->session->appsession($location,$app,$data);
4381				}
4382				else
4383				{
4384					echo 'mail_msg: save_session_cache_item('.__LINE__.'): session_cache_debug_nosave disallows actual saving of data<br />';
4385				}
4386				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 />'; }
4387				return True;
4388			}
4389			else
4390			{
4391				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 />'; }
4392				return False;
4393			}
4394			*/
4395		}
4396
4397
4398		/*!
4399		@function read_session_cache_item  BEING OVERHAULED preliminary complete.
4400		@abstract get data from the appsession cache, requires a "handler" to be in this function to return the desired data.
4401		@param $data_name (string) The name of the data you want from the appsession cache, needs a handler in this function to return it.
4402		@param $acctnum (int) optional, can pass as an empty string the this function will use the currently active account.
4403		However, if you are not doiung the usual "show X msgs in a folder", such as showing search results from multiple
4404		sources, then there really is no "currently active" acctnum so you SHOULD pass it in that case.
4405		@param $ex_folder (string) folder name, in "preped_out" urlencoded form, the data applies to, such as for restoring "folder_status_info"
4406		@param $ex_msgnum (int) msgnum data applies to, currently only needed for restoring "msg_structure" and "phpgw_header" data
4407		which of course need the $ex_folder param too, any data requiring this $ex_msgnum param also requires the $ex_folder param.
4408		@author Angles
4409		@discussion used with appsession TEMPORARY DATA CACHING server-side caching of limited,
4410		ephermal data, such as a list of messages from an imap search, via appsession.
4411		NOTE: currently only ONE "msgball_list" is saved per account, and when user changes folders
4412		within an account, we must request a new "msgball_list" for that folder from the mail server.
4413		ISSUES (pro) this saves appsession from building up massive lists of msgballs, and (con) this
4414		results in needless requesting of info that we may have already asked for. NOTE the msgball_list
4415		does not care about number new, and only looks at number all to know when to refresh itself.
4416		The primary handler of that status info is function "get_folder_status_info".  THEREFOR
4417		msgball_list most important freshness meta_data is "number_all" for what we need to do.
4418		We need to use an event to directly make fresh the cached data and save it back to cache,
4419		so in order for the data not to look "stale" we need to do some math on the "number_all" in
4420		that msgball_list meta_data also.
4421		FURTHER DISCUSSION is that this is a PRIVATE function, generally you ask for a
4422		peice if data with the "get_arg_value" function, or in some cases by calling a specific function
4423		such as "get_msgball_list", then those functions determine HOW you get the data, including
4424		IF the data should be returned from the appsession cache. Therefor, for whatever data you
4425		want, YOU SHOULD NOT DIRECTLY call this function UNLESS YOU REALLY KNOW
4426		what you are doing and you are doing some exotic stuff.
4427		NOTE: ALL INFO SUBJECT TO CHANGE.
4428		@access private
4429		*/
4430		function read_session_cache_item($data_name='misc', $acctnum='', $ex_folder='', $ex_msgnum='', $ex_part_no='')
4431		{
4432			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 />'); }
4433			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 />'); }
4434			if ($this->debug_session_caching > 0) { $this->dbug->out('<br />'); }
4435			$font_start = '<font color="purple">';
4436			$font_end = '</font>';
4437
4438			if ((!isset($acctnum))
4439			|| ((string)$acctnum == ''))
4440			{
4441				$acctnum = $this->get_acctnum();
4442			}
4443
4444			if ($this->session_cache_enabled == False)
4445			{
4446				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 />'); }
4447				// EXITING NOW!~!!!!!
4448				return False;
4449			}
4450
4451			// we are here so at least some caching is allowed, get the data from appsession
4452			// handle it accordingly
4453			if (($data_name == 'folder_status_info')
4454			&& ($this->session_cache_extreme == True))
4455			{
4456				// GET DATA
4457				// for DB sessions_db ONLY
4458				if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4459				|| ($this->use_private_table == True))
4460				{
4461					$my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder;
4462					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 />'); }
4463					if ($this->use_private_table == True)
4464					{
4465						$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]
4466							= $this->so->so_get_data($my_location);
4467					}
4468					else
4469					{
4470						$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]
4471							= $GLOBALS['phpgw']->session->appsession($my_location, 'email');
4472					}
4473					//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>'; }
4474				}
4475
4476				// GET DATA
4477				// "folder_status_info" DATA IS ONLY appsession CACHED IF $this->session_cache_extreme IS TRUE
4478				$folder_status_info = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder];
4479
4480				if ($folder_status_info)
4481				{
4482					// HAVE WE ALREADY DONE THE TIMESTAMP CHECK ?
4483					// we only need the timestamp text once
4484					$already_checked_ok = $this->get_isset_arg('["folder_status_info_already_checked_ok"]["'.$ex_folder.'"]', $acctnum);
4485					//$this->get_arg_value('["msgball"]["msgnum"]')
4486
4487					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); }
4488					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 />'); }
4489					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 />'); }
4490					// this is set as a class param in file mail_msg_base
4491					$timestamp_age_limit = $this->timestamp_age_limit;
4492
4493					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 />'); }
4494					// this special handler uses timestamp info to determine "freshness"
4495					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 />'); }
4496					//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 />'; }
4497					//$folder_status_info = unserialize($folder_status_info);
4498					$timestamp_age = (time() - $folder_status_info['timestamp']);
4499					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 />'); }
4500					if ($this->debug_session_caching > 2) { $this->dbug->out('class_msg: get_folder_status_info: ('.__LINE__.') $folder_status_info DUMP:', $folder_status_info); }
4501					if ($already_checked_ok == True)
4502					{
4503						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 />'); }
4504					}
4505					elseif ($timestamp_age > $timestamp_age_limit)
4506					{
4507						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 />'); }
4508						$got_data = '';
4509						unset($got_data);
4510						//$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder] = array();
4511						//unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]);
4512						// this does the same thing, so use this instead
4513						$this->expire_session_cache_item('folder_status_info', $acctnum, $ex_folder);
4514
4515						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 />'); }
4516						return False;
4517
4518					}
4519					// set flag "folder_status_info_already_checked_ok" for this ex_folder so we do NOT timestamp check again the rest of THIS PAGEVIEW
4520					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 />'); }
4521					$this->set_arg_value('["folder_status_info_already_checked_ok"]["'.$ex_folder.'"]', 'timestamp_ok', $acctnum);
4522					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 />'); }
4523					return $folder_status_info;
4524				}
4525				else
4526				{
4527					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 />'); }
4528					return False;
4529				}
4530
4531			}
4532			elseif ($data_name == 'msgball_list')
4533			{
4534
4535				if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handler exists for $data_name ['.$data_name.']<br />'); }
4536
4537				/*!
4538				@capability VERIFY appsession cached "msgball_list" data is still valid (in read_session_cache_item)
4539				@discussion The "msgball_list" is a list of all messages in a folder sorted according to user prefs and
4540				containing additional information to make each message UID into a msgball by adding the acctnum
4541				and folder data to the msgnum. This data is put into a numbered array we call the "msgball_list".
4542				This "msgball_list" is cached in the appsession along with some meta_data that we use verify the
4543				cached msgball_list as (a) applicable to the current mailserver and user, and (b) for "freshness".
4544				For (b) "freshness", we compare the meta_data items for folder status against the
4545				current "folder_status_info" data to determine if this cached msgball_list is "fresh" or "stale".
4546				See the example for the exact data structure of the cached data. Additionally, for (a) "applicablility"
4547				just to be super safe, the cached msgball_list meta_data is tested against the current "mailsvr_callstr" and
4548				the current "mailsvr_account_username" args, not really needed but is does make us confident
4549				that the cached msgball_list applies to the same account as existed when the cache was set. It is not
4550				really known how they could not match, but this additional test can not hurt.
4551				This test is the SAME for session_cache_extreme True or False,
4552				the difference is that for session_cache_extreme TRUE, the "number_all" element
4553				of this data and the "number_all" element of the "folder_status_info" appsession cached
4554				data is manually updated so (1) they are "fresh" with respect to changes the user makes
4555				such as moving or deleting mail from a folder, and (2) so the following test which matches
4556				"number_all" elements of both data sets remain the same, so the "msgball_list" is considered
4557				"fresh" by this test.
4558		@example This is the meta data that is saved and tested on reading the data from cache.
4559		$validity_test['fldball'] = array();
4560		$validity_test['fldball']['acctnum'] = $acctnum;
4561		$validity_test['fldball']['folder'] = $this->get_arg_value('folder', $acctnum);
4562		$validity_test['folder_status_info']['uidnext'] = $folder_info['uidnext'];
4563		$validity_test['folder_status_info']['uidvalidity'] = $folder_info['uidvalidity'];
4564		$validity_test['folder_status_info']['number_all']  = $folder_info['number_all'];
4565		$validity_test['sort'] = $this->get_arg_value('sort', $acctnum);
4566		$validity_test['order'] = $this->get_arg_value('order', $acctnum);
4567		$validity_test['mailsvr_callstr'] = $this->get_arg_value('mailsvr_callstr', $acctnum);
4568		$validity_test['mailsvr_account_username'] = $this->get_arg_value('mailsvr_account_username', $acctnum);
4569				@author Angles
4570				*/
4571
4572				// NEW: NOW WE NEED A FOLDER NAME TO COMPLETE THE KEY
4573				// if one is not provided as a param,
4574				// (a) WE MUST GET ONE, and
4575				// (b) it must be URLENCODED, or preped_out
4576				if ($ex_folder != '')
4577				{
4578					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 />'); }
4579				}
4580				else
4581				{
4582					$ex_folder = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
4583					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 />'); }
4584				}
4585
4586				// GET DATA
4587				// for DB sessions_db ONLY
4588				if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4589				|| ($this->use_private_table == True))
4590				{
4591					//$my_location = (string)$acctnum.';'.$data_name;
4592					// WE N OW USE FOLDER DATA TOO
4593					$my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder;
4594					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 />'); }
4595					if ($this->use_private_table == True)
4596					{
4597						//$got_data = array();
4598						//$got_data = $this->so->so_get_data($my_location);
4599						//$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]
4600						//	= $got_data;
4601
4602						//$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]
4603						//	= $this->so->so_get_data($my_location);
4604
4605						// TRY USING COMPRESSION
4606						//$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]
4607						//	= $this->so->so_get_data($my_location, True);
4608
4609						// TRY USING COMPRESSION
4610						$got_data = array();
4611						$got_data = $this->so->so_get_data($my_location, True);
4612						$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]
4613							= $got_data;
4614					}
4615					else
4616					{
4617						// NOTE: no compression available for appsession
4618						$got_data = array();
4619						$got_data = $GLOBALS['phpgw']->session->appsession($my_location, 'email');
4620						$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]
4621							= $got_data;
4622
4623						// NOTE: no compression available for appsession
4624						//$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]
4625						//	= $GLOBALS['phpgw']->session->appsession($my_location, 'email');
4626					}
4627					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]); }
4628				}
4629				//$got_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name];
4630
4631				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); }
4632				if (!$got_data)
4633				{
4634					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 />'); }
4635					return False;
4636				}
4637				else
4638				{
4639					// folder_info used to test validity (stale or not) of the cached msgball_list data
4640					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 />'); }
4641					// UPDATE THIS CRAP TO HANDLE ANY FOLDER WITHIN A SINGLE ACCOUNT, not just the current one
4642					$fldball = array();
4643					$fldball['acctnum'] = $acctnum;
4644					$fldball['folder'] = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
4645					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 />'); }
4646					$folder_info = $this->get_folder_status_info($fldball);
4647					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 />'); }
4648
4649					// "validity_test" is what the test is like, but it's only really used for the debug data dump
4650					// because the real test below gets all the same info all over again as exists in this "match_to_be_fresh" array
4651					$validity_test = array();
4652					$validity_test['fldball'] = array();
4653					$validity_test['fldball']['acctnum'] = $acctnum;
4654					$validity_test['fldball']['folder'] = $this->prep_folder_out($this->get_arg_value('folder', $acctnum));
4655					$validity_test['folder_status_info']['uidnext'] = $folder_info['uidnext'];
4656					$validity_test['folder_status_info']['uidvalidity'] = $folder_info['uidvalidity'];
4657					$validity_test['folder_status_info']['number_all']  = $folder_info['number_all'];
4658					$validity_test['sort'] = $this->get_arg_value('sort', $acctnum);
4659					$validity_test['order'] = $this->get_arg_value('order', $acctnum);
4660					$validity_test['mailsvr_callstr'] = $this->get_arg_value('mailsvr_callstr', $acctnum);
4661					$validity_test['mailsvr_account_username'] = $this->get_arg_value('mailsvr_account_username', $acctnum);
4662
4663					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); }
4664					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']); }
4665
4666					// only is admin is using DB as session store, if "php4" session is used instead, this is not necessary
4667					//if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4668					//&& (!strstr($got_data['validity']['mailsvr_callstr'] ,'{')) )
4669					//{
4670					//	// NOTE: YOU BETTER BASE64 ENCODE "DEFANG" the "mailsvr_callstr" OR RISK b0rking your database!!!
4671					//	// applies to DBs where ' " / \ and other offensive chars are not handled at the api level in phpgw
4672					//	$got_data['validity']['mailsvr_callstr'] = base64_decode($got_data['validity']['mailsvr_callstr']);
4673					//	// COMMENT THAT OUT if you can prove that DBs are immune to this b0rking
4674					//}
4675
4676					if (($got_data['validity']['fldball']['folder'] == $this->prep_folder_out($this->get_arg_value('folder', $acctnum)))
4677					&& ($got_data['validity']['fldball']['acctnum'] == $acctnum)
4678					&& ($got_data['validity']['folder_status_info']['uidnext'] == $folder_info['uidnext'])
4679					&& ($got_data['validity']['folder_status_info']['uidvalidity'] == $folder_info['uidvalidity'])
4680					&& ($got_data['validity']['folder_status_info']['number_all']  == $folder_info['number_all'])
4681					&& ($got_data['validity']['sort'] == $this->get_arg_value('sort', $acctnum))
4682					&& ($got_data['validity']['order'] == $this->get_arg_value('order', $acctnum))
4683					&& ($got_data['validity']['mailsvr_callstr'] == $this->get_arg_value('mailsvr_callstr', $acctnum))
4684					&& ($got_data['validity']['mailsvr_account_username'] == $this->get_arg_value('mailsvr_account_username', $acctnum)))
4685					{
4686						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); }
4687						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 />'); }
4688						return $got_data['msgball_list'];
4689					}
4690					else
4691					{
4692						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 />'); }
4693						//$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name] = array();
4694						//unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]);
4695						// this is better to use
4696						//$this->expire_session_cache_item($data_name, $acctnum);
4697						// NOW WE USE FOLDER IN DATA KEY ALSO
4698						$this->expire_session_cache_item($data_name, $acctnum, $ex_folder);
4699						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 />'); }
4700						return False;
4701					}
4702				}
4703			}
4704			elseif ($data_name == 'mailsvr_callstr_OLD_HANDLER')
4705			//elseif ($data_name == 'mailsvr_callstr')
4706			{
4707				// GET DATA
4708				$got_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name];
4709				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 />'); }
4710				// this special handler decodes the database defanging
4711				if ($got_data)
4712				{
4713					// DATABASE DEFANG, this item has "database unfriendly" chars in it so we encode it before it goes to appsession cache
4714					// now we mode DECODE it coming out of the appsession cache
4715					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); }
4716					$got_data = base64_decode($got_data);
4717					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); }
4718					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 />'); }
4719					return $got_data;
4720				}
4721				else
4722				{
4723					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 />'); }
4724					return False;
4725				}
4726			}
4727			elseif (($data_name == 'mailsvr_namespace')
4728			|| ($data_name == 'folder_list')
4729			|| ($data_name == 'mailsvr_callstr'))
4730			{
4731				// GET DATA
4732				// for DB sessions_db ONLY
4733				if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4734				|| ($this->use_private_table == True))
4735				{
4736					$my_location = (string)$acctnum.';'.$data_name;
4737					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 />'); }
4738					if ($this->use_private_table == True)
4739					{
4740						$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]
4741							= $this->so->so_get_data($my_location);
4742					}
4743					else
4744					{
4745						$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]
4746							= $GLOBALS['phpgw']->session->appsession($my_location, 'email');
4747					}
4748					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]); }
4749				}
4750				$got_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name];
4751
4752				// "mailsvr_callstr" if session is NOT "db" is handled here because no database defang if necessary
4753				if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handler exists for $data_name ['.$data_name.']<br />'); }
4754				// this is not really a special handler
4755				if ($got_data)
4756				{
4757					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); }
4758					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 />'); }
4759					return $got_data;
4760				}
4761				else
4762				{
4763					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 />'); }
4764					return False;
4765				}
4766
4767			}
4768			elseif (($this->session_cache_extreme == True)
4769			&& (
4770					($data_name == 'msg_structure')
4771				||  ($data_name == 'phpgw_header'))
4772			)
4773			{
4774				// THIS DATA IS NEVER CACHED IF $this->session_cache_extreme IS FALSE
4775				if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handler exists for $data_name ['.$data_name.']<br />'); }
4776				// this IS a special handler because we do not return the entire cached array of "msg_structure" elements,
4777				// we only return ONE SINGLE "msg_structure" (or "phpgw_header" ) value from that array,
4778				// if it exists in that array.
4779				// the array can exist but not have the specific element we are looking for
4780				// NOTE: we need param $special_extra_stuff because other cached items are returned whole
4781				// but "msg_structure" (or "phpgw_header" ) need that EXTRA peice of information
4782				// to return only a portion of cached data.
4783
4784				// GET DATA
4785				// for DB sessions_db ONLY
4786				if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4787				|| ($this->use_private_table == True))
4788				{
4789					$my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum;
4790					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 />'); }
4791					if ($this->use_private_table == True)
4792					{
4793						$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum]
4794							= $this->so->so_get_data($my_location);
4795					}
4796					else
4797					{
4798						$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum]
4799							= $GLOBALS['phpgw']->session->appsession($my_location, 'email');
4800					}
4801					//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>'; }
4802				}
4803				$got_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum];
4804
4805				if ($got_data)
4806				{
4807					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); }
4808					// we need more info than the usual cached item to get what we want, because we want a SINGLE element only
4809					// the array KEY in the key,value pair is passed in param $special_extra_stuff
4810
4811					// SUCCESS - desired single element within that array does exist
4812					// also unserialize it back into an object
4813					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 />'); }
4814					// NOTE unserializing an already inserialized thing gives you NOTHING, so damn well better serialize this on saving to the appsession cache.
4815					return $got_data;
4816
4817				}
4818			}
4819			elseif (($this->session_cache_extreme == True)
4820			&& ($data_name == 'phpgw_fetchbody'))
4821			{
4822				// THIS DATA IS NEVER CACHED IF $this->session_cache_extreme IS FALSE
4823				if ($this->debug_session_caching > 1) { $this->dbug->out('mail_msg: read_session_cache_item('.__LINE__.'): handler exists for $data_name ['.$data_name.']<br />'); }
4824
4825				// GET DATA
4826				// for DB sessions_db ONLY
4827				if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4828				|| ($this->use_private_table == True))
4829				{
4830					$my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum.';'.$ex_part_no;
4831					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 />'); }
4832					if ($this->use_private_table == True)
4833					{
4834						$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no]
4835							= $this->so->so_get_data($my_location);
4836					}
4837					else
4838					{
4839						$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no]
4840							= $GLOBALS['phpgw']->session->appsession($my_location, 'email');
4841					}
4842					//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>'; }
4843				}
4844				$got_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no];
4845
4846				if ($got_data)
4847				{
4848					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); }
4849					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 />'); }
4850					return $got_data;
4851
4852				}
4853				else
4854				{
4855					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 />'); }
4856					return False;
4857				}
4858			}
4859			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 />'); }
4860			//return $got_data;
4861			return False;
4862		}
4863
4864		/*!
4865		@function expire_session_cache_item  BEING OVERHAULED  preliminary complete.
4866		@abstract ?
4867		@discussion used with appsession TEMPORARY DATA CACHING server-side caching of limited,
4868		ephermal data, such as a list of messages from an imap search, via appsession
4869		NOTE we get the old data, if any, to see whether it was an array or string, HOWEVER using get_arg_value
4870		for "folder_list" WILL ATTEMPT A LOGIN so to avoid that we use "_direct_access_arg_value" instead.
4871		@author Angles
4872		@access private
4873		*/
4874		// ---- session-only data cached to appsession  ----
4875		function expire_session_cache_item($data_name='misc',$acctnum='', $ex_folder='', $ex_msgnum='', $ex_part_no='')
4876		{
4877			if ((!isset($acctnum))
4878			|| ((string)$acctnum == ''))
4879			{
4880				$acctnum = $this->get_acctnum();
4881			}
4882			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 />'); }
4883			// process $extra_keys input
4884
4885			// BEFORE we do anything else, if extreme-mode is OFF then we MANDATORY, ALWAYS, NO-MATTER-WHAT expire "folder_status_info"
4886			// NOTE: in non-extreme cache mode, "folder_status_info" is exipred ALLWAYS whenever we expire ANYTHING
4887			// why? because I'm assuming (correctly?) that if we are expiring something then it *probably* effects "folder_status_info" too
4888			// NOTE also that in extreme mode, "folder_status_info" is NOT put in L1 cache EVER, we directly use appsession.
4889			if ($this->session_cache_extreme == False)
4890			{
4891				// ---  get rid of any L1 cache for "folder_status_info"  ---
4892				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 />'); }
4893				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 />'); }
4894				// cache data in a class var (L1 Cache)
4895				// ALWAYS expire "folder_status_info" because many time this expire function is called because of a message move or delete
4896				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 />'); }
4897				$empty_array = array();
4898				//$this->set_arg_value('folder_status_info', $empty_array, $acctnum);
4899				$this->set_arg_value('["folder_status_info"]["'.$ex_folder.'"]', $empty_array, $acctnum);
4900
4901			}
4902
4903			// now eliminate the EXPIRED data, 1st get rid of any L1 cache it it exists for this item
4904			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 />'); }
4905			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 />'); }
4906
4907			// FIRST -- EXPIRE ANY L1 CACHE for this data_name
4908			if (($this->get_isset_arg($data_name, $acctnum))
4909			&& (strstr($data_name, 'folder_status_info') == False))
4910			{
4911				// NOTE L1 cached "folder_status_info" was (a) expired above for non-extreme mode and (b) never uses L1 cached in extreme mode
4912				//$old_content = $this->get_arg_value($data_name, $acctnum);
4913				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 />'); }
4914				$old_content = $this->_direct_access_arg_value($data_name, $acctnum);
4915				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); }
4916				if (gettype($old_content) == 'array')
4917				{
4918					$empty_data = array();
4919				}
4920				else
4921				{
4922					$empty_data = '';
4923				}
4924				// set the arg item to this blank value, effectively clearing/expiring it
4925				$this->set_arg_value($data_name, $empty_data, $acctnum);
4926			}
4927
4928			// SECOND -- EXPIRE ANY APPSESSION CACHE for this data_name
4929			// ---  now get rid of any "$data_name" value saved in the session cache  ---
4930			// also note that in extreme mode we expire "folder_status_info" here because
4931			//    (a) it would be handled above for non-extreme mode and
4932			//    (b) it uses subitems so we need to handle it here so we expire single sub item and not the whole thing
4933
4934			// DELETE DATA
4935			// for DB sessions_db ONLY
4936			if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db')
4937			|| ($this->use_private_table == True))
4938			{
4939				// generic fallback value
4940				$my_location = (string)$acctnum.';'.$data_name;
4941				if (((string)$ex_folder != '')
4942				&& ((string)$ex_msgnum != '')
4943				&& ((string)$ex_part_no != ''))
4944				{
4945					$my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum.';'.$ex_part_no;
4946				}
4947				elseif (((string)$ex_folder != '')
4948				&& ((string)$ex_msgnum != ''))
4949				{
4950					$my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder.';'.$ex_msgnum;
4951				}
4952				elseif ((string)$ex_folder != '')
4953				{
4954					$my_location = (string)$acctnum.';'.$data_name.';'.$ex_folder;
4955				}
4956
4957				if ($this->use_private_table == True)
4958				{
4959					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 />'); }
4960					$this->so->so_delete_data($my_location);
4961				}
4962				else
4963				{
4964					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 />'); }
4965					$GLOBALS['phpgw']->session->appsession($my_location, 'email', '');
4966				}
4967			}
4968
4969			//if ((!$ex_folder)
4970			//&& (!$ex_msgnum)
4971			if (((string)$ex_folder == '')
4972			&& ((string)$ex_msgnum == '')
4973			&& (isset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name])))
4974			{
4975				$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name] = '';
4976				unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]);
4977				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 />'); }
4978				return True;
4979			}
4980			//elseif (($ex_folder)
4981			//&& (!$ex_msgnum)
4982			elseif (((string)$ex_folder != '')
4983			&& ((string)$ex_msgnum == '')
4984			&& (isset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder])))
4985			{
4986				$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder] = '';
4987				unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder]);
4988				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 />'); }
4989				return True;
4990			}
4991			//elseif (($ex_folder)
4992			//&& ($ex_msgnum)
4993			elseif (((string)$ex_folder != '')
4994			&& ((string)$ex_msgnum != '')
4995			&& (isset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum])))
4996			{
4997				$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum] = '';
4998				unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum]);
4999				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 />'); }
5000				return True;
5001			}
5002			elseif (((string)$ex_folder != '')
5003			&& ((string)$ex_msgnum != '')
5004			&& ((string)$ex_part_no != '')
5005			&& (isset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no])))
5006			{
5007				$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no] = '';
5008				unset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$ex_folder][$ex_msgnum][$ex_part_no]);
5009				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 />'); }
5010				return True;
5011			}
5012			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 />'); }
5013			return False;
5014		}
5015
5016
5017		/**************************************************************************\
5018		* END  CACHING HANDLERS								*
5019		* - - - - - - - - - - - - - - - - - - - - - - - - -									*
5020		* BEGIN PARAM / ARGS / PREFS  ACCESS FUNCTIONS 			*
5021		\**************************************************************************/
5022
5023		/*!
5024		@capability OOP-Style Access Methods to Private Object Properties
5025		@abstract  simple access methods to read and set data, with transparent account number handling
5026		@discussion When multiple email accounts are enables, they may even be active at the same time,
5027		thus the properties and preferences for any individual email account must be distinctly accessable
5028		for each email account with as little brain damage to the developer as possible. These access methods
5029		server two purposes:
5030		(1) centralize access to all params and oprefs into a common, standardized methodology, and
5031		(2) these access functions also transparently handly the dirty work of tracking which email account
5032		the data applies to, takes care of any special handling a param may require, and it's classic OOP style.
5033		With the exception of a few paramaters/arguments that are not specific to any individual email acount,
5034		such as for private, internal object core properties, the developer need only use these functions to
5035		access object params, arguments, and preferences.
5036		@author Angles
5037		*/
5038
5039		/*!
5040		@function get_acctnum
5041		@abstract  read which account number the object is currently activated on
5042		@param $unset_returns_default  boolean  default True. If no acctnum is currently set,
5043		should this function return a boolean False or a hardcoded "fallback default" account number,
5044		typically integer 0. Default is to return a fallback default account number.
5045		@returns (most typically) the internal account number of the currently active email account,
5046		but can be set, via the $unset_returns_default param,
5047		@discussion When multiple email accounts are enabled, all arg/param and preference access
5048		functions "pivot" off of this "object->acctnum" property, it serves essentially as the array key
5049		which maps the various access functions to the data of the intended account number.
5050		DEVELOPERS NOTE: The integer zero returned by this function can sometimes be mistaken
5051		as "empty" of "false", when using conditionals such as
5052		if ($my_acctnum) { then do this };
5053		may incorrectly interper integer 0 as a "false" and this example conditional would not behave
5054		as expected, since there is infact a valid acount number of 0 in the variable. The preferred test
5055		for that type of condition is:
5056		if ((string)$my_acctnum != '') { then do this };
5057		which produces a more desirable result.
5058		@author Angles
5059		*/
5060		function get_acctnum($unset_returns_default=True)
5061		{
5062			if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: get_acctnum: ENTERING, (parm $unset_returns_default=['.serialize($unset_returns_default).'])<br />'); }
5063
5064			if ((isset($this->acctnum))
5065			&& ((string)$this->acctnum != ''))
5066			{
5067				if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: get_acctnum: LEAVING, $this->acctnum exists, returning it: '.serialize($this->acctnum).'<br />'); }
5068				return $this->acctnum;
5069			}
5070			// ok, no useful acctnumber exists, what should we do
5071			elseif ($unset_returns_default == True)
5072			{
5073
5074				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 />'); }
5075				return $this->fallback_default_acctnum;
5076			}
5077			else
5078			{
5079				if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: get_acctnum: LEAVING, NO $this->acctnum exists, returning FALSE<br />'); }
5080				return False;
5081			}
5082		}
5083
5084		/*!
5085		@function set_acctnum
5086		@abstract  instruct the object which email account is the desired active account for all params,
5087		args, and preferences should refer to.
5088		@param $acctnum  integer
5089		@result True if a valid param $acctnum is given and the object->acctnum value is set, False if
5090		invalid data is passed in the param.
5091		@author Angles
5092		@discussion ?
5093		@access public
5094		*/
5095		function set_acctnum($acctnum='')
5096		{
5097			if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: set_acctnum: ENTERING, (parm $acctnum=['.serialize($acctnum).'])<br />'); }
5098			if ((isset($acctnum))
5099			&& ((string)$acctnum != ''))
5100			{
5101				$this->acctnum = $acctnum;
5102				if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: set_acctnum: LEAVING, returning True, made $this->acctnum = $acctnum ('.serialize($acctnum).')<br />'); }
5103				return True;
5104			}
5105			else
5106			{
5107				if ($this->debug_accts > 0) { $this->dbug->out('mail_msg: set_acctnum: LEAVING, returning False, value $acctnum not sufficient to set $this->acctnum<br />'); }
5108				return False;
5109			}
5110		}
5111
5112
5113		/* * * * * * * * * * * * * * * * * *
5114		* OOP-Style Access Methods for Preference Values
5115		* * * * * * * * * * * * * * * * * */
5116		/*!
5117		@function get_pref_value
5118		@abstract OOP access of pref items for any given email account.
5119		@param $pref_name (string) id if the desired pref item
5120		@param $acctnum (int) OPTIONAL acctnum this applies to, "current acctnum" will be supplied if this arg is not given.
5121		@result value of the desired pref IF it exists, if no you are returned nothing (null).
5122		@discussion ?
5123		@access public
5124		@author Angles
5125		*/
5126		function get_pref_value($pref_name='',$acctnum='')
5127		{
5128			if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_pref_value: ENTERING, $pref_name: ['.$pref_name.'] $acctnum: ['.$acctnum.']'.'<br />'); }
5129			if ((!isset($acctnum))
5130			|| ((string)$acctnum == ''))
5131			{
5132				$acctnum = $this->get_acctnum();
5133				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 />'); }
5134			}
5135
5136			if ((isset($pref_name))
5137			&& ((string)$pref_name != '')
5138			&& (isset($this->a[$acctnum]['prefs'][$pref_name])))
5139			{
5140				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 />'); }
5141				return $this->a[$acctnum]['prefs'][$pref_name];
5142			}
5143			else
5144			{
5145				// arg not set, or invalid input $arg_name
5146				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 />'); }
5147				return;
5148			}
5149		}
5150
5151		/*!
5152		@function set_pref_value
5153		@abstract set pref value for a pref items for any given email account, TEMPORARY to this page view.
5154		@param $pref_name  (string)
5155		@param $this_value (string, int, or array)
5156		@param $acctnum  (int) OPTIONAL
5157		@result True if pref value was set, False on failure to set the item, such as invalid data is passed in the param.
5158		@author Angles
5159		@discussion Used when certain prefs are derived from other prefs, they may not actually exist in the
5160		prefs DB, so we figure them out in the code somewhere and set the value here, Lasts only as long a
5161		a single script run.
5162		@access public
5163		*/
5164		function set_pref_value($pref_name='', $this_value='', $acctnum='')
5165		{
5166			if ((!isset($acctnum))
5167			|| ((string)$acctnum == ''))
5168			{
5169				$acctnum = $this->get_acctnum();
5170			}
5171
5172			if ((isset($pref_name))
5173			&& ((string)$pref_name != ''))
5174			{
5175				$this->a[$acctnum]['prefs'][$pref_name] = $this_value;
5176				// return True to indicate success
5177				return True;
5178			}
5179			else
5180			{
5181				// return False to indicate invalid input $arg_name
5182				return False;
5183			}
5184		}
5185
5186		/*!
5187		@function get_isset_pref
5188		@abstract Check if a given preference is set
5189		@param $pref_name  (string)
5190		@param $acctnum  (int) OPTIONAL
5191		@result True if preference $pref_name value exists, False if not
5192		@author Angles
5193		@discussion It is common the boolean preference items are simply not set if their value
5194		is supposed to be false. This function can be used for this discovery. Note that some
5195		string preferences items, such as the email sig, can be set yet have a value of an empty string,
5196		in this case this function follows strict logic and returns True because the preference exists.
5197		Similarly, uwash mail location is another example of a preference item where an empty string
5198		is a valid value.
5199		@access public
5200		*/
5201		function get_isset_pref($pref_name='',$acctnum='')
5202		{
5203			if ((!isset($acctnum))
5204			|| ((string)$acctnum == ''))
5205			{
5206				$acctnum = $this->get_acctnum();
5207			}
5208			// error check
5209			if ((isset($pref_name))
5210			&& ((string)$pref_name != '')
5211			&& (isset($this->a[$acctnum]['prefs'][$pref_name])))
5212			{
5213				return True;
5214			}
5215			else
5216			{
5217				// arg not set, or invalid input $arg_name
5218				return False;
5219			}
5220		}
5221
5222		/*!
5223		@function unset_pref
5224		@abstract unset a preference item.
5225		@param $pref_name  (string)
5226		@param $acctnum  (int) OPTIONAL
5227		@result True if $pref_name existed and was made unset, False on failure, such as $pref_name not
5228		being set in the first place. This function can not unset something that does not exist to begin with.
5229		@author Angles
5230		@discussion ?
5231		@access public
5232		*/
5233		function unset_pref($pref_name='', $acctnum='')
5234		{
5235			if ((!isset($acctnum))
5236			|| ((string)$acctnum == ''))
5237			{
5238				$acctnum = $this->get_acctnum();
5239			}
5240
5241			if ((isset($pref_name))
5242			&& ((string)$pref_name != ''))
5243			{
5244				$this->a[$acctnum]['prefs'][$pref_name] = '';
5245				unset($this->a[$acctnum]['prefs'][$pref_name]);
5246				// return True to indicate success
5247				return True;
5248			}
5249			else
5250			{
5251				// return False to indicate invalid input $pref_name
5252				return False;
5253			}
5254		}
5255
5256		/*!
5257		@function get_all_prefs
5258		@abstract get the entire preference data array FOR ONE ACCOUNT
5259		@param $acctnum  (int) OPTIONAL
5260		@result none
5261		@author Angles
5262		@discussion The result are not the raw preferences directly from the database, this function returns
5263		the preference array for an email account as explosed to the email app, that is these are preferences that
5264		have passed through some logic to process and normalize them,
5265		@access public
5266		*/
5267		function get_all_prefs($acctnum='')
5268		{
5269			if ((!isset($acctnum))
5270			|| ((string)$acctnum == ''))
5271			{
5272				$acctnum = $this->get_acctnum();
5273			}
5274
5275			if (isset($this->a[$acctnum]['prefs']))
5276			{
5277				return $this->a[$acctnum]['prefs'];
5278			}
5279			else
5280			{
5281				// arg not set, or invalid input $arg_name
5282				return;
5283			}
5284		}
5285
5286		/*!
5287		@function set_pref_array
5288		@abstract set the entire preference data array FOR ONE ACCOUNT
5289		@param $pref_array_data  (array) either (a) correctly formed email pref array data, or (b) an empty array
5290		@param $acctnum  (int) OPTIONAL
5291		@result boolean True is successfully sets $pref_array_data, False to indicate all we did was clear the args, no data was fed
5292		@author Angles
5293		@discussion NOTE  the first thing this function does is clear the existing preference array for the
5294		emal account. This happens no matter what. This effectively is a way to clear an accounts email preference
5295		array by passing an empty array, which can be useful in certain situations. More commonly this function
5296		is used to set the entire preference array for an account in one operation. In that case you better know
5297		what youre doing, $pref_array_data must be correctly formed emai pref array data. By clearing the
5298		existing preference array no matter what, this is why a return value of False indicates that, while no
5299		new preference data was set, still something did occur and that was the clearing of any pre-existing
5300		preference array.
5301		@access private
5302		*/
5303		function set_pref_array($pref_array_data='', $acctnum='')
5304		{
5305			if ((!isset($acctnum))
5306			|| ((string)$acctnum == ''))
5307			{
5308				$acctnum = $this->get_acctnum();
5309			}
5310
5311			$this->a[$acctnum]['prefs'] = array();
5312
5313			if ((isset($pref_array_data))
5314			&& (count($pref_array_data > 0)))
5315			{
5316				$this->a[$acctnum]['prefs'] = $pref_array_data;
5317				// return True to indicate we filled, not just cleared
5318				return True;
5319			}
5320			else
5321			{
5322				// return False to indicate all we did was clear the args, no data was fed
5323				return False;
5324			}
5325		}
5326
5327
5328		/* * * * * * * * * * * * * * * * * *
5329		* OOP-Style Access Methods for Class Params/Args Values
5330		* * * * * * * * * * * * * * * * * */
5331		/*!
5332		@function _get_arg_is_known
5333		@abstract utility function for private use, tests if a given  $arg_name is in the $this->known_external_args[] array.
5334		@param $arg_name  (string)
5335		@param $calling_function_name  (string) used for debug output
5336		@result boolean
5337		@author Angles
5338		@discussion ?
5339		@access private
5340		*/
5341		function _get_arg_is_known($arg_name='', $calling_function_name='')
5342		{
5343			// skip this unless debug level 4
5344			if ($this->debug_args_oop_access < 4)
5345			{
5346				return False;
5347			}
5348
5349			if ($arg_name == '')
5350			{
5351				return False;
5352			}
5353			if ($calling_function_name == '')
5354			{
5355				$calling_function_name == 'UNSPECIFIED';
5356			}
5357			// loop thru known externally controlled args
5358			$finding = False;
5359			$report = '';
5360			for($i=0; $i < count($this->known_external_args); $i++)
5361			{
5362				if ($arg_name == $this->known_external_args[$i])
5363				{
5364					$finding = True;
5365					$report = '*is* known (external)';
5366					break;
5367				}
5368			}
5369			// check internal args
5370			for($i=0; $i < count($this->known_internal_args); $i++)
5371			{
5372				if ($arg_name == $this->known_internal_args[$i])
5373				{
5374					$finding = True;
5375					$report = '*is* known (internal)';
5376					break;
5377				}
5378			}
5379			if (!$finding)
5380			{
5381				$report = '*NOT* KNOWN *NOT* KNOWN *NOT* KNOWN *NOT* KNOWN';
5382			}
5383			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 />'); }
5384			return $finding;
5385		}
5386
5387		/*!
5388		@function get_isset_arg
5389		@abstract Check if a given variable is set
5390		@param $arg_name  (string)
5391		@param $acctnum  (int) OPTIONAL
5392		@param $extra_keys (string) for SPECIAL INTERNAL use only. Rarely used. DEPRECIATED use sublevels instead.
5393		@result boolean
5394		@author Angles
5395		@discussion ?
5396		@access public
5397		*/
5398		function get_isset_arg($arg_name='',$acctnum='', $extra_keys='')
5399		{
5400			if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: ENTERING, $arg_name: ['.$arg_name.'] $acctnum: ['.$acctnum.']'.'<br />'); }
5401			if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'get_isset_arg'); }
5402
5403			if ((!isset($acctnum))
5404			|| ((string)$acctnum == ''))
5405			{
5406				$acctnum = $this->get_acctnum();
5407				if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: obtained $acctnum from $this->get_acctnum(): ['.$acctnum.']'.'<br />'); }
5408			}
5409
5410			/*
5411			// OOP VERSION if PROBLEMATIC
5412			// but it may not give intended answer because
5413			// "get_arg_value" will handoff processing to specialized functions that WILL fill the value
5414			// sometimes simply with default values, which would cause this function to return unexpected results
5415			$test_this = $this->get_arg_value($arg_name, $acctnum);
5416			if (isset($test_this))
5417			{
5418				return True;
5419			}
5420			*/
5421
5422			// Best Version at this time, if something is not set, DO NOT handoff to a support function to fill it
5423			// that way we can return false if something is indeed NOT set
5424
5425			// $arg_name has sub-levels
5426			if ((isset($arg_name))
5427			&& ((string)$arg_name != '')
5428			&& (strstr($arg_name, '][')))
5429			{
5430				// request for $arg_name['sub-element']
5431				if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: $arg_name is requesting sub-level array element(s),  use EVAL, $arg_name: '.serialize($arg_name).'<br />'); }
5432				$evaled = '';
5433				//$code = '$evaled = $this->a[$acctnum][\'args\']'.$arg_name.';';
5434				$code = '$evaled = $this->a[$acctnum]["args"]'.$arg_name.';';
5435				if ($this->debug_args_oop_access > 1) { $this->dbug->out(' * $code: '.$code.'<br />'); }
5436				eval($code);
5437				if ($this->debug_args_oop_access > 1) { $this->dbug->out(' * $evaled: '.$evaled.'<br />'); }
5438				if (isset($evaled))
5439				{
5440					if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: LEAVING returning $evaled: ['.$evaled.'] produced by $code: '.$code.'<br />'); }
5441					return True;
5442				}
5443			}
5444			// $arg_name has NO sub-levels
5445			elseif ((isset($this->a[$acctnum]['args'][$arg_name]))
5446			&& (!$extra_keys))
5447			{
5448				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 />'); }
5449				return True;
5450			}
5451			elseif ((isset($this->a[$acctnum]['args'][$arg_name][$extra_keys]))
5452			&& ($extra_keys))
5453			{
5454				if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: LEAVING (using EXTRA_KEYS) returning $this->a[$acctnum('.$acctnum.')][args][$arg_name][$extra_keys]: '.$this->a[$acctnum]['args'][$arg_name][$extra_keys].'<br />'); }
5455				return True;
5456			}
5457			// if we get here, it was not set
5458			if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_isset_arg: LEAVING returning False<br />'); }
5459			return False;
5460		}
5461
5462		/*!
5463		@function unset_arg
5464		@abstract unset a class variable
5465		@param $arg_name  (string)
5466		@param $acctnum  (int) OPTIONAL
5467		@result boolean True if $arg_name existed and was made unset, False on failure, such as $arg_name not
5468		being set in the first place. This function can not unset something that does not exist to begin with.
5469		@author Angles
5470		@discussion ?
5471		@access public
5472		*/
5473		function unset_arg($arg_name='', $acctnum='')
5474		{
5475			if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'unset_arg'); }
5476
5477			if ((!isset($acctnum))
5478			|| ((string)$acctnum == ''))
5479			{
5480				$acctnum = $this->get_acctnum();
5481			}
5482
5483			// $arg_name has sub-levels
5484			if ((isset($arg_name))
5485			&& ((string)$arg_name != '')
5486			&& (strstr($arg_name, '][')))
5487			{
5488				// make it equal a blank string
5489				$code = '$this->a[$acctnum]["args"]'.$arg_name.' = "";';
5490				if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg (sublevels): $arg_name is requesting sub-level array element(s),  use EVAL, $arg_name: '.serialize($arg_name).'<br />'); }
5491				if ($this->debug_args_oop_access > 1) { $this->dbug->out(' unset_arg (sublevels) * $code: '.$code.'<br />'); }
5492				eval($code);
5493				// unset it
5494				$code = 'unset($this->a[$acctnum]["args"]'.$arg_name.');';
5495				if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg (sublevels): $arg_name is requesting sub-level array element(s),  use EVAL, $arg_name: '.serialize($arg_name).'<br />'); }
5496				if ($this->debug_args_oop_access > 1) { $this->dbug->out(' unset_arg (sublevels) * $code: '.$code.'<br />'); }
5497				eval($code);
5498
5499				// now were we successful?
5500				$code = '$evaled = isset($this->a[$acctnum]["args"]'.$arg_name.');';
5501				if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg (sublevels): VERIFY that we succeeded, again use EVAL, $arg_name: '.serialize($arg_name).'<br />'); }
5502				$evaled = '';
5503				if ($this->debug_args_oop_access > 1) { $this->dbug->out(' unset_arg(sublevels): (VERIFY) * $code: '.$code.'<br />'); }
5504				eval($code);
5505				if ($this->debug_args_oop_access > 1) { $this->dbug->out(' unset_arg(sublevels): (VERIFY) * $evaled: '.$evaled.'<br />'); }
5506				if (isset($evaled))
5507				{
5508					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 $evaled is still set<br />'); }
5509					return False;
5510				}
5511				else
5512				{
5513					if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): unset_arg (sublevels): LEAVING returning True, because VERIFY showed $evaled is unset<br />'); }
5514					return True;
5515				}
5516			}
5517			elseif ((isset($arg_name))
5518			&& ((string)$arg_name != ''))
5519			{
5520				$this->a[$acctnum]['args'][$arg_name] = '';
5521				unset($this->a[$acctnum]['args'][$arg_name]);
5522				// return True to indicate success
5523				return True;
5524			}
5525			else
5526			{
5527				// return False to indicate invalid input $arg_name
5528				return False;
5529			}
5530		}
5531
5532		/*!
5533		@function get_arg_value
5534		@abstract Obtain the value of a given class variable, will handoff to helper functions if necessary.
5535		@param $arg_name (string)
5536		@param $acctnum (int) OPTIONAL
5537		@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.
5538		Currently no arg that requires a "special handler" uses an $extra_keys param anyway. DEPRECIATED use sublevels instead.
5539		@result (string, int, or array)
5540		@author Angles
5541		@discussion Some class variables, such as "mailsvr_namespace", have functions dedicated only to determining their value.
5542		In these cases this function will hand off the request directly to that specialized function. In other cases the
5543		class variable desired is a simple variable and its value is returned.
5544		@access public
5545		*/
5546		function get_arg_value($arg_name='',$acctnum='', $extra_keys='')
5547		{
5548			if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: ENTERING ($arg_name: ['.$arg_name.'], $acctnum: ['.$acctnum.'] )<br />'); }
5549			if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'get_arg_value'); }
5550
5551			if ((!isset($acctnum))
5552			|| ((string)$acctnum == ''))
5553			{
5554				$acctnum = $this->get_acctnum();
5555			}
5556
5557			if ((isset($arg_name))
5558			&& ((string)$arg_name != ''))
5559			{
5560				// ----  SPECIAL HANDLERS  ----
5561				if ($arg_name == 'mailsvr_callstr')
5562				{
5563					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 />'); }
5564					return $this->get_mailsvr_callstr($acctnum);
5565				}
5566				elseif ($arg_name == 'mailsvr_namespace')
5567				{
5568					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 />'); }
5569					return $this->get_mailsvr_namespace($acctnum);
5570				}
5571				elseif ($arg_name == 'mailsvr_delimiter')
5572				{
5573					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 />'); }
5574					return $this->get_mailsvr_delimiter($acctnum);
5575				}
5576				elseif ($arg_name == 'folder_list')
5577				{
5578					if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: LEAVING with HANDOFF to get_folder_list()<br />'); }
5579					return $this->get_folder_list($acctnum);
5580				}
5581				elseif ($arg_name == 'verified_trash_folder_long')
5582				{
5583					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 />'); }
5584					return $this->get_verified_trash_folder_long($acctnum);
5585				}
5586				/*
5587				elseif ($arg_name == 'folder')
5588				{
5589					if ($this->debug_args_oop_access > 1) { echo 'mail_msg(_wrappers): get_arg_value: request for backwards compat arg "folder"<br />'; }
5590					// look for foder in (1) msgball , then (2) fldball , then (3) return default value INBOX
5591					if ( (isset($this->a[$acctnum]['args']['msgball']['folder']))
5592					&& ($this->a[$acctnum]['args']['msgball']['folder'] != '') )
5593					{
5594						$folder_arg_decision = $this->a[$acctnum]['args']['msgball']['folder'];
5595						if ($this->debug_args_oop_access > 1) { echo 'mail_msg(_wrappers): get_arg_value: request for "folder" will use value in $this->a['.$acctnum.'][args][msgball][folder] = ['.$folder_arg_decision.']<br />'; }
5596					}
5597					elseif ( (isset($this->a[$acctnum]['args']['fldball']['folder']))
5598					&& ($this->a[$acctnum]['args']['fldball']['folder'] != '') )
5599					{
5600						$folder_arg_decision = $this->a[$acctnum]['args']['fldball']['folder'];
5601						if ($this->debug_args_oop_access > 1) { echo 'mail_msg(_wrappers): get_arg_value: request for "folder" will use value in $this->a['.$acctnum.'][args][fldball][folder] = ['.$folder_arg_decision.']<br />'; }
5602					}
5603					else
5604					{
5605						if ($this->debug_args_oop_access > 1) { echo 'mail_msg(_wrappers): get_arg_value: request for "folder" using "INBOX", found nothing in [args][msgball][folder] nor [args][fldball][folder]<br />'; }
5606						$folder_arg_decision = 'INBOX';
5607					}
5608					if ($this->debug_args_oop_access > 0) { echo 'mail_msg(_wrappers): get_arg_value: LEAVING, returning (backward compat) $folder_arg_decision ['.$folder_arg_decision.']<br />'; }
5609					return $folder_arg_decision;
5610				}
5611				*/
5612				// ----  STANDARD HANDLER (arg_name has sub-levels) ----
5613				elseif (strstr($arg_name, ']['))
5614				{
5615					if ($extra_keys)
5616					{
5617						// request for $arg_name['sub-element'] [$extra_keys]
5618						// represents code which typically is an array referencing a system/api property
5619						$code = '$evaled = $this->a[$acctnum]["args"]'.$arg_name.'[$extra_keys];';
5620					}
5621					else
5622					{
5623						// request for $arg_name['sub-element']
5624						// represents code which typically is an array referencing a system/api property
5625						//$code = '$evaled = $this->a[$acctnum][\'args\']'.$arg_name.';';
5626						$code = '$evaled = $this->a[$acctnum]["args"]'.$arg_name.';';
5627					}
5628					if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: $arg_name is requesting sub-level array element(s),  use EVAL, $arg_name: '.serialize($arg_name).'<br />'); }
5629					$evaled = '';
5630					if ($this->debug_args_oop_access > 1) { $this->dbug->out(' * $code: '.$code.'<br />'); }
5631					eval($code);
5632					if ($this->debug_args_oop_access > 1) { $this->dbug->out(' * $evaled: '.$evaled.'<br />'); }
5633					if ((isset($evaled))
5634					&& (!$extra_keys)
5635					&& (strstr($arg_name, 'folder_status_info')))
5636					{
5637						if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: sublevels with folder_status_info NEED values to be serialized since we eval it, so unserialize(base64_decode($evaled)) now: <br />'); }
5638						$evaled = unserialize(base64_decode($evaled));
5639					}
5640					if (isset($evaled))
5641					{
5642						if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers): get_arg_value: LEAVING returning $evaled: ['.$evaled.'] produced by $code: '.$code.'<br />'); }
5643						return $evaled;
5644					}
5645				}
5646				// ----  STANDARD HANDLER (arg_name has NO sub-levels) ----
5647				elseif (($extra_keys)
5648				&& (isset($this->a[$acctnum]['args'][$arg_name][$extra_keys])))
5649				{
5650					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 />'); }
5651					return $this->a[$acctnum]['args'][$arg_name];
5652				}
5653				elseif ((!$extra_keys)
5654				&& (isset($this->a[$acctnum]['args'][$arg_name])))
5655				{
5656					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 />'); }
5657					return $this->a[$acctnum]['args'][$arg_name];
5658				}
5659			}
5660
5661			// we ONLY get here if there's no data to return,
5662			// arg not set, or invalid input $arg_name
5663			// otherwise, anything that is sucessful returns and exist at that point, never gets to here
5664			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 />'); }
5665			return;
5666		}
5667
5668		/*!
5669		@function _direct_access_arg_value
5670		@abstract utility function for private use, used to bypass any special handlers, to directly access the "args" array.
5671		@param $arg_name  (string)
5672		@param $acctnum  (int) optional
5673		@result (mixed)
5674		@author Angles
5675		@discussion Esoteric utility function for specialized private use.
5676		@access private
5677		*/
5678		function _direct_access_arg_value($arg_name='',$acctnum='')
5679		{
5680			if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, '_direct_access_arg_value'); }
5681
5682			// PRIVATE - for use by internal functions
5683			if ((!isset($acctnum))
5684			|| ((string)$acctnum == ''))
5685			{
5686				$acctnum = $this->get_acctnum();
5687			}
5688			//	$this->set_arg_value('["folder_status_info"]["'.$ex_folder.'"]', $empty_array, $acctnum);
5689
5690			if (isset($this->a[$acctnum]['args'][$arg_name]))
5691			{
5692				return $this->a[$acctnum]['args'][$arg_name];
5693			}
5694			else
5695			{
5696				// arg not set, or invalid input $arg_name
5697				return;
5698			}
5699		}
5700
5701		/*!
5702		@function _get_arg_ref
5703		@abstract utility function for private use, used to bypass any special handlers, get a reference to something in
5704		the args array.
5705		@param $arg_name  (string)
5706		@param $acctnum  (int) optional
5707		@result (mixed) direct refernce to an arg value in memory, or a reference to a constant "##NOTHING##" on failure.
5708		@author Angles
5709		@discussion Esoteric utility function for specialized private use. Primary for use where speed is an issue.
5710		NOTE: Returning References requires the ampersand in BOTH the call to the function AND the function
5711		declaration here.
5712		@example
5713			function &find_var ($param)
5714			{
5715				...code...
5716				return $found_var;
5717			}
5718			$foo =& find_var ($bar);
5719			// that was straing from the phpmanual
5720		@access private
5721		*/
5722		function &_get_arg_ref($arg_name='',$acctnum='')
5723		{
5724			if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'get_arg_ref'); }
5725
5726			// PRIVATE - for use by internal functions
5727			if ((!isset($acctnum))
5728			|| ((string)$acctnum == ''))
5729			{
5730				$acctnum = $this->get_acctnum();
5731			}
5732
5733			if (isset($this->a[$acctnum]['args'][$arg_name]))
5734			{
5735				return $this->a[$acctnum]['args'][$arg_name];
5736			}
5737			else
5738			{
5739				// arg not set, or invalid input $arg_name
5740				//return '##NOTHING##';
5741				return $this->nothing;
5742			}
5743		}
5744
5745
5746		/*!
5747		@function get_arg_value_ref
5748		@abstract return reference to the value, but smart enough to make it if possible, then return reference.
5749		@param (string) $arg_name
5750		@param (int) $acctnum (optional)
5751		@result REFERENCE
5752		@discussion get a ref instead of a copy of a value, Try to use "special handler" to generate the
5753		value if it does not already exist, then again try to return the reference. Returns ref to
5754		$this->nothing on failure.
5755		@author Angles
5756		*/
5757		function &get_arg_value_ref($arg_name='',$acctnum='')
5758		{
5759			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 />'); }
5760			if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'get_arg_value_ref'); }
5761
5762			if ((!isset($acctnum))
5763			|| ((string)$acctnum == ''))
5764			{
5765				$acctnum = $this->get_acctnum();
5766			}
5767
5768			if (isset($this->a[$acctnum]['args'][$arg_name]))
5769			{
5770				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 />'); }
5771				return $this->a[$acctnum]['args'][$arg_name];
5772			}
5773			else
5774			{
5775				// try to geberate the arg value, then again try to return the ref
5776				if ($arg_name == 'mailsvr_callstr')
5777				{
5778					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 />'); }
5779					$this->get_mailsvr_callstr($acctnum);
5780
5781				}
5782				elseif ($arg_name == 'mailsvr_namespace')
5783				{
5784					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 />'); }
5785					$this->get_mailsvr_namespace($acctnum);
5786				}
5787				elseif ($arg_name == 'mailsvr_delimiter')
5788				{
5789					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 />'); }
5790					$this->get_mailsvr_delimiter($acctnum);
5791				}
5792				elseif ($arg_name == 'folder_list')
5793				{
5794					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 />'); }
5795					$this->get_folder_list($acctnum);
5796				}
5797				elseif ($arg_name == 'verified_trash_folder_long')
5798				{
5799					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 />'); }
5800					$this->get_verified_trash_folder_long($acctnum);
5801				}
5802				else
5803				{
5804					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 />'); }
5805					return $this->nothing;
5806				}
5807			}
5808			// ok, we tried to generate the arg value, were we successful?
5809			if (isset($this->a[$acctnum]['args'][$arg_name]))
5810			{
5811				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 />'); }
5812				return $this->a[$acctnum]['args'][$arg_name];
5813			}
5814			// fallback, we must have failed to find or make then find an arg value, so no reference to something we can not find
5815			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 />'); }
5816			return $this->nothing;
5817		}
5818
5819		/*!
5820		@function set_arg_value
5821		@abstract Sets a variable in the "args" array.  Should only be used for args that do not require specialized functions.
5822		@param $arg_name  (string)
5823		@param $this_value  (mixed)
5824		@param $acctnum  (int) optional
5825		@result (mixed)
5826		@author Angles
5827		@discussion ?
5828		@access public
5829		*/
5830		function set_arg_value($arg_name='', $this_value='', $acctnum='', $extra_keys='')
5831		{
5832			if ($this->debug_args_oop_access > 1) { $this->_get_arg_is_known($arg_name, 'set_arg_value'); }
5833			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 />'); }
5834
5835			if ((!isset($acctnum))
5836			|| ((string)$acctnum == ''))
5837			{
5838				$acctnum = $this->get_acctnum();
5839			}
5840
5841			// NOTE we have NO special handlers in this set code
5842			// ----  STANDARD HANDLER (arg_name has sub-levels) ----
5843			if ((isset($arg_name))
5844			&& ((string)$arg_name != '')
5845			&& (strstr($arg_name, '][')))
5846			{
5847				// NOTE $this_value BETTER BE SIMPLE string or int if you are doing it like this
5848				if (strstr($arg_name, 'folder_status_info'))
5849				{
5850					$code = '$this->a[$acctnum]["args"]'.$arg_name.' = "'.base64_encode(serialize($this_value)).'";';
5851				}
5852				elseif (is_string($this_value))
5853				{
5854					$code = '$this->a[$acctnum]["args"]'.$arg_name.' = "'.$this_value.'";';
5855				}
5856				else
5857				{
5858					$code = '$this->a[$acctnum]["args"]'.$arg_name.' = '.$this_value.';';
5859				}
5860				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),  use EVAL, $arg_name: '.serialize($arg_name).'<br />'); }
5861				if ($this->debug_args_oop_access > 1) { $this->dbug->out(' set_arg_value (sublevels) * $code: '.$code.'<br />'); }
5862				eval($code);
5863				// additional handling to get an array into an evaled string
5864				//if (is_array($this_value))
5865				//{
5866				//	$code = '$this->a[$acctnum]["args"]'.$arg_name.' = unserialize($this->a[$acctnum]["args"]'.$arg_name.');';
5867				//	if ($this->debug_args_oop_access > 1) { echo ' additional array handling: set_arg_value (sublevels) * $code: '.$code.'<br />'; }
5868				//	eval($code);
5869				//}
5870
5871				// now were we successful?
5872				$code = '$evaled = $this->a[$acctnum]["args"]'.$arg_name.';';
5873				if ($this->debug_args_oop_access > 1) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value (sublevels): VERIFY that we succeeded, again use EVAL, $arg_name: '.serialize($arg_name).'<br />'); }
5874				$evaled = '';
5875				if ($this->debug_args_oop_access > 1) { $this->dbug->out(' set_arg_value(sublevels): (VERIFY) * $code: '.$code.'<br />'); }
5876				eval($code);
5877				if ($this->debug_args_oop_access > 1) { $this->dbug->out(' set_arg_value(sublevels): (VERIFY) * $evaled (seriialized for this display): '.serialize($evaled).'<br />'); }
5878				if (isset($evaled))
5879				{
5880					if ($this->debug_args_oop_access > 0) { $this->dbug->out('mail_msg(_wrappers)('.__LINE__.'): set_arg_value (sublevels): LEAVING returning True, VERIFY indicates $evaled was indeded set<br />'); }
5881					return True;
5882				}
5883				else
5884				{
5885					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 $evaled was unset<br />'); }
5886					return False;
5887				}
5888			}
5889			// ----  STANDARD HANDLER (arg_name has NO sub-levels) ----
5890			elseif ((isset($arg_name))
5891			&& ((string)$arg_name != ''))
5892			{
5893				/*
5894				// can not do prep_folder_in because it calls "folder_lookup" which requires an active mailsvr stream login
5895				// ----  SPECIAL HANDLERS  ----
5896				//if ($arg_name == 'folder')
5897				//{
5898				//	$processed_value = $this->prep_folder_in($this_value);
5899				//	$this_value = $processed_value;
5900				//}
5901				*/
5902				// SET it, any special processing should be taken care just above here
5903				$this->a[$acctnum]['args'][$arg_name] = $this_value;
5904				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 />'); }
5905				// return True to indicate success
5906				return True;
5907			}
5908			else
5909			{
5910				// return False to indicate invalid input $arg_name
5911				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 />'); }
5912				return False;
5913			}
5914		}
5915
5916		/*!
5917		@function set_arg_array
5918		@abstract ?
5919		@param $arg_array_data  (array)
5920		@param $acctnum  (int) optional
5921		@result boolean
5922		@author Angles
5923		@discussion ?
5924		@access private
5925		*/
5926		function set_arg_array($arg_array_data='', $acctnum='')
5927		{
5928			if ((!isset($acctnum))
5929			|| ((string)$acctnum == ''))
5930			{
5931				$acctnum = $this->get_acctnum();
5932			}
5933
5934			$this->a[$acctnum]['args'] = array();
5935
5936			if ((isset($arg_array_data))
5937			&& (count($arg_array_data > 0)))
5938			{
5939				/*
5940				while(list($key,$value) = each($arg_array_data))
5941				{
5942					$this->set_arg_value($key, $arg_array_data[$key]);
5943				}
5944				*/
5945				$this->a[$acctnum]['args'] = $arg_array_data;
5946				// return True to indicate we filled, not just cleared
5947				return True;
5948			}
5949			else
5950			{
5951				// return False to indicate all we did was clear the args, no data was fed
5952				return False;
5953			}
5954		}
5955
5956		/*!
5957		@function get_all_args
5958		@abstract ?
5959		@param $acctnum  (int) optional
5960		@result (mixed)
5961		@author Angles
5962		@discussion ?
5963		@access private
5964		*/
5965		function get_all_args($acctnum='')
5966		{
5967			if ((!isset($acctnum))
5968			|| ((string)$acctnum == ''))
5969			{
5970				$acctnum = $this->get_acctnum();
5971			}
5972
5973			if (isset($this->a[$acctnum]['args']))
5974			{
5975				return $this->a[$acctnum]['args'];
5976			}
5977			else
5978			{
5979				// arg not set, or invalid input $arg_name
5980				return;
5981			}
5982		}
5983
5984		/*!
5985		@function unset_all_args
5986		@abstract ?
5987		@param $acctnum  (int) optional
5988		@result none
5989		@author Angles
5990		@discussion ?
5991		@access private
5992		*/
5993		function unset_all_args($acctnum='')
5994		{
5995			if ((!isset($acctnum))
5996			|| ((string)$acctnum == ''))
5997			{
5998				$acctnum = $this->get_acctnum();
5999			}
6000
6001			$this->a[$acctnum]['args'] = array();
6002		}
6003
6004
6005		// depreciated
6006		//function get_folder($acctnum='')
6007		//{
6008		//	return $this->get_arg_value('folder');
6009		//}
6010
6011		// depreciated
6012		//function get_msgnum($acctnum='')
6013		//{
6014		//	return $this->get_arg_value('["msgball"]["msgnum"]');
6015		//}
6016
6017		//function get_pref_layout($acctnum='')
6018		//{
6019		//	return $this->get_pref_value('layout', $acctnum);
6020		//}
6021
6022
6023	}  // end class mail_msg_wrappers
6024?>
6025