1<?php 2 /**************************************************************************\ 3 * AngleMail http://www.anglemail.org * 4 */ 5 /**************************************************************************\ 6 * AngleMail - E-Mail Message Processing Core Functions * 7 * This file written by Angelo "Angles" Puglisi <angles@aminvestments.com> * 8 * Copyright (C) 2001-2003 Angelo "Angles" Puglisi * 9 * ------------------------------------------------------------------------- * 10 * Originally Based on Aeromail by Mark Cushman <mark@cushman.net> * 11 * http://the.cushman.net/ * 12 * AngleMail appreciates the origins of the phpGroupWare email app from * 13 * from Cushman. The phpGroupWare email module was maintained by Angles * 14 * prior to developing into what is today known as AngleMail * 15 * ------------------------------------------------------------------------- * 16 * This file designed to work as part of a drop in module for phpGroupWare * 17 * http://www.phpgroupware.org * 18 * ------------------------------------------------------------------------- * 19 * This library is free software; you can redistribute it and/or modify it * 20 * under the terms of the GNU Lesser General Public License as published by * 21 * the Free Software Foundation; either version 2.1 of the License, * 22 * or any later version. * 23 * This library is distributed in the hope that it will be useful, but * 24 * WITHOUT ANY WARRANTY; without even the implied warranty of * 25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * 26 * See the GNU Lesser General Public License for more details. * 27 * You should have received a copy of the GNU Lesser General Public License * 28 * along with this library; if not, write to the Free Software Foundation, * 29 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * 30 \**************************************************************************/ 31 32 /* $Id: class.mail_msg_base.inc.php 15724 2005-02-15 06:48:06Z skwashd $ */ 33 34 /**************************************************************************\ 35 * STRUCTURES 36 \**************************************************************************/ 37 38 /*! 39 @class mail_dcom_holder 40 @abstract This class has one purpose, PHP3 compatibility. It simply holds the DataCommunications Object. 41 @author Angles 42 @discussion In tests PHP3 could not handle having an object as a simple array item. Therefor we had to 43 make this class to be the thing that holdes a class.dcom (DataCommunications) object. PHP4 had no problem 44 with the previous code but it was changed to this for backwards compatibility with PHP3, otherwise the 45 script exits with a parse error. Multiple account capability requires that any account can have a stream open 46 to its server at any time, independant of any other DataCommunications object, simce one account may be IMAP 47 while another account may be POP3. Using an array of these mail_dcom_holders, we can achieve this goal and 48 still have PHP3 compatibility. 49 @example 50 $GLOBALS['phpgw_dcom_'.$acctnum] = new mail_dcom_holder; 51 $GLOBALS['phpgw_dcom_'.$acctnum]->dcom = CreateObject("email.mail_dcom", $this_server_type); 52 now we have an array based structure where each array key can have a DataCommunications object of its 53 own, thereby allowing for multiple mail accounts of any type to be open simultaneously. 54 */ 55 class mail_dcom_holder 56 { 57 var $dcom = ''; 58 } 59 60 /**************************************************************************\ 61 * BASE MAIL CLASS 62 \**************************************************************************/ 63 /*! 64 @class mail_msg_base 65 @abstract One of three classes that combine to form the mail_msg object. The other classes are mail_msg_wrappers 66 and filename mail_msg_display which is actually called mail_msg because it is the final class extension loaded and 67 therefor bears the name we want the class as a whole to have, This class forms the common core of the email functionality, 68 @author Angles, and with some small remnants of Aeromail present 69 @discussion The three files and classes inter relate in the following way to end up with the mail_msg object 70 FIRST, include class mail_msg_base, then SECONDLY incluse mail_msg_wrappers extending mail_msg_base, 71 then THIRDLY include mail_msg which extends mail_msg_wrappers and, by inheritance, mail_msg_base 72 All functions that are at the heart of email functionality are in this class. This class is in the process of being further OOPd so 73 programmers can more easily use it without having to know abou the internal details. When multiple accounts are in 74 use, each active account can be accessed and controlled through this class. Each active account with a stream open to its 75 maiul server has its own DataCommunications object which used to be a part of this class but had to be moved elsewhere 76 for PHP3 compatibility, but still each DataCommunications object is in an array and is accessed via an account number 77 which is comtrolled in this class. This class handles organizing the preferences for each of the multiple accounts. In general, 78 a simple class var array keeps the multiple account information organized as a numbered array based on integer account number. 79 There are already many OOP methods that hide complexities from the programmer, such as the preference and arg access functions. 80 Many of those functions can optionally take an account number and foilder name, but if none is supplies the functions uses logic to 81 obtain valid account number and folder name for whatever account you are dealing with. There is extensice debug output available 82 by setting the various debug flags between 1 to 3, or to 0 if no debug is wanted. More documentation is provided for each function 83 in this class. 84 */ 85 86 /*! 87 @classvar known_external_args 88 @abstract List of GET POST variables that the email class and app is supposed to be aware of. 89 @discussion In a complex app such as email it becomes difficult to keep track of all the GET POST vars that the app 90 is expected to know about. Therefor, all GPC vars this class is expected to be aware of are listed here with an explanation 91 of what they do. 92 @param msgball (typed array) msgball "object" is a message-descriptive "object" or associative arrays that has all 93 important message reference data as array data, passed via URI (real or embedded). With multiple accounts enabled 94 most data such as a folder name or a message number, mean nothing by themselves because we do not know which 95 account they are supposed to apply to. Msgball typed array combines all necessary data, the acctnum, folder, msgnum, 96 and sometimes other data such as part_no, into one thing. Use msgball anytime you are dealing with messages, if you only 97 you do not care about individual email messages, such as when switching from one folder to another, then you can use the 98 fldball typed array, see below, which does not require such detailed information. 99 @param fldball (typed array) ldball "object" is an assiciative array of folder data passed via URI (real or embedded). 100 Use fldball when instructing this class to do things that are not specific to any particular message number, such as when 101 opening a stream to an account, or when switching from folder to folder. Generally the least amount of information necessary is 102 fldball[acctnum] (int)and fldball[folder] (string). This class know to expect less information in a fldball, whereas a msgball is expected 103 to contain very detailed information. 104 @param fldball_fake_uri (string in the form of a URI request) This is usually sourced from a folder combobox where 105 HTML only allows a single value to be passed, thus we make a string in the syntax of a URI to contain multiple data values 106 in that single HTML element, in this way we embed extra data in an otherwise very limiting HTML element. 107 Note: even php's POST vars array handling can not do anything with a HTML combobox option value. 108 See this example: POST data: 109 folder_fake_uri="fldball['folder']=INBOX&fldball['acctnum']=0" 110 Will be processed into this (using php function "parse_str()" to emulate URI GET behavior) 111 fldball[folder] => INBOX 112 fldball[acctnum] => 0 113 @param delmov_list (numbered array with each element being a msgball Fake URI string) 114 Used with mail message moves, appends, and deletes, holds the "from" data, as in move this message "from" 115 here to... and the "to" destimation data is contained in the to_fldball, see below. 116 This comes from the checkbox form data in uiindex.index page, where multiple boxes may be checked 117 but the POST data is limited to a simple string per checkbox, so additional information is embedded in delmov_list 118 and converted to an associative array via php function "parse_str". This is typically used to move or delete a list of 119 messages, but since this array are all msgball items, mail functions such as append and move are no longer limited 120 to one account or one folder, the msgball array can instruct the class to move messages beween different mail accounts 121 of different types and to and from any folder therein. 122 @param to_fldball_fake_uri (string in the form of a URI get request) Used to pass complex data deom a combo box 123 which is limited to submitting only a single string. This is generally used to describe the destination acctnum and folder 124 for message moves, appends, and deletes. Php function parse_str is used to make this string data into the typed array 125 to_fldball, see below. 126 @param to_fldball (typed array with emements [acctnum] as int, and [folder] as string) Used to describe the destination 127 in mail moves, or appends and it is formed by using php function parse_str on a POST submitted arg "to_fldball_fake_uri". 128 @param move_postmove_goto ? When moving a message while viewing it in the view message page, this var will be passed 129 used to tell us what to show the user after we do the move, it will be a URI string that begins with "menuaction". 130 @param sort ? 131 @param order ? 132 @param start these three vars preserve the users current choice of sort and order between page views and message actions, 133 and start is used to help the app in nextmatches behavior. 134 @param td (int) ? 135 @param tm (int) ? 136 @param tf (string) these three vars are used to === REPORT ON MOVES/DELETES === 137 td = total deleted ; tm = total moved, tm used with tf, folder messages were moved to. 138 usage: (outgoing) class.boaction: when action on a message is taken, report info is passed in these. 139 (in) uiindex.index: here the report is diaplayed above the message list, used to give user feedback. 140 Generally these are in the URI (GET var, not a form POST var) 141 @param what (string) === MOVE/DELETE MESSAGE INSTRUCTIONS === 142 Possible Values: (outgoing) class.uiindex "move", "delall", used with delmov_list to move or delete messages. 143 AND with "to_fldball" which is the destination acctnum and folder for the move. 144 (outgoing) uimessage: "delete" used with a msgball to delete or move an individual message directly from the view 145 message page. 146 (in) class.boaction: instruction on what action to preform on 1 or more message(s) (move or delete) 147 NOTE: the destination for the move is described in "delmov_list" which is a msgball list of 148 msgball's which are message-descriptive "objects" or associative arrays that have all 149 the necessary data on each message that is to be deleted or moved. 150 The iuindex.index page uses the same form with different submit buttons (what) 151 so the "delmov_list" is applicable to either deleting or moving messages depending on which submit button was clicked. 152 @param action (string) used for === INSTRUCTIONS FOR ACTION ON A MESSAGE OR FOLDER === 153 (a) (out and in) uifolder: used with "target_folder" and (for renaming) "source_folder" 154 and has instructions to add/delete/rename folders: create(_expert), delete(_expert), rename(_expert) 155 where "X_expert" indicates do not modify the target_folder, the user know about of namespaces and delimiters. 156 (b) uicompose: can be "reply" "replyall" "forward" which is passed on to bosend 157 (c) bosend: when set to "forward" and used with "fwd_proc" instructs on how to construct the SMTP mail 158 @param msgball[part_no] (string) representing a specific MIME part number (example "2.1.2") within a multipart message. 159 Used by (a) uicompose: used in combination with msgball, (b) boaction.get_attach: used in combination with msgball. 160 @param encoding (string) possible values "base64" "qprint" Used by 161 (a) uicompose: if replying to, we get the body part to reply to, it may need to be un-qprint-ed, and 162 (b) boaction.get_attach: appropriate decoding of the part to feed to the browser. 163 @param fwd_proc (string) Possible Values "encapsulation", "pushdown (not yet supported)" Used as 164 (outgoing) uimessage much detail is known about the messge, there the forward proc method is determined. Used by: 165 (a) uicompose: used with action = forward, (outgoing) passed on to bosend, 166 (b) bosend: used with action = forward, instructs on how the SMTP message should be structured. 167 @param name (string) the name of an attachment 168 @param type (string) the mime type of an attachment 169 @param subtype (string) the mime subtype of an attachment. 170 These 3 args comprise this info is passed to the browser to help the browser know what to do with the part a.k.a. attachment. 171 (outgoing) uimessage: "name" is set in the link to the addressbook, it's the actual "personal" name part of the email address 172 and boaction.get_attach: the name of the attachment. 173 Note these params are NOT part of the msgball array, because with the other data already in msgball, it should be obvious 174 what these items are supposed to apply to. 175 @param target_fldball (typed array) and 176 @param source_fldball_fake_uri (string of type URI Get) used to make the source_fldball param, see below. 177 @param source_fldball (typed array) used for === FOLDER ADD/DELETE/RENAME & DISPLAY === 178 Note param source_fldball is used in renaming folders only. Used for: 179 (outgoing) and (in) bofolder: used with "action" to add/delete/rename a mailbox folder, 180 where "action" can be: create, delete, rename, create_expert, delete_expert, rename_expert. 181 @param show_long (string "true" if set) Used by uifolder it is set there and sent back to itself uifolder. 182 If set: indicates to show 'long' folder names with namespace and delimiter NOT stripped off. 183 @param to (string) part of === COMPOSE VARS === 184 @param cc (string) ? 185 @param bcc (string) ? 186 @param body (string) These compose vars, as most commonly NOT used with "mailto" have following usage 187 (note if used with "mailto", less common, then see "mailto" below). Used as: 188 (outgoing) uiindex, uimessage: any click on a clickable email address in these pages, will call uicompose 189 passing "to" (possibly in rfc long form address), 190 (outgoing) uimessage: when reading a message and you click reply, replyall, or forward calls uicompose with EITHER 191 (1) a msgball so that compose gets all needed info, (more effecient than passing all those GPC args) OR 192 (2) to,cc,subject,body may be passed. 193 (outgoing) uicompose: ALL contents of input items to, cc, subject, body, etc... are passed as GPC args to bosend. 194 (in) (a) compose.php: text that should go in to and cc (and maybe subject and body) text boxes 195 are passed as incoming GPC args, and 196 (in) (b) bosend: (fill me in - I got lazy) 197 @param sender (string) Less Common Usage RFC says use header "Sender" ONLY WHEN the sender of the 198 email is NOT the author, this is somewhat rare. 199 @param req_notify This is a recent addition to request notification of delivery for the message being composed 200 @param attach_sig (boolean True is set, or not present or unset if False) USAGE 201 (outgoing) uicompose: if checkbox attach sig is checked, this is passed as GPC var to bosent, and 202 (in) bosend: indicate if message should have the user's "sig" added to the message. 203 @param msgtype (string) DEPRECIATED, flag to tell phpgw to invoke "special" custom processing of the message 204 extremely rare, may be obsolete (not sure), most implementation code is commented out. Used as: 205 (outgoing) currently NO page actually sets this var, and 206 (a) bosend: will add the flag, if present, to the header of outgoing mail, and 207 (b) bomessage identify the flag and call a custom proc. 208 @param personal (string) the name part of an email address,m used with the following param 209 @param mailto (string) === MAILTO URI SUPPORT === USAGE 210 (in and out) bocompose: support for the standard mailto html document mail app call can be used with the typical compose 211 vars (see above), indicates that to, cc, and subject should be treated as simple MAILTO args. 212 @param no_fmt (boolean) === MESSAGE VIEWING MOD === Usage 213 (in and outgoing) uimessage: will display plain body parts without any html formatting added. 214 @param html_part (string) === VIEW HTML INSTRUCTIONS === 215 actually a pre-processed HTML/RELATED MIME part with the image ID's swapped with msgball data 216 for each "related" image, so the MUA may obtain the images from the email server using these msgball details. 217 @param force_showsize (boolean) === FOLDER STATISTICS - CALCULATE TOTAL FOLDER SIZE. 218 As a speed up measure, and to reduce load on the IMAP server, there is an option to skip the calculating of the total folder size 219 if certain conditions are met, such as more then 100 messages in a folder, the user may request an override of this for 1 page view. 220 @param mlist_set === SEARCH RESULT MESSAGE SET === DEPRECIATED - not yet fixed. 221 @param folder (string) most often a part of a msgball or fldball, the function begin_request will obtain the folder value from 222 (1) args_array, the args passed directly to the begin_request function in the form of folder => Sent, if any, 223 or (2) a fldball GPC or (3) a msgball GPC, or (4) default "INBOX". === THE FOLDER ARG discussion === 224 Folder name is used in almost every procedure, IMAP can be logged into only one folder at a time and POP3 225 has only one folder anyway (INBOX), INBOX is the assumed default value for "folder". 226 @expunge_folders (array) list of folder names (not urlencoded) that need to be expunged, if any, for an account 227 @param ex_acctnum (int) all preference handling of extra accounts passes this as the account number "ex" = "extra". 228 @param COMPLETE_ME, are there more GPC args we use in the email app? 229 */ 230 231 class mail_msg_base 232 { 233 // ---- account - an array where key=mail_account and value=all_class_vars for that account 234 var $a = array(); 235 var $acctnum = 0; 236 var $fallback_default_acctnum = 0; 237 238 // this object is 3 files, each an object "extending" the other, this prevents 3 constructor calls 239 var $been_constructed = False; 240 // data storage for caching functions moved to SO object 241 var $so = '##NOTHING##'; 242 243 // ---- compat for PHP < 4.1 vs. > 4.2 244 var $ref_GET = '##NOTHING##'; 245 var $ref_POST = '##NOTHING##'; 246 var $ref_SERVER = '##NOTHING##'; 247 var $ref_FILES = '##NOTHING##'; 248 var $ref_SESSION = '##NOTHING##'; 249 250 // ---- args that are known to be used for email 251 // externally filled args, such as thru GPC values, or xmlrpc call 252 var $known_external_args = array(); 253 // args that are typically set and controlled internally by this class 254 var $known_internal_args = array(); 255 // ---- class-wide settings - not account specific 256 // some functions use $not_set instead of actuallt having something be "unset" 257 var $not_set = '-1'; 258 // EXPERIMENTAL: functions required to return a refernce can return a ref to this to indicate a failure 259 var $nothing = '##NOTHING##'; 260 // EXPERIMENTAL: straight delete (not a move to trash) use this psudo acct. folder name to fill the "to_fldball" 261 var $del_pseudo_folder = '##DELETE##'; 262 // when uploading files for attachment to outgoing mail, use this location in the filesystem 263 var $att_files_dir; 264 // a limited group of folder related langs are handled here, most others are page specific not here 265 var $common_langs=array(); 266 // *maybe* future use - does the client's browser support CSS 267 var $browser = 0; 268 // use message UIDs instead of "message sequence numbers" in requests to the mail server 269 var $force_msg_uids = True; 270 // phpgw 0.9.16 was last for old template system, after that is xslt, make note of version below 271 var $phpgw_before_xslt = '-1'; 272 // raw prefs, before we process them to extract extra acct and/or filters data, not of much use 273 var $unprocessed_prefs=array(); 274 // raw filters array for use by the filters class, we just put the data here, that is all, while collecting other prefs 275 var $raw_filters=array(); 276 // move URI data is buffered to here, then executed at one time 277 var $buffered_move_commmands = array(); 278 // since move URIs are added in a speed sensitive loop, manually track the count, avoids repeated count() commands 279 var $buffered_move_commmands_count=0; 280 // delete URI data is buffered to here, then executed at one time (FUTURE) 281 var $buffered_delete_commmands = array(); 282 // I think crypto var this is no longer used, uses global crypto now I think (which does little anyway, w/o mcrypt) 283 //var $crypto; 284 285 // reply messages get this "quoting" prefix to each line, see bocompose and bosend 286 //var $reply_prefix = '>'; 287 var $reply_prefix = '> '; 288 //var $reply_prefix = '| '; 289 290 // ---- Data Caching ---- 291 var $use_cached_prefs = True; 292 //var $use_cached_prefs = False; 293 294 // (A) session data caching in appsession, for data that is temporary in nature 295 // right now this means msgball_list in appsession, and a bunch of stuff we generate (mailsvr_str) stored in L1 cache 296 // also tries to appsession cache the "processed prefs" during begin_request (NOTE: expire this on pref subit so new prefs actually take effect) 297 var $session_cache_enabled=True; 298 //var $session_cache_enabled=False; 299 300 // ---- session cache runthru without actuall saving data to appsession (for debugging only, rarely useful anyway) 301 //var $session_cache_debug_nosave = True; 302 var $session_cache_debug_nosave = False; 303 304 // ---- session cache uses "events" to directly "freshen" the cache without the mailserver 305 // NOTE msgball_list is ALWAYS appsession cached in "session_cache_enabled" even if "session_cache_extreme" is false, 306 // repeat: msgball_list is still appsession cached in non-extreme mode as long as "session_cache_enabled" is True. 307 // also, note that folder_info is ONLY appsession cached in extreme-mode, BUT folder_info is only L1 cached in non-extreme mode 308 var $session_cache_extreme = True; 309 //var $session_cache_extreme = False; 310 311 // ---- Private Table Caching ---- data store is migrating to anglemails own DB table, should we use it? 312 // value will be double checked to make sure the table is present, if not present, it does to False 313 var $use_private_table = True; 314 //var $use_private_table = False; 315 316 // ---- how long to assume appsession cached "folder_status_info" is deemed VALID in seconds 317 // ---- only applies if "session_cache_extreme" is true 318 // ---- please no lower than 10 seconds 319 var $timestamp_age_limit = 240; 320 321 // EXTRA ACCOUNTS 322 // used for looping thru extra account data during begin request 323 var $ex_accounts_count = 0; 324 // extra_accounts[X][acctnum] = integer 325 // extra_accounts[X][status] = empty | enabled | disabled 326 var $extra_accounts = array(); 327 // same as above but includes the default account, makes checking streams easier 328 var $extra_and_default_acounts = array(); 329 330 // svc_debug object goes here in the constructor 331 var $dbug='##NOTHING##'; 332 333 // DEBUG FLAGS generally take int 0, 1, 2, or 3 334 var $debug_logins = 0; 335 var $debug_session_caching = 0; 336 // email so object debug level 337 var $debug_so_class = 0; 338 // debuugging level3 can lead to dumping is msgball_list may have thousands of elements 339 var $debug_allow_magball_list_dumps = False; 340 //var $debug_allow_magball_list_dumps = True; 341 // these "events" are used to alter cached data to keep it reasonably in sync with the server, so we do not need 342 // to contact the server again if we can emulate the data change resulting from an event. 343 var $debug_events = 0; 344 var $debug_wrapper_dcom_calls = 0; 345 var $debug_accts = 0; 346 var $debug_args_input_flow = 0; 347 var $debug_args_oop_access = 0; 348 var $debug_args_special_handlers = 0; 349 var $debug_index_page_display = 0; 350 // this is just being implemented 351 var $debug_message_display = 0; 352 // dormant code, "longterm_caching" currently OBSOLETE 353 var $debug_longterm_caching = 0; 354 //var $skip_args_special_handlers = 'get_mailsvr_callstr, get_mailsvr_namespace, get_mailsvr_delimiter, get_folder_list'; 355 //var $skip_args_special_handlers = 'get_folder_list'; 356 var $skip_args_special_handlers = ''; 357 358 /*! 359 @function mail_msg_base 360 @abstract CONSTRUCTOR place holder, does nothing 361 */ 362 function mail_msg_base() 363 { 364 if (($this->debug_logins > 0) && (is_object($this->dbug->out))) { $this->dbug->out('mail_msg('.__LINE__.'): *constructor*: $GLOBALS[PHP_SELF] = ['.$GLOBALS['PHP_SELF'].'] $this->acctnum = ['.$this->acctnum.'] get_class($this) : "'.get_class($this).'" ; get_parent_class($this) : "'.get_parent_class($this).'"<br />'); } 365 if ($this->debug_logins > 0) { echo 'mail_msg('.__LINE__.'): *constructor*: $GLOBALS[PHP_SELF] = ['.$GLOBALS['PHP_SELF'].'] $this->acctnum = ['.$this->acctnum.'] get_class($this) : "'.get_class($this).'" ; get_parent_class($this) : "'.get_parent_class($this).'"<br />'; } 366 return; 367 } 368 369 /*! 370 @function initialize_mail_msg 371 @abstract the real CONSTRUCTOR needs to be called by name. 372 @discussion This used to be called in the final extends file to this aggregrate class. 373 NEW now called only from bootstrap class, because the preferences API class keeps constructing 374 this object for every account it makes preferences for, I would change that but changing the API 375 is like moving a mountain, so I remove all auto constructor functions and make this have to 376 be called explicitly to stop useless runthroughs caused by preferences API. 377 */ 378 function initialize_mail_msg() 379 { 380 if ($this->been_constructed == True) 381 { 382 // do not run thru this again, probably one of the "extends" objects call this 383 return; 384 } 385 // Set this so we do not run thru this again 386 $this->been_constructed = True; 387 388 // ... OK ... now we actually do the CONSTRUCTOR 389 // svc_debug object goes here 390 if ($this->dbug == '##NOTHING##') 391 { 392 $this->dbug = CreateObject('email.svc_debug'); 393 } 394 395 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg.initialize_mail_msg('.__LINE__.'): ENTERING manual *constructor*: $GLOBALS[PHP_SELF] = ['.$GLOBALS['PHP_SELF'].'] $this->acctnum = ['.$this->acctnum.'] get_class($this) : "'.get_class($this).'" ; get_parent_class($this) : "'.get_parent_class($this).'"<br />'); } 396 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.initialize_mail_msg('.__LINE__.'): manual *constructor*: $this->acctnum = ['.$this->acctnum.'] ; $this->a DUMP:', $this->a); } 397 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.initialize_mail_msg('.__LINE__.'): manual *constructor*: extra data $p1 (if provided): '.serialize($p1).'<br />'); } 398 399 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.initialize_mail_msg('.__LINE__.'): manual *constructor*: checking and or setting GET and POST reference based on PHP version<br />'); } 400 // make GPC reference for php versions < 4.1 and > 4.2 401 // since this constructor is apparently called many times 402 // during the script run (not sure why) we check if we've already done it first 403 $force_GPC_new = False; 404 //$force_GPC_new = True; 405 if (($this->ref_GET == '##NOTHING##') 406 || ($this->ref_POST == '##NOTHING##') 407 || ($this->ref_SERVER == '##NOTHING##') 408 || ($this->ref_FILES == '##NOTHING##') 409 || ($this->ref_SESSION == '##NOTHING##')) 410 { 411 // set this to force using the new superglobals 412 if ($force_GPC_new == True) 413 { 414 $this->ref_GET = &$_GET; 415 $this->ref_POST = &$_POST; 416 $this->ref_SERVER = &$_SERVER; 417 $this->ref_FILES = &$_FILES; 418 $this->ref_SESSION = &$_SESSION; 419 } 420 // make the appropriate reference (pointer) based on php version 4.1.0 421 elseif ($this->minimum_version("4.1.0")) 422 { 423 $this->ref_GET = &$_GET; 424 $this->ref_POST = &$_POST; 425 $this->ref_SERVER = &$_SERVER; 426 $this->ref_FILES = &$_FILES; 427 $this->ref_SESSION = &$_SESSION; 428 } 429 // fallback to the "old way" 430 else 431 { 432 $this->ref_GET = &$GLOBALS['HTTP_GET_VARS']; 433 $this->ref_POST = &$GLOBALS['HTTP_POST_VARS']; 434 $this->ref_SERVER = &$GLOBALS['HTTP_SERVER_VARS']; 435 //$this->ref_FILES = &$HTTP_POST_FILES; 436 $this->ref_FILES = &$GLOBALS['HTTP_POST_FILES']; 437 $this->ref_SESSION = &$GLOBALS['HTTP_SESSION_VARS']; 438 } 439 } 440 441 // SO object has data storage functions 442 if ($this->so == '##NOTHING##') 443 { 444 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.initialize_mail_msg('.__LINE__.'): manual *constructor*: creating sub SO object "so_mail_msg"<br />'); } 445 $this->so = CreateObject('email.so_mail_msg'); 446 } 447 448 // Data Store Double Check 449 // TEMPORARY ONLY DURING MIGRATION AND TABLE DEVELOPMENT 450 if ($this->use_private_table) 451 { 452 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.initialize_mail_msg('.__LINE__.'): manual *constructor*: checking if "so_am_table_exists"<br />'); } 453 if ($this->so->so_am_table_exists() == False) 454 { 455 $this->use_private_table = False; 456 } 457 } 458 459 // UNDER DEVELOPMENT when to use cached preferences 460 if ($this->use_cached_prefs == True) 461 { 462 // any preferences page menuaction is a NO NO to cached prefs 463 if (stristr($this->ref_GET['menuaction'], 'preferences.')) 464 { 465 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.initialize_mail_msg('.__LINE__.'): manual *constructor*: string "preferences." is in menuaction so NO CACHED PREFS, setting $this->use_cached_prefs to False<br />'); } 466 $this->use_cached_prefs = False; 467 } 468 } 469 470 // UNDER DEVELOPMENT bulk data query from AngleMail DB 471 // only necessary to grab huge bulk data for INDEX page 472 // and some other menuactions too, but we will add more later 473 if ((stristr($this->ref_GET['menuaction'], 'email.uiindex')) 474 || (stristr($this->ref_GET['menuaction'], 'email.uimessage.message'))) 475 { 476 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.initialize_mail_msg('.__LINE__.'): manual *constructor*: calling $this->so->so_prop_use_group_data(True)<br />'); } 477 //$this->so->use_group_data = True; 478 $this->so->so_prop_use_group_data(True); 479 } 480 else 481 { 482 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.initialize_mail_msg('.__LINE__.'): manual *constructor*: calling $this->so->so_prop_use_group_data(False)<br />'); } 483 //$this->so->use_group_data = False; 484 $this->so->so_prop_use_group_data(False); 485 } 486 487 // trying this new thing for template porting issues 488 // relfbecker recommends NOT using a version test for xslt check 489 if ($this->phpgw_before_xslt == '-1') 490 { 491 if (is_object($GLOBALS['phpgw']->xslttpl)) 492 { 493 $this->phpgw_before_xslt = False; 494 } 495 else 496 { 497 $this->phpgw_before_xslt = True; 498 } 499 } 500 /* 501 // relfbecker recommends NOT using a version test for xslt check 502 if ($this->phpgw_before_xslt == '-1') 503 { 504 $this_ver = $GLOBALS['phpgw_info']['server']['versions']['phpgwapi']; 505 $pre_xslt_ver = '0.9.14.0.1.1'; 506 if (!$this_ver) 507 { 508 // damn stupid fallback if the api moves the version to another place 509 $this->phpgw_before_xslt = True; 510 } 511 // this is a function in phpgwapi "common_functions" file for phpgw 0.9.15+ 512 elseif (function_exists(amorethanb)) 513 { 514 if (amorethanb($this_ver, $pre_xslt_ver)) 515 { 516 // this phpgw version is after the switch to xslt templates 517 $this->phpgw_before_xslt = False; 518 } 519 else 520 { 521 // this phpgw version is NOT in the xslt era 522 $this->phpgw_before_xslt = True; 523 } 524 } 525 else 526 { 527 if ($GLOBALS['phpgw']->common->cmp_version_long($this_ver, $pre_xslt_ver)) 528 { 529 // this phpgw version is after the switch to xslt templates 530 $this->phpgw_before_xslt = False; 531 } 532 else 533 { 534 // this phpgw version is NOT in the xslt era 535 $this->phpgw_before_xslt = True; 536 } 537 } 538 } 539 */ 540 541 $this->known_external_args = array( 542 // === NEW GPC "OBJECTS" or Associative Arrays === 543 // msgball "object" is a message-descriptive "object" or associative arrays that has all 544 // important message reference data as array data, passed via URI (real or embedded) 545 'msgball', 546 // fldball "object" is an assiciative array of folder data passed via URI (real or embedded) 547 'fldball', 548 549 // === NEW HTTP POST VARS Embedded Associative Arrays === 550 // "fldball_fake_uri" HTTP_POST_VARS varsion of a URI GET "fldball" 551 // usually sourced from a folder combobox where HTML only allows a single value to be passed 552 // thus we make a string in the syntax of a URI to contain multiple data values in that single HTML element 553 // in this way we embed extra data in an otherwise very limiting HTML element 554 // note: even php's POST vars array handling can not do anything with a HTML combobox option value. 555 // example: POST data 556 // folder_fake_uri="fldball['folder']=INBOX&fldball['acctnum']=0" 557 // Will be processed into this (using php function "parse_str()" to emulate URI GET behavior) 558 // fldball[folder] => INBOX 559 // fldball[acctnum] => 0 560 'fldball_fake_uri', 561 562 // "delmov_list_fake_uri" 563 // comes from the checkbox form data in uiindex.index page, where multiple 564 // boxes may be checked but the POST data is limited to a simple string per checkbox, 565 // so additional information is embedded in delmov_list_fake_uri and converted to an 566 // associative array via php function "parse_str" 567 //'delmov_list_fake_uri', 568 'delmov_list', 569 // if moving msgs, this is where they should go 570 'to_fldball_fake_uri', 571 'to_fldball', 572 // when moving a message while viewing it in the uimessage page, this var will be passed 573 // telling us what to show the user after we do the move, it will be a URI string that begins with "menuaction" 574 'move_postmove_goto', 575 // === SORT/ORDER/START === 576 // if sort,order, and start are sometimes passed as GPC's, if not, default prefs are used 577 'sort', 578 'order', 579 'start', 580 581 // newsmode is NOT yet implemented 582 //'newsmode', 583 584 // === REPORT ON MOVES/DELETES === 585 // ---- td, tm: integer ---- 586 // ---- tf: string ---- 587 // USAGE: 588 // td = total deleted ; tm = total moved, tm used with tf, folder messages were moved to 589 // (outgoing) class.boaction: when action on a message is taken, report info is passed in these 590 // (in) index.php: here the report is diaplayed above the message list, used to give user feedback 591 // generally these are in the URI (GET var, not a form POST var) 592 'td', 593 'tm', 594 'tf', 595 596 // === MOVE/DELETE MESSAGE INSTRUCTIONS === 597 // ---- what: string ---- 598 // USAGE: 599 // (outgoing) class.uiindex "move", "delall" 600 // used with msglist (see below) an array (1 or more) of message numbers to move or delete 601 // AND with "toacctnum" which is the acctnum associated with the "tofolder" 602 // (outgoing) message.php: "delete" used with msgnum (see below) what individual message to delete 603 // (in) class.boaction: instruction on what action to preform on 1 or more message(s) (move or delete) 604 'what', 605 //'tofolder', 606 //'toacctnum', 607 // *update* 608 // both "tofolder" and "toacctnum" are incorporated into "delmov_list" which is a msgball list of 609 // msgball's which are message-descriptive "objects" or associative arrays that have all 610 // the necessary data on each message that is to be deleted or moved. 611 // the iuindex.index page uses the same form with different submit buttons (what) 612 // so the "delmov_list" is applicable to either deleting or moving messages depending 613 // on which submit button was clicked 614 // 'delmov_list', (see above) 615 616 // (passed from class.uiindex) this may be an array of numbers if many boxes checked and a move or delete is called 617 //'msglist', 618 619 // *update* "msglist" is being depreciated! 620 621 // === INSTRUCTIONS FOR ACTION ON A MESSAGE OR FOLDER === 622 // ---- action: string ---- 623 // USAGE: 624 // (a) (out and in) folder.php: used with "target_folder" and (for renaming) "source_folder" 625 // instructions to add/delete/rename folders: create(_expert), delete(_expert), rename(_expert) 626 // where "X_expert" indicates do not modify the target_folder, the user know about of namespaces and delimiters 627 // (b) compose.php: can be "reply" "replyall" "forward" 628 // passed on to send_message.php 629 // (c) send_message.php: when set to "forward" and used with "fwd_proc" instructs on how to construct 630 // the SMTP mail 631 'action', 632 // ---- orig_action: string ---- 633 // USAGE: 634 // preserves the original "action" of the compose page because new and forward body lines 635 // need to be shorter then reply to we need to remember the desired "action" and store it here 636 // also used to preserve this thru the spell check process too 637 // initially we only put this only in the GET part of GPC 638 // why is this different, "orig_action" can have the value "new" meaning new mail 639 // whereas plain old "action" can not tell us of a new mail situation, not right now anyway, 640 // so the "new" value can be preserved to the send code and also thru the spell page and back too 641 'orig_action', 642 643 // === MESSAGE NUMBER AND MIME PART REFERENCES === 644 // *update* now in msgball 645 // msgnum: integer 646 // USAGE: 647 // (a) class.boaction, called from from message.php: used with "what=delete" to indicate a single message for deletion 648 // (b) compose.php: indicates the referenced message for reply, replyto, and forward handling 649 // (c) boaction.get_attach: the msgnum of the email that contains the desired body part to get 650 // *update* now in msgball 651 //'msgnum', 652 653 // ---- part_no: string ---- 654 // representing a specific MIME part number (example "2.1.2") within a multipart message 655 // (a) compose.php: used in combination with msgnum 656 // (b) boaction.get_attach: used in combination with msgnum 657 658 // *update* now in msgball 659 //'part_no', 660 661 // ---- encoding: string ---- 662 // USAGE: "base64" "qprint" 663 // (a) compose.php: if replying to, we get the body part to reply to, it may need to be un-qprint'ed 664 // (b) boaction.get_attach: appropriate decoding of the part to feed to the browser 665 'encoding', 666 667 // ---- fwd_proc: string ---- 668 // USAGE: "encapsulation", "pushdown (not yet supported 9/01)" 669 // (outgoing) message.php much detail is known about the messge, there the forward proc method is determined 670 // (a) compose.php: used with action = forward, (outgoing) passed on to send_message.php 671 // (b) send_message.php: used with action = forward, instructs on how the SMTP message should be structured 672 'fwd_proc', 673 // ---- name, type, subtype: string ---- 674 // the name, mime type, mime subtype of the attachment 675 // this info is passed to the browser to help the browser know what to do with the part 676 // (outgoing) message.php: "name" is set in the link to the addressbook, it's the actual "personal" name part of the email address 677 // boaction.get_attach: the name of the attachment 678 679 // NOT in msgball, with the other data already in msgball, it should be obvious 680 // what these items are ment to apply to 681 'name', 682 'type', 683 'subtype', 684 685 // === FOLDER ADD/DELETE/RENAME & DISPLAY === 686 // ---- "target_folder" , "source_folder" (source used in renaming only) ---- 687 // (outgoing) and (in) folder.php: used with "action" to add/delete/rename a mailbox folder 688 // where "action" can be: create, delete, rename, create_expert, delete_expert, rename_expert 689 //'target_folder', 690 'target_fldball', 691 //'source_folder', 692 'source_fldball', 693 'source_fldball_fake_uri', 694 // ---- show_long: unset / true ---- 695 // folder.php: set there and sent back to itself 696 // if set - indicates to show 'long' folder names with namespace and delimiter NOT stripped off 697 'show_long', 698 699 // === COMPOSE VARS === 700 // as most commonly NOT used with "mailto" then the following applies 701 // (if used with "mailto", less common, then see "mailto" below) 702 // USAGE: 703 // ---- to, cc, body, subject: string ---- 704 // (outgoing) index.php, message.php: any click on a clickable email address in these pages 705 // will call compose.php passing "to" (possibly in rfc long form address) 706 // (outgoing) message.php: when reading a message and you click reply, replyall, or forward 707 // calls compose.php with EITHER 708 // (1) a msgnum ref then compose gets all needed info, (more effecient than passing all those GPC args) OR 709 // (2) to,cc,subject,body may be passed 710 // (outgoing) compose.php: ALL contents of input items to, cc, subject, body, etc... 711 // are passed as GPC args to send_message.php 712 // (in) (a) compose.php: text that should go in to and cc (and maybe subject and body) text boxes 713 // are passed as incoming GPC args 714 // (in) (b) send_message.php: (fill me in - I got lazy) 715 'to', 716 'cc', 717 // bcc: we send the MTA the "RCPT TO" command for these BUT no bcc info is put in the message headers 718 'bcc', 719 // body - POST var, never in URI (GET) that I know of, but it is possible, URI (EXTREMELY rare) 720 'body', 721 'subject', 722 // Less Common Usage: 723 // ---- sender : string : set or unset 724 // RFC says use header "Sender" ONLY WHEN the sender of the email is NOT the author, this is somewhat rare 725 'sender', 726 // ---- attach_sig: set-True/unset ---- 727 // USAGE: 728 // (outgoing) compose.php: if checkbox attach sig is checked, this is passed as GPC var to sent_message.php 729 // (in) send_message.php: indicate if message should have the user's "sig" added to the message 730 'attach_sig', 731 // ---- req_notify: set-True/unset ---- 732 // USAGE: 733 // (outgoing) compose.php: if checkbox req notify is checked, this should go as GPC to sent_message.php 734 // (in) send_message.php: FIXME! should (somehow) attach the appropiate headers to the outgoing mail 735 'req_notify', 736 // ---- msgtype: string ---- 737 // USAGE: 738 // flag to tell phpgw to invoke "special" custom processing of the message 739 // extremely rare, may be obsolete (not sure), most implementation code is commented out 740 // (outgoing) currently NO page actually sets this var 741 // (a) send_message.php: will add the flag, if present, to the header of outgoing mail 742 // (b) message.php: identify the flag and call a custom proc 743 'msgtype', 744 745 // === MAILTO URI SUPPORT === 746 // ---- mailto: unset / ?set? ---- 747 // USAGE: 748 // (in and out) compose.php: support for the standard mailto html document mail app call 749 // can be used with the typical compose vars (see above) 750 // indicates that to, cc, and subject should be treated as simple MAILTO args 751 'mailto', 752 'personal', 753 754 // === MESSAGE VIEWING MODS === 755 // ---- no_fmt: set-True/unset ---- 756 // USAGE: 757 // (in and outgoing) message.php: will display plain body parts without any html formatting added 758 'no_fmt', 759 760 // === VIEW HTML INSTRUCTIONS === 761 // html_part: string : actually a pre-processed HTML/RELATED MIME part with 762 // the image ID's swapped with msgball data for each "related" image, so the 763 // MUA may obtain the images from the email server using these msgball details 764 'html_part', 765 766 // === FOLDER STATISTICS - CALCULATE TOTAL FOLDER SIZE 767 // as a speed up measure, and to reduce load on the IMAP server 768 // there is an option to skip the calculating of the total folder size 769 // user may request an override of this for 1 page view 770 'force_showsize', 771 772 // === SEARCH RESULT MESSAGE SET === 773 'mlist_set', 774 // *update* DEPRECIATED - not yet fixed 775 776 // === THE FOLDER ARG === 777 // used in almost every procedure, IMAP can be logged into only one folder at a time 778 // and POP3 has only one folder anyway (INBOX) 779 // this *may* be overrided elsewhere in the class initialization and/or login 780 // if not supplied anywhere, then INBOX is the assumed default value for "folder" 781 782 // *update* "folder" obtains it's value from (1) args_array, (2) fldball, (3) msgball, (4) default "INBOX" 783 'folder', 784 785 // keeps track of what folders, if any, need to be "expunged" for an account 786 // MOVED to internal arg, this has nothing to do with GPC vars (see below) 787 //'expunge_folders', 788 789 // which email account is the object of this operation 790 // *update* now in fldball 791 //'acctnum', 792 // all preference handling of extra accounts passes this as the account number "ex" = "extra" 793 'ex_acctnum' 794 ); 795 796 $this->known_internal_args = array( 797 // === OTHER ARGS THAT ARE USED INTERNALLY === 798 'folder_status_info', 799 'folder_list', 800 'mailsvr_callstr', 801 'mailsvr_namespace', 802 'mailsvr_delimiter', 803 'mailsvr_stream', 804 'mailsvr_account_username', 805 // use this uri in any auto-refresh request - filled during "fill_sort_order_start_msgnum()" 806 'index_refresh_uri', 807 'verified_trash_folder_long', 808 809 // keeps track of what folders, if any, need to be "expunged" for an account 810 // UPDATE: "expunge_folders" can NOT BE HERE because it should NOT EXIST unless set during a move or delete 811 // putting it here will initialize it to a value of "" (empty string) which is different than unset. 812 //'expunge_folders', 813 814 // experimental: Set Flag indicative we've run thru this function 815 'already_grab_class_args_gpc' 816 ); 817 //if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.initialize_mail_msg('.__LINE__.'): manual constructor: $this->known_args[] DUMP:', $this->known_args); } 818 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg.initialize_mail_msg('.__LINE__.'): manual *constructor*: LEAVING<br />'); } 819 } 820 821 /*! 822 @function begin_request 823 @abstract initializes EVERYTHING, do not forget to call end_session before you leave this transaction. 824 @param $args_array May be phased out, but right now the most used param is "do_login" => True 825 @author Angles 826 @description the who enchalada happens here. Recently only class msg_bootstrap calls this directly. 827 */ 828 // ---- BEGIN request from Mailserver / Initialize This Mail Session ----- 829 function begin_request($args_array) 830 { 831 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): ENTERING'.'<br />'); } 832 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): feed var args_array[] DUMP:', $args_array); } 833 834 // Grab GPC vars, after we get an acctnum, we'll put them in the appropriate account's "args" data 835 // issue?: which acctnum arg array would this be talking to when we inquire about "already_grab_class_args_gpc"? 836 if ( ($this->get_isset_arg('already_grab_class_args_gpc')) 837 && ((string)$this->get_arg_value('already_grab_class_args_gpc') != '') ) 838 { 839 // somewhere, there's already been a call to grab_class_args_gpc(), do NOT re-run 840 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): "already_grab_class_args_gpc" is set, do not re-grab<br />'); } 841 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): "already_grab_class_args_gpc" pre-existing $this->get_all_args() DUMP:', $this->get_all_args()); } 842 $got_args=array(); 843 } 844 else 845 { 846 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): "already_grab_class_args_gpc" is NOT set, call grab_class_args_gpc() now<br />'); } 847 $got_args=array(); 848 $got_args = $this->grab_class_args_gpc(); 849 } 850 851 // FIND THE "BEST ACCTNUM" and set it 852 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): about to call: get_best_acctnum($args_array, $got_args) <br />'); } 853 $acctnum = $this->get_best_acctnum($args_array, $got_args); 854 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): "get_best_acctnum" returns $acctnum ['.$acctnum.']<br />'); } 855 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): * * * *SETTING CLASS ACCTNUM* * * * by calling $this->set_acctnum('.serialize($acctnum).')<br />'); } 856 $this->set_acctnum($acctnum); 857 858 // SET GOT_ARGS TO THAT ACCTNUM 859 // use that acctnum to set "got_args" to the appropiate acctnum 860 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): about to call: $this->set_arg_array($got_args); <br />'); } 861 $this->set_arg_array($got_args, $acctnum); 862 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): post set_arg_array $this->get_all_args() DUMP:', $this->get_all_args()); } 863 864 // Initialize Internal Args 865 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): about to call: "init_internal_args_and_set_them('.$acctnum.')"<br />'); } 866 $this->init_internal_args_and_set_them($acctnum); 867 868 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): POST "grab_class_args_gpc", "get_best_acctnum", and "init_internal_args_and_set_them" : this->get_all_args() DUMP:', $this->get_all_args()); } 869 870 // (chopped out the re-use existing object code - never worked right, maybe later...) 871 872 // ---- Things To Be Done Whether You Login Or Not ----- 873 874 // UNDER DEVELOPMEMT - backwards_compat with sessions_db where php4 sessions are not being used 875 // ALSO UNDER DEVELOPMENT - using private table for anglemail 876 if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db') 877 || ($this->use_private_table == True)) 878 { 879 /* 880 if (! is_object($this->so)) 881 { 882 $this->initialize_mail_msg(); 883 } 884 */ 885 886 // REF_SESSION should not really be in $_SESSION namespace so RE-CREATE all this outside of php4 sessions 887 $this->so->prep_db_session_compat('begin_request LINE '.__LINE__); 888 } 889 890 891 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): about to handle email preferences and setup extra accounts<br />'); } 892 // ---- Obtain Preferences Data ---- 893 894 /* 895 // UNDER DEVELOPMEMT: caching the prefs data 896 // data we need to DB save to cache final processed prefs 897 $this->unprocessed_prefs 898 $this->raw_filters 899 $this->ex_accounts_count 900 $this->extra_accounts 901 $this->extra_and_default_acounts 902 $this->a[X]->prefs 903 // where X is the account number, we can use "set_pref_array(array_data, acctnum) for each account 904 905 // ok lets make an array to hold this data in the DB 906 $cached_prefs = array(); 907 $cached_prefs['unprocessed_prefs'] = array(); 908 $cached_prefs['raw_filters'] = array(); 909 $cached_prefs['ex_accounts_count'] = '0'; 910 $cached_prefs['extra_accounts'] = array(); 911 $cached_prefs['extra_and_default_acounts'] = array(); 912 $cached_prefs['a'] = array(); 913 */ 914 // ---- GET FROM CACHE THE COMPLETED PREF DATA 915 //$this->use_cached_prefs = True; 916 //$this->use_cached_prefs = False; 917 if ($this->use_cached_prefs == False) 918 { 919 $cached_prefs = $this->nothing; 920 } 921 else 922 { 923 /* 924 // data we need to DB save to cache final processed prefs 925 $this->unprocessed_prefs 926 $this->raw_filters 927 $this->ex_accounts_count 928 $this->extra_accounts 929 $this->extra_and_default_acounts 930 $this->a[X]->['prefs'] 931 // where X is the account number, we can use "set_pref_array(array_data, acctnum) for each account 932 933 // ok this is what we should get from the DB storage (we use appsession for now) 934 $cached_prefs = array(); 935 $cached_prefs['unprocessed_prefs'] = array(); 936 $cached_prefs['raw_filters'] = array(); 937 $cached_prefs['ex_accounts_count'] = '0'; 938 $cached_prefs['extra_accounts'] = array(); 939 $cached_prefs['extra_and_default_acounts'] = array(); 940 $cached_prefs['a'] = array(); 941 */ 942 // get the data from appsession, we use compression to avoid problems unserializing 943 $my_location = '0;cached_prefs'; 944 $cached_prefs = $this->so->so_appsession_passthru($my_location); 945 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): raw $cached_prefs as returned from cache DUMP:', $cached_prefs); } 946 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): raw serialized $cached_prefs is '.htmlspecialchars(serialize($cached_prefs)).'<br />'); } 947 } 948 949 // ok if we actually got cached_prefs then maybe we can use them 950 if (($this->use_cached_prefs == True) 951 && ((string)$cached_prefs != $this->nothing) 952 && (is_array($cached_prefs)) 953 && (isset($cached_prefs['extra_and_default_acounts']))) 954 { 955 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): raw $cached_prefs deemed to actually have usable data, so process it<br />'); } 956 // UN-defang the filters 957 // NO remember that filters are left in defang (htmlquotes encoded) form 958 // UNTIL they are going to be used, then bofilters defangs them 959 //for ($x=0; $x < count($cached_prefs['raw_filters']); $x++) 960 //{ 961 // $cached_prefs['raw_filters'][$x]['filtername'] = $this->db_defang_decode($cached_prefs['raw_filters'][$x]['filtername']); 962 // $cached_prefs['raw_filters'][$x]['source_accounts']['folder'] = $this->db_defang_decode($cached_prefs['raw_filters'][$x]['source_accounts']['folder']); 963 // for ($y=0; $y < count($cached_prefs['raw_filters']['matches']); $y++) 964 // { 965 // $cached_prefs['raw_filters'][$x]['matches'][$y]['matchthis'] 966 // = $this->db_defang_decode($cached_prefs['raw_filters'][$x]['matches'][$y]['matchthis']); 967 // } 968 // for ($y=0; $y < count($cached_prefs['raw_filters']['actions']); $y++) 969 // { 970 // $cached_prefs['raw_filters'][$x]['actions'][$y]['folder'] 971 // = $this->db_defang_decode($cached_prefs['raw_filters'][$x]['actions'][$y]['folder']); 972 // } 973 //} 974 // UN-defang the rest of the prefs that may need it 975 $defang_these = array(); 976 $defang_these[0] = 'passwd'; 977 $defang_these[1] = 'email_sig'; 978 $defang_these[2] = 'trash_folder_name'; 979 $defang_these[3] = 'sent_folder_name'; 980 $defang_these[4] = 'userid'; 981 $defang_these[5] = 'address'; 982 $defang_these[6] = 'mail_folder'; 983 $defang_these[7] = 'fullname'; 984 $defang_these[8] = 'account_name'; 985 $loops = count($cached_prefs['extra_and_default_acounts']); 986 for ($i=0; $i < $loops; $i++) 987 { 988 for ($x=0; $x < count($defang_these); $x++) 989 { 990 $defang_word = $defang_these[$x]; 991 if (isset($cached_prefs['a'][$i]['prefs'][$defang_word])) 992 { 993 $cached_prefs['a'][$i]['prefs'][$defang_word] 994 = $this->db_defang_decode($cached_prefs['a'][$i]['prefs'][$defang_word]); 995 } 996 } 997 } 998 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): retrieved $cached_prefs AFTER UN-defang DUMP:', $cached_prefs); } 999 // lets fill the data 1000 $this->unprocessed_prefs = $cached_prefs['unprocessed_prefs']; 1001 $this->raw_filters = $cached_prefs['raw_filters']; 1002 $this->ex_accounts_count = $cached_prefs['ex_accounts_count']; 1003 $this->extra_accounts = $cached_prefs['extra_accounts']; 1004 $this->extra_and_default_acounts = $cached_prefs['extra_and_default_acounts']; 1005 $loops = count($this->extra_and_default_acounts); 1006 for ($i=0; $i < $loops; $i++) 1007 { 1008 $this->set_pref_array($cached_prefs['a'][$i]['prefs'], $i); 1009 } 1010 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): successfully retrieved and applied $cached_prefs<br />'); } 1011 } 1012 //$allow_prefs_shortcut = True; 1013 //$allow_prefs_shortcut = False; 1014 //if ((is_array($GLOBALS['phpgw_info']['user']['preferences']['email']) == True) 1015 //&& ($allow_prefs_shortcut == True)) 1016 //{ 1017 // if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): prefs array already created by the API, NOT calling "create_email_preferences"<br />'); } 1018 // $this->unprocessed_prefs = array(); 1019 // $this->unprocessed_prefs['email'] = array(); 1020 // $this->unprocessed_prefs['email'] = $GLOBALS['phpgw_info']['user']['preferences']['email']; 1021 // if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): did NOT call create_email_preferences, prefs were already available in $GLOBALS["phpgw_info"]["user"]["preferences"]["email"] <br />'); } 1022 //} 1023 else 1024 { 1025 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $cached_prefs either disabled or no data was cached<br />'); } 1026 // make this empty without question, since cached prefs were not recovered 1027 $cached_prefs = array(); 1028 // IT SEEMS PREFS FOR ACCT 0 NEED TO RUN THRU THIS TO FILL "Account Name" thingy 1029 // obtain the preferences from the database, put them in $this->unprocessed_prefs, note THIS GETS ALL PREFS for some reason, not just email prefs? 1030 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): BEFORE processing email prefs, GLOBALS[phpgw_info][user][preferences][email] DUMP:', $GLOBALS['phpgw_info']['user']['preferences']['email']); } 1031 1032 //$this->unprocessed_prefs = $GLOBALS['phpgw']->preferences->create_email_preferences(); 1033 $tmp_email_only_prefs = array(); 1034 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): calling create_email_preferences, may be time consuming<br />'); } 1035 $tmp_email_only_prefs = $GLOBALS['phpgw']->preferences->create_email_preferences(); 1036 // clean "unprocessed_prefs" so all prefs oher than email are NOT included 1037 $this->unprocessed_prefs = array(); 1038 $this->unprocessed_prefs['email'] = array(); 1039 $this->unprocessed_prefs['email'] = $tmp_email_only_prefs['email']; 1040 $tmp_email_only_prefs = array(); 1041 unset($tmp_email_only_prefs); 1042 //if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): AFTER "create_email_preferences" GLOBALS[phpgw_info][user][preferences] DUMP<pre>'; print_r($GLOBALS['phpgw_info']['user']['preferences']); echo '</pre>'); } 1043 1044 // BACKWARDS COMPAT for apps that have no clue what multiple accounts are about 1045 // fill email's $GLOBALS['phpgw_info']['user']['preferences'] with the data for backwards compatibility (we don't use that) 1046 // damn, where did email's prefs get filled already? Where are they getting filled, anyway do not re-fill if not needed 1047 // NO - IT IS POSSIBLE THIS MAY NOT CATCH ALL PREF CHANGES IN CORNER CASES 1048 if (is_array($GLOBALS['phpgw_info']['user']['preferences']['email']) == False) 1049 { 1050 //$GLOBALS['phpgw_info']['user']['preferences'] = $this->unprocessed_prefs; 1051 $GLOBALS['phpgw_info']['user']['preferences']['email'] = array(); 1052 $GLOBALS['phpgw_info']['user']['preferences']['email'] = $this->unprocessed_prefs['email']; 1053 } 1054 //echo 'dump3 <pre>'; print_r($GLOBALS['phpgw_info']['user']['preferences']); echo '</pre>'; 1055 // BUT DO NOT put unneeded stuff in there, [ex_accounts] and [filters] multilevel arrays 1056 // are not needed for mackward compat, we need them internally but external apps do not use this raw data 1057 //if (isset($GLOBALS['phpgw_info']['user']['preferences']['email']['ex_accounts'])) 1058 //{ 1059 // $GLOBALS['phpgw_info']['user']['preferences']['email']['ex_accounts'] = array(); 1060 // unset($GLOBALS['phpgw_info']['user']['preferences']['email']['ex_accounts']); 1061 //} 1062 //if (isset($GLOBALS['phpgw_info']['user']['preferences']['email']['filters'])) 1063 //{ 1064 // $GLOBALS['phpgw_info']['user']['preferences']['email']['filters'] = array(); 1065 // unset($GLOBALS['phpgw_info']['user']['preferences']['email']['filters']); 1066 //} 1067 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): AFTER backwards_compat and cleaning GLOBALS[phpgw_info][user][preferences] DUMP:', $GLOBALS['phpgw_info']['user']['preferences']); } 1068 1069 1070 // first, put the filter data from prefs in a holding var for use by the filters class if needed 1071 // raw filters array for use by the filters class, we just put the data here, that is all, while collecting other prefs 1072 $this->raw_filters = array(); 1073 if ((isset($this->unprocessed_prefs['email']['filters'])) 1074 && (is_array($this->unprocessed_prefs['email']['filters']))) 1075 { 1076 $this->raw_filters = $this->unprocessed_prefs['email']['filters']; 1077 // not get that out of "unprocessed_prefs" because it is not needed there any more 1078 $this->unprocessed_prefs['email']['filters'] = array(); 1079 unset($this->unprocessed_prefs['email']['filters']); 1080 } 1081 //if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): put filter data in $this->raw_filters DUMP<pre>'; print_r($this->raw_filters); echo '</pre>'); } 1082 1083 // second, set the prefs for the default, base acct 0, BUT do not give it data it does not need 1084 // we already got "filters" out of "unprocessed_prefs", so when setting acct0 prefs, do not give it the "ex_accounts" array 1085 $acct0_prefs_cleaned = array(); 1086 $acct0_prefs_cleaned = $this->unprocessed_prefs; 1087 if ((isset($acct0_prefs_cleaned['email']['ex_accounts'])) 1088 && (is_array($acct0_prefs_cleaned['email']['ex_accounts']))) 1089 { 1090 $acct0_prefs_cleaned['email']['ex_accounts'] = array(); 1091 unset($acct0_prefs_cleaned['email']['ex_accounts']); 1092 } 1093 // now we can use that to set the prefs for the base account 1094 1095 // --- process pres for in multi account enviornment --- 1096 // for our use, put prefs in a class var to be accessed thru OOP-style access calls in mail_msg_wrapper 1097 // since we know these prefs to be the top level prefs, for the default email account, force them into acctnum 0 1098 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): putting top level, default account, pref data in acct 0 with $this->set_pref_array($acct0_prefs_cleaned[email], 0); <br />'); } 1099 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $acct0_prefs_cleaned[email] DUMP:', $acct0_prefs_cleaned['email']); } 1100 //$this->set_pref_array($this->unprocessed_prefs['email'], 0); 1101 $this->set_pref_array($acct0_prefs_cleaned['email'], 0); 1102 $acct0_prefs_cleaned = array(); 1103 unset($acct0_prefs_cleaned); 1104 1105 1106 // === EXTRA ACCOUNTS === 1107 // they are located in an array based at $this->unprocessed_prefs['email']['ex_accounts'][] 1108 // determine what extra accounts have been defined 1109 // note: php3 DOES have is_array(), ok to use it here 1110 if ((isset($this->unprocessed_prefs['email']['ex_accounts'])) 1111 && (is_array($this->unprocessed_prefs['email']['ex_accounts']))) 1112 { 1113 $this->ex_accounts_count = count($this->unprocessed_prefs['email']['ex_accounts']); 1114 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $this->unprocessed_prefs[email][ex_accounts] is set and is_array, its count: $this->ex_accounts_count: ['.$this->ex_accounts_count.']<br />'); } 1115 if ($this->debug_logins > 2) { $this->dbug->out('$this->unprocessed_prefs[email][ex_accounts] DUMP:', $this->unprocessed_prefs['email']['ex_accounts']); } 1116 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): about to process extra account data ; $this->ex_accounts_count: ['.$this->ex_accounts_count.']<br />'); } 1117 // note: extra accounts lowest possible value = 1, NOT 0 1118 // also, $key, although numbered integers, may not be conticuous lowest to highest (may be empty or missing elements inbetween) 1119 1120 // ---- what accounts have some data defined 1121 // array_extra_accounts[X]['acctnum'] : integer 1122 // array_extra_accounts[X]['status'] string = "enabled" | "disabled" | "empty" 1123 //while(list($key,$value) = each($this->unprocessed_prefs['email']['ex_accounts'])) 1124 while(list($key,$value) = each($this->unprocessed_prefs['email']['ex_accounts'])) 1125 { 1126 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): inside loop: for each $this->unprocessed_prefs[email][ex_accounts] ; $key: ['.serialize($key).'] $value DUMP:', $value); } 1127 // if we are here at all then this array item must have some data defined 1128 $next_pos = count($this->extra_accounts); 1129 $this->extra_accounts[$next_pos] = array(); 1130 $this->extra_accounts[$next_pos]['acctnum'] = (int)$key; 1131 // ---- is this account "enabled", "disabled" or is this array item "empty" 1132 // first, see if it has essential data, if not, it's an empty array item 1133 if ( (!isset($this->unprocessed_prefs['email']['ex_accounts'][$key]['fullname'])) 1134 || (!isset($this->unprocessed_prefs['email']['ex_accounts'][$key]['email_sig'])) 1135 || (!isset($this->unprocessed_prefs['email']['ex_accounts'][$key]['layout'])) ) 1136 { 1137 // this account lacks essential data needed to describe an account, it must be an "empty" element 1138 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): inside loop: account ['.$key.'] is *empty*: $this->unprocessed_prefs[email][ex_accounts]['.$key.']: ['.serialize($this->unprocessed_prefs['email']['ex_accounts'][$key]).']<br />'); } 1139 $this->extra_accounts[$next_pos]['status'] = 'empty'; 1140 } 1141 // ... so the account is not empty ... 1142 elseif ( (isset($this->unprocessed_prefs['email']['ex_accounts'][$key]['ex_account_enabled'])) 1143 && ((string)$this->unprocessed_prefs['email']['ex_accounts'][$key]['ex_account_enabled'] != '')) 1144 { 1145 // this account is defined AND enabled, 1146 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): inside loop: account ['.$key.'] is *enabled*: $this->unprocessed_prefs[email][ex_accounts]['.$key.'][ex_account_enabled]: ['.serialize($this->unprocessed_prefs['email']['ex_accounts'][$key]['ex_account_enabled']).']<br />'); } 1147 $this->extra_accounts[$next_pos]['status'] = 'enabled'; 1148 } 1149 else 1150 { 1151 // this account is defined BUT not enabled 1152 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): inside loop: account ['.$key.'] is *disabled*: $this->unprocessed_prefs[email][ex_accounts]['.$key.'][ex_account_enabled]: ['.serialize($this->unprocessed_prefs['email']['ex_accounts'][$key]['ex_account_enabled']).']<br />'); } 1153 $this->extra_accounts[$next_pos]['status'] = 'disabled'; 1154 } 1155 1156 // IF ENABLED, then 1157 if ($this->extra_accounts[$next_pos]['status'] == 'enabled') 1158 { 1159 // PROCESS EXTRA ACCOUNT PREFS 1160 // run thru the create prefs function requesting this particular acctnum 1161 // fills in certain missing data, and does some sanity checks, and any data processing that may be necessary 1162 $sub_tmp_prefs = array(); 1163 // we "fool" create_email_preferences into processing extra account info as if it were top level data 1164 // by specifing the secong function arg as the integer of this particular enabled account 1165 $this_ex_acctnum = $this->extra_accounts[$next_pos]['acctnum']; 1166 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): CALLING create_email_preferences("", $this_ex_acctnum) for specific account, where $this_ex_acctnum: ['.serialize($this_ex_acctnum).'] <br />'); } 1167 $sub_tmp_prefs = $GLOBALS['phpgw']->preferences->create_email_preferences('', $this_ex_acctnum); 1168 // now put these processed prefs in the correct location in our prefs array 1169 $this->set_pref_array($sub_tmp_prefs['email'], $this_ex_acctnum); 1170 } 1171 } 1172 // extra_and_default_acounts is the same as above but has default account inserted at position zero 1173 $this->extra_and_default_acounts = array(); 1174 // first put in the default account 1175 $this->extra_and_default_acounts[0]['acctnum'] = 0; 1176 $this->extra_and_default_acounts[0]['status'] = 'enabled'; 1177 // now add whetever extra accounts we processed above 1178 $loops = count($this->extra_accounts); 1179 for ($i=0; $i < $loops; $i++) 1180 { 1181 $this->extra_and_default_acounts[$i+1]['acctnum'] = $this->extra_accounts[$i]['acctnum']; 1182 $this->extra_and_default_acounts[$i+1]['status'] = $this->extra_accounts[$i]['status']; 1183 } 1184 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $this->extra_accounts DUMP:', $this->extra_accounts); } 1185 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $this->extra_and_default_acounts DUMP:', $this->extra_and_default_acounts); } 1186 } 1187 else 1188 { 1189 $this->ex_accounts_count = 0; 1190 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $this->unprocessed_prefs[email][ex_accounts] NOT set or NOT is_array, $this->ex_accounts_count: ['.$this->ex_accounts_count.']<br />'); } 1191 } 1192 // if NO extra accounts axist, we STILL need to put the default account inextra_and_default_acounts 1193 // extra_and_default_acounts will not have been handled whatsoever if no extra accounts exist 1194 // so make sure the default account is there 1195 if (count($this->extra_and_default_acounts) == 0) 1196 { 1197 $this->extra_and_default_acounts = array(); 1198 // first put in the default account 1199 $this->extra_and_default_acounts[0]['acctnum'] = 0; 1200 $this->extra_and_default_acounts[0]['status'] = 'enabled'; 1201 } 1202 // -end- extra account init handling 1203 } 1204 1205 //if ($this->debug_logins > 2) { echo 'mail_msg.begin_request('.__LINE__.'): POST create_email_preferences GLOBALS[phpgw_info][user][preferences][email] dump:<pre>'; print_r($GLOBALS['phpgw_info']['user']['preferences']['email']) ; echo '</pre>';} 1206 //if ($this->debug_logins > 2) { echo 'mail_msg.begin_request('.__LINE__.'): POST create_email_preferences $this->get_all_prefs() dump:<pre>'; print_r($this->get_all_prefs()) ; echo '</pre>';} 1207 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): POST create_email_preferences direct access dump of $this->a DUMP:', $this->a); } 1208 //if ($this->debug_logins > 2) { echo 'mail_msg.begin_request('.__LINE__.'): preferences->create_email_preferences called, GLOBALS[phpgw_info][user][preferences] dump:<pre>'; print_r($GLOBALS['phpgw_info']['user']['preferences']) ; echo '</pre>';} 1209 //if ($this->debug_logins > 2) { echo 'mail_msg.begin_request('.__LINE__.'): preferences->create_email_preferences called, GLOBALS[phpgw_info][user] dump:<pre>'; print_r($GLOBALS['phpgw_info']['user']) ; echo '</pre>';} 1210 //if ($this->debug_logins > 2) { echo 'mail_msg.begin_request('.__LINE__.'): preferences->create_email_preferences called, GLOBALS[phpgw_info] dump:<pre>'; print_r($GLOBALS['phpgw_info']) ; echo '</pre>';} 1211 //if ($this->debug_logins > 2) { echo 'mail_msg.begin_request('.__LINE__.'): preferences->create_email_preferences called, GLOBALS[phpgw] dump:<pre>'; print_r($GLOBALS['phpgw']) ; echo '</pre>';} 1212 1213 // ---- CACHE THE COMPLETED PREF DATA 1214 if (($this->use_cached_prefs == True) 1215 && (!$cached_prefs)) 1216 { 1217 // for whever reason we did not get any data from the stored prefs 1218 /* 1219 // data we need to DB save to cache final processed prefs 1220 $this->unprocessed_prefs 1221 $this->raw_filters 1222 $this->ex_accounts_count 1223 $this->extra_accounts 1224 $this->extra_and_default_acounts 1225 $this->a[X]->['prefs'] 1226 // where X is the account number, we can use "set_pref_array(array_data, acctnum) for each account 1227 */ 1228 // ok lets make an array to hold this data in the DB 1229 $cached_prefs = array(); 1230 $cached_prefs['unprocessed_prefs'] = array(); 1231 $cached_prefs['raw_filters'] = array(); 1232 $cached_prefs['ex_accounts_count'] = '0'; 1233 $cached_prefs['extra_accounts'] = array(); 1234 $cached_prefs['extra_and_default_acounts'] = array(); 1235 $cached_prefs['a'] = array(); 1236 // lets fill the data 1237 $cached_prefs['unprocessed_prefs'] = $this->unprocessed_prefs; 1238 $cached_prefs['raw_filters'] = $this->raw_filters; 1239 // defang the filters 1240 // NO remember bofilters defangs, htmlquotes encodes, the filters FOR US 1241 // they are stored in the preferences DB already in defanged state 1242 // we never need to degang or UN-defang filters 1243 // because bofilters handles ALL that for us 1244 //for ($x=0; $x < count($cached_prefs['raw_filters']); $x++) 1245 //{ 1246 // $cached_prefs['raw_filters'][$x]['filtername'] = $this->db_defang_encode($cached_prefs['raw_filters'][$x]['filtername']); 1247 // $cached_prefs['raw_filters'][$x]['source_accounts']['folder'] = $this->db_defang_encode($cached_prefs['raw_filters'][$x]['source_accounts']['folder']); 1248 // for ($y=0; $y < count($cached_prefs['raw_filters']['matches']); $y++) 1249 // { 1250 // $cached_prefs['raw_filters'][$x]['matches'][$y]['matchthis'] 1251 // = $this->db_defang_encode($cached_prefs['raw_filters'][$x]['matches'][$y]['matchthis']); 1252 // } 1253 // for ($y=0; $y < count($cached_prefs['raw_filters']['actions']); $y++) 1254 // { 1255 // $cached_prefs['raw_filters'][$x]['actions'][$y]['folder'] 1256 // = $this->db_defang_encode($cached_prefs['raw_filters'][$x]['actions'][$y]['folder']); 1257 // } 1258 //} 1259 $cached_prefs['ex_accounts_count'] = $this->ex_accounts_count; 1260 $cached_prefs['extra_accounts'] = $this->extra_accounts; 1261 $cached_prefs['extra_and_default_acounts'] = $this->extra_and_default_acounts; 1262 $cached_prefs['a'] = array(); 1263 $defang_these = array(); 1264 $defang_these[0] = 'passwd'; 1265 $defang_these[1] = 'email_sig'; 1266 $defang_these[2] = 'trash_folder_name'; 1267 $defang_these[3] = 'sent_folder_name'; 1268 $defang_these[4] = 'userid'; 1269 $defang_these[5] = 'address'; 1270 $defang_these[6] = 'mail_folder'; 1271 $defang_these[7] = 'fullname'; 1272 $defang_these[8] = 'account_name'; 1273 $loops = count($this->extra_and_default_acounts); 1274 for ($i=0; $i < $loops; $i++) 1275 { 1276 $cached_prefs['a'][$i] = array(); 1277 $cached_prefs['a'][$i]['prefs'] = array(); 1278 $cached_prefs['a'][$i]['prefs'] = $this->a[$i]['prefs']; 1279 // defang 1280 for ($x=0; $x < count($defang_these); $x++) 1281 { 1282 $defang_word = $defang_these[$x]; 1283 if (isset($cached_prefs['a'][$i]['prefs'][$defang_word])) 1284 { 1285 $cached_prefs['a'][$i]['prefs'][$defang_word] = $this->db_defang_encode($cached_prefs['a'][$i]['prefs'][$defang_word]); 1286 } 1287 } 1288 } 1289 // just use account 0 for this eventhough the prefs are for every account 1290 $my_location = '0;cached_prefs'; 1291 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): POST create_email_preferences we made the $cached_prefs for storage DUMP:', $cached_prefs); } 1292 $this->so->so_appsession_passthru($my_location, $cached_prefs); 1293 } 1294 1295 // ---- SET important class vars ---- 1296 $this->att_files_dir = $GLOBALS['phpgw_info']['server']['temp_dir'].SEP.$GLOBALS['phpgw_info']['user']['sessionid']; 1297 1298 // and.or get some vars we will use later in this function 1299 $mailsvr_callstr = $this->get_arg_value('mailsvr_callstr'); 1300 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $mailsvr_callstr '.$mailsvr_callstr.'<br />'); } 1301 1302 // set class var "$this->cache_mailsvr_data" based on prefs info 1303 // FIXME: why have this in 2 places, just keep it in prefs (todo) 1304 // THIS IS DEPRECIATED but may be used again in the future. 1305 if ((isset($this->cache_mailsvr_data_disabled)) 1306 && ($this->cache_mailsvr_data_disabled == True)) 1307 { 1308 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): OLD DEFUNCT OPTION folder cache DISABLED, $this->cache_mailsvr_data_disabled = '.serialize($this->cache_mailsvr_data_disabled).'<br />'); } 1309 $this->cache_mailsvr_data = False; 1310 } 1311 elseif (($this->get_isset_pref('cache_data')) 1312 && ($this->get_pref_value('cache_data') != '')) 1313 { 1314 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): OLD DEFUNCT OPTION folder cache is enabled in user prefs'.'<br />'); } 1315 $this->cache_mailsvr_data = True; 1316 } 1317 else 1318 { 1319 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): OLD DEFUNCT OPTION folder cache is NOT enabled in user prefs'.'<br />'); } 1320 $this->cache_mailsvr_data = False; 1321 } 1322 1323 // ---- Should We Login ----- 1324 if (!isset($args_array['do_login'])) 1325 { 1326 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $args_array[do_login] was NOT set, so we set it to default value "FALSE"'.'<br />'); } 1327 $args_array['do_login'] = False; 1328 } 1329 // ---- newer 3 way do_login_ex value from the bootstrap class 1330 if ( (!defined(BS_LOGIN_NOT_SPECIFIED)) 1331 || (!isset($args_array['do_login_ex'])) ) 1332 { 1333 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $args_array[do_login_ex] not set, getting default from a temp local bootstrap object'.'<br />'); } 1334 // that means somewhere the bootstrap class has been run 1335 $local_bootstrap = CreateObject('email.msg_bootstrap'); 1336 $local_bootstrap->set_do_login($args_array['do_login'], 'begin_request'); 1337 $args_array['do_login_ex'] = $local_bootstrap->get_do_login_ex(); 1338 $local_bootstrap = ''; 1339 unset($local_bootstrap); 1340 } 1341 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $args_array[] DUMP ['.serialize($args_array).']'.'<br />'); } 1342 1343 /* 1344 // ---- Are We In Newsmode Or Not ----- 1345 // FIXME: !!! this needs better handling 1346 if ((isset($args_array['newsmode'])) 1347 && (($args_array['newsmode'] == True) || ($args_array['newsmode'] == "on"))) 1348 { 1349 $args_array['newsmode'] = True; 1350 $this->set_arg_value('newsmode', True); 1351 $this->set_pref_value('mail_server_type', 'nntp'); 1352 } 1353 else 1354 { 1355 $args_array['newsmode'] = False; 1356 $this->set_arg_value('newsmode', False); 1357 } 1358 */ 1359 1360 // Browser Detection =FUTURE= 1361 // 0 = NO css ; 1 = CSS supported ; 2 = text only 1362 // currently not implemented, use default 0 (NO CSS support in browser) 1363 $this->browser = 0; 1364 //$this->browser = 1; 1365 1366 // ---- Process "sort" "order" and "start" GPC args (if any) passed to the script ----- 1367 // these args are so fundamental, they get stored in their own class vars 1368 // no longer referenced as args after this 1369 // requires args saved to $this->a[$this->acctnum]['args'], only relevant if you login 1370 $this->fill_sort_order_start(); 1371 1372 // ---- Things Specific To Loging In, and Actually Logging In ----- 1373 // $args_array['folder'] gets prep_folder_in and then is stored in class var $this->get_arg_value('folder') 1374 1375 // test for previous login, meaning we have all the cached data we need 1376 //if (($args_array['do_login'] == True) 1377 1378 /*! 1379 @capability do_login if False prevents even trying to login 1380 @abstract this is for preferences pages or other pages where we may 1381 not need nor want a mailserver login BUT we still want the prefs handling and 1382 other functions available in the msg class. Note that caching can eliminate 1383 the need for some logins, but that is a different issue. do_login set to 1384 False will disallow testing cache (which may itself require a login) or trying to login 1385 anyway. 1386 */ 1387 1388 1389 // test for previous login, meaning we have all the cached data we need 1390 if (($args_array['do_login_ex'] >= BS_LOGIN_ONLY_IF_NEEDED) 1391 && ($this->session_cache_enabled == True) 1392 && ($this->session_cache_extreme == True)) 1393 { 1394 // IF we already have a cached_folder_list, we DO NOT NEED to immediately log in 1395 // if and when a login is required, calls to "ensure_stream_and_folder" will take care of that login 1396 // actually, we could even test the L1 class cashed folder_list first, that is a sure sign we have the data 1397 // note _direct_access_arg_value returns NULL (nothing) if that arg is not set 1398 $L1_cached_folder_list = $this->_direct_access_arg_value('folder_list', $acctnum); 1399 if ($this->debug_logins > 1) { $this->dbug->out('begin_request: LINE '.__LINE__.' check for $L1_cached_folder_list DUMP:', $L1_cached_folder_list); } 1400 if ((isset($L1_cached_folder_list) == False) 1401 || (!$L1_cached_folder_list)) 1402 { 1403 $appsession_cached_folder_list = $this->read_session_cache_item('folder_list', $acctnum); 1404 if ($this->debug_logins > 1) { $this->dbug->out('begin_request: LINE '.__LINE__.' check for $appsession_cached_folder_list DUMP:', $appsession_cached_folder_list); } 1405 // while we are here, if we got a folder list now put it in L1 cache so no more aueries to the DB 1406 // but only if it a new style, full folder_list including the folder_short elements 1407 if (isset($appsession_cached_folder_list[0]['folder_short'])) 1408 { 1409 // cache the result in "Level 1 cache" class object var 1410 if (($this->debug_logins > 1) || ($this->debug_args_special_handlers > 1)) { $this->dbug->out('begin_request: LINE '.__LINE__.' while we are here, put folder_list into Level 1 class var "cache" so no more queries to DB for this<br />'); } 1411 $this->set_arg_value('folder_list', $appsession_cached_folder_list, $acctnum); 1412 } 1413 } 1414 else 1415 { 1416 // we have L1 data, no need to query the database 1417 $appsession_cached_folder_list = $L1_cached_folder_list; 1418 } 1419 if (($L1_cached_folder_list) 1420 || ($appsession_cached_folder_list)) 1421 { 1422 // in this case, extreme caching is in use, AND we already have cached data, so NO NEED TO LOGIN 1423 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): session extreme caching IS in use, AND we have a cached "folder_list", which means should also have all necessary cached data, so NO LOGIN NEEDED<br />'); } 1424 $decision_to_login = False; 1425 1426 // get a few more things that we would otherwise get during the login code (which we'll be skiping) 1427 $processed_folder_arg = $this->get_best_folder_arg($args_array, $got_args, $acctnum); 1428 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): session extreme caching IS in use, Login may NOT occur, so about to issue: $this->set_arg_value("folder", '.$processed_folder_arg.', '.serialize($acctnum).')<br />'); } 1429 $this->set_arg_value('folder', $processed_folder_arg, $acctnum); 1430 if ( $this->get_isset_pref('userid') 1431 && ($this->get_pref_value('userid') != '')) 1432 { 1433 $user = $this->get_pref_value('userid'); 1434 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): session extreme caching IS in use, Login may NOT occur, so about to issue: $this->set_arg_value("mailsvr_account_username", '.$user.', '.serialize($acctnum).')<br />'); } 1435 $this->set_arg_value('mailsvr_account_username', $user, $acctnum); 1436 } 1437 } 1438 else 1439 { 1440 // in this case, extreme caching is in use, HOWEVER we do not have necessary cached data, so WE NEED A LOGIN 1441 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): session extreme caching IS in use, but we do NOT have a cached "folder_list", meaning we probably do NOT have any cached data, so we NEED A LOGIN, allow it if requested<br />'); } 1442 $decision_to_login = True; 1443 } 1444 } 1445 elseif ($args_array['do_login_ex'] == BS_LOGIN_NEVER) 1446 { 1447 // whether or not extreme caching is on, if "BS_LOGIN_NEVER" then we DO NOT login 1448 $decision_to_login = False; 1449 1450 // get a few more things that we would otherwise get during the login code (which we'll be skiping) 1451 $processed_folder_arg = $this->get_best_folder_arg($args_array, $got_args, $acctnum); 1452 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): we are NOT allowed to log in (see code this line) but we still need to get this info, so about to issue: $this->set_arg_value("folder", '.$processed_folder_arg.', '.serialize($acctnum).')<br />'); } 1453 $this->set_arg_value('folder', $processed_folder_arg, $acctnum); 1454 if ( $this->get_isset_pref('userid') 1455 && ($this->get_pref_value('userid') != '')) 1456 { 1457 $user = $this->get_pref_value('userid'); 1458 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): we are NOT allowed to log in (see code this line) but we still need to get this info, so about to issue: $this->set_arg_value("mailsvr_account_username", '.$user.', '.serialize($acctnum).')<br />'); } 1459 $this->set_arg_value('mailsvr_account_username', $user, $acctnum); 1460 } 1461 } 1462 /* 1463 elseif ($args_array['do_login_ex'] == BS_LOGIN_ONLY_IF_NEEDED) 1464 { 1465 // if extreme is on and "BS_LOGIN_ONLY_IF_NEEDED" then that's taken care of above, 1466 // therefor 1467 // * if we are here then caching is NOT on 1468 // * we are told a login is "not 100% necessary" 1469 // in that case we'll pass thru this function without logging in 1470 // and rely on the rest of the code to open a stream if it is needed 1471 $decision_to_login = False; 1472 1473 // get a few more things that we would otherwise get during the login code (which we'll be skiping) 1474 $processed_folder_arg = $this->get_best_folder_arg($args_array, $got_args, $acctnum); 1475 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: begin_request ('.__LINE__.'): we are NOT allowed to log in (see code this line) but we still need to get this info, so about to issue: $this->set_arg_value("folder", '.$processed_folder_arg.', '.serialize($acctnum).')<br />'); } 1476 $this->set_arg_value('folder', $processed_folder_arg, $acctnum); 1477 if ( $this->get_isset_pref('userid') 1478 && ($this->get_pref_value('userid') != '')) 1479 { 1480 $user = $this->get_pref_value('userid'); 1481 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: begin_request ('.__LINE__.'): we are NOT allowed to log in (see code this line) but we still need to get this info, so about to issue: $this->set_arg_value("mailsvr_account_username", '.$user.', '.serialize($acctnum).')<br />'); } 1482 $this->set_arg_value('mailsvr_account_username', $user, $acctnum); 1483 } 1484 } 1485 */ 1486 else 1487 { 1488 // extreme caching and logins handled above in the first if .. then 1489 // if we are here, generally we are allowed to login 1490 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): session extreme caching is NOT in use, any begin_request logins ARE allowed <br />'); } 1491 $decision_to_login = True; 1492 } 1493 1494 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): <u>maybe about to enter login sequence</u>, $args_array[]: ['.serialize($args_array).'] ; $decision_to_login ['.serialize($decision_to_login).'] <br />'); } 1495 1496 // now actually use that test result 1497 if ($decision_to_login == True) 1498 { 1499 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): entered and starting login sequence <br />'); } 1500 1501 // ---- Get Email Password 1502 if ($this->get_isset_pref('passwd') == False) 1503 { 1504 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): this->a[$this->acctnum][prefs][passwd] NOT set, fallback to $GLOBALS[phpgw_info][user][passwd]'.'<br />'); } 1505 // DO NOT alter the password and put that altered password BACK into the preferences array 1506 // why not? used to have a reason, but that was obviated, no reason at the moment 1507 //$this->set_pref_value('passwd',$GLOBALS['phpgw_info']['user']['passwd']); 1508 //$this->a[$this->acctnum]['prefs']['passwd'] = $GLOBALS['phpgw_info']['user']['passwd']; 1509 $pass = $GLOBALS['phpgw_info']['user']['passwd']; 1510 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): pass grabbed from GLOBALS[phpgw_info][user][passwd] = '.htmlspecialchars(serialize($pass)).'<br />'); } 1511 } 1512 else 1513 { 1514 // DO NOT alter the password and do NOT put that altered password BACK into the preferences array 1515 // keep the one in GLOBALS in encrypted form if possible ???? 1516 $pass = $this->get_pref_value('passwd'); 1517 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): pass from prefs: already defanged for us, but still encrypted <pre>'.$pass.'</pre><br />'."\r\n"); } 1518 // IMPORTANT: (this note on "defanging" still valid as of Jan 24, 2002 1519 // the last thing you do before saving to the DB is "de-fang" 1520 // so the FIRST thing class prefs does when reading from the db MUST be to "UN-defang", and that IS what happens there 1521 // so by now phpgwapi/class.preferences has ALREADY done the "de-fanging" 1522 $pass = $this->decrypt_email_passwd($pass); 1523 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): pass from prefs: decrypted: <pre>'.$pass.'</pre><br />'."\r\n"); } 1524 } 1525 // ---- ISSET CHECK for userid and passwd to avoid garbage logins ---- 1526 if ( $this->get_isset_pref('userid') 1527 && ($this->get_pref_value('userid') != '') 1528 && (isset($pass)) 1529 && ($pass != '') ) 1530 { 1531 $user = $this->get_pref_value('userid'); 1532 } 1533 else 1534 { 1535 // FIXME make this use an official error function 1536 // problem - invalid or nonexistant info for userid and/or passwd 1537 //if ($this->debug_logins > 0) { 1538 echo 'mail_msg.begin_request('.__LINE__.'): ERROR: userid or passwd empty'."<br />\r\n" 1539 .' * * $this->get_pref_value(userid) = ' 1540 .$this->get_pref_value('userid')."<br />\r\n" 1541 .' * * if the userid is filled, then it must be the password that is missing'."<br />\r\n" 1542 .' * * tell your admin if a) you have a custom email password or not when reporting this error'."<br />\r\n"; 1543 //} 1544 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): LEAVING with ERROR: userid or passwd empty<br />'); } 1545 return False; 1546 } 1547 1548 // ---- Create email server Data Communication Class ---- 1549 // 1st arg to the constructor is the "mail_server_type" 1550 // we feed from here because when there are multiple mail_msg objects 1551 // we need to make sure we load the appropriate type dcom class 1552 // which that class may not know which accounts prefs to use, so tell it here 1553 1554 //$this->a[$this->acctnum]['dcom'] = CreateObject("email.mail_dcom",$this->get_pref_value('mail_server_type')); 1555 1556 // ---- php3 compatibility ---- 1557 // make a "new" holder object to hold the dcom object 1558 // remember, by now we have determined an acctnum 1559 $this_server_type = $this->get_pref_value('mail_server_type'); 1560 // ok, now put that object into the array 1561 //$this_acctnum = $this->get_acctnum(); 1562 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): creating new dcom_holder at $GLOBALS[phpgw_dcom_'.$acctnum.'] = new mail_dcom_holder'.'<br />'); } 1563 $GLOBALS['phpgw_dcom_'.$acctnum] = new mail_dcom_holder; 1564 $GLOBALS['phpgw_dcom_'.$acctnum]->dcom = CreateObject("email.mail_dcom", $this_server_type); 1565 // initialize the dcom class variables 1566 $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->mail_dcom_base(); 1567 1568 // ---- there are 2 settings from this mail_msg object we need to pass down to the child dcom object: ---- 1569 // (1) Do We Use UTF7 encoding/decoding of folder names 1570 if (($this->get_isset_pref('enable_utf7')) 1571 && ($this->get_pref_value('enable_utf7'))) 1572 { 1573 $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->enable_utf7 = True; 1574 } 1575 // (2) Do We Force use of msg UID's 1576 if ($this->force_msg_uids == True) 1577 { 1578 $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->force_msg_uids = True; 1579 } 1580 1581 //@set_time_limit(60); 1582 // login to INBOX because we know that always(?) should exist on an imap server and pop server 1583 // after we are logged in we can get additional info that will lead us to the desired folder (if not INBOX) 1584 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): about to call dcom->open: $GLOBALS["phpgw_dcom_".$acctnum('.$acctnum.')]->dcom->open('.$mailsvr_callstr."INBOX".', '.$user.', '.$pass.', )'.'<br />'); } 1585 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): <font color="red">MAIL SERVER COMMAND</font>'.'<br />'); } 1586 $mailsvr_stream = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->open($mailsvr_callstr."INBOX", $user, $pass, ''); 1587 $pass = ''; 1588 //@set_time_limit(0); 1589 1590 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): open returns $mailsvr_stream = ['.serialize($mailsvr_stream).']<br />'); } 1591 1592 // Logged In Success or Faliure check 1593 if ( (!isset($mailsvr_stream)) 1594 || ($mailsvr_stream == '') ) 1595 { 1596 // set the "mailsvr_stream" to blank so all will know the login failed 1597 $this->set_arg_value('mailsvr_stream', ''); 1598 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): LEAVING with ERROR: failed to open mailsvr_stream : '.$mailsvr_stream.'<br />'); } 1599 // we return false, but SHOULD WE ERROR EXIT HERE? 1600 return False; 1601 } 1602 1603 // SUCCESS - we are logged in to the server, at least we got to "INBOX" 1604 $this->set_arg_value('mailsvr_stream', $mailsvr_stream, $acctnum); 1605 $this->set_arg_value('mailsvr_account_username', $user, $acctnum); 1606 // BUT if "folder" != "INBOX" we still have to "reopen" the stream to that "folder" 1607 1608 // ---- Get additional Data now that we are logged in to the mail server ---- 1609 // namespace is often obtained by directly querying the mailsvr 1610 $mailsvr_namespace = $this->get_arg_value('mailsvr_namespace'); 1611 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $mailsvr_namespace: '.serialize($mailsvr_namespace).'<br />'); } 1612 $mailsvr_delimiter = $this->get_arg_value('mailsvr_delimiter'); 1613 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): $mailsvr_delimiter: '.serialize($mailsvr_delimiter).'<br />'); } 1614 1615 1616 // FIND FOLDER VALUE 1617 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): <b> *** FIND FOLDER VALUE *** </b><br />'); } 1618 // get best available, most legit, folder value that we can find, and prep it in 1619 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): about to call: "get_best_folder_arg($args_array, $got_args, $acctnum(='.$acctnum.'))"<br />'); } 1620 $processed_folder_arg = $this->get_best_folder_arg($args_array, $got_args, $acctnum); 1621 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): "get_best_folder_arg" returns $processed_folder_arg ['.htmlspecialchars(serialize($processed_folder_arg)).']<br />'); } 1622 1623 // ---- Switch To Desired Folder If Necessary ---- 1624 if ($processed_folder_arg == 'INBOX') 1625 { 1626 // NO need to switch to another folder 1627 // put this $processed_folder_arg in arg "folder", replacing any unprocessed value that may have been there 1628 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): NO need to switch folders, about to issue: $this->set_arg_value("folder", '.$processed_folder_arg.', '.serialize($acctnum).')<br />'); } 1629 $this->set_arg_value('folder', $processed_folder_arg, $acctnum); 1630 } 1631 else 1632 { 1633 // switch to the desired folder now that we are sure we have it's official name 1634 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): need to switch folders (reopen) from INBOX to $processed_folder_arg: '.$processed_folder_arg.'<br />'); } 1635 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): about to issue: $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->reopen('.$mailsvr_stream.', '.$mailsvr_callstr.$processed_folder_arg,', )'.'<br />'); } 1636 //$did_reopen = $tmp_a['dcom']->reopen($mailsvr_stream, $mailsvr_callstr.$processed_folder_arg, ''); 1637 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: begin_request('.__LINE__.'): <font color="red">MAIL SERVER COMMAND</font>'.'<br />'); } 1638 $did_reopen = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->reopen($mailsvr_stream, $mailsvr_callstr.$processed_folder_arg, ''); 1639 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): reopen returns: '.serialize($did_reopen).'<br />'); } 1640 // error check 1641 if ($did_reopen == False) 1642 { 1643 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): LEAVING with re-open ERROR, closing stream, FAILED to reopen (change folders) $mailsvr_stream ['.$mailsvr_stream.'] INBOX to ['.$mailsvr_callstr.$processed_folder_arg.'<br />'); } 1644 // log out since we could not reopen, something must have gone wrong 1645 $this->end_request(); 1646 return False; 1647 } 1648 else 1649 { 1650 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): Successful switch folders (reopen) from (default initial folder) INBOX to ['.$processed_folder_arg.']<br />'); } 1651 // put this $processed_folder_arg in arg "folder", since we were able to successfully switch folders 1652 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): switched folders (via reopen), about to issue: $this->set_arg_value("folder", '.$processed_folder_arg.', $acctnum(='.$acctnum.'))<br />'); } 1653 $this->set_arg_value('folder', $processed_folder_arg, $acctnum); 1654 } 1655 } 1656 1657 // now we have folder, sort and order, make a URI for auto-refresh use 1658 // we can NOT put "start" in auto refresh or user may not see the 1st index page on refresh 1659 $this_index_refresh_uri = 1660 'menuaction=email.uiindex.index' 1661 .'&fldball[folder]='.$this->prep_folder_out() 1662 .'&fldball[acctnum]='.$this->get_acctnum() 1663 .'&sort='.$this->get_arg_value('sort') 1664 .'&order='.$this->get_arg_value('order'); 1665 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): about to call $this->set_arg_value(index_refresh_uri, $this_index_refresh_uri, $acctnum(='.$acctnum.')); ; where $this_index_refresh_uri: '.htmlspecialchars($this_index_refresh_uri).'<br />'); } 1666 $this->set_arg_value('index_refresh_uri', $this_index_refresh_uri, $acctnum); 1667 1668 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): about to leave, direct access dump of $this->a DUMP:', $this->a); } 1669 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): LEAVING, success'.'<br />'); } 1670 // returning this is vestigal, not really necessary, but do it anyway 1671 // it's importance is that it returns something other then "False" on success 1672 return $this->get_arg_value('mailsvr_stream', $acctnum); 1673 } 1674 else 1675 { 1676 // EXPERIMENTAL since we did not login can we still get a good refresh URI? 1677 // now we have folder, sort and order, make a URI for auto-refresh use 1678 // we can NOT put "start" in auto refresh or user may not see the 1st index page on refresh 1679 $this_index_refresh_uri = 1680 'menuaction=email.uiindex.index' 1681 .'&fldball[folder]='.$this->prep_folder_out() 1682 .'&fldball[acctnum]='.$this->get_acctnum() 1683 .'&sort='.$this->get_arg_value('sort') 1684 .'&order='.$this->get_arg_value('order'); 1685 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg.begin_request('.__LINE__.'): about to call $this->set_arg_value(index_refresh_uri, $this_index_refresh_uri, $acctnum(='.$acctnum.')); ; where $this_index_refresh_uri: '.htmlspecialchars($this_index_refresh_uri).'<br />'); } 1686 $this->set_arg_value('index_refresh_uri', $this_index_refresh_uri, $acctnum); 1687 1688 //if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: begin_request ('.__LINE__.'): LEAVING, we were NOT allowed to, $args_array[do_login]: ['.serialize($args_array['do_login']).'] if TRUE, then we must return *something* so calling function does NOT think error, so return $args_array[do_login] <br />'); } 1689 //return $args_array['do_login']; 1690 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: begin_request ('.__LINE__.'): LEAVING, we did NOT login, see above debug output, $args_array[do_login]: ['.serialize($args_array['do_login']).'] if TRUE, then we must return *something* so calling function does NOT think error, so return TRUE (what impact does this have??) <br />'); } 1691 return True; 1692 } 1693 } 1694 1695 /*! 1696 @function logout 1697 @abstract simply calls this->end_request with no args, so it closes all open streams. 1698 @author Angles 1699 @discussion Simplified way to logout. Closes all open streams for all accounts. Usually 1700 closing selected streams only is an internal only thing used in special circumstances, 1701 so this function SHOULD BE CALLED AT THE END of your page view, for example, 1702 just before the last template "pfp" (or whatever output function you use). NOTE: 1703 IF THERE IS A WAY TO "HOOK" THIS so it happens AUTOMATICALLY after 1704 the last parse of the api template, that would be a "good thing" 1705 */ 1706 function logout() 1707 { 1708 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: logout: ENTERING, about to call ->end_request with no args'.'<br />'); } 1709 $this->end_request(array()); 1710 } 1711 1712 /*! 1713 @function end_request 1714 @abstract Closes open streams. 1715 @author Angles 1716 @param $args_array OPTIONAL array of type fldball. If noy provided, all open streams are closed. 1717 @discussion Streams are left open during any particular mail operation and are not closed until this 1718 function is called. If this function is not called then the stream becomes a zombie and the mail server 1719 will close it after a certain amount of time. Mail streams before PHP 4,2 are not persistent, they last 1720 only as long as the page view or mail operation. This function should be called so the streams are 1721 properly closed with the logout command to the mail server. 1722 */ 1723 function end_request($args_array='') 1724 { 1725 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: end_request: ENTERING'.'<br />'); } 1726 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg: end_request: $args_array DUMP', $args_array); } 1727 $check_streams = array(); 1728 if ((isset($args_array['acctnum'])) 1729 && ((string)$args_array['acctnum'] != '')) 1730 { 1731 // we were asked to close only this specific stream, not all possible open streams 1732 $check_streams[0]['acctnum'] = (int)$args_array['acctnum']; 1733 } 1734 else 1735 { 1736 // we were asked to close all possible open streams 1737 // put together a list of all enabled accounts so we will check them for an open stream 1738 for ($i=0; $i < count($this->extra_and_default_acounts); $i++) 1739 { 1740 if ($this->extra_and_default_acounts[$i]['status'] == 'enabled') 1741 { 1742 $next_idx = count($check_streams); 1743 $check_streams[$next_idx]['acctnum'] = $this->extra_and_default_acounts[$i]['acctnum']; 1744 } 1745 } 1746 } 1747 if ($this->debug_logins > 2) { $this->dbug->out('mail_msg: end_request: $check_streams DUMP', $check_streams); } 1748 1749 // so now we know what acctnums we need to check (at least they are enabled), loop thru them 1750 for ($i=0; $i < count($check_streams); $i++) 1751 { 1752 $this_acctnum = $check_streams[$i]['acctnum']; 1753 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: end_request: stream check, will examine $this_acctnum = $check_streams['.$i.'][acctnum] = ['.$check_streams[$i]['acctnum'].']<br />'); } 1754 if (($this->get_isset_arg('mailsvr_stream', $this_acctnum) == True) 1755 && ((string)$this->get_arg_value('mailsvr_stream', $this_acctnum) != '')) 1756 { 1757 $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $this_acctnum); 1758 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: end_request: stream exists, for $this_acctnum ['.$this_acctnum.'] , $mailsvr_stream : ['.$mailsvr_stream.'] ; logging out'.'<br />'); } 1759 // SLEEP seems to give the server time to send its OK response, used tcpdump to confirm this 1760 //sleep(1); 1761 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: end_request('.__LINE__.'): <font color="red">MAIL SERVER COMMAND</font>'.'<br />'); } 1762 $GLOBALS['phpgw_dcom_'.$this_acctnum]->dcom->close($mailsvr_stream); 1763 // sleep here does not have any effect 1764 //sleep(1); 1765 $this->set_arg_value('mailsvr_stream', '', $this_acctnum); 1766 } 1767 } 1768 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: end_request: LEAVING'.'<br />'); } 1769 } 1770 1771 /*! 1772 @function ensure_stream_and_folder 1773 @abstract make sure a stream is open and the desired folder is selected, can automatically do this for us 1774 @author Angles 1775 @param $fldball descrfibes the acctnum and folder to open, SPECIAL NOTE you *may* pass a 1776 $fldball["no_switch_away"] = True value IF the command you will issue does not require a specific opened folder, SUCH 1777 AS STATUS, which does not require that folder to be selected in order to get information about it. 1778 THIS FUNCTION UNDERSTANDS THIS SPECIAL CIRCUMSTANCE of this possible empty 1779 $fldball["folder"] value. 1780 @param $called_from (string) name of the function that you called this from, used to aid in debugging. 1781 @discussion Typically used for moving mail between seperate accounts, use this function to make sure 1782 the source or destination mail server stream is open and the required folder is selected. If not, this 1783 function will open the connection and select the desired folder. 1784 */ 1785 function ensure_stream_and_folder($fldball='', $called_from='') 1786 { 1787 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: ensure_stream_and_folder: ENTERING, $fldball: ['.serialize($fldball).'] ; $called_from: ['.$called_from.']<br />'); } 1788 1789 if ((isset($fldball['acctnum'])) 1790 && ((string)$fldball['acctnum'] != '')) 1791 { 1792 $acctnum = (int)$fldball['acctnum']; 1793 } 1794 else 1795 { 1796 $acctnum = $this->get_acctnum(); 1797 } 1798 if ((isset($fldball['folder'])) 1799 && ((string)$fldball['folder'] != '')) 1800 { 1801 $input_folder_arg = $fldball['folder']; 1802 //$input_folder_arg = urldecode($fldball['folder']); 1803 //$input_folder_arg = $this->prep_folder_in($fldball['folder']); 1804 } 1805 else 1806 { 1807 // an empty string means folder is NOT important, such as with "listmailbox" 1808 //$input_folder_arg = ''; 1809 1810 // DAMN - this thing has been moved to the "no_select_away" 1811 // therefor this *should* be INBOX if none was given 1812 $input_folder_arg = 'INBOX'; 1813 } 1814 1815 // initialize this stuff 1816 $ctrl_info = array(); 1817 $ctrl_info['first_open'] = ''; 1818 $ctrl_info['pre_existing_folder_arg'] = ''; 1819 $ctrl_info['no_switch_away'] = ''; 1820 $ctrl_info['do_reopen_to_folder'] = ''; 1821 1822 // fill it with what we know 1823 if (($this->get_isset_arg('folder', $acctnum)) 1824 && ($this->get_arg_value('folder', $acctnum) != '')) 1825 { 1826 $ctrl_info['pre_existing_folder_arg'] = $this->get_arg_value('folder', $acctnum); 1827 // folder arg is stored urlDEcoded, but fldball and all other folder stuff is urlENcoded until the last second 1828 $ctrl_info['pre_existing_folder_arg'] = $this->prep_folder_out($ctrl_info['pre_existing_folder_arg']); 1829 } 1830 if ((isset($fldball['no_switch_away'])) 1831 && ($fldball['no_switch_away'])) 1832 { 1833 // "no_switch_away" means folder is NOT important, such as with "listmailbox" 1834 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: there may be NO need to switch folders: setting $ctrl_info[no_switch_away] because $fldball[no_switch_away] is ['.serialize($fldball['no_switch_away']).'], $called_from: ['.$called_from.']<br />'); } 1835 $ctrl_info['no_switch_away'] = True; 1836 } 1837 1838 1839 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: $acctnum: ['.serialize($acctnum).'] ; $input_folder_arg: ['.serialize($input_folder_arg).']<br />'); } 1840 // get mailsvr_callstr now, it does not require a login stream 1841 $mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum); 1842 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: $mailsvr_callstr: '.serialize($mailsvr_callstr).'<br />'); } 1843 1844 if (($this->get_isset_arg('mailsvr_stream', $acctnum)) 1845 && ((string)$this->get_arg_value('mailsvr_stream', $acctnum) != '')) 1846 { 1847 $ctrl_info['first_open'] = False; 1848 $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum); 1849 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: ensure_stream_and_folder: PRE-EXISTING stream, do not re-login, $mailsvr_stream ['.serialize($mailsvr_stream).'] <br />'); } 1850 } 1851 else 1852 { 1853 $ctrl_info['first_open'] = True; 1854 $mailsvr_stream = ''; 1855 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: stream for this account needs to be opened, login to $acctnum ['.$acctnum.']'.'<br />'); } 1856 if ($this->get_isset_pref('passwd', $acctnum) == False) 1857 { 1858 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: this->a[$this->acctnum][prefs][passwd] NOT set, fallback to $GLOBALS[phpgw_info][user][passwd]'.'<br />'); } 1859 $pass = $GLOBALS['phpgw_info']['user']['passwd']; 1860 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: pass grabbed from GLOBALS[phpgw_info][user][passwd] = '.htmlspecialchars(serialize($pass)).'<br />'); } 1861 } 1862 else 1863 { 1864 $pass = $this->get_pref_value('passwd', $acctnum); 1865 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: pass from prefs: already "defanged" for us, but still ancrypted '.htmlspecialchars(serialize($pass)).'<br />'); } 1866 $pass = $this->decrypt_email_passwd($pass); 1867 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: pass from prefs: decrypted: '.htmlspecialchars(serialize($pass)).'<br />'); } 1868 } 1869 if ( $this->get_isset_pref('userid', $acctnum) 1870 && ($this->get_pref_value('userid', $acctnum) != '') 1871 && (isset($pass)) 1872 && ($pass != '') ) 1873 { 1874 $user = $this->get_pref_value('userid', $acctnum); 1875 } 1876 else 1877 { 1878 echo 'mail_msg: ensure_stream_and_folder: ERROR: userid or passwd empty'."<br />\r\n" 1879 .' * * $this->get_pref_value(userid, '.$acctnum.') = ' 1880 .$this->get_pref_value('userid', $acctnum)."<br />\r\n" 1881 .' * * if the userid is filled, then it must be the password that is missing'."<br />\r\n" 1882 .' * * tell your admin if a) you have a custom email password or not when reporting this error'."<br />\r\n"; 1883 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: ensure_stream_and_folder: LEAVING with ERROR: userid or passwd empty<br />'); } 1884 return False; 1885 } 1886 1887 // ---- Create email server Data Communication Class ---- 1888 $this_server_type = $this->get_pref_value('mail_server_type', $acctnum); 1889 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: creating new dcom_holder at $GLOBALS["phpgw_dcom_'.$acctnum.'] = new mail_dcom_holder'.'<br />'); } 1890 $GLOBALS['phpgw_dcom_'.$acctnum] = new mail_dcom_holder; 1891 $GLOBALS['phpgw_dcom_'.$acctnum]->dcom = CreateObject("email.mail_dcom", $this_server_type); 1892 $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->mail_dcom_base(); 1893 1894 if (($this->get_isset_pref('enable_utf7', $acctnum)) 1895 && ($this->get_pref_value('enable_utf7', $acctnum))) 1896 { 1897 $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->enable_utf7 = True; 1898 } 1899 if ($this->force_msg_uids == True) 1900 { 1901 $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->force_msg_uids = True; 1902 } 1903 // log in to INBOX because we know INBOX should exist on every mail server, "reopen" to desired folder (if different) later 1904 //@set_time_limit(60); 1905 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: about to call dcom->open: $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->open('.$mailsvr_callstr."INBOX".', '.$user.', '.$pass.', )'.'<br />'); } 1906 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): <font color="red">MAIL SERVER COMMAND</font>'.'<br />'); } 1907 $mailsvr_stream = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->open($mailsvr_callstr."INBOX", $user, $pass, ''); 1908 $pass = ''; 1909 //@set_time_limit(0); 1910 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: open returns $mailsvr_stream = ['.serialize($mailsvr_stream).']<br />'); } 1911 1912 if ( (!isset($mailsvr_stream)) || ($mailsvr_stream == '') ) 1913 { 1914 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder ('.__LINE__.'): $mailsvr_stream FAILS ['.serialize($mailsvr_stream).']<br />'); } 1915 //$this->set_arg_value('mailsvr_stream', '', $acctnum); 1916 // this error function will try to call this function again to attempt RedHat bug recovery 1917 // the "ensure_stream_and_folder_already_tried_again" lets us try again before exiting 1918 // otherwise the code would never continue below to a place where recovery could be detected 1919 //$GLOBALS['phpgw']->msg->login_error($GLOBALS['PHP_SELF'].', mail_msg: ensure_mail_msg_exists(), called_from: '.$called_from); 1920 // DIRECTLY call the retry logic 1921 $mail_server_type = $this->get_pref_value('mail_server_type', $acctnum); 1922 //$this->loginerr_tryagain_buggy_cert('ensure_stream_and_folder line ('.__LINE__.'), which was called_from: '.$called_from, 'error_report_HUH?', $mail_server_type, $acctnum); 1923 // oops, that means we just skipped possible showing the right login error message 1924 $this->login_error($GLOBALS['PHP_SELF'].', mail_msg: ensure_stream_and_folder(), called_from: '.$called_from, $acctnum); 1925 1926 //if ($this->debug_logins > 0) { echo 'mail_msg: ensure_stream_and_folder: LEAVING with ERROR: failed to open mailsvr_stream : '.$mailsvr_stream.'<br />';} 1927 //return False; 1928 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder ('.__LINE__.'): code just called the "login_error" function, did we get to here?<br />'); } 1929 } 1930 1931 // if we get here either 1932 // (a) all is fine and dandy, 1933 // or (b) then the error function has called us again with the RehHat buggy server fix attempt 1934 // in case of (b) we need to test the $mailsvr_stream again 1935 // if the failure was not recoverable or if already tried, the above error function would have exited the script by now 1936 if ( (!isset($mailsvr_stream)) || ($mailsvr_stream == '') ) 1937 { 1938 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder ('.__LINE__.'): 2nd test, $mailsvr_stream fails ['.serialize($mailsvr_stream).'] as expected, but the recursive call may have left behind a a sign this has been fixed ...<br />'); } 1939 // try to obtain the mailsvr_stream that the recursive call to here may have left for us 1940 $mailsvr_stream_test2 = $this->get_arg_value('mailsvr_stream', $acctnum); 1941 if ( (isset($mailsvr_stream_test2)) 1942 && ((string)$mailsvr_stream_test2 != '') ) 1943 { 1944 // recursive call to this function has done the job for us 1945 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: ensure_stream_and_folder: LEAVING, apparently a recursive call to this function fixed the RH bug for us, returning $this->get_arg_value(mailsvr_stream, '.$acctnum.') ['.$mailsvr_stream_test2.']<br />'); } 1946 // IF THE RECURSIVE FUNCION DID THE JOB, I GUESS WE JUST EXIT NOW 1947 return $mailsvr_stream_test2; 1948 } 1949 else 1950 { 1951 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: ensure_stream_and_folder: LEAVING, 2nd test using $mailsvr_stream_test2 looked for recursive fix but not found, now calling this->login_error which will exit the script probably<br />'); } 1952 //$GLOBALS['phpgw']->msg->login_error($GLOBALS['PHP_SELF'].', mail_msg ('.__LINE__.'): ensure_mail_msg_exists(), called_by: '.$called_by); 1953 $this->login_error($GLOBALS['PHP_SELF'].', mail_msg ('.__LINE__.'): ensure_stream_and_folder(), called_by: '.$called_by); 1954 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder ('.__LINE__.'): 2nd test, code just called the "login_error" function, did we get to here?<br />'); } 1955 } 1956 } 1957 else 1958 { 1959 // if login_error is able to recover, it will set "mailsvr_stream", we do not want to over write the recovered mailsvr_stream 1960 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder ('.__LINE__.'): $mailsvr_stream is GOOD<br />'); } 1961 $this->set_arg_value('mailsvr_stream', $mailsvr_stream, $acctnum); 1962 } 1963 $this->set_arg_value('mailsvr_account_username', $user, $acctnum); 1964 // SET FOLDER ARG NOW because we'll need to check against it below!!! 1965 // WHY: because we DID actually OPEN a stream AND we DID select the INBOX 1966 // as a practice we ALWAYS open the inbox and then LATER switch to the desired folder 1967 // unless fldball["no_select_away"] is set 1968 $this->set_arg_value('folder', 'INBOX', $acctnum); 1969 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: ... we just opened stream for $acctnum: ['.serialize($acctnum).'] continue ...<br />'); } 1970 } 1971 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder: we have a stream for $acctnum: ['.serialize($acctnum).'] continue ...<br />'); } 1972 1973 $current_folder_arg = ''; 1974 if (($this->get_isset_arg('folder', $acctnum)) 1975 && ($this->get_arg_value('folder', $acctnum) != '')) 1976 { 1977 $current_folder_arg = $this->get_arg_value('folder', $acctnum); 1978 // folder arg is stored urlDEcoded, but fldball and all other folder stuff is urlENcoded until the last second 1979 $current_folder_arg = $this->prep_folder_out($current_folder_arg); 1980 } 1981 // ---- Switch To Desired Folder If Necessary ---- 1982 // NOTE1: get_arg_value "folder" MAY BE SET before an actual stream is established 1983 // because we can cache stuff and only open the stream when we lack info in the cache 1984 // NOTE2: fldball["no_select_away"] tells us the calling function does not *require* 1985 // a particular folder to be selected, HOWEVER 1986 // NOTE3: 1987 // IF (a) if this is the FIRST REAL opening of the stream 1988 // - - AND - - 1989 // (b) the calling function does not care about the selected folder 1990 // - - THEN - - 1991 // we MUST ACTUALLY SELECT (reopen) TO THE PRE-EXISTING FOLDER ARG 1992 // because that folder arg is the folder we were dealing with, we just had no need to 1993 // actually open the stream till now. Furthermore, since we are now opening it 1994 // and the calling func does not *require* us to change folders, WE MUST USE 1995 // the folder arg we had before. 1996 if (($ctrl_info['first_open']) 1997 && ($ctrl_info['pre_existing_folder_arg']) 1998 && ($ctrl_info['no_switch_away'])) 1999 { 2000 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): already had a folder arg, first open of stream, calling func does not care about reopen, so we MUST open to the preexisting folder arg ['.htmlspecialchars($ctrl_info['pre_existing_folder_arg']).'], $called_from: ['.$called_from.']<br />'); } 2001 $ctrl_info['do_reopen_to_folder'] = $ctrl_info['pre_existing_folder_arg']; 2002 } 2003 elseif (($ctrl_info['no_switch_away']) 2004 && ($ctrl_info['first_open']) == False) 2005 { 2006 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): stream was already open, calling func does not care about reopen, so NO NEED to switch folder, $called_from: ['.$called_from.']<br />'); } 2007 } 2008 elseif (($input_folder_arg == 'INBOX') 2009 && ($current_folder_arg == 'INBOX' )) 2010 { 2011 // no need to do anything because 2012 // 1) "INBOX" does not need to be passed thru $this->prep_folder_in(), so we directly can test against $input_folder_arg 2013 // 2) if we're here then it's because we (a) had an existing stream opened to INBOX or (b) we just opened a stream to INBOX just above here 2014 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): NO need to switch folders: both $input_folder_arg and $current_folder_arg == INBOX<br />'); } 2015 } 2016 elseif ($input_folder_arg == $current_folder_arg) 2017 { 2018 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): NO need to switch folders: both $input_folder_arg == $current_folder_arg ['.htmlspecialchars($input_folder_arg).'] == ['.htmlspecialchars($current_folder_arg).'<br />'); } 2019 } 2020 else 2021 { 2022 // unless we missed something, we WILL SWITCH FOLDERS 2023 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): no "skip folder change" conditions match, so WE WILL CHANGE FOLDERS to $input_folder_arg ['.htmlspecialchars($input_folder_arg).'], $called_from: ['.$called_from.']<br />'); } 2024 $ctrl_info['do_reopen_to_folder'] = $input_folder_arg; 2025 } 2026 2027 // PROCEED ... 2028 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): after logic, if $ctrl_info[do_reopen_to_folder] is filled we WILL REOPEN folder, it is ['.htmlspecialchars($ctrl_info['do_reopen_to_folder']).']<br />'); } 2029 if ($ctrl_info['do_reopen_to_folder']) 2030 { 2031 // class will get this data on its own to do the lookup in prep_folder_in anyway, so might as well get it for us here at the same time 2032 $mailsvr_namespace = $this->get_arg_value('mailsvr_namespace', $acctnum); 2033 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): $mailsvr_namespace: '.serialize($mailsvr_namespace).'<br />'); } 2034 $mailsvr_delimiter = $this->get_arg_value('mailsvr_delimiter', $acctnum); 2035 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): $mailsvr_delimiter: '.serialize($mailsvr_delimiter).'<br />'); } 2036 // do this now so we can check against it in the elseif block without having to call it several different times 2037 $preped_folder = $this->prep_folder_in($ctrl_info['do_reopen_to_folder']); 2038 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): $preped_folder: '.serialize($preped_folder).'<br />'); } 2039 // one last check (maybe redundant now) 2040 $preped_current_folder_arg = $this->prep_folder_in($current_folder_arg); 2041 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): $preped_current_folder_arg: '.serialize($preped_current_folder_arg).'<br />'); } 2042 2043 if (($current_folder_arg != '') 2044 && ($preped_current_folder_arg == $preped_folder)) 2045 { 2046 // the desired folder is already opened, note this could simply be INBOX 2047 // because we did set "folder" arg during the initial open just above 2048 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): NO need to switch folders: $preped_current_folder_arg ['.$processed_folder_arg.'] == $preped_folder ['.$preped_folder.']<br />'); } 2049 } 2050 else 2051 { 2052 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): need to switch folders (reopen) from $preped_current_folder_arg ['.$preped_current_folder_arg.'] to $preped_folder: '.$preped_folder.'<br />'); } 2053 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): about to issue: $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->reopen('.$mailsvr_stream.', '.$mailsvr_callstr.$preped_folder,', )'.'<br />'); } 2054 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): <font color="red">MAIL SERVER COMMAND</font>'.'<br />'); } 2055 $did_reopen = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->reopen($mailsvr_stream, $mailsvr_callstr.$preped_folder, ''); 2056 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): reopen returns: '.serialize($did_reopen).'<br />'); } 2057 if ($did_reopen == False) 2058 { 2059 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): LEAVING with re-open ERROR, closing stream, FAILED to reopen (change folders) $mailsvr_stream ['.$mailsvr_stream.'] $pre_opened_folder ['.$pre_opened_folder.'] to ['.$mailsvr_callstr.$processed_folder_arg.'<br />'); } 2060 $end_request_args = array(); 2061 $end_request_args['acctnum'] = $acctnum; 2062 // only need to close this specific stream, leave other streams (if any) alone 2063 $this->end_request($end_request_args); 2064 return False; 2065 } 2066 else 2067 { 2068 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): Successful switch folders (reopen) from (default initial folder) INBOX to ['.$preped_folder.']<br />'); } 2069 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): switched folders (via reopen), about to issue: $this->set_arg_value("folder", '.$preped_folder.')<br />'); } 2070 $this->set_arg_value('folder', $preped_folder, $acctnum); 2071 } 2072 } 2073 } 2074 $return_mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum); 2075 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: ensure_stream_and_folder('.__LINE__.'): LEAVING, returning $this->get_arg_value(mailsvr_stream, '.$acctnum.') ['.$return_mailsvr_stream.']<br />'); } 2076 return $return_mailsvr_stream; 2077 } 2078 2079 /*! 2080 @function login_error 2081 @abstract reports some details about a login failure, uses imap_last_error 2082 @author Angles 2083 @param $called_from (string) name of the function that you called this from, used to aid in debugging. 2084 @discussion ? 2085 */ 2086 function login_error($called_from='', $acctnum='') 2087 { 2088 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: login_error('.__LINE__.'): ENTERING, $called_from ['.$called_from.'], $acctnum: ['.$acctnum.']<br />'); } 2089 // usually acctnum is only supplied by "ensure_stream_and_folder" 2090 // because it is there that streams other then the current account may be opened on demand 2091 if ((!isset($acctnum)) 2092 || ((string)$acctnum == '')) 2093 { 2094 $acctnum = $this->get_acctnum(); 2095 } 2096 2097 if ($called_from == '') 2098 { 2099 $called_from = lang('this data not supplied.'); 2100 } 2101 // NOTE THIS "imap_last_error" NEEDS TO BE WRAPPED 2102 //$imap_err = imap_last_error(); 2103 // this will not work because we have no dcom object to talk to because there was an error, duhhh 2104 //$imap_err = $this->phpgw_server_last_error($acctnum); 2105 if (function_exists('imap_last_error')) 2106 { 2107 $imap_err = imap_last_error(); 2108 } 2109 else 2110 { 2111 $imap_err = ''; 2112 } 2113 2114 if ($imap_err == '') 2115 { 2116 $error_report = lang('No Error Returned From Server'); 2117 } 2118 else 2119 { 2120 $error_report = $imap_err; 2121 } 2122 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: login_error('.__LINE__.'): $error_report ['.$error_report.']<br />'); } 2123 2124 // ATTEMPT TO RECOVER FROM KNOWS PHP BUG even if "Certificate failure" is not obvious 2125 $always_try_recover = True; 2126 2127 if ($this->get_isset_arg('beenthere_loginerr_tryagain_buggy_cert', $acctnum)) 2128 { 2129 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: login_error('.__LINE__.'): ALREADY TRIED THIS: this arg is set: "beenthere_loginerr_tryagain_buggy_cert"<br />'); } 2130 } 2131 elseif ((stristr($imap_err,'Certificate failure')) 2132 || ($always_try_recover == True)) 2133 { 2134 $mail_server_type = $this->get_pref_value('mail_server_type', $acctnum); 2135 // onhy happens with non-ssl connections 2136 if (($mail_server_type == 'pop3') 2137 || ($mail_server_type == 'imap')) 2138 { 2139 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: login_error('.__LINE__.'): LEAVING, with call to: $this->loginerr_tryagain_buggy_cert('.$called_from.', '.$error_report.', '.$mail_server_type.', '.$acctnum.');<br />'); } 2140 $this->loginerr_tryagain_buggy_cert($called_from, $error_report, $mail_server_type, $acctnum); 2141 return; 2142 } 2143 // not recoverable, continue with error report 2144 } 2145 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: login_error('.__LINE__.'): this is not an error related to RH Cert issue because server string is already (apparently) correct in that respect.<br />'); } 2146 2147 /* 2148 // this should be templated 2149 echo "<p><center><b>" 2150 . lang("There was an error trying to connect to your mail server.<br />Please, check your username and password, or contact your admin.")."<br /> \r\n" 2151 ."source: email class.mail_msg_base.inc.php"."<br /> \r\n" 2152 ."called from: ".$called_from."<br /> \r\n" 2153 ."imap_last_error: [".$error_report."]<br /> \r\n" 2154 ."tried RH bug recovery?: [".$this->get_isset_arg('beenthere_loginerr_tryagain_buggy_cert', $acctnum)."] <br /> \r\n" 2155 ."if there is no obvious error, then check your username and password first.<br /> \r\n" 2156 . "</b></center></p>" 2157 .'<p><center>' 2158 .'<a href="'.$GLOBALS['phpgw']->link('/index.php').'">Click here to continue.</a>' 2159 .'</center></p>'."<br /> \r\n"; 2160 */ 2161 // we could just return this text 2162 $error_text_plain = 2163 lang("There was an error trying to connect to your mail server.<br />Please, check your username and password, or contact your admin.")."\r\n" 2164 ."source: email class.mail_msg_base.inc.php"."\r\n" 2165 ."called from: ".$called_from."\r\n" 2166 ."imap_last_error: [".$error_report."]\r\n" 2167 ."tried RH bug recovery?: [".$this->get_isset_arg('beenthere_loginerr_tryagain_buggy_cert', $acctnum)."]\r\n" 2168 .lang('if there is no obvious error, check your username and password first.')."\r\n"; 2169 // or use this text in an html error report 2170 $error_text_formatted = 2171 lang("There was an error trying to connect to your mail server.<br />Please, check your username and password, or contact your admin.")."<br /> \r\n" 2172 ."<br /> \r\n" 2173 ."<br /> \r\n" 2174 ."source: email class.mail_msg_base.inc.php"."<br /> \r\n" 2175 ."<br /> \r\n" 2176 ."called from: ".$called_from."<br /> \r\n" 2177 ."<br /> \r\n" 2178 ."imap_last_error: [".$error_report."]<br /> \r\n" 2179 ."tried RH bug recovery?: [".$this->get_isset_arg('beenthere_loginerr_tryagain_buggy_cert', $acctnum)."] <br /> \r\n" 2180 ."<br /> \r\n" 2181 ."<br /> \r\n" 2182 .lang('if there is no obvious error, check your username and password first.')."<br /> \r\n"; 2183 // HOW we were called determines HOW we display the error 2184 if (stristr($this->ref_SERVER['REQUEST_URI'] ,'index.php?menuaction=email')) 2185 { 2186 // we were called from within the email app itself 2187 // so show the error PAGE and then have it EXIT for us 2188 // use the error report page widget 2189 $widgets = CreateObject("email.html_widgets"); 2190 $widgets->init_error_report_values(); 2191 $widgets->prop_error_report_text($error_text_formatted); 2192 2193 if ((string)$acctnum == '0') 2194 { 2195 $go_somewhere_url = $GLOBALS['phpgw']->link('/index.php',array( 2196 'menuaction' => 'email.uipreferences.preferences', 2197 'show_help' => '1')); 2198 } 2199 else 2200 { 2201 $go_somewhere_url = $GLOBALS['phpgw']->link('/index.php',array( 2202 'menuaction' => 'email.uipreferences.ex_accounts_edit', 2203 'ex_acctnum' => $acctnum, 2204 'show_help' => '1')); 2205 } 2206 $go_somewhere_text = lang('click here to edit the settings for this email account.'); 2207 $widgets->prop_go_somewhere_link($go_somewhere_url, $go_somewhere_text); 2208 2209 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: login_error('.__LINE__.'): LEAVING, called from within the email app, so use out own error page and exit.<br />'); } 2210 // by putting anything (or TRUE) in the param of this function, it will shutdown the script for us. 2211 $widgets->display_error_report_page('do_exit'); 2212 // we should not get here if the error widget exits for us 2213 //$GLOBALS['phpgw']->common->phpgw_exit(); 2214 } 2215 else 2216 { 2217 // we were called by another app, maybe the home page, do not monopolize the page, but DO EXIT the script so we don't loop 2218 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: login_error('.__LINE__.'): LEAVING, we were called by another app, the home page perhaps, so simple output the message and common EXIT (return causes a loop).<br />'); } 2219 //echo '<center><b>'.$error_text_plain.'</b></center>'; 2220 echo '<center><b>'.$error_text_formatted.'</b></center>'."<br /> \r\n"; 2221 $GLOBALS['phpgw']->common->phpgw_exit(); 2222 } 2223 // we should not get here 2224 $GLOBALS['phpgw']->common->phpgw_exit(False); 2225 } 2226 2227 /*! 2228 @function loginerr_tryagain_buggy_cert 2229 @abstract try to recover from a known php bug and reattempt login 2230 @param $called_from 2231 @param $error_report 2232 @param $mail_server_type 2233 @param $acctnum 2234 @author Angles 2235 @discussion as of RedHat 7.3 there us a bug in php and UWash requiring unusual mailscr_callstr 2236 containing "novalidate-cert" even for NON-SSL connections. If possible, this function adjusts the 2237 mailsvr_callstr and continues execution of the script. As long as we "return" from this function, 2238 instead of exiting, we can continue the script from where the error occured, assuming we have 2239 fixed the error. This is a cool thing, the option to fix and continue just by using "return", or to 2240 exit with "phpgw_exit", which ends execution of the script. 2241 */ 2242 function loginerr_tryagain_buggy_cert($called_from='', $error_report='', $mail_server_type='', $acctnum='') 2243 { 2244 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert('.__LINE__.'): ENTERING<br />'); } 2245 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert('.__LINE__.'): $called_from ['.$called_from.'], $error_report: ['.$error_report.'], $mail_server_type: ['.$mail_server_type.'], $acctnum: ['.$acctnum.']<br />'); } 2246 if ((!isset($acctnum)) 2247 || ((string)$acctnum == '')) 2248 { 2249 $acctnum = $this->get_acctnum(); 2250 } 2251 // avoid infinite RECURSION by setting this flag, says we've alreasy been here 2252 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert('.__LINE__.'): setting flag "beenthere_loginerr_tryagain_buggy_cert" to "beenthere" so we know we have been here.<br />'); } 2253 $this->set_arg_value('beenthere_loginerr_tryagain_buggy_cert', 'beenthere', $acctnum); 2254 /*! 2255 @capability "show_recover_msg" during loginerr_tryagain_buggy_cert 2256 @abstract whether ot not to show the user a "show_recover_msg" 2257 @discussion set this "show_recover_msg" var to True to show the user an error 2258 message during the recovery from the initial error a RH73+ php imap module can cause. 2259 Or set "show_recover_msg" to False to not show such a message. NOTE the error generated 2260 by php itself is controlled by your ini file settings, show_errors and log_errors. 2261 */ 2262 //$show_recover_msg = True; 2263 $show_recover_msg = False; 2264 if ($show_recover_msg == True) 2265 { 2266 // this should be templated 2267 //echo "<br /><center><b>" 2268 // .'You have encountered a <u>KNOWN PHP - UWASH bug</u>, called the "<u>non-ssl no validate cert bug</u>"' 2269 // .' attempting to recover ...'.'<br />'."\r\n" 2270 // . "</b></center><br />"; 2271 echo '<small><i>' 2272 .'Please ignore this message, you will only see this once on login' 2273 .'</i></small><br />'."\r\n"; 2274 } 2275 2276 // MAKE A NEW MAILSVR_CALLSTR with the "novalidate-cert" 2277 // UPDATE: using "notls" because user did not specifically request encryption 2278 $old_mailsvr_callstr = $this->get_mailsvr_callstr($acctnum); 2279 // NOTE: now that we set flag "beenthere_loginerr_tryagain_buggy_cert" we NEVER GET HERE a 2nd time any more 2280 // SO this text below will NEVER get a chance to be shown to the user. 2281 if (($mail_server_type != 'pop3') 2282 && ($mail_server_type != 'imap')) 2283 { 2284 echo "<p><center><b>" 2285 .'detected that this is a different situation, unable to recover'.'<br />'."\r\n" 2286 .'exiting...' 2287 . "</b></center></p>"; 2288 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert('.__LINE__.'): LEAVING, calling $this->login_error because it will show the error msg to the user.<br />'); } 2289 //$GLOBALS['phpgw']->common->phpgw_exit(False); 2290 $this->login_error('mail_msg: loginerr_tryagain_buggy_cert(LINE '.__LINE__.'), called_from: '.$called_from, $acctnum); 2291 } 2292 //elseif(stristr($old_mailsvr_callstr,'novalidate-cert')) 2293 elseif(stristr($old_mailsvr_callstr,'notls')) 2294 { 2295 echo "<p><center><b>" 2296 .'detected that there has already been an attempting to recover that failed'.'<br />'."\r\n" 2297 .'exiting...' 2298 . "</b></center></p>"; 2299 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert('.__LINE__.'): LEAVING, calling $this->login_error because it will show the error msg to the user.<br />'); } 2300 //$GLOBALS['phpgw']->common->phpgw_exit(False); 2301 $this->login_error('mail_msg: loginerr_tryagain_buggy_cert(LINE '.__LINE__.'), called_from: '.$called_from, $acctnum); 2302 } 2303 else 2304 { 2305 // MAKE A NEW MAILSVR_CALLSTR with the "novalidate-cert" 2306 // later changed to "notls" because non-ssl sessions are not encrypted unless asked for. 2307 $mail_port = $this->get_pref_value('mail_port', $acctnum); 2308 if ($mail_server_type == 'pop3') 2309 { 2310 //$new_mailsvr_callstr = str_replace($mail_port.'/pop3}',$mail_port.'/pop3/novalidate-cert}',$old_mailsvr_callstr); 2311 $new_mailsvr_callstr = str_replace($mail_port.'/pop3}',$mail_port.'/pop3/notls}',$old_mailsvr_callstr); 2312 } 2313 else 2314 { 2315 //$new_mailsvr_callstr = str_replace($mail_port.'}',$mail_port.'/imap/novalidate-cert}',$old_mailsvr_callstr); 2316 $new_mailsvr_callstr = str_replace($mail_port.'}',$mail_port.'/imap/notls}',$old_mailsvr_callstr); 2317 } 2318 // cache the result in L1 cache 2319 // we are not certain yet this will work, we need to set this in L1 cache so that "" will use this $new_mailsvr_callstr instead of the old one 2320 // if "ensure_stream_and_folder" returns NON-False then we can cache the new_mailsvr_callstr to the appsession cache 2321 $this->set_arg_value('mailsvr_callstr', $new_mailsvr_callstr, $acctnum); 2322 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert('.__LINE__.'): set "level 1 cache, class var" arg $this->set_arg_value(mailsvr_callstr, '.htmlspecialchars($new_mailsvr_callstr).', '.$acctnum.']) <br />'); } 2323 2324 //echo "<p><center><b>" 2325 // .'ADJUSTED mailsvr_callstr: '.$new_mailsvr_callstr.'<br />'."\r\n" 2326 // .'now attempting recovery...' 2327 // . "</b></center></p>"; 2328 2329 // attempt recovery 2330 $fldball = array(); 2331 if ($this->get_isset_arg('fldball', $acctnum)) 2332 { 2333 $fldball = $this->get_arg_value('fldball', $acctnum); 2334 } 2335 elseif ($this->get_isset_arg('msgball', $acctnum)) 2336 { 2337 $fldball = $this->get_arg_value('msgball', $acctnum); 2338 } 2339 else 2340 { 2341 // during the recursive attempt fcalled directly rom "ensure_stream_and_folder" 2342 // which we will call again (recursively) below, fldball may STILL have nothing 2343 $fldball['acctnum'] = $acctnum; 2344 if ($this->get_isset_arg('folder', $acctnum)) 2345 { 2346 $fldball['folder'] = $this->get_arg_value('folder', $acctnum); 2347 } 2348 else 2349 { 2350 $fldball['folder'] = 'INBOX'; 2351 } 2352 } 2353 2354 //$fldball['folder'] = $this->get_arg_value('folder', $acctnum); 2355 //$fldball['acctnum'] = $acctnum; 2356 // function ensure_stream_and_folder($fldball='', $called_from='') 2357 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert('.__LINE__.'): try RECOVERY ATTEMPT: calling $this->ensure_stream_and_folder('.htmlspecialchars(serialize($fldball)).']; NOTE that under certain circumstances we just in fact EXITED this function here, because we do not alsays return.<br />'); } 2358 $did_recover = $this->ensure_stream_and_folder($fldball, 'from mail_msg_base: loginerr_tryagain_buggy_cert FOR '.$called_from); 2359 if ($this->debug_logins > 1) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert('.__LINE__.'): just returned from call to $this->ensure_stream_and_folder, $did_recover ['.serialize($did_recover).']<br />'); } 2360 if ((is_bool($did_recover)) && ($did_recover == False)) 2361 { 2362 echo 'mail_msg: loginerr_tryagain_buggy_cert: UNABLE to recover from this bug, exiting ...'; 2363 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert('.__LINE__.'): LEAVING<br />'); } 2364 $GLOBALS['phpgw']->common->phpgw_exit(False); 2365 } 2366 else 2367 { 2368 // we only put it in appsession cache AFTER "ensure_stream_and_folder" returns NON-False 2369 // ----------- 2370 // SAVE DATA TO APPSESSION CACHE 2371 // ----------- 2372 // save "mailsvr_callstr" to appsession data store 2373 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert: set appsession cache $this->save_session_cache_item(mailsvr_callstr, '.$new_mailsvr_callstr.', '.$acctnum.']) <br />'); } 2374 $this->save_session_cache_item('mailsvr_callstr', $new_mailsvr_callstr, $acctnum); 2375 // back to index page 2376 // NOTE: by NOT calling "phpgw_exit" we can simply fix the problem and continue... 2377 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert('.__LINE__.'): LEAVING quietly, we succeeded in fixing the RH Cert problem, by NOT calling "phpgw_exit" we can simply fix the problem and continue.<br />'); } 2378 return; 2379 } 2380 } 2381 echo 'mail_msg_base: loginerr_tryagain_buggy_cert: unhandled if .. then situation, returning to script'; 2382 if ($this->debug_logins > 0) { $this->dbug->out('mail_msg: loginerr_tryagain_buggy_cert('.__LINE__.'): LEAVING, we should not get here, UNHANDLED if .. then scenario.<br />'); } 2383 return; 2384 2385 } 2386 2387 // ---- Various Functions Used To Support Email ----- 2388 /*! 2389 @function prep_folder_in 2390 @abstract make sure the folder name has the correct namespace and delimiter, if not supplied they will be added. 2391 @author Angles 2392 @param $feed_folder (string) the folder name to work on. 2393 @param $acctnum (int) which account the folder belongs to, defaults to 0, the default account number. 2394 @result Folder long name as obtained from the folder lookup which uses server supplied folder names. 2395 @discussion Mail servers ecpect the foldername to be in a particluar form. This function makes sure of this. 2396 If a foldername without a namespace is provided, this function will preform a lookup of all available folders 2397 for the given acount and get the closest match. The lookup may be nevessary because the namespace and 2398 delimiter can differ from server to server, although most typically the name space is "INBOX" and the 2399 delimiter is a period. NOTE this DOES A LOOKUP and returns what was found there that 2400 reasonable matches param $feed_folder, the return is in FOLDER LONG form as directly supplied 2401 by the server itself to the lookup list. 2402 */ 2403 function prep_folder_in($feed_folder, $acctnum='') 2404 { 2405 if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: prep_folder_in: ENTERING , feed $feed_folder: ['.htmlspecialchars($feed_folder).'], feed (optional) $acctnum: ['.$acctnum.']<br />'); } 2406 // ---- Ensure a Folder Variable exists, if not, set to INBOX (typical practice) ----- 2407 if (!$feed_folder) 2408 { 2409 return 'INBOX'; 2410 // note: return auto-exits this function 2411 } 2412 2413 if ((!isset($acctnum)) 2414 || ((string)$acctnum == '')) 2415 { 2416 $acctnum = $this->get_acctnum(); 2417 } 2418 2419 // FILESYSTEM imap server "dot_slash" or "bare slash" CHECK 2420 if ( ((strstr(urldecode($feed_folder), './')) 2421 || (trim(urldecode($feed_folder)) == '/' ) ) 2422 && ((($this->get_pref_value('imap_server_type', $acctnum) == 'UW-Maildir') 2423 || ($this->get_pref_value('imap_server_type', $acctnum) == 'UWash'))) ) 2424 { 2425 // UWash and UW-Maildir IMAP servers are filesystem based, 2426 // so anything like "./" or "../" *might* make the server list files and directories 2427 // somewhere in the parent directory of the users mail directory 2428 // this could be undesirable a 2429 // (a) IMAP servers really should not do this unless specifically enabled and/or told to do so, and 2430 // (b) many would consider this a security risk to display filesystem data outside the users directory 2431 // same with a bare slash "/" as an erronious folder name might display undesirable information. 2432 return 'INBOX'; 2433 // note: return auto-exits this function 2434 } 2435 2436 // an incoming folder name has generally been urlencoded before it gets here 2437 // particularly if the folder has spaces and is included in the URI, then a + will be where the speces are 2438 $feed_folder = urldecode($feed_folder); 2439 $prepped_folder_in = $this->folder_lookup('', $feed_folder, $acctnum); 2440 if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: prep_folder_in: LEAVING , returning $prepped_folder_in: ['.$prepped_folder_in.'] again with htmlspecialchars(): ['.htmlspecialchars($prepped_folder_in).']<br />'); } 2441 return $prepped_folder_in; 2442 } 2443 2444 /*! 2445 @function prep_folder_out 2446 @abstract Used to prepare folder names for use in a GPC request, currently just urlencodes the foldername. 2447 @author Angles 2448 @param $feed_folder (string) OPTIONAL the folder name to prepare. 2449 @access Private 2450 @discussion Folder if not passed will be obtained from the class which keeps track od the folder currently selected, 2451 this allows us to call this with no args and the current folder is "prep-ed". Foldnames with spaces and other 2452 URL unfriendly chars are encoded here must be decoded on the next input (script session) to undo what we do here. 2453 */ 2454 function prep_folder_out($feed_folder='') 2455 { 2456 if ($feed_folder == '') 2457 { 2458 // this allows us to call this with no args and the current folder is "prep'ed" 2459 // foldnames with spaces and other URL unfriendly chars are encoded here 2460 // must be decoded on the next input (script session) to undo what we do here 2461 $feed_folder = $this->get_arg_value('folder'); 2462 } 2463 //echo 'prep_folder_out: param $feed_folder ['.$feed_folder.'], :: '; 2464 $preped_folder = $this->ensure_one_urlencoding($feed_folder); 2465 $preped_folder = str_replace('&', '%26', $preped_folder); 2466 //echo ' $preped_folder ['.$preped_folder.']<br />'; 2467 return $preped_folder; 2468 } 2469 2470 /*! 2471 @function ensure_no_brackets 2472 @abstract used for removing the bracketed server call string from a full IMAP folder name string. 2473 @author Angles 2474 @param $feed_str (string) the folder name to work on. 2475 @access Private 2476 @syntax ** note this has chars that will not show up in the inline doc parser ** 2477 ensure_no_brackets('{mail.yourserver.com:143}INBOX') = 'INBOX' 2478 @example ** same as syntax but can be viewed in the inline doc parser ** 2479 ensure_no_brackets('{mail.yourserver.com:143}INBOX') results in 'INBOX' 2480 */ 2481 function ensure_no_brackets($feed_str='') 2482 { 2483 if ((strstr($feed_str,'{') == False) && (strstr($feed_str,'}') == False)) 2484 { 2485 // no brackets to remove 2486 $no_brackets = $feed_str; 2487 } 2488 else 2489 { 2490 // get everything to the right of the bracket "}", INCLUDES the bracket itself 2491 $no_brackets = strstr($feed_str,'}'); 2492 // get rid of that 'needle' "}" 2493 $no_brackets = substr($no_brackets, 1); 2494 } 2495 return $no_brackets; 2496 } 2497 2498 /*! 2499 @function get_mailsvr_callstr 2500 @abstract will generate the appropriate string to access a mail server of type pop3, pop3s, imap, imaps 2501 @result the returned string is the server call string from beginning bracker "{" to ending bracket "}" 2502 the returned string is the server call string from beginning bracker "{" to ending bracket "}" 2503 @discussion After updating to RH73, a new bug popped up where PHP was checking the validity 2504 of the mail server certificate even for NON-SSL sessions, under certain circumstances. This has been 2505 handles in the login_error routine where this particular error is detected and fixes by adding 2506 "novalidate-cert" to the non-ssl imap or pop mailsvr_callstr, note this breaks good servers, so it's 2507 handled only after an error pops up and is determined to be a result of this bug. 2508 CACHE NOTE: this item is saved in the appsession cache, AND is bese64_encoded there, 2509 which encoding and decoding is handled in "save_session_cache_item" and 2510 "read_session_cache_item", respectively, where this dataname "mailsvr_namespace" has 2511 a special handler for this purpose. 2512 because this data has "database unfriendly" chars in it. 2513 @syntax {mail.yourserver.com:143} 2514 @example {mail.yourserver.com:143} 2515 @access PRIVATE - public access is object->get_arg_value("mailsvr_namespace") 2516 */ 2517 function get_mailsvr_callstr($acctnum='') 2518 { 2519 if (stristr($this->skip_args_special_handlers, 'get_mailsvr_callstr')) 2520 { 2521 $fake_return = '{brick.earthlink.net:143}'; 2522 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_callstr: debug SKIP, $fake_return: '.serialize($fake_return).' <br />'); } 2523 return $fake_return; 2524 } 2525 2526 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_callstr: ENTERING , feed $acctnum: ['.$acctnum.']<br />'); } 2527 2528 if ((!isset($acctnum)) 2529 || ((string)$acctnum == '')) 2530 { 2531 $acctnum = $this->get_acctnum(); 2532 } 2533 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_callstr: after testing feed arg, using $acctnum: ['.$acctnum.']<br />'); } 2534 2535 // do we have "level one cache" class var data that we can use? 2536 $class_cached_mailsvr_callstr = $this->_direct_access_arg_value('mailsvr_callstr', $acctnum); 2537 if ($class_cached_mailsvr_callstr != '') 2538 { 2539 // return the "level one cache" class var data 2540 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_callstr: LEAVING, returned class var cached data: '.serialize($class_cached_mailsvr_callstr).'<br />'); } 2541 return $class_cached_mailsvr_callstr; 2542 } 2543 2544 // ----------- 2545 // TRY CACHED DATA FROM APPSESSION 2546 // ----------- 2547 // try to restore "mailsvr_callstr" from saved appsession data store 2548 $appsession_cached_mailsvr_callstr = $this->read_session_cache_item('mailsvr_callstr', $acctnum); 2549 if ($this->debug_args_special_handlers > 2) { $this->dbug->out('mail_msg: get_mailsvr_callstr: $appsession_cached_mailsvr_callstr is ['.serialize($appsession_cached_mailsvr_callstr).']<br />'); } 2550 if ($appsession_cached_mailsvr_callstr) 2551 { 2552 // cache the result in "level one cache" class var holder 2553 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_callstr: recovered "mailsvr_callstr" data from appsession <br />'); } 2554 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_callstr: put appsession retored data into "level 1 cache, class var" arg $this->set_arg_value(mailsvr_namespace, '.$appsession_cached_mailsvr_callstr['mailsvr_callstr'].', '.$acctnum.']) <br />'); } 2555 $this->set_arg_value('mailsvr_callstr', $appsession_cached_mailsvr_callstr, $acctnum); 2556 2557 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_callstr: LEAVING, returned appsession cached data: '.serialize($appsession_cached_mailsvr_callstr['mailsvr_callstr']).'<br />'); } 2558 return $appsession_cached_mailsvr_callstr; 2559 } 2560 2561 // no cached data of any kind we can use ... 2562 2563 // what's the name or IP of the mail server 2564 $mail_server = $this->get_pref_value('mail_server', $acctnum); 2565 2566 // what's the port we should use 2567 // mail port is not yet *really* a user settable pref, SO WE WILL PUT LOGIC HERE TO FIGURE OUT WHAT IT SHOULD BE 2568 //$pref_mail_port = $this->get_pref_value('mail_port', $acctnum) 2569 /*! 2570 @concept pref_mail_port 2571 @discussion The preferences for email were designed to *someday* let the user specify a 2572 non standard port to use for the mail server. This preference never came to be. The user 2573 still can not actually set a port number to use for the mail server. However, in the preferences 2574 api there is still code initializing a preference item called "mail_port", with a note that some day it 2575 would be user settable, but since it is not currently, the api preferences class has a function to 2576 determine the port number based on what kind of server we are connecting to. THIS DOES NOT 2577 REALLY BELONG IN PREFS, it never because a user preference, so the api preferences should 2578 not be concerned with mail_port. THEREFOR we will SET IT HERE. For backwards compatibility 2579 we still set a pref value for mail_port after we are done here, until we replace any code that asks 2580 prefs for a port number. Then, port number should be exclusively treated as an "arg" accessable through 2581 the OOP style ->get_arg_value('mail_port') function. We do not have a seperate functin for this, 2582 we do it here, because the "mail_port" and the "mail_server_type" and the "mailsvr_callstr" are 2583 inextricibly linked, they exist only as a related group if data. CONCLUSION: we determine 2584 "mail_port" in this function "get_mailsvr_callstr" which is a private function anyway. 2585 */ 2586 2587 // determine the Mail Server Call String AND PORT 2588 // construct the email server call string from the opening bracket "{" to the closing bracket "}" 2589 $mail_server_type = $this->get_pref_value('mail_server_type', $acctnum); 2590 if ($mail_server_type == 'imaps') 2591 { 2592 // IMAP over SSL 2593 $callstr_extra = '/imap/ssl/novalidate-cert'; 2594 $mail_port = 993; 2595 } 2596 elseif ($mail_server_type == 'pop3s') 2597 { 2598 // POP3 over SSL 2599 $callstr_extra = '/pop3/ssl/novalidate-cert'; 2600 $mail_port = 995; 2601 } 2602 elseif ($mail_server_type == 'pop3') 2603 { 2604 // POP3 normal connection, No SSL 2605 $callstr_extra = '/pop3/notls'; 2606 $mail_port = 110; 2607 } 2608 elseif ($mail_server_type == 'imap') 2609 { 2610 // IMAP normal connection, No SSL 2611 $callstr_extra = '/imap/notls'; 2612 $mail_port = 143; 2613 } 2614 elseif ($mail_server_type == 'nntp') 2615 { 2616 // NOT CURRENTLY USED 2617 // NNTP news server port 2618 $callstr_extra = '/nntp'; 2619 $port_number = 119; 2620 } 2621 else 2622 { 2623 // UNKNOW SERVER type 2624 // assume IMAP normal connection, No SSL 2625 // return a default value that is likely to work 2626 // probably should raise some kind of error here 2627 $callstr_extra = ''; 2628 $mail_port = 143; 2629 } 2630 2631 $mail_port = (string)$mail_port; 2632 $mailsvr_callstr = '{'.$mail_server.':'.$mail_port.$callstr_extra .'}'; 2633 2634 // cache the result in "level one cache" class var holder 2635 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_callstr: set "level 1 cache, class var" arg $this->set_arg_value(mailsvr_callstr, '.htmlspecialchars($mailsvr_callstr).', '.$acctnum.']) <br />'); } 2636 $this->set_arg_value('mailsvr_callstr', $mailsvr_callstr, $acctnum); 2637 2638 // ----------- 2639 // SAVE DATA TO APPSESSION CACHE 2640 // ----------- 2641 // save "mailsvr_callstr" to appsession data store 2642 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_callstr: set appsession cache (will base64_encode) $this->save_session_cache_item(mailsvr_callstr, '.$mailsvr_callstr.', '.$acctnum.']) <br />'); } 2643 $this->save_session_cache_item('mailsvr_callstr', $mailsvr_callstr, $acctnum); 2644 2645 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_callstr: LEAVING, returning $mailsvr_callstr: '.serialize($mailsvr_callstr).' for $acctnum ['.$acctnum.']<br />'); } 2646 return $mailsvr_callstr; 2647 } 2648 2649 /*! 2650 @function get_mailsvr_namespace 2651 @abstract will generate the appropriate namespace (aka filter) string to access an imap mail server 2652 @param $acctnum of the server in question. 2653 @syntax {mail.servyou.com:143}INBOX where INBOX is returned as the namespace 2654 @example get_mailsvr_namespace({mail.servyou.com:143}INBOX) returns INBOX 2655 @discussion for more info see: see http://www.rfc-editor.org/rfc/rfc2342.txt 2656 CACHE NOTE: this item is saved in the appsession cache. 2657 @access PRIVATE - public access is object->get_arg_value("mailsvr_namespace") 2658 */ 2659 function get_mailsvr_namespace($acctnum='') 2660 { 2661 if (stristr($this->skip_args_special_handlers, 'get_mailsvr_namespace')) 2662 { 2663 $fake_return = ''; 2664 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_namespace: debug SKIP, $fake_return: '.serialize($fake_return).' <br />'); } 2665 return $fake_return; 2666 } 2667 2668 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_namespace: ENTERING , feed $acctnum: ['.$acctnum.']<br />'); } 2669 2670 if ((!isset($acctnum)) 2671 || ((string)$acctnum == '')) 2672 { 2673 $acctnum = $this->get_acctnum(); 2674 } 2675 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: after testing feed arg, using $acctnum: ['.$acctnum.']<br />'); } 2676 2677 // UWash patched for Maildir style: $Maildir.Junque ????? 2678 // Cyrus and Courier style =" INBOX" 2679 // UWash style: "mail" 2680 2681 // do we have cached data that we can use? 2682 $class_cached_mailsvr_namespace = $this->_direct_access_arg_value('mailsvr_namespace', $acctnum); 2683 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: check for L1 class var cached data: $this->_direct_access_arg_value(mailsvr_namespace, '.$acctnum.'); returns: '.serialize($class_cached_mailsvr_namespace).'<br />'); } 2684 if ($class_cached_mailsvr_namespace != '') 2685 { 2686 // return the cached data 2687 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_namespace: LEAVING, returned class var cached data: '.serialize($class_cached_mailsvr_namespace).'<br />'); } 2688 return $class_cached_mailsvr_namespace; 2689 } 2690 2691 // ----------- 2692 // TRY CACHED DATA FROM APPSESSION 2693 // ----------- 2694 // try to restore "mailsvr_namespace" from saved appsession data store 2695 $appsession_cached_mailsvr_namespace = $this->read_session_cache_item('mailsvr_namespace', $acctnum); 2696 if ($appsession_cached_mailsvr_namespace) 2697 { 2698 // cache the result in "level one cache" class var holder 2699 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: put appsession retored data into "level 1 cache, class var" arg $this->set_arg_value(mailsvr_namespace, '.$appsession_cached_mailsvr_namespace['mailsvr_namespace'].', '.$acctnum.']) <br />'); } 2700 $this->set_arg_value('mailsvr_namespace', $appsession_cached_mailsvr_namespace, $acctnum); 2701 2702 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_namespace: LEAVING, returned appsession cached data: '.serialize($appsession_cached_mailsvr_namespace['mailsvr_namespace']).'<br />'); } 2703 return $appsession_cached_mailsvr_namespace; 2704 } 2705 2706 2707 // no cached data of any kind we can use ... 2708 2709 // we *may* need this data later 2710 $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum); 2711 $mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum); 2712 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: got these for later use: $mailsvr_stream: ['.$mailsvr_stream.'] ; $mailsvr_callstr: ['.$mailsvr_callstr.']<br />'); } 2713 2714 if (($this->get_pref_value('imap_server_type', $acctnum) == 'UW-Maildir') 2715 || ($this->get_pref_value('imap_server_type', $acctnum) == 'UWash')) 2716 { 2717 if (($this->get_isset_pref('mail_folder', $acctnum)) 2718 && (trim($this->get_pref_value('mail_folder', $acctnum)) != '')) 2719 { 2720 // if the user fills this option correctly, this should yield an unqualified foldername which 2721 // UWash should qualify (juat like any unix command line "cd" command) with the 2722 // appropriate $HOME variable (I THINK) ... 2723 // DO I NEED to add the "~" here too? 2724 $name_space = trim($this->get_pref_value('mail_folder', $acctnum)); 2725 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: user supplied UWash namespace is $name_space ['.$name_space.'] ; needs testing!<br />'); } 2726 $test_result = $this->uwash_string_ok($name_space); 2727 if (!$test_result) 2728 { 2729 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: user supplied UWash namespace returns $test_result ['.serialize($test_result).'] FAILS OK TEST, use "~" instead<br />'); } 2730 $name_space = '~'; 2731 } 2732 else 2733 { 2734 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: user supplied UWash namespace returns $test_result ['.serialize($test_result).'] passed OK test, we use that retuen<br />'); } 2735 $name_space = $test_result; 2736 } 2737 } 2738 else 2739 { 2740 // in this case, the namespace is blank, indicating the user's $HOME is where the MBOX files are 2741 // or in the case of UW-Maildir, where the maildir files are 2742 // thus we can not have <blank><slash> preceeding a folder name 2743 // note that we *may* have <tilde><slash> preceeding a folder name, SO: 2744 // default value for this UWash server, $HOME = tilde (~) 2745 $name_space = '~'; 2746 } 2747 } 2748 /* 2749 elseif ($this->get_pref_value('imap_server_type') == 'Cyrus') 2750 // ALSO works for Courier IMAP 2751 { 2752 $name_space = 'INBOX'; 2753 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: Assume,GUESSING: $name_space = INBOX <br />'); } 2754 } 2755 // TEMP DO NOT USE THIS, MAY BE MORE TROUBLE THAN IT'S WORTH 2756 // JUST ASSUME INBOX, the below code is "by the book" but may be causeing problems with window based installs 2757 // ------- Dynamically Discover User's Private Namespace --------- 2758 // existing "$this->get_arg_value('mailsvr_stream')" means we are logged in and can querey the server 2759 elseif ((isset($mailsvr_stream) == True) 2760 && ($mailsvr_stream != '')) 2761 { 2762 // a LIST querey with "%" returns the namespace of the current reference 2763 // in format {SERVER_NAME:PORT}NAMESPACE 2764 // also, it MAY (needs testing) return all available namespaces 2765 // however this is less useful if the IMAP server makes available shared folders and/or usenet groups 2766 // in addition to the users private mailboxes 2767 // see http://www.faqs.org/rfcs/rfc2060.html section 6.3.8 (which is not entirely clear on this) 2768 //if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: issuing: $GLOBALS[phpgw_dcom_'.$acctnum.']->dcom->listmailbox('.$mailsvr_stream.', '.$mailsvr_callstr.', %)'.'<br />'); } 2769 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: issuing: $this->phpgw_listmailbox('.$mailsvr_callstr.', \'%\', '.$acctnum.')<br />'); } 2770 2771 //$name_space = $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->listmailbox($mailsvr_stream, $mailsvr_callstr, '%'); 2772 $name_space = $this->phpgw_listmailbox($mailsvr_callstr, '%', $acctnum); 2773 2774 if ($this->debug_args_special_handlers > 2) { $this->dbug->out('mail_msg: get_mailsvr_namespace: raw $name_space dump<pre>'; print_r($name_space); echo '</pre>'); } 2775 2776 if (!$name_space) 2777 { 2778 // if the server returns nothing, just use the most common namespace, "INBOX" 2779 // note: "INBOX" is NOT case sensitive according to rfc2060 2780 $name_space = 'INBOX'; 2781 } 2782 elseif (is_array($name_space)) 2783 { 2784 // if the server returns an array of namespaces, the first one is usually the users personal namespace 2785 // tyically "INBOX", there can be any number of other, unpredictable, namespaces also 2786 // used for the shared folders and/or nntp access (like #ftp), but we want the users "personal" 2787 // namespace used for their mailbox folders here 2788 // most likely that the first element of the array is the users primary personal namespace 2789 // I'm not sure but I think it's possible to have more than one personal (i.e. not public) namespace 2790 // note: do not use php function "is_array()" because php3 does not have it 2791 // later note: i think php3 does have "is_array()" 2792 $processed_name_space = $this->ensure_no_brackets($name_space[0]); 2793 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: ($name_space is_array) $processed_name_space = $this->ensure_no_brackets($name_space[0]) [that arg='.$name_space[0].'] returns '.serialize($processed_name_space).'<br />'); } 2794 // put that back in name_space var 2795 $name_space = $processed_name_space; 2796 } 2797 elseif (is_string($name_space)) 2798 { 2799 // if the server returns a string (not likely) just get rid of the brackets 2800 // note: do not use is_string() because php3 does not have it ??? 2801 $processed_name_space = $this->ensure_no_brackets($name_space); 2802 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: ($name_space is string) $processed_name_space = $this->ensure_no_brackets($name_space) [that arg='.$name_space.'] returns '.serialize($processed_name_space).'<br />'); } 2803 // put that back in name_space var 2804 $name_space = $processed_name_space; 2805 } 2806 else 2807 { 2808 // something really screwed up, EDUCATED GUESS 2809 // note: "INBOX" is NOT case sensitive according to rfc2060 2810 $name_space = 'INBOX'; 2811 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: ($name_space is NOT string nor array) GUESSING: $name_space = '.serialize($name_space).'<br />'); } 2812 } 2813 } 2814 */ 2815 else 2816 { 2817 // GENERIC IMAP NAMESPACE 2818 // imap servers usually use INBOX as their namespace 2819 // this is supposed to be discoverablewith the NAMESPACE command 2820 // see http://www.rfc-editor.org/rfc/rfc2342.txt 2821 // however as of PHP 4.0 this is not implemented, and some IMAP servers do not cooperate with it anyway 2822 $name_space = 'INBOX'; 2823 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: GUESSING: $name_space = '.serialize($name_space).'<br />'); } 2824 } 2825 2826 // cache the result in "level one cache" class var holder 2827 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: set "level 1 cache, class var" arg $this->set_arg_value(mailsvr_namespace, '.$name_space.', '.$acctnum.']) <br />'); } 2828 $this->set_arg_value('mailsvr_namespace', $name_space, $acctnum); 2829 2830 // ----------- 2831 // SAVE DATA TO APPSESSION CACHE 2832 // ----------- 2833 // save "mailsvr_namespace" to appsession data store 2834 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_namespace: set appsession cache $this->save_session_cache_item(mailsvr_namespace, '.$name_space.', '.$acctnum.']) <br />'); } 2835 $this->save_session_cache_item('mailsvr_namespace', $name_space, $acctnum); 2836 2837 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_namespace: LEAVING, returning $name_space: '.serialize($name_space).'<br />'); } 2838 return $name_space; 2839 } 2840 2841 /*! 2842 @function uwash_string_ok 2843 @abstract make sure we do not use bad char sequence for uwash filesystem namespace 2844 @result (string) namespace if OK, or boolean False if namespace is NOT OK 2845 @param $namespace (string) the UWash namespace to test 2846 @discussion This can "validate" a UWash namespace AND ALSO ANY UWash 2847 folder string, checking for chars that should not be used in a filesystem based folder 2848 name string, such as a bare forward slash, or dot dot slash, etc. 2849 @access private or public 2850 */ 2851 function uwash_string_ok($namespace='') 2852 { 2853 // it os OK unless we find bad stuff 2854 $is_ok = True; 2855 $ns = trim($namespace); 2856 if ($ns == '') 2857 { 2858 $is_ok = False; 2859 } 2860 elseif (($ns == '/') 2861 || ($ns == SEP)) 2862 { 2863 $is_ok = False; 2864 } 2865 elseif ((stristr($ns,'..')) 2866 || (stristr($ns,'./')) 2867 || (stristr($ns,'/.'))) 2868 { 2869 $is_ok = False; 2870 } 2871 // return False if bad, else return the ns we verified as OK 2872 if ($is_ok == True) 2873 { 2874 return $ns; 2875 } 2876 else 2877 { 2878 return False; 2879 } 2880 } 2881 2882 /*! 2883 @function get_mailsvr_delimiter 2884 @abstract will generate the appropriate token that goes between the namespace and the inferior folders (subfolders) 2885 @example (a) typical imap "INBOX.Sent" returns the "." as the delimiter, 2886 (b) UWash imap (stock mbox) "email/Sent" returns the "/" as the delimiter 2887 @access PRIVATE - public access is object->get_arg_value("mailsvr_delimiter") 2888 */ 2889 function get_mailsvr_delimiter($acctnum='') 2890 { 2891 if (stristr($this->skip_args_special_handlers, 'get_mailsvr_delimiter')) 2892 { 2893 $fake_return = '/'; 2894 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_delimiter: debug SKIP, $fake_return: '.serialize($fake_return).' <br />'); } 2895 return $fake_return; 2896 } 2897 2898 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_delimiter: ENTERING , feed $acctnum: ['.$acctnum.']<br />'); } 2899 2900 if ((!isset($acctnum)) 2901 || ((string)$acctnum == '')) 2902 { 2903 $acctnum = $this->get_acctnum(); 2904 } 2905 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_delimiter: after testing feed arg, using $acctnum: ['.$acctnum.']<br />'); } 2906 2907 // UWash style: "/" 2908 // all other imap servers *should* be "." 2909 2910 // do we have cached data that we can use? 2911 $class_cached_mailsvr_delimiter = $this->_direct_access_arg_value('mailsvr_delimiter', $acctnum); 2912 if ($class_cached_mailsvr_delimiter != '') 2913 { 2914 // return the cached data 2915 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_delimiter: LEAVING, returned class var cached data: '.serialize($class_cached_mailsvr_delimiter).'<br />'); } 2916 return $class_cached_mailsvr_delimiter; 2917 } 2918 2919 //if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_delimiter: $this->get_pref_value(imap_server_type, '.$acctnum.') returns: ['.$this->get_pref_value('imap_server_type', $acctnum).'] ; api var SEP: ['.serialize(SEP).']<br />'); } 2920 if ($this->get_pref_value('imap_server_type', $acctnum) == 'UWash') 2921 { 2922 //$delimiter = '/'; 2923 //$delimiter = SEP; 2924 2925 // UWASH is a filesystem based thing, so the delimiter is whatever the system SEP is 2926 // unix = / and win = \ (win maybe even "\\" because the backslash needs escaping??? 2927 // currently the filesystem seterator is provided by phpgw api as constant "SEP" 2928 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_delimiter: imap_server_type is UWash<br />'); } 2929 if (!SEP) 2930 { 2931 $delimiter = '/'; 2932 } 2933 else 2934 { 2935 $delimiter = SEP; 2936 } 2937 } 2938 else 2939 { 2940 // GENERIC IMAP DELIMITER 2941 // imap servers usually use a "." as their delimiter 2942 // this is supposed to be discoverable with the NAMESPACE command 2943 // see http://www.rfc-editor.org/rfc/rfc2342.txt 2944 // however as of PHP 4.0 this is not implemented 2945 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_delimiter: imap_server_type is OTHER than UWash<br />'); } 2946 $delimiter = '.'; 2947 } 2948 // cache the result to "level 1 cache" class arg holder var 2949 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_mailsvr_delimiter: set "level 1 cache, class var" arg $this->set_arg_value(mailsvr_delimiter, '.$delimiter.', '.$acctnum.']) <br />'); } 2950 $this->set_arg_value('mailsvr_delimiter', $delimiter, $acctnum); 2951 2952 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_mailsvr_delimiter: LEAVING, returning: '.serialize($delimiter).' for $acctnum ['.$acctnum.']<br />'); } 2953 return $delimiter; 2954 } 2955 2956 /*! 2957 @function get_mailsvr_supports_folders 2958 @abstract imap and nntp servers have folders, pop has only inbox, this is a utility function. 2959 @result boolean 2960 @access private 2961 */ 2962 function get_mailsvr_supports_folders($acctnum='') 2963 { 2964 if ((!isset($acctnum)) 2965 || ((string)$acctnum == '')) 2966 { 2967 $acctnum = $this->get_acctnum(); 2968 } 2969 2970 // Does This Mailbox Support Folders (i.e. more than just INBOX)? 2971 2972 //if (($this->get_pref_value('mail_server_type',$acctnum ) == 'imap') 2973 //|| ($this->get_pref_value('mail_server_type', $acctnum) == 'imaps') 2974 //|| ($this->newsmode)) 2975 $mail_server_type = $this->get_pref_value('mail_server_type', $acctnum); 2976 if (stristr($mail_server_type, 'imap')) 2977 { 2978 return True; 2979 } 2980 else 2981 { 2982 return False; 2983 } 2984 } 2985 2986 /*! 2987 @function get_folder_long 2988 @abstract will generate the long name of an imap folder name, contains NAMESPACE_DELIMITER_FOLDER string 2989 but NOT the {serverName:port} part, (inline docparser repeat): ... but NOT the {serverName:port} part 2990 @param $feed_folder (string) optional, defaults to inbox 2991 @result string the long name of an imap folder name, contains NAMESPACE_DELIMITER_FOLDER string 2992 @discussion Note that syntax "{serverName:port}NAMESPACE_DELIMITER_FOLDER" is called a "fully qualified" 2993 folder name here. The param $feed_folder will be compared to the folder list supplied by the server to insure 2994 an accurate folder name is returned because a param $feed_folder LACKING a namespace or delimiter MUST 2995 have them added in order to become a "long" folder name, and just guessing is not good enough to ensure accuracy. 2996 Works with supported imap servers: UW-Maildir, Cyrus, Courier, UWash 2997 Example (Cyrus or Courier): INBOX.Templates 2998 Example (if subfolders a.k.a. "inferior folders" are enabled): INBOX.drafts.rfc 2999 ???? Example (UW-Maildir only): /home/James.Drafts ???? 3000 The above examle would suggext that UW-Maildir takes "~" as namespace and "/" as its pre-folder name delimiter, 3001 which as somewhat nonstandard because it appears the rest of the folder name uses "." as the delimiter. 3002 @access Public 3003 */ 3004 function get_folder_long($feed_folder='INBOX') 3005 { 3006 $feed_folder = urldecode($feed_folder); 3007 $folder = $this->ensure_no_brackets($feed_folder); 3008 if ($folder == 'INBOX') 3009 { 3010 // INBOX is (always?) a special reserved word with nothing preceeding it in long or short form 3011 $folder_long = 'INBOX'; 3012 } 3013 else 3014 { 3015 $name_space = $this->get_arg_value('mailsvr_namespace'); 3016 $delimiter = $this->get_arg_value('mailsvr_delimiter'); 3017 //if (strstr($folder,"$name_space" ."$delimiter") == False) 3018 // "INBOX" as namespace is NOT supposed to be case sensitive 3019 if (stristr($folder,"$name_space" ."$delimiter") == False) 3020 { 3021 // the [namespace][delimiter] string was not present 3022 // CONTROVERSIAL: add the [namespace][delimiter] string 3023 // this will incorrectly change a shared folder name, whose name may not 3024 // supposed to have the [namespace][delimiter] string 3025 $folder_long = "$name_space" ."$delimiter" ."$folder"; 3026 } 3027 else 3028 { 3029 // this folder is already in "long" format (it's namespace and delimiter already there) 3030 $folder_long = $folder; 3031 } 3032 } 3033 //echo 'get_folder_long('.$folder.')='.$folder_long.'<br />'; 3034 return trim($folder_long); 3035 } 3036 3037 /*! 3038 @function get_folder_short 3039 @abstract will generate the SHORT name of an imap folder name, i.e. strip off {SERVER}NAMESPACE_DELIMITER 3040 (inline docparser repeat): ... strip off {SERVER}NAMESPACE_DELIMITER 3041 @param $feed_folder string 3042 @result string the "shortened" name of a given imap folder name 3043 @discussion Simply, this is the folder name without the {serverName:port} nor the NAMESPACE nor the DELIMITER 3044 preceeding it. Inline docparser repeat: ... without the {serverName:port} nor the NAMESPACE 3045 nor the DELIMITER preceeding it. 3046 Works with supported imap servers UWash, UW-Maildir, Cyrus, Courier 3047 (old) Example (Cyrus or Courier): Templates 3048 (old) Example (Cyrus only): drafts.rfc 3049 */ 3050 function get_folder_short($feed_folder='INBOX', $acctnum='') 3051 { 3052 // Example: "Sent" 3053 // Cyrus may support "Sent.Today" 3054 // note: we need $acctnum to obtain the right namespace and delimiter, so we can strip them 3055 if ((!isset($acctnum)) 3056 || ((string)$acctnum == '')) 3057 { 3058 $acctnum = $this->get_acctnum(); 3059 } 3060 $feed_folder = urldecode($feed_folder); 3061 $folder = $this->ensure_no_brackets($feed_folder); 3062 if ($folder == 'INBOX') 3063 { 3064 // INBOX is (always?) a special reserved word with nothing preceeding it in long or short form 3065 $folder_short = 'INBOX'; 3066 } 3067 else 3068 { 3069 $name_space = $this->get_arg_value('mailsvr_namespace', $acctnum); 3070 $delimiter = $this->get_arg_value('mailsvr_delimiter', $acctnum); 3071 //if (strstr($folder,"$name_space" ."$delimiter") == False) 3072 // "INBOX" as namespace is NOT supposed to be case sensitive 3073 if (stristr($folder,"$name_space" ."$delimiter") == False) 3074 { 3075 $folder_short = $folder; 3076 } 3077 else 3078 { 3079 //$folder_short = strstr($folder,$delimiter); 3080 $folder_short = stristr($folder,$delimiter); 3081 // get rid of that delimiter (it's included from the stristr above) 3082 $folder_short = substr($folder_short, 1); 3083 } 3084 } 3085 return $folder_short; 3086 } 3087 3088 /*! 3089 @function folder_list_change_callback 3090 @abstract dcom class callback to alert when dcom class has made a change to the folder list 3091 @param $acctnum OPTIONAL 3092 discussion CACHE NOTE: this item is saved in the appsession cache, the folder_list, so altering 3093 that list requires wiping that saved info because it has become stale. 3094 @author Angles 3095 */ 3096 function folder_list_change_callback($acctnum='') 3097 { 3098 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: folder_list_change_callback('.__LINE__.'): ENTERING, param $acctnum ['.$acctnum.']<br />'); } 3099 // what acctnum is operative here, we can only get a folder list for one account at a time (obviously) 3100 if ((!isset($acctnum)) 3101 || ((string)$acctnum == '')) 3102 { 3103 $acctnum = $this->get_acctnum(); 3104 } 3105 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: folder_list_change_callback('.__LINE__.'): willo use $acctnum ['.$acctnum.']<br />'); } 3106 // class dcom recorded a change in the folder list 3107 // supposed to happen when create or delete or rename mailbox is called 3108 // dcom class will callback to this function to handle cleanup of stale folder_list data 3109 // expire cached data 3110 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: folder_list_change_callback('.__LINE__.'): calling $this->expire_session_cache_item(folder_list, '.$acctnum.') <br />'); } 3111 $sucess = $this->expire_session_cache_item('folder_list', $acctnum); 3112 3113 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: folder_list_change_callback('.__LINE__.'): LEAVING, returning $sucess ['.$sucess.']<br />'); } 3114 return $sucess; 3115 } 3116 3117 /*! 3118 @function get_folder_list 3119 @abstract list of folders in a numbered array, each element has 2 properties, "folder_long" and "folder_short" 3120 @param $acctnum (int) OPTIONAL 3121 @param $force_refresh boolean, will cause any cached folder data to expire, and "fresh" data is retrieved from the mailserver 3122 @return array numbered, with each numbered element having array keys "folder_long" and "folder_short" 3123 @discussion returns a numbered array, each element has 2 properties, "folder_long" and "folder_short" 3124 so every available folder is in the structure in both long form [namespace][delimiter][foldername] 3125 and short form (does not have the [namespace][delimiter] prefix to the folder name) 3126 This function can cache data in 2 ways 3127 (1) caching as server data in the prefs DB cache department, and 3128 (2) in the class var $this->get_arg_value("folder_list") 3129 Data will be grabbed from cache when available and when allowed. 3130 CACHE NOTE: this item is saved in the appsession cache. 3131 @access private - public access is object->get_arg_value("folder_list") but may be 3132 called directly if you need to manually force_refresh any cached data, although this is still for 3133 private use as well. 3134 */ 3135 function get_folder_list($acctnum='', $force_refresh=False) 3136 { 3137 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_folder_list: ENTERING<br />'); } 3138 // what acctnum is operative here, we can only get a folder list for one account at a time (obviously) 3139 if ((!isset($acctnum)) 3140 || ((string)$acctnum == '')) 3141 { 3142 $acctnum = $this->get_acctnum(); 3143 } 3144 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: for the rest of this function we will use $acctnum: ['.$acctnum.'] <br />'); } 3145 // hardcore debug 3146 if (stristr($this->skip_args_special_handlers, 'get_folder_list')) 3147 { 3148 $fake_return = array(); 3149 $fake_return[0] = array(); 3150 $fake_return[0]['folder_long'] = 'INBOX'; 3151 $fake_return[0]['folder_short'] = 'INBOX'; 3152 $fake_return[0]['acctnum'] = $acctnum; 3153 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_folder_list: LEAVING, debug SKIP, $fake_return: '.serialize($fake_return).' <br />'); } 3154 return $fake_return; 3155 } 3156 3157 //if ($this->debug_args_special_handlers > 2) { $this->dbug->out('mail_msg: get_folder_list: $this->_direct_access_arg_value(folder_list, '.$acctnum.') dump:<pre>'; print_r($this->_direct_access_arg_value('folder_list', $acctnum)); echo '</pre>'); } 3158 3159 // check if class dcom reports that the folder list has changed 3160 // is this accounts dcom object has not been created yet, then obviously we did not just change its folder list 3161 // NOTE THIS IS OBSOLETED - THE DCOM CLASS NOW USES CALLBACK FUNCTION "folder_list_change_callback" 3162 if ((is_object($GLOBALS['phpgw_dcom_'.$acctnum]->dcom)) 3163 && ($GLOBALS['phpgw_dcom_'.$acctnum]->dcom->folder_list_changed == True)) 3164 { 3165 // class dcom recorded a change in the folder list 3166 // supposed to happen when create or delete mailbox is called 3167 // reset the changed flag 3168 $GLOBALS['phpgw_dcom_'.$acctnum]->dcom->folder_list_changed = False; 3169 // set up for a force_refresh 3170 $force_refresh = True; 3171 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: class dcom report folder list changed<br />'); } 3172 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: make sure folder data is removed from cache <br />'); } 3173 // expire appsession cache 3174 $this->expire_session_cache_item('folder_list', $acctnum); 3175 } 3176 3177 // see if we have object class var cached data that we can use 3178 $class_cached_folder_list = $this->_direct_access_arg_value('folder_list', $acctnum); 3179 if ($this->debug_args_special_handlers > 2) { $this->dbug->out('mail_msg: get_folder_list: $this->_direct_access_arg_value(folder_list, '.$acctnum.') DUMP:', $this->_direct_access_arg_value('folder_list', $acctnum)); } 3180 if ((count($class_cached_folder_list) > 0) 3181 && ($force_refresh == False)) 3182 { 3183 // use the cached data 3184 if ($this->debug_args_special_handlers > 2) { $this->dbug->out(' * * $class_cached_folder_list DUMP:', $class_cached_folder_list); } 3185 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_folder_list: LEAVING, using object cached folder list data<br />'); } 3186 return $class_cached_folder_list; 3187 } 3188 elseif (($this->get_pref_value('mail_server_type', $acctnum) == 'pop3') 3189 || ($this->get_pref_value('mail_server_type', $acctnum) == 'pop3s')) 3190 { 3191 // normalize the folder_list property 3192 $my_folder_list = array(); 3193 // POP3 servers have 1 folder: INBOX 3194 $my_folder_list[0] = array(); 3195 $my_folder_list[0]['folder_long'] = 'INBOX'; 3196 $my_folder_list[0]['folder_short'] = 'INBOX'; 3197 $my_folder_list[0]['acctnum'] = $acctnum; 3198 // save result to "Level 1 cache" class arg holder var 3199 $this->set_arg_value('folder_list', $my_folder_list, $acctnum); 3200 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_folder_list: LEAVING, pop3 servers only have one folder: INBOX<br />'); } 3201 return $my_folder_list; 3202 } 3203 elseif ($force_refresh == False) 3204 { 3205 // ----------- 3206 // TRY CACHED DATA FROM APPSESSION 3207 // ----------- 3208 // try to restore "folder_list" from saved appsession data store 3209 $appsession_cached_folder_list = $this->read_session_cache_item('folder_list', $acctnum); 3210 if ($appsession_cached_folder_list) 3211 { 3212 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: got appsession cached data<br />'); } 3213 $cached_data = $appsession_cached_folder_list; 3214 if ($this->debug_args_special_handlers > 2) { $this->dbug->out('mail_msg: get_folder_list: appsession cached data DUMP:', $cached_data); } 3215 // we no longer need this var 3216 $appsession_cached_folder_list = ''; 3217 unset($appsession_cached_folder_list); 3218 } 3219 else 3220 { 3221 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: NO appsession cached data was available<br />'); } 3222 $cached_data = False; 3223 } 3224 3225 // if there's no data we'll get back a FALSE 3226 if ($cached_data) 3227 { 3228 //if ($this->debug_args_special_handlers > 1) { echo 'mail_msg: get_folder_list: using *Prefs DB* cached folder list data<br />';} 3229 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: using appsession cached folder list data<br />'); } 3230 if (!isset($cached_data[0]['folder_short'])) 3231 { 3232 // OLD cached folder list does NOT contain "folder_short" data 3233 // that cuts cached data in 1/2, no need to cache something this easy to deduce 3234 // therefor... add FOLDER SHORT element to cached_data array structure 3235 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: (L1) adding [folder_short] element to $cached_data array<br />'); } 3236 for ($i=0; $i<count($cached_data);$i++) 3237 { 3238 $my_folder_long = $cached_data[$i]['folder_long']; 3239 $my_folder_acctnum = $cached_data[$i]['acctnum']; 3240 $my_folder_short = $this->get_folder_short($my_folder_long, $my_folder_acctnum); 3241 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('* * mail_msg: get_folder_list: add folder_short loop (L1) ['.$i.']: $my_folder_long ['.$my_folder_long.'] ; $my_folder_acctnum ['.$my_folder_acctnum.'] ; $my_folder_short ['.$my_folder_short.']<br />'); } 3242 $cached_data[$i]['folder_short'] = $my_folder_short; 3243 //$cached_data[$i]['folder_short'] = $this->get_folder_short($cached_data[$i]['folder_long']); 3244 if ($this->debug_args_special_handlers > 2) { $this->dbug->out(' * * $cached_data['.$i.'][folder_long]='.htmlspecialchars($cached_data[$i]['folder_long']).' ; $cached_data['.$i.'][folder_short]='.htmlspecialchars($cached_data[$i]['folder_short']).'<br />'); } 3245 } 3246 if ($this->debug_args_special_handlers > 2) { $this->dbug->out('mail_msg: get_folder_list: $cached_data *after* adding "folder_short" data DUMP:', $cached_data); } 3247 // ----------- 3248 // SAVE DATA TO APPSESSION DB CACHE (WITH the [folder_short] data) 3249 // ----------- 3250 // save "folder_list" (WITH ADDED folder short data) to appsession data store 3251 // new style folder_list is stored FULL, has all elements 3252 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: added folder short, now resave in DB as new style, complete folder list, set appsession cache $this->save_session_cache_item(folder_list, $cached_data, '.$acctnum.']) <br />'); } 3253 $this->save_session_cache_item('folder_list', $cached_data, $acctnum); 3254 } 3255 // cache the result in "Level 1 cache" class object var 3256 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: put folder_list into Level 1 class var "cache" $this->set_arg_value(folder_list, $cached_data, '.$acctnum.');<br />'); } 3257 $this->set_arg_value('folder_list', $cached_data, $acctnum); 3258 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_folder_list: LEAVING, got data from cache<br />'); } 3259 return $cached_data; 3260 } 3261 else 3262 { 3263 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: NO cached folder list data, fallback to get data from mailserver<br />'); } 3264 } 3265 } 3266 3267 // if we get here we must actually get the data from the mailsvr 3268 // otherwise we would have return/broke out of this function 3269 // only IF statement above that allows code to reach here is if we are allowed to use 3270 // cached data, BUT none exists 3271 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: need to get data from mailserver<br />'); } 3272 3273 // Establish Email Server Connectivity Information 3274 $mailsvr_stream = $this->get_arg_value('mailsvr_stream', $acctnum); 3275 $mailsvr_callstr = $this->get_arg_value('mailsvr_callstr', $acctnum); 3276 $name_space = $this->get_arg_value('mailsvr_namespace', $acctnum); 3277 $delimiter = $this->get_arg_value('mailsvr_delimiter', $acctnum); 3278 3279 // get a list of available folders from the server 3280 if ($this->get_pref_value('imap_server_type', $acctnum) == 'UWash') 3281 { 3282 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: mailserver is of type UWash<br />'); } 3283 /*! 3284 @concept UWash IMAP Namespace 3285 @discussion uwash is file system based, so it requires a filesystem slash after the namespace. 3286 Note that with uwash the delimiter is in fact the file system slash. 3287 example: requesting list for "mail/*" 3288 (NOTE this <slash><star> request will NOT yield a list the INBOX folder included in the returned folder list). 3289 However, we have no choice since without the <slash> filesystem delimiter, requesting "email*" returns NOTHING. 3290 Example queries: (a) "~/" 3291 OR (b) if the user specifies specific mbox folder, then: "~/emails/*" 3292 OR (c) "emails/*" give the same result, much like a unix "ls" command. 3293 At this time we use "unqualified" a.k.a. "relative" directory names if the user provides a namespace. 3294 UWash will consider it relative to the mailuser's $HOME property as with "emails/*" (DOES THIS WORK ON ALL PLATFORMS??). 3295 BUT we use <tilde><slash> "~/" if no namespace is given. 3296 @returns 3297 UWASH IMAP returns information in this format (same as any other IMAP server format): 3298 {SERVER_NAME:PORT}FOLDERNAME 3299 (inline docparser repeat): {SERVER_NAME:PORT}FOLDERNAME 3300 For Example: 3301 {some.server.com:143}Trash AND 3302 {some.server.com:143}Archives/Letters 3303 (inline docparser repeat): 3304 {some.server.com:143}Trash AND 3305 {some.server.com:143}Archives/Letters 3306 */ 3307 $mailboxes = $this->phpgw_listmailbox($mailsvr_callstr, "$name_space" ."$delimiter" ."*", $acctnum); 3308 // UWASH IMAP returns information in this format: 3309 // {SERVER_NAME:PORT}FOLDERNAME 3310 // example: 3311 // {some.server.com:143}Trash 3312 // {some.server.com:143}Archives/Letters 3313 } 3314 else 3315 { 3316 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: mailserver is other than UWash type<br />'); } 3317 /*! 3318 @concept non-UWash IMAP Server Namespace 3319 @discussion when handling handle non-UWash IMAP servers, 3320 i.e. typical IMAP servers that do not use a filesystem slash as the "delimiter", 3321 the last arg is typically "INBOX*" (no dot) which DOES include the inbox in the list of folders. 3322 Wheres adding the delimiter "INBOX.*" (has dot) will NOT include the INBOX in the list of folders. 3323 So - it's theoretically safe to include the delimiter here, but the INBOX will not be included in the list, 3324 this is typically the ONLY TIME you would ever *not* use the delimiter between the namespace and what comes after it. 3325 HOWEVER to get *shared* folders included in the return, better NOT include the "." delimiter. 3326 For example: Cyrus does not like anything but a "*" as the pattern IF you want shared folders returned. 3327 Return data is a list suck as this: 3328 {some.server.com:143}INBOX 3329 {some.server.com:143}INBOX.Trash 3330 (inline docparser repeat): 3331 {some.server.com:143}INBOX AND 3332 {some.server.com:143}INBOX.Trash 3333 */ 3334 $mailboxes = $this->phpgw_listmailbox($mailsvr_callstr, "*", $acctnum); 3335 // returns information in this format: 3336 // {SERVER_NAME:PORT} NAMESPACE DELIMITER FOLDERNAME 3337 // example: 3338 // {some.server.com:143}INBOX 3339 // {some.server.com:143}INBOX.Trash 3340 } 3341 if ($this->debug_args_special_handlers > 2) { $this->dbug->out('mail_msg: get_folder_list: server returned $mailboxes DUMP:', $mailboxes); } 3342 //echo 'raw mailbox list:<br />'.htmlspecialchars(serialize($mailboxes)).'<br />'; 3343 3344 // ERROR DETECTION 3345 if (!$mailboxes) 3346 { 3347 // we got no information back, clear the folder_list property 3348 // normalize the folder_list property 3349 $my_folder_list = array(); 3350 // *assume* (i.e. pretend) we have a server with only one box: INBOX 3351 $my_folder_list[0] = array(); 3352 $my_folder_list[0]['folder_long'] = 'INBOX'; 3353 $my_folder_list[0]['folder_short'] = 'INBOX'; 3354 $my_folder_list[0]['acctnum'] = $acctnum; 3355 // save result to "Level 1 cache" class arg holder var 3356 $this->set_arg_value('folder_list', $my_folder_list, $acctnum); 3357 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: error, no mailboxes returned from server, fallback to "INBOX" as only folder, $this->set_arg_value(folder_list, $my_folder_list) to hold that value<br />'); } 3358 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_folder_list: LEAVING, with error, no mailboxes returned from server, return list with only INBOX<br />'); } 3359 return $my_folder_list; 3360 } 3361 3362 // was INBOX included in the list? Some servers (uwash) do not return it 3363 $has_inbox = False; 3364 for ($i=0; $i<count($mailboxes);$i++) 3365 { 3366 $this_folder = $this->get_folder_short($mailboxes[$i]); 3367 //if ($this_folder == 'INBOX') 3368 // rfc2060 says "INBOX" as a namespace can not be case sensitive 3369 if ((stristr($this_folder, 'INBOX')) 3370 && (strlen($this_folder) == strlen('INBOX'))) 3371 { 3372 $has_inbox = True; 3373 break; 3374 } 3375 } 3376 // ADD INBOX if necessary 3377 if ($has_inbox == False) 3378 { 3379 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: adding INBOX to mailboxes data<br />'); } 3380 // use the same "fully qualified" folder name format that "phpgw_listmailbox" returns, includes the {serverName:port} 3381 $add_inbox = $mailsvr_callstr.'INBOX'; 3382 $next_available = count($mailboxes); 3383 // add it to the $mailboxes array 3384 $mailboxes[$next_available] = $add_inbox; 3385 } 3386 3387 // sort folder names 3388 // note: php3 DOES have is_array(), ok to use it here 3389 if (is_array($mailboxes)) 3390 { 3391 // mainly to avoid warnings 3392 sort($mailboxes); 3393 } 3394 3395 // normalize the folder_list property, we will transfer raw data in $mailboxes array to processed data in $my_folder_list 3396 $my_folder_list = array(); 3397 3398 // make a $my_folder_list array structure with ONLY FOLDER LONG data 3399 // save that to cache, that cuts cached data in 1/2 3400 // (LATER - we will add the "folder_short" data) 3401 for ($i=0; $i<count($mailboxes);$i++) 3402 { 3403 // "is_imap_folder" really just a check on what UWASH imap returns, may be files that are not MBOX's 3404 if ($this->is_imap_folder($mailboxes[$i])) 3405 { 3406 //$this->a[$acctnum]['folder_list'][$i]['folder_long'] = $this->get_folder_long($mailboxes[$i]); 3407 // what we (well, me, Angles) calls a "folder long" is the raw data returned from the server (fully qualified name) 3408 // MINUS the bracketed server, so we are calling "folder long" a NAMESPACE_DELIMITER_FOLDER string 3409 // WITHOUT the {serverName:port} part, if that part is included we (Angles) call this "fully qualified" 3410 $next_idx = count($my_folder_list); 3411 $my_folder_list[$next_idx]['folder_long'] = $this->ensure_no_brackets($mailboxes[$i]); 3412 // AS SOON as possible, add data indicating WHICH ACCOUNT this folder list came from 3413 // while it is still somewhat easy to determine this 3414 $my_folder_list[$next_idx]['acctnum'] = $acctnum; 3415 } 3416 } 3417 if ($this->debug_args_special_handlers > 2) { $this->dbug->out('mail_msg: get_folder_list: my_folder_list with only "folder_long" DUMP:', $my_folder_list); } 3418 // NEW just save the complete list, leaving out the folder short does not reduce size my a material amount 3419 //// ----------- 3420 //// SAVE DATA TO APPSESSION DB CACHE (without the [folder_short] data) 3421 //// ----------- 3422 //// save "folder_list" (without folder short data) to appsession data store 3423 //if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: set appsession cache $this->save_session_cache_item(folder_list, $my_folder_list, '.$acctnum.']) <br />'); } 3424 //$this->save_session_cache_item('folder_list', $my_folder_list, $acctnum); 3425 3426 // add FOLDER SHORT element to folder_list array structure 3427 // that cuts cached data in 1/2, no need to cache something this easy to deduce 3428 // NEW: forget about it, just add folder short THEN SAVE it, additional data is not that much more 3429 for ($i=0; $i<count($my_folder_list);$i++) 3430 { 3431 $my_folder_long = $my_folder_list[$i]['folder_long']; 3432 $my_folder_acctnum = $my_folder_list[$i]['acctnum']; 3433 $my_folder_short = $this->get_folder_short($my_folder_long, $my_folder_acctnum); 3434 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: add folder_short loop['.$i.']: $my_folder_long ['.$my_folder_long.'] ; $my_folder_acctnum ['.$my_folder_acctnum.'] ; $my_folder_short ['.$my_folder_short.']<br />'); } 3435 $my_folder_list[$i]['folder_short'] = $my_folder_short; 3436 } 3437 3438 // ----------- 3439 // SAVE DATA TO APPSESSION DB CACHE (WITH the [folder_short] data) 3440 // ----------- 3441 // save "folder_list" (WITH ADDED folder short data) to appsession data store 3442 // new style folder_list is stored FULL, has all elements 3443 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: set appsession cache $this->save_session_cache_item(folder_list, $my_folder_list, '.$acctnum.']) <br />'); } 3444 $this->save_session_cache_item('folder_list', $my_folder_list, $acctnum); 3445 3446 // cache the result to "level 1 cache" class arg holder var 3447 if ($this->debug_args_special_handlers > 1) { $this->dbug->out('mail_msg: get_folder_list: set Level 1 class var "cache" $this->set_arg_value(folder_list, $my_folder_list, '.$acctnum.') <br />'); } 3448 $this->set_arg_value('folder_list', $my_folder_list, $acctnum); 3449 3450 // finished, return the folder_list array atructure 3451 if ($this->debug_args_special_handlers > 2) { $this->dbug->out('mail_msg: get_folder_list: finished, $my_folder_list DUMP:', $my_folder_list); } 3452 if ($this->debug_args_special_handlers > 0) { $this->dbug->out('mail_msg: get_folder_list: LEAVING, got folder data from server<br />'); } 3453 return $my_folder_list; 3454 } 3455 3456 3457 /*! 3458 @function folder_lookup 3459 @abstract searches thru the list of available folders to determine if a given folder already exists 3460 @param $mailsvr_stream - DEPRECIATED to not use, class provides this. 3461 @param $folder_needle string ? 3462 @param $acctnum int ? 3463 @discussion uses "folder_list[folder_long]" as the "haystack" because it is the most unaltered folder 3464 information returned from the server that we have 3465 if TRUE, then the "official" folder_long name is returned - the one supplied by the server itself 3466 during the get_folder_list routine - "folder_list[folder_long]" 3467 if False, an empty string is returned. 3468 */ 3469 function folder_lookup($mailsvr_stream, $folder_needle='INBOX', $acctnum='') 3470 { 3471 if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: folder_lookup: ENTERING , feed $folder_needle: ['.htmlspecialchars($folder_needle).'], feed (optional) $acctnum: ['.$acctnum.']<br />'); } 3472 if ((!isset($acctnum)) 3473 || ((string)$acctnum == '')) 3474 { 3475 $acctnum = $this->get_acctnum(); 3476 } 3477 3478 //$folder_list = $this->get_folder_list($acctnum); 3479 $folder_list = $this->get_arg_value('folder_list', $acctnum); 3480 //$folder_list =& $this->get_arg_value_ref('folder_list', $acctnum); 3481 3482 if ($this->debug_args_input_flow > 1) 3483 { 3484 $debug_folder_lookup = True; 3485 } 3486 else 3487 { 3488 $debug_folder_lookup = False; 3489 } 3490 3491 // retuen an empty string if the lookup fails 3492 $needle_official_long = ''; 3493 for ($i=0; $i<count($folder_list);$i++) 3494 { 3495 // folder_haystack is the official folder long name returned from the server during "get_folder_list" 3496 $folder_haystack = $folder_list[$i]['folder_long']; 3497 if ($debug_folder_lookup) { $this->dbug->out(' _ ['.$i.'] [folder_needle] '.$folder_needle.' len='.strlen($folder_needle).' [folder_haystack] '.$folder_haystack.' len='.strlen($folder_haystack).'<br />' ); } 3498 3499 // first try to match the whole name, i.e. needle is already a folder long type name 3500 // the NAMESPACE should NOT be case sensitive 3501 // mostly, this means "INBOX" must not be matched case sensitively 3502 if (stristr($folder_haystack, $folder_needle)) 3503 { 3504 if ($debug_folder_lookup) { $this->dbug->out(' _ entered stristr statement<br />'); } 3505 if (strlen($folder_haystack) == strlen($folder_needle)) 3506 { 3507 // exact match - needle is already a fully legit folder_long name 3508 if ($debug_folder_lookup) { $this->dbug->out(' _ folder exists, exact match, already legit long name: '.$needle_official_long.'<br />'); } 3509 $needle_official_long = $folder_haystack; 3510 break; 3511 } 3512 if ($debug_folder_lookup) { $this->dbug->out(' _ exact match failed<br />'); } 3513 // if the needle is smaller than the haystack, then it is possible that the 3514 // needle is a partial folder name that will match a portion of the haystack 3515 // look for pattern [delimiter][folder_needle] in the last portion of string haystack 3516 // because we do NOT want to match a partial word, folder_needle should be a whole folder name 3517 //tried this: if (preg_match('/.*([\]|[.]|[\\/]){1}'.$folder_needle.'$/i', $folder_haystack)) 3518 // problem: unescaped forward slashes will be in UWASH folder names needles 3519 // and unescaped dots will be in other folder names needles 3520 // so use non-regex comparing 3521 // haystack must be larger then needle+1 (needle + a delimiter) for this to work 3522 if (strlen($folder_haystack) > strlen($folder_needle)) 3523 { 3524 if ($debug_folder_lookup) { $this->dbug->out(' _ entered partial match logic<br />'); } 3525 // at least the needle is somewhere in the haystack 3526 // 1) get the length of the needle 3527 $needle_len = strlen($folder_needle); 3528 // get a negative value for use in substr 3529 $needle_len_negative = ($needle_len * (-1)); 3530 // go back one more char in haystack to get the delimiter 3531 $needle_len_negative = $needle_len_negative - 1; 3532 if ($debug_folder_lookup) { $this->dbug->out(' _ needle_len: '.$needle_len.' and needle_len_negative-1: '.$needle_len_negative.'<br />' ); } 3533 // get the last part of haystack that is that length 3534 $haystack_end = substr($folder_haystack, $needle_len_negative); 3535 // look for pattern [delimiter][folder_needle] 3536 // because we do NOT want to match a partial word, folder_needle should be a whole folder name 3537 if ($debug_folder_lookup) { $this->dbug->out(' _ haystack_end: '.$haystack_end.'<br />' ); } 3538 if ((stristr('/'.$folder_needle, $haystack_end)) 3539 || (stristr('.'.$folder_needle, $haystack_end)) 3540 || (stristr('\\'.$folder_needle, $haystack_end))) 3541 { 3542 $needle_official_long = $folder_haystack; 3543 if ($debug_folder_lookup) { $this->dbug->out(' _ folder exists, lookup found partial match, official long name: '.$needle_official_long.'<br />'); } 3544 break; 3545 } 3546 if ($debug_folder_lookup) { $this->dbug->out(' _ partial match failed<br />'); } 3547 } 3548 } 3549 } 3550 if ($this->debug_args_input_flow > 0) { $this->dbug->out('mail_msg: folder_lookup: LEAVING, returning $needle_official_long: ['.htmlspecialchars($needle_official_long).']<br />'); } 3551 return $needle_official_long; 3552 } 3553 3554 /*! 3555 @function is_imap_folder 3556 @abstract Used with UWash servers folder list to filter out names that are most like not folders. 3557 @param $folder string ? 3558 @result boolean 3559 @discussion returns True if the given foldername is probable an IMAP folder. Uses some general 3560 criteria such as anything with a DOT_SLASH is probably not the name of an IMAP folder. 3561 */ 3562 function is_imap_folder($folder) 3563 { 3564 // UWash is the only (?) imap server where there is any question whether a folder is legit or not 3565 if ($this->get_pref_value('imap_server_type') != 'UWash') 3566 { 3567 //echo 'is_imap_folder TRUE 1<br />'; 3568 return True; 3569 } 3570 3571 $folder_long = $this->get_folder_long($folder); 3572 3573 // INBOX is ALWAYS a valid folder, and is ALWAYS called INBOX because it's a special reserved word 3574 // although it is NOT case sensitive 3575 //if ($folder_long == 'INBOX') 3576 if ((stristr($folder_long, 'INBOX')) 3577 && (strlen($folder_long) == strlen('INBOX'))) 3578 { 3579 //echo 'is_imap_folder TRUE 2<br />'; 3580 return True; 3581 } 3582 3583 // UWash IMAP server looks for MBOX files, which it considers to be email "folders" 3584 // and will return any file, whether it's an actual IMAP folder or not 3585 if (strstr($folder_long,"/.")) 3586 { 3587 // any pattern matching "/." for UWash is NOT an MBOX 3588 // because the delimiter for UWash is "/" and the immediately following "." indicates a hidden file 3589 // not an MBOX file, at least on Linux type system 3590 //echo 'is_imap_folder FALSE 3<br />'; 3591 return False; 3592 } 3593 3594 // if user specifies namespace like "mail" then MBOX files are in $HOME/mail 3595 // so this server knows to put MBOX files in a special place 3596 // BUT if the namespace used is associated with $HOME, such as ~ 3597 // then how many folders deep do you want to go? UWash is recursive, it will go as deep as possible into $HOME 3598 3599 // is this a $HOME type of namespace 3600 $the_namespace = $this->get_arg_value('mailsvr_namespace'); 3601 if ($the_namespace == '~') 3602 { 3603 $home_type_namespace = True; 3604 } 3605 else 3606 { 3607 $home_type_namespace = False; 3608 } 3609 3610 // DECISION: no more than 4 DIRECTORIES DEEP of recursion 3611 $num_slashes = $this->substr_count_ex($folder_long, "/"); 3612 if (($home_type_namespace) 3613 && ($num_slashes >= 4)) 3614 { 3615 // this folder name indicates we are too deeply recursed, we don't care beyond here 3616 //echo 'is_imap_folder FALSE 4<br />'; 3617 return False; 3618 } 3619 3620 // if you get all the way to here then this must be a valid folder name 3621 //echo 'is_imap_folder TRUE 5<br />'; 3622 return True; 3623 } 3624 3625 /*! 3626 @function care_about_unseen DEPRECIATED 3627 @abstract When reporting the number of unseen, or new, messages in a folder, we may skip 3628 folders such as Trash and Sent because we do not care what is unseen in those folders. 3629 @param $folder string ? 3630 @result boolean 3631 @discussion DEPRECIATED function was used when the number of unseen messages in a 3632 folder was included in the dropdown folder list combobox HTML select widget. 3633 */ 3634 function care_about_unseen($folder) 3635 { 3636 $folder = $this->get_folder_short($folder); 3637 // we ALWAYS care about new messages in the INBOX 3638 if ((stristr($folder_long, 'INBOX')) 3639 && (strlen($folder_long) == strlen('INBOX'))) 3640 { 3641 return True; 3642 } 3643 3644 $we_care = True; // initialize 3645 $ignore_these_folders = Array(); 3646 // DO NOT CHECK UNSEEN for these folders 3647 $ignore_these_folders[0] = "sent"; 3648 $ignore_these_folders[1] = "trash"; 3649 $ignore_these_folders[2] = "templates"; 3650 for ($i=0; $i<count($ignore_these_folders); $i++) 3651 { 3652 $match_this = $ignore_these_folders[$i]; 3653 if (eregi("^.*$match_this$",$folder)) 3654 { 3655 $we_care = False; 3656 break; 3657 } 3658 } 3659 return $we_care; 3660 } 3661 3662 3663 3664 3665 // ---- Password Crypto Workaround broken common->en/decrypt ----- 3666 /*! 3667 @function encrypt_email_passwd 3668 @abstract passes directly to crypto class 3669 @param $data data string to be encrypted 3670 @discussion if mcrypt is not enabled, then the password data should be unmolested thru the 3671 crypto functions, i.e. do not alter the string if mcrypt will not be preformed on that string. 3672 */ 3673 function encrypt_email_passwd($data) 3674 { 3675 if(!is_object($this->crypto)) 3676 { 3677 $cryptovars[0] = md5($GLOBALS['phpgw_info']['server']['encryptkey']); 3678 $cryptovars[1] = $GLOBALS['phpgw_info']['server']['mcrypt_iv']; 3679 $this->crypto = CreateObject('phpgwapi.crypto',$cryptovars); 3680 } 3681 return $this->crypto->encrypt($data); 3682 } 3683 3684 /*! 3685 @function decrypt_email_pass 3686 @abstract decrypt $data 3687 @param $data data to be decrypted 3688 @discussion if mcrypt is not enabled, then the password data should be unmolested thru the 3689 crypto functions, i.e. do not alter the string if mcrypt will not be preformed on that string. 3690 */ 3691 function decrypt_email_passwd($data) 3692 { 3693 if(!is_object($this->crypto)) 3694 { 3695 $cryptovars[0] = md5($GLOBALS['phpgw_info']['server']['encryptkey']); 3696 $cryptovars[1] = $GLOBALS['phpgw_info']['server']['mcrypt_iv']; 3697 $this->crypto = CreateObject('phpgwapi.crypto',$cryptovars); 3698 } 3699 $unencrypted = $this->crypto->decrypt($data); 3700 $encrypted = $this->crypto->encrypt($unencrypted); 3701 if($data <> $encrypted) 3702 { 3703 $unencrypted = $GLOBALS['phpgw']->crypto->decrypt($data); 3704 $encrypted = $GLOBALS['phpgw']->crypto->encrypt($unencrypted); 3705 } 3706 return $unencrypted; 3707 } 3708 /* 3709 // THIS CODE is depreciated, it was needed before the crypto in the API was fixed 3710 // However, the issue of screwed up, multiply serialized passwords from pre 0.9.12 is 3711 // perhaps still an issue, maybe this code will need to be used on rare occasions 3712 function decrypt_email_passwd($data) 3713 { 3714 if ($GLOBALS['phpgw_info']['server']['mcrypt_enabled'] && extension_loaded('mcrypt')) 3715 { 3716 $passwd = $data; 3717 // this will return a string that has: 3718 // (1) been decrypted with mcrypt (assuming mcrypt is enabled and working) 3719 // (2) had stripslashes applied and 3720 // (3) *MAY HAVE* been unserialized (ambiguous... see next comment) 3721 // correction Dec 14, 2001, (3) and definately was unserialized 3722 $cryptovars[0] = md5($GLOBALS['phpgw_info']['server']['encryptkey']); 3723 $cryptovars[1] = $GLOBALS['phpgw_info']['server']['mcrypt_iv']; 3724 $crypto = CreateObject('phpgwapi.crypto', $cryptovars); 3725 //$passwd = $crypto->decrypt($passwd); 3726 $passwd = $crypto->decrypt_mail_pass($passwd); 3727 } 3728 else 3729 { 3730 $passwd = $data; 3731 // ASSUMING set_magic_quotes_runtime(0) has been specified, then 3732 // there should be NO escape slashes coming from the database 3733 //if ($this->is_serialized($passwd)) 3734 if ($this->is_serialized_str($passwd)) 3735 { 3736 $passwd = unserialize($passwd); 3737 } 3738 3739 3740 // #### (begin) Upgrade Routine for 0.9.12 and earlier versions #### 3741 /*! 3742 @capability Upgrade Routine for 0.9.12 and earlier Custom Passwords DEPRECIATED 3743 @discussion the phpgw versions prior to and including 0.9.12 *may* have double or even tripple serialized 3744 passwd strings stored in their preferences table. SO: 3745 (1) check for this 3746 (2) unserialize to the real string 3747 (3) feed the unserialized / fixed passwd in the prefs class and save the "upgraded" passwd 3748 */ 3749 3750 /* 3751 // (1) check for this 3752 //$multi_serialized = $this->is_serialized($passwd); 3753 $multi_serialized = $this->is_serialized_str($passwd); 3754 if ($multi_serialized) 3755 { 3756 $pre_upgrade_passwd = $passwd; 3757 // (2) unserialize to the real string 3758 $failure = 10; 3759 $loop_num = 0; 3760 do 3761 { 3762 $loop_num++; 3763 if ($loop_num == $failure) 3764 { 3765 break; 3766 } 3767 $passwd = unserialize($passwd); 3768 } 3769 //while ($this->is_serialized($passwd)); 3770 while ($this->is_serialized_str($passwd)); 3771 3772 // 10 loops is too much, something is wrong 3773 if ($loop_num == $failure) 3774 { 3775 // screw it and continue as normal, user will need to reenter password 3776 echo 'ERROR: decrypt_email_passwd: custom pass upgrade procedure failed to restore passwd to useable state<br />'; 3777 $passwd = $pre_upgrade_passwd; 3778 } 3779 else 3780 { 3781 // (3) SAVE THE FIXED / UPGRADED PASSWD TO PREFS 3782 // feed the unserialized / fixed passwd in the prefs class 3783 $GLOBALS['phpgw']->preferences->delete('email','passwd'); 3784 // make any html quote entities back to real form (i.e. ' or ") 3785 $encrypted_passwd = $this->html_quotes_decode($passwd); 3786 // encrypt it as it would be as if the user had just submitted the preferences page (no need to strip slashes, no POST occured) 3787 $encrypted_passwd = $this->encrypt_email_passwd($passwd); 3788 // store in preferences so this does not happen again 3789 $GLOBALS['phpgw']->preferences->add('email','passwd',$encrypted_passwd); 3790 $GLOBALS['phpgw']->preferences->save_repository(); 3791 } 3792 } 3793 // #### (end) Upgrade Routine for 0.9.12 and earlier versions #### 3794 3795 $passwd = $this->html_quotes_decode($passwd); 3796 //echo 'decrypt_email_passwd result: '.$passwd; 3797 } 3798 return $passwd; 3799 } 3800 */ 3801 3802 // ---- Make Address accoring to RFC2822 Standards ----- 3803 /*! 3804 @function make_rfc2822_address 3805 @abstract Make Address accoring to RFC2822 Standards 3806 @param $addy_data object from php email address structure from IMAP_HEADER 3807 @param $html_encode boolean used to encode HTML offensive chars for display in a browser. 3808 @result string 3809 @author Angles 3810 @discussion will produce a string containing email addresses for (a) display a browser such as 3811 on the compose form, or (b) for use in an email mesage header, hence the standards 3812 compliance necessity, message headers must follow strict form. 3813 */ 3814 function make_rfc2822_address($addy_data, $html_encode=True) 3815 { 3816 //echo '<br />'.$this->htmlspecialchars_encode(serialize($addy_data)).'<br />'.'<br />'; 3817 3818 if ((!isset($addy_data->mailbox)) && (!$addy_data->mailbox) 3819 && (!isset($addy_data->host)) && (!$addy_data->host)) 3820 { 3821 // fallback value, we do not want to sent a string like this "@" if no data if available 3822 return ''; 3823 } 3824 // now we can continue, 1st make a simple, plain address 3825 // RFC2822 allows this simple form if not using "personal" info 3826 $rfc_addy = $addy_data->mailbox.'@'.$addy_data->host; 3827 // add "personal" data if it exists 3828 if (isset($addy_data->personal) && ($addy_data->personal)) 3829 { 3830 // why DECODE when we are just going to feed it right back into a header? 3831 // answer - it looks crappy to have rfc2047 encoded personal info in the to: box 3832 $personal = $this->decode_header_string($addy_data->personal); 3833 // need to format according to RFC2822 spec for non-plain email address 3834 $rfc_addy = '"'.$personal.'" <'.$rfc_addy.'>'; 3835 // if using this addy in an html page, we need to encode the ' " < > chars 3836 if ($html_encode) 3837 { 3838 $rfc_addy = $this->htmlspecialchars_encode($rfc_addy); 3839 //NOTE: in rfc_comma_sep we will decode any html entities back into these chars 3840 } 3841 } 3842 return $rfc_addy; 3843 } 3844 3845 3846 // ---- Make a To: string of comma seperated email addresses into an array structure ----- 3847 /*! 3848 @function make_rfc_addy_array 3849 @abstract Make a typical string of "To " adresses into an array structure which easily manages all seperate elements of the addresses 3850 @param $data (string) should be a comma seperated string of email addresses (or just one email address) such as 3851 @discussion To adress(es) in a string as we would get from a submitted compose form hold alot of useful information that 3852 can be extracted by sperating out the individual elements of the email address and, if many addresses are present, making an 3853 array of this data. This is especially useful during the send procedure, when we feed each individual address to the MTA, 3854 because the MTA expects a SIMPLE email address in the RCPT TO: command arg, not an address that contains any of the 3855 other things that an email address can contain, such as a users name (personal part) which itself can contain chars outside 3856 of the ASCII range (whether ISO encoded or not) that the MTA does not need and does not want to see in the RCPT TO: commands. 3857 @example * * using html special chars so the inline doc parser will show the brackets and stuff * * 3858 ->make_rfc_addy_array('john@doe.com,"Php Group"<info@phpgroupware.org>,jerry@example.com,"joe john" <jj@example.com>') 3859 which will be decomposed into an array of individual email addresses 3860 where each numbered item will be like this this: 3861 array[x]['personal'] 3862 array[x]['plain'] 3863 the above example would return this structure: 3864 array[0]['personal'] = "" 3865 array[0]['plain'] = "john@doe.com" 3866 array[1]['personal'] = "Php Group" 3867 array[1]['plain'] = "info@phpgroupware.org" 3868 array[2]['personal'] = "" 3869 array[2]['plain'] = "jerry@example.com" 3870 array[3]['personal'] = "joe john" 3871 array[3]['plain'] = "jj@example.com" 3872 @syntax ASCII example, inline docs will not show correctly 3873 john@doe.com,"Php Group" <info@phpgroupware.org>,jerry@example.com,"joe john" <jj@example.com> <br /> 3874 which will be decomposed into an array of individual email addresses <br /> 3875 where each numbered item will be like this this: <br /> 3876 array[x]['personal'] 3877 array[x]['plain'] 3878 the above example would return this structure: 3879 array[0]['personal'] = "" 3880 array[0]['plain'] = "john@doe.com" 3881 array[1]['personal'] = "Php Group" 3882 array[1]['plain'] = "info@phpgroupware.org" 3883 array[2]['personal'] = "" 3884 array[2]['plain'] = "jerry@example.com" 3885 array[3]['personal'] = "joe john" 3886 array[3]['plain'] = "jj@example.com" 3887 @author Angles 3888 */ 3889 function make_rfc_addy_array($data) 3890 { 3891 // if we are fed a null value, return nothing (i.e. a null value) 3892 if (isset($data)) 3893 { 3894 $data = trim($data); 3895 // if we are fed a whitespace only string, return a blank string 3896 if ($data == '') 3897 { 3898 return $data; 3899 // return performs an implicit break, so we are outta here 3900 } 3901 // in some cases the data may be in html entity form 3902 // i.e. the compose page uses html entities when filling the To: box with a predefined value 3903 $data = $this->htmlspecialchars_decode($data); 3904 //reduce all multiple spaces to just one space 3905 //$data = ereg_replace("[' ']{2,20}", ' ', $data); 3906 $this_space = " "; 3907 $data = ereg_replace("$this_space{2,20}", " ", $data); 3908 // explode into an array of email addys 3909 //$data = explode(",", $data); 3910 3911 3912 // WORKAROUND - comma inside the "personal" part will incorrectly explode 3913 //$debug_explode = True; 3914 $debug_explode = False; 3915 3916 /*// === ATTEMPT 1 ==== 3917 // replace any comma(s) INSIDE the "personal" part with this: "C-O-M-M-A" 3918 echo 'PRE replace: '.$this->htmlspecialchars_encode($data).'<br />'; 3919 $comma_replacement = "C_O_M_M_A"; 3920 do 3921 { 3922 //$data = preg_replace('/(".*?)[,](.*?")/',"$1"."C_O_M_M_A"."$2", $data); 3923 //$data = preg_replace('/("[/001-/063,/065-/255]*?)[,]([/001-/063,/065-/255]*?")/',"$1"."$comma_replacement"."$2", $data); 3924 $data = preg_replace('/("(.(?!@))*?)[,]((.(?!@))*?")/',"$1"."$comma_replacement"."$3", $data); 3925 } 3926 while (preg_match('/("(.(?!@))*?)[,]((.(?!@))*?")/',$data)); 3927 echo 'POST replace: '.$this->htmlspecialchars_encode($data).'<br />'; 3928 //DEBUG 3929 return " "; 3930 // explode into an array of email addys 3931 //$data = explode(",", $data); 3932 */ 3933 3934 // === Explode Prep: STEP 1 ==== 3935 // little is known about an email address at this point 3936 // what is known is that the following pattern should be present in ALL non-simple addy's 3937 // " < (doublequote_space_lessThan) 3938 // so replace that with a known temp string 3939 3940 if ($debug_explode) { $this->dbug->out('[known sep] PRE replace: '.$this->htmlspecialchars_encode($data).'<br />'.'<br />'); } 3941 //$known_sep_item = "_SEP_COMPLEX_SEP_"; 3942 // introduce some randomness to make accidental replacements less likely 3943 $sep_rand = $GLOBALS['phpgw']->common->randomstring(3); 3944 $known_sep_item = "_SEP_COMPLEX_".$sep_rand."_SEP_"; 3945 $data = str_replace('" <',$known_sep_item,$data); 3946 if ($debug_explode) { $this->dbug->out('[known sep] POST replace: '.$this->htmlspecialchars_encode($data).'<br />'.'<br />'); } 3947 3948 // === Explode Prep: STEP 2 ==== 3949 // now we know more 3950 // the area BETWEEN a " (doubleQuote) and the $known_sep_item is the "personal" part of the addy 3951 // replace any comma(s) in there with another known temp string 3952 if ($debug_explode) { $this->dbug->out('PRE replace: '.$this->htmlspecialchars_encode($data).'<br />'.'<br />'); } 3953 //$comma_replacement = "_C_O_M_M_A_"; 3954 // introduce some randomness to make accidental replacements less likely 3955 $comma_rand = $GLOBALS['phpgw']->common->randomstring(3); 3956 $comma_replacement = "_C_O_M_".$comma_rand."_M_A_"; 3957 //$data = preg_replace('/(".*?)[,](.*?'.$known_sep_item.')/',"$1"."$comma_replacement "."$2", $data); 3958 //$data = preg_replace('/(".*?)(?<!>)[,](.*?'.$known_sep_item.')/',"$1"."$comma_replacement"."$2", $data); 3959 do 3960 { 3961 $data = preg_replace('/("(.(?<!'.$known_sep_item.'))*?)[,](.*?'.$known_sep_item.')/',"$1"."$comma_replacement"."$3", $data); 3962 } 3963 while (preg_match('/("(.(?<!'.$known_sep_item.'))*?)[,](.*?'.$known_sep_item.')/',$data)); 3964 if ($debug_explode) { $this->dbug->out('POST replace: '.$this->htmlspecialchars_encode($data).'<br />'.'<br />'); } 3965 3966 // Regex Pattern Explanation: 3967 // openQuote_anythingExcept$known_sep_item_repeated0+times_NOT GREEDY 3968 // _aComma_anything_repeated0+times_NOT GREEDY_$known_sep_item 3969 // syntax: "*?" is 0+ repetion symbol with the immediately following '?' being the Not Greedy modifier 3970 // NotGreedy: match as little as possible that still makes the pattern match 3971 // syntax: "?<!" is a "lookbehind negative assertion" 3972 // indicating that the ". *" can not contain anything EXCEPT the $known_sep_item string 3973 // lookbehind is necessary because this assertion applies to something BEFORE the thing (comma) we are trying to capture with the regex 3974 // Methodology: 3975 // (1) We need to specify NO $known_sep_item before the comma or else the regex will match 3976 // commas OUTSIDE of the intended "personal" part of the email addy, which are the 3977 // special commas that seperate email addresses in a comma seperated string 3978 // these special commas MUST NOT be altered 3979 // (2) this preg_replace will only replace ONE comma in the designated "personal" part 3980 // therefor we need a do ... while loop to keep running the preg_replace until all matches are replaced 3981 // the while statement is the SAME regex expression used in a preg_match function 3982 3983 // === Explode Prep: STEP 3 ==== 3984 // UNDO the str_replace from STEP 1 3985 $data = str_replace($known_sep_item, '" <', $data); 3986 if ($debug_explode) { $this->dbug->out('UNDO Step 1: '.$this->htmlspecialchars_encode($data).'<br />'.'<br />'); } 3987 3988 // === ACTUAL EXPLODE ==== 3989 // now the only comma(s) (if any) existing in $data *should* be the 3990 // special commas that seperate email addresses in a comma seperated string 3991 // with this as a (hopefully) KNOWN FACTOR - we can now EXPLODE by comma 3992 // thus: Explode into an array of email addys 3993 $data = explode(",", $data); 3994 if ($debug_explode) { $this->dbug->out('EXPLODED: '.$this->htmlspecialchars_encode(serialize($data)).'<br />'.'<br />'); } 3995 3996 // === POST EXPLODE CLEANING==== 3997 // explode occasionally produces empty elements in the resulting array, so 3998 // (1) eliminate any empty array elements 3999 // (2) UNDO the preg_replace from STEP 2 (add back the actual comma(s) in the "personal" part) 4000 $data_clean = Array(); 4001 for ($i=0;$i<count($data);$i++) 4002 { 4003 // is there actual data in this array element 4004 if ((isset($data[$i])) && ($data[$i] != '')) 4005 { 4006 // OK, now undo the preg_replace from step 2 above 4007 $data[$i] = str_replace($comma_replacement, ',', $data[$i]); 4008 // add this to our $data_clean array 4009 $next_empty = count($data_clean); 4010 $data_clean[$next_empty] = $data[$i]; 4011 } 4012 } 4013 if ($debug_explode) { $this->dbug->out('Cleaned Exploded Data: '.$this->htmlspecialchars_encode(serialize($data_clean)).'<br />'.'<br />'); } 4014 4015 4016 // --- Create Compund Array Structure To Hold Decomposed Addresses ----- 4017 // addy_array is a simple numbered array, each element is a addr_spec_array 4018 $addy_array = Array(); 4019 // $addr_spec_array has this structure: 4020 // addr_spec_array['plain'] 4021 // addr_spec_array['personal'] 4022 4023 // decompose addy's into that array, and format according to rfc specs 4024 for ($i=0;$i<count($data_clean);$i++) 4025 { 4026 // trim off leading and trailing whitespaces and \r and \n 4027 $data_clean[$i] = trim($data_clean[$i]); 4028 // is this a rfc 2822 compound address (not a simple one) 4029 if (strstr($data_clean[$i], '" <')) 4030 { 4031 // SEPERATE "personal" part from the <x@x.com> part 4032 $addr_spec_parts = explode('" <', $data_clean[$i]); 4033 // that got rid of the closing " in personal, now get rig of the first " 4034 $addy_array[$i]['personal'] = substr($addr_spec_parts[0], 1); 4035 // the "<" was already removed, , NOW remove the closing ">" 4036 $grab_to = strlen($addr_spec_parts[1]) - 1; 4037 $addy_array[$i]['plain'] = substr($addr_spec_parts[1], 0, $grab_to); 4038 4039 // QPRINT NON US-ASCII CHARS in "personal" string, as per RFC2047 4040 // the actual "plain" address may NOT have any other than US-ASCII chars, as per rfc2822 4041 $addy_array[$i]['personal'] = $this->encode_header($addy_array[$i]['personal']); 4042 4043 // REVISION: rfc2047 says the following escaping technique is not much help 4044 // use the encoding above instead 4045 /* 4046 // ESCAPE SPECIALS: rfc2822 requires the "personal" comment string to escape "specials" inside the quotes 4047 // the non-simple (i.e. "personal" info is included) need special escaping 4048 // escape these: ' " ( ) 4049 $addy_array[$i]['personal'] = ereg_replace('\'', "\\'", $addy_array[$i]['personal']); 4050 $addy_array[$i]['personal'] = str_replace('"', '\"', $addy_array[$i]['personal']); 4051 $addy_array[$i]['personal'] = str_replace("(", "\(", $addy_array[$i]['personal']); 4052 $addy_array[$i]['personal'] = str_replace(")", "\)", $addy_array[$i]['personal']); 4053 */ 4054 } 4055 else 4056 { 4057 // this is an old style simple address 4058 $addy_array[$i]['personal'] = ''; 4059 $addy_array[$i]['plain'] = $data_clean[$i]; 4060 } 4061 4062 //echo 'addy_array['.$i.'][personal]: '.$this->htmlspecialchars_encode($addy_array[$i]['personal']).'<br />'; 4063 //echo 'addy_array['.$i.'][plain]: '.$this->htmlspecialchars_encode($addy_array[$i]['plain']).'<br />'; 4064 } 4065 if ($debug_explode) { $this->dbug->out('FINAL processed addy_array:<br />'.$this->htmlspecialchars_encode(serialize($addy_array)).'<br />'.'<br />'); } 4066 return $addy_array; 4067 } 4068 } 4069 4070 // takes an array generated by "make_rfc_addy_array()" and makes it into a string 4071 // ytpically used to make to and from headers, etc... 4072 /*! 4073 @function addy_array_to_str 4074 @param $data array of email addresses from the function make_rfc_addy_array 4075 @param $include_personal boolean whether to return simple email address or to also include the personal part 4076 @result string 4077 @author Angles 4078 @discussion this is the opposite of the function make_rfc_addy_array 4079 */ 4080 function addy_array_to_str($data, $include_personal=True) 4081 { 4082 $addy_string = ''; 4083 4084 // reconstruct data in the correct email address format 4085 //if (count($data) == 0) 4086 //{ 4087 // $addy_string = ''; 4088 //} 4089 if (count($data) == 1) 4090 { 4091 if (($include_personal == False) || (strlen(trim($data[0]['personal'])) < 1)) 4092 { 4093 $addy_string = trim($data[0]['plain']); 4094 } 4095 else 4096 { 4097 $addy_string = '"'.trim($data[0]['personal']).'" <'.trim($data[0]['plain']).'>'; 4098 } 4099 } 4100 elseif ($include_personal == False) 4101 { 4102 // CLASS SEND CAN NOT HANDLE FOLDED HEADERS OR PERSONAL ADDRESSES 4103 // (UPDATE - now it can) 4104 // this snippit just assembles the headers 4105 for ($i=0;$i<count($data);$i++) 4106 { 4107 // addresses should be seperated by one comma with NO SPACES AT ALL 4108 $addy_string = $addy_string .trim($data[$i]['plain']) .','; 4109 } 4110 // catch any situations where a blank string was included, resulting in two commas with nothing inbetween 4111 $addy_string = ereg_replace("[,]{2}", ',', $addy_string); 4112 // trim again, strlen needs to be accurate without trailing spaces included 4113 $addy_string = trim($addy_string); 4114 // eliminate that final comma 4115 $grab_to = strlen($addy_string) - 1; 4116 $addy_string = substr($addy_string, 0, $grab_to); 4117 } 4118 else 4119 { 4120 // if folding headers - use SEND_2822 instead of class.send 4121 // FRC2822 recommended max header line length, excluding the required CRLF 4122 $rfc_max_length = 78; 4123 4124 // establish an arrays in case we need a multiline header string 4125 $header_lines = Array(); 4126 $line_num = 0; 4127 $header_lines[$line_num] = ''; 4128 // loop thru the addresses, construct the header string 4129 for ($z=0;$z<count($data);$z++) 4130 { 4131 // make a string for this individual address 4132 if (trim($data[$z]['personal']) != '') 4133 { 4134 $this_address = '"'.trim($data[$z]['personal']).'" <'.trim($data[$z]['plain']).'>'; 4135 } 4136 else 4137 { 4138 $this_address = trim($data[$z]['plain']); 4139 } 4140 // see how long this line would be if this address were added 4141 //if ($z == 0) 4142 $cur_len = strlen($header_lines[$line_num]); 4143 if ($cur_len < 1) 4144 { 4145 $would_be_str = $this_address; 4146 } 4147 else 4148 { 4149 $would_be_str = $header_lines[$line_num] .','.$this_address; 4150 } 4151 //echo 'would_be_str: '.$this->htmlspecialchars_encode($would_be_str).'<br />'; 4152 //echo 'strlen(would_be_str): '.strlen($would_be_str).'<br />'; 4153 if ((strlen($would_be_str) > $rfc_max_length) 4154 && ($cur_len > 1)) 4155 { 4156 // Fold Header: RFC2822 "fold" = CRLF followed by a "whitespace" (#9 or #32) 4157 // preferable to "fold" after the comma, and DO NOT TRIM that white space, preserve it 4158 //$whitespace = " "; 4159 $whitespace = chr(9); 4160 $header_lines[$line_num] = $header_lines[$line_num].','."\r\n"; 4161 // advance to the next line 4162 $line_num++; 4163 // now start the new line with the "folding whitespace" then the address 4164 $header_lines[$line_num] = $whitespace .$this_address; 4165 } 4166 else 4167 { 4168 // simply comma sep the items (as we did when making "would_be_str") 4169 $header_lines[$line_num] = $would_be_str; 4170 } 4171 } 4172 // assemble $header_lines array into a single string 4173 $addy_string = ''; 4174 for ($x=0;$x<count($header_lines);$x++) 4175 { 4176 $addy_string = $addy_string .$header_lines[$x]; 4177 } 4178 $addy_string = trim($addy_string); 4179 } 4180 // data leaves here with NO FINAL (trailing) CRLF - will add that later 4181 return $addy_string; 4182 } 4183 4184 // ---- Ensure CR and LF are always together, RFCs prefer the CRLF combo ----- 4185 /*! 4186 @function normalize_crlf 4187 @abstract Ensure CR and LF are always together, RFCs prefer the CRLF combo 4188 @param $data string 4189 @result string 4190 @author Angles 4191 @discussion ? 4192 */ 4193 function normalize_crlf($data) 4194 { 4195 // this is to catch all plain \n instances and replace them with \r\n. 4196 $data = ereg_replace("\r\n", "\n", $data); 4197 $data = ereg_replace("\r", "\n", $data); 4198 $data = ereg_replace("\n", "\r\n", $data); 4199 4200 //$data = preg_replace("/(?<!\r)\n/m", "\r\n", $data); 4201 //$data = preg_replace("/\r(?!\n)/m", "\r\n", $data); 4202 return $data; 4203 } 4204 4205 // ---- Explode by Linebreak, ANY kind of line break ----- 4206 /*! 4207 @function explode_linebreaks 4208 @abstract Explode by Linebreak, ANY kind of line break 4209 @param $data string 4210 @result string 4211 @author Angles 4212 @discussion ? 4213 */ 4214 function explode_linebreaks($data) 4215 { 4216 $data = preg_split("/\r\n|\r(?!\n)|(?<!\r)\n/m",$data); 4217 // match \r\n, OR \r with no \n after it , OR /n with no /r before it 4218 // modifier m = multiline 4219 return $data; 4220 } 4221 4222 // ---- Create a Unique Mime Boundary ----- 4223 /*! 4224 @function make_boundary 4225 @abstract Create a Unique Mime Boundary 4226 @param $part_length int ? 4227 @result string 4228 @author Angles 4229 @discussion Users some randomization and RFC standards to make a MIME part seperator. 4230 */ 4231 function make_boundary($part_length=4) 4232 { 4233 $part_length = (int)$part_length; 4234 4235 $rand_stuff = Array(); 4236 $rand_stuff[0]['length'] = $part_length; 4237 $rand_stuff[0]['string'] = $GLOBALS['phpgw']->common->randomstring($rand_stuff[0]['length']); 4238 $rand_stuff[0]['rand_numbers'] = ''; 4239 for ($i = 0; $i < $rand_stuff[0]['length']; $i++) 4240 { 4241 if ((ord($rand_stuff[0]['string'][$i]) > 47) 4242 && (ord($rand_stuff[0]['string'][$i]) < 58)) 4243 { 4244 // this char is already a digit 4245 $rand_stuff[0]['rand_numbers'] .= $rand_stuff[0]['string'][$i]; 4246 } 4247 else 4248 { 4249 // turn this into number form, based on this char's ASCII value 4250 $rand_stuff[0]['rand_numbers'] .= ord($rand_stuff[0]['string'][$i]); 4251 } 4252 } 4253 $rand_stuff[1]['length'] = $part_length; 4254 $rand_stuff[1]['string'] = $GLOBALS['phpgw']->common->randomstring($rand_stuff[1]['length']); 4255 $rand_stuff[1]['rand_numbers'] = ''; 4256 for ($i = 0; $i < $rand_stuff[1]['length']; $i++) 4257 { 4258 if ((ord($rand_stuff[1]['string'][$i]) > 47) 4259 && (ord($rand_stuff[1]['string'][$i]) < 58)) 4260 { 4261 // this char is already a digit 4262 $rand_stuff[1]['rand_numbers'] .= $rand_stuff[1]['string'][$i]; 4263 } 4264 else 4265 { 4266 // turn this into number form, based on this char's ASCII value 4267 $rand_stuff[1]['rand_numbers'] .= ord($rand_stuff[1]['string'][$i]); 4268 } 4269 } 4270 $unique_boundary = '---=_Next_Part_'.$rand_stuff[0]['rand_numbers'].'_'.$GLOBALS['phpgw']->common->randomstring($part_length) 4271 .'_'.$GLOBALS['phpgw']->common->randomstring($part_length).'_'.$rand_stuff[1]['rand_numbers']; 4272 4273 return $unique_boundary; 4274 } 4275 4276 // ---- Create a Unique RFC2822 Message ID ----- 4277 /*! 4278 @function make_message_id 4279 @abstract Create a Unique RFC2822 Message ID 4280 @result string 4281 @author Angles 4282 @discussion Users some randomization and some datetime elements to produce an RFC compliant MessageID header string. 4283 */ 4284 function make_message_id() 4285 { 4286 if ($GLOBALS['phpgw_info']['server']['hostname'] != '') 4287 { 4288 $id_suffix = $GLOBALS['phpgw_info']['server']['hostname']; 4289 } 4290 else 4291 { 4292 $id_suffix = $GLOBALS['phpgw']->common->randomstring(3).'local'; 4293 } 4294 // gives you timezone dot microseconds space datetime 4295 $stamp = microtime(); 4296 $stamp = explode(" ",$stamp); 4297 // get rid of tomezone info 4298 $grab_from = strpos($stamp[0], ".") + 1; 4299 $stamp[0] = substr($stamp[0], $grab_from); 4300 // formay the datetime into YYYYMMDD 4301 $stamp[1] = date('Ymd', $stamp[1]); 4302 // a small random string for the middle 4303 $rand_middle = $GLOBALS['phpgw']->common->randomstring(3); 4304 4305 $mess_id = '<'.$stamp[1].'.'.$rand_middle.'.'.$stamp[0].'@'.$id_suffix.'>'; 4306 return $mess_id; 4307 } 4308 4309 /*! 4310 @function make_flags_str 4311 @abstract ConvertPHP type message header IMAP flag data into string data that the IMAP server understands. 4312 @result string 4313 @author Angles 4314 @discussion for use in an IMAP_APPEND command, and anytime you need to give the IMAP server Flag information 4315 ina format that it understands, as opposed to the PHP object which is not understandable to an IMAP server. ALSO 4316 can be used in string matching functions to verify if a message has a certain flag. 4317 @example example of verifing a message has been replied to 4318 if (strisr('Answered'), $flags_str) then (show replied flag) 4319 */ 4320 function make_flags_str($hdr_envelope='') 4321 { 4322 /* 4323 // --- Message Flags --- 4324 var $Recent = ''; // 'R' if recent and seen, 'N' if recent and not seen, ' ' if not recent 4325 var $Unseen = ''; // 'U' if not seen AND not recent, ' ' if seen OR not seen and recent 4326 var $Answered = ''; // 'A' if answered, ' ' if unanswered 4327 var $Deleted = ''; // 'D' if deleted, ' ' if not deleted 4328 var $Draft = ''; // 'X' if draft, ' ' if not draft 4329 var $Flagged = ''; // 'F' if flagged, ' ' if not flagged 4330 */ 4331 /* == RFC2060: == 4332 \Seen Message has been read 4333 \Answered Message has been answered 4334 \Flagged Message is "flagged" for urgent/special attention 4335 \Deleted Message is "deleted" for removal by later EXPUNGE 4336 \Draft Message has not completed composition (marked as a draft). 4337 \Recent Message is "recently" arrived in this mailbox. (long story... 4338 Note: The \Recent system flag is a special case of a 4339 session flag. \Recent can not be used as an argument in a 4340 STORE command, and thus can not be changed at all. 4341 */ 4342 if ($hdr_envelope == '') 4343 { 4344 return 'ERROR'; 4345 } 4346 $flags_str = ''; 4347 $flags_array = array(); 4348 if (($hdr_envelope->Recent != 'N') && ($hdr_envelope->Unseen != 'U')) 4349 { 4350 $flags_str .= "\\Seen "; 4351 } 4352 if ((isset($hdr_envelope->Answered)) 4353 && ($hdr_envelope->Answered == 'A')) 4354 { 4355 $flags_str .= "\\Answered "; 4356 } 4357 if ((isset($hdr_envelope->Flagged)) 4358 && ($hdr_envelope->Flagged == 'F')) 4359 { 4360 $flags_str .= "\\Flagged "; 4361 } 4362 if ((isset($hdr_envelope->Deleted)) 4363 && ($hdr_envelope->Deleted == 'D')) 4364 { 4365 $flags_str .= "\\Deleted "; 4366 } 4367 if ((isset($hdr_envelope->Draft)) 4368 && ($hdr_envelope->Draft == 'X')) 4369 { 4370 $flags_str .= "\\Draft "; 4371 } 4372 // Recent is not settable in an append, so forge about it 4373 return trim($flags_str); 4374 } 4375 4376 // ---- HTML - Related Utility Functions ----- 4377 /*! 4378 @function qprint 4379 @abstract Decode quoted-printable encoded text to ASCII 4380 @result string 4381 @discussion This function originally did 2 extra things 4382 before using the php "quoted_printable_decode" command. First, it would 4383 change any underscores "_" to a space, (NOW commented out) and 4384 second, it would change the sequence "=CRLF" to nothing, 4385 in other words erasing that, BECAUSE php "quoted_printable_decode" does not 4386 correctly handle the "qprint line folding" whereby lines of length longer than 4387 76 chars are terminated with a "=CRLF" and continue on the next line, 4388 BUT the php function "imap_qprint" DOES HANDLE IT correctly. 4389 Note that function "imap_qprint" is part of the IMAP module , these 2 php 4390 functions should do the same thing except that "quoted_printable_decode" does 4391 not require the IMAP module compiled into php BUT does require replacing 4392 =CRLF with empty string to work. 4393 @author previous authors, Angles 4394 */ 4395 function qprint($string) 4396 { 4397 if (function_exists('imap_qprint')) 4398 { 4399 return imap_qprint($string); 4400 } 4401 else 4402 { 4403 ////$string = str_replace("_", " ", $string); 4404 $string = str_replace("=\r\n","",$string); 4405 return quoted_printable_decode($string); 4406 } 4407 } 4408 4409 4410 // ---- RFC Header Decoding ----- 4411 /*! 4412 @function decode_rfc_header_glob 4413 @abstract feed "decode_rfc_header" one line at a time. 4414 @author Angles 4415 @discussion split multi line data into single line strings for processing by "decode_rfc_header" one line at a time. 4416 Currently supports array data or a large string with CRLF pairs that can be exploded into an array. 4417 Not handled is an array with glob data as its elements. Feed this function reasonable simple data structures. 4418 */ 4419 function decode_rfc_header_glob($data) 4420 { 4421 //$debug_me = 2; 4422 $debug_me = 0; 4423 4424 if ($debug_me > 0) { $this->dbug->out('mail_msg_base: decode_rfc_header_glob: ENTERING <br />'); } 4425 if ($debug_me > 2) { $this->dbug->out('mail_msg_base: decode_rfc_header_glob: ENTERING $data DUMP:', $data); } 4426 // multiline glob needs to be an array 4427 if (!is_array($data)) 4428 { 4429 if ($debug_me > 1) { $this->dbug->out('mail_msg_base: decode_rfc_header_glob: $data is NOT an array, strlen = ['.strlen($data).'] <br />'); } 4430 $data_was_array = False; 4431 if (stristr($data, "\r\n")) 4432 { 4433 $array_data = array(); 4434 $array_data = $this->explode_linebreaks($data); 4435 } 4436 else 4437 { 4438 // maybe a single line slipped in here 4439 $array_data = array(); 4440 $array_data[0] = $data; 4441 } 4442 } 4443 else 4444 { 4445 if ($debug_me > 1) { $this->dbug->out('mail_msg_base: decode_rfc_header_glob: $data is array, count = ['.count($data).'] <br />'); } 4446 $data_was_array = True; 4447 } 4448 4449 // so now we KNOW we have an array, right? 4450 // decode its elements 4451 $return_data = array(); 4452 $loops = count($array_data); 4453 for ($i = 0; $i < $loops; $i++) 4454 { 4455 $return_data[$i] = $this->decode_rfc_header($array_data[$i]); 4456 } 4457 4458 // put data back into its original form and return it 4459 if ($data_was_array == True) 4460 { 4461 if ($debug_me > 2) { $this->dbug->out('mail_msg_base: decode_rfc_header_glob: $return_data DUMP:', $return_data); } 4462 if ($debug_me > 0) { $this->dbug->out('mail_msg_base: decode_rfc_header_glob: LEAVING, $data_was_array was ['.serialize($data_was_array).'] <br />'); } 4463 return $return_data; 4464 } 4465 else 4466 { 4467 $my_glob = ''; 4468 $my_glob = implode("\r\n", $return_data); 4469 if ($debug_me > 2) { $this->dbug->out('mail_msg_base: decode_rfc_header_glob: $my_glob DUMP:', $my_glob); } 4470 if ($debug_me > 0) { $this->dbug->out('mail_msg_base: decode_rfc_header_glob: LEAVING, $data_was_array was ['.serialize($data_was_array).'] <br />'); } 4471 return $my_glob; 4472 } 4473 } 4474 4475 /*! 4476 @function decode_rfc_header 4477 @abstract Email header must have chars within US-ASCII limits, any other chars must be encoded, this function DECODES said text. 4478 @result string 4479 @author Angles 4480 @discussion Email header must have chars within US-ASCII limits, any other chars must 4481 be encoded according to RFC2822 spec, this function DECODES said chars, usually one word is encoded individually. 4482 Uses regex to handle either base64 or quoted-printable encoded email headers, 4483 does not care about the specified charset, just decodes based on Q or B encoding key. 4484 @syntax Non-us-ascii chars in email headers MUST be encoded using the special format 4485 =?charset?Q?word?= 4486 =?charset?B?word?= 4487 currently only qprint and base64 encoding is specified by RFCs, represented by the Q or B, 4488 which can be upper OR lower case. 4489 */ 4490 function decode_rfc_header($data) 4491 { 4492 // SAME FUNCTIONALITY as decode_header_string() (but Faster, hopefully) 4493 // non-us-ascii chars in email headers MUST be encoded using the special format: 4494 // =?charset?Q?word?= 4495 // currently only qprint and base64 encoding is specified by RFCs 4496 if (ereg("=\?.*\?(Q|q)\?.*\?=", $data)) 4497 { 4498 $data = ereg_replace("=\?.*\?(Q|q)\?", '', $data); 4499 $data = ereg_replace("\?=", '', $data); 4500 $data = $this->qprint(str_replace("_"," ",$data)); 4501 } 4502 if (ereg("=\?.*\?(B|b)\?.*\?=", $data)) 4503 { 4504 $data = ereg_replace("=\?.*\?(B|b)\?", '', $data); 4505 $data = ereg_replace("\?=", '', $data); 4506 $data = urldecode(base64_decode($data)); 4507 } 4508 return $data; 4509 } 4510 4511 4512 // non-us-ascii chars in email headers MUST be encoded using the special format: 4513 // =?charset?Q?word?= 4514 // commonly: 4515 // =?iso-8859-1?Q?encoded_word?= 4516 // currently only qprint and base64 encoding is specified by RFCs 4517 /*! 4518 @function decode_header_string 4519 @abstract Email header must have chars within US-ASCII limits, any other chars must be encoded, this function DECODES said text. 4520 @result string 4521 @author previous authors 4522 @discussion same capability as function decode_rfc_header but does not use any regex, but 4523 also needs to be tweaked to not be b0rked by the various charset encoding strings which increasingly 4524 do not follow the predictable pattern this function is used to seeing. 4525 @syntax This finction is tuned for this encoding 4526 =?iso-8859-1?Q?encoded_word?= 4527 =?iso-8859-1?B?encoded_word?= 4528 it needs some tweaking to handle the different lengths of various modern encoding type descriptors, like 4529 =?UTF-8?Q?encoded_word?= 4530 =?iso-8859-15?Q?encoded_word?= 4531 =?GB2312?Q?encoded_word?= 4532 =?big5?Q?encoded_word?= 4533 NOTE that Q and B are both supported, it is the length of the charset descriptor that is an issue. 4534 */ 4535 function decode_header_string($string) 4536 { 4537 //return $this->decode_rfc_header($string); 4538 return $this->decode_header_string_orig($string); 4539 } 4540 function decode_header_string_orig($string) 4541 { 4542 if($string) 4543 { 4544 $pos = strpos($string,"=?"); 4545 if(!is_int($pos)) 4546 { 4547 return $string; 4548 } 4549 // save any preceding text 4550 $preceding = substr($string,0,$pos); 4551 $end = strlen($string); 4552 // the mime header spec says this is the longest a single encoded word can be 4553 $search = substr($string,$pos+2,$end - $pos - 2 ); 4554 $d1 = strpos($search,"?"); 4555 if(!is_int($d1)) 4556 { 4557 return $string; 4558 } 4559 $charset = strtolower(substr($string,$pos+2,$d1)); 4560 $search = substr($search,$d1+1); 4561 $d2 = strpos($search,"?"); 4562 if(!is_int($d2)) 4563 { 4564 return $string; 4565 } 4566 $encoding = substr($search,0,$d2); 4567 $search = substr($search,$d2+1); 4568 $end = strpos($search,"?="); 4569 if(!is_int($end)) 4570 { 4571 return $string; 4572 } 4573 $encoded_text = substr($search,0,$end); 4574 $rest = substr($string,(strlen($preceding.$charset.$encoding.$encoded_text)+6)); 4575 if(strtoupper($encoding) == "Q") 4576 { 4577 $decoded = $this->qprint(str_replace("_"," ",$encoded_text)); 4578 } 4579 if (strtoupper($encoding) == "B") 4580 { 4581 $decoded = urldecode(base64_decode($encoded_text)); 4582 } 4583 return $preceding . $decoded . $this->decode_header_string($rest); 4584 } 4585 else 4586 { 4587 return $string; 4588 } 4589 } 4590 4591 /*! 4592 @function encode_iso88591_word 4593 @abstract Private utility function used by encode_header to encode non US-ASCII text in email headers 4594 @param $string 4595 @author Angles 4596 @discussion SUB-FUNCTION - do not call directly, used by function encode_header() SPEED NOTE 4597 this function does not use preg yet does a similar test as the only function that directly calls this 4598 function, perhaps this could be used to reduce the preg matching in function "encode_header" that calls this. 4599 @access private 4600 */ 4601 function encode_iso88591_word($string) 4602 { 4603 $qprint_prefix = '=?iso-8859-1?Q?'; 4604 $qprint_suffix = '?='; 4605 $new_str = ''; 4606 $did_encode = False; 4607 4608 for( $i = 0 ; $i < strlen($string) ; $i++ ) 4609 { 4610 $val = ord($string[$i]); 4611 // my interpetation of what to encode from RFC2045 and RFC2822 4612 if ( (($val >= 1) && ($val <= 31)) 4613 || (($val >= 33) && ($val <= 47)) 4614 || ($val == 61) 4615 || ($val == 62) 4616 || ($val == 64) 4617 || (($val >= 91) && ($val <= 94)) 4618 || ($val == 96) 4619 || ($val >= 123)) 4620 { 4621 $did_encode = True; 4622 //echo 'val needs encode: '.$val.'<br />'; 4623 $val = dechex($val); 4624 // rfc2045 requires quote printable HEX letters to be uppercase 4625 $val = strtoupper($val); 4626 //echo 'val AFTER encode: '.$val.'<br />'; 4627 //$text .= '='.$val; 4628 $new_str = $new_str .'='.$val; 4629 } 4630 else 4631 { 4632 $new_str = $new_str . $string[$i]; 4633 } 4634 } 4635 if ($did_encode) 4636 { 4637 $new_str = $qprint_prefix .$new_str .$qprint_suffix; 4638 } 4639 return $new_str; 4640 } 4641 4642 // encode email headers as per rfc2047, non US-ASCII chars in email headers 4643 // basic idea is to qprint any word with "header-unfriendly" chars in it 4644 // then surround that qprinted word with the stuff specified in rfc2047 4645 // Example: 4646 // "my //name\\ {iS} L@@T" <leet@email.com> 4647 // that email address has "header-unfriendly" chars in it 4648 // this function would encode it suitable for email transport 4649 /*! 4650 @function encode_header 4651 @abstract Encode non US-ASCII text in email headers, takes a line of text at a time 4652 @param $string (string) one like on email headers 4653 @author Angles 4654 @result string only encoded if needed, else returns the same string given as the $string param. 4655 @discussion encode email headers as per rfc2047, non US-ASCII chars in email headers. 4656 Basic idea is to qprint any word with "header-unfriendly" chars in it, but note that base64 encoding 4657 is n alternative way to do the same thing. This function uses quoted-printable method for simplicity. 4658 The encoded word must be surrouned with the specific syntax outlined in rfc2047. 4659 NOTE text is only encoded if necessary, otherwise this function simply returns the same 4660 text it was givin as the $string param, if no encoding is needed. 4661 NOTE this encoding is typically found in the subject, from, to, or cc headers, other email 4662 headers should have no need to use text outside US-ASCII, since email headers are highly 4663 typed strings strictly defined in relevant RFCs. 4664 NOTE the chars encoded by this function are my (Angles) interpetation of what to encode 4665 from RFC2045, RFC2047, and RFC2822 because all these chars seem to cause trouble, 4666 so they are encoded here, BUT I have seen email headers with some of these chars 4667 not encoded so it is possible that this function encoded a broader range of chars than 4668 is actually necessary. NOTE also that any email client that can decode email headers, 4669 and they all must, can decoded ANY encoded text as long as the encoding syntax is correct. 4670 So even IF this function encoded more chars than necessary, there is no problem with that. 4671 Email clients will still decode the headers into the intended text. 4672 SPEED NOTE notice that there is a loop thru every letter of a string, where every letter 4673 is subject to a complicated preg test, could the preg test be simplified, or is this even a 4674 speed issue at all? 4675 @syntax This email address has "header-unfriendly" chars in it, 4676 "my //name\\ {iS} L@@T" <leet@email.com> 4677 same example for the inline doc parser 4678 "my //name\\ {iS} L@@T" <leet@email.com> 4679 this function would encode it suitable for email transport 4680 */ 4681 function encode_header($data) 4682 { 4683 // explode string into an array or words 4684 $words = explode(' ', $data); 4685 4686 for($i=0; $i<count($words); $i++) 4687 { 4688 //echo 'words['.$i.'] in loop: '.$words[$i].'<br />'; 4689 4690 // my interpetation of what to encode from RFC2045, RFC2047, and RFC2822 4691 // all these chars seem to cause trouble, so encode them 4692 if (preg_match('/' 4693 . '['.chr(1).'-'.chr(31).']' 4694 . '['.chr(33).'-'.chr(38).']' 4695 .'|[\\'.chr(39).']' 4696 .'|['.chr(40).'-'.chr(46).']' 4697 .'|[\\'.chr(47).']' 4698 .'|['.chr(61).'-'.chr(62).']' 4699 .'|['.chr(64).']' 4700 .'|['.chr(91).'-'.chr(94).']' 4701 .'|['.chr(96).']' 4702 .'|['.chr(123).'-'.chr(255).']' 4703 .'/', $words[$i])) 4704 { 4705 /* 4706 // qprint this word, and add rfc2047 header special words 4707 $len_before = strlen($words[$i]); 4708 echo 'words['.$i.'] needs encode: '.$words[$i].'<br />'; 4709 $words[$i] = imap_8bit($words[$i]); 4710 echo 'words['.$i.'] AFTER encode: '.$words[$i].'<br />'; 4711 // php may not encode everything that I expect, so check to see if encoding happened 4712 $len_after = strlen($words[$i]); 4713 if ($len_before != $len_after) 4714 { 4715 // indeed, encoding did happen, add rfc2047 header special words 4716 $words[$i] = $qprint_prefix .$words[$i] .$qprint_suffix; 4717 } 4718 */ 4719 4720 // qprint this word, and add rfc2047 header special words 4721 //echo 'words['.$i.'] needs encode: '.$words[$i].'<br />'; 4722 $words[$i] = $this->encode_iso88591_word($words[$i]); 4723 //echo 'words['.$i.'] AFTER encode: '.$words[$i].'<br />'; 4724 } 4725 } 4726 4727 // reassemble the string 4728 $encoded_str = implode(' ',$words); 4729 return $encoded_str; 4730 } 4731 4732 /*! 4733 @function needs_utf7_encoding TESTING 4734 @abstract if string has char 38, ampersand, or char including and greater than 127, then returns true. 4735 @param $string 4736 @author Angles 4737 @discussion This can be used to test if a foldername may need utf7 encoding, IT DOES NOT DO 4738 ANY ENCODING, it just tests if such encoding would be necessary under the rules of RFC2060 4739 sect 5.1.3 concerning "modified UTF7" mailbox encodings. DELETE THIS FUNCTION IF THIS PROVES USELESS. 4740 */ 4741 function needs_utf7_encoding($string) 4742 { 4743 for( $i = 0 ; $i < strlen($string) ; $i++ ) 4744 { 4745 $val = ord($string[$i]); 4746 if (($val == 37) 4747 || ($val >= 127)) 4748 { 4749 return True; 4750 } 4751 } 4752 } 4753 4754 /*! 4755 @function needs_utf7_decoding TESTING 4756 @abstract if string has a pattern associated with rfc2060 utf7 encodings, we guess it would need decoding 4757 @param $string 4758 @author Angles 4759 @discussion This can be used to test if a foldername COMING FROM A MAILSERVER may need utf7 4760 decoding, as the mailserver will store the foldername in encoded form and the MUA (us) must decode if 4761 necessary. This function DOES NOT DO ANY DECODING, it just tests if such decoding would be necessary 4762 under the rules of RFC2060 sect 5.1.3 concerning "modified UTF7" mailbox encodings. The pattern we look for 4763 is a string which is a foldername (string inside any delimiter slash or dot) and which begins with an 4764 ampersand and ends with a dash. Note that the delimiter slash or dot can break up such patterns. Such 4765 as NAMESPACE_peter_DELIMITER_mail_DELIMITER_&ZeVnLIqe-_DELIMITER_&U,BTFw- where 4766 the _DELIMITER_ would typically be a slash or a dot. Since we want to use this function without having 4767 to know or care about what the delimiter is, we use preg match an make an educated guess, because the 4768 ampersand need not be at the beginning at the name nor does the dash have to be at the end of the name. 4769 DELETE THIS FUNCTION IF THIS PROVES USELESS. 4770 */ 4771 function needs_utf7_decoding($string) 4772 { 4773 // ~peter/mail/&ZeVnLIqe-/&U,BTFw- 4774 // mail/Re&5w-ur&6Q-p&4A-ce 4775 // mail/Pe&3w-e&9g-n 4776 preg_match('/&.*[-]/',$string); 4777 } 4778 4779 // PHP "htmpspecialchars" is unreliable sometimes, and does not encode single quotes (unless told to) 4780 // this is a somewhat more reliable version of that PHP function 4781 // with a corresponding 'decode' function below it 4782 /*! 4783 @function htmlspecialchars_encode 4784 @abstract a more reliable version of php function htmpspecialchars 4785 @param $str (string) of plain text 4786 @author Angles 4787 @result string where the most html sensitive chars have been converted to htmlspecialchars. 4788 @discussion PHP "htmpspecialchars" is unreliable sometimes, and does not encode 4789 single quotes unless specifically told to, this is a somewhat more reliable version of 4790 that PHP function with a corresponding decode function also provided. These are 4791 chars that are common in html markup that, when not used as markup, should be 4792 encoded into something else so as not to be interpeted as markup. 4793 @syntax Currently the these chars will be encoded 4794 & , " , ' , > , < , 4795 repeat for the inline doc parser 4796 & , " , ' , < , > 4797 */ 4798 function htmlspecialchars_encode($str) 4799 { 4800 /*// replace ' and " with htmlspecialchars */ 4801 $str = ereg_replace('&', '&', $str); 4802 // any ampersand & that ia already in a "&" should NOT be encoded 4803 //$str = preg_replace("/&(?![:alnum:]*;)/", "&", $str); 4804 $str = ereg_replace('"', '"', $str); 4805 $str = ereg_replace('\'', ''', $str); 4806 $str = ereg_replace('<', '<', $str); 4807 $str = ereg_replace('>', '>', $str); 4808 // these { and } must be html encoded or else they conflict with the template system 4809 $str = str_replace("{", '{', $str); 4810 $str = str_replace("}", '}', $str); 4811 return $str; 4812 } 4813 4814 // reverse of the above encode function 4815 /*! 4816 @function htmlspecialchars_decode 4817 @abstract reverse of the htmlspecialchars_encode function 4818 @param $str (string) of text which may have chars in html token form. 4819 @author Angles 4820 @result string where certain htmlspecialchars encoded chars have been converted to plain ASCII. 4821 @discussion reverse of the htmlspecialchars_encode function 4822 @syntax Currently the these chars will be decoded 4823 & , " , ' , < , > 4824 */ 4825 function htmlspecialchars_decode($str) 4826 { 4827 /*// reverse of htmlspecialchars */ 4828 $str = str_replace('}', "}", $str); 4829 $str = str_replace('{', "{", $str); 4830 4831 $str = ereg_replace('>', '>', $str); 4832 $str = ereg_replace('<', '<', $str); 4833 $str = ereg_replace(''', '\'', $str); 4834 $str = ereg_replace('"', '"', $str); 4835 $str = ereg_replace('&', '&', $str); 4836 return $str; 4837 } 4838 4839 // == "poor-man's" database compatibility == 4840 /*! 4841 @function db_defang_encode 4842 @abstract alias to function html_quotes_encode 4843 @author Angles 4844 */ 4845 function db_defang_encode($str) 4846 { 4847 return $this->html_quotes_encode($str); 4848 } 4849 /*! 4850 @function html_quotes_encode 4851 @abstract "poor-mans" database compatibility, encode database unfriendly chars as html entities 4852 @author Angles 4853 @discussion phpGroupWare supports a variets of databases and to date still needs certain database 4854 unfriendly chars to be encoded into something else so as not to corrupt data. Such database unfriendly 4855 chars are the double quote, single quote, comma, forward slash, and back slash. Adding to this need is 4856 the fact that some of phpGroupWare data is stored in the database in the form of php serialized items, 4857 which are sensitive to the same chars. Various databases may escape or otherwise alter the data 4858 as their native handling of these unencoded chars, which data altering may completely destroy the 4859 integrity of a php serialized item, i.e. the ability of the serialized item to be converted back into its 4860 native unserialized format. phpGroupWare preference database is the primary use for this encoding. 4861 NOTE this function uses html entities as replacements for the database unfriendly chars, but any 4862 encoding technique that does not itself use those chars could have been used. 4863 */ 4864 function html_quotes_encode($str) 4865 { 4866 // == "poor-man's" database compatibility == 4867 // encode database unfriendly chars as html entities 4868 // it'll work for now, and it can be easily un-done later when real DB classes take care of this issue 4869 // replace ' and " with htmlspecialchars 4870 $str = ereg_replace('"', '"', $str); 4871 $str = ereg_replace('\'', ''', $str); 4872 // replace , (comma) 4873 $str = ereg_replace(',', ',', $str); 4874 // replace / (forward slash) 4875 $str = ereg_replace('/', '/', $str); 4876 // replace \ (back slash) 4877 $str = ereg_replace("\\\\", '\', $str); 4878 return $str; 4879 } 4880 4881 // == "poor-man's" database compatibility == 4882 /*! 4883 @function db_defang_decode 4884 @abstract alias to function html_quotes_decode 4885 @author Angles 4886 */ 4887 function db_defang_decode($str) 4888 { 4889 return $this->html_quotes_decode($str); 4890 } 4891 /*! 4892 @function html_quotes_decode 4893 @abstract "poor-mans" database compatibility, decode chars encoded using html_quotes_encode or its alias. 4894 @author Angles 4895 @discussion reverse of function html_quotes_encode, html tokens are converted into ASCII. This 4896 is used for database unfriendly chars which have been encoded with function html_quotes_encode for data 4897 to be stored in a database, and when retrieved from the database this function should be immediately applied. 4898 NOTE this is a quick substitute to REAL database handling of these chars, but it works now, and works 4899 across databases. The name of these two functions originated in the first chars handled by these functions 4900 were simple the doible quote and the single quote. Later these functions were augmented to also handle the 4901 backslash, forward slash, and comma. 4902 */ 4903 function html_quotes_decode($str) 4904 { 4905 // == "poor-man's" database compatibility == 4906 // reverse of html_quotes_encode - html specialchar convert to actual ascii char 4907 // backslash \ 4908 $str = ereg_replace('\', "\\", $str); 4909 // forward slash / 4910 $str = ereg_replace('/', '/', $str); 4911 // comma , 4912 $str = ereg_replace(',', ',', $str); 4913 // single quote ' 4914 $str = ereg_replace(''', '\'', $str); 4915 // double quote " 4916 $str = ereg_replace('"', '"', $str); 4917 return $str; 4918 } 4919 4920 // base64 decoding 4921 /*! 4922 @function de_base64 4923 @abstract ? 4924 */ 4925 function de_base64($text) 4926 { 4927 //return $this->a[$this->acctnum]['dcom']->base64($text); 4928 //return imap_base64($text); 4929 return base64_decode($text); 4930 } 4931 4932 /*! 4933 @function ensure_one_urlencoding 4934 @abstract TESTING - make sure folder arg is urlencoded ONCE only 4935 @param $str (string) 4936 @author Angles 4937 */ 4938 function ensure_one_urlencoding($str='') 4939 { 4940 // check for things we know should not exist in a URLENCODED string 4941 if ( (ereg('"', $str)) 4942 || (ereg('\'', $str)) 4943 // check for , (comma) 4944 || (ereg(',', $str)) 4945 // check for / (forward slash) 4946 || (ereg('/', $str)) 4947 // check for \ (back slash) 4948 || (ereg("\\\\", $str)) 4949 || (ereg('~', $str)) 4950 || (ereg(' ', $str)) 4951 ) 4952 { 4953 return urlencode($str); 4954 } 4955 else 4956 { 4957 return $str; 4958 } 4959 } 4960 4961 /*! 4962 @function body_hard_wrap 4963 @abstract Wrap test calls either the php4 wordwrap OR optionally sucky native code 4964 @author Angles 4965 @discussion when php3 compat was necessary I made a sucky hand made body wrap, 4966 but now php4 is expected so this function should call the php4 function wordwrap instead. 4967 */ 4968 function body_hard_wrap($in='', $size=78) 4969 { 4970 // use sucky hand made function 4971 //return $this->body_hard_wrap_ex($in, $size); 4972 // use the php4 builting function 4973 return wordwrap($in, $size, "\r\n"); 4974 4975 } 4976 // my implementation of a PHP4 only function 4977 /*! 4978 @function body_hard_wrap_ex 4979 @abstract my implementation of a PHP4 only function which keeps lines of text under a certain length. 4980 @author Angles 4981 @discussion Keeps lines of text under a certain length, adding linebreaks to break up lines if 4982 necessary. Used to keep email message body line lengths inline with whatever rules you need. 4983 Recent RFCs greatly expanded the maximum line length allowed in an email message body, 4984 but for backwards compatibility it is generally common practice to keep line lengths in 4985 line with the older standard, which was 78 chars plus CRLF which is 80 chars max. 4986 I believe the modern maximum line length is 998 chars plus CRLF which is 1000 chars. 4987 NEED TO VERIFY THIS. 4988 NOTE immediately that I probably did not do the best job emulating the 4989 php4 function which does the same, but at the time of this functions origination it 4990 was necessary to implement all non php3 functions with a compatibility function. 4991 */ 4992 function body_hard_wrap_ex($in, $size=80) 4993 { 4994 // this function formats lines according to the defined 4995 // linesize. Linebrakes (\n\n) are added when neccessary, 4996 // but only between words. 4997 4998 $out=''; 4999 $exploded = explode ("\r\n",$in); 5000 5001 for ($i = 0; $i < count($exploded); $i++) 5002 { 5003 $this_line = $exploded[$i]; 5004 $this_line_len = strlen($this_line); 5005 if ($this_line_len > $size) 5006 { 5007 $temptext=''; 5008 $temparray = explode (' ',$this_line); 5009 $z = 0; 5010 while ($z <= count($temparray)) 5011 { 5012 while ((strlen($temptext.' '.$temparray[$z]) < $size) && ($z <= count($temparray))) 5013 { 5014 $temptext = $temptext.' '.$temparray[$z]; 5015 $z++; 5016 } 5017 $out = $out."\r\n".$temptext; 5018 $temptext = $temparray[$z]; 5019 $z++; 5020 } 5021 } 5022 else 5023 { 5024 //$out = trim($out); 5025 // get the rest of the line now 5026 $out = $out . $this_line . "\r\n"; 5027 } 5028 //$out = trim($out); 5029 //$out = $out . "\r\n"; 5030 } 5031 // one last trimming 5032 $temparray = explode("\r\n",$out); 5033 for ($i = 0; $i < count($temparray); $i++) 5034 { 5035 //$temparray[$i] = trim($temparray[$i]); 5036 // NOTE: I see NO reason to trim the LEFT part of the string, use RTRIM instead 5037 $temparray[$i] = rtrim($temparray[$i]); 5038 } 5039 $out = implode("\r\n",$temparray); 5040 5041 return $out; 5042 } 5043 5044 /*! 5045 @function recall_desired_action 5046 @abstract used to preserve if this originated as a reply, replyall, forward, or new mail 5047 @author Angles 5048 @discussion Used in both bocompose and bosend so we put it here for general access. 5049 Line lengths will differ for new mail and forwarded orig body, vs. reply mail that has longer 5050 lines. So this preserves this info for later use. Particularly we like to preserve this thru the spelling pass also. 5051 We look for GPC args "action" or "orig_action", as keys, and their 5052 values are limited to "reply", "replyall", "forward", and "new", with "new" being deduced on the 5053 initial compose page call and put into "orig_action" for later use, while the others possible "action" 5054 values simply get stored in "orig_action" no deduction is required, it is specified. 5055 If new future actions are added, adjust this function accordingly. 5056 @access public 5057 */ 5058 function recall_desired_action() 5059 { 5060 // what action are we dealing with here, reply(all), forward, or newmail 5061 // we care because new and forward get different line length then reply mail that has ">" 5062 $orig_action = 'unknown'; 5063 if (($this->get_isset_arg('action')) 5064 && ( 5065 ($this->get_arg_value('action') == 'forward') 5066 || ($this->get_arg_value('action') == 'reply') 5067 || ($this->get_arg_value('action') == 'replyall') 5068 ) 5069 ) 5070 { 5071 $orig_action = $this->get_arg_value('action'); 5072 } 5073 elseif (($this->get_isset_arg('orig_action')) 5074 && ( 5075 ($this->get_arg_value('orig_action') == 'forward') 5076 || ($this->get_arg_value('orig_action') == 'reply') 5077 || ($this->get_arg_value('orig_action') == 'replyall') 5078 || ($this->get_arg_value('orig_action') == 'new') 5079 ) 5080 ) 5081 { 5082 $orig_action = $this->get_arg_value('orig_action'); 5083 } 5084 else 5085 { 5086 // if not reply, replyall, nor forward "action", then we have NEW message 5087 // if this is set now then the above "orig_action" should preserve it 5088 $orig_action = 'new'; 5089 } 5090 return $orig_action; 5091 } 5092 5093 5094 /**************************************************************************\ 5095 * 5096 * Functions PHP Should Have OR Functions From PHP4+ Backported to PHP3 * 5097 * 5098 \**************************************************************************/ 5099 5100 // magic_quotes_gpc PHP MANUAL: 5101 /*! 5102 @function stripslashes_gpc 5103 @abstract strip GPC magic quotes from incoming data ONLY IF magic quotes is indeed being used. 5104 @author various sources 5105 @discussion THIS is what MAGIC QUOTES are, quoted from the php online manual. 5106 BEGIN QUOTE Sets the magic_quotes state for GPC (Get/Post/Cookie) operations. When magic_quotes are on, 5107 all ' (single-quote), " (double quote), \ (backslash) and NULs are escaped with a backslash automatically. 5108 GPC means GET/POST/COOKIE which is actually EGPCS these days (Environment, GET, POST, Cookie, Server). 5109 (UPDATE this has changed again, but the idea is the same). This cannot be turned off in 5110 your script because it operates on the data before your script is called. You can check if it is on 5111 using that function and treat the data accordingly. (by Rasmus Lerdorf) END QUOTE. 5112 So ths function will get rid of the escape \ that magic_quotes HTTP POST will add, " becomes \" 5113 and ' becomes \' but ONLY if magic_quotes is on, less likely to strip user intended slashes this way. 5114 */ 5115 function stripslashes_gpc($data) 5116 { /* get rid of the escape \ that magic_quotes HTTP POST will add, " becomes \" and ' becomes \' 5117 but ONLY if magic_quotes is on, less likely to strip user intended slashes this way */ 5118 if (get_magic_quotes_gpc()==1) 5119 { 5120 return stripslashes($data); 5121 } 5122 else 5123 { 5124 return $data; 5125 } 5126 } 5127 5128 /*! 5129 @function addslashes_gpc 5130 @abstract reverse of function stripslashes_gpc BUT THIS IS NEVER USED. 5131 @discussion Magic quotes, if they are turned on, are added BY PHP before the 5132 script is called. Therefor this function HAS NO USE. It is the decoding that is important 5133 to the coder. 5134 */ 5135 function addslashes_gpc($data) 5136 { /* add the escape \ that magic_quotes HTTP POST would add, " becomes \" and ' becomes \' 5137 but ONLY if magic_quotes is OFF, else we may *double* add slashes */ 5138 if (get_magic_quotes_gpc()==1) 5139 { 5140 return $data; 5141 } 5142 else 5143 { 5144 return addslashes($data); 5145 } 5146 } 5147 5148 /*! 5149 @function is_serialized 5150 @abstract find out if something is already serialized 5151 @param $data could be almost anything 5152 */ 5153 function is_serialized($data) 5154 { 5155 /* not totally complete: currently works with strings, arrays, and booleans (update this if more is added) */ 5156 5157 /* FUTURE: detect a serialized data that had addslashes appplied AFTER it was serialized 5158 you can NOT unserialize that data until those post-serialization slashes are REMOVED */ 5159 5160 //echo 'is_serialized initial input [' .$data .']<br />'; 5161 //echo 'is_serialized unserialized input [' .unserialize($data) .']<br />'; 5162 5163 if (is_array($data)) 5164 { 5165 // arrays types are of course not serialized (at least not at the top level) 5166 // BUT there may be serialization INSIDE in a sub part 5167 return False; 5168 } 5169 elseif ($this->is_bool_ex($data)) 5170 { 5171 // a boolean type is of course not serialized 5172 return False; 5173 } 5174 elseif ((is_string($data)) 5175 && (($data == 'b:0;') || ($data == 'b:1;')) ) 5176 { 5177 // check for easily identifiable serialized boolean values 5178 return True; 5179 } 5180 elseif ((is_string($data)) 5181 && (unserialize($data) == False)) 5182 { 5183 // when you unserialize a normal (not-serialized) string, you get False 5184 return False; 5185 } 5186 elseif ((is_string($data)) 5187 && (ereg('^s:[0-9]+:"',$data) == True)) 5188 { 5189 // identify pattern of a serialized string (that did NOT have slashes added AFTER serialization ) 5190 return True; 5191 } 5192 elseif ((is_string($data)) 5193 && (is_array(unserialize($data)))) 5194 { 5195 // if unserialization produces an array out of a string, it was serialized 5196 //(ereg('^a:[0-9]+:\{',$data) == True)) also could work 5197 return True; 5198 } 5199 //Best Guess - UNKNOWN / ERROR / NOY YET SUPPORTED TYPE 5200 elseif (is_string($data)) 5201 { 5202 return True; 5203 } 5204 else 5205 { 5206 return False; 5207 } 5208 } 5209 5210 /*! 5211 @function is_serialized_str 5212 @abstract find out if a string is already serialized, speed increases since string is known type 5213 @param $string_data SHOULD be a string, or else call "is_serialized()" instead 5214 */ 5215 function is_serialized_str($string_data) 5216 { 5217 if ((is_string($string_data)) 5218 && (unserialize($string_data) == False)) 5219 { 5220 // when you unserialize a normal (not-serialized) string, you get False 5221 return False; 5222 } 5223 else 5224 { 5225 return True; 5226 } 5227 } 5228 5229 /*! 5230 @function is_serialized_smarter 5231 @abstract find out if a string is already serialized, BUT NOT FOOLED BY SLASH problems on unserizalize. 5232 @param $string_data SHOULD be a string 5233 */ 5234 function is_serialized_smarter($string_data) 5235 { 5236 if ((is_string($string_data)) 5237 && (unserialize($string_data) == False)) 5238 { 5239 // when you unserialize a normal (not-serialized) string, you get False 5240 // HOWEVER slashes may b0rk unserialize, do not be fooled, it is still serialized 5241 // so use a second test here, piss poor test, but it helps 5242 if (stristr($string_data, ':"stdClass":')) 5243 { 5244 // unserialize failed but the source str appears to look like a serialized thing 5245 return True; 5246 } 5247 else 5248 { 5249 // second test still says this is not serialized str 5250 return False; 5251 } 5252 } 5253 else 5254 { 5255 return True; 5256 } 5257 } 5258 5259 // PHP3 SAFE Version of "substr_count" 5260 /*! 5261 @function substr_count_ex 5262 @abstract returns the number of times the "needle" substring occurs in the "haystack" string 5263 @param $haystack string 5264 @param $needle string 5265 */ 5266 function substr_count_ex($haystack='', $needle='') 5267 { 5268 if (floor(phpversion()) == 3) 5269 { 5270 if (($haystack == '') || ($needle == '')) 5271 { 5272 return 0; 5273 } 5274 5275 $crtl_struct = Array(); 5276 // how long is needle 5277 $crtl_struct['needle_len'] = strlen($needle); 5278 // how long is haystack before the replacement 5279 $crtl_struct['haystack_orig_len'] = strlen($haystack); 5280 5281 // we will replace needle with a BLANK STRING 5282 $crtl_struct['haystack_new'] = str_replace("$needle",'',$haystack); 5283 // how long is the new haystack string 5284 $crtl_struct['haystack_new_len'] = strlen($crtl_struct['haystack_new']); 5285 // the diff in length between orig haystack and haystack_new diveded by len of needle = the number of occurances of needle 5286 $crtl_struct['substr_count'] = ($crtl_struct['haystack_orig_len'] - $crtl_struct['haystack_new_len']) / $crtl_struct['needle_len']; 5287 5288 //echo '<br />'; 5289 //var_dump($crtl_struct); 5290 //echo '<br />'; 5291 5292 // return the finding 5293 return $crtl_struct['substr_count']; 5294 } 5295 else 5296 { 5297 return substr_count($haystack, $needle); 5298 } 5299 } 5300 5301 // PHP3 SAFE Version of "is_bool" 5302 /*! 5303 @function is_bool_ex 5304 @abstract Find out whether a variable is boolean 5305 @param $bool mixed 5306 @author gleaned from the user notes of the php manual 5307 @discussion This is a PHP3 SAFE Version of php function is_bool. 5308 */ 5309 function is_bool_ex($bool) 5310 { 5311 if (floor(phpversion()) == 3) 5312 { 5313 // this was suggested in the user notes of the php manual 5314 // yes I know there are other ways, but for now this must work in .12 and devel versions 5315 return (gettype($bool) == 'boolean'); 5316 } 5317 else 5318 { 5319 return is_bool($bool); 5320 } 5321 } 5322 5323 // PHP3 and PHP<4.0.6 SAFE Version of "array_search" 5324 /*! 5325 @function array_search_ex 5326 @abstract Search an array for a string. 5327 @author Angles 5328 @discussion This is a PHP3 SAFE and PHP< 4.0.6 Version of php function array_search. 5329 NOTE I did not implement the $strict param. 5330 */ 5331 function array_search_ex($needle='', $haystack='', $strict=False) 5332 { 5333 if(!$haystack) 5334 { 5335 $haystack=array(); 5336 } 5337 // error check 5338 if ((trim($needle) == '') 5339 || (!$haystack) 5340 || (count($haystack) == 0)) 5341 { 5342 return False; 5343 } 5344 5345 $finding = False; 5346 @reset($haystack); 5347 $i = 0; 5348 while(list($key,$value) = each($haystack)) 5349 { 5350 //if ((string)$value == (string)$needle) 5351 if ((string)$haystack[$key] == (string)$needle) 5352 { 5353 $finding = $i; 5354 break; 5355 } 5356 else 5357 { 5358 $i++; 5359 } 5360 } 5361 return $finding; 5362 } 5363 5364 /*! 5365 @function minimum_version 5366 @abstract check if the version of php running the script is at least version "vercheck" 5367 @param $vercheck (string) is the minimum version of php you desire, like "4.1.0" 5368 a blank param will retuen false 5369 @discussion semi replacement version-compare, which is a php4.1+ function. This provides 5370 similar functionality. The code was found in the user comments on date Jul 25 2002 on php 5371 doc page www.php.net/manual/en/function.version-compare.php author is webmaster@mgs2online.f2s.com 5372 as indicated on that page. 5373 @example 5374 if (minimum_version("4.1.0")) { 5375 echo "version supports action" 5376 } 5377 @author webmaster@mgs2online.f2s.com from page www.php.net/manual/en/function.version-compare.php 5378 */ 5379 function minimum_version($vercheck='1.0.0') 5380 { 5381 $minver = (int)str_replace('.', '', $vercheck); 5382 $curver = (int)str_replace('.', '', phpversion()); 5383 if($curver >= $minver) 5384 { 5385 return true; 5386 } 5387 else 5388 { 5389 return false; 5390 } 5391 } 5392 5393 5394 /**************************************************************************\ 5395 * 5396 * EVENTS 5397 * 5398 \**************************************************************************/ 5399 5400 /*! 5401 @capability EVENTS that effect *parts* of cached data. 5402 @discussion These "events" are used to alter cached data to keep it reasonably in sync 5403 with the server, so we do not need to contact the server again if we can estimate the data 5404 change resulting from an event. We alter the cached items directly, and hopefully this 5405 will match what the data on the server is after an event. Used for things like 5406 clearing a messages "Recent" or "Unseen" flags. 5407 */ 5408 5409 /*! 5410 @function event_begin_big_move 5411 @abstract when about to start a big batch move or delete, turn off fancy extreme stuff 5412 @author Angles 5413 @discussion Normally with "extreme" caching, each view, move, or delete of a message 5414 is "mirrored" in the local cache, we pull that item out of cache, manually alter it to be 5415 reasonably "in sync" with the mailserver, and put the item back in cache without asking 5416 the mailserver for any updated information. However, during large, bulk mail moves or 5417 deletes, such as with filtering operations, this "extreme" fancy stuff is really overkill because 5418 so much stuff is changing, it is easier to simply expire items that might be effected, so we 5419 do not do "fancy" stuff with each single move or delete, because the cache data is not 5420 available to pull out and manipulate after we expire it AND BECAUSE we TURN OFF 5421 "session_cache_extreme"for the remainder of this script run. 5422 UNDER DEVELOPMEMT. 5423 */ 5424 function event_begin_big_move($fldball='', $called_by='not_specified') 5425 { 5426 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: event_begin_big_move: ('.__LINE__.') ENTERING, called by ['.$called_by.'], $this->session_cache_extreme is ['.serialize($this->session_cache_extreme).']<br />'); } 5427 // remember the *initial* session_cache_extreme value, we will return that 5428 $initial_session_cache_extreme = $this->session_cache_extreme; 5429 $this->set_arg_value('initial_session_cache_extreme', 0, $initial_session_cache_extreme); 5430 $this->set_arg_value('big_move_in_progress', 0, True); 5431 // currently param $fldball is NOT used in this function 5432 if (($this->session_cache_enabled == True) 5433 && ($this->session_cache_extreme == True)) 5434 { 5435 // EXTREME MODE 5436 if ($this->debug_events > 1) { $this->dbug->out('mail_msg_base: event_begin_big_move: ('.__LINE__.') (extreme mode) pre-expire cached items before a big delete or move operation, so we do not directly alter cached items for each single move or delete<br />'); } 5437 $this->batch_expire_cached_items('mail_msg_base: event_begin_big_move: LINE '.__LINE__); 5438 5439 if ($this->debug_events > 1) { $this->dbug->out('mail_msg_base: event_begin_big_move: ('.__LINE__.') (extreme mode) now that we expired stuff, we can TURN OFF extreme caching for the rest of this operation, this puts "folder_status_info" in L1 cache only<br />'); } 5440 // TURN OFF "session_cache_extreme"for the remainder of this script run 5441 $this->session_cache_extreme = False; 5442 } 5443 else 5444 { 5445 if ($this->debug_events > 1) { $this->dbug->out('mail_msg_base: event_begin_big_move('.__LINE__.'): eventhough $this->session_cache_extreme is off, WE STILL NEED TO EXPIRE MSGBALL_LIST, because it is cached in non-extreme mode too<br />'); } 5446 $this->batch_expire_cached_items('mail_msg_base: event_begin_big_move: LINE '.__LINE__.' but only for msgball_list', True); 5447 } 5448 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: event_begin_big_move: LEAVING, ('.__LINE__.') exiting $this->session_cache_extreme is ['.serialize($this->session_cache_extreme).'], returning the $initial_session_cache_extreme ['.serialize($initial_session_cache_extreme).']<br />'); } 5449 return $initial_session_cache_extreme; 5450 } 5451 5452 /*! 5453 @function event_begin_big_end 5454 @abstract cache extreme is duisabled during a big batch move or delete, this will restore it to its original state. 5455 @author Angles 5456 @discussion If session_cache_extreme was ON before the even to notify of a big move or delete, then 5457 this function will restore that original value when this is called, so that after the big move or delete, when 5458 the next page is displayed, the caching may begin again immediately. Otherwise session_cache_extreme 5459 would remain disabled until the next page view, even when its initial value before the bigmove notice 5460 may have been enabled. UNDER DEVELOPMEMT. 5461 */ 5462 function event_begin_big_end($called_by='not_specified') 5463 { 5464 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: event_begin_big_end: ('.__LINE__.') ENTERING, called by ['.$called_by.'], at this moment $this->session_cache_extreme is ['.serialize($this->session_cache_extreme).']<br />'); } 5465 // remember the *initial* session_cache_extreme value, we will return that 5466 $temp_session_cache_extreme = $this->session_cache_extreme; 5467 if ( ($this->get_isset_arg('initial_session_cache_extreme', 0)) 5468 && ($this->get_arg_value('initial_session_cache_extreme', 0) == True) 5469 && ($this->get_isset_arg('big_move_in_progress', 0)) 5470 && ($this->get_arg_value('big_move_in_progress', 0) == True) 5471 && ($temp_session_cache_extreme != True)) 5472 { 5473 // restore EXTREME MODE setting 5474 if ($this->debug_events > 1) { $this->dbug->out('mail_msg_base: event_begin_big_end: ('.__LINE__.') session_cache_extreme WAS True before disabling for the big move, now restoring value to True, so caching may begin again<br />'); } 5475 $this->session_cache_extreme = True; 5476 // unset these temporary flags 5477 $this->unset_arg('initial_session_cache_extreme', 0); 5478 $this->unset_arg('big_move_in_progress', 0); 5479 } 5480 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: event_begin_big_end: LEAVING, ('.__LINE__.') returning now current $this->session_cache_extreme ['.serialize($this->session_cache_extreme).']<br />'); } 5481 return $this->session_cache_extreme; 5482 } 5483 5484 5485 /*! 5486 @function batch_expire_cached_items 5487 @abstract expires all data associated with "extreme" caching for ALL account 5488 @param (string) $called_by optional for debug information 5489 @param (boolean) $only_msgball_list DEFAULT is False, specify true when extreme mode is off BUT 5490 WE STILL NEED TO EXPIRE ALL MSGBALL_LIST DATA because it is cached outside of extreme mode. 5491 @author Angles 5492 @discussion Plain, unconditional expiration of phpgw_header, msg_structure, 5493 msgball_list, folder_status_info (in appsession) items, for all accounts that are "enabled". 5494 Does a loop thru existing accounts. NOTE THIS REALLY WIPES DATA completely, it is not 5495 very smart, it wipes cached data that may still be useful, so this really does clear the cache. 5496 UNDER DEVELOPMEMT 5497 UPDATE we use folder as a key in msgball_list but batch expire still works because 5498 it wipes data based on a key prior to folder name, same as with other data that has a 5499 folder name in its data key. 5500 */ 5501 function batch_expire_cached_items($called_by='not_specified', $only_msgball_list=False) 5502 { 5503 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: batch_expire_cached_items: ('.__LINE__.') ENTERING, called by ['.$called_by.'], $only_msgball_list: ['.serialize($only_msgball_list).'], $this->session_cache_extreme is ['.serialize($this->session_cache_extreme).']<br />'); } 5504 for ($i=0; $i < count($this->extra_and_default_acounts); $i++) 5505 { 5506 if ($this->extra_and_default_acounts[$i]['status'] == 'enabled') 5507 { 5508 $this_acctnum = $this->extra_and_default_acounts[$i]['acctnum']; 5509 $this->expire_session_cache_item('msgball_list', $this_acctnum); 5510 if ($this->debug_events > 1) { $this->dbug->out(' * mail_msg_base: batch_expire_cached_items: ('.__LINE__.') (extreme OR non-extreme mode) for acctnum ['.$this_acctnum.'] expire whatever msgball_list is cached for this account<br />'); } 5511 if ($only_msgball_list == False) 5512 { 5513 if ($this->debug_events > 1) { $this->dbug->out(' * mail_msg_base: batch_expire_cached_items: ('.__LINE__.') (extreme mode) for acctnum ['.$this_acctnum.'] expire extreme cached items NOTE this will WIPE CLEAN most all cached items, pretty extreme<br />'); } 5514 $this->expire_session_cache_item('phpgw_header', $this_acctnum); 5515 $this->expire_session_cache_item('msg_structure', $this_acctnum); 5516 $this->expire_session_cache_item('folder_status_info', $this_acctnum); 5517 } 5518 } 5519 } 5520 // for DB sessions_db ONLY 5521 if ( 5522 ( 5523 ($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db') 5524 || ($this->use_private_table == True) 5525 ) 5526 && ($only_msgball_list == False)) 5527 { 5528 // we already expired actual DB msgball data above, this will erase all other data, that function may save a few important things though 5529 if ($this->debug_events > 1) { $this->dbug->out('mail_msg_base: batch_expire_cached_items: ('.__LINE__.') session_db IS in use, calling the appsession eraser function $this->so->expire_db_session_bulk_data <br />'); } 5530 $this->so->expire_db_session_bulk_data($called_by='batch_expire_cached_items LINE '.__LINE__); 5531 } 5532 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: batch_expire_cached_items: ('.__LINE__.') LEAVING, called by ['.$called_by.'], $only_msgball_list: ['.serialize($only_msgball_list).'], $this->session_cache_extreme is ['.serialize($this->session_cache_extreme).']<br />'); } 5533 } 5534 5535 5536 /*! 5537 @function event_msg_seen 5538 @abstract when a message is viewed 5539 @author Angles 5540 @result (boolean) True if the item needed altering, False if item did not need altering 5541 @discussion When a message is viewed, its "recent" and/or"unseen" flag is cleared. If 5542 we cache this information, to stay reasonably in sync with the actual messge flags, 5543 we need to alter the cached item so it has no "recent" or "seen" flags. NOTE on 5544 php headers structure FLAGS handling, if a flag is NOT SET, that structure 5545 will have a " " as that flags value, that is a STRING WITH ONE BLANK SPACE. 5546 So if we are clearing a flag, we set it to " " in that structure, and save it back to the cache. 5547 UNDER DEVELOPMEMT. 5548 */ 5549 function event_msg_seen($msgball='', $called_by='not_specified') 5550 { 5551 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: event_msg_seen('.__LINE__.'): ENTERING, called by ['.$called_by.'], $this->session_cache_extreme is ['.serialize($this->session_cache_extreme).']<br />'); } 5552 5553 // CACHE NOTE: FLAGS: if this message we are about to read has flags saying it is UNREAD 5554 // (a) $this->session_cache_extreme == False - expire that "phpgw_header" item 5555 // (b) $this->session_cache_extreme == True - ALTER the "phpgw_header" cached item and save back to cache 5556 5557 if (($this->session_cache_enabled == True) 5558 && ($this->session_cache_extreme == False)) 5559 { 5560 // NON-EXTREME MODE 5561 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: event_msg_seen('.__LINE__.'): (non-extreme mode) session_cache_extreme is ['.serialize($this->session_cache_extreme).'] (false) means "phpgw_header" is NOT cached and we DO NOTHING here.<br />'); } 5562 // DO NOTHING, this data is not cached in non-extreme mode 5563 $did_expire = False; 5564 5565 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: event_msg_seen('.__LINE__.'): (non-extreme mode) LEAVING, $did_expire is ['.serialize($did_expire).']<br />'); } 5566 return $did_expire; 5567 } 5568 elseif (($this->session_cache_enabled == True) 5569 && ($this->session_cache_extreme == True)) 5570 { 5571 // EXTREME MODE 5572 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: event_msg_seen('.__LINE__.'): (extreme mode) $this->session_cache_extreme is ['.serialize($this->session_cache_extreme).'] means we should directly alter a stale "phpgw_header" item and resave to cache <br />'); } 5573 // we only care about doing this is caching is enabled 5574 // this should already be cached, if not, it will be after this call 5575 // this works OK for both php4 sessions AND sessions_db 5576 $msg_headers = $this->phpgw_header($msgball); 5577 if ($this->debug_events > 2) { $this->dbug->out('email_msg_base: event_msg_seen('.__LINE__.'): SEEN-UNSEEN "phpgw_header" examination for $msg_headers DUMP:', $msg_headers); } 5578 //if ($this->debug_events > 2) { $this->dbug->out('email_msg_base: event_msg_seen('.__LINE__.'): (extreme mode) SEEN-UNSEEN "phpgw_header" examination for $msg_headers <br /> * '.serialize($msg_headers).'<br />'); } 5579 $did_alter = False; 5580 // SEEN OR UNSEEN/NEW test 5581 if (($msg_headers->Unseen == 'U') || ($msg_headers->Recent == 'N')) 5582 { 5583 // cached data says the message is unseen, yet we are about to see it right now! 5584 // need to clear "Unseen" and/or "Recent" flags 5585 if (isset($msg_headers->Unseen)) 5586 { 5587 $msg_headers->Unseen = ' '; 5588 } 5589 if (isset($msg_headers->Recent)) 5590 { 5591 $msg_headers->Recent = ' '; 5592 } 5593 if ($this->debug_events > 2) { $this->dbug->out('email_msg_base: event_msg_seen('.__LINE__.'): (extreme mode) SEEN-UNSEEN "phpgw_header" needed to be cleared, altered $msg_headers <br /> * '.serialize($msg_headers).'<br />'); } 5594 // this is the way we pass phpgw_header data to the caching function 5595 $meta_data = array(); 5596 $meta_data['msgball'] = array(); 5597 $meta_data['msgball'] = $msgball; 5598 $meta_data['phpgw_header'] = $msg_headers; 5599 if ($this->debug_events > 1) { $this->dbug->out('email_msg_base: event_msg_seen('.__LINE__.'): (extreme mode) cached SEEN-UNSEEN "phpgw_header" flags cleared and saved back to cache, for $msgball ['.serialize($msgball).']<br />'); } 5600 // this works OK for both php4 sessions AND sessions_db 5601 $this->save_session_cache_item('phpgw_header', $meta_data, $meta_data['msgball']['acctnum']); 5602 $did_alter = True; 5603 5604 5605 // FUTURE: PART TWO: ALTER FOLDER STATUS INFO, REDUCE UNSEEN COUNT BY ONE 5606 if ($this->debug_events > 1) { $this->dbug->out('mail_msg_base: event_msg_seen('.__LINE__.'): (extreme mode) (step 2) code will adjust folder_status_info to REDUCE UNSEEN count by 1, and resave that to cache <br />'); } 5607 5608 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_seen('.__LINE__.'): (extreme mode) (step 2) grabbing folder_status_info DIRECTLY from appsession, <br /> * can not call "read_session_cache_item" because when moving multiple mails, we do not "expunge" until the last one, so validity check will fail because we are *ahead* of the mail server in "freshness"<br />'); } 5609 $acctnum = $msgball['acctnum']; 5610 $extra_keys = $msgball['folder']; 5611 $data_name = 'folder_status_info'; 5612 //$location = 'acctnum='.(string)$acctnum.';data_name='.$data_name.';extra_keys='.$extra_keys; 5613 //$app = 'email'; 5614 // get session data 5615 $folder_status_info = array(); 5616 //$folder_status_info = $GLOBALS['phpgw']->session->appsession($location,$app); 5617 5618 // for DB sessions_db ONLY 5619 if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db') 5620 || ($this->use_private_table == True)) 5621 { 5622 $my_location = (string)$acctnum.';'.$data_name.';'.$extra_keys; 5623 if ($this->debug_events > 1) { $this->dbug->out('email_msg_base: event_msg_seen('.__LINE__.'): (extreme mode) 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 />'); } 5624 if ($this->use_private_table == True) 5625 { 5626 $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$extra_keys] 5627 = $this->so->so_get_data($my_location); 5628 } 5629 else 5630 { 5631 $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$extra_keys] 5632 = $GLOBALS['phpgw']->session->appsession($my_location, 'email'); 5633 } 5634 if ($this->debug_events > 2) { $this->dbug->out('email_msg_base: event_msg_seen('.__LINE__.'): (extreme mode) [email][dat]['.$acctnum.']['.$data_name.']['.$extra_keys.'] DUMP:', $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$extra_keys]); } 5635 } 5636 5637 $folder_status_info = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$extra_keys]; 5638 5639 if ($this->debug_events > 1) { $this->dbug->out('mail_msg_base: event_msg_seen('.__LINE__.'): (extreme mode) (step 2) grabbed $folder_status_info DUMP:', $folder_status_info); } 5640 //if ($this->debug_events > 1) { $this->dbug->out('mail_msg_base: event_msg_seen: (extreme mode) (step 2) grabbed folder_status_info :: unserialized $meta_data[$specific_key] DUMP <pre>'; print_r($folder_status_info); echo '</pre>'); } 5641 //$folder_status_info = unserialize($meta_data['folder_status_info'][$specific_key]); 5642 5643 if (!$folder_status_info) 5644 { 5645 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_seen('.__LINE__.'): (extreme mode) (step 2) no cached "folder_status_info" exists<br />'); } 5646 } 5647 else 5648 { 5649 if ($this->debug_events > 2) { $this->dbug->out('mail_msg: event_msg_seen('.__LINE__.'): (extreme mode) (step 2) cached msgball_list $folder_status_info DUMP:', $folder_status_info); } 5650 5651 $prev_new_count = $folder_status_info['number_new']; 5652 $adjusted_new_count = ($prev_new_count - 1); 5653 $folder_status_info['number_new'] = $adjusted_new_count; 5654 5655 // the user alert string needs updating also 5656 $folder_status_info['alert_string'] = str_replace((string)$prev_new_count, (string)$adjusted_new_count, $folder_status_info['alert_string']); 5657 5658 // save altered data back into the cache 5659 if ($this->debug_events > 2) { $this->dbug->out('mail_msg: event_msg_seen('.__LINE__.'): (extreme mode) (step 2) save ADJUSTED "folder_status_info" DUMP:', $folder_status_info); } 5660 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_seen('.__LINE__.'): (extreme mode) (step 2) save ADJUSTED "folder_status_info" back to cache with "save_session_cache_item", note the timestamp not changed<br />'); } 5661 // thid call is OK, it will not change the data, it just puts it in cache, no need for direct APPSESSION call 5662 //$this->save_session_cache_item('folder_status_info', $folder_status_info, $acctnum, $extra_keys); 5663 $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$extra_keys] = $folder_status_info; 5664 // for DB sessions_db ONLY 5665 if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db') 5666 || ($this->use_private_table == True)) 5667 { 5668 $my_location = (string)$acctnum.';'.$data_name.';'.$extra_keys; 5669 if ($this->debug_events > 1) { $this->dbug->out('email_msg_base: event_msg_seen('.__LINE__.'): (extreme mode) sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to save data to phpgw_app_sessions table, $my_location ['.$my_location.']<br />'); } 5670 if ($this->use_private_table == True) 5671 { 5672 $this->so->so_set_data($my_location, $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$extra_keys]); 5673 } 5674 else 5675 { 5676 $GLOBALS['phpgw']->session->appsession($my_location, 'email', $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name][$extra_keys]); 5677 } 5678 } 5679 $did_alter = True; 5680 } 5681 } 5682 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: event_msg_seen('.__LINE__.'): (extreme mode) LEAVING, $did_alter is ['.serialize($did_alter).']<br />'); } 5683 return $did_alter; 5684 } 5685 5686 if ($this->debug_events > 0) { $this->dbug->out('mail_msg_base: event_msg_seen: LEAVING, unhaandled situation, or caching is turned off<br />'); } 5687 return False; 5688 } 5689 5690 /*! 5691 @function event_msg_move_or_delete 5692 @abstract when a message is moved out of a folder. 5693 @author Angles 5694 @discussion When a message moved OUT of a folder, whether deleted or moved to 5695 another folder, the "msgball_list", is not longer valid. If we are caching with periods of 5696 forced non-connection to the mail server, we need to pop out that individual magball 5697 from the msgball_list.. UNDER DEVELOPMEMT. FUTURE, PART TWO, alter folder status info. 5698 If message being moved has flags "Recent" or "Unseen", folder status info needs its unseen count reduced 5699 by one AND its total count reduced by one. 5700 */ 5701 function event_msg_move_or_delete($msgball='', $called_by='not_specified', $to_fldball='') 5702 { 5703 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') ENTERING, called by ['.$called_by.']<br />'); } 5704 if (($this->session_cache_enabled == False) 5705 && ($this->session_cache_extreme == False)) 5706 { 5707 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') LEAVING, BOTH session_cache_enabled AND session_cache_extreme are FALSE, we have nothing to do here, returning False<br />'); } 5708 return False; 5709 } 5710 5711 if ( (isset($msgball) == False) 5712 || (!$msgball) 5713 || (is_array($msgball) == False) ) 5714 { 5715 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') FALLBACK BATCH EXPIRE because param $msgball ['.serialize($msgball).'] is not set or not an array, we do not know what account nor folder we need to operate on, but we still need to clean cache so it matches reality after the move or delete<br />'); } 5716 $this->batch_expire_cached_items('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (because we got erronious msgball data) '); 5717 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') LEAVING, returning True because we did expire stuff<br />'); } 5718 return True; 5719 } 5720 $did_alter_or_expire = False; 5721 /* 5722 // make sure folder name in msgball is in URLENCODED form 5723 if (isset($msgball['folder'])) 5724 //&& (stristr($msgball['folder'],'%') == False)) 5725 { 5726 $msgball['folder'] = $this->prep_folder_out($msgball['folder']); 5727 } 5728 */ 5729 $clean_folder = $this->prep_folder_in($msgball['folder']); 5730 $urlencoded_folder = $this->prep_folder_out($clean_folder); 5731 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') $clean_folder ['.$clean_folder.'], $urlencoded_folder ['.$urlencoded_folder.']<br />'); } 5732 5733 $msgball['folder'] = $urlencoded_folder; 5734 $acctnum = $msgball['acctnum']; 5735 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (pre step 1) $this->read_session_cache_item("msgball_list", $acctnum); to see if we have a session cached folderlist (is that the right function to call?)<br />'); } 5736 // so we have data in the cache? 5737 $data_name = 'msgball_list'; 5738 // currently we DO NOT use the $extra_keys param for msgball_list data 5739 // UPDATE YES NOW WE USE FOLDER NAME IN THE DATA KEYS FOR MSGBALL_LIST 5740 $ex_folder = $urlencoded_folder; 5741 // get session data 5742 //if (($this->debug_events > 1) || ($this->debug_session_caching > 1)) { echo 'mail_msg: event_msg_move_or_delete('.__LINE__.'): DIRECT CALL to get appsession data for $location ['.$location.'], $app ['.$app.']<br />'; } 5743 5744 //$cached_msgball_data = $GLOBALS['phpgw']->session->appsession($location,$app); 5745 //$cached_msgball_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum][$data_name]; 5746 //$cached_msgball_data = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list']; 5747 5748 // $cached_msgball_data is saved in cache with validity data, like this: 5749 // $cached_msgball_data['msgball_list'] 5750 // $cached_msgball_data['validity'] 5751 // this makes for a strange looking array with the string "msgball_list" appearing two times in a row, but that is how it works, like this 5752 // ['email']['dat'][0]['msgball_list'] 5753 // is the base "node" the data is attached to, and the rest looks like this 5754 // ['email']['dat'][0]['msgball_list']['msgball_list'] 5755 // ['email']['dat'][0]['msgball_list']['validity'] 5756 5757 // for DB sessions_db ONLY 5758 if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db') 5759 || ($this->use_private_table == True)) 5760 { 5761 if ((isset($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list'][$ex_folder]) == False) 5762 || (!$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list'][$ex_folder])) 5763 { 5764 //$my_location = (string)$acctnum.';msgball_list'; 5765 // NOW WE USE FOLDER TOO as a data key for msgball_list 5766 $my_location = (string)$acctnum.';msgball_list;'.$ex_folder; 5767 if (($this->debug_events > 1) || ($this->debug_session_caching > 1)) { echo 'mail_msg: event_msg_move_or_delete('.__LINE__.'): DIRECT CALL to get appsession data for $location ['.$location.'], $app ['.$app.']<br />'; } 5768 if ($this->debug_events > 1) { $this->dbug->out('email_msg_base: event_msg_move_or_delete('.__LINE__.'): (extreme mode) 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 />'); } 5769 if ($this->use_private_table == True) 5770 { 5771 //$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list'][$ex_folder] 5772 // = $this->so->so_get_data($my_location); 5773 // TRY USING COMPRESSION for msgball_list 5774 $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list'][$ex_folder] 5775 = $this->so->so_get_data($my_location, True); 5776 } 5777 else 5778 { 5779 $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list'][$ex_folder] 5780 = $GLOBALS['phpgw']->session->appsession($my_location, 'email'); 5781 } 5782 //if ($this->debug_events > 2) { $this->dbug->out('email_msg_base: event_msg_move_or_delete('.__LINE__.'): (extreme mode) [email][dat]['.$acctnum.'][msgball_list] DUMP:<pre>'; print_r($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list'][ex_folder]); echo '</pre>'); } 5783 } 5784 } 5785 5786 $cached_msgball_data =& $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list'][$ex_folder]; 5787 5788 if (($this->debug_events > 2) && ($this->debug_allow_magball_list_dumps)) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): for $my_location ['.$my_location.'], restored $cached_msgball_data DUMP:', $cached_msgball_data); } 5789 5790 if ((!$cached_msgball_data) 5791 && ($this->session_cache_extreme == False)) 5792 { 5793 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') LEAVING, because NOTHING TO DO, IN NON-EXTREME MODE, and we have NO CACHED MSGBALL_LIST, there is no action we need to take, return False<br />'); } 5794 return False; 5795 } 5796 elseif ((!$cached_msgball_data) 5797 && ($this->session_cache_extreme == True)) 5798 { 5799 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') LEAVING, because NOTHING TO DO, we are in EXTREME-MODE, BUT we have NO CACHED MSGBALL_LIST, so skip down to the other stuff we do in extreme mode here<br />'); } 5800 } 5801 elseif (($this->session_cache_extreme == False) 5802 && ($cached_msgball_data)) 5803 { 5804 // NON-EXTREME MODE but session cache is on, so expire msgball_list for this folder 5805 if ($this->debug_events > 1) { $this->dbug->out('mail_msg_base: event_msg_move_or_delete: ('.__LINE__.') (non-extreme mode) session_cache_extreme is ['.serialize($this->session_cache_extreme).'] (false) means "msg_structure" and "phpgw_header" is NOT cached BUT msgball_list IS cached in non-extreme mode, so ...<br />'); } 5806 5807 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (non-extreme mode) session_cache_extreme is ['.serialize($this->session_cache_extreme).'] means we should simply expire the entire "msgball_list" (and maybe the "folder_status_info" too? no "folder_status_info" is not even cached in non extreme mode<br />'); } 5808 // expire entire msgball_list and the folder_status_info 5809 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (non-extreme mode) calling $this->expire_session_cache_item("msgball_list", '.$msgball['acctnum'].')<br />'); } 5810 // FUTURE: if each account ever saves msgball_list for individual folders instead of just one folder per account, then add extra_keys to this command 5811 $this->expire_session_cache_item('msgball_list', $msgball['acctnum'], $ex_folder); 5812 5813 // ANYTIME a message is moved out of a folder, we need to remove any cached "msg_structure" and "phpgw_header" data 5814 // damn why are we doing this in non-extreme mode? 5815 ////$specific_key = (string)$msgball['msgnum'].'_'.$msgball['folder']; 5816 //$specific_key = $msgball['folder'].'_'.(string)$msgball['msgnum']; 5817 //if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (non-extreme mode) extreme or not, "msg_structure" and "phpgw_header" needs expired this specific message leaving this folder, $specific_key ['.$specific_key.'] (but why would that data exist in non-extreme mode?)<br />'); } 5818 //$this->expire_session_cache_item('msg_structure', $msgball['acctnum'], $specific_key); 5819 //$this->expire_session_cache_item('phpgw_header', $msgball['acctnum'], $specific_key); 5820 5821 // folder_status_info in "non-extreme" mode is not saved to appsession, so it does not need expiring 5822 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (non-extreme mode) in non-extreme mode we do NOT alter the "folder_status_info", in fact "folder_status_info" is not even appsession cached in non-extreme mode <br />'); } 5823 5824 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (non-extreme mode) LEAVING, expiring entire msgball list<br />'); } 5825 return True; 5826 } 5827 // IF EXTREME MODE IS OFF, WE SIMPLY EXPIRE THE WHOLE MSGBALL_LIST 5828 // UNLESS POPPING SINGLE ITEMS IS USEFUL ENOUGH TO DO ANYWAY 5829 // lex added this code, angles commented out (oops :( but I saw what he did and incorporated it above 5830 // however we still need to answer that qestion in cap letters 2 lines up 5831 elseif (($this->session_cache_extreme == True) 5832 && ($cached_msgball_data)) 5833 { 5834 // EXTREME MODE 5835 // directloy manipulate existing cached items to make them "fresh" and resave to cache 5836 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (extreme mode) (step 1) pop out a single msgball from the msgball_list and resave to cache<br />'); } 5837 if ($this->debug_events > 2) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (extreme mode) (step 1) search msgball_list looking for this $msgball DUMP:', $msgball); } 5838 $did_alter = False; 5839 // STEP ONE: 5840 // we should be able to pop out a single msgball from the msg_ball list 5841 // when mail moves OUT of a folder 5842 5843 // NOTE: can not call "read_session_cache_item" because when moving multiple mails, we do not "expunge" until the last one, so validity check will fail because we are *ahead* of the mail server in "freshness" 5844 //$meta_data = $this->read_session_cache_item('msgball_list', $msgball['acctnum']); 5845 // daa... we already got the msgball_list up above 5846 //if ($this->debug_events > 2) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (extreme mode) (step 1) cached msgball_list $meta_data DUMP<pre>'; print_r($meta_data); echo '</pre>'); } 5847 5848 /* 5849 // get the array index if the msgball we want to delete 5850 // fallback indicator 5851 $found_msgball_idx = $this->not_set; 5852 $loops = count($cached_msgball_data['msgball_list']); 5853 for ($i = 0; $i < $loops; $i++) 5854 { 5855 $this_msgball = $cached_msgball_data['msgball_list'][$i]; 5856 if ($this->debug_events > 2) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (extreme mode) (step 1) cached msgball_list loop ['.$i.'] $this_msgball DUMP<pre>'; print_r($this_msgball); echo '</pre>'); } 5857 if (($this_msgball['acctnum'] == $msgball['acctnum']) 5858 && ($this_msgball['folder'] == $msgball['folder']) 5859 && ($this_msgball['msgnum'] == $msgball['msgnum'])) 5860 { 5861 $found_msgball_idx = $i; 5862 break; 5863 } 5864 } 5865 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (extreme mode) (step 1) searched for msgball from the msgball_list, $found_msgball_idx ['.serialize($found_msgball_idx).'] <br />'); } 5866 */ 5867 // get the array index if the msgball we want to delete 5868 if ((!isset($msgball['uri'])) 5869 || (!$msgball['uri'])) 5870 { 5871 $msgball['uri'] = 5872 'msgball[msgnum]='.$msgball['msgnum'] 5873 .'&msgball[folder]='.$msgball['folder'] 5874 .'&msgball[acctnum]='.$msgball['acctnum']; 5875 } 5876 // get the idx of the msgball if it is in the msgball_list 5877 $found_msgball_idx = array_search($msgball['uri'],$cached_msgball_data['msgball_list']); 5878 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (extreme mode) (step 1) searched for msgball from the msgball_list, $found_msgball_idx ['.serialize($found_msgball_idx).'] <br />'); } 5879 5880 // if we have an idx, we can delete it 5881 //if ((string)$found_msgball_idx != $this->not_set) 5882 if ($found_msgball_idx === False) 5883 { 5884 // DO NOTHING, we did not get an index value 5885 } 5886 else 5887 { 5888 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (extreme mode) (step 1) searched SUCCESS, $found_msgball_idx ['.serialize($found_msgball_idx).'] , now doing an ARRAY_SPLICE<br />'); } 5889 array_splice($cached_msgball_data['msgball_list'], $found_msgball_idx, 1); 5890 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (extreme mode) (step 1) now msgball_list has 1 less item, update msgball_list "vality" data to match this deletion, $cached_msgball_data[validity][folder_status_info][number_all] before '.serialize($cached_msgball_data['validity']['folder_status_info']['number_all']).'<br />'); } 5891 $old_count = (int)$cached_msgball_data['validity']['folder_status_info']['number_all']; 5892 $new_count = ($old_count - 1); 5893 $cached_msgball_data['validity']['folder_status_info']['number_all'] = $new_count; 5894 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (extreme mode) (step 1) $cached_msgball_data[validity][folder_status_info][number_all] AFTER reduction '.serialize($cached_msgball_data['validity']['folder_status_info']['number_all']).'<br />'); } 5895 if (($this->debug_events > 2) && ($this->debug_allow_magball_list_dumps)) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (extreme mode) (step 1) array_splice of $cached_msgball_data[msgball_list] results in this $cached_msgball_data DUMP:', $cached_msgball_data); } 5896 5897 // save altered data back into the cache 5898 // NOT needed if using a REFERENCE and only using regular appsession (i.e. NOT the anglemail table) 5899 //if (($this->debug_session_caching > 1) || ($this->debug_events > 1)) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') saving altered msgball_list directly to appsession, location: ['.$location.'] $app ['.$app.']<br />'); } 5900 // COMMENT IF USING REF, UNCOMMENT IF NOT USING REFERENCES 5901 //$this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list'] = $cached_msgball_data; 5902 5903 // for DB sessions_db ONLY 5904 if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db') 5905 || ($this->use_private_table == True)) 5906 { 5907 //$my_location = (string)$acctnum.';msgball_list'; 5908 // NOW WE USE FOLDER TOO as a data key for msgball_list 5909 $my_location = (string)$acctnum.';msgball_list;'.$ex_folder; 5910 if ($this->debug_events > 1) { $this->dbug->out('email_msg_base: event_msg_move_or_delete('.__LINE__.'): (extreme mode) sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to save data to a database table, $my_location ['.$my_location.'], if using anglemail table this step is always necessary<br />'); } 5911 if ($this->use_private_table == True) 5912 { 5913 //$this->so->so_set_data($my_location, $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list']); 5914 // TRY USING COMPRESSION for msgball_list (only available for anglemail table) 5915 $this->so->so_set_data( 5916 $my_location, 5917 $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list'][$ex_folder], 5918 True 5919 ); 5920 } 5921 else 5922 { 5923 $GLOBALS['phpgw']->session->appsession($my_location, 'email', $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['msgball_list'][$ex_folder]); 5924 } 5925 } 5926 $did_alter = True; 5927 } 5928 } 5929 // PARTS 2 and 3 -- only attempt if in extreme-mode 5930 if ($this->session_cache_extreme == True) 5931 { 5932 // PART TWO, alter folder status info. 5933 // reduct TOTAL by one, reduce UNDEEN by one if moving an UNSEEN mail 5934 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: (extreme mode) (step 2) alter and resave the "folder_status_info" appsession cache<br />'); } 5935 5936 // this grabs from MAILSERVER if required, do we really want this? 5937 //$msg_headers = $GLOBALS['phpgw']->msg->phpgw_header($msgball); 5938 // NO, but we kind of do need this seen vs. unseen data 5939 // if we want to make the folder_status_info accurate to what is actually the stats of the folder on the mailserver 5940 // BUT if this requires is to grab these headers, WE DO NOT GAIN ANY SPEED advantage, 5941 // ONLY do this is the $msg_headers are ALREADY in cache 5942 // this call is OK because it only returns data if it exists, false if not, $extra_keys is FOLDERNAME_MSGNUM 5943 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (extreme mode) check if mail leaving folder was UNSEEN, possible only IF "phpgw_header" is cached, else we loose speed to obtain ot from mailserver<br />'); } 5944 //$extra_keys = $msgball['folder'].'_'.(string)$msgball['msgnum']; 5945 //$msg_headers = $this->read_session_cache_item('phpgw_header', $msgball['acctnum'], $extra_keys); 5946 $msg_headers = $this->read_session_cache_item('phpgw_header', $msgball['acctnum'], $msgball['folder'], $msgball['msgnum']); 5947 if (!$msg_headers) 5948 { 5949 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (extreme mode) NO $msg_headers data was cached, THIS IS NOT ACCURATE but just assume mail leaving folder was NOT recent, NOT unseen, we do not want to contact mailserver that is slow <br />'); } 5950 $reduce_unseen = False; 5951 } 5952 // SEEN OR UNSEEN/NEW test 5953 elseif (($msg_headers->Unseen == 'U') || ($msg_headers->Recent == 'N')) 5954 { 5955 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (extreme mode) msg_headers indicate mail leaving folder was UNSEEN <br />'); } 5956 $reduce_unseen = True; 5957 } 5958 else 5959 { 5960 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (extreme mode) msg_headers indicate mail leaving folder was NOT recent, NOT unseen <br />'); } 5961 $reduce_unseen = False; 5962 } 5963 5964 5965 //$did_alter = False; 5966 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (extreme mode) (step 2) grabbing folder_status_info DIRECTLY from appsession, <br /> * can not call "read_session_cache_item" because when moving multiple mails, we do not "expunge" until the last one, so validity check will fail because we are *ahead* of the mail server in "freshness"<br />'); } 5967 $acctnum = $msgball['acctnum']; 5968 $ex_folder = $msgball['folder']; 5969 $ex_msgnum = $msgball['msgnum']; 5970 $data_name = 'folder_status_info'; 5971 // get session data 5972 $folder_status_info = array(); 5973 //$folder_status_info = $GLOBALS['phpgw']->session->appsession($location,$app); 5974 // for DB sessions_db ONLY 5975 if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db') 5976 || ($this->use_private_table == True)) 5977 { 5978 $my_location = (string)$acctnum.';folder_status_info;'.$ex_folder; 5979 if ($this->debug_events > 1) { $this->dbug->out('email_msg_base: event_msg_move_or_delete('.__LINE__.'): (extreme mode) 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 />'); } 5980 if ($this->use_private_table == True) 5981 { 5982 $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['folder_status_info'][$ex_folder] 5983 = $this->so->so_get_data($my_location); 5984 } 5985 else 5986 { 5987 $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['folder_status_info'][$ex_folder] 5988 = $GLOBALS['phpgw']->session->appsession($my_location, 'email'); 5989 } 5990 //if ($this->debug_events > 2) { $this->dbug->out('email_msg_base: event_msg_move_or_delete('.__LINE__.'): (extreme mode) [email][dat]['.$acctnum.'][folder_status_info]['.$ex_folder.'] DUMP:<pre>'; print_r($this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['folder_status_info'][$ex_folder]); echo '</pre>'); } 5991 } 5992 5993 $folder_status_info = $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['folder_status_info'][$ex_folder]; 5994 5995 //if ($this->debug_events > 1) { $this->dbug->out('mail_msg_base: event_msg_move_or_delete('.__LINE__.'): (extreme mode) (step 2) session ['.$location.','.$app.'] grabbed $meta_data serialized DUMP <pre>'; echo "\r\n".serialize($meta_data)."\r\n"; echo '</pre>'); } 5996 if ($this->debug_events > 1) { $this->dbug->out('mail_msg_base: event_msg_move_or_delete('.__LINE__.'): (extreme mode) (step 2) session ['.$location.','.$app.'] grabbed $folder_status_info DUMP:', $folder_status_info); } 5997 5998 if (!$folder_status_info) 5999 { 6000 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: (extreme mode) (step 2) no cached "folder_status_info" exists<br />'); } 6001 } 6002 else 6003 { 6004 if ($this->debug_events > 2) { $this->dbug->out('mail_msg: event_msg_move_or_delete: (extreme mode) (step 2) unaltered cached msgball_list $meta_data DUMP:', $meta_data); } 6005 // reducr NUMBER ALL - obviously if mail is leaving a folder, number_all must be reduced 6006 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: (extreme mode) reducing "folder_status_info" number_all count<br />'); } 6007 $prev_total_count = $folder_status_info['number_all']; 6008 $adjusted_total_count = ($prev_total_count - 1); 6009 $folder_status_info['number_all'] = $adjusted_total_count; 6010 6011 // reduce UNSEEN if necessary 6012 if ($reduce_unseen == True) 6013 { 6014 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: (extreme mode) reducing "folder_status_info" UNSEEN count<br />'); } 6015 $prev_new_count = $folder_status_info['number_new']; 6016 $adjusted_new_count = ($prev_new_count - 1); 6017 $folder_status_info['number_new'] = $adjusted_new_count; 6018 6019 // the user alert string needs updating also 6020 $folder_status_info['alert_string'] = str_replace((string)$prev_new_count, (string)$adjusted_new_count, $folder_status_info['alert_string']); 6021 } 6022 6023 // save altered data back into the cache 6024 if ($this->debug_events > 2) { $this->dbug->out('mail_msg: event_msg_move_or_delete: (extreme mode) (step 2) ADJUSTED $folder_status_info DUMP:', $folder_status_info); } 6025 // the $location we used above is still usable 6026 if (($this->debug_session_caching > 1) || ($this->debug_events > 1)) { $this->dbug->out('mail_msg: event_msg_move_or_delete: saving altered folder_status_info **directly** to appsession, $location: ['.$location.'] $app['.$app.']<br />'); } 6027 //$GLOBALS['phpgw']->session->appsession($location,$app,$folder_status_info); 6028 $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['folder_status_info'][$ex_folder] = $folder_status_info; 6029 // for DB sessions_db ONLY 6030 if (($GLOBALS['phpgw_info']['server']['sessions_type'] == 'db') 6031 || ($this->use_private_table == True)) 6032 { 6033 $my_location = (string)$acctnum.';folder_status_info;'.$ex_folder; 6034 if ($this->debug_events > 1) { $this->dbug->out('email_msg_base: event_msg_move_or_delete('.__LINE__.'): (extreme mode) sessions_type is ['.$GLOBALS['phpgw_info']['server']['sessions_type'].'] SO we have this additional step to save data to phpgw_app_sessions table, $my_location ['.$my_location.']<br />'); } 6035 if ($this->use_private_table == True) 6036 { 6037 $this->so->so_set_data($my_location, $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['folder_status_info'][$ex_folder]); 6038 } 6039 else 6040 { 6041 $GLOBALS['phpgw']->session->appsession($my_location, 'email', $this->ref_SESSION['phpgw_session']['phpgw_app_sessions']['email']['dat'][$acctnum]['folder_status_info'][$ex_folder]); 6042 } 6043 } 6044 $did_alter = True; 6045 } 6046 6047 // PART3 - THINGS NECESSARY DURING ANY DELETE 6048 // ANYTIME a message is moved out of a folder, we need to remove any cached "msg_structure" and "phpgw_header" data 6049 $ex_folder = $msgball['folder']; 6050 $ex_msgnum = $msgball['msgnum']; 6051 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (step 3a) (extreme mode) extreme or not, "msg_structure" and "phpgw_header" needs expired this specific message leaving this folder, $extra_keys ['.$extra_keys.']<br />'); } 6052 // we got this above, so since we have it, we test if it existed before we try to expire it 6053 if ($msg_headers) 6054 { 6055 $this->expire_session_cache_item('phpgw_header', $msgball['acctnum'], $ex_folder, $ex_msgnum); 6056 } 6057 // we do not know if this exists or not right now, so just call expire, if it exists it will be erased 6058 $this->expire_session_cache_item('msg_structure', $msgball['acctnum'], $ex_folder, $ex_msgnum); 6059 6060 6061 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (step 3b) (extreme mode) IF a target folder is provided and is a valid folder name, EXPIRE the "folder_status_info" for that TARGET folder, $to_fldball ['.serialize($to_fldball).']<br />'); } 6062 //if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: (step 3) (extreme mode) <b>DISABLED, problem expiring current status info</b> "folder_status_info" for the TARGET folder (if known) needs expired, I will not go to the brain-damaging extent of adjusting Target folder stats, $to_fldball ['.serialize($to_fldball).']<br />'); } 6063 if ((isset($to_fldball['folder'])) 6064 && (isset($to_fldball['acctnum'])) 6065 && ($to_fldball['folder'] != $this->del_pseudo_folder)) 6066 { 6067 $target_clean = $this->prep_folder_in($to_fldball['folder']); 6068 $urlencoded_target = $this->prep_folder_out($target_clean); 6069 // make sure that $to_fldball['folder'] is in PREPPED_OUT encoded 6070 $to_fldball['folder'] = $urlencoded_target; 6071 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (step 3b) (extreme mode) prepped $target_clean ['.$target_clean.'], $urlencoded_target ['.$urlencoded_target.']; $to_fldball ['.serialize($to_fldball).']<br />'); } 6072 if ((isset($target_clean)) 6073 || (trim($target_clean) != '')) 6074 { 6075 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (step 3b) (extreme mode) <b>expiring current status info</b> "folder_status_info" for the TARGET folder (was provided and it exists) needs expired, I will not go to the brain-damaging extent of adjusting Target folder stats, $to_fldball ['.serialize($to_fldball).']<br />'); } 6076 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete: ('.__LINE__.') (step 3b) (extreme mode) TARGET folder data was provided, MUST expire target folders "folder_status_info", $to_fldball[acctnum] is ['.$acctnum.'], $ex_folder is urlencoded target folder name ['.$urlencoded_target.']<br />'); } 6077 $this->expire_session_cache_item('folder_status_info', $to_fldball['acctnum'], $urlencoded_target); 6078 } 6079 else 6080 { 6081 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (step 3b) (extreme mode) can not do this step3b because TARGET FOLDER data was provided BUT empty $target_clean ['.$target_clean.'] indicates we could not verify it is a known valid folder, $to_fldball ['.serialize($to_fldball).']<br />'); } 6082 } 6083 } 6084 else 6085 { 6086 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (step 3b) (extreme mode) can not do this step3 because TARGET FOLDER data was NOT provided OR the folder is $this->del_pseudo_folder: ['.$this->del_pseudo_folder.'], note data for $to_fldball was ['.serialize($to_fldball).']<br />'); } 6087 } 6088 6089 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): (extreme mode) LEAVING, $did_alter ['.serialize($did_alter).']<br />'); } 6090 return $did_alter; 6091 } 6092 6093 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_move_or_delete('.__LINE__.'): LEAVING, unhandled situation or caching not enabled<br />'); } 6094 return False; 6095 } 6096 6097 /*! 6098 @function event_msg_append 6099 @abstract when a message is appended to a folder 6100 @author Angles 6101 @discussion ? 6102 */ 6103 function event_msg_append($target_fldball='', $called_by='not_specified') 6104 { 6105 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_append: ENTERING<br />'); } 6106 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_append: DISABLED UNTIL I FIGURE OUT HOW NOT TO EXPIRE CURRENT FOLDER STATS WHEN PASSED NOT ENOUGH INFO<br />'); } 6107 6108 $did_expire = False; 6109 /* 6110 $current_fldball = array(); 6111 $current_fldball['folder'] = $this->get_arg_value('folder'); 6112 $current_fldball['acctnum'] = $this->get_acctnum(); 6113 6114 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_append: we expire ONLY if $current_fldball ['.serialize($current_fldball).'] == $target_fldball ['.serialize($target_fldball).']<br />'); } 6115 if (($target_fldball['folder'] == $current_fldball['folder']) 6116 && ($target_fldball['acctnum'] == $current_fldball['acctnum'])) 6117 { 6118 if ($this->debug_events > 1) { $this->dbug->out('mail_msg: event_msg_append: we MUST expire "msgball_list" because $current_fldball == $target_fldball<br />'); } 6119 $this->expire_session_cache_item('msgball_list', $target_fldball['acctnum']); 6120 $did_expire = True; 6121 } 6122 */ 6123 if ($this->debug_events > 0) { $this->dbug->out('mail_msg: event_msg_append: LEAVING, returning $did_expire ['.serialize($did_expire).']<br />'); } 6124 return $did_expire; 6125 } 6126 6127 /**************************************************************************\ 6128 * USEFUL AND SIMPLE HTML FUNCTIONS * 6129 \**************************************************************************/ 6130 6131 /*! 6132 @function href_maketag 6133 @abstract will generate a typical A HREF html item 6134 */ 6135 function href_maketag($href_link='',$href_text='default text') 6136 { 6137 return '<a href="' .$href_link .'">' .$href_text .'</a>' ."\n"; 6138 } 6139 6140 /*! 6141 @function href_maketag_class 6142 @abstract will generate a typical A HREF html item with optional CLASS value for css specs 6143 */ 6144 function href_maketag_class($href_link='',$href_text='default text', $css_class_name='') 6145 { 6146 if ($css_class_name != '') 6147 { 6148 $class_prop=' class="'.$css_class_name.'" '; 6149 } 6150 else 6151 { 6152 $class_prop=''; 6153 } 6154 return '<a '.$class_prop.' href="' .$href_link .'">' .$href_text .'</a>' ."\n"; 6155 } 6156 6157 /*! 6158 @function img_maketag 6159 @abstract will generate a typical IMG html item 6160 */ 6161 function img_maketag($location='',$alt='',$height='',$width='',$border='') 6162 { 6163 $alt_default_txt = 'image'; 6164 $alt_unknown_txt = 'unknown'; 6165 if ($location == '') 6166 { 6167 return '<img src="" alt="['.$alt_unknown_txt.']">'; 6168 } 6169 if ($alt != '') 6170 { 6171 $alt_tag = ' alt="['.$alt.']"'; 6172 $title_tag = ' title="'.$alt.'"'; 6173 } 6174 else 6175 { 6176 $alt_tag = ' alt="['.$alt_default_txt.']"'; 6177 $title_tag = ''; 6178 } 6179 if ($height != '') 6180 { 6181 $height_tag = ' height="' .$height .'"'; 6182 } 6183 else 6184 { 6185 $height_tag = ''; 6186 } 6187 if ($width != '') 6188 { 6189 $width_tag = ' width="' .$width .'"'; 6190 } 6191 else 6192 { 6193 $width_tag = ''; 6194 } 6195 if ($border != '') 6196 { 6197 $border_tag = ' border="' .$border .'"'; 6198 } 6199 else 6200 { 6201 $border_tag = ''; 6202 } 6203 $image_html = '<img src="'.$location.'"' .$height_tag .$width_tag .$border_tag .$title_tag .$alt_tag .'>'; 6204 return $image_html; 6205 } 6206 6207 } 6208 // end of class mail_msg 6209?> 6210