From 1cded85790206afe084e1baff371c543711b2b18 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Sat, 03 Dec 2005 11:54:12 -0500
Subject: [PATCH] Re-design of caching (new database table added\!); some bugfixes; Postgres support

---
 program/steps/addressbook/list.inc         |    4 
 INSTALL                                    |   20 
 config/db.inc.php.dist                     |   18 
 program/include/main.inc                   |  312 +++-
 program/steps/mail/compose.inc             |  180 +-
 skins/default/mail.css                     |   27 
 program/steps/addressbook/delete.inc       |    6 
 SQL/mysql.update.sql                       |   35 
 program/steps/settings/manage_folders.inc  |   15 
 program/localization/en_GB/labels.inc      |    3 
 index.php                                  |   17 
 program/include/rcube_shared.inc           |  109 -
 program/include/session.inc                |   16 
 skins/default/templates/mail.html          |    4 
 program/steps/settings/delete_identity.inc |    2 
 program/localization/ee/labels.inc         |  182 +++
 skins/default/images/sort_desc.gif         |    0 
 program/localization/de/labels.inc         |    4 
 program/include/cache.inc                  |    5 
 program/steps/mail/func.inc                |  121 +
 program/steps/settings/func.inc            |    4 
 program/steps/addressbook/show.inc         |    2 
 program/include/rcube_imap.inc             |  780 +++++++++----
 program/steps/mail/addcontact.inc          |    8 
 CHANGELOG                                  |  126 +-
 program/steps/addressbook/edit.inc         |    2 
 program/steps/addressbook/save.inc         |   12 
 program/steps/addressbook/func.inc         |    6 
 program/steps/settings/save_identity.inc   |   12 
 UPGRADING                                  |   30 
 config/main.inc.php.dist                   |    2 
 program/include/rcube_mdb2.inc             |   16 
 SQL/sqlite.initial.sql                     |   54 
 program/localization/en/labels.inc         |    4 
 program/localization/de/messages.inc       |    2 
 program/localization/ee/messages.inc       |   80 +
 program/localization/en/messages.inc       |    8 
 program/steps/mail/sendmail.inc            |   15 
 program/steps/settings/edit_identity.inc   |    5 
 skins/default/pngbehavior.htc              |    2 
 program/include/rcube_db.inc               |  469 ++++---
 program/include/rcube_sqlite.inc           |   71 +
 SQL/postgres.initial.sql                   |  354 ++---
 SQL/mysql.initial.sql                      |   38 
 skins/default/templates/compose.html       |    5 
 skins/default/images/sort_asc.gif          |    0 
 program/js/app.js                          |   99 +
 program/localization/index.inc             |    1 
 48 files changed, 2,167 insertions(+), 1,120 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index ae7631f..afadced 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,68 +1,9 @@
 CHANGELOG RoundCube Webmail
 ---------------------------
 
-2005/08/11
+2005/12/03
 ----------
-- Write list header to client even if list is empty
-- Add functions "select all", "select none" to message list
-- Improved filter for HTML messages to remove potentially malicious tags (script, iframe, object) and event handlers.
-- Buttons for next/previous message in view mode
-- Add new created contact to list and show confirmation status
-- Added folder management (subscribe/create/delete)
-- Log message sending (SMTP log)
-- Grant access for Camino browser
-- Added German translation
-
-
-2005/08/20
-----------
-- Improved cacheing of mailbox messagecount
-- Fixed javascript bug when creating a new message folder
-- Fixed javascript bugs #1260990 and #1260992: folder selection
-- Make Trash folder configurable
-- Auto create folders Inbox, Sent and Trash (if configured)
-- Support for IMAP root folder
-- Added support fot text/enriched messages
-- Make list of special mailboxes configurable
-
-
-2005/10/07
-----------
-- Added French, Italian, Spanish, Danish, Dutch translation
-- Clarified license (Bug #1305966)
-- Fixed PHP warnings (Bug #1299403)
-- Fixed english translation (Bug #1295406)
-- Fixed bug #1290833: Last character of email not seen
-- Fixed bug #1292199 when creating new user
-- Allow more browsers (Bug #1285101)
-- Added setting for showing pretty dates
-- Added support for SQLite database
-- Make use of message caching configurable
-- Also add attachments when forwarding a message
-- Long folder names will not flow over message list (Bug #1267232)
-- Show nested mailboxes hieracically
-- Enable IMAPS by host
-
-
-2005/10/20
-----------
-- Added Swedish, Latvian, Portuguese and Catalan translation
-- Make SMTP auth method configurable
-- Make mailboxlist scrollable (Bug #1326372)
-- Fixed SSL support
-- Improved support for Courier IMAP (root folder and delimiter issues)
-- Moved taskbar from bottom to top
-- Added 'session_lifetime' parameter
-- Fixed wrong unread count when deleting message (Bug #1332434)
-- Srip tags when creating a new folder (Bug #1332084)
-- Translate HTML tags in message headers (Bug #1330134)
-- Correction in German translation (Bug #1329434)
-- Display folder names with special chars correctly (Bug #1330157)
-
-
-2005/11/18
-----------
-- Added Finnish, Romanian, Polish, Czech, British, Norwegian, Greek, Russian and Chinese translation
+- Added Finnish, Romanian, Polish, Czech, British, Norwegian, Greek, Russian, Estonian and Chinese translation
 - Get IMAP server capabilities in array
 - Check for NAMESPACE capability before sending command
 - Set default user language from config 'locale_string'
@@ -85,5 +26,68 @@
 - Decode attachment file names
 - Make delimiter for message headers configurable
 - Add generic footer to sent messages
+- Choose the rigt identity when replying
+- Remove signature when replying (Request #1333167)
+- Signatures for each identity
+- Select charset when composing message
+- Complete re-design of the caching mechanism
 
 
+2005/08/11
+----------
+- Write list header to client even if list is empty
+- Add functions "select all", "select none" to message list
+- Improved filter for HTML messages to remove potentially malicious tags (script, iframe, object) and event handlers.
+- Buttons for next/previous message in view mode
+- Add new created contact to list and show confirmation status
+- Added folder management (subscribe/create/delete)
+- Log message sending (SMTP log)
+- Grant access for Camino browser
+- Added German translation
+
+
+2005/10/20
+----------
+- Added Swedish, Latvian, Portuguese and Catalan translation
+- Make SMTP auth method configurable
+- Make mailboxlist scrollable (Bug #1326372)
+- Fixed SSL support
+- Improved support for Courier IMAP (root folder and delimiter issues)
+- Moved taskbar from bottom to top
+- Added 'session_lifetime' parameter
+- Fixed wrong unread count when deleting message (Bug #1332434)
+- Srip tags when creating a new folder (Bug #1332084)
+- Translate HTML tags in message headers (Bug #1330134)
+- Correction in German translation (Bug #1329434)
+- Display folder names with special chars correctly (Bug #1330157)
+
+
+2005/10/07
+----------
+- Added French, Italian, Spanish, Danish, Dutch translation
+- Clarified license (Bug #1305966)
+- Fixed PHP warnings (Bug #1299403)
+- Fixed english translation (Bug #1295406)
+- Fixed bug #1290833: Last character of email not seen
+- Fixed bug #1292199 when creating new user
+- Allow more browsers (Bug #1285101)
+- Added setting for showing pretty dates
+- Added support for SQLite database
+- Make use of message caching configurable
+- Also add attachments when forwarding a message
+- Long folder names will not flow over message list (Bug #1267232)
+- Show nested mailboxes hieracically
+- Enable IMAPS by host
+
+
+2005/08/20
+----------
+- Improved cacheing of mailbox messagecount
+- Fixed javascript bug when creating a new message folder
+- Fixed javascript bugs #1260990 and #1260992: folder selection
+- Make Trash folder configurable
+- Auto create folders Inbox, Sent and Trash (if configured)
+- Support for IMAP root folder
+- Added support fot text/enriched messages
+- Make list of special mailboxes configurable
+
diff --git a/INSTALL b/INSTALL
index cca4fa1..f343c82 100644
--- a/INSTALL
+++ b/INSTALL
@@ -31,6 +31,7 @@
 > quit
 # mysql roundcubemail < SQL/mysql.initial.sql
 
+
 * SQLite
 --------
 Sqlite requires specifically php5 (sqlite in php4 currently doesn't
@@ -44,6 +45,25 @@
 webserver can write to the file.
 
 
+* PostgreSQL
+------------
+To use RoundCube with PostgreSQL support you have to follow the next
+simple steps, which have to be done with the postgres system user (or
+which ever is the database superuser):
+
+$ createuser roundcubemail
+$ createdb -O roundcubemail roundcubemail
+$ psql roundcubemail
+
+roundcubemail =# ALTER USER roundcube WITH PASSWORD 'the_new_password';
+roundcubemail =# \c - roundcubemail
+roundcubemail => \i SQL/postgres.initial.sql
+
+All this has been tested with PostgreSQL 8.0.x and 7.4.x. Older
+versions don't have a -O option for the createdb, so if you are
+using that version you'll have to change ownership of the DB later.
+
+
 UPGRADING
 =========
 If you already have a previous version of RoundCube installed,
diff --git a/SQL/mysql.initial.sql b/SQL/mysql.initial.sql
index 09d9b80..21444ed 100644
--- a/SQL/mysql.initial.sql
+++ b/SQL/mysql.initial.sql
@@ -11,7 +11,7 @@
 CREATE TABLE `cache` (
   `cache_id` int(10) unsigned NOT NULL auto_increment,
   `user_id` int(10) unsigned NOT NULL default '0',
-  `session_id` varchar(32) default NULL,
+  `session_id` varchar(40) default NULL,
   `cache_key` varchar(128) NOT NULL default '',
   `created` datetime NOT NULL default '0000-00-00 00:00:00',
   `data` longtext NOT NULL,
@@ -31,7 +31,7 @@
   `contact_id` int(10) unsigned NOT NULL auto_increment,
   `user_id` int(10) unsigned NOT NULL default '0',
   `changed` datetime NOT NULL default '0000-00-00 00:00:00',
-  `del` enum('0','1') NOT NULL default '0',
+  `del` tinyint(1) NOT NULL default '0',
   `name` varchar(128) NOT NULL default '',
   `email` varchar(128) NOT NULL default '',
   `firstname` varchar(128) NOT NULL default '',
@@ -50,8 +50,8 @@
 CREATE TABLE `identities` (
   `identity_id` int(10) unsigned NOT NULL auto_increment,
   `user_id` int(10) unsigned NOT NULL default '0',
-  `del` enum('0','1') NOT NULL default '0',
-  `default` enum('0','1') NOT NULL default '0',
+  `del` tinyint(1) NOT NULL default '0',
+  `standard` tinyint(1) NOT NULL default '0',
   `name` varchar(128) NOT NULL default '',
   `organization` varchar(128) NOT NULL default '',
   `email` varchar(128) NOT NULL default '',
@@ -94,3 +94,33 @@
   `preferences` text NOT NULL,
   PRIMARY KEY  (`user_id`)
 ) TYPE=MyISAM;
+
+-- --------------------------------------------------------
+
+-- 
+-- Table structure for table `messages`
+-- 
+
+CREATE TABLE `messages` (
+  `message_id` int(11) unsigned NOT NULL auto_increment,
+  `user_id` int(11) unsigned NOT NULL default '0',
+  `del` tinyint(1) NOT NULL default '0',
+  `cache_key` varchar(128) NOT NULL default '',
+  `idx` int(11) unsigned NOT NULL default '0',
+  `uid` int(11) unsigned NOT NULL default '0',
+  `subject` varchar(255) NOT NULL default '',
+  `from` varchar(255) NOT NULL default '',
+  `to` varchar(255) NOT NULL default '',
+  `cc` varchar(255) NOT NULL default '',
+  `date` datetime NOT NULL default '0000-00-00 00:00:00',
+  `size` int(11) unsigned NOT NULL default '0',
+  `headers` text NOT NULL,
+  `body` longtext,
+  PRIMARY KEY  (`message_id`),
+  KEY `user_id` (`user_id`),
+  KEY `cache_key` (`cache_key`),
+  KEY `idx` (`idx`),
+  KEY `uid` (`uid`)
+) TYPE=MyISAM;
+
+
diff --git a/SQL/mysql.update.sql b/SQL/mysql.update.sql
index 0119225..778919a 100644
--- a/SQL/mysql.update.sql
+++ b/SQL/mysql.update.sql
@@ -11,4 +11,39 @@
 -- Version 0.1-20051021
 
 ALTER TABLE `session` CHANGE `sess_id` `sess_id` VARCHAR(40) NOT NULL;
+
+ALTER TABLE `contacts` CHANGE `del` `del` TINYINT(1) NOT NULL;
 ALTER TABLE `contacts` ADD `changed` DATETIME NOT NULL AFTER `user_id`;
+
+UPDATE `contacts`  SET `del`=0 WHERE `del`=1;
+UPDATE `contacts`  SET `del`=1 WHERE `del`=2;
+
+ALTER TABLE `identities` CHANGE `default` `standard` TINYINT(1) NOT NULL;
+ALTER TABLE `identities` CHANGE `del` `del` TINYINT(1) NOT NULL;
+
+UPDATE `identities`  SET `del`=0 WHERE `del`=1;
+UPDATE `identities`  SET `del`=1 WHERE `del`=2;
+UPDATE `identities`  SET `standard`=0 WHERE `standard`=1;
+UPDATE `identities`  SET `standard`=1 WHERE `standard`=2;
+
+CREATE TABLE `messages` (
+  `message_id` int(11) unsigned NOT NULL auto_increment,
+  `user_id` int(11) unsigned NOT NULL default '0',
+  `del` tinyint(1) NOT NULL default '0',
+  `cache_key` varchar(128) NOT NULL default '',
+  `idx` int(11) unsigned NOT NULL default '0',
+  `uid` int(11) unsigned NOT NULL default '0',
+  `subject` varchar(255) NOT NULL default '',
+  `from` varchar(255) NOT NULL default '',
+  `to` varchar(255) NOT NULL default '',
+  `cc` varchar(255) NOT NULL default '',
+  `date` datetime NOT NULL default '0000-00-00 00:00:00',
+  `size` int(11) unsigned NOT NULL default '0',
+  `headers` text NOT NULL,
+  `body` longtext,
+  PRIMARY KEY  (`message_id`),
+  KEY `user_id` (`user_id`),
+  KEY `cache_key` (`cache_key`),
+  KEY `idx` (`idx`),
+  KEY `uid` (`uid`)
+) TYPE=MyISAM;
diff --git a/SQL/postgres.initial.sql b/SQL/postgres.initial.sql
index 29c134d..4e74a22 100755
--- a/SQL/postgres.initial.sql
+++ b/SQL/postgres.initial.sql
@@ -1,146 +1,5 @@
 --
--- PostgreSQL database dump
---
-
-SET client_encoding = 'UNICODE';
-SET check_function_bodies = false;
-SET search_path = public, pg_catalog;
-
-ALTER TABLE ONLY public.identities DROP CONSTRAINT "$1";
-ALTER TABLE ONLY public.contacts DROP CONSTRAINT "$1";
-ALTER TABLE ONLY public."cache" DROP CONSTRAINT "$2";
-ALTER TABLE ONLY public."cache" DROP CONSTRAINT "$1";
-ALTER TABLE ONLY public.users DROP CONSTRAINT users_pkey;
-ALTER TABLE ONLY public."session" DROP CONSTRAINT session_pkey;
-ALTER TABLE ONLY public.identities DROP CONSTRAINT identities_pkey;
-ALTER TABLE ONLY public.contacts DROP CONSTRAINT contacts_pkey;
-ALTER TABLE ONLY public."cache" DROP CONSTRAINT cache_pkey;
-DROP TABLE public.users;
-DROP TABLE public."session";
-DROP TABLE public.identities;
-DROP TABLE public.contacts;
-DROP TABLE public."cache";
-DROP SEQUENCE public.user_ids;
-DROP SEQUENCE public.identity_ids;
-DROP SEQUENCE public.contact_ids;
-DROP SEQUENCE public.cache_ids;
---
--- TOC entry 4 (OID 15282470)
--- Name: cache_ids; Type: SEQUENCE; Schema: public; Owner: postgres
---
-
-CREATE SEQUENCE cache_ids
-    INCREMENT BY 1
-    NO MAXVALUE
-    NO MINVALUE
-    CACHE 1;
-
-
---
--- TOC entry 5 (OID 15282472)
--- Name: contact_ids; Type: SEQUENCE; Schema: public; Owner: postgres
---
-
-CREATE SEQUENCE contact_ids
-    START WITH 1
-    INCREMENT BY 1
-    NO MAXVALUE
-    NO MINVALUE
-    CACHE 1;
-
-
---
--- TOC entry 6 (OID 15282474)
--- Name: identity_ids; Type: SEQUENCE; Schema: public; Owner: postgres
---
-
-CREATE SEQUENCE identity_ids
-    START WITH 1
-    INCREMENT BY 1
-    NO MAXVALUE
-    NO MINVALUE
-    CACHE 1;
-
-
---
--- TOC entry 7 (OID 15282476)
--- Name: user_ids; Type: SEQUENCE; Schema: public; Owner: postgres
---
-
-CREATE SEQUENCE user_ids
-    INCREMENT BY 1
-    NO MAXVALUE
-    NO MINVALUE
-    CACHE 1;
-
-
---
--- TOC entry 8 (OID 15282478)
--- Name: cache; Type: TABLE; Schema: public; Owner: postgres
---
-
-CREATE TABLE "cache" (
-    cache_id integer DEFAULT nextval('cache_ids'::text) NOT NULL,
-    user_id integer DEFAULT 0 NOT NULL,
-    session_id character varying(32),
-    cache_key character varying(128) DEFAULT ''::character varying NOT NULL,
-    created timestamp with time zone DEFAULT now() NOT NULL,
-    data text NOT NULL
-);
-
-
---
--- TOC entry 10 (OID 15282486)
--- Name: contacts; Type: TABLE; Schema: public; Owner: postgres
---
-
-CREATE TABLE contacts (
-    contact_id integer DEFAULT nextval('contact_ids'::text) NOT NULL,
-    user_id integer DEFAULT 0 NOT NULL,
-    del boolean DEFAULT false NOT NULL,
-    name character varying(128) DEFAULT ''::character varying NOT NULL,
-    email character varying(128) DEFAULT ''::character varying NOT NULL,
-    firstname character varying(128) DEFAULT ''::character varying NOT NULL,
-    surname character varying(128) DEFAULT ''::character varying NOT NULL,
-    vcard text NOT NULL
-);
-
-
---
--- TOC entry 11 (OID 15282494)
--- Name: identities; Type: TABLE; Schema: public; Owner: postgres
---
-
-CREATE TABLE identities (
-    identity_id integer DEFAULT nextval('identity_ids'::text) NOT NULL,
-    user_id integer DEFAULT 0 NOT NULL,
-    del boolean DEFAULT false NOT NULL,
-    "default" boolean DEFAULT false NOT NULL,
-    name character varying(128) NOT NULL,
-    organization character varying(128),
-    email character varying(128) NOT NULL,
-    "reply-to" character varying(128),
-    bcc character varying(128),
-    signature text
-);
-
-
---
--- TOC entry 12 (OID 15282503)
--- Name: session; Type: TABLE; Schema: public; Owner: postgres
---
-
-CREATE TABLE "session" (
-    sess_id character varying(32) DEFAULT ''::character varying NOT NULL,
-    created timestamp with time zone DEFAULT now() NOT NULL,
-    changed timestamp with time zone DEFAULT now() NOT NULL,
-    ip character varying(16) NOT NULL,
-    vars text NOT NULL
-);
-
-
---
--- TOC entry 13 (OID 15282510)
+-- Table "users"
 -- Name: users; Type: TABLE; Schema: public; Owner: postgres
 --
 
@@ -156,100 +15,209 @@
 );
 
 
+
 --
--- TOC entry 14 (OID 15282518)
--- Name: cache_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
+-- Table "session"
+-- Name: session; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE "session" (
+    sess_id character varying(40) DEFAULT ''::character varying NOT NULL,
+    created timestamp with time zone DEFAULT now() NOT NULL,
+    changed timestamp with time zone DEFAULT now() NOT NULL,
+    ip character varying(16) NOT NULL,
+    vars text NOT NULL
+);
+
+
+
+--
+-- Table "identities"
+-- Name: identities; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE identities (
+    identity_id integer DEFAULT nextval('identity_ids'::text) NOT NULL,
+    user_id integer DEFAULT 0 NOT NULL,
+    del integer DEFAULT 0 NOT NULL,
+    standard integer DEFAULT 0 NOT NULL,
+    name character varying(128) NOT NULL,
+    organization character varying(128),
+    email character varying(128) NOT NULL,
+    "reply-to" character varying(128),
+    bcc character varying(128),
+    signature text
+);
+
+
+--
+-- Table "contacts"
+-- Name: contacts; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE contacts (
+    contact_id integer DEFAULT nextval('contact_ids'::text) NOT NULL,
+    user_id integer DEFAULT 0 NOT NULL,
+    changed timestamp with time zone DEFAULT now() NOT NULL,
+    del integer DEFAULT 0 NOT NULL,
+    name character varying(128) DEFAULT ''::character varying NOT NULL,
+    email character varying(128) DEFAULT ''::character varying NOT NULL,
+    firstname character varying(128) DEFAULT ''::character varying NOT NULL,
+    surname character varying(128) DEFAULT ''::character varying NOT NULL,
+    vcard text
+);
+
+
+
+--
+-- Table "cache"
+-- Name: cache; Type: TABLE; Schema: public; Owner: postgres
+--
+
+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_key character varying(128) DEFAULT ''::character varying NOT NULL,
+    created timestamp with time zone DEFAULT now() NOT NULL,
+    data text NOT NULL
+);
+
+
+
+--
+-- Table "messages"
+-- Name: messages; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE "messages" (
+    message_id integer DEFAULT nextval('message_ids'::text) NOT NULL,
+    user_id integer DEFAULT 0 NOT NULL,
+    del integer DEFAULT 0 NOT NULL,
+    cache_key character varying(128) DEFAULT ''::character varying NOT NULL,
+    idx integer DEFAULT 0 NOT NULL,
+    uid integer DEFAULT 0 NOT NULL,
+    subject character varying(128) DEFAULT ''::character varying NOT NULL,
+    "from" character varying(128) DEFAULT ''::character varying NOT NULL,
+    "to" character varying(128) DEFAULT ''::character varying NOT NULL,
+    cc character varying(128) DEFAULT ''::character varying NOT NULL,
+    date timestamp with time zone NOT NULL,
+    size integer DEFAULT 0 NOT NULL,
+    headers text NOT NULL,
+    body text
+);
+
+
+
+--
+-- Add primary keys
 --
 
 ALTER TABLE ONLY "cache"
     ADD CONSTRAINT cache_pkey PRIMARY KEY (cache_id);
 
 
---
--- TOC entry 15 (OID 15282520)
--- Name: contacts_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
---
-
-ALTER TABLE ONLY contacts
+ALTER TABLE ONLY "contacts"
     ADD CONSTRAINT contacts_pkey PRIMARY KEY (contact_id);
 
-
---
--- TOC entry 16 (OID 15282522)
--- Name: identities_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
---
 
 ALTER TABLE ONLY identities
     ADD CONSTRAINT identities_pkey PRIMARY KEY (identity_id);
 
 
---
--- TOC entry 17 (OID 15282524)
--- Name: session_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
---
-
 ALTER TABLE ONLY "session"
     ADD CONSTRAINT session_pkey PRIMARY KEY (sess_id);
 
 
---
--- TOC entry 18 (OID 15282526)
--- Name: users_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
---
-
-ALTER TABLE ONLY users
+ALTER TABLE ONLY "users"
     ADD CONSTRAINT users_pkey PRIMARY KEY (user_id);
 
 
+ALTER TABLE ONLY "messages"
+    ADD CONSTRAINT messages_pkey PRIMARY KEY (message_id);
+
+
 --
--- TOC entry 19 (OID 15282528)
--- Name: $1; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+-- Reference keys
 --
 
 ALTER TABLE ONLY "cache"
     ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id);
-
-
---
--- TOC entry 20 (OID 15282532)
--- Name: $2; Type: FK CONSTRAINT; Schema: public; Owner: postgres
---
 
 ALTER TABLE ONLY "cache"
     ADD CONSTRAINT "$2" FOREIGN KEY (session_id) REFERENCES "session"(sess_id);
 
 
---
--- TOC entry 21 (OID 15282536)
--- Name: $1; Type: FK CONSTRAINT; Schema: public; Owner: postgres
---
+ALTER TABLE ONLY "contacts"
+    ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id);
 
-ALTER TABLE ONLY contacts
+
+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);
 
 
 --
--- TOC entry 22 (OID 15282540)
--- Name: $1; Type: FK CONSTRAINT; Schema: public; Owner: postgres
+-- Sequence "cache_ids"
+-- Name: cache_ids; Type: SEQUENCE; Schema: public; Owner: postgres
 --
 
-ALTER TABLE ONLY identities
-    ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id);
+CREATE SEQUENCE cache_ids
+    INCREMENT BY 1
+    NO MAXVALUE
+    NO MINVALUE
+    CACHE 1;
 
-
-SET SESSION AUTHORIZATION 'postgres';
 
 --
--- TOC entry 3 (OID 15282469)
--- Name: SCHEMA public; Type: COMMENT; Schema: -; Owner: postgres
+-- Sequence "contact_ids"
+-- Name: contact_ids; Type: SEQUENCE; Schema: public; Owner: postgres
 --
 
-COMMENT ON SCHEMA public IS 'Standard public schema';
+CREATE SEQUENCE contact_ids
+    START WITH 1
+    INCREMENT BY 1
+    NO MAXVALUE
+    NO MINVALUE
+    CACHE 1;
 
-
-SET SESSION AUTHORIZATION 'postgres';
 
 --
--- TOC entry 9 (OID 15282478)
--- Name: TABLE "cache"; Type: COMMENT; Schema: public; Owner: postgres
---
\ No newline at end of file
+-- Sequence "identity_ids"
+-- Name: identity_ids; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE identity_ids
+    START WITH 1
+    INCREMENT BY 1
+    NO MAXVALUE
+    NO MINVALUE
+    CACHE 1;
+
+
+--
+-- Sequence "user_ids"
+-- Name: user_ids; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE user_ids
+    INCREMENT BY 1
+    NO MAXVALUE
+    NO MINVALUE
+    CACHE 1;
+
+
+--
+-- Sequence "message_ids"
+-- Name: message_ids; Type: SEQUENCE; Schema: public; Owner: postgres
+--
+
+CREATE SEQUENCE message_ids
+    INCREMENT BY 1
+    NO MAXVALUE
+    NO MINVALUE
+    CACHE 1;
+
diff --git a/SQL/sqlite.initial.sql b/SQL/sqlite.initial.sql
index 01f51d6..19ca6a5 100644
--- a/SQL/sqlite.initial.sql
+++ b/SQL/sqlite.initial.sql
@@ -11,7 +11,7 @@
 CREATE TABLE cache (
   cache_id integer NOT NULL PRIMARY KEY,
   user_id integer NOT NULL default 0,
-  session_id varchar(32) default NULL,
+  session_id varchar(40) default NULL,
   cache_key varchar(128) NOT NULL default '',
   created datetime NOT NULL default '0000-00-00 00:00:00',
   data longtext NOT NULL
@@ -20,6 +20,7 @@
 CREATE INDEX ix_cache_user_id ON cache(user_id);
 CREATE INDEX ix_cache_cache_key ON cache(cache_key);
 CREATE INDEX ix_cache_session_id ON cache(session_id);
+
 
 -- --------------------------------------------------------
 
@@ -30,7 +31,8 @@
 CREATE TABLE contacts (
   contact_id integer NOT NULL PRIMARY KEY,
   user_id integer NOT NULL default '0',
-  del integer NOT NULL default '0',
+  created datetime NOT NULL default '0000-00-00 00:00:00',
+  del tinyint NOT NULL default '0',
   name varchar(128) NOT NULL default '',
   email varchar(128) NOT NULL default '',
   firstname varchar(128) NOT NULL default '',
@@ -49,10 +51,10 @@
 CREATE TABLE identities (
   identity_id integer NOT NULL PRIMARY KEY,
   user_id integer NOT NULL default '0',
-  del integer NOT NULL default '0',
-  "default" integer NOT NULL default '0',
+  del tinyint NOT NULL default '0',
+  standard tinyint NOT NULL default '0',
   name varchar(128) NOT NULL default '',
-  organization varchar(128) NOT NULL default '',
+  organization varchar(128) default '',
   email varchar(128) NOT NULL default '',
   "reply-to" varchar(128) NOT NULL default '',
   bcc varchar(128) NOT NULL default '',
@@ -78,3 +80,45 @@
   language varchar(5) NOT NULL default 'en',
   preferences text NOT NULL default ''
 );
+
+
+-- --------------------------------------------------------
+
+-- 
+-- Table structure for table session
+-- 
+
+CREATE TABLE session (
+  sess_id varchar(40) NOT NULL PRIMARY KEY,
+  created datetime NOT NULL default '0000-00-00 00:00:00',
+  changed datetime NOT NULL default '0000-00-00 00:00:00',
+  ip varchar(15) NOT NULL default '',
+  vars text NOT NULL
+);
+
+
+-- --------------------------------------------------------
+
+-- 
+-- Table structure for table messages
+-- 
+
+CREATE TABLE messages (
+  message_id integer NOT NULL PRIMARY KEY,
+  user_id integer NOT NULL default '0',
+  del tinyint NOT NULL default '0',
+  cache_key varchar(128) NOT NULL default '',
+  idx integer NOT NULL default '0',
+  uid integer NOT NULL default '0',
+  subject varchar(255) NOT NULL default '',
+  "from" varchar(255) NOT NULL default '',
+  "to" varchar(255) NOT NULL default '',
+  cc varchar(255) NOT NULL default '',
+  date datetime NOT NULL default '0000-00-00 00:00:00',
+  size integer NOT NULL default '0',
+  headers text NOT NULL,
+  body text
+);
+
+CREATE INDEX ix_messages_user_id ON messages(user_id);
+CREATE INDEX ix_messages_cache_key ON messages(cache_key);
diff --git a/UPGRADING b/UPGRADING
index 57be50b..fbfd47b 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -10,7 +10,7 @@
 - replace index.php
 - replace all files in folder /program/
 - replace all files in folder /skins/default/
-- rund SQL queries in order to update the database
+- run all commands in SQL/*.update.sql or re-initalize database with *.initial.sql
 - add these line to /config/main.inc.php
   $rcmail_config['trash_mbox'] = 'Trash';
   $rcmail_config['default_imap_folders'] = array('INBOX', 'Drafts', 'Sent', 'Junk', 'Trash');
@@ -22,9 +22,12 @@
   $rcmail_config['message_sort_col'] = 'date';
   $rcmail_config['message_sort_order'] = 'DESC';
   $rcmail_config['log_dir'] = 'logs/';
+  $rcmail_config['temp_dir'] = 'temp/';
 - replace database properties (db_type, db_host, db_user, db_pass, $d_name)
   in /config/db.inc.php with the following line:
   $rcmail_config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail';
+- add these lines to /config/db.inc.php
+  $rcmail_config['db_max_length'] = 512000;
 
 
 from version 0.1-20050820
@@ -32,7 +35,7 @@
 - replace index.php
 - replace all files in folder /program/
 - replace all files in folder /skins/default/
-- rund SQL queries in order to update the database
+- run all commands in SQL/*.update.sql or re-initalize database with *.initial.sql
 - add these line to /config/main.inc.php
   $rcmail_config['prettydate'] = TRUE;
   $rcmail_config['smtp_port'] = 25;
@@ -41,9 +44,12 @@
   $rcmail_config['message_sort_col'] = 'date';
   $rcmail_config['message_sort_order'] = 'DESC';
   $rcmail_config['log_dir'] = 'logs/';
+  $rcmail_config['temp_dir'] = 'temp/';
 - replace database properties (db_type, db_host, db_user, db_pass, $d_name)
   in /config/db.inc.php with the following line:
   $rcmail_config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail';
+- add these lines to /config/db.inc.php
+  $rcmail_config['db_max_length'] = 512000;
 
 
 from version 0.1-20051007
@@ -51,20 +57,38 @@
 - replace index.php
 - replace all files in folder /program/
 - 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['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['message_sort_col'] = 'date';
   $rcmail_config['message_sort_order'] = 'DESC';
   $rcmail_config['log_dir'] = 'logs/';
-  
+  $rcmail_config['temp_dir'] = 'temp/';
+- add these lines to /config/db.inc.php
+  $rcmail_config['db_max_length'] = 512000;  
+  $rcmail_config['db_sequence_user_ids'] = 'user_ids';
+  $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';  
+
 
 from version 0.1-20051021
 ----------------------------------------
 - replace index.php
 - replace all files in folder /program/
 - 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['message_sort_col'] = 'date';
   $rcmail_config['message_sort_order'] = 'DESC';
   $rcmail_config['log_dir'] = 'logs/';
+  $rcmail_config['temp_dir'] = 'temp/';
+- add these lines to /config/db.inc.php
+  $rcmail_config['db_max_length'] = 512000;
+  $rcmail_config['db_sequence_user_ids'] = 'user_ids';
+  $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
diff --git a/config/db.inc.php.dist b/config/db.inc.php.dist
index 6db4178..c797168 100644
--- a/config/db.inc.php.dist
+++ b/config/db.inc.php.dist
@@ -19,6 +19,7 @@
 // currentyl suported db_providers: mysql, sqlite
 
 $rcmail_config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail';
+// postgres example: 'pgsql://roundcube:pass@localhost/roundcubemail';
 // sqlite example: 'sqlite://./sqlite.db?mode=0646';
 
 // PEAR database DSN for read only operations (if empty write database will be used)
@@ -27,6 +28,9 @@
 
 // database backend to use (only db or mdb2 are supported)
 $rcmail_config['db_backend'] = 'db';
+
+// maximum length of a query in bytes
+$rcmail_config['db_max_length'] = 512000;  // 500K
 
 // you can define specific table names used to store webmail data
 $rcmail_config['db_table_users'] = 'users';
@@ -39,6 +43,20 @@
 
 $rcmail_config['db_table_cache'] = 'cache';
 
+$rcmail_config['db_table_messages'] = 'messages';
+
+
+// you can define specific sequence names used in PostgreSQL
+$rcmail_config['db_sequence_users'] = 'user_ids';
+
+$rcmail_config['db_sequence_identities'] = 'identity_ids';
+
+$rcmail_config['db_sequence_contacts'] = 'contact_ids';
+
+$rcmail_config['db_sequence_cache'] = 'cache_ids';
+
+$rcmail_config['db_sequence_messages'] = 'message_ids';
+
 
 // end db config file
 ?>
diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist
index 6a0ada4..ec1614e 100644
--- a/config/main.inc.php.dist
+++ b/config/main.inc.php.dist
@@ -88,7 +88,7 @@
 $rcmail_config['date_long'] = 'd.m.Y H:i';
 
 // add this user-agent to message headers when sending
-$rcmail_config['useragent'] = 'RoundCube Webmail/0.1-20051021';
+$rcmail_config['useragent'] = 'RoundCube Webmail/0.1b';
 
 // only list folders within this path
 $rcmail_config['imap_root'] = '';
diff --git a/index.php b/index.php
index 16a3540..31d4e75 100644
--- a/index.php
+++ b/index.php
@@ -62,7 +62,7 @@
 
 // increase maximum execution time for php scripts
 // (does not work in safe mode)
-@set_time_limit('120');
+@set_time_limit(120);
 
 // include base files
 require_once('include/rcube_shared.inc');
@@ -199,6 +199,13 @@
   }
 
 
+// handle keep-alive signal
+if ($_action=='keep-alive')
+  {
+  rcube_remote_response('');
+  exit;
+  }
+
 
 // include task specific files
 if ($_task=='mail')
@@ -286,14 +293,6 @@
   if ($_action=='folders' || $_action=='subscribe' || $_action=='unsubscribe' || $_action=='create-folder' || $_action=='delete-folder')
     include('program/steps/settings/manage_folders.inc');
 
-  }
-
-
-// handle keep-alive signal
-if ($_action=='keep-alive')
-  {
-  rcube_remote_response('');
-  exit;
   }
 
 
diff --git a/program/include/cache.inc b/program/include/cache.inc
index 8d088e5..06e0681 100644
--- a/program/include/cache.inc
+++ b/program/include/cache.inc
@@ -43,8 +43,8 @@
 
   return $data;
   }
-  
-  
+
+
 function rcube_write_cache($key, $data, $session_cache=FALSE)
   {
   global $DB, $CACHE_KEYS, $sess_id;
@@ -89,7 +89,6 @@
                 $data);
     }
   }
-
 
 
 function rcube_clear_cache($key)
diff --git a/program/include/main.inc b/program/include/main.inc
index ddb4218..40ca1d4 100644
--- a/program/include/main.inc
+++ b/program/include/main.inc
@@ -69,7 +69,7 @@
 
   // 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 @@
 // 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 @@
   }
 
 
+// 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 @@
               $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 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";
@@ -527,6 +542,117 @@
                                 $JS_OBJECT_NAME,
                                 $name,
                                 rep_specialchars_output(rcube_label($name), 'js')));  
+  }
+
+
+// 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)] = '&euro;';
+        }
+      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['"'] = '&quot;';
+    }
+
+  // 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;
   }
 
 
@@ -653,104 +779,53 @@
     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 @@
   // 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('<img src="%%s"%s />', $attrib_str);
     $btn_content = sprintf($img_tag, $skin_path.$attrib['image']);
     if ($attrib['label'])
@@ -1161,4 +1236,39 @@
   }
 
 
+
+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 f13ab55..acb13ce 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 $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;
+  var $a_query_results = array('dummy');
+  var $last_res_id = 0;
 
-    // PHP 5 constructor
-    function __construct($db_dsnw,$db_dsnr='')
+
+  // 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;
+      // 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;
-            }
+      // 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);
+    $params = func_get_args();
+    $query = array_shift($params);
+
+    return $this->_query($query, 0, 0, $params);
+    }
+
+
+  // Query with limits
+  function limitquery()
+    {
+    $params = func_get_args();
+    $query = array_shift($params);
+    $offset = array_shift($params);
+    $numrows = array_shift($params);
 		
-		return $this->_query($query, 0, 0, $params);
+    return $this->_query($query, $offset, $numrows, $params);
     }
-   
-	function limitquery()
+
+
+  function _query($query, $offset, $numrows, $params)
     {
-		$params = func_get_args();
-		$query = array_shift($params);
-		$offset = array_shift($params);
-		$numrows = array_shift($params);
-		
-		return $this->_query($query, $offset, $numrows, $params);
+    // Read or write ?
+    if (strtolower(trim(substr($query,0,6)))=='select')
+      $mode='r';
+    else
+      $mode='w';
+        
+    $this->db_connect($mode);
+
+    if ($this->db_provider == 'sqlite')
+      $this->_sqlite_prepare();
+
+    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 _query($query, $offset, $numrows, $params)
+
+
+  function num_rows($res_id=NULL)
     {
-        // Read or write ?
-        if (strtolower(trim(substr($query,0,6)))=='select')
-            $mode='r';
-        else
-            $mode='w';
-        
-        $this->db_connect($mode);
+    if (!$this->db_handle)
+      return FALSE;
 
-        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 ($result = $this->_get_result($res_id))    
+      return $result->numRows();
+    else
+      return FALSE;
+    }
 
+
+  function affected_rows($res_id=NULL)
+    {
+    if (!$this->db_handle)
+      return FALSE;
+
+    return $this->db_handle->affectedRows();
+    }
+
+
+  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().'; QUERY: '.$query), TRUE, FALSE);
-             return false;
-            }
+          raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 
+                            'message' => $result->getMessage()), TRUE, FALSE);
 
-        return $this->_add_result($result, $query);
-    }
-    
-    function num_rows($res_id=NULL)
-    {
-        if (!$this->db_handle)
-            return FALSE;
-
-        $result = $this->_get_result($res_id);
-    
-        if ($result)    
-              return $result->numRows();
-        else
-              return FALSE;
-    }
-
-    function affected_rows($res_id=NULL)
-    {
-        if (!$this->db_handle)
-            return FALSE;
-    
-        return $this->db_handle->affectedRows();
-    }
-
-    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;
+        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);
+    $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;
-        }
+    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)
     {
-        // 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;
-        }
+    return $this->quoteIdentifier($str);
     }
 
 
-    function _get_result($res_id)
+  function unixtimestamp($field)
     {
-        if ($res_id==NULL)
-            $res_id = $this->last_res_id;
+    switch($this->db_provider)
+      {
+      case 'pgsql':
+        return "EXTRACT (EPOCH FROM $field)";
+        break;
+
+      default:
+        return "UNIX_TIMESTAMP($field)";
+      }
+    }
+
+
+  function fromunixtime($timestamp)
+    {
+    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 ;
+    if (empty($fileName) || !is_string($fileName))
+      return ;
 
-        $data = '';
-        if ($fd = fopen($fileName, 'r'))
-          {
-          $data = fread($fd, filesize($fileName));
-          fclose($fd);
-          }
+    $data = '';
+    if ($fd = fopen($fileName, 'r'))
+      {
+      $data = fread($fd, filesize($fileName));
+      fclose($fd);
+      }
 
-        if (strlen($data))
-          sqlite_exec($dbh->connection, $data);
+    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 2237b38..ed7c3ed 100644
--- a/program/include/rcube_imap.inc
+++ b/program/include/rcube_imap.inc
@@ -28,6 +28,7 @@
 
 class rcube_imap
   {
+  var $db;
   var $conn;
   var $root_ns = '';
   var $root_dir = '';
@@ -38,21 +39,23 @@
   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 @@
     // get account namespace
     if ($this->conn)
       {
+      $this->_parse_capability($this->conn->capability);
       iil_C_NameSpace($this->conn);
       
       if (!empty($this->conn->delimiter))
@@ -182,6 +186,13 @@
   function get_mailbox_name()
     {
     return $this->conn ? $this->_mod_mailbox($this->mailbox, 'out') : '';
+    }
+
+
+  function get_capability($cap)
+    {
+    $cap = strtoupper($cap);
+    return $this->capabilities[$cap];
     }
 
 
@@ -298,200 +309,131 @@
 
 
   // private method for listing message header
-  // by DrSlump <drslump@drslump.biz>
-  function __list_headers($mailbox='', $page=NULL, $sort_field='date', $sort_order='DESC')
-    {
-    $a_out = array();
-    $cached_count = 0;
-
-    if (!strlen($mailbox))
-      return $a_out;
-
-    $mbox_count = $this->_messagecount($mailbox /*, 'ALL', TRUE*/);
-
-    $revalidate = false;
-    if ($mbox_count)
-      {
-      // 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)
-        {
-        $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)
-          {
-          //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;
-          }
-        }
-
-      //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
-        {
-        $a_dirty = $a_out;
-        $a_out = array();
-        $a_buffers = array();
-
-        //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);
-
-          //compare the fetched headers with the ones in the cache
-          $idx = 0;
-          foreach ($a_buffers[$from] as $k=>$hdr)
-            {
-            //if it's different the comparison ends
-            if (!isset($a_dirty[$hdr->uid]) || $a_dirty[$hdr->uid]->id !== $hdr->id)
-              break;
-
-            //if we arrive here then we know that the older messages in cache are ok
-            $found = $hdr->id;
-            $idx++;
-            }
-
-          //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);
-
-        //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;
-            }
-          }
-        }
-         
-      //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);
-    }
-
-
-  // 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)
+    $max = $this->_messagecount($mailbox);
+    $start_msg = ($this->list_page-1) * $this->page_size;
+    
+    if ($page=='all')
       {
-      $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;
+      $begin = 0;
+      $end = $max;
+      }
+    else if ($sort_order=='DESC')
+      {
+      $begin = $max - $this->page_size - $start_msg;
+      $end =   $max - $start_msg;
       }
     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);
-      
-    // 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);
+      $begin = $start_msg;
+      $end =   $start_msg + $this->page_size;
       }
 
-	if (empty($a_headers))
-		return array();
-		
-    // return complete list of messages
-    if (strtolower($page)=='all')
-      return $a_headers;
+    if ($begin < 0) $begin = 0;
+    if ($end < 0) $end = $max;
+    if ($end > $max) $end = $max;
 
-    $start_msg = ($this->list_page-1) * $this->page_size;
-    return array_slice($a_headers, $start_msg, $this->page_size);
+//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
+      {
+      // retrieve headers from IMAP
+      if ($this->get_capability('sort') && ($msg_index = iil_C_Sort($this->conn, $mailbox, $sort_field)))
+        {
+//console("$mailbox: ".count($msg_index));
+        
+        $msgs = $msg_index[$begin];
+        for ($i=$begin; $i < $end; $i++)
+          {
+          if ($sort_order == 'DESC')
+            $msgs = $msg_index[$i].','.$msgs;
+          else
+            $msgs = $msgs.','.$msg_index[$i];
+          }
+
+        $sorted = TRUE;
+        }
+      else
+        {
+        $msgs = sprintf("%d:%d", $begin+1, $end);
+        $sorted = FALSE;
+        }
+
+
+      // 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);
+        }      
+
+
+      // 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)
+            {
+            // delete from cache
+            if ($cache_index[$headers->id] && $cache_index[$headers->id] == $headers->uid)
+              $this->remove_message_cache($cache_key, $headers->id);
+
+            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;
+          }
+        }
+
+      // delete cached messages with a higher index than $max
+      $this->clear_message_cache($cache_key, $max);
+
+
+      // kick child process to sync cache
+      
+      }
+
+
+    // return empty array if no messages found
+	if (!is_array($a_msg_headers) || empty($a_msg_headers))
+		return array();
+
+
+    // 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 @@
     }
 
 
-  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 @@
   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 @@
 
     // 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 @@
     // 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 @@
     // 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 @@
     // 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 @@
     $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,18 +748,23 @@
   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);
       }
       
     return $result;
     }
-
 
 
   /* --------------------------------
@@ -824,13 +827,18 @@
   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 @@
 
     // 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 @@
       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 @@
     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 @@
       }
     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 @@
 
 
 
+  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);
+    }
+
+
+
   /* --------------------------------
-   *   encoding/decoding functions
+   *   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 methods
    * --------------------------------*/
 
   
@@ -986,9 +1266,15 @@
     }
 
 
-  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 @@
     }
 
 
+
   /* --------------------------------
    *         private methods
    * --------------------------------*/
@@ -1149,6 +1436,33 @@
     }
 
 
+  // 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 35973ad..54a40e7 100755
--- a/program/include/rcube_mdb2.inc
+++ b/program/include/rcube_mdb2.inc
@@ -238,6 +238,22 @@
 	}
 
 
+	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 400e345..75db760 100644
--- a/program/include/rcube_shared.inc
+++ b/program/include/rcube_shared.inc
@@ -1185,99 +1185,6 @@
   }
 
 
-// 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)] = '&euro;';
-        }
-      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['"'] = '&quot;';
-    }
-
-  // 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 @@
 
   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 0000000..78c671d
--- /dev/null
+++ b/program/include/rcube_sqlite.inc
@@ -0,0 +1,71 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_sqlite.inc                                      |
+ |                                                                       |
+ | This file is part of the RoundCube Webmail client                     |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland                      |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Provide callback functions for sqlite that will emulate             |
+ |   sone MySQL functions                                                |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com>                        |
+ +-----------------------------------------------------------------------+
+
+ $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 54ed798..f10a2b3 100644
--- a/program/include/session.inc
+++ b/program/include/session.inc
@@ -45,7 +45,7 @@
 
   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_write($key, $vars)
   {
   global $DB;
-  
+
   $sql_result = $DB->query("SELECT 1
                             FROM ".get_table_name('session')."
                             WHERE  sess_id=?",
@@ -83,6 +83,8 @@
                 $key,
                 $vars,
                 $_SERVER['REMOTE_ADDR']);
+                
+
     }
 
   return TRUE;
@@ -102,7 +104,9 @@
   $DB->query("DELETE FROM ".get_table_name('session')."
               WHERE sess_id=?",
               $key);
-                     
+
+  rcmail_clear_session_temp($key);
+
   return TRUE;
   }
 
@@ -115,7 +119,7 @@
   // 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 @@
                 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;
   }
 
diff --git a/program/js/app.js b/program/js/app.js
index 095c89d..d65af42 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -210,7 +210,7 @@
     this.enable_command('logout', true);
 
     // disable browser's contextmenus
-    //document.oncontextmenu = function(){ return false; }
+    // document.oncontextmenu = function(){ return false; }
 
     // flag object as complete
     this.loaded = true;
@@ -286,6 +286,7 @@
       return false;
     
     //this.messageform = this.gui_objects.messageform;
+    var input_from = rcube_find_object('_from');
     var input_to = rcube_find_object('_to');
     var input_cc = rcube_find_object('_cc');
     var input_bcc = rcube_find_object('_bcc');
@@ -300,6 +301,10 @@
       this.init_address_input_events(input_cc);
     if (input_bcc)
       this.init_address_input_events(input_bcc);
+      
+    // add signature according to selected identity
+    if (input_from && input_from.type=='select-one')
+      this.change_identity(input_from);
 
     if (input_to && input_to.value=='')
       input_to.focus();
@@ -461,8 +466,17 @@
         // get the type of sorting
         var a_sort = props.split('_');
         var sort_col = a_sort[0];
-        var sort_order = a_sort[1].toUpperCase();
+        var sort_order = a_sort[1] ? a_sort[1].toUpperCase() : null;
         var header;
+        
+        // no sort order specified: toggle
+        if (sort_order==null)
+          {
+          if (this.env.sort_col==sort_col)
+            sort_order = this.env.sort_order=='ASC' ? 'DESC' : 'ASC';
+          else
+            sort_order = this.env.sort_order;
+          }
         
         if (this.env.sort_col==sort_col && this.env.sort_order==sort_order)
           break;
@@ -478,7 +492,7 @@
         this.env.sort_order = sort_order;
 
         // reload message list
-        this.list_mailbox('', '', props);
+        this.list_mailbox('', '', sort_col+'_'+sort_order);
         break;
 
       case 'nextpage':
@@ -805,7 +819,7 @@
         break;
 
       case 'delete-folder':
-        if (confirm('Do you really want to delete this folder?'))
+        if (confirm(this.get_label('deletefolderconfirm')))
           this.delete_folder(props);
         break;
 
@@ -933,8 +947,8 @@
 
     if (!this.in_selection_before)
       {
-      var shift = this.check_shiftkey(e);
-      this.select(id, shift);
+      var ctrl = this.check_ctrlkey(e);
+      this.select(id, ctrl);
       }
     
     if (this.selection.length)
@@ -951,7 +965,7 @@
   // onmouseup-handler of message list row
   this.click_row = function(e, id)
     {
-    var shift = this.check_shiftkey(e);
+    var ctrl = this.check_ctrlkey(e);
     
     // don't do anything (another action processed before)
     if (this.dont_select)
@@ -961,13 +975,13 @@
       }
     
     if (!this.drag_active && this.in_selection_before==id)
-      this.select(id, (shift && this.task!='settings'));
+      this.select(id, (ctrl && this.task!='settings'));
     
     this.drag_start = false;
     this.in_selection_before = false;
         
     // row was double clicked
-    if (this.task=='mail' && this.list_rows && this.list_rows[id].clicked && !shift)
+    if (this.task=='mail' && this.list_rows && this.list_rows[id].clicked && !ctrl)
       {
       this.show_message(id);
       return false;
@@ -1332,6 +1346,38 @@
   /*********************************************************/
   /*********        message compose methods        *********/
   /*********************************************************/
+  
+  
+  this.change_identity = function(obj)
+    {
+    if (!obj || !obj.options)
+      return false;
+
+    var id = obj.options[obj.selectedIndex].value;
+    var input_message = rcube_find_object('_message');
+    var message = input_message ? input_message.value : '';
+
+    // remove the 'old' signature
+    if (this.env.identity && this.env.signatures && this.env.signatures[this.env.identity])
+      {
+      var sig = this.env.signatures[this.env.identity];
+      
+      if (p = message.lastIndexOf(sig))
+        message = message.substring(0, p-1) + message.substring(p+sig.length, message.length);
+      }
+
+    // add the new signature string
+    if (this.env.signatures && this.env.signatures[id])
+      {
+      var sig = this.env.signatures[id];
+      message += '\n'+sig;
+      }
+
+    if (input_message && message)
+      input_message.value = message;
+      
+    this.env.identity = id;
+    };
 
 
   this.show_attachment_form = function(a)
@@ -1854,16 +1900,20 @@
     {
     if (folder)
       {
-      for (var id in this.env.subscriptionrows)
-        if (this.env.subscriptionrows[id]==folder)
-          break;
-          
-      var row;
-      if (id && (row = document.getElementById(id)))
-        row.style.display = 'none';
-
       this.http_request('delete-folder', '_mboxes='+escape(folder));
       }
+    };
+
+
+  this.remove_folder_row = function(folder)
+    {
+    for (var id in this.env.subscriptionrows)
+      if (this.env.subscriptionrows[id]==folder)
+        break;
+
+    var row;
+    if (id && (row = document.getElementById(id)))
+      row.style.display = 'none';    
     };
 
 
@@ -2491,6 +2541,21 @@
       return false;
     }
 
+  // check if Shift-key is pressed on event
+  this.check_ctrlkey = function(e)
+    {
+    if(!e && window.event)
+      e = window.event;
+
+    if(bw.linux && bw.ns4 && e.modifiers)
+      return true;
+   else if (bw.mac)
+       return this.check_shiftkey(e);
+    else if((bw.ns4 && e.modifiers & Event.CTRL_MASK) || (e && e.ctrlKey))
+      return true;
+    else
+      return false;
+    }
 
   this.get_mouse_pos = function(e)
     {
diff --git a/program/localization/de/labels.inc b/program/localization/de/labels.inc
index 4bbb14c..12ec06e 100644
--- a/program/localization/de/labels.inc
+++ b/program/localization/de/labels.inc
@@ -109,6 +109,7 @@
 $labels['compose']  = 'Neue Nachricht verfassen';
 $labels['sendmessage']  = 'Nachricht jetzt senden';
 $labels['addattachment']  = 'Datei anf�gen';
+$labels['charset']  = 'Zeichensatz';
 
 $labels['attachments'] = 'Anh�nge';
 $labels['upload'] = 'Hochladen';
@@ -121,7 +122,6 @@
 $labels['highest'] = 'H�chste';
 
 $labels['nosubject']  = '(kein Betreff)';
-
 $labels['showimages'] = 'Bilder anzeigen';
 
 
@@ -165,7 +165,7 @@
 $labels['language']  = 'Sprache';
 $labels['timezone']  = 'Zeitzone';
 $labels['pagesize']  = 'Eintr�ge pro Seite';
-
+$labels['signature'] = 'Signatur';
 
 $labels['folders']  = 'Ordner';
 $labels['foldername']  = 'Ordnername';
diff --git a/program/localization/de/messages.inc b/program/localization/de/messages.inc
index e8eabe1..145c7b4 100644
--- a/program/localization/de/messages.inc
+++ b/program/localization/de/messages.inc
@@ -54,6 +54,8 @@
 
 $messages['errorsaving'] = 'Beim Speichern ist ein Fehler aufgetreten';
 
+$messages['deletefolderconfirm']  = 'Wollen Sie diesen Ordner wirklich l�schen?';
+
 $messages['formincomplete']    = 'Das Formular wurde nicht vollst�ndig ausgef�llt';
 
 $messages['noemailwarning']    = 'Bitte geben Sie eine g�ltige E-Mail-Adresse ein';
diff --git a/program/localization/ee/labels.inc b/program/localization/ee/labels.inc
new file mode 100644
index 0000000..7605072
--- /dev/null
+++ b/program/localization/ee/labels.inc
@@ -0,0 +1,182 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/ee/labels.inc                                                |
+ |                                                                       |
+ | Language file of the RoundCube Webmail client                         |
+ | Copyright (C) 2005, RoundQube Dev. - Switzerland                      |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: �llar Pajus <yllar.pajus@gmail.com>                           |
+ +-----------------------------------------------------------------------+
+
+ $Id$
+
+*/
+
+$labels = array();
+
+// login page
+$labels['username']  = 'Kasutajanimi';
+$labels['password']  = 'Parool';
+$labels['server']    = 'Server';
+$labels['login']     = 'Logi sisse';
+
+// taskbar
+$labels['logout']   = 'Logi v�lja';
+$labels['mail']     = 'Postkast';
+$labels['settings'] = 'Seaded';
+$labels['addressbook'] = 'Aadressiraamat';
+
+// mailbox names
+$labels['inbox']  = 'Sissetulevad';
+$labels['sent']   = 'Saadetud';
+$labels['trash']  = 'Pr�gikast';
+$labels['drafts'] = 'Ootel';
+$labels['junk']   = 'R�mps';
+
+// message listing
+$labels['subject'] = 'Pealkiri';
+$labels['from']    = 'Saatja';
+$labels['to']      = 'Saaja';
+$labels['cc']      = 'Koopia';
+$labels['bcc']     = 'Bcc';
+$labels['replyto'] = 'Vastus aadressile';
+$labels['date']    = 'Kuup�ev';
+$labels['size']    = 'Suurus';
+$labels['priority'] = 'T�htsus';
+$labels['organization'] = 'Organisatsioon';
+
+// aliases
+$labels['reply-to'] = $labels['replyto'];
+
+$labels['mailboxlist'] = 'Kaustad';
+$labels['messagesfromto'] = 'Kirjed $from kuni $to, kokku $count';
+$labels['messagenrof'] = 'Kiri $nr, kokku $count';
+
+$labels['moveto']   = 'liiguta kausta...';
+$labels['download'] = 'lae arvutisse';
+
+$labels['filename'] = 'Faili nimi';
+$labels['filesize'] = 'Faili suurus';
+
+$labels['preferhtml'] = 'Eelista HTMLi';
+$labels['htmlmessage'] = 'HTML kirjad';
+$labels['prettydate'] = 'Kenad kuup�evad';
+
+$labels['addtoaddressbook'] = 'Lisa aadressiraamatusse';
+
+// weekdays short
+$labels['sun'] = 'P';
+$labels['mon'] = 'E';
+$labels['tue'] = 'T';
+$labels['wed'] = 'K';
+$labels['thu'] = 'N';
+$labels['fri'] = 'R';
+$labels['sat'] = 'L';
+
+// weekdays long
+$labels['sunday']    = 'P�hap�ev';
+$labels['monday']    = 'Esmasp�ev';
+$labels['tuesday']   = 'Teisip�ev';
+$labels['wednesday'] = 'Kolmap�ev';
+$labels['thursday']  = 'Neljap�ev';
+$labels['friday']    = 'Reede';
+$labels['saturday']  = 'Laup�ev';
+
+$labels['today'] = 'T�na';
+
+// toolbar buttons
+$labels['writenewmessage']  = 'Kirjuta uus kiri';
+$labels['replytomessage']   = 'Vasta kirjale';
+$labels['replytoallmessage'] = 'Vasta saatjale ja teistele kirja saanutele';
+$labels['forwardmessage']   = 'Edasta see kiri';
+$labels['deletemessage']    = 'Liiguta kiri pr�gikasti';
+$labels['printmessage']     = 'Tr�ki kiri';
+$labels['previousmessages'] = 'N�ita eelmisi kirju';
+$labels['nextmessages']     = 'N�ita j�rgmisi kirju';
+$labels['backtolist']       = 'Tagasi kirjade nimekirja';
+$labels['viewsource']       = 'N�ita l�htekoodi';
+
+$labels['select'] = 'Vali';
+$labels['all'] = 'k�ik';
+$labels['none'] = 'mitte midagi';
+$labels['unread'] = 'mitte loetud';
+
+// message compose
+$labels['compose']  = 'Koosta kiri';
+$labels['sendmessage']  = 'Saada kiri kohe';
+$labels['addattachment']  = 'Lisa fail';
+
+$labels['attachments'] = 'Manused';
+$labels['upload'] = 'Kinnita manus';
+$labels['close']  = 'Sulge';
+
+$labels['low']     = 'Madal';
+$labels['lowest']  = 'Madalaim';
+$labels['normal']  = 'Tavaline';
+$labels['high']    = 'K�rge';
+$labels['highest'] = 'K�rgeim';
+
+$labels['nosubject']  = '(teema puudub)';
+
+$labels['showimages'] = 'N�ita pilte';
+
+
+// address boook
+$labels['name']      = 'N�idatav nimi';
+$labels['firstname'] = 'Eesnimi';
+$labels['surname']   = 'Perekonnanimi';
+$labels['email']     = 'E-Mail';
+
+$labels['addcontact'] = 'Lisa uus kontakt';
+$labels['editcontact'] = 'Muuda kontakti';
+
+$labels['edit']   = 'Muuda';
+$labels['cancel'] = 'Katkesta';
+$labels['save']   = 'Salvesta';
+$labels['delete'] = 'Kustuta';
+
+$labels['newcontact']     = 'Loo uus sissekanne';
+$labels['deletecontact']  = 'Kustuta m�rgistatud kontaktid';
+$labels['composeto']      = 'Kirjuta kiri';
+$labels['contactsfromto'] = 'Kirjed $from kuni $to, kokku $count';
+$labels['print']          = 'Tr�ki';
+$labels['export']         = 'Ekspordi';
+
+
+// settings
+$labels['settingsfor']  = 'Kasutajaeelistused kontole';
+
+$labels['preferences']  = 'Eelistused';
+$labels['userpreferences']  = 'Kasutaja eelistused';
+$labels['editpreferences']  = 'Muuda kasutaja eelistusi';
+
+$labels['identities']  = 'Identiteedid';
+$labels['manageidentities']  = 'Halda selle konto identiteete';
+$labels['newidentity']  = 'Uus identiteet';
+
+$labels['newitem']  = 'Uus sissekanne';
+$labels['edititem']  = 'Muuda sissekannet';
+
+$labels['setdefault']  = 'Muuda vaikeseadeks';
+$labels['language']  = 'Keel';
+$labels['timezone']  = 'Ajatsoon';
+$labels['pagesize']  = 'Ridu lehe kohta';
+
+
+$labels['folders']  = 'Kaustad';
+$labels['foldername']  = 'Kausta nimi';
+$labels['subscribed']  = 'N�itan';
+$labels['create']  = 'Loo';
+$labels['createfolder']  = 'Loo uus kaust';
+$labels['deletefolder']  = 'Kustuta kaust';
+$labels['managefolders']  = 'Manage folders';
+
+$labels['sortby'] = 'J�rjesta';
+$labels['sortasc']  = 'J�rjesta kasvavalt';
+$labels['sortdesc'] = 'J�rjesta kahanevalt';
+
+?>
diff --git a/program/localization/ee/messages.inc b/program/localization/ee/messages.inc
new file mode 100644
index 0000000..6aeff28
--- /dev/null
+++ b/program/localization/ee/messages.inc
@@ -0,0 +1,80 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | language/ee/messages.inc                                              |
+ |                                                                       |
+ | Language file of the RoundCube Webmail client                         |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland                      |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: �llar Pajus <yllar.pajus@gmail.com>                           |
+ +-----------------------------------------------------------------------+
+
+ $Id$
+
+*/
+
+$messages = array();
+
+$messages['loginfailed']  = 'Sisselogimine eba�nnestus';
+
+$messages['cookiesdisabled'] = 'Sinu veebilehitseja ei v�ta pr��nikuid vastu';
+
+$messages['sessionerror'] = 'Sinu sessioon on aegunud v�i vigane';
+
+$messages['imaperror'] = 'Ei �nnestunud IMAP serveriga �hendust luua';
+
+$messages['nomessagesfound'] = 'Postkast paistab t�hi olevat';
+
+$messages['loggedout'] = 'Sinu sessioon on edukalt l�petatud. N�gemiseni!';
+
+$messages['mailboxempty'] = 'Postkast on t�hi';
+
+$messages['loading'] = 'Laen...';
+
+$messages['loadingdata'] = 'Laen andmeid...';
+
+$messages['sendingmessage'] = 'Saadan kirja...';
+
+$messages['messagesent'] = 'Kiri edukalt saadetud';
+
+$messages['successfullysaved'] = 'Edukalt salvestatud';
+
+$messages['addedsuccessfully'] = 'Kontakt lisati edukalt aadressiraamatusse';
+
+$messages['contactexists'] = 'Sama e-maili aadressiga kontakt on juba olemas';
+
+$messages['blockedimages'] = 'Sinu privaatsuse kaitsmiseks on selles kirjas v�lised pildid blokeeritud.';
+
+$messages['encryptedmessage'] = 'See on kr�pteeritud kiri ja kahjuks pole seda v�imalik n�idata. Andestust!';
+
+$messages['nocontactsfound'] = 'Ei leidnud �htegi kontakti';
+
+$messages['sendingfailed'] = 'Kirja saatmine eba�nnestus';
+
+$messages['errorsaving'] = 'Salvestamie ajal ilmnes viga';
+
+$messages['errormoving'] = 'Ei suutnud seda kirja liigutada';
+
+$messages['errordeleting'] = 'Ei suutnud seda kirja kustutada';
+
+$messages['errordeleting'] = 'Ei suutnud seda kirja kustutada';
+
+$messages['formincomplete']    = 'Vormi k�ik v�ljad ei ole t�idetud';
+
+$messages['noemailwarning']    = 'Palun sisesta toimiv e-maili aadress';
+
+$messages['nonamewarning']     = 'Palun sisesta nimi';
+
+$messages['nopagesizewarning'] = 'Palun sisesta lehek�lje suurus';
+
+$messages['norecipientwarning'] = 'Palun sisesta v�hemalt �ks kirjasaaja';
+
+$messages['nosubjectwarning']  = 'V�li "Pealkiri" on t�hi. Soovid selle �ra t�ita ?';
+
+$messages['nobodywarning'] = 'Saadan selle kirja ilma tekstita ?';
+
+
+?>
diff --git a/program/localization/en/labels.inc b/program/localization/en/labels.inc
index 35813cd..b37b481 100644
--- a/program/localization/en/labels.inc
+++ b/program/localization/en/labels.inc
@@ -109,6 +109,7 @@
 $labels['compose']  = 'Compose a message';
 $labels['sendmessage']  = 'Send the message now';
 $labels['addattachment']  = 'Attach a file';
+$labels['charset']  = 'Charset';
 
 $labels['attachments'] = 'Attachments';
 $labels['upload'] = 'Upload';
@@ -121,7 +122,6 @@
 $labels['highest'] = 'Highest';
 
 $labels['nosubject']  = '(no subject)';
-
 $labels['showimages'] = 'Display images';
 
 
@@ -165,7 +165,7 @@
 $labels['language']  = 'Language';
 $labels['timezone']  = 'Time zone';
 $labels['pagesize']  = 'Rows per page';
-
+$labels['signature'] = 'Signature';
 
 $labels['folders']  = 'Folders';
 $labels['foldername']  = 'Folder name';
diff --git a/program/localization/en/messages.inc b/program/localization/en/messages.inc
index 1ce6bba..d0286e2 100644
--- a/program/localization/en/messages.inc
+++ b/program/localization/en/messages.inc
@@ -60,13 +60,13 @@
 
 $messages['errordeleting'] = 'Could not delete the message';
 
-$messages['errordeleting'] = 'Could not delete the message';
+$messages['deletefolderconfirm']  = 'Do you really want to delete this folder?';
 
-$messages['formincomplete']    = 'The form was not completely filled out';
+$messages['formincomplete'] = 'The form was not completely filled out';
 
-$messages['noemailwarning']    = 'Please enter a valid email address';
+$messages['noemailwarning'] = 'Please enter a valid email address';
 
-$messages['nonamewarning']     = 'Please enter a name';
+$messages['nonamewarning']  = 'Please enter a name';
 
 $messages['nopagesizewarning'] = 'Please enter a page size';
 
diff --git a/program/localization/en_GB/labels.inc b/program/localization/en_GB/labels.inc
index d1a2749..cd9b503 100644
--- a/program/localization/en_GB/labels.inc
+++ b/program/localization/en_GB/labels.inc
@@ -120,6 +120,7 @@
 $labels['high']    = 'High';
 $labels['highest'] = 'Highest';
 
+$labels['nosubject']  = '(no subject)';
 $labels['showimages'] = 'Display images';
 
 
@@ -163,7 +164,7 @@
 $labels['language']  = 'Language';
 $labels['timezone']  = 'Time zone';
 $labels['pagesize']  = 'Rows per page';
-
+$labels['signature'] = 'Signature';
 
 $labels['folders']  = 'Folders';
 $labels['foldername']  = 'Folder name';
diff --git a/program/localization/index.inc b/program/localization/index.inc
index 3386ebd..82424e6 100644
--- a/program/localization/index.inc
+++ b/program/localization/index.inc
@@ -32,6 +32,7 @@
 	'da'    => 'Dansk',
  	'de'    => 'Deutsch',
  	'es'    => 'Espa&ntilde;ol',
+ 	'ee'    => 'Estonian',
 	'fr'    => 'Fran&ccedil;ais', 
 	'ga'    => 'Galician',
 	'el'    => 'Greek',
diff --git a/program/steps/addressbook/delete.inc b/program/steps/addressbook/delete.inc
index a3fcefb..b95988d 100644
--- a/program/steps/addressbook/delete.inc
+++ b/program/steps/addressbook/delete.inc
@@ -24,7 +24,7 @@
 if ($_GET['_cid'])
   {
   $DB->query("UPDATE ".get_table_name('contacts')."
-              SET    del='1'
+              SET    del=1
               WHERE  user_id=?
               AND    contact_id IN (".$_GET['_cid'].")",
               $_SESSION['user_id']);
@@ -40,7 +40,7 @@
   // count contacts for this user
   $sql_result = $DB->query("SELECT COUNT(contact_id) AS rows
                             FROM ".get_table_name('contacts')."
-                            WHERE  del<>'1'
+                            WHERE  del<>1
                             AND    user_id=?",
                             $_SESSION['user_id']);
                                    
@@ -60,7 +60,7 @@
 
     // get contacts from DB
     $sql_result = $DB->limitquery("SELECT * FROM ".get_table_name('contacts')."
-                                   WHERE  del<>'1'
+                                   WHERE  del<>1
                                    AND    user_id=?
                                    ORDER BY name",
                                    $start_row,
diff --git a/program/steps/addressbook/edit.inc b/program/steps/addressbook/edit.inc
index feb794f..a129d00 100644
--- a/program/steps/addressbook/edit.inc
+++ b/program/steps/addressbook/edit.inc
@@ -26,7 +26,7 @@
   $DB->query("SELECT * FROM ".get_table_name('contacts')."
              WHERE  contact_id=?
              AND    user_id=?
-             AND    del<>'1'",
+             AND    del<>1",
              $cid,
              $_SESSION['user_id']);
   
diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc
index 4cc79ba..8065219 100644
--- a/program/steps/addressbook/func.inc
+++ b/program/steps/addressbook/func.inc
@@ -43,7 +43,7 @@
   // count contacts for this user
   $sql_result = $DB->query("SELECT COUNT(contact_id) AS rows
                             FROM ".get_table_name('contacts')."
-                            WHERE  del<>'1'
+                            WHERE  del<>1
                             AND    user_id=?",
                             $_SESSION['user_id']);
 
@@ -56,7 +56,7 @@
 
     // get contacts from DB
     $sql_result = $DB->limitquery("SELECT * FROM ".get_table_name('contacts')."
-                                   WHERE  del<>'1'
+                                   WHERE  del<>1
                                    AND    user_id= ?
                                    ORDER BY name",
                                    $start_row,
@@ -173,7 +173,7 @@
   if ($max===NULL)
     {
     $sql_result = $DB->query("SELECT 1 FROM ".get_table_name('contacts')."
-                              WHERE  del<>'1'
+                              WHERE  del<>1
                               AND    user_id=?",
                               $_SESSION['user_id']);
 
diff --git a/program/steps/addressbook/list.inc b/program/steps/addressbook/list.inc
index ecb634b..0aa4873 100644
--- a/program/steps/addressbook/list.inc
+++ b/program/steps/addressbook/list.inc
@@ -24,7 +24,7 @@
 // count contacts for this user
 $sql_result = $DB->query("SELECT COUNT(contact_id) AS rows
                           FROM ".get_table_name('contacts')."
-                          WHERE  del<>'1'
+                          WHERE  del<>1
                           AND    user_id=?",
                           $_SESSION['user_id']);
                                    
@@ -40,7 +40,7 @@
 
 // get contacts from DB
 $sql_result = $DB->limitquery("SELECT * FROM ".get_table_name('contacts')."
-                               WHERE  del<>'1'
+                               WHERE  del<>1
                                AND    user_id=?
                                ORDER BY name",
                                $start_row,
diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc
index 2f54e43..f5ba139 100644
--- a/program/steps/addressbook/save.inc
+++ b/program/steps/addressbook/save.inc
@@ -52,7 +52,7 @@
                 SET    changed=now(), ".join(', ', $a_write_sql)."
                 WHERE  contact_id=?
                 AND    user_id=?
-                AND    del<>'1'",
+                AND    del<>1",
                 $_POST['_cid'],
                 $_SESSION['user_id']);
                        
@@ -73,7 +73,7 @@
       $sql_result = $DB->query("SELECT * FROM ".get_table_name('contacts')."
                                 WHERE  contact_id=?
                                 AND    user_id=?
-                                AND    del<>'1'",
+                                AND    del<>1",
                                $_POST['_cid'],
                                $_SESSION['user_id']);
                          
@@ -109,7 +109,7 @@
   $sql_result = $DB->query("SELECT 1 FROM ".get_table_name('contacts')."
                             WHERE  user_id=?
                             AND    email=?
-                            AND    del<>'1'",
+                            AND    del<>1",
                            $_SESSION['user_id'],
                            $_POST['_email']);
 
@@ -134,11 +134,11 @@
   if (sizeof($a_insert_cols))
     {
     $DB->query("INSERT INTO ".get_table_name('contacts')."
-                (user_id, changed, ".join(', ', $a_insert_cols).")
-                VALUES (?, now(), ".join(', ', $a_insert_values).")",
+                (user_id, changed, del, ".join(', ', $a_insert_cols).")
+                VALUES (?, now(), 0, ".join(', ', $a_insert_values).")",
                 $_SESSION['user_id']);
                        
-    $insert_id = $DB->insert_id();
+    $insert_id = $DB->insert_id(get_sequence_name('contacts'));
     }
     
   if ($insert_id)
diff --git a/program/steps/addressbook/show.inc b/program/steps/addressbook/show.inc
index 83db486..960ea1c 100644
--- a/program/steps/addressbook/show.inc
+++ b/program/steps/addressbook/show.inc
@@ -26,7 +26,7 @@
   $DB->query("SELECT * FROM ".get_table_name('contacts')."
               WHERE  contact_id=?
               AND    user_id=?
-              AND    del<>'1'",
+              AND    del<>1",
               $cid,
               $_SESSION['user_id']);
   
diff --git a/program/steps/mail/addcontact.inc b/program/steps/mail/addcontact.inc
index ad1544e..b1129ec 100644
--- a/program/steps/mail/addcontact.inc
+++ b/program/steps/mail/addcontact.inc
@@ -32,7 +32,7 @@
       $sql_result = $DB->query("SELECT 1 FROM ".get_table_name('contacts')."
                                 WHERE  user_id=?
                                 AND    email=?
-                                AND    del<>'1'",
+                                AND    del<>1",
                                 $_SESSION['user_id'],$contact['mailto']);
 
     // contact entry with this mail address exists
@@ -42,13 +42,13 @@
     else if ($contact['mailto'])
       {
       $DB->query("INSERT INTO ".get_table_name('contacts')."
-                  (user_id, changed, name, email)
-                  VALUES (?, now(), ?, ?)",
+                  (user_id, changed, del, name, email)
+                  VALUES (?, now(), 0, ?, ?)",
                   $_SESSION['user_id'],
                   $contact['name'],
                   $contact['mailto']);
 
-      $added = $DB->insert_id();
+      $added = $DB->insert_id(get_sequence_name('contacts'));
       }
     }
 
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 9509b49..45f1112 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -88,50 +88,20 @@
   switch ($part)
     {
     case 'from':
-      // pass the following attributes to the form class
-      $field_attrib = array('name' => '_from');
-      foreach ($attrib as $attr => $value)
-        if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex')))
-          $field_attrib[$attr] = $value;
-    
-      // get this user's identities
-      $sql_result = $DB->query("SELECT identity_id, name, email
-                                FROM   ".get_table_name('identities')." WHERE  user_id=?
-                                AND    del<>'1'
-                                ORDER BY ".$DB->quoteIdentifier('default')." DESC, name ASC",
-                                $_SESSION['user_id']);
-                                   
-      if ($DB->num_rows($sql_result))
-        {        
-        $select_from = new select($field_attrib);
-        while ($sql_arr = $DB->fetch_assoc($sql_result))
-          $select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $sql_arr['identity_id']);
-        
-        $out = $select_from->show($_POST['_from']);
-        }
-      else
-        {
-        $input_from = new textfield($field_attrib);
-        $out = $input_from->show($_POST['_from']);
-        }
-        
-      if ($form_start)
-        $out = $form_start.$out;
-
-      return $out;
-    
+      return rcmail_compose_header_from($attrib);
 
     case 'to':
       $fname = '_to';
       $header = 'to';
-      
+
       // we have contact id's as get parameters
-      if (!empty($_GET['_to']) && preg_match('/[0-9]+,?/', $_GET['_to']))
+      if (!empty($_GET['_to']) && preg_match('/^([0-9]+,?)+$/', $_GET['_to']))
         {
         $a_recipients = array();
         $sql_result = $DB->query("SELECT name, email
-                                  FROM ".get_table_name('contacts')." WHERE user_id=?
-                                  AND    del<>'1'
+                                  FROM ".get_table_name('contacts')."
+                                  WHERE user_id=?
+                                  AND    del<>1
                                   AND    contact_id IN (".$_GET['_to'].")",
                                   $_SESSION['user_id']);
                                          
@@ -229,71 +199,91 @@
   }
 
 
-/*function rcube_compose_headers($attrib)
+
+function rcmail_compose_header_from($attrib)
   {
-  global $CONFIG, $OUTPUT;
+  global $IMAP, $REPLY_MESSAGE, $DB, $OUTPUT, $JS_OBJECT_NAME;
+    
+  // pass the following attributes to the form class
+  $field_attrib = array('name' => '_from');
+  foreach ($attrib as $attr => $value)
+    if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex')))
+      $field_attrib[$attr] = $value;
 
-  list($form_start, $form_end) = get_form_tags($attrib);
+  // extract all recipients of the reply-message
+  $a_recipients = array();
+  if ($REPLY_MESSAGE && is_object($REPLY_MESSAGE['headers']))
+    {
+    $a_to = $IMAP->decode_address_list($REPLY_MESSAGE['headers']->to);        
+    foreach ($a_to as $addr)
+      {
+      if (!empty($addr['mailto']))
+        $a_recipients[] = $addr['mailto'];
+      }
 
-  // allow the following attributes to be added to the headers table
-  $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border'));
-  
-  $labels = array();
-  $labels['from'] = rcube_label('from');
-  $labels['to'] = rcube_label('to');
-  $labels['cc'] = rcube_label('cc');
-  $labels['bcc'] = rcube_label('bcc');
-  $labels['replyto'] = rcube_label('replyto');
+    if (!empty($REPLY_MESSAGE['headers']->cc))
+      {
+      $a_cc = $IMAP->decode_address_list($REPLY_MESSAGE['headers']->cc);
+      foreach ($a_cc as $addr)
+        {
+        if (!empty($addr['mailto']))
+          $a_recipients[] = $addr['mailto'];
+        }
+      }
+    }
 
-  $input_from = new textfield(array('name' => '_from', 'size' => 30));
-  $input_to = new textfield(array('name' => '_to', 'size' => 30));
-  $input_cc = new textfield(array('name' => '_cc', 'size' => 30));
-  $input_bcc = new textfield(array('name' => '_bcc', 'size' => 30));
-  $input_replyto = new textfield(array('name' => '_replyto', 'size' => 30));
+  // get this user's identities
+  $sql_result = $DB->query("SELECT identity_id, name, email, signature
+                            FROM   ".get_table_name('identities')."
+                            WHERE user_id=?
+                            AND    del<>1
+                            ORDER BY ".$DB->quoteIdentifier('standard')." DESC, name ASC",
+                           $_SESSION['user_id']);
+                                   
+  if ($DB->num_rows($sql_result))
+    {
+    $from_id = 0;
+    $a_signatures = array();
+    
+    $field_attrib['onchange'] = "$JS_OBJECT_NAME.change_identity(this)";
+    $select_from = new select($field_attrib);
+    
+    while ($sql_arr = $DB->fetch_assoc($sql_result))
+      {
+      $select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $sql_arr['identity_id']);
 
-  $fields = array();
-  $fields['from'] = $input_from->show($_POST['_from']);
-  $fields['to'] = $input_to->show($_POST['_to']);
-  $fields['cc'] = $input_cc->show($_POST['_cc']);
-  $fields['bcc'] = $input_bcc->show($_POST['_bcc']);
-  $fields['replyto'] = $input_replyto->show($_POST['_replyto']);
+      // add signature to array
+      if (!empty($sql_arr['signature']))
+        $a_signatures[$sql_arr['identity_id']] = $sql_arr['signature'];
+      
+      // set identity if it's one of the reply-message recipients
+      if (in_array($sql_arr['email'], $a_recipients))
+        $from_id = $sql_arr['identity_id'];
+      }
 
+    // overwrite identity selection with post parameter
+    if (isset($_POST['_from']))
+      $from_id = $_POST['_from'];
 
-  $out = <<<EOF
-$form_start
-<table$attrib_str><tr>
+    $out = $select_from->show($from_id);
+    
 
-<td class="title">$labels[from]</td>
-<td>$fields[from]</td>
+    // add signatures to client
+    $OUTPUT->add_script(sprintf("%s.set_env('signatures', %s);", $JS_OBJECT_NAME, array2js($a_signatures)));  
+    }
+  else
+    {
+    $input_from = new textfield($field_attrib);
+    $out = $input_from->show($_POST['_from']);
+    }
+    
+  if ($form_start)
+    $out = $form_start.$out;
 
-</tr><tr>
-
-<td class="title">$labels[to]</td>
-<td>$fields[to]</td>
-
-</tr><tr>
-
-<td class="title">$labels[cc]</td>
-<td>$fields[cc]</td>
-
-</tr><tr>
-
-<td class="title">$labels[bcc]</td>
-<td>$fields[bcc]</td>
-
-</tr><tr>
-
-<td class="title">$labels[replyto]</td>
-<td>$fields[replyto]</td>
-
-</tr></table>
-$form_end
-EOF;
-
-  return $out;  
+  return $out;
   }
-*/
 
+  
 
 function rcmail_compose_body($attrib)
   {
@@ -360,6 +350,14 @@
   $pefix = sprintf("\n\n\nOn %s, %s wrote:\n",
            $REPLY_MESSAGE['headers']->date,
            $IMAP->decode_header($REPLY_MESSAGE['headers']->from));
+           
+
+  // try to remove the signature
+  if ($sp = strrpos($body, '--'))
+    {
+    if ($body{$sp+3}==' ' || $body{$sp+3}=="\n" || $body{$sp+3}=="\r")
+      $body = substr($body, 0, $sp-1);
+    }
 
   return $pefix.$body;
   }
@@ -596,7 +594,7 @@
 
 $sql_result = $DB->query("SELECT name, email
                           FROM ".get_table_name('contacts')." WHERE  user_id=?
-                          AND  del<>'1'",$_SESSION['user_id']);
+                          AND  del<>1",$_SESSION['user_id']);
                                    
 if ($DB->num_rows($sql_result))
   {        
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index ca72f74..c81dd2f 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -305,26 +305,41 @@
 
     // make sort links
     $sort = '';
-    if (in_array($col, $a_sort_cols) && (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton'])))
+    if ($IMAP->get_capability('sort') && in_array($col, $a_sort_cols))
       {
-      $sort = '&nbsp;&nbsp;';
+      // have buttons configured
+      if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton']))
+        {
+        $sort = '&nbsp;&nbsp;';
 
-      // asc link
-      if (!empty($attrib['sortascbutton']))
-        {
-        $sort .= rcube_button(array('command' => 'sort',
-                                    'prop' => $col.'_ASC',
-                                    'image' => $attrib['sortascbutton'],
-                                    'title' => 'sortasc'));
-        }        
+        // asc link
+        if (!empty($attrib['sortascbutton']))
+          {
+          $sort .= rcube_button(array('command' => 'sort',
+                                      'prop' => $col.'_ASC',
+                                      'image' => $attrib['sortascbutton'],
+                                      'align' => 'absmiddle',
+                                      'title' => 'sortasc'));
+          }       
         
-      // desc link
-      if (!empty($attrib['sortdescbutton']))
+        // desc link
+        if (!empty($attrib['sortdescbutton']))
+          {
+          $sort .= rcube_button(array('command' => 'sort',
+                                      'prop' => $col.'_DESC',
+                                      'image' => $attrib['sortdescbutton'],
+                                      'align' => 'absmiddle',
+                                      'title' => 'sortdesc'));        
+          }
+        }
+      // just add a link tag to the header
+      else
         {
-        $sort .= rcube_button(array('command' => 'sort',
-                                    'prop' => $col.'_DESC',
-                                    'image' => $attrib['sortdescbutton'],
-                                    'title' => 'sortdesc'));        
+        $col_name = sprintf('<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>',
+                            $JS_OBJECT_NAME,
+                            $col,
+                            rcube_label('sortby'),
+                            $col_name);
         }
       }
       
@@ -1128,20 +1143,76 @@
 // get source code of a specific message and cache it
 function rcmail_message_source($uid)
   {
-  global $IMAP, $DB;
+  global $IMAP, $DB, $CONFIG;
 
-  // get message ID if uid is given  
-  $headers = $IMAP->get_headers($uid);
+  // get message ID if uid is given
+  $cache_key = $IMAP->mailbox.'.msg';
+  $cached = $IMAP->get_cached_message($cache_key, $uid, FALSE);
+  
+  // message is cached in database
+  if ($cached && !empty($cached->body))
+    return $cached->body;
+
+  if (!$cached)
+    $headers = $IMAP->get_headers($uid);
+  else
+    $headers = &$cached;
+
+
   $message_id = $headers->messageID;
   
-  // get cached message source
-  $msg_source = rcube_read_cache($message_id);
+  $temp_dir = $CONFIG['temp_dir'].(!eregi('\/$', $CONFIG['temp_dir']) ? '/' : '');
+  $cache_dir = $temp_dir.$_SESSION['client_id'];
+  $cache_path = $cache_dir.'/'.$message_id;
 
-  // get message from server and cache it
-  if (!$msg_source)
+  // message is cached in temp dir
+  if (is_dir($cache_dir) && is_file($cache_path))
     {
-    $msg_source = $IMAP->get_raw_body($uid);
-    rcube_write_cache($message_id, $msg_source, TRUE);
+    if ($fp = fopen($cache_path, 'r'))
+      {
+      $msg_source = fread($fp, filesize($cache_path));
+      fclose($fp);
+      return $msg_source;
+      }
+    }
+
+
+  // get message from server
+  $msg_source = $IMAP->get_raw_body($uid);
+
+  // let's cache the message body within the database
+  if ($CONFIG['enable_caching'] && $cached && ($CONFIG['db_max_length'] -300) > $headers->size)
+    {
+    $DB->query("UPDATE ".get_table_name('messages')."
+                SET    body=?
+                WHERE  user_id=?
+                AND    cache_key=?
+                AND    uid=?",
+               $msg_source,
+               $_SESSION['user_id'],
+               $cache_key,
+               $uid);
+
+    return $msg_source;
+    }
+
+
+  // create dir for caching
+  if (!is_dir($cache_dir))
+    $dir = mkdir($cache_dir);
+  else
+    $dir = true;
+
+  // attempt to write a file with the message body    
+  if ($dir && ($fp = fopen($cache_path, 'w')))
+    {
+    fwrite($fp, $msg_source);
+    fclose($fp);
+    }
+  else
+    {
+    raise_error(array('code' => 403, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__, 
+                      'message' => "Failed to write to temp dir"), TRUE, FALSE);
     }
 
   return $msg_source;
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index 11fb559..8ec30b0 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -46,7 +46,7 @@
                             FROM ".get_table_name('identities')."
                             WHERE  identity_id=?
                             AND    user_id=?
-                            AND    del<>'1'",
+                            AND    del<>1",
                             $id,$_SESSION['user_id']);
                                    
   if ($DB->num_rows($sql_result))
@@ -78,8 +78,8 @@
   $CHARSET = 'ISO-8859-1';
 
 
-$mailto_regexp = array('/,\s*[\r\n]+/', '/[\r\n]+/', '/,\s*$/m');
-$mailto_replace = array(' ', ', ', '');
+$mailto_regexp = array('/[,;]\s*[\r\n]+/', '/[\r\n]+/', '/[,;]\s*$/m');
+$mailto_replace = array(', ', ', ', '');
 
 // repalce new lines and strip ending ', '
 $mailto = preg_replace($mailto_regexp, $mailto_replace, stripslashes($_POST['_to']));
@@ -175,13 +175,16 @@
   foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath)
     $MAIL_MIME->addAttachment($filepath, $files['type'][$i], $files['name'][$i], TRUE);
 
+
+$message_charset = isset($_POST['_charset']) ? $_POST['_charset'] : $CHARSET;
+
 // encoding settings for mail composing
 $message_param = array('text_encoding' => '7bit',
                        'html_encoding' => 'quoted-printable',
                        'head_encoding' => 'quoted-printable',
-                       'head_charset'  => $CHARSET,
-                       'html_charset'  => $CHARSET,
-                       'text_charset'  => $CHARSET);
+                       'head_charset'  => $message_charset,
+                       'html_charset'  => $message_charset,
+                       'text_charset'  => $message_charset);
 
 // compose message body and get headers
 $msg_body = $MAIL_MIME->get($message_param);
diff --git a/program/steps/settings/delete_identity.inc b/program/steps/settings/delete_identity.inc
index 3bca860..b8f9f52 100644
--- a/program/steps/settings/delete_identity.inc
+++ b/program/steps/settings/delete_identity.inc
@@ -24,7 +24,7 @@
 if ($_GET['_iid'])
   {
   $DB->query("UPDATE ".get_table_name('identities')."
-              SET    del='1'
+              SET    del=1
               WHERE  user_id=?
               AND    identity_id IN (".$_GET['_iid'].")",
               $_SESSION['user_id']);
diff --git a/program/steps/settings/edit_identity.inc b/program/steps/settings/edit_identity.inc
index 6649c20..07cd8fa 100644
--- a/program/steps/settings/edit_identity.inc
+++ b/program/steps/settings/edit_identity.inc
@@ -25,7 +25,7 @@
   $DB->query("SELECT * FROM ".get_table_name('identities')."
               WHERE  identity_id=?
               AND    user_id=?
-              AND    del<>'1'",
+              AND    del<>1",
               $id,
               $_SESSION['user_id']);
   
@@ -63,7 +63,8 @@
                        'organization' => array('type' => 'text'),
                        'reply-to'     => array('type' => 'text', 'label' => 'replyto'),
                        'bcc'          => array('type' => 'text'),
-                       'default'      => array('type' => 'checkbox', 'label' => 'setdefault'));
+                       'signature'	  => array('type' => 'textarea'),
+                       'standard'     => array('type' => 'checkbox', 'label' => 'setdefault'));
 
 
   // a specific part is requested
diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc
index 01b6923..b33f8d4 100644
--- a/program/steps/settings/func.inc
+++ b/program/steps/settings/func.inc
@@ -146,9 +146,9 @@
 
   // get contacts from DB
   $sql_result = $DB->query("SELECT * FROM ".get_table_name('identities')."
-                            WHERE  del<>'1'
+                            WHERE  del<>1
                             AND    user_id=?
-                            ORDER BY ".$DB->quoteIdentifier('default')." DESC, name ASC",
+                            ORDER BY standard DESC, name ASC",
                             $_SESSION['user_id']);
 
 
diff --git a/program/steps/settings/manage_folders.inc b/program/steps/settings/manage_folders.inc
index 04b2a46..33d83df 100644
--- a/program/steps/settings/manage_folders.inc
+++ b/program/steps/settings/manage_folders.inc
@@ -69,10 +69,15 @@
 else if ($_action=='delete-folder')
   {
   if (strlen($_GET['_mboxes']))
-    $IMAP->delete_mailbox(explode(',', $_GET['_mboxes']));
+    $deleted = $IMAP->delete_mailbox(explode(',', $_GET['_mboxes']));
 
-  if ($_GET['_remote'])
-    rcube_remote_response('// deleted');
+  if ($_GET['_remote'] && $deleted)
+    rcube_remote_response(sprintf("this.remove_folder_row('%s')", rep_specialchars_output($_GET['_mboxes'], 'js')));
+  else if ($_GET['_remote'])
+    {
+    $commands = show_message('errorsaving', 'error');
+    rcube_remote_response($commands);
+    }
   }
 
 
@@ -174,5 +179,9 @@
   }
 
 
+// add some labels to client
+rcube_add_label('deletefolderconfirm');
+
+
 parse_template('managefolders');
 ?>
\ No newline at end of file
diff --git a/program/steps/settings/save_identity.inc b/program/steps/settings/save_identity.inc
index 2e42987..dc61b78 100644
--- a/program/steps/settings/save_identity.inc
+++ b/program/steps/settings/save_identity.inc
@@ -19,7 +19,7 @@
 
 */
 
-$a_save_cols = array('name', 'email', 'organization', 'reply-to', 'bcc', 'default');
+$a_save_cols = array('name', 'email', 'organization', 'reply-to', 'bcc', 'standard', 'signature');
 
 
 // check input
@@ -51,7 +51,7 @@
                 SET ".join(', ', $a_write_sql)."
                 WHERE  identity_id=?
                 AND    user_id=?
-                AND    del<>'1'",
+                AND    del<>1",
                 $_POST['_iid'],
                 $_SESSION['user_id']);
                        
@@ -64,10 +64,10 @@
 
     // mark all other identities as 'not-default'
     $DB->query("UPDATE ".get_table_name('identities')."
-                SET ".$DB->quoteIdentifier('default')."='0'
+                SET ".$DB->quoteIdentifier('standard')."='0'
                 WHERE  user_id=?
                 AND    identity_id<>?
-                AND    del<>'1'",
+                AND    del<>1",
                 $_SESSION['user_id'],
                 $_POST['_iid']);
     
@@ -106,8 +106,8 @@
                 (user_id, ".join(', ', $a_insert_cols).")
                 VALUES (?, ".join(', ', $a_insert_values).")",
                 $_SESSION['user_id']);
-                       
-    $insert_id = $DB->insert_id();
+
+    $insert_id = $DB->insert_id(get_sequence_name('identities'));
     }
     
   if ($insert_id)
diff --git a/skins/default/images/sort_asc.gif b/skins/default/images/sort_asc.gif
new file mode 100644
index 0000000..244db10
--- /dev/null
+++ b/skins/default/images/sort_asc.gif
Binary files differ
diff --git a/skins/default/images/sort_desc.gif b/skins/default/images/sort_desc.gif
new file mode 100644
index 0000000..2427311
--- /dev/null
+++ b/skins/default/images/sort_desc.gif
Binary files differ
diff --git a/skins/default/mail.css b/skins/default/mail.css
index f429407..9d507fe 100644
--- a/skins/default/mail.css
+++ b/skins/default/mail.css
@@ -75,9 +75,10 @@
   position: absolute;
   top: 60px;
   right: 40px;
-  width: 200px;
+  width: 220px;
   height: 20px;
   text-align: right;
+  white-space: nowrap;
 }
 
 #messagecountbar span
@@ -330,6 +331,26 @@
 #messagelist thead tr td.sortedDESC
 {
   background-image: url(images/listheader_dark.gif); 
+}
+
+#messagelist thead tr td.sortedASC a
+{
+  background: url(images/sort_asc.gif) top right no-repeat;
+}
+
+#messagelist thead tr td.sortedDESC a
+{
+  background: url(images/sort_desc.gif) top right no-repeat;
+}
+
+#messagelist thead tr td a,
+#messagelist thead tr td a:hover
+{
+  display: block;
+  width: auto !important;
+  width: 100%;
+  color: #333333;
+  text-decoration: none;
 }
 
 #messagelist tbody tr td
@@ -645,9 +666,10 @@
 #compose-body
 {
   margin-top: 10px;
+  margin-bottom: 5px;
   width: 99% !important;
   width: 95%;
-  height: 95%;
+  height: 90%;
   min-height: 300px;
   font-size: 9pt;
   font-family: "Courier New", Courier, monospace;
@@ -701,5 +723,4 @@
 {
   margin-top: 8px;
 }
-
 
diff --git a/skins/default/pngbehavior.htc b/skins/default/pngbehavior.htc
index 553699a..24bfd4c 100644
--- a/skins/default/pngbehavior.htc
+++ b/skins/default/pngbehavior.htc
@@ -40,7 +40,7 @@
       element.src = blankSrc;
       // set filter
       element.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" +
-                                     src + "',sizingMethod='scale')";
+                                     src + "',sizingMethod='crop')";
    }
    else {
       // remove filter
diff --git a/skins/default/templates/compose.html b/skins/default/templates/compose.html
index aa28098..0a3f874 100644
--- a/skins/default/templates/compose.html
+++ b/skins/default/templates/compose.html
@@ -87,8 +87,9 @@
 
 </tr><tr>
 
-<td style="width:100%; height:100%; vertical-align:top;">
-<roundcube:object name="composeBody" id="compose-body" form="form" cols="80" rows="20" warp="virtual" tabindex="7" />
+<td style="width:100%; height:95%; vertical-align:top;">
+<roundcube:object name="composeBody" id="compose-body" form="form" cols="80" rows="20" warp="virtual" tabindex="7" /><br />
+<roundcube:label name="charset" />:&nbsp;<roundcube:object name="charsetSelector" tabindex="8" />
 </td>
 
 </tr></table>
diff --git a/skins/default/templates/mail.html b/skins/default/templates/mail.html
index 61d0e6d..0f87c63 100644
--- a/skins/default/templates/mail.html
+++ b/skins/default/templates/mail.html
@@ -37,9 +37,7 @@
   messageIcon="/images/icons/dot.png"
   unreadIcon="/images/icons/unread.png"
   repliedIcon="/images/icons/replied.png"
-  attachmentIcon="/images/icons/attachment.png"
-  sortDescButton="/images/buttons/up_arrow.png"
-  sortAscButton="/images/buttons/down_arrow.png" />
+  attachmentIcon="/images/icons/attachment.png" />
 </div>
 
 <div id="listcontrols">

--
Gitblit v1.9.1