From 1cded85790206afe084e1baff371c543711b2b18 Mon Sep 17 00:00:00 2001 From: thomascube Date: Sat, 3 Dec 2005 16:54:12 +0000 Subject: Re-design of caching (new database table added\!); some bugfixes; Postgres support --- program/include/cache.inc | 5 +- program/include/main.inc | 312 ++++++++++------ program/include/rcube_db.inc | 465 +++++++++++++----------- program/include/rcube_imap.inc | 748 +++++++++++++++++++++++++++------------ program/include/rcube_mdb2.inc | 16 + program/include/rcube_shared.inc | 109 +----- program/include/rcube_sqlite.inc | 71 ++++ program/include/session.inc | 16 +- 8 files changed, 1107 insertions(+), 635 deletions(-) create mode 100644 program/include/rcube_sqlite.inc (limited to 'program/include') diff --git a/program/include/cache.inc b/program/include/cache.inc index 8d088e57b..06e0681ce 100644 --- a/program/include/cache.inc +++ b/program/include/cache.inc @@ -43,8 +43,8 @@ function rcube_read_cache($key) return $data; } - - + + function rcube_write_cache($key, $data, $session_cache=FALSE) { global $DB, $CACHE_KEYS, $sess_id; @@ -91,7 +91,6 @@ function rcube_write_cache($key, $data, $session_cache=FALSE) } - function rcube_clear_cache($key) { global $DB; diff --git a/program/include/main.inc b/program/include/main.inc index ddb42181a..40ca1d4d7 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -69,7 +69,7 @@ function rcmail_startup($task='mail') // we can use the database for storing session data // session queries do not work with MDB2 - if ($CONFIG['db_backend']!='mdb2' && is_object($DB) && $DB->db_provider!='sqlite') + if ($CONFIG['db_backend']!='mdb2' && is_object($DB) /* && $DB->db_provider!='sqlite' */) include_once('include/session.inc'); @@ -143,9 +143,9 @@ function rcmail_auth_hash($sess_id, $ts) // create IMAP object and connect to server function rcmail_imap_init($connect=FALSE) { - global $CONFIG, $IMAP; + global $CONFIG, $DB, $IMAP; - $IMAP = new rcube_imap(); + $IMAP = new rcube_imap($DB); // connect with stored session data if ($connect) @@ -227,6 +227,22 @@ function get_table_name($table) } +// return correct name for a specific database sequence +// (used for Postres only) +function get_sequence_name($sequence) + { + global $CONFIG; + + // return table name if configured + $config_key = 'db_sequence_'.$sequence; + + if (strlen($CONFIG[$config_key])) + return $CONFIG[$config_key]; + + return $table; + } + + // init output object for GUI and add common scripts function load_gui() @@ -380,15 +396,15 @@ function rcmail_create_user($user, $host) $host, $_SESSION['user_lang']); - if ($user_id = $DB->insert_id('user_ids')) + if ($user_id = $DB->insert_id(get_sequence_name('users'))) { $user_email = strstr($user, '@') ? $user : sprintf('%s@%s', $user, $host); $user_name = $user!=$user_email ? $user : ''; // also create a new identity record $DB->query("INSERT INTO ".get_table_name('identities')." - (user_id, `default`, name, email) - VALUES (?, '1', ?, ?)", + (user_id, del, standard, name, email) + VALUES (?, 0, 1, ?, ?)", $user_id, $user_name, $user_email); @@ -479,7 +495,6 @@ function decrypt_passwd($cypher) function rcube_remote_response($js_code) { send_nocacheing_headers(); - //header('Content-Type: text/javascript'); header('Content-Type: application/x-javascript'); print '/** remote response ['.date('d/M/Y h:i:s O')."] **/\n"; @@ -530,6 +545,117 @@ function rcube_add_label() } +// remove temp files of a session +function rcmail_clear_session_temp($sess_id) + { + global $CONFIG; + + $temp_dir = $CONFIG['temp_dir'].(!eregi('\/$', $CONFIG['temp_dir']) ? '/' : ''); + $cache_dir = $temp_dir.$sess_id; + + if (is_dir($cache_dir)) + { + clear_directory($cache_dir); + rmdir($cache_dir); + } + } + + + +// replace specials characters to a specific encoding type +function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) + { + global $OUTPUT_TYPE, $CHARSET; + static $html_encode_arr, $js_rep_table, $rtf_rep_table, $xml_rep_table; + + if (!$enctype) + $enctype = $GLOBALS['OUTPUT_TYPE']; + + // convert nbsps back to normal spaces if not html + if ($enctype!='html') + $str = str_replace(chr(160), ' ', $str); + + + // encode for plaintext + if ($enctype=='text') + return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str); + + // encode for HTML output + if ($enctype=='html') + { + if (!$html_encode_arr) + { + if ($CHARSET=='ISO-8859-1') + { + $html_encode_arr = get_html_translation_table(HTML_ENTITIES); + $html_encode_arr[chr(128)] = '€'; + } + else + $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS); + + unset($html_encode_arr['?']); + unset($html_encode_arr['&']); + } + + $ltpos = strpos($str, '<'); + $encode_arr = $html_encode_arr; + + // don't replace quotes and html tags + if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false) + { + unset($encode_arr['"']); + unset($encode_arr['<']); + unset($encode_arr['>']); + } + else if ($mode=='remove') + $str = strip_tags($str); + + $out = strtr($str, $encode_arr); + + return $newlines ? nl2br($out) : $out; + } + + + if ($enctype=='url') + return rawurlencode($str); + + + // if the replace tables for RTF, XML and JS are not yet defined + if (!$js_rep_table) + { + $js_rep_table = $rtf_rep_table = $xml_rep_table = array(); + + for ($c=160; $c<256; $c++) // can be increased to support more charsets + { + $hex = dechex($c); + $rtf_rep_table[Chr($c)] = "\\'$hex"; + $xml_rep_table[Chr($c)] = "&#$c;"; + + if ($CHARSET=='ISO-8859-1') + $js_rep_table[Chr($c)] = sprintf("\u%s%s", str_repeat('0', 4-strlen($hex)), $hex); + } + + $js_rep_table['"'] = sprintf("\u%s%s", str_repeat('0', 4-strlen(dechex(34))), dechex(34)); + $xml_rep_table['"'] = '"'; + } + + // encode for RTF + if ($enctype=='xml') + return strtr($str, $xml_rep_table); + + // encode for javascript use + if ($enctype=='js') + return preg_replace(array("/\r\n/", '/"/', "/([^\\\])'/"), array('\n', '\"', "$1\'"), strtr($str, $js_rep_table)); + + // encode for RTF + if ($enctype=='rtf') + return preg_replace("/\r\n/", "\par ", strtr($str, $rtf_rep_table)); + + // no encoding given -> return original string + return $str; + } + + // ************** template parsing and gui functions ************** @@ -653,104 +779,53 @@ function rcube_xml_command($command, $str_attrib, $a_attrib=NULL) case 'object': $object = strtolower($attrib['name']); + $object_handlers = array( + // MAIL + 'mailboxlist' => 'rcmail_mailbox_list', + 'messages' => 'rcmail_message_list', + 'messagecountdisplay' => 'rcmail_messagecount_display', + 'messageheaders' => 'rcmail_message_headers', + 'messagebody' => 'rcmail_message_body', + 'messageattachments' => 'rcmail_message_attachments', + 'blockedobjects' => 'rcmail_remote_objects_msg', + 'messagecontentframe' => 'rcmail_messagecontent_frame', + 'messagepartframe' => 'rcmail_message_part_frame', + 'messagepartcontrols' => 'rcmail_message_part_controls', + 'composeheaders' => 'rcmail_compose_headers', + 'composesubject' => 'rcmail_compose_subject', + 'composebody' => 'rcmail_compose_body', + 'composeattachmentlist' => 'rcmail_compose_attachment_list', + 'composeattachmentform' => 'rcmail_compose_attachment_form', + 'composeattachment' => 'rcmail_compose_attachment_field', + 'priorityselector' => 'rcmail_priority_selector', + 'charsetselector' => 'rcmail_charset_selector', + + // ADDRESS BOOK + 'addresslist' => 'rcmail_contacts_list', + 'addressframe' => 'rcmail_contact_frame', + 'recordscountdisplay' => 'rcmail_rowcount_display', + 'contactdetails' => 'rcmail_contact_details', + 'contacteditform' => 'rcmail_contact_editform', + + // USER SETTINGS + 'userprefs' => 'rcmail_user_prefs_form', + 'itentitieslist' => 'rcmail_identities_list', + 'identityframe' => 'rcmail_identity_frame', + 'identityform' => 'rcube_identity_form', + 'foldersubscription' => 'rcube_subscription_form', + 'createfolder' => 'rcube_create_folder_form', + 'composebody' => 'rcmail_compose_body' + ); + if ($object=='loginform') return rcmail_login_form($attrib); else if ($object=='message') return rcmail_message_container($attrib); - - // MAIL - else if ($object=='mailboxlist' && function_exists('rcmail_mailbox_list')) - return rcmail_mailbox_list($attrib); - - else if ($object=='messages' && function_exists('rcmail_message_list')) - return rcmail_message_list($attrib); - - else if ($object=='messagecountdisplay' && function_exists('rcmail_messagecount_display')) - return rcmail_messagecount_display($attrib); - - else if ($object=='messageheaders' && function_exists('rcmail_message_headers')) - return rcmail_message_headers($attrib); - - else if ($object=='messageattachments' && function_exists('rcmail_message_attachments')) - return rcmail_message_attachments($attrib); - - else if ($object=='messagebody' && function_exists('rcmail_message_body')) - return rcmail_message_body($attrib); - - else if ($object=='blockedobjects' && function_exists('rcmail_remote_objects_msg')) - return rcmail_remote_objects_msg($attrib); - - else if ($object=='messagecontentframe' && function_exists('rcmail_messagecontent_frame')) - return rcmail_messagecontent_frame($attrib); - - else if ($object=='messagepartframe' && function_exists('rcmail_message_part_frame')) - return rcmail_message_part_frame($attrib); - - else if ($object=='messagepartcontrols' && function_exists('rcmail_message_part_controls')) - return rcmail_message_part_controls($attrib); - - else if ($object=='composeheaders' && function_exists('rcmail_compose_headers')) - return rcmail_compose_headers($attrib); - - else if ($object=='composesubject' && function_exists('rcmail_compose_subject')) - return rcmail_compose_subject($attrib); - - else if ($object=='composebody' && function_exists('rcmail_compose_body')) - return rcmail_compose_body($attrib); - - else if ($object=='composeattachmentlist' && function_exists('rcmail_compose_attachment_list')) - return rcmail_compose_attachment_list($attrib); - - else if ($object=='composeattachmentform' && function_exists('rcmail_compose_attachment_form')) - return rcmail_compose_attachment_form($attrib); - - else if ($object=='composeattachment' && function_exists('rcmail_compose_attachment_field')) - return rcmail_compose_attachment_field($attrib); - - else if ($object=='priorityselector' && function_exists('rcmail_priority_selector')) - return rcmail_priority_selector($attrib); - - else if ($object=='priorityselector' && function_exists('rcmail_priority_selector')) - return rcmail_priority_selector($attrib); - - - // ADDRESS BOOK - else if ($object=='addresslist' && function_exists('rcmail_contacts_list')) - return rcmail_contacts_list($attrib); - - else if ($object=='addressframe' && function_exists('rcmail_contact_frame')) - return rcmail_contact_frame($attrib); - - else if ($object=='recordscountdisplay' && function_exists('rcmail_rowcount_display')) - return rcmail_rowcount_display($attrib); - - else if ($object=='contactdetails' && function_exists('rcmail_contact_details')) - return rcmail_contact_details($attrib); - - else if ($object=='contacteditform' && function_exists('rcmail_contact_editform')) - return rcmail_contact_editform($attrib); - - - // USER SETTINGS - else if ($object=='userprefs' && function_exists('rcmail_user_prefs_form')) - return rcmail_user_prefs_form($attrib); - - else if ($object=='itentitieslist' && function_exists('rcmail_identities_list')) - return rcmail_identities_list($attrib); - - else if ($object=='identityframe' && function_exists('rcmail_identity_frame')) - return rcmail_identity_frame($attrib); - - else if ($object=='identityform' && function_exists('rcube_identity_form')) - return rcube_identity_form($attrib); - - else if ($object=='foldersubscription' && function_exists('rcube_subscription_form')) - return rcube_subscription_form($attrib); - - else if ($object=='createfolder' && function_exists('rcube_create_folder_form')) - return rcube_create_folder_form($attrib); - + + // execute object handler function + else if ($object_handlers[$object] && function_exists($object_handlers[$object])) + return call_user_func($object_handlers[$object], $attrib); else if ($object=='pagetitle') { @@ -878,7 +953,7 @@ function rcube_button($attrib) // generate image tag if ($attrib['type']=='image') { - $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'width', 'height', 'border', 'hspace', 'vspace', 'alt')); + $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'width', 'height', 'border', 'hspace', 'vspace', 'align', 'alt')); $img_tag = sprintf('', $attrib_str); $btn_content = sprintf($img_tag, $skin_path.$attrib['image']); if ($attrib['label']) @@ -1161,4 +1236,39 @@ EOF; } + +function rcmail_charset_selector($attrib) + { + // pass the following attributes to the form class + $field_attrib = array('name' => '_charset'); + foreach ($attrib as $attr => $value) + if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex'))) + $field_attrib[$attr] = $value; + + $charsets = array( + 'US-ASCII' => 'ASCII (English)', + 'X-EUC-JP' => 'EUC-JP (Japanese)', + 'EUC-KR' => 'EUC-KR (Korean)', + 'BIG5' => 'BIG5 (Chinese)', + 'GB2312' => 'GB2312 (Chinese)', + 'ISO-8859-1' => 'ISO-8859-1 (Latin-1)', + 'ISO-8859-2' => 'ISO-8895-2 (Central European)', + 'ISO-8859-7' => 'ISO-8859-7 (Greek)', + 'ISO-8859-9' => 'ISO-8859-9 (Turkish)', + 'Windows-1251' => 'Windows-1251 (Cyrillic)', + 'Windows-1252' => 'Windows-1252 (Western)', + 'Windows-1255' => 'Windows-1255 (Hebrew)', + 'Windows-1256' => 'Windows-1256 (Arabic)', + 'Windows-1257' => 'Windows-1257 (Baltic)', + 'UTF-8' => 'UTF-8' + ); + + $select = new select($field_attrib); + $select->add(array_values($charsets), array_keys($charsets)); + + $set = $_POST['_charset'] ? $_POST['_charset'] : $GLOBALS['CHARSET']; + return $select->show($set); + } + + ?> \ No newline at end of file diff --git a/program/include/rcube_db.inc b/program/include/rcube_db.inc index f13ab55c0..acb13ce37 100755 --- a/program/include/rcube_db.inc +++ b/program/include/rcube_db.inc @@ -23,299 +23,332 @@ require_once('DB.php'); class rcube_db -{ - var $db_dsnw; // DSN for write operations - var $db_dsnr; // DSN for read operations - var $db_connected=false; // Already connected ? - var $db_mode=''; // Connection mode - var $db_handle=0; // Connection handle - - var $a_query_results = array('dummy'); - var $last_res_id = 0; - - // PHP 5 constructor - function __construct($db_dsnw,$db_dsnr='') + { + var $db_dsnw; // DSN for write operations + var $db_dsnr; // DSN for read operations + var $db_connected = false; // Already connected ? + var $db_mode = ''; // Connection mode + var $db_handle = 0; // Connection handle + + var $a_query_results = array('dummy'); + var $last_res_id = 0; + + + // PHP 5 constructor + function __construct($db_dsnw,$db_dsnr='') { - if ($db_dsnr=='') $db_dsnr=$db_dsnw; + if ($db_dsnr=='') + $db_dsnr=$db_dsnw; - $this->db_dsnw = $db_dsnw; - $this->db_dsnr = $db_dsnr; + $this->db_dsnw = $db_dsnw; + $this->db_dsnr = $db_dsnr; - $dsn_array = DB::parseDSN($db_dsnw); - $this->db_provider = $dsn_array['phptype']; + $dsn_array = DB::parseDSN($db_dsnw); + $this->db_provider = $dsn_array['phptype']; } - // PHP 4 compatibility - function rcube_db($db_dsnw,$db_dsnr='') + + // PHP 4 compatibility + function rcube_db($db_dsnw,$db_dsnr='') { - $this->__construct($db_dsnw,$db_dsnr); + $this->__construct($db_dsnw,$db_dsnr); } - // Connect to specific database - function dsn_connect($dsn) + + // Connect to specific database + function dsn_connect($dsn) { - // Use persistent connections if available - $dbh = DB::connect($dsn, array('persistent' => $true)); + // Use persistent connections if available + $dbh = DB::connect($dsn, array('persistent' => TRUE)); - if (DB::isError($dbh)) - raise_error(array('code' => 500, + if (DB::isError($dbh)) + raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => $dbh->getMessage()), TRUE, FALSE); - else if ($this->db_provider=='sqlite') - { - $dsn_array = DB::parseDSN($dsn); - if (!filesize($dsn_array['database']) && !empty($this->sqlite_initials)) - $this->_sqlite_create_database($dbh, $this->sqlite_initials); - } + else if ($this->db_provider=='sqlite') + { + $dsn_array = DB::parseDSN($dsn); + if (!filesize($dsn_array['database']) && !empty($this->sqlite_initials)) + $this->_sqlite_create_database($dbh, $this->sqlite_initials); + } - return $dbh; + return $dbh; } - // Connect to appropiate databse - function db_connect ($mode) - { - $this->db_mode = $mode; - // Already connected - if ($this->db_connected) - { - // no replication, current connection is ok - if ($this->db_dsnw==$this->db_dsnr) return; + // Connect to appropiate databse + function db_connect ($mode) + { + $this->db_mode = $mode; + + // Already connected + if ($this->db_connected) + { + // no replication, current connection is ok + if ($this->db_dsnw==$this->db_dsnr) + return; - // connected to master, current connection is ok - if ($this->db_mode=='w') return; - - // Same mode, current connection is ok - if ($this->db_mode==$mode) return; - } + // connected to master, current connection is ok + if ($this->db_mode=='w') + return; + + // Same mode, current connection is ok + if ($this->db_mode==$mode) + return; + } - if ($mode=='r') - $dsn=$this->db_dsnr; - else - $dsn=$this->db_dsnw; + if ($mode=='r') + $dsn = $this->db_dsnr; + else + $dsn = $this->db_dsnw; - $this->db_handle = $this->dsn_connect($dsn); - $this->db_connected = true; + $this->db_handle = $this->dsn_connect($dsn); + $this->db_connected = true; } - // Query database - - function query() + + // Query database + function query() { - $params = func_get_args(); - $query = array_shift($params); - - return $this->_query($query, 0, 0, $params); + $params = func_get_args(); + $query = array_shift($params); + + return $this->_query($query, 0, 0, $params); } - - function limitquery() + + + // Query with limits + function limitquery() { - $params = func_get_args(); - $query = array_shift($params); - $offset = array_shift($params); - $numrows = array_shift($params); + $params = func_get_args(); + $query = array_shift($params); + $offset = array_shift($params); + $numrows = array_shift($params); - return $this->_query($query, $offset, $numrows, $params); + return $this->_query($query, $offset, $numrows, $params); } - - function _query($query, $offset, $numrows, $params) + + + function _query($query, $offset, $numrows, $params) { - // Read or write ? - if (strtolower(trim(substr($query,0,6)))=='select') - $mode='r'; - else - $mode='w'; + // Read or write ? + if (strtolower(trim(substr($query,0,6)))=='select') + $mode='r'; + else + $mode='w'; - $this->db_connect($mode); + $this->db_connect($mode); - if ($this->db_provider == 'sqlite') - $query = $this->_sqlite_prepare_query($query); - - if ($numrows || $offset) - { - $result = $this->db_handle->limitQuery($query,$offset,$numrows,$params); - } - else - $result = $this->db_handle->query($query, $params); + if ($this->db_provider == 'sqlite') + $this->_sqlite_prepare(); - if (DB::isError($result)) - { - raise_error(array('code' => 500, - 'type' => 'db', - 'line' => __LINE__, - 'file' => __FILE__, - 'message' => $result->getMessage().'; QUERY: '.$query), TRUE, FALSE); - return false; - } - - return $this->_add_result($result, $query); + if ($numrows || $offset) + $result = $this->db_handle->limitQuery($query,$offset,$numrows,$params); + else + $result = $this->db_handle->query($query, $params); + + // add result, even if it's an error + return $this->_add_result($result); } - - function num_rows($res_id=NULL) + + + function num_rows($res_id=NULL) { - if (!$this->db_handle) - return FALSE; + if (!$this->db_handle) + return FALSE; - $result = $this->_get_result($res_id); - - if ($result) - return $result->numRows(); - else - return FALSE; + if ($result = $this->_get_result($res_id)) + return $result->numRows(); + else + return FALSE; } - function affected_rows($res_id=NULL) + + function affected_rows($res_id=NULL) { - if (!$this->db_handle) - return FALSE; - - return $this->db_handle->affectedRows(); + if (!$this->db_handle) + return FALSE; + + return $this->db_handle->affectedRows(); } - function insert_id($sequence = '') + + function insert_id($sequence = '') { - if (!$this->db_handle || $this->db_mode=='r') - return FALSE; - - switch($this->db_provider) - { - case 'pgsql': - // PostgreSQL uses sequences - $result =& $this->db_handle->getOne("SELECT CURRVAL('$sequence')"); - if (DB::isError($result)) { - raise_error( array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, - 'message' => $result->getMessage()), TRUE, TRUE); - } - return $result; + if (!$this->db_handle || $this->db_mode=='r') + return FALSE; + + switch($this->db_provider) + { + case 'pgsql': + // PostgreSQL uses sequences + $result =& $this->db_handle->getOne("SELECT CURRVAL('$sequence')"); + if (DB::isError($result)) + raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, + 'message' => $result->getMessage()), TRUE, FALSE); + + return $result; - case 'mysql': // This is unfortuneate - return mysql_insert_id($this->db_handle->connection); + case 'mysql': // This is unfortuneate + return mysql_insert_id($this->db_handle->connection); - case 'mysqli': - return mysqli_insert_id($this->db_handle->connection); + case 'mysqli': + return mysqli_insert_id($this->db_handle->connection); - case 'sqlite': - return sqlite_last_insert_rowid($this->db_handle->connection); + case 'sqlite': + return sqlite_last_insert_rowid($this->db_handle->connection); - default: - die("portability issue with this database, please have the developer fix"); - } + default: + die("portability issue with this database, please have the developer fix"); + } } - function fetch_assoc($res_id=NULL) + function fetch_assoc($res_id=NULL) { - $result = $this->_get_result($res_id); - - if (DB::isError($result)) - { - raise_error( array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, - 'message' => $this->db_link->getMessage()), TRUE, FALSE); - return FALSE; - } + $result = $this->_get_result($res_id); + + if (DB::isError($result)) + { + raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, + 'message' => $this->db_link->getMessage()), TRUE, FALSE); + return FALSE; + } - return $result->fetchRow(DB_FETCHMODE_ASSOC); + return $result->fetchRow(DB_FETCHMODE_ASSOC); } - function quote($input, $type=null) + function quote($input, $type=null) { - if (!$this->db_handle) - $this->db_connect('r'); + if (!$this->db_handle) + $this->db_connect('r'); - return $this->db_handle->quote($input); + return $this->db_handle->quote($input); } - function quoteIdentifier($str) + function quoteIdentifier($str) { - if (!$this->db_handle) - $this->db_connect('r'); + if (!$this->db_handle) + $this->db_connect('r'); - return $this->db_handle->quoteIdentifier($str); - } - - function quote_identifier($str) - { - return $this->quoteIdentifier($str); + return $this->db_handle->quoteIdentifier($str); } - - function unixtimestamp($field) - { - switch($this->db_provider) - { - case 'pgsql': - return "EXTRACT (EPOCH FROM $field)"; - break; - default: - return "UNIX_TIMESTAMP($field)"; - } - } - - function _add_result($res, $query) + + function quote_identifier($str) + { + return $this->quoteIdentifier($str); + } + + + function unixtimestamp($field) { - // sql error occured - if (DB::isError($res)) - { - raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => $res->getMessage() . " Query: " . substr(preg_replace('/[\r\n]+\s*/', ' ', $query), 0, 1024)), TRUE, FALSE); - return FALSE; - } - else - { - $res_id = sizeof($this->a_query_results); - $this->a_query_results[$res_id] = $res; - $this->last_res_id = $res_id; - return $res_id; - } + switch($this->db_provider) + { + case 'pgsql': + return "EXTRACT (EPOCH FROM $field)"; + break; + + default: + return "UNIX_TIMESTAMP($field)"; + } } - function _get_result($res_id) + function fromunixtime($timestamp) { - if ($res_id==NULL) - $res_id = $this->last_res_id; + switch($this->db_provider) + { + case 'mysqli': + case 'mysql': + case 'sqlite': + return "FROM_UNIXTIME($timestamp)"; + + default: + return date("'Y-m-d H:i:s'", $timestamp); + } + } + + + function _add_result($res) + { + // sql error occured + if (DB::isError($res)) + { + raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, + 'message' => $res->getMessage() . " Query: " . substr(preg_replace('/[\r\n]+\s*/', ' ', $res->userinfo), 0, 1024)), TRUE, FALSE); + return FALSE; + } + else + { + $res_id = sizeof($this->a_query_results); + $this->a_query_results[$res_id] = $res; + $this->last_res_id = $res_id; + return $res_id; + } + } + + + function _get_result($res_id) + { + if ($res_id==NULL) + $res_id = $this->last_res_id; - if ($res_id && isset($this->a_query_results[$res_id])) - return $this->a_query_results[$res_id]; - else - return FALSE; + if ($res_id && isset($this->a_query_results[$res_id])) + return $this->a_query_results[$res_id]; + else + return FALSE; } - // create a sqlite database from a file - function _sqlite_create_database($dbh, $fileName) + // create a sqlite database from a file + function _sqlite_create_database($dbh, $fileName) { - if (empty($fileName) || !is_string($fileName)) - return ; - - $data = ''; - if ($fd = fopen($fileName, 'r')) - { - $data = fread($fd, filesize($fileName)); - fclose($fd); - } - - if (strlen($data)) - sqlite_exec($dbh->connection, $data); + if (empty($fileName) || !is_string($fileName)) + return ; + + $data = ''; + if ($fd = fopen($fileName, 'r')) + { + $data = fread($fd, filesize($fileName)); + fclose($fd); + } + + if (strlen($data)) + sqlite_exec($dbh->connection, $data); } - // transform a query so that it is sqlite2 compliant - function _sqlite_prepare_query($query) + function _sqlite_prepare() { - if (!is_string($query)) - return ($query); - - $search = array('/NOW\(\)/i', '/`/'); - $replace = array("datetime('now')", '"'); - $query = preg_replace($search, $replace, $query); + include_once('include/rcube_sqlite.inc'); - return ($query); + // we emulate via callback some missing MySQL function + sqlite_create_function($this->db_handle->connection, "from_unixtime", "rcube_sqlite_from_unixtime"); + sqlite_create_function($this->db_handle->connection, "unix_timestamp", "rcube_sqlite_unix_timestamp"); + sqlite_create_function($this->db_handle->connection, "now", "rcube_sqlite_now"); + sqlite_create_function($this->db_handle->connection, "md5", "rcube_sqlite_md5"); } - -} + +/* + // transform a query so that it is sqlite2 compliant + function _sqlite_prepare_query($query) + { + if (!is_string($query)) + return ($query); + + + $search = array('/NOW\(\)/i', '/`/'); + $replace = array("datetime('now')", '"'); + $query = preg_replace($search, $replace, $query); + + return ($query); + } +*/ + } // end class rcube_db ?> \ No newline at end of file diff --git a/program/include/rcube_imap.inc b/program/include/rcube_imap.inc index 2237b38f3..ed7c3edcd 100644 --- a/program/include/rcube_imap.inc +++ b/program/include/rcube_imap.inc @@ -28,6 +28,7 @@ require_once('lib/utf7.inc'); class rcube_imap { + var $db; var $conn; var $root_ns = ''; var $root_dir = ''; @@ -38,21 +39,23 @@ class rcube_imap var $caching_enabled = FALSE; var $default_folders = array('inbox', 'drafts', 'sent', 'junk', 'trash'); var $cache = array(); + var $cache_keys = array(); var $cache_changes = array(); var $uid_id_map = array(); var $msg_headers = array(); + var $capabilities = array(); // PHP 5 constructor - function __construct() + function __construct($db_conn) { - + $this->db = $db_conn; } // PHP 4 compatibility - function rcube_imap() + function rcube_imap($db_conn) { - $this->__construct(); + $this->__construct($db_conn); } @@ -95,6 +98,7 @@ class rcube_imap // get account namespace if ($this->conn) { + $this->_parse_capability($this->conn->capability); iil_C_NameSpace($this->conn); if (!empty($this->conn->delimiter)) @@ -185,6 +189,13 @@ class rcube_imap } + function get_capability($cap) + { + $cap = strtoupper($cap); + return $this->capabilities[$cap]; + } + + function get_hierarchy_delimiter() { if ($this->conn && empty($this->delimiter)) @@ -298,200 +309,131 @@ class rcube_imap // private method for listing message header - // by DrSlump - function __list_headers($mailbox='', $page=NULL, $sort_field='date', $sort_order='DESC') + function _list_headers($mailbox='', $page=NULL, $sort_field='date', $sort_order='DESC') { - $a_out = array(); - $cached_count = 0; - if (!strlen($mailbox)) - return $a_out; + return array(); + + $max = $this->_messagecount($mailbox); + $start_msg = ($this->list_page-1) * $this->page_size; + + if ($page=='all') + { + $begin = 0; + $end = $max; + } + else if ($sort_order=='DESC') + { + $begin = $max - $this->page_size - $start_msg; + $end = $max - $start_msg; + } + else + { + $begin = $start_msg; + $end = $start_msg + $this->page_size; + } - $mbox_count = $this->_messagecount($mailbox /*, 'ALL', TRUE*/); + if ($begin < 0) $begin = 0; + if ($end < 0) $end = $max; + if ($end > $max) $end = $max; - $revalidate = false; - if ($mbox_count) +//console("fetch headers $start_msg to ".($start_msg+$this->page_size)." (msg $begin to $end)"); + + $headers_sorted = FALSE; + $cache_key = $mailbox.'.msg'; + $cache_status = $this->check_cache_status($mailbox, $cache_key); + +//console("Cache status = $cache_status"); + + // cache is OK, we can get all messages from local cache + if ($cache_status>0) + { + $a_msg_headers = $this->get_message_cache($cache_key, $start_msg, $start_msg+$this->page_size, $sort_field, $sort_order); + $headers_sorted = TRUE; + } + else { - // get cached headers - $a_out = $this->get_cache($mailbox.'.msg'); - $a_out = is_array($a_out) ? $a_out : array(); // make sure we get an array - - $cached_count = count($a_out); - $a_new = array(); - $revalidate = true; // revalidate by default - - // if the cache count is greater then there have been changes for sure - if ($cached_count <= $mbox_count) + // retrieve headers from IMAP + if ($this->get_capability('sort') && ($msg_index = iil_C_Sort($this->conn, $mailbox, $sort_field))) { - $from = $cached_count?$cached_count:1; - - //get new headers (at least one is returned) - $a_temp = iil_C_FetchHeaders($this->conn, $mailbox, $from . ':' . $mbox_count); - $duplicated = $cached_count?true:false; - - foreach ($a_temp as $hdr) +//console("$mailbox: ".count($msg_index)); + + $msgs = $msg_index[$begin]; + for ($i=$begin; $i < $end; $i++) { - //skip the first one if duplicated - if ($duplicated) - { - //check for changes using the UID - $lastCacheHdr = end($a_out); - if ($hdr->uid === $lastCacheHdr->uid) - $revalidate = false; - - $duplicated = false; - continue; - } - - //skip deleted ones - if (! $hdr->deleted) - $a_new[ $hdr->uid ] = $hdr; + if ($sort_order == 'DESC') + $msgs = $msg_index[$i].','.$msgs; + else + $msgs = $msgs.','.$msg_index[$i]; } - } - //revalidate cache if needed - $to = $mbox_count - count($a_new); - if ($revalidate && $to !== 0) //we'll need to reindex the array so we have to make a copy + $sorted = TRUE; + } + else { - $a_dirty = $a_out; - $a_out = array(); - $a_buffers = array(); + $msgs = sprintf("%d:%d", $begin+1, $end); + $sorted = FALSE; + } - //fetch chunks of 20 headers - $step = 20; - $found = false; - - //fetch headers in blocks starting from new to old - do { - $from = $to-$step; - if ($from < 1) $from = 1; - //store the block in a temporal buffer - $a_buffers[$from] = iil_C_FetchHeaders($this->conn, $mailbox, $from . ':' . $to); + // cache is dirty, sync it + if ($this->caching_enabled && $cache_status==-1) + { + $this->sync_header_index($mailbox); + return $this->_list_headers($mailbox, $page, $sort_field, $sort_order); + } + - //compare the fetched headers with the ones in the cache - $idx = 0; - foreach ($a_buffers[$from] as $k=>$hdr) + // cache is incomplete + $cache_index = $this->get_message_cache_index($cache_key); + + // fetch reuested headers from server + $a_header_index = iil_C_FetchHeaders($this->conn, $mailbox, $msgs); + $a_msg_headers = array(); + + + if (!empty($a_header_index)) + { + foreach ($a_header_index as $i => $headers) + { + if ($headers->deleted) { - //if it's different the comparison ends - if (!isset($a_dirty[$hdr->uid]) || $a_dirty[$hdr->uid]->id !== $hdr->id) - break; + // delete from cache + if ($cache_index[$headers->id] && $cache_index[$headers->id] == $headers->uid) + $this->remove_message_cache($cache_key, $headers->id); - //if we arrive here then we know that the older messages in cache are ok - $found = $hdr->id; - $idx++; + continue; } - //remove from the buffer the headers which are already cached - if ($found) - $a_buffers[$from] = array_splice($a_buffers[$from], 0, $idx ); - - $to = $from-1; - } - while ($found===false && $from > 1); + // add message to cache + if ($this->caching_enabled && $cache_index[$headers->id] != $headers->uid) + $this->add_message_cache($cache_key, $headers->id, $headers); - //just keep the headers we are certain that didn't change in the cache - if ($found !== false) - { - foreach ($a_dirty as $hdr) - { - if ($hdr->id > $found) break; - $a_out[$hdr->uid] = $hdr; - } - } - - //we builded the block buffers from new to older, we process them in reverse order - ksort($a_buffers, SORT_NUMERIC); - foreach ($a_buffers as $a_buff) - { - foreach ($a_buff as $hdr) - { - if (! $hdr->deleted) - $a_out[$hdr->uid] = $hdr; - } + $a_msg_headers[$headers->uid] = $headers; } } - - //array_merge() would reindex the keys, so we use this 'hack' - $a_out += $a_new; - } - - //write headers list to cache if needed - if ($revalidate || count($a_out)!=$cached_count) { - $this->update_cache($mailbox.'.msg', $a_out); - } - - //sort headers by a specific col - $a_out = iil_SortHeaders( $a_out, $sort_field, $sort_order ); - - // return complete list of messages - if (strtolower($page)=='all') - return $a_out; - $start_msg = ($this->list_page-1) * $this->page_size; - return array_slice($a_out, $start_msg, $this->page_size); - } + // delete cached messages with a higher index than $max + $this->clear_message_cache($cache_key, $max); - // original function; replaced 2005/10/18 - // private method for listing message header - function _list_headers($mailbox='', $page=NULL, $sort_field='date', $sort_order='DESC') - { - $max = $this->_messagecount($mailbox); - - if (!strlen($mailbox)) - return array(); - - // get cached headers - $a_msg_headers = $this->get_cache($mailbox.'.msg'); - - // retrieve headers from IMAP - if (!is_array($a_msg_headers) || sizeof($a_msg_headers) != $max) - { - $a_header_index = iil_C_FetchHeaders($this->conn, $mailbox, "1:$max"); - $a_msg_headers = array(); - - if (!empty($a_header_index)) - foreach ($a_header_index as $i => $headers) - if (!$headers->deleted) - $a_msg_headers[$headers->uid] = $headers; - } - else - $headers_cached = TRUE; - - if (!is_array($a_msg_headers)) - return array(); - - // sort headers by a specific col - $a_headers = iil_SortHeaders($a_msg_headers, $sort_field, $sort_order); - $headers_count = count($a_headers); - - // free memory - unset($a_msg_headers); - - // write headers list to cache - if (!$headers_cached) - $this->update_cache($mailbox.'.msg', $a_headers); + // kick child process to sync cache - // update message count cache - $a_mailbox_cache = $this->get_cache('messagecount'); - if (isset($a_mailbox_cache[$mailbox]['ALL']) && $a_mailbox_cache[$mailbox]['ALL'] != $headers_count) - { - $a_mailbox_cache[$mailbox]['ALL'] = (int)$headers_count; - $this->update_cache('messagecount', $a_mailbox_cache); } - if (empty($a_headers)) + + // return empty array if no messages found + if (!is_array($a_msg_headers) || empty($a_msg_headers)) return array(); - - // return complete list of messages - if (strtolower($page)=='all') - return $a_headers; - $start_msg = ($this->list_page-1) * $this->page_size; - return array_slice($a_headers, $start_msg, $this->page_size); + + // if not already sorted + if (!$headers_sorted) + $a_msg_headers = iil_SortHeaders($a_msg_headers, $sort_field, $sort_order); + + return array_values($a_msg_headers); } - + // return sorted array of message UIDs function message_index($mbox='', $sort_field='date', $sort_order='DESC') @@ -510,9 +452,54 @@ class rcube_imap } - function sync_header_index($mbox=NULL) + function sync_header_index($mailbox) { - + $cache_key = $mailbox.'.msg'; + $cache_index = $this->get_message_cache_index($cache_key); + $msg_count = $this->_messagecount($mailbox); + + // fetch complete message index + $a_message_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, "1:$msg_count", 'UID'); + + foreach ($a_message_index as $id => $uid) + { + // message in cache at correct position + if ($cache_index[$id] == $uid) + { +// console("$id / $uid: OK"); + unset($cache_index[$id]); + continue; + } + + // message in cache but in wrong position + if (in_array((string)$uid, $cache_index, TRUE)) + { +// console("$id / $uid: Moved"); + unset($cache_index[$id]); + } + + // other message at this position + if (isset($cache_index[$id])) + { +// console("$id / $uid: Delete"); + $this->remove_message_cache($cache_key, $id); + unset($cache_index[$id]); + } + + +// console("$id / $uid: Add"); + + // fetch complete headers and add to cache + $headers = iil_C_FetchHeader($this->conn, $mailbox, $id); + $this->add_message_cache($cache_key, $headers->id, $headers); + } + + // those ids that are still in cache_index have been deleted + if (!empty($cache_index)) + { + foreach ($cache_index as $id => $uid) + $this->remove_message_cache($cache_key, $id); + } } @@ -527,22 +514,19 @@ class rcube_imap function get_headers($uid, $mbox=NULL) { $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox; - + // get cached headers - $a_msg_headers = $this->get_cache($mailbox.'.msg'); - - // return cached header - if ($a_msg_headers[$uid]) - return $a_msg_headers[$uid]; + if ($headers = $this->get_cached_message($mailbox.'.msg', $uid)) + return $headers; $msg_id = $this->_uid2id($uid); - $header = iil_C_FetchHeader($this->conn, $mailbox, $msg_id); + $headers = iil_C_FetchHeader($this->conn, $mailbox, $msg_id); // write headers cache - $a_msg_headers[$uid] = $header; - $this->update_cache($mailbox.'.msg', $a_msg_headers); + if ($headers) + $this->add_message_cache($mailbox.'.msg', $msg_id, $headers); - return $header; + return $headers; } @@ -595,19 +579,20 @@ class rcube_imap // reload message headers if cached $cache_key = $this->mailbox.'.msg'; - if ($this->caching_enabled && $result && ($a_cached_headers = $this->get_cache($cache_key))) + if ($this->caching_enabled) { - // close and re-open connection - $this->reconnect(); - - foreach ($uids as $uid) + foreach ($msg_ids as $id) { - if (isset($a_cached_headers[$uid])) + if ($cached_headers = $this->get_cached_message($cache_key, $id)) { - unset($this->cache[$cache_key][$uid]); - $this->get_headers($uid); + $this->remove_message_cache($cache_key, $id); + //$this->get_headers($uid); } } + + // close and re-open connection + // this prevents connection problems with Courier + $this->reconnect(); } // set nr of messages that were flaged @@ -633,7 +618,7 @@ class rcube_imap // make shure mailbox exists if (in_array($mailbox, $this->_list_mailboxes())) $saved = iil_C_Append($this->conn, $mailbox, $message); - + if ($saved) { // increase messagecount of the target mailbox @@ -672,20 +657,24 @@ class rcube_imap // really deleted from the source mailbox if ($moved) { - $this->expunge($from_mbox, FALSE); - $this->clear_cache($to_mbox.'.msg'); + $this->_expunge($from_mbox, FALSE); $this->_clear_messagecount($from_mbox); $this->_clear_messagecount($to_mbox); } // update cached message headers $cache_key = $from_mbox.'.msg'; - if ($moved && ($a_cached_headers = $this->get_cache($cache_key))) + if ($moved && ($a_cache_index = $this->get_message_cache_index($cache_key))) { + $start_index = 100000; foreach ($a_uids as $uid) - unset($a_cached_headers[$uid]); + { + $index = array_search($uid, $a_cache_index); + $start_index = min($index, $start_index); + } - $this->update_cache($cache_key, $a_cached_headers); + // clear cache from the lowest index on + $this->clear_message_cache($cache_key, $start_index); } return $moved; @@ -716,17 +705,23 @@ class rcube_imap // really deleted from the mailbox if ($deleted) { - $this->expunge($mailbox, FALSE); + $this->_expunge($mailbox, FALSE); $this->_clear_messagecount($mailbox); } // remove deleted messages from cache - if ($deleted && ($a_cached_headers = $this->get_cache($mailbox.'.msg'))) + $cache_key = $mailbox.'.msg'; + if ($deleted && ($a_cache_index = $this->get_message_cache_index($cache_key))) { + $start_index = 100000; foreach ($a_uids as $uid) - unset($a_cached_headers[$uid]); + { + $index = array_search($uid, $a_cache_index); + $start_index = min($index, $start_index); + } - $this->update_cache($mailbox.'.msg', $a_cached_headers); + // clear cache from the lowest index on + $this->clear_message_cache($cache_key, $start_index); } return $deleted; @@ -740,7 +735,10 @@ class rcube_imap $msg_count = $this->_messagecount($mailbox, 'ALL'); if ($msg_count>0) + { + $this->clear_message_cache($mailbox.'.msg'); return iil_C_ClearFolder($this->conn, $mailbox); + } else return 0; } @@ -750,12 +748,18 @@ class rcube_imap function expunge($mbox='', $clear_cache=TRUE) { $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox; - + return $this->_expunge($mailbox, $clear_cache); + } + + + // send IMAP expunge command and clear cache + function _expunge($mailbox, $clear_cache=TRUE) + { $result = iil_C_Expunge($this->conn, $mailbox); if ($result>=0 && $clear_cache) { - $this->clear_cache($mailbox.'.msg'); + //$this->clear_message_cache($mailbox.'.msg'); $this->_clear_messagecount($mailbox); } @@ -763,7 +767,6 @@ class rcube_imap } - /* -------------------------------- * folder managment * --------------------------------*/ @@ -824,13 +827,18 @@ class rcube_imap function create_mailbox($name, $subscribe=FALSE) { $result = FALSE; + + // replace backslashes + $name = preg_replace('/[\\\]+/', '-', $name); + $name_enc = UTF7EncodeString($name); + + // reduce mailbox name to 100 chars + $name_enc = substr($name_enc, 0, 100); + $abs_name = $this->_mod_mailbox($name_enc); $a_mailbox_cache = $this->get_cache('mailboxes'); - - //if (strlen($this->root_ns)) - // $abs_name = $this->root_ns.$abs_name; - + if (strlen($abs_name) && (!is_array($a_mailbox_cache) || !in_array($abs_name, $a_mailbox_cache))) $result = iil_C_CreateFolder($this->conn, $abs_name); @@ -875,37 +883,41 @@ class rcube_imap // clear mailboxlist cache if ($deleted) + { + $this->clear_message_cache($mailbox.'.msg'); $this->clear_cache('mailboxes'); + } - return $updated; + return $deleted; } /* -------------------------------- - * internal caching functions + * internal caching methods * --------------------------------*/ function set_caching($set) { - if ($set && function_exists('rcube_read_cache')) + if ($set && is_object($this->db)) $this->caching_enabled = TRUE; else $this->caching_enabled = FALSE; } + function get_cache($key) { // read cache if (!isset($this->cache[$key]) && $this->caching_enabled) { - $cache_data = rcube_read_cache('IMAP.'.$key); + $cache_data = $this->_read_cache_record('IMAP.'.$key); $this->cache[$key] = strlen($cache_data) ? unserialize($cache_data) : FALSE; } - return $this->cache[$key]; + return $this->cache[$key]; } @@ -924,7 +936,7 @@ class rcube_imap foreach ($this->cache as $key => $data) { if ($this->cache_changes[$key]) - rcube_write_cache('IMAP.'.$key, serialize($data)); + $this->_write_cache_record('IMAP.'.$key, serialize($data)); } } } @@ -935,7 +947,7 @@ class rcube_imap if ($key===NULL) { foreach ($this->cache as $key => $data) - rcube_clear_cache('IMAP.'.$key); + $this->_clear_cache_record('IMAP.'.$key); $this->cache = array(); $this->cache_changed = FALSE; @@ -943,7 +955,7 @@ class rcube_imap } else { - rcube_clear_cache('IMAP.'.$key); + $this->_clear_cache_record('IMAP.'.$key); $this->cache_changes[$key] = FALSE; unset($this->cache[$key]); } @@ -951,8 +963,276 @@ class rcube_imap + function _read_cache_record($key) + { + $cache_data = FALSE; + + if ($this->db) + { + // get cached data from DB + $sql_result = $this->db->query( + "SELECT cache_id, data + FROM ".get_table_name('cache')." + WHERE user_id=? + AND cache_key=?", + $_SESSION['user_id'], + $key); + + if ($sql_arr = $this->db->fetch_assoc($sql_result)) + { + $cache_data = $sql_arr['data']; + $this->cache_keys[$key] = $sql_arr['cache_id']; + } + } + + return $cache_data; + } + + + function _write_cache_record($key, $data) + { + if (!$this->db) + return FALSE; + + // check if we already have a cache entry for this key + if (!isset($this->cache_keys[$key])) + { + $sql_result = $this->db->query( + "SELECT cache_id + FROM ".get_table_name('cache')." + WHERE user_id=? + AND cache_key=?", + $_SESSION['user_id'], + $key); + + if ($sql_arr = $this->db->fetch_assoc($sql_result)) + $this->cache_keys[$key] = $sql_arr['cache_id']; + else + $this->cache_keys[$key] = FALSE; + } + + // update existing cache record + if ($this->cache_keys[$key]) + { + $this->db->query( + "UPDATE ".get_table_name('cache')." + SET created=now(), + data=? + WHERE user_id=? + AND cache_key=?", + $data, + $_SESSION['user_id'], + $key); + } + // add new cache record + else + { + $this->db->query( + "INSERT INTO ".get_table_name('cache')." + (created, user_id, cache_key, data) + VALUES (now(), ?, ?, ?)", + $_SESSION['user_id'], + $key, + $data); + } + } + + + function _clear_cache_record($key) + { + $this->db->query( + "DELETE FROM ".get_table_name('cache')." + WHERE user_id=? + AND cache_key=?", + $_SESSION['user_id'], + $key); + } + + + + /* -------------------------------- + * message caching methods + * --------------------------------*/ + + + // checks if the cache is up-to-date + // return: -3 = off, -2 = incomplete, -1 = dirty + function check_cache_status($mailbox, $cache_key) + { + if (!$this->caching_enabled) + return -3; + + $cache_index = $this->get_message_cache_index($cache_key, TRUE); + $msg_count = $this->_messagecount($mailbox); + $cache_count = count($cache_index); + + // console("Cache check: $msg_count !== ".count($cache_index)); + + if ($cache_count==$msg_count) + { + // get highest index + $header = iil_C_FetchHeader($this->conn, $mailbox, "$msg_count"); + $cache_uid = array_pop($cache_index); + + // uids of highes message matches -> cache seems OK + if ($cache_uid == $header->uid) + return 1; + + // cache is dirty + return -1; + } + // if cache count differs less that 10% report as dirty + else if (abs($msg_count - $cache_count) < $msg_count/10) + return -1; + else + return -2; + } + + + + function get_message_cache($key, $from, $to, $sort_field, $sort_order) + { + $cache_key = "$key:$from:$to:$sort_field:$sort_order"; + $db_header_fields = array('idx', 'uid', 'subject', 'from', 'to', 'cc', 'date', 'size'); + + if (!in_array($sort_field, $db_header_fields)) + $sort_field = 'idx'; + + if ($this->caching_enabled && !isset($this->cache[$cache_key])) + { + $this->cache[$cache_key] = array(); + $sql_result = $this->db->limitquery( + "SELECT idx, uid, headers + FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=? + ORDER BY ".$this->db->quoteIdentifier($sort_field)." ". + strtoupper($sort_order), + $from, + $to-$from, + $_SESSION['user_id'], + $key); + + while ($sql_arr = $this->db->fetch_assoc($sql_result)) + { + $uid = $sql_arr['uid']; + $this->cache[$cache_key][$uid] = unserialize($sql_arr['headers']); + } + } + + return $this->cache[$cache_key]; + } + + + function get_cached_message($key, $uid, $body=FALSE) + { + if (!$this->caching_enabled) + return FALSE; + + $internal_key = '__single_msg'; + if ($this->caching_enabled && (!isset($this->cache[$internal_key][$uid]) || $body)) + { + $sql_select = "idx, uid, headers"; + if ($body) + $sql_select .= ", body"; + + $sql_result = $this->db->query( + "SELECT $sql_select + FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=? + AND uid=?", + $_SESSION['user_id'], + $key, + $uid); + + if ($sql_arr = $this->db->fetch_assoc($sql_result)) + { + $headers = unserialize($sql_arr['headers']); + if (is_object($headers) && !empty($sql_arr['body'])) + $headers->body = $sql_arr['body']; + + $this->cache[$internal_key][$uid] = $headers; + } + } + + return $this->cache[$internal_key][$uid]; + } + + + function get_message_cache_index($key, $force=FALSE) + { + static $sa_message_index = array(); + + if (!empty($sa_message_index[$key]) && !$force) + return $sa_message_index[$key]; + + $sa_message_index[$key] = array(); + $sql_result = $this->db->query( + "SELECT idx, uid + FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=? + ORDER BY idx ASC", + $_SESSION['user_id'], + $key); + + while ($sql_arr = $this->db->fetch_assoc($sql_result)) + $sa_message_index[$key][$sql_arr['idx']] = $sql_arr['uid']; + + return $sa_message_index[$key]; + } + + + function add_message_cache($key, $index, $headers) + { + $this->db->query( + "INSERT INTO ".get_table_name('messages')." + (user_id, del, cache_key, idx, uid, subject, ".$this->db->quoteIdentifier('from').", ".$this->db->quoteIdentifier('to').", cc, date, size, headers) + VALUES (?, 0, ?, ?, ?, ?, ?, ?, ?, ".$this->db->fromunixtime($headers->timestamp).", ?, ?)", + $_SESSION['user_id'], + $key, + $index, + $headers->uid, + $this->decode_header($headers->subject, TRUE), + $this->decode_header($headers->from, TRUE), + $this->decode_header($headers->to, TRUE), + $this->decode_header($headers->cc, TRUE), + $headers->size, + serialize($headers)); + } + + + function remove_message_cache($key, $index) + { + $this->db->query( + "DELETE FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=? + AND idx=?", + $_SESSION['user_id'], + $key, + $index); + } + + + function clear_message_cache($key, $start_index=1) + { + $this->db->query( + "DELETE FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=? + AND idx>=?", + $_SESSION['user_id'], + $key, + $start_index); + } + + + + /* -------------------------------- - * encoding/decoding functions + * encoding/decoding methods * --------------------------------*/ @@ -986,9 +1266,15 @@ class rcube_imap } - function decode_header($input) + function decode_header($input, $remove_quotes=FALSE) { - return $this->decode_mime_string($input); + $str = $this->decode_mime_string($input); + if ($str{0}=='"' && $remove_quotes) + { + $str = str_replace('"', '', $str); + } + + return $str; } @@ -1094,6 +1380,7 @@ class rcube_imap } + /* -------------------------------- * private methods * --------------------------------*/ @@ -1149,6 +1436,33 @@ class rcube_imap } + // parse string or array of server capabilities and put them in internal array + function _parse_capability($caps) + { + if (!is_array($caps)) + $cap_arr = explode(' ', $caps); + else + $cap_arr = $caps; + + foreach ($cap_arr as $cap) + { + if ($cap=='CAPABILITY') + continue; + + if (strpos($cap, '=')>0) + { + list($key, $value) = explode('=', $cap); + if (!is_array($this->capabilities[$key])) + $this->capabilities[$key] = array(); + + $this->capabilities[$key][] = $value; + } + else + $this->capabilities[$cap] = TRUE; + } + } + + // subscribe/unsubscribe a list of mailboxes and update local cache function _change_subscription($a_mboxes, $mode) { diff --git a/program/include/rcube_mdb2.inc b/program/include/rcube_mdb2.inc index 35973ad5d..54a40e796 100755 --- a/program/include/rcube_mdb2.inc +++ b/program/include/rcube_mdb2.inc @@ -238,6 +238,22 @@ class rcube_db } + function format_date($timestamp) + { + switch($this->db_provider) + { + case 'mysqli': + case 'mysql': + return "FROM_UNIXTIME($timestamp)"; + break; + case 'sqlite': + return "datetime('$timestamp')"; + break; + default: + return date("Y-m-d H:i:s", $timestamp); + } + } + function _add_result($res, $query) { // sql error occured diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc index 400e345c0..75db7603f 100644 --- a/program/include/rcube_shared.inc +++ b/program/include/rcube_shared.inc @@ -1185,99 +1185,6 @@ function send_future_expire_header() } -// replace specials characters to a specific encoding type -function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) - { - global $OUTPUT_TYPE, $CHARSET; - static $html_encode_arr, $js_rep_table, $rtf_rep_table, $xml_rep_table; - - if (!$enctype) - $enctype = $GLOBALS['OUTPUT_TYPE']; - - // convert nbsps back to normal spaces if not html - if ($enctype!='html') - $str = str_replace(chr(160), ' ', $str); - - - // encode for plaintext - if ($enctype=='text') - return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str); - - // encode for HTML output - if ($enctype=='html') - { - if (!$html_encode_arr) - { - if ($CHARSET=='ISO-8859-1') - { - $html_encode_arr = get_html_translation_table(HTML_ENTITIES); - $html_encode_arr[chr(128)] = '€'; - } - else - $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS); - - unset($html_encode_arr['?']); - unset($html_encode_arr['&']); - } - - $ltpos = strpos($str, '<'); - $encode_arr = $html_encode_arr; - - // don't replace quotes and html tags - if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false) - { - unset($encode_arr['"']); - unset($encode_arr['<']); - unset($encode_arr['>']); - } - else if ($mode=='remove') - $str = strip_tags($str); - - $out = strtr($str, $encode_arr); - - return $newlines ? nl2br($out) : $out; - } - - - if ($enctype=='url') - return rawurlencode($str); - - - // if the replace tables for RTF, XML and JS are not yet defined - if (!$js_rep_table) - { - $js_rep_table = $rtf_rep_table = $xml_rep_table = array(); - - for ($c=160; $c<256; $c++) // can be increased to support more charsets - { - $hex = dechex($c); - $rtf_rep_table[Chr($c)] = "\\'$hex"; - $xml_rep_table[Chr($c)] = "&#$c;"; - - if ($CHARSET=='ISO-8859-1') - $js_rep_table[Chr($c)] = sprintf("\u%s%s", str_repeat('0', 4-strlen($hex)), $hex); - } - - $js_rep_table['"'] = sprintf("\u%s%s", str_repeat('0', 4-strlen(dechex(34))), dechex(34)); - $xml_rep_table['"'] = '"'; - } - - // encode for RTF - if ($enctype=='xml') - return strtr($str, $xml_rep_table); - - // encode for javascript use - if ($enctype=='js') - return preg_replace(array("/\r\n/", '/"/', "/([^\\\])'/"), array('\n', '\"', "$1\'"), strtr($str, $js_rep_table)); - - // encode for RTF - if ($enctype=='rtf') - return preg_replace("/\r\n/", "\par ", strtr($str, $rtf_rep_table)); - - // no encoding given -> return original string - return $str; - } - function decode_specialchars($input, $charset='') { @@ -1462,7 +1369,21 @@ function abbrevate_string($str, $maxlength, $place_holder='...') return $str; } - + + +// delete all files within a folder +function clear_directory($dir_path) + { + $dir = @opendir($dir_path); + if(!$dir) return FALSE; + + while ($file = readdir($dir)) + if (strlen($file)>2) + unlink("$dir_path/$file"); + + closedir($dir); + return TRUE; + } ?> \ No newline at end of file diff --git a/program/include/rcube_sqlite.inc b/program/include/rcube_sqlite.inc new file mode 100644 index 000000000..78c671d38 --- /dev/null +++ b/program/include/rcube_sqlite.inc @@ -0,0 +1,71 @@ + | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +function rcube_sqlite_from_unixtime($timestamp) + { + $timestamp = trim($timestamp); + if (!preg_match("/^[0-9]+$/is", $timestamp)) + $ret = strtotime($timestamp); + else + $ret = $timestamp; + + $ret = date("Y-m-d H:i:s", $ret); + rcube_sqlite_debug("FROM_UNIXTIME ($timestamp) = $ret"); + return $ret; + } + + +function rcube_sqlite_unix_timestamp($timestamp="") + { + $timestamp = trim($timestamp); + if (!$timestamp) + $ret = time(); + else if (!preg_match("/^[0-9]+$/is", $timestamp)) + $ret = strtotime($timestamp); + else + $ret = $timestamp; + + rcube_sqlite_debug("UNIX_TIMESTAMP ($timestamp) = $ret"); + return $ret; + } + + +function rcube_sqlite_now() + { + rcube_sqlite_debug("NOW() = ".date("Y-m-d H:i:s")); + return date("Y-m-d H:i:s"); + } + + +function rcube_sqlite_md5($str) + { + return md5($str); + } + + +function rcube_sqlite_debug($str) + { + //console($str); + } + +?> \ No newline at end of file diff --git a/program/include/session.inc b/program/include/session.inc index 54ed798fc..f10a2b37e 100644 --- a/program/include/session.inc +++ b/program/include/session.inc @@ -45,7 +45,7 @@ function sess_read($key) if ($sql_arr = $DB->fetch_assoc($sql_result)) { - $SESS_CHANGED = $sql_arr['changed']; + $SESS_CHANGED = mktime(); //$sql_arr['changed']; if (strlen($sql_arr['vars'])) return $sql_arr['vars']; @@ -59,7 +59,7 @@ function sess_read($key) function sess_write($key, $vars) { global $DB; - + $sql_result = $DB->query("SELECT 1 FROM ".get_table_name('session')." WHERE sess_id=?", @@ -83,6 +83,8 @@ function sess_write($key, $vars) $key, $vars, $_SERVER['REMOTE_ADDR']); + + } return TRUE; @@ -102,7 +104,9 @@ function sess_destroy($key) $DB->query("DELETE FROM ".get_table_name('session')." WHERE sess_id=?", $key); - + + rcmail_clear_session_temp($key); + return TRUE; } @@ -115,7 +119,7 @@ function sess_gc($maxlifetime) // get all expired sessions $sql_result = $DB->query("SELECT sess_id FROM ".get_table_name('session')." - WHERE ".$DB->unixtimestamp('now()')."-".$DB->unixtimestamp('created')." > ?", + WHERE ".$DB->unixtimestamp('now()')."-".$DB->unixtimestamp('changed')." > ?", $maxlifetime); $a_exp_sessions = array(); @@ -134,6 +138,10 @@ function sess_gc($maxlifetime) WHERE sess_id IN ('".join("','", $a_exp_sessions)."')"); } + // remove session specific temp dirs + foreach ($a_exp_sessions as $key) + rcmail_clear_session_temp($key); + return TRUE; } -- cgit v1.2.3