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)] = '€'; + } + else + $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS); + + unset($html_encode_arr['?']); + unset($html_encode_arr['&']); + } + + $ltpos = strpos($str, '<'); + $encode_arr = $html_encode_arr; + + // don't replace quotes and html tags + if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false) + { + unset($encode_arr['"']); + unset($encode_arr['<']); + unset($encode_arr['>']); + } + else if ($mode=='remove') + $str = strip_tags($str); + + $out = strtr($str, $encode_arr); + + return $newlines ? nl2br($out) : $out; + } + + + if ($enctype=='url') + return rawurlencode($str); + + + // if the replace tables for RTF, XML and JS are not yet defined + if (!$js_rep_table) + { + $js_rep_table = $rtf_rep_table = $xml_rep_table = array(); + + for ($c=160; $c<256; $c++) // can be increased to support more charsets + { + $hex = dechex($c); + $rtf_rep_table[Chr($c)] = "\\'$hex"; + $xml_rep_table[Chr($c)] = "&#$c;"; + + if ($CHARSET=='ISO-8859-1') + $js_rep_table[Chr($c)] = sprintf("\u%s%s", str_repeat('0', 4-strlen($hex)), $hex); + } + + $js_rep_table['"'] = sprintf("\u%s%s", str_repeat('0', 4-strlen(dechex(34))), dechex(34)); + $xml_rep_table['"'] = '"'; + } + + // encode for RTF + if ($enctype=='xml') + return strtr($str, $xml_rep_table); + + // encode for javascript use + if ($enctype=='js') + return preg_replace(array("/\r\n/", '/"/', "/([^\\\])'/"), array('\n', '\"', "$1\'"), strtr($str, $js_rep_table)); + + // encode for RTF + if ($enctype=='rtf') + return preg_replace("/\r\n/", "\par ", strtr($str, $rtf_rep_table)); + + // no encoding given -> return original string + return $str; } @@ -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)] = '€'; - } - else - $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS); - - unset($html_encode_arr['?']); - unset($html_encode_arr['&']); - } - - $ltpos = strpos($str, '<'); - $encode_arr = $html_encode_arr; - - // don't replace quotes and html tags - if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false) - { - unset($encode_arr['"']); - unset($encode_arr['<']); - unset($encode_arr['>']); - } - else if ($mode=='remove') - $str = strip_tags($str); - - $out = strtr($str, $encode_arr); - - return $newlines ? nl2br($out) : $out; - } - - - if ($enctype=='url') - return rawurlencode($str); - - - // if the replace tables for RTF, XML and JS are not yet defined - if (!$js_rep_table) - { - $js_rep_table = $rtf_rep_table = $xml_rep_table = array(); - - for ($c=160; $c<256; $c++) // can be increased to support more charsets - { - $hex = dechex($c); - $rtf_rep_table[Chr($c)] = "\\'$hex"; - $xml_rep_table[Chr($c)] = "&#$c;"; - - if ($CHARSET=='ISO-8859-1') - $js_rep_table[Chr($c)] = sprintf("\u%s%s", str_repeat('0', 4-strlen($hex)), $hex); - } - - $js_rep_table['"'] = sprintf("\u%s%s", str_repeat('0', 4-strlen(dechex(34))), dechex(34)); - $xml_rep_table['"'] = '"'; - } - - // encode for RTF - if ($enctype=='xml') - return strtr($str, $xml_rep_table); - - // encode for javascript use - if ($enctype=='js') - return preg_replace(array("/\r\n/", '/"/', "/([^\\\])'/"), array('\n', '\"', "$1\'"), strtr($str, $js_rep_table)); - - // encode for RTF - if ($enctype=='rtf') - return preg_replace("/\r\n/", "\par ", strtr($str, $rtf_rep_table)); - - // no encoding given -> return original string - return $str; - } - function decode_specialchars($input, $charset='') { @@ -1462,7 +1369,21 @@ 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ñol', + 'ee' => 'Estonian', 'fr' => 'Franç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 = ' '; + // have buttons configured + if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton'])) + { + $sort = ' '; - // 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" />: <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