summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorthomascube <thomas@roundcube.net>2006-01-05 00:37:10 +0000
committerthomascube <thomas@roundcube.net>2006-01-05 00:37:10 +0000
commit15a9d1ce671fcbc44ea3e4858d7aa6f5b22300c9 (patch)
tree30e15c3f52ff435d5b08691424515995d8b077c9
parent977a295eb1e97e0c230063da40b8296fca778814 (diff)
Optimized loading time; added periodic mail check; added EXPUNGE command
-rw-r--r--CHANGELOG14
-rw-r--r--INSTALL1
-rw-r--r--SQL/mysql.initial.sql2
-rw-r--r--SQL/mysql.update.sql4
-rwxr-xr-xSQL/postgres.initial.sql74
-rw-r--r--UPGRADING7
-rw-r--r--config/main.inc.php.dist3
-rw-r--r--index.php14
-rw-r--r--program/include/main.inc106
-rwxr-xr-xprogram/include/rcube_db.inc251
-rw-r--r--program/include/rcube_imap.inc289
-rw-r--r--program/js/app.js148
-rw-r--r--program/js/common.js2
-rw-r--r--program/lib/imap.inc5
-rw-r--r--program/localization/de/labels.inc4
-rw-r--r--program/localization/en/labels.inc4
-rw-r--r--program/steps/mail/check_recent.inc47
-rw-r--r--program/steps/mail/folders.inc61
-rw-r--r--program/steps/mail/func.inc29
-rw-r--r--program/steps/mail/getunread.inc36
-rw-r--r--program/steps/mail/sendmail.inc9
-rw-r--r--program/steps/mail/upload.inc2
-rw-r--r--skins/default/mail.css30
-rw-r--r--skins/default/templates/mail.html5
24 files changed, 887 insertions, 260 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 5f6e38041..facd5b9ed 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,20 @@
CHANGELOG RoundCube Webmail
---------------------------
+2006/01/04
+----------
+- Fixed bug when inserting signatures with !?&
+- Chopping message headers before inserting into the message cache table (to avoid bugs in Postgres)
+- Allow one-char domains in e-mail addresses
+- Make product name in page title configurable
+- Make username available as skin object
+- Added session_write_close() in rcube_db class destructor to avoid problems in PHP 5.0.5
+- Use move_uploaded_file() instead of copy() for a more secure handling of uploaded attachments
+- Additional config parameter to show/hide deleted messages
+- Added periodic request for checking new mails (Request #1307821)
+- Added EXPUNGE command
+- Optimized loading time for mail interface
+
2005/12/16
----------
diff --git a/INSTALL b/INSTALL
index f343c8228..92b8bee0d 100644
--- a/INSTALL
+++ b/INSTALL
@@ -76,6 +76,7 @@ REQUIREMENTS
* The Apache Webserver
* .htaccess support allowing overrides for DirectoryIndex
* PHP Version 4.3.1 or greater
+* PCRE (perl compatible regular expression) installed with PHP
* php.ini options:
- error_reporting E_ALL & ~E_NOTICE (or lower)
- file_uploads on (for attachment upload features)
diff --git a/SQL/mysql.initial.sql b/SQL/mysql.initial.sql
index 21444edda..eabc75e1f 100644
--- a/SQL/mysql.initial.sql
+++ b/SQL/mysql.initial.sql
@@ -91,7 +91,7 @@ CREATE TABLE `users` (
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`last_login` datetime NOT NULL default '0000-00-00 00:00:00',
`language` varchar(5) NOT NULL default 'en',
- `preferences` text NOT NULL,
+ `preferences` text NOT NULL default '',
PRIMARY KEY (`user_id`)
) TYPE=MyISAM;
diff --git a/SQL/mysql.update.sql b/SQL/mysql.update.sql
index 778919acf..e93fc980f 100644
--- a/SQL/mysql.update.sql
+++ b/SQL/mysql.update.sql
@@ -2,8 +2,8 @@
-- Version 0.1-20051007
-ALTER TABLE session ADD ip VARCHAR(15) NOT NULL AFTER changed;
-ALTER TABLE users ADD alias VARCHAR(128) NOT NULL AFTER mail_host;
+ALTER TABLE `session` ADD `ip` VARCHAR(15) NOT NULL AFTER changed;
+ALTER TABLE `users` ADD `alias` VARCHAR(128) NOT NULL AFTER mail_host;
diff --git a/SQL/postgres.initial.sql b/SQL/postgres.initial.sql
index 554614ed2..b251755ad 100755
--- a/SQL/postgres.initial.sql
+++ b/SQL/postgres.initial.sql
@@ -68,7 +68,7 @@ CREATE SEQUENCE message_ids
--
CREATE TABLE users (
- user_id integer DEFAULT nextval('user_ids'::text) NOT NULL,
+ user_id integer DEFAULT nextval('user_ids'::text) PRIMARY KEY,
username character varying(128) DEFAULT ''::character varying NOT NULL,
mail_host character varying(128) DEFAULT ''::character varying NOT NULL,
alias character varying(128) DEFAULT ''::character varying NOT NULL,
@@ -86,7 +86,7 @@ CREATE TABLE users (
--
CREATE TABLE "session" (
- sess_id character varying(40) DEFAULT ''::character varying NOT NULL,
+ sess_id character varying(40) DEFAULT ''::character varying PRIMARY KEY,
created timestamp with time zone DEFAULT now() NOT NULL,
changed timestamp with time zone DEFAULT now() NOT NULL,
ip character varying(16) NOT NULL,
@@ -101,8 +101,8 @@ CREATE TABLE "session" (
--
CREATE TABLE identities (
- identity_id integer DEFAULT nextval('identity_ids'::text) NOT NULL,
- user_id integer DEFAULT 0 NOT NULL,
+ identity_id integer DEFAULT nextval('identity_ids'::text) PRIMARY KEY,
+ user_id integer NOT NULL REFERENCES users (user_id),
del integer DEFAULT 0 NOT NULL,
standard integer DEFAULT 0 NOT NULL,
name character varying(128) NOT NULL,
@@ -120,8 +120,8 @@ CREATE TABLE identities (
--
CREATE TABLE contacts (
- contact_id integer DEFAULT nextval('contact_ids'::text) NOT NULL,
- user_id integer DEFAULT 0 NOT NULL,
+ contact_id integer DEFAULT nextval('contact_ids'::text) PRIMARY KEY,
+ user_id integer NOT NULL REFERENCES users (user_id),
changed timestamp with time zone DEFAULT now() NOT NULL,
del integer DEFAULT 0 NOT NULL,
name character varying(128) DEFAULT ''::character varying NOT NULL,
@@ -139,9 +139,9 @@ CREATE TABLE contacts (
--
CREATE TABLE "cache" (
- cache_id integer DEFAULT nextval('cache_ids'::text) NOT NULL,
- user_id integer DEFAULT 0 NOT NULL,
- session_id character varying(40),
+ cache_id integer DEFAULT nextval('cache_ids'::text) PRIMARY KEY,
+ user_id integer NOT NULL REFERENCES users (user_id),
+ session_id character varying(40) REFERENCES "session" (session_id),
cache_key character varying(128) DEFAULT ''::character varying NOT NULL,
created timestamp with time zone DEFAULT now() NOT NULL,
data text NOT NULL
@@ -155,8 +155,8 @@ CREATE TABLE "cache" (
--
CREATE TABLE "messages" (
- message_id integer DEFAULT nextval('message_ids'::text) NOT NULL,
- user_id integer DEFAULT 0 NOT NULL,
+ message_id integer DEFAULT nextval('message_ids'::text) PRIMARY KEY,
+ user_id integer NOT NULL REFERENCES users (user_id),
del integer DEFAULT 0 NOT NULL,
cache_key character varying(128) DEFAULT ''::character varying NOT NULL,
idx integer DEFAULT 0 NOT NULL,
@@ -171,55 +171,3 @@ CREATE TABLE "messages" (
body text
);
-
-
---
--- Add primary keys
---
-
-ALTER TABLE ONLY "cache"
- ADD CONSTRAINT cache_pkey PRIMARY KEY (cache_id);
-
-
-ALTER TABLE ONLY "contacts"
- ADD CONSTRAINT contacts_pkey PRIMARY KEY (contact_id);
-
-
-ALTER TABLE ONLY identities
- ADD CONSTRAINT identities_pkey PRIMARY KEY (identity_id);
-
-
-ALTER TABLE ONLY "session"
- ADD CONSTRAINT session_pkey PRIMARY KEY (sess_id);
-
-
-ALTER TABLE ONLY "users"
- ADD CONSTRAINT users_pkey PRIMARY KEY (user_id);
-
-
-ALTER TABLE ONLY "messages"
- ADD CONSTRAINT messages_pkey PRIMARY KEY (message_id);
-
-
---
--- Reference keys
---
-
-ALTER TABLE ONLY "cache"
- ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id);
-
-ALTER TABLE ONLY "cache"
- ADD CONSTRAINT "$2" FOREIGN KEY (session_id) REFERENCES "session"(sess_id);
-
-
-ALTER TABLE ONLY "contacts"
- ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id);
-
-
-ALTER TABLE ONLY "identities"
- ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id);
-
-
-ALTER TABLE ONLY "messages"
- ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id);
-
diff --git a/UPGRADING b/UPGRADING
index fbfd47b19..21512c138 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -19,6 +19,7 @@ from versions 0.1-alpha and 0.1-20050811
$rcmail_config['smtp_port'] = 25;
$rcmail_config['default_port'] = 143;
$rcmail_config['session_lifetime'] = 20;
+ $rcmail_config['skip_deleted'] = FALSE;
$rcmail_config['message_sort_col'] = 'date';
$rcmail_config['message_sort_order'] = 'DESC';
$rcmail_config['log_dir'] = 'logs/';
@@ -41,6 +42,7 @@ from version 0.1-20050820
$rcmail_config['smtp_port'] = 25;
$rcmail_config['default_port'] = 143;
$rcmail_config['session_lifetime'] = 20;
+ $rcmail_config['skip_deleted'] = FALSE;
$rcmail_config['message_sort_col'] = 'date';
$rcmail_config['message_sort_order'] = 'DESC';
$rcmail_config['log_dir'] = 'logs/';
@@ -61,6 +63,7 @@ from version 0.1-20051007
- add these lines to /config/main.inc.php
$rcmail_config['smtp_auth_type'] = ''; // if you need to specify an auth method for SMTP
$rcmail_config['session_lifetime'] = 20; // to specify the session lifetime in minutes
+ $rcmail_config['skip_deleted'] = FALSE;
$rcmail_config['message_sort_col'] = 'date';
$rcmail_config['message_sort_order'] = 'DESC';
$rcmail_config['log_dir'] = 'logs/';
@@ -81,6 +84,7 @@ from version 0.1-20051021
- replace all files in folder /skins/default/
- run all commands in SQL/*.update.sql or re-initalize database with *.initial.sql
- add these lines to /config/main.inc.php
+ $rcmail_config['skip_deleted'] = FALSE;
$rcmail_config['message_sort_col'] = 'date';
$rcmail_config['message_sort_order'] = 'DESC';
$rcmail_config['log_dir'] = 'logs/';
@@ -91,4 +95,5 @@ from version 0.1-20051021
$rcmail_config['db_sequence_identity_ids'] = 'identity_ids';
$rcmail_config['db_sequence_contact_ids'] = 'contact_ids';
$rcmail_config['db_sequence_cache_ids'] = 'cache_ids';
- $rcmail_config['db_sequence_message_ids'] = 'message_ids'; \ No newline at end of file
+ $rcmail_config['db_sequence_message_ids'] = 'message_ids';
+
diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist
index dc147763f..0ea3864c5 100644
--- a/config/main.inc.php.dist
+++ b/config/main.inc.php.dist
@@ -97,6 +97,9 @@ $rcmail_config['date_long'] = 'd.m.Y H:i';
// add this user-agent to message headers when sending
$rcmail_config['useragent'] = 'RoundCube Webmail/0.1b';
+// use this name to compose page titles
+$rcmail_config['product_name'] = 'RoundCube Webmail';
+
// only list folders within this path
$rcmail_config['imap_root'] = '';
diff --git a/index.php b/index.php
index ee65f8a7c..f864aca48 100644
--- a/index.php
+++ b/index.php
@@ -3,7 +3,7 @@
/*
+-----------------------------------------------------------------------+
| RoundCube Webmail IMAP Client |
- | Version 0.1-20051214 |
+ | Version 0.1-20060104 |
| |
| Copyright (C) 2005, RoundCube Dev. - Switzerland |
| Licensed under the GNU GPL |
@@ -41,6 +41,9 @@
*/
+define('RCMAIL_VERSION', '0.1-20060104');
+
+
// define global vars
$INSTALL_PATH = dirname($_SERVER['SCRIPT_FILENAME']);
$OUTPUT_TYPE = 'html';
@@ -240,6 +243,15 @@ if ($_task=='mail')
if ($_action=='addcontact')
include('program/steps/mail/addcontact.inc');
+
+ if ($_action=='expunge')
+ include('program/steps/mail/folders.inc');
+
+ if ($_action=='check-recent')
+ include('program/steps/mail/check_recent.inc');
+
+ if ($_action=='getunread')
+ include('program/steps/mail/getunread.inc');
if ($_action=='list' && $_GET['_remote'])
include('program/steps/mail/list.inc');
diff --git a/program/include/main.inc b/program/include/main.inc
index 4cfb5b270..24110d343 100644
--- a/program/include/main.inc
+++ b/program/include/main.inc
@@ -146,6 +146,9 @@ function rcmail_imap_init($connect=FALSE)
global $CONFIG, $DB, $IMAP;
$IMAP = new rcube_imap($DB);
+ $IMAP->debug_level = $CONFIG['debug_level'];
+ $IMAP->skip_deleted = $CONFIG['skip_deleted'];
+
// connect with stored session data
if ($connect)
@@ -591,14 +594,25 @@ function decrypt_passwd($cypher)
// send correct response on a remote request
-function rcube_remote_response($js_code)
+function rcube_remote_response($js_code, $flush=FALSE)
{
- send_nocacheing_headers();
- header('Content-Type: application/x-javascript');
+ static $s_header_sent = FALSE;
+
+ if (!$s_header_sent)
+ {
+ $s_header_sent = TRUE;
+ send_nocacheing_headers();
+ header('Content-Type: application/x-javascript');
+ print '/** remote response ['.date('d/M/Y h:i:s O')."] **/\n";
+ }
- print '/** remote response ['.date('d/M/Y h:i:s O')."] **/\n";
+ // send response code
print $js_code;
- exit;
+
+ if ($flush) // flush the output buffer
+ flush();
+ else // terminate script
+ exit;
}
@@ -879,8 +893,13 @@ function rcube_xml_command($command, $str_attrib, $a_attrib=NULL)
$object = strtolower($attrib['name']);
$object_handlers = array(
+ // GENERAL
+ 'loginform' => 'rcmail_login_form',
+ 'username' => 'rcmail_current_username',
+
// MAIL
'mailboxlist' => 'rcmail_mailbox_list',
+ 'message' => 'rcmail_message_container',
'messages' => 'rcmail_message_list',
'messagecountdisplay' => 'rcmail_messagecount_display',
'messageheaders' => 'rcmail_message_headers',
@@ -916,32 +935,28 @@ function rcube_xml_command($command, $str_attrib, $a_attrib=NULL)
'composebody' => 'rcmail_compose_body'
);
- if ($object=='loginform')
- return rcmail_login_form($attrib);
-
- else if ($object=='message')
- return rcmail_message_container($attrib);
// execute object handler function
- else if ($object_handlers[$object] && function_exists($object_handlers[$object]))
+ if ($object_handlers[$object] && function_exists($object_handlers[$object]))
return call_user_func($object_handlers[$object], $attrib);
else if ($object=='pagetitle')
{
$task = $GLOBALS['_task'];
+ $title = !empty($CONFIG['product_name']) ? $CONFIG['product_name'].' :: ' : '';
+
if ($task=='mail' && isset($GLOBALS['MESSAGE']['subject']))
- return rep_specialchars_output("RoundCube|Mail :: ".$GLOBALS['MESSAGE']['subject']);
+ $title .= $GLOBALS['MESSAGE']['subject'];
else if (isset($GLOBALS['PAGE_TITLE']))
- return rep_specialchars_output("RoundCube|Mail :: ".$GLOBALS['PAGE_TITLE']);
+ $title .= $GLOBALS['PAGE_TITLE'];
else if ($task=='mail' && ($mbox_name = $IMAP->get_mailbox_name()))
- return "RoundCube|Mail :: ".rep_specialchars_output(UTF7DecodeString($mbox_name), 'html', 'all');
+ $title .= UTF7DecodeString($mbox_name);
else
- return "RoundCube|Mail :: $task";
+ $title .= $task;
+
+ return rep_specialchars_output($title, 'html', 'all');
}
- else if ($object=='about')
- return '';
-
break;
}
@@ -1266,6 +1281,38 @@ function rcmail_message_container($attrib)
}
+// return the IMAP username of the current session
+function rcmail_current_username($attrib)
+ {
+ global $DB;
+ static $s_username;
+
+ // alread fetched
+ if (!empty($s_username))
+ return $s_username;
+
+ // get e-mail address form default identity
+ $sql_result = $DB->query("SELECT email AS mailto
+ FROM ".get_table_name('identities')."
+ WHERE user_id=?
+ AND standard=1
+ AND del<>1",
+ $_SESSION['user_id']);
+
+ if ($DB->num_rows($sql_result))
+ {
+ $sql_arr = $DB->fetch_assoc($sql_result);
+ $s_username = $sql_arr['mailto'];
+ }
+ else if (strstr($_SESSION['username'], '@'))
+ $s_username = $_SESSION['username'];
+ else
+ $s_username = $_SESSION['username'].'@'.$_SESSION['imap_host'];
+
+ return $s_username;
+ }
+
+
// return code for the webmail login form
function rcmail_login_form($attrib)
{
@@ -1373,4 +1420,27 @@ function rcmail_charset_selector($attrib)
}
+
+function rcube_timer()
+ {
+ list($usec, $sec) = explode(" ", microtime());
+ return ((float)$usec + (float)$sec);
+ }
+
+
+function rcube_print_time($timer, $label='Timer')
+ {
+ static $print_count = 0;
+
+ $print_count++;
+ $now = rcube_timer();
+ $diff = $now-$timer;
+
+ if (empty($label))
+ $label = 'Timer '.$print_count;
+
+ console(sprintf("%s: %0.4f sec", $label, $diff));
+ }
+
+
?> \ No newline at end of file
diff --git a/program/include/rcube_db.inc b/program/include/rcube_db.inc
index acb13ce37..e54dcc989 100755
--- a/program/include/rcube_db.inc
+++ b/program/include/rcube_db.inc
@@ -20,8 +20,24 @@
*/
+
+/**
+ * Obtain the PEAR::DB class that is used for abstraction
+ */
require_once('DB.php');
+
+/**
+ * Database independent query interface
+ *
+ * This is a wrapper for the PEAR::DB class
+ *
+ * @package RoundCube Webmail
+ * @author David Saez Padros <david@ols.es>
+ * @author Thomas Bruederli <roundcube@gmail.com>
+ * @version 1.14
+ * @link http://pear.php.net/package/DB
+ */
class rcube_db
{
var $db_dsnw; // DSN for write operations
@@ -34,8 +50,13 @@ class rcube_db
var $last_res_id = 0;
- // PHP 5 constructor
- function __construct($db_dsnw,$db_dsnr='')
+ /**
+ * Object constructor
+ *
+ * @param string DSN for read/write operations
+ * @param string Optional DSN for read only operations
+ */
+ function __construct($db_dsnw, $db_dsnr='')
{
if ($db_dsnr=='')
$db_dsnr=$db_dsnw;
@@ -48,25 +69,44 @@ class rcube_db
}
- // PHP 4 compatibility
+ /**
+ * PHP 4 object constructor
+ *
+ * @see rcube_db::__construct
+ */
function rcube_db($db_dsnw,$db_dsnr='')
{
$this->__construct($db_dsnw,$db_dsnr);
}
- // Connect to specific database
+ /**
+ * Object destructor
+ */
+ function __destruct()
+ {
+ // before closing the database connection, write session data
+ session_write_close();
+ }
+
+
+ /**
+ * Connect to specific database
+ *
+ * @param string DSN for DB connections
+ * @return object PEAR database handle
+ * @access private
+ */
function dsn_connect($dsn)
{
// Use persistent connections if available
$dbh = DB::connect($dsn, array('persistent' => TRUE));
if (DB::isError($dbh))
- raise_error(array('code' => 500,
- 'type' => 'db',
- 'line' => __LINE__,
- 'file' => __FILE__,
+ {
+ raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__,
'message' => $dbh->getMessage()), TRUE, FALSE);
+ }
else if ($this->db_provider=='sqlite')
{
@@ -79,8 +119,14 @@ class rcube_db
}
- // Connect to appropiate databse
- function db_connect ($mode)
+ /**
+ * Connect to appropiate databse
+ * depending on the operation
+ *
+ * @param string Connection mode (r|w)
+ * @access public
+ */
+ function db_connect($mode)
{
$this->db_mode = $mode;
@@ -99,7 +145,7 @@ class rcube_db
if ($this->db_mode==$mode)
return;
}
-
+
if ($mode=='r')
$dsn = $this->db_dsnr;
else
@@ -110,7 +156,14 @@ class rcube_db
}
- // Query database
+ /**
+ * Execute a SQL query
+ *
+ * @param string SQL query to execute
+ * @param mixed Values to be inserted in query
+ * @return number Query handle identifier
+ * @access public
+ */
function query()
{
$params = func_get_args();
@@ -120,7 +173,16 @@ class rcube_db
}
- // Query with limits
+ /**
+ * Execute a SQL query with limits
+ *
+ * @param string SQL query to execute
+ * @param number Offset for LIMIT statement
+ * @param number Number of rows for LIMIT statement
+ * @param mixed Values to be inserted in query
+ * @return number Query handle identifier
+ * @access public
+ */
function limitquery()
{
$params = func_get_args();
@@ -132,6 +194,16 @@ class rcube_db
}
+ /**
+ * Execute a SQL query with limits
+ *
+ * @param string SQL query to execute
+ * @param number Offset for LIMIT statement
+ * @param number Number of rows for LIMIT statement
+ * @param array Values to be inserted in query
+ * @return number Query handle identifier
+ * @access private
+ */
function _query($query, $offset, $numrows, $params)
{
// Read or write ?
@@ -155,6 +227,14 @@ class rcube_db
}
+ /**
+ * Get number of rows for a SQL query
+ * If no query handle is specified, the last query will be taken as reference
+ *
+ * @param number Optional query handle identifier
+ * @return mixed Number of rows or FALSE on failure
+ * @access public
+ */
function num_rows($res_id=NULL)
{
if (!$this->db_handle)
@@ -167,7 +247,13 @@ class rcube_db
}
- function affected_rows($res_id=NULL)
+ /**
+ * Get number of affected rows fort he last query
+ *
+ * @return mixed Number of rows or FALSE on failure
+ * @access public
+ */
+ function affected_rows()
{
if (!$this->db_handle)
return FALSE;
@@ -176,6 +262,14 @@ class rcube_db
}
+ /**
+ * Get last inserted record ID
+ * For Postgres databases, a sequence name is required
+ *
+ * @param string Sequence name for increment
+ * @return mixed ID or FALSE on failure
+ * @access public
+ */
function insert_id($sequence = '')
{
if (!$this->db_handle || $this->db_mode=='r')
@@ -185,10 +279,12 @@ class rcube_db
{
case 'pgsql':
// PostgreSQL uses sequences
- $result =& $this->db_handle->getOne("SELECT CURRVAL('$sequence')");
+ $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;
@@ -207,6 +303,14 @@ class rcube_db
}
+ /**
+ * Get an associative array for one row
+ * If no query handle is specified, the last query will be taken as reference
+ *
+ * @param number Optional query handle identifier
+ * @return mixed Array with col values or FALSE on failure
+ * @access public
+ */
function fetch_assoc($res_id=NULL)
{
$result = $this->_get_result($res_id);
@@ -222,30 +326,66 @@ class rcube_db
}
- function quote($input, $type=null)
+ /**
+ * Formats input so it can be safely used in a query
+ *
+ * @param mixed Value to quote
+ * @return string Quoted/converted string for use in query
+ * @access public
+ */
+ function quote($input)
{
+ // create DB handle if not available
if (!$this->db_handle)
$this->db_connect('r');
-
- return $this->db_handle->quote($input);
+
+ // escape pear identifier chars
+ $rep_chars = array('?' => '\?',
+ '!' => '\!',
+ '&' => '\&');
+
+ return $this->db_handle->quoteSmart(strtr($input, $rep_chars));
}
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * @param string Value to quote
+ * @return string Quoted string for use in query
+ * @deprecated Replaced by rcube_db::quote_identifier
+ * @see rcube_db::quote_identifier
+ * @access public
+ */
function quoteIdentifier($str)
{
- if (!$this->db_handle)
- $this->db_connect('r');
-
- return $this->db_handle->quoteIdentifier($str);
+ return $this->quote_identifier($str);
}
+ /**
+ * Quotes a string so it can be safely used as a table or column name
+ *
+ * @param string Value to quote
+ * @return string Quoted string for use in query
+ * @access public
+ */
function quote_identifier($str)
{
- return $this->quoteIdentifier($str);
+ if (!$this->db_handle)
+ $this->db_connect('r');
+
+ return $this->db_handle->quoteIdentifier($str);
}
+ /**
+ * Return SQL statement to convert a field value into a unix timestamp
+ *
+ * @param string Field name
+ * @return string SQL statement to use in query
+ * @access public
+ */
function unixtimestamp($field)
{
switch($this->db_provider)
@@ -260,6 +400,13 @@ class rcube_db
}
+ /**
+ * Return SQL statement to convert from a unix timestamp
+ *
+ * @param string Field name
+ * @return string SQL statement to use in query
+ * @access public
+ */
function fromunixtime($timestamp)
{
switch($this->db_provider)
@@ -275,13 +422,20 @@ class rcube_db
}
+ /**
+ * Adds a query result and returns a handle ID
+ *
+ * @param object Query handle
+ * @return mixed Handle ID or FALE on failure
+ * @access private
+ */
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);
+ 'message' => $res->getMessage() . " Query: " . substr(preg_replace('/[\r\n]+\s*/', ' ', $res->userinfo), 0, 512)), TRUE, FALSE);
return FALSE;
}
else
@@ -294,7 +448,15 @@ class rcube_db
}
- function _get_result($res_id)
+ /**
+ * Resolves a given handle ID and returns the according query handle
+ * If no ID is specified, the last ressource handle will be returned
+ *
+ * @param number Handle ID
+ * @return mixed Ressource handle or FALE on failure
+ * @access private
+ */
+ function _get_result($res_id=NULL)
{
if ($res_id==NULL)
$res_id = $this->last_res_id;
@@ -306,16 +468,22 @@ class rcube_db
}
- // create a sqlite database from a file
- function _sqlite_create_database($dbh, $fileName)
+ /**
+ * Create a sqlite database from a file
+ *
+ * @param object SQLite database handle
+ * @param string File path to use for DB creation
+ * @access private
+ */
+ function _sqlite_create_database($dbh, $file_name)
{
- if (empty($fileName) || !is_string($fileName))
- return ;
+ if (empty($file_name) || !is_string($file_name))
+ return;
$data = '';
- if ($fd = fopen($fileName, 'r'))
+ if ($fd = fopen($file_name, 'r'))
{
- $data = fread($fd, filesize($fileName));
+ $data = fread($fd, filesize($file_name));
fclose($fd);
}
@@ -323,6 +491,13 @@ class rcube_db
sqlite_exec($dbh->connection, $data);
}
+
+ /**
+ * Add some proprietary database functions to the current SQLite handle
+ * in order to make it MySQL compatible
+ *
+ * @access private
+ */
function _sqlite_prepare()
{
include_once('include/rcube_sqlite.inc');
@@ -334,21 +509,7 @@ class rcube_db
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 593225da2..7b71dc09c 100644
--- a/program/include/rcube_imap.inc
+++ b/program/include/rcube_imap.inc
@@ -21,11 +21,24 @@
*/
+/**
+ * Obtain classes from the Iloha IMAP library
+ */
require_once('lib/imap.inc');
require_once('lib/mime.inc');
require_once('lib/utf7.inc');
+/**
+ * Interface class for accessing an IMAP server
+ *
+ * This is a wrapper that implements the Iloha IMAP Library (IIL)
+ *
+ * @package RoundCube Webmail
+ * @author Thomas Bruederli <roundcube@gmail.com>
+ * @version 1.22
+ * @link http://ilohamail.org
+ */
class rcube_imap
{
var $db;
@@ -46,33 +59,53 @@ class rcube_imap
var $uid_id_map = array();
var $msg_headers = array();
var $capabilities = array();
+ var $skip_deleted = FALSE;
+ var $debug_level = 1;
- // PHP 5 constructor
+ /**
+ * Object constructor
+ *
+ * @param object Database connection
+ */
function __construct($db_conn)
{
$this->db = $db_conn;
}
- // PHP 4 compatibility
+
+ /**
+ * PHP 4 object constructor
+ *
+ * @see rcube_imap::__construct
+ */
function rcube_imap($db_conn)
{
$this->__construct($db_conn);
}
+ /**
+ * Connect to an IMAP server
+ *
+ * @param string Host to connect
+ * @param string Username for IMAP account
+ * @param string Password for IMAP account
+ * @param number Port to connect to
+ * @param boolean Use SSL connection
+ * @return boolean TRUE on success, FALSE on failure
+ * @access public
+ */
function connect($host, $user, $pass, $port=143, $use_ssl=FALSE)
{
- global $ICL_SSL, $ICL_PORT, $CONFIG;
+ global $ICL_SSL, $ICL_PORT;
// check for Open-SSL support in PHP build
if ($use_ssl && in_array('openssl', get_loaded_extensions()))
$ICL_SSL = TRUE;
else if ($use_ssl)
{
- raise_error(array('code' => 403,
- 'type' => 'imap',
- 'file' => __FILE__,
+ raise_error(array('code' => 403, 'type' => 'imap', 'file' => __FILE__,
'message' => 'Open SSL not available;'), TRUE, FALSE);
$port = 143;
}
@@ -86,7 +119,7 @@ class rcube_imap
$this->ssl = $use_ssl;
// print trace mesages
- if ($this->conn && ($CONFIG['debug_level'] & 8))
+ if ($this->conn && ($this->debug_level & 8))
console($this->conn->message);
// write error log
@@ -116,6 +149,12 @@ class rcube_imap
}
+ /**
+ * Close IMAP connection
+ * Usually done on script shutdown
+ *
+ * @access public
+ */
function close()
{
if ($this->conn)
@@ -123,6 +162,12 @@ class rcube_imap
}
+ /**
+ * Close IMAP connection and re-connect
+ * This is used to avoid some strange socket errors when talking to Courier IMAP
+ *
+ * @access public
+ */
function reconnect()
{
$this->close();
@@ -130,6 +175,15 @@ class rcube_imap
}
+ /**
+ * Set a root folder for the IMAP connection.
+ *
+ * Only folders within this root folder will be displayed
+ * and all folder paths will be translated using this folder name
+ *
+ * @param string Root folder
+ * @access public
+ */
function set_rootdir($root)
{
if (ereg('[\.\/]$', $root)) //(substr($root, -1, 1)==='/')
@@ -142,6 +196,12 @@ class rcube_imap
}
+ /**
+ * This list of folders will be listed above all other folders
+ *
+ * @param array Indexed list of folder names
+ * @access public
+ */
function set_default_mailboxes($arr)
{
if (is_array($arr))
@@ -159,6 +219,14 @@ class rcube_imap
}
+ /**
+ * Set internal mailbox reference.
+ *
+ * All operations will be perfomed on this mailbox/folder
+ *
+ * @param string Mailbox/Folder name
+ * @access public
+ */
function set_mailbox($mbox)
{
$mailbox = $this->_mod_mailbox($mbox);
@@ -173,24 +241,49 @@ class rcube_imap
}
+ /**
+ * Set internal list page
+ *
+ * @param number Page number to list
+ * @access public
+ */
function set_page($page)
{
$this->list_page = (int)$page;
}
+ /**
+ * Set internal page size
+ *
+ * @param number Number of messages to display on one page
+ * @access public
+ */
function set_pagesize($size)
{
$this->page_size = (int)$size;
}
+ /**
+ * Returns the currently used mailbox name
+ *
+ * @return string Name of the mailbox/folder
+ * @access public
+ */
function get_mailbox_name()
{
return $this->conn ? $this->_mod_mailbox($this->mailbox, 'out') : '';
}
+ /**
+ * Returns the IMAP server's capability
+ *
+ * @param string Capability name
+ * @return mixed Capability value or TRUE if supported, FALSE if not
+ * @access public
+ */
function get_capability($cap)
{
$cap = strtoupper($cap);
@@ -198,6 +291,12 @@ class rcube_imap
}
+ /**
+ * Returns the delimiter that is used by the IMAP server for folder separation
+ *
+ * @return string Delimiter string
+ * @access public
+ */
function get_hierarchy_delimiter()
{
if ($this->conn && empty($this->delimiter))
@@ -209,8 +308,17 @@ class rcube_imap
return $this->delimiter;
}
- // public method for mailbox listing
- // convert mailbox name with root dir first
+
+ /**
+ * Public method for mailbox listing.
+ *
+ * Converts mailbox name with root dir first
+ *
+ * @param string Optional root folder
+ * @param string Optional filter for mailbox listing
+ * @return array List of mailboxes/folders
+ * @access public
+ */
function list_mailboxes($root='', $filter='*')
{
$a_out = array();
@@ -229,7 +337,14 @@ class rcube_imap
return $a_out;
}
- // private method for mailbox listing
+
+ /**
+ * Private method for mailbox listing
+ *
+ * @return array List of mailboxes/folders
+ * @access private
+ * @see rcube_imap::list_mailboxes
+ */
function _list_mailboxes($root='', $filter='*')
{
$a_defaults = $a_out = array();
@@ -261,7 +376,7 @@ class rcube_imap
}
- // get message count for a specific mailbox; acceptes modes are: ALL, UNSEEN
+ // get message count for a specific mailbox; acceptes modes are: ALL, UNSEEN, RECENT
function messagecount($mbox='', $mode='ALL', $force=FALSE)
{
$mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
@@ -274,7 +389,7 @@ class rcube_imap
$a_mailbox_cache = FALSE;
$mode = strtoupper($mode);
- if (!$mailbox)
+ if (empty($mailbox))
$mailbox = $this->mailbox;
$a_mailbox_cache = $this->get_cache('messagecount');
@@ -282,22 +397,37 @@ class rcube_imap
// return cached value
if (!$force && is_array($a_mailbox_cache[$mailbox]) && isset($a_mailbox_cache[$mailbox][$mode]))
return $a_mailbox_cache[$mailbox][$mode];
-
- $search_str = "ALL UNDELETED";
- // get message count and store in cache
- if ($mode == 'UNSEEN')
- $search_str .= " UNSEEN";
+ // RECENT count is fetched abit different
+ if ($mode == 'RECENT')
+ $count = iil_C_CheckForRecent($this->conn, $mailbox);
- // get message count using SEARCH
- // not very performant but more precise (using UNDELETED)
- $count = 0;
- $index = $this->_search_index($mailbox, $search_str);
- if (is_array($index))
+ // use SEARCH for message counting
+ else if ($this->skip_deleted)
{
- $str = implode(",", $index);
- if (!empty($str))
- $count = count($index);
+ $search_str = "ALL UNDELETED";
+
+ // get message count and store in cache
+ if ($mode == 'UNSEEN')
+ $search_str .= " UNSEEN";
+
+ // get message count using SEARCH
+ // not very performant but more precise (using UNDELETED)
+ $count = 0;
+ $index = $this->_search_index($mailbox, $search_str);
+ if (is_array($index))
+ {
+ $str = implode(",", $index);
+ if (!empty($str))
+ $count = count($index);
+ }
+ }
+ else
+ {
+ if ($mode == 'UNSEEN')
+ $count = iil_C_CountUnseen($this->conn, $mailbox);
+ else
+ $count = iil_C_CountMessages($this->conn, $mailbox);
}
if (is_array($a_mailbox_cache[$mailbox]))
@@ -348,7 +478,7 @@ class rcube_imap
else
{
$begin = $start_msg;
- $end = $start_msg + $this->page_size;
+ $end = $start_msg + $this->page_size;
}
if ($begin < 0) $begin = 0;
@@ -372,16 +502,16 @@ class rcube_imap
else
{
// retrieve headers from IMAP
- if ($this->get_capability('sort') && ($msg_index = iil_C_Sort($this->conn, $mailbox, $this->sort_field)))
+ if ($this->get_capability('sort') && ($msg_index = iil_C_Sort($this->conn, $mailbox, $this->sort_field, $this->skip_deleted ? 'UNDELETED' : '')))
{
-//console("$mailbox: ".count($msg_index));
+//console("$mailbox: ".join(',', $msg_index));
$msgs = $msg_index[$begin];
- for ($i=$begin; $i < $end; $i++)
+ for ($i=$begin+1; $i < $end; $i++)
{
- if ($this->sort_order == 'DESC')
- $msgs = $msg_index[$i].','.$msgs;
- else
+ //if ($this->sort_order == 'DESC')
+ // $msgs = $msg_index[$i].','.$msgs;
+ //else
$msgs = $msgs.','.$msg_index[$i];
}
@@ -401,44 +531,16 @@ class rcube_imap
return $this->_list_headers($mailbox, $page, $this->sort_field, $this->sort_order, TRUE);
}
-
- // 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();
- $deleted_count = 0;
-
- if (!empty($a_header_index))
- {
- foreach ($a_header_index as $i => $headers)
- {
- if ($headers->deleted)
- {
- // delete from cache
- if ($cache_index[$headers->id] && $cache_index[$headers->id] == $headers->uid)
- $this->remove_message_cache($cache_key, $headers->id);
-
- $deleted_count++;
- continue;
- }
-
- // add message to cache
- if ($this->caching_enabled && $cache_index[$headers->id] != $headers->uid)
- $this->add_message_cache($cache_key, $headers->id, $headers);
-
- $a_msg_headers[$headers->uid] = $headers;
- }
- }
+ $deleted_count = $this->_fetch_headers($mailbox, $msgs, $a_msg_headers, $cache_key);
// delete cached messages with a higher index than $max
$this->clear_message_cache($cache_key, $max);
-
- // fetch more headers of there were any deleted messages
- // ...
-
+
// kick child process to sync cache
// ...
@@ -458,6 +560,53 @@ class rcube_imap
}
+ /**
+ * Fetches message headers
+ * Used for loop
+ *
+ * @param string Mailbox name
+ * @param string Message indey to fetch
+ * @param array Reference to message headers array
+ * @param array Array with cache index
+ * @return number Number of deleted messages
+ * @access private
+ */
+ function _fetch_headers($mailbox, $msgs, &$a_msg_headers, $cache_key)
+ {
+ // 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);
+ $deleted_count = 0;
+
+ if (!empty($a_header_index))
+ {
+ foreach ($a_header_index as $i => $headers)
+ {
+ if ($headers->deleted && $this->skip_deleted)
+ {
+ // delete from cache
+ if ($cache_index[$headers->id] && $cache_index[$headers->id] == $headers->uid)
+ $this->remove_message_cache($cache_key, $headers->id);
+
+ $deleted_count++;
+ continue;
+ }
+
+ // add message to cache
+ if ($this->caching_enabled && $cache_index[$headers->id] != $headers->uid)
+ $this->add_message_cache($cache_key, $headers->id, $headers);
+
+ $a_msg_headers[$headers->uid] = $headers;
+ }
+ }
+
+ return $deleted_count;
+ }
+
+
+
// return sorted array of message UIDs
function message_index($mbox='', $sort_field=NULL, $sort_order=NULL)
{
@@ -584,7 +733,7 @@ class rcube_imap
}
- function get_headers($uid, $mbox=NULL)
+ function get_headers($id, $mbox=NULL, $is_uid=TRUE)
{
$mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
@@ -592,7 +741,7 @@ class rcube_imap
if ($headers = $this->get_cached_message($mailbox.'.msg', $uid))
return $headers;
- $msg_id = $this->_uid2id($uid);
+ $msg_id = $is_uid ? $this->_uid2id($id) : $id;
$headers = iil_C_FetchHeader($this->conn, $mailbox, $msg_id);
// write headers cache
@@ -802,9 +951,9 @@ class rcube_imap
// clear all messages in a specific mailbox
- function clear_mailbox($mbox)
+ function clear_mailbox($mbox=NULL)
{
- $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox;
+ $mailbox = !empty($mbox) ? $this->_mod_mailbox($mbox) : $this->mailbox;
$msg_count = $this->_messagecount($mailbox, 'ALL');
if ($msg_count>0)
@@ -1270,10 +1419,10 @@ class rcube_imap
$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),
+ substr($this->decode_header($headers->subject, TRUE), 0, 128),
+ substr($this->decode_header($headers->from, TRUE), 0, 128),
+ substr($this->decode_header($headers->to, TRUE), 0, 128),
+ substr($this->decode_header($headers->cc, TRUE), 0, 128),
(int)$headers->size,
serialize($headers));
}
diff --git a/program/js/app.js b/program/js/app.js
index 213f62b21..dc0275c4b 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -6,11 +6,11 @@
| Copyright (C) 2005, RoundCube Dev, - Switzerland |
| Licensed under the GNU GPL |
| |
- | Modified: 2005/12/16 (roundcube) |
- | |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
+
+ $Id$
*/
@@ -137,7 +137,7 @@ function rcube_webmail()
this.enable_command('add-attachment', 'send-attachment', 'send', true);
if (this.env.messagecount)
- this.enable_command('select-all', 'select-none', 'sort', true);
+ this.enable_command('select-all', 'select-none', 'sort', 'expunge', true);
this.set_page_buttons();
@@ -151,6 +151,10 @@ function rcube_webmail()
// show printing dialog
if (this.env.action=='print')
window.print();
+
+ // get unread count for each mailbox
+ if (this.gui_objects.mailboxlist)
+ this.http_request('getunread', '');
break;
@@ -219,9 +223,9 @@ function rcube_webmail()
if (this.pending_message)
this.display_message(this.pending_message[0], this.pending_message[1]);
- // start interval for keep-alive siganl
+ // start interval for keep-alive/recent_check signal
if (this.kepp_alive_interval)
- this.kepp_alive_int = setInterval(this.ref+'.send_keep_alive()', this.kepp_alive_interval);
+ this.kepp_alive_int = setInterval(this.ref+'.'+(this.task=='mail'?'check_for_recent()':'send_keep_alive()'), this.kepp_alive_interval);
};
@@ -436,6 +440,15 @@ function rcube_webmail()
return false;
}
+
+
+ // check input before leaving compose step
+ if (this.task=='mail' && this.env.action=='compose' && (command=='list' || command=='mail' || command=='addressbook' || command=='settings'))
+ {
+ if (this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning')))
+ return false;
+ }
+
// process command
switch (command)
@@ -460,13 +473,7 @@ function rcube_webmail()
// misc list commands
case 'list':
if (this.task=='mail')
- {
- // check input before leaving compose step
- if (this.env.action=='compose' && this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning')))
- break;
-
this.list_mailbox(props);
- }
else if (this.task=='addressbook')
this.list_contacts();
break;
@@ -512,6 +519,16 @@ function rcube_webmail()
this.list_page('prev');
break;
+ case 'expunge':
+ if (this.env.messagecount)
+ this.expunge_mailbox(this.env.mailbox);
+ break;
+
+ case 'clear-mailbox':
+ //if (this.env.messagecount)
+ //this.clear_mailbox(this.env.mailbox);
+ break;
+
// common commands used in multiple tasks
case 'show':
@@ -1168,7 +1185,18 @@ function rcube_webmail()
// send remote request to load message list
this.list_mailbox_remote = function(mbox, page, add_url)
{
- // clear message list
+ // clear message list first
+ this.clear_message_list();
+
+ // send request to server
+ var url = '_mbox='+escape(mbox)+(page ? '&_page='+page : '');
+ this.set_busy(true, 'loading');
+ this.http_request('list', url+add_url, true);
+ };
+
+
+ this.clear_message_list = function()
+ {
var table = this.gui_objects.messagelist;
var tbody = document.createElement('TBODY');
table.insertBefore(tbody, table.tBodies[0]);
@@ -1176,11 +1204,26 @@ function rcube_webmail()
this.message_rows = new Array();
this.list_rows = this.message_rows;
+
+ };
+
+
+ this.expunge_mailbox = function(mbox)
+ {
+ var lock = false;
+ var add_url = '';
+
+ // lock interface if it's the active mailbox
+ if (mbox == this.env.mailbox)
+ {
+ lock = true;
+ this.set_busy(true, 'loading');
+ add_url = '&_reload=1';
+ }
// send request to server
- var url = '_mbox='+escape(mbox)+(page ? '&_page='+page : '');
- this.set_busy(true, 'loading');
- this.http_request('list', url+add_url, true);
+ var url = '_mbox='+escape(mbox);
+ this.http_request('expunge', url+add_url, lock);
};
@@ -2263,7 +2306,7 @@ function rcube_webmail()
// create a table row in the message list
- this.add_message_row = function(uid, cols, flags, attachment)
+ this.add_message_row = function(uid, cols, flags, attachment, attop)
{
if (!this.gui_objects.messagelist || !this.gui_objects.messagelist.tBodies[0])
return false;
@@ -2277,8 +2320,8 @@ function rcube_webmail()
var row = document.createElement('TR');
row.id = 'rcmrow'+uid;
- row.className = 'message '+(even ? 'even' : 'odd')+(flags.unread ? ' unread' : '');
-
+ row.className = 'message '+(even ? 'even' : 'odd')+(flags.unread ? ' unread' : '')+(flags.deleted ? ' deleted' : '');
+
if (this.in_selection(uid))
row.className += ' selected';
@@ -2304,7 +2347,11 @@ function rcube_webmail()
col.innerHTML = attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" border="0" />' : '';
row.appendChild(col);
- tbody.appendChild(row);
+ if (attop && tbody.rows.length)
+ tbody.insertBefore(row, tbody.firstChild);
+ else
+ tbody.appendChild(row);
+
this.init_message_row(row);
};
@@ -2321,35 +2368,44 @@ function rcube_webmail()
// update the mailboxlist
- this.set_unread_count = function(mbox, count)
+ this.set_unread_count = function(mbox, count, set_title)
{
if (!this.gui_objects.mailboxlist)
return false;
- mbox = String(mbox).toLowerCase().replace(this.mbox_expression, '');
-
var item, reg, text_obj;
- for (var n=0; n<this.gui_objects.mailboxlist.childNodes.length; n++)
+ mbox = String(mbox).toLowerCase().replace(this.mbox_expression, '');
+ item = document.getElementById('rcmbx'+mbox);
+
+ if (item && item.className && item.className.indexOf('mailbox '+mbox)>=0)
{
- item = this.gui_objects.mailboxlist.childNodes[n];
+ // set new text
+ text_obj = item.firstChild;
+ reg = /\s+\([0-9]+\)$/i;
- if (item.className && item.className.indexOf('mailbox '+mbox)>=0)
- {
- // set new text
- text_obj = item.firstChild;
- reg = /\s+\([0-9]+\)$/i;
-
- if (count && text_obj.innerHTML.match(reg))
- text_obj.innerHTML = text_obj.innerHTML.replace(reg, ' ('+count+')');
- else if (count)
- text_obj.innerHTML += ' ('+count+')';
- else
- text_obj.innerHTML = text_obj.innerHTML.replace(reg, '');
+ if (count && text_obj.innerHTML.match(reg))
+ text_obj.innerHTML = text_obj.innerHTML.replace(reg, ' ('+count+')');
+ else if (count)
+ text_obj.innerHTML += ' ('+count+')';
+ else
+ text_obj.innerHTML = text_obj.innerHTML.replace(reg, '');
- // set the right classes
- this.set_classname(item, 'unread', count>0 ? true : false);
- break;
- }
+ // set the right classes
+ this.set_classname(item, 'unread', count>0 ? true : false);
+ }
+
+ // set unread count to window title
+ if (set_title && document.title)
+ {
+ var doc_title = String(document.title);
+ reg = /^\([0-9]+\)\s+/i;
+
+ if (count && doc_title.match(reg))
+ document.title = doc_title.replace(reg, '('+count+') ');
+ else if (count)
+ document.title = '('+count+') '+doc_title;
+ else
+ document.title = doc_title.replace(reg, '');
}
};
@@ -2527,9 +2583,10 @@ function rcube_webmail()
if (this.env.action=='show')
this.command('list');
break;
-
+
case 'list':
- this.enable_command('select-all', 'select-none', this.env.messagecount ? true : false);
+ case 'expunge':
+ this.enable_command('select-all', 'select-none', 'expunge', this.env.messagecount ? true : false);
break;
}
@@ -2556,7 +2613,14 @@ function rcube_webmail()
var d = new Date();
this.http_request('keep-alive', '_t='+d.getTime());
};
+
+ // send periodic request to check for recent messages
+ this.check_for_recent = function()
+ {
+ var d = new Date();
+ this.http_request('check-recent', '_t='+d.getTime());
+ };
/********************************************************/
diff --git a/program/js/common.js b/program/js/common.js
index 0c9917ad9..02f698b90 100644
--- a/program/js/common.js
+++ b/program/js/common.js
@@ -269,7 +269,7 @@ function rcube_check_email(input, inline)
{
if (input && window.RegExp)
{
- var reg_str = '([a-z0-9][-a-z0-9\.\+_]*)\@([a-z0-9]([-a-z0-9][\.]?)*[a-z0-9]\.[a-z]{2,9})';
+ var reg_str = '([a-z0-9][-a-z0-9\.\+_]*)\@(([-a-z0-9][\.]?)*[a-z0-9]\.[a-z]{2,9})';
var reg1 = inline ? new RegExp(reg_str, 'i') : new RegExp('^'+reg_str+'$', 'i');
var reg2 = /[\._\-\@]{2}/;
return reg1.test(input) && !reg2.test(input) ? true : false;
diff --git a/program/lib/imap.inc b/program/lib/imap.inc
index a1dbd7b98..daacb03b4 100644
--- a/program/lib/imap.inc
+++ b/program/lib/imap.inc
@@ -607,7 +607,7 @@ function iil_StrToTime($str){
return $time2;
}
-function iil_C_Sort(&$conn, $mailbox, $field){
+function iil_C_Sort(&$conn, $mailbox, $field, $add=''){
/* Do "SELECT" command */
if (!iil_C_Select($conn, $mailbox)) return false;
@@ -618,7 +618,8 @@ function iil_C_Sort(&$conn, $mailbox, $field){
if (!$fields[$field]) return false;
$fp = $conn->fp;
- $command = 's SORT ('.$field.') US-ASCII ALL UNDELETED'."\r\n";
+ $command = 's SORT ('.$field.') US-ASCII ALL '."$add\r\n";
+ //$command = 's SORT ('.$field.') US-ASCII ALL UNDELETED'."\r\n";
$line = $data = '';
if (!fputs($fp, $command)) return false;
diff --git a/program/localization/de/labels.inc b/program/localization/de/labels.inc
index 12ec06e8c..ae0cfa3d2 100644
--- a/program/localization/de/labels.inc
+++ b/program/localization/de/labels.inc
@@ -105,6 +105,9 @@ $labels['all'] = 'Alle';
$labels['none'] = 'Keine';
$labels['unread'] = 'Ungelesene';
+$labels['compact'] = 'Packen';
+
+
// message compose // Nachrichten erstellen
$labels['compose'] = 'Neue Nachricht verfassen';
$labels['sendmessage'] = 'Nachricht jetzt senden';
@@ -167,6 +170,7 @@ $labels['timezone'] = 'Zeitzone';
$labels['pagesize'] = 'Einträge pro Seite';
$labels['signature'] = 'Signatur';
+$labels['folder'] = 'Ordner';
$labels['folders'] = 'Ordner';
$labels['foldername'] = 'Ordnername';
$labels['subscribed'] = 'Abonniert';
diff --git a/program/localization/en/labels.inc b/program/localization/en/labels.inc
index b37b48140..086c3080f 100644
--- a/program/localization/en/labels.inc
+++ b/program/localization/en/labels.inc
@@ -105,6 +105,9 @@ $labels['all'] = 'All';
$labels['none'] = 'None';
$labels['unread'] = 'Unread';
+$labels['compact'] = 'Compact';
+
+
// message compose
$labels['compose'] = 'Compose a message';
$labels['sendmessage'] = 'Send the message now';
@@ -167,6 +170,7 @@ $labels['timezone'] = 'Time zone';
$labels['pagesize'] = 'Rows per page';
$labels['signature'] = 'Signature';
+$labels['folder'] = 'Folder';
$labels['folders'] = 'Folders';
$labels['foldername'] = 'Folder name';
$labels['subscribed'] = 'Subscribed';
diff --git a/program/steps/mail/check_recent.inc b/program/steps/mail/check_recent.inc
new file mode 100644
index 000000000..fbf8871f9
--- /dev/null
+++ b/program/steps/mail/check_recent.inc
@@ -0,0 +1,47 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/check_recent.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Check for recent messages |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id$
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+$mbox = $IMAP->get_mailbox_name();
+
+if ($recent_count = $IMAP->messagecount(NULL, 'RECENT'))
+ {
+ $count = $IMAP->messagecount();
+ $unread_count = $IMAP->messagecount(NULL, 'UNSEEN');
+
+ $commands = sprintf("this.set_unread_count('%s', %d, true);\n", addslashes($mbox), $unread_count);
+ $commands .= sprintf("this.set_env('messagecount', %d);\n", $count);
+ $commands .= sprintf("this.set_rowcount('%s');\n", rcmail_get_messagecount_text());
+
+ // add new message headers to list
+ $a_headers = array();
+ for ($i=$recent_count, $id=$count-$recent_count+1; $i>0; $i--, $id++)
+ $a_headers[] = $IMAP->get_headers($id, NULL, FALSE);
+
+ $commands .= rcmail_js_message_list($a_headers, TRUE);
+ }
+
+if (strtoupper($mbox)!='INBOX' && $IMAP->messagecount('INBOX', 'RECENT'))
+ $commands = sprintf("this.set_unread_count('INBOX', %d);\n", $IMAP->messagecount('INBOX', 'UNSEEN'));
+
+
+rcube_remote_response($commands);
+?> \ No newline at end of file
diff --git a/program/steps/mail/folders.inc b/program/steps/mail/folders.inc
new file mode 100644
index 000000000..e1730ef93
--- /dev/null
+++ b/program/steps/mail/folders.inc
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/folders.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Implement folder operations line EXPUNGE and Clear |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id$
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+$mbox = $IMAP->get_mailbox_name();
+
+
+// send EXPUNGE command
+if ($_action=='expunge')
+ {
+ $success = $IMAP->expunge();
+
+ // reload message list if current mailbox
+ if ($success && $_GET['_reload'])
+ {
+ rcube_remote_response('this.clear_message_list();', TRUE);
+ $_action = 'list';
+ return;
+ }
+ else
+ $commands = "// expunged: $success\n";
+ }
+
+// clear mailbox
+else if ($_action=='purge')
+ {
+ $success = $IMAP->clear_mailbox();
+
+ if ($success && $_GET['_reload'])
+ {
+ $commands = "this.set_env('messagecount', 0);\n";
+ $commands .= "this.set_env('pagecount', 0);\n";
+ $commands .= sprintf("this.set_rowcount('%s');\n", rcmail_get_messagecount_text());
+ $commands .= sprintf("this.set_unread_count('%s', 0);\n", addslashes($mbox));
+ }
+ else
+ $commands = "// purged: $success";
+ }
+
+
+
+rcube_remote_response($commands);
+?> \ No newline at end of file
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index c430467d1..8ebd1c59c 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -69,6 +69,8 @@ function rcmail_mailbox_list($attrib)
static $s_added_script = FALSE;
static $a_mailboxes;
+// $mboxlist_start = rcube_timer();
+
$type = $attrib['type'] ? $attrib['type'] : 'ul';
$add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') :
array('style', 'class', 'id');
@@ -100,7 +102,9 @@ function rcmail_mailbox_list($attrib)
$a_folders = $IMAP->list_mailboxes();
$delimiter = $IMAP->get_hierarchy_delimiter();
$a_mailboxes = array();
-
+
+// rcube_print_time($mboxlist_start, 'list_mailboxes()');
+
foreach ($a_folders as $folder)
rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter);
}
@@ -112,6 +116,8 @@ function rcmail_mailbox_list($attrib)
else
$out .= rcmail_render_folder_tree_html($a_mailboxes, $special_mailboxes, $mbox, $attrib['maxlength']);
+// rcube_print_time($mboxlist_start, 'render_folder_tree()');
+
if ($type=='ul')
$OUTPUT->add_script(sprintf("%s.gui_object('mailboxlist', '%s');", $JS_OBJECT_NAME, $attrib['id']));
@@ -181,7 +187,7 @@ function rcmail_render_folder_tree_html(&$arrFolders, &$special, &$mbox, $maxlen
}
// add unread message count display
- if ($unread_count = $IMAP->messagecount($folder['id'], 'UNSEEN', ($folder['id']==$mbox)))
+ if ($unread_count = $IMAP->messagecount($folder['id'], 'RECENT', ($folder['id']==$mbox)))
$foldername .= sprintf(' (%d)', $unread_count);
// make folder name safe for ids and class names
@@ -397,7 +403,12 @@ function rcmail_message_list($attrib)
if ($attrib['attachmenticon'] && preg_match("/multipart\/m/i", $header->ctype))
$attach_icon = $attrib['attachmenticon'];
- $out .= sprintf('<tr id="rcmrow%d" class="message'.($header->seen ? '' : ' unread').' '.$zebra_class.'">'."\n", $header->uid);
+ $out .= sprintf('<tr id="rcmrow%d" class="message%s%s %s">'."\n",
+ $header->uid,
+ $header->seen ? '' : ' unread',
+ $header->deleted ? ' deleted' : '',
+ $zebra_class);
+
$out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : '');
// format each col
@@ -495,12 +506,16 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE)
$a_msg_flags['unread'] = $header->seen ? 0 : 1;
$a_msg_flags['replied'] = $header->answered ? 1 : 0;
+
+ if ($header->deleted)
+ $a_msg_flags['deleted'] = 1;
- $commands .= sprintf("this.add_message_row(%s, %s, %s, %b);\n",
+ $commands .= sprintf("this.add_message_row(%s, %s, %s, %b, %b);\n",
$header->uid,
array2js($a_msg_cols),
array2js($a_msg_flags),
- preg_match("/multipart\/m/i", $header->ctype));
+ preg_match("/multipart\/m/i", $header->ctype),
+ $insert_top);
}
return $commands;
@@ -1377,11 +1392,11 @@ function rcmail_compose_cleanup()
// remove attachment files from temp dir
if (is_array($_SESSION['compose']['attachments']))
foreach ($_SESSION['compose']['attachments'] as $attachment)
- unlink($attachment['path']);
+ @unlink($attachment['path']);
// kill temp dir
if ($_SESSION['compose']['temp_dir'])
- rmdir($_SESSION['compose']['temp_dir']);
+ @rmdir($_SESSION['compose']['temp_dir']);
unset($_SESSION['compose']);
}
diff --git a/program/steps/mail/getunread.inc b/program/steps/mail/getunread.inc
new file mode 100644
index 000000000..d35dcf9f1
--- /dev/null
+++ b/program/steps/mail/getunread.inc
@@ -0,0 +1,36 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/getunread.inc |
+ | |
+ | This file is part of the RoundCube Webmail client |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland |
+ | Licensed under the GNU GPL |
+ | |
+ | PURPOSE: |
+ | Check all mailboxes for unread messages and update GUI |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+
+ $Id$
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+$a_folders = $IMAP->list_mailboxes();
+
+if (!empty($a_folders))
+ {
+ foreach ($a_folders as $mbox)
+ {
+ $commands = sprintf("this.set_unread_count('%s', %d);\n", $mbox, $IMAP->messagecount($mbox, 'UNSEEN'));
+ rcube_remote_response($commands, TRUE);
+ }
+ }
+
+exit;
+?> \ No newline at end of file
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index 3c9f60378..c29fcf1d3 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -224,14 +224,17 @@ else
{
// unset some headers because they will be added by the mail() function
$headers_php = $headers;
+ $headers_enc = $MAIL_MIME->headers($headers);
unset($headers_php['To'], $headers_php['Subject']);
+ // reset stored headers and overwrite
+ $MAIL_MIME->_headers = array();
$header_str = $MAIL_MIME->txtHeaders($headers_php);
if(ini_get('safe_mode'))
- $sent = mail($mailto, $msg_subject, $msg_body, $header_str);
- else
- $sent = mail($mailto, $msg_subject, $msg_body, $header_str, "-f$from");
+ $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str);
+ else
+ $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str, "-f$from");
}
diff --git a/program/steps/mail/upload.inc b/program/steps/mail/upload.inc
index 308ec796e..4cd929d0d 100644
--- a/program/steps/mail/upload.inc
+++ b/program/steps/mail/upload.inc
@@ -39,7 +39,7 @@ $response = '';
foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath)
{
$tmpfname = tempnam($temp_dir, 'rcmAttmnt');
- if (copy($filepath, $tmpfname))
+ if (move_uploaded_file($filepath, $tmpfname))
{
$_SESSION['compose']['attachments'][] = array('name' => $_FILES['_attachments']['name'][$i],
'mimetype' => $_FILES['_attachments']['type'][$i],
diff --git a/skins/default/mail.css b/skins/default/mail.css
index 9d507fe7b..28fe2ee63 100644
--- a/skins/default/mail.css
+++ b/skins/default/mail.css
@@ -51,7 +51,10 @@
#listcontrols a,
#listcontrols a:active,
-#listcontrols a:visited
+#listcontrols a:visited,
+#mailboxcontrols a,
+#mailboxcontrols a:active,
+#mailboxcontrols a:visited
{
color: #999999;
font-size: 11px;
@@ -60,12 +63,16 @@
#listcontrols a.active,
#listcontrols a.active:active,
-#listcontrols a.active:visited
+#listcontrols a.active:visited,
+#mailboxcontrols a.active,
+#mailboxcontrols a.active:active,
+#mailboxcontrols a.active:visited
{
color: #CC0000;
}
-#listcontrols a.active:hover
+#listcontrols a.active:hover,
+#mailboxcontrols a.active:hover
{
text-decoration: underline;
}
@@ -294,6 +301,17 @@
}
+#mailboxcontrols
+{
+ position: absolute;
+ left: 20px;
+ width: 160px;
+ bottom: 20px;
+ height: 16px;
+ overflow: hidden;
+}
+
+
/** message list styles */
body.messagelist
@@ -430,6 +448,12 @@ body.messagelist
color: #FFFFFF;
}
+#messagelist tr.deleted td,
+#messagelist tr.deleted td a
+{
+ color: #CCCCCC;
+}
+
/** message view styles */
diff --git a/skins/default/templates/mail.html b/skins/default/templates/mail.html
index 0f87c635d..1af25ed88 100644
--- a/skins/default/templates/mail.html
+++ b/skins/default/templates/mail.html
@@ -28,6 +28,11 @@
<div id="mailboxlist-header"><roundcube:label name="mailboxlist" /></div>
<div id="mailboxlist-container"><roundcube:object name="mailboxlist" id="mailboxlist" maxlength="16" /></div>
+<div id="mailboxcontrols">
+<roundcube:label name="folder" />:&nbsp;
+<roundcube:button command="expunge" label="compact" classAct="active" />&nbsp;
+</div>
+
<div id="mailcontframe">
<roundcube:object name="messages"