From 1cded85790206afe084e1baff371c543711b2b18 Mon Sep 17 00:00:00 2001 From: thomascube Date: Sat, 3 Dec 2005 16:54:12 +0000 Subject: Re-design of caching (new database table added\!); some bugfixes; Postgres support --- CHANGELOG | 104 ++-- INSTALL | 20 + SQL/mysql.initial.sql | 38 +- SQL/mysql.update.sql | 35 ++ SQL/postgres.initial.sql | 286 +++++------ SQL/sqlite.initial.sql | 54 ++- UPGRADING | 30 +- config/db.inc.php.dist | 18 + config/main.inc.php.dist | 2 +- index.php | 17 +- program/include/cache.inc | 5 +- program/include/main.inc | 312 ++++++++---- program/include/rcube_db.inc | 465 +++++++++--------- program/include/rcube_imap.inc | 748 ++++++++++++++++++++--------- program/include/rcube_mdb2.inc | 16 + program/include/rcube_shared.inc | 109 +---- program/include/rcube_sqlite.inc | 71 +++ program/include/session.inc | 16 +- program/js/app.js | 99 +++- program/localization/de/labels.inc | 4 +- program/localization/de/messages.inc | 2 + program/localization/ee/labels.inc | 182 +++++++ program/localization/ee/messages.inc | 80 +++ program/localization/en/labels.inc | 4 +- program/localization/en/messages.inc | 8 +- program/localization/en_GB/labels.inc | 3 +- program/localization/index.inc | 1 + program/steps/addressbook/delete.inc | 6 +- program/steps/addressbook/edit.inc | 2 +- program/steps/addressbook/func.inc | 6 +- program/steps/addressbook/list.inc | 4 +- program/steps/addressbook/save.inc | 12 +- program/steps/addressbook/show.inc | 2 +- program/steps/mail/addcontact.inc | 8 +- program/steps/mail/compose.inc | 182 ++++--- program/steps/mail/func.inc | 121 ++++- program/steps/mail/sendmail.inc | 15 +- program/steps/settings/delete_identity.inc | 2 +- program/steps/settings/edit_identity.inc | 5 +- program/steps/settings/func.inc | 4 +- program/steps/settings/manage_folders.inc | 15 +- program/steps/settings/save_identity.inc | 12 +- skins/default/images/sort_asc.gif | Bin 0 -> 121 bytes skins/default/images/sort_desc.gif | Bin 0 -> 123 bytes skins/default/mail.css | 27 +- skins/default/pngbehavior.htc | 2 +- skins/default/templates/compose.html | 5 +- skins/default/templates/mail.html | 4 +- 48 files changed, 2105 insertions(+), 1058 deletions(-) create mode 100644 program/include/rcube_sqlite.inc create mode 100644 program/localization/ee/labels.inc create mode 100644 program/localization/ee/messages.inc create mode 100644 skins/default/images/sort_asc.gif create mode 100644 skins/default/images/sort_desc.gif diff --git a/CHANGELOG b/CHANGELOG index ae7631f28..afadced18 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,38 @@ CHANGELOG RoundCube Webmail --------------------------- +2005/12/03 +---------- +- 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' +- Added sorting patch for message list +- Make default sort col/order configurable +- Fixed XSS in address book and identities +- Added more XSS protection (Bug #1308236) +- Added tab indexes for compose form +- Added 'changed' col to contacts table +- Support for 160-bit session hashes +- Added input check for contacts and identities (Patch #1346523) +- Added messages/warning to compose step (Patch #1323895) +- Added favicon to the default skin +- Fixed Bug #1334337 as far as possible +- Added Reply-To-All functionality (Request #1326395, Patch #1349777) +- Redesign of client side AJAX code (enable multi threading) +- Added keep-alive signal every minute +- Make logs dir configurable +- Added support for SMTPS +- 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 @@ -14,16 +46,20 @@ CHANGELOG RoundCube Webmail - Added German translation -2005/08/20 +2005/10/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 +- 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 @@ -44,46 +80,14 @@ CHANGELOG RoundCube Webmail - 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 +2005/08/20 ---------- -- Added Finnish, Romanian, Polish, Czech, British, Norwegian, Greek, Russian and Chinese translation -- Get IMAP server capabilities in array -- Check for NAMESPACE capability before sending command -- Set default user language from config 'locale_string' -- Added sorting patch for message list -- Make default sort col/order configurable -- Fixed XSS in address book and identities -- Added more XSS protection (Bug #1308236) -- Added tab indexes for compose form -- Added 'changed' col to contacts table -- Support for 160-bit session hashes -- Added input check for contacts and identities (Patch #1346523) -- Added messages/warning to compose step (Patch #1323895) -- Added favicon to the default skin -- Fixed Bug #1334337 as far as possible -- Added Reply-To-All functionality (Request #1326395, Patch #1349777) -- Redesign of client side AJAX code (enable multi threading) -- Added keep-alive signal every minute -- Make logs dir configurable -- Added support for SMTPS -- Decode attachment file names -- Make delimiter for message headers configurable -- Add generic footer to sent messages - +- 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 cca4fa12a..f343c8228 100644 --- a/INSTALL +++ b/INSTALL @@ -31,6 +31,7 @@ roundcube user. Here is an example of that procedure: > quit # mysql roundcubemail < SQL/mysql.initial.sql + * SQLite -------- Sqlite requires specifically php5 (sqlite in php4 currently doesn't @@ -44,6 +45,25 @@ Make sure your configuration points to the sqlite.db file and that the 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 09d9b8017..21444edda 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 @@ CREATE TABLE `contacts` ( `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 `contacts` ( 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 @@ CREATE TABLE `users` ( `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 0119225ef..778919acf 100644 --- a/SQL/mysql.update.sql +++ b/SQL/mysql.update.sql @@ -11,4 +11,39 @@ ALTER TABLE users ADD alias VARCHAR(128) NOT NULL AFTER mail_host; -- 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 29c134d14..4e74a222f 100755 --- a/SQL/postgres.initial.sql +++ b/SQL/postgres.initial.sql @@ -1,255 +1,223 @@ -- --- 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 +-- Table "users" +-- Name: users; Type: TABLE; 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 TABLE users ( + user_id integer DEFAULT nextval('user_ids'::text) NOT NULL, + username character varying(128) DEFAULT ''::character varying NOT NULL, + mail_host character varying(128) DEFAULT ''::character varying NOT NULL, + alias character varying(128) DEFAULT ''::character varying NOT NULL, + created timestamp with time zone DEFAULT now() NOT NULL, + last_login timestamp with time zone DEFAULT now() NOT NULL, + "language" character varying(5) DEFAULT 'en'::character varying NOT NULL, + preferences text DEFAULT ''::text NOT NULL +); -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 +-- Table "session" +-- Name: session; Type: TABLE; 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 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 +); -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 +-- Table "identities" +-- Name: identities; Type: TABLE; Schema: public; Owner: postgres -- -CREATE TABLE "cache" ( - cache_id integer DEFAULT nextval('cache_ids'::text) NOT NULL, +CREATE TABLE identities ( + identity_id integer DEFAULT nextval('identity_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 + 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 ); -- --- TOC entry 10 (OID 15282486) +-- 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, - del boolean DEFAULT false 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 NOT NULL + vcard text ); --- --- 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 +-- Table "cache" +-- Name: cache; Type: TABLE; Schema: public; Owner: postgres -- -CREATE TABLE "session" ( - sess_id character varying(32) DEFAULT ''::character varying NOT NULL, +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, - changed timestamp with time zone DEFAULT now() NOT NULL, - ip character varying(16) NOT NULL, - vars text NOT NULL + data text NOT NULL ); + -- --- TOC entry 13 (OID 15282510) --- Name: users; Type: TABLE; Schema: public; Owner: postgres +-- Table "messages" +-- Name: messages; Type: TABLE; Schema: public; Owner: postgres -- -CREATE TABLE users ( - user_id integer DEFAULT nextval('user_ids'::text) NOT NULL, - username character varying(128) DEFAULT ''::character varying NOT NULL, - mail_host character varying(128) DEFAULT ''::character varying NOT NULL, - alias character varying(128) DEFAULT ''::character varying NOT NULL, - created timestamp with time zone DEFAULT now() NOT NULL, - last_login timestamp with time zone DEFAULT now() NOT NULL, - "language" character varying(5) DEFAULT 'en'::character varying NOT NULL, - preferences text DEFAULT ''::text NOT NULL +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 ); + -- --- TOC entry 14 (OID 15282518) --- Name: cache_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- 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); +ALTER TABLE ONLY "cache" + ADD CONSTRAINT "$2" FOREIGN KEY (session_id) REFERENCES "session"(sess_id); + + +ALTER TABLE ONLY "contacts" + ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id); + + +ALTER TABLE ONLY "identities" + ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id); + + +ALTER TABLE ONLY "messages" + ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id); + -- --- TOC entry 20 (OID 15282532) --- Name: $2; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- Sequence "cache_ids" +-- Name: cache_ids; Type: SEQUENCE; Schema: public; Owner: postgres -- -ALTER TABLE ONLY "cache" - ADD CONSTRAINT "$2" FOREIGN KEY (session_id) REFERENCES "session"(sess_id); +CREATE SEQUENCE cache_ids + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; -- --- TOC entry 21 (OID 15282536) --- Name: $1; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- Sequence "contact_ids" +-- Name: contact_ids; Type: SEQUENCE; Schema: public; Owner: postgres -- -ALTER TABLE ONLY contacts - ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id); +CREATE SEQUENCE contact_ids + START WITH 1 + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; -- --- TOC entry 22 (OID 15282540) --- Name: $1; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- Sequence "identity_ids" +-- Name: identity_ids; Type: SEQUENCE; Schema: public; Owner: postgres -- -ALTER TABLE ONLY identities - ADD CONSTRAINT "$1" FOREIGN KEY (user_id) REFERENCES users(user_id); - +CREATE SEQUENCE identity_ids + START WITH 1 + 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 "user_ids" +-- Name: user_ids; Type: SEQUENCE; Schema: public; Owner: postgres -- -COMMENT ON SCHEMA public IS 'Standard public schema'; - +CREATE SEQUENCE user_ids + 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 "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 01f51d6a8..19ca6a578 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 @@ -21,6 +21,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 INDEX ix_cache_session_id ON cache(session_id); 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 INDEX ix_contacts_user_id ON contacts(user_id); 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 @@ CREATE TABLE users ( 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 57be50bbf..fbfd47b19 100644 --- a/UPGRADING +++ b/UPGRADING @@ -10,7 +10,7 @@ from versions 0.1-alpha and 0.1-20050811 - 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 @@ from versions 0.1-alpha and 0.1-20050811 $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 @@ from version 0.1-20050820 - 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 @@ from version 0.1-20050820 $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 @@ from version 0.1-20051007 - 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 6db417860..c797168db 100644 --- a/config/db.inc.php.dist +++ b/config/db.inc.php.dist @@ -19,6 +19,7 @@ $rcmail_config = array(); // 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) @@ -28,6 +29,9 @@ $rcmail_config['db_dsnr'] = ''; // 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_session'] = 'session'; $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 6a0ada4c5..ec1614e18 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -88,7 +88,7 @@ $rcmail_config['date_short'] = 'D H:i'; $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 16a3540d7..31d4e7574 100644 --- a/index.php +++ b/index.php @@ -62,7 +62,7 @@ ini_set('error_reporting', E_ALL&~E_NOTICE); // 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 @@ if (!$_SESSION['user_id']) } +// handle keep-alive signal +if ($_action=='keep-alive') + { + rcube_remote_response(''); + exit; + } + // include task specific files if ($_task=='mail') @@ -289,14 +296,6 @@ if ($_task=='settings') } -// handle keep-alive signal -if ($_action=='keep-alive') - { - rcube_remote_response(''); - exit; - } - - // only allow these templates to be included $valid_tasks = array('mail','settings','addressbook'); diff --git a/program/include/cache.inc b/program/include/cache.inc index 8d088e57b..06e0681ce 100644 --- a/program/include/cache.inc +++ b/program/include/cache.inc @@ -43,8 +43,8 @@ function rcube_read_cache($key) return $data; } - - + + function rcube_write_cache($key, $data, $session_cache=FALSE) { global $DB, $CACHE_KEYS, $sess_id; @@ -91,7 +91,6 @@ function rcube_write_cache($key, $data, $session_cache=FALSE) } - function rcube_clear_cache($key) { global $DB; diff --git a/program/include/main.inc b/program/include/main.inc index ddb42181a..40ca1d4d7 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -69,7 +69,7 @@ function rcmail_startup($task='mail') // we can use the database for storing session data // session queries do not work with MDB2 - if ($CONFIG['db_backend']!='mdb2' && is_object($DB) && $DB->db_provider!='sqlite') + if ($CONFIG['db_backend']!='mdb2' && is_object($DB) /* && $DB->db_provider!='sqlite' */) include_once('include/session.inc'); @@ -143,9 +143,9 @@ function rcmail_auth_hash($sess_id, $ts) // create IMAP object and connect to server function rcmail_imap_init($connect=FALSE) { - global $CONFIG, $IMAP; + global $CONFIG, $DB, $IMAP; - $IMAP = new rcube_imap(); + $IMAP = new rcube_imap($DB); // connect with stored session data if ($connect) @@ -227,6 +227,22 @@ function get_table_name($table) } +// return correct name for a specific database sequence +// (used for Postres only) +function get_sequence_name($sequence) + { + global $CONFIG; + + // return table name if configured + $config_key = 'db_sequence_'.$sequence; + + if (strlen($CONFIG[$config_key])) + return $CONFIG[$config_key]; + + return $table; + } + + // init output object for GUI and add common scripts function load_gui() @@ -380,15 +396,15 @@ function rcmail_create_user($user, $host) $host, $_SESSION['user_lang']); - if ($user_id = $DB->insert_id('user_ids')) + if ($user_id = $DB->insert_id(get_sequence_name('users'))) { $user_email = strstr($user, '@') ? $user : sprintf('%s@%s', $user, $host); $user_name = $user!=$user_email ? $user : ''; // also create a new identity record $DB->query("INSERT INTO ".get_table_name('identities')." - (user_id, `default`, name, email) - VALUES (?, '1', ?, ?)", + (user_id, del, standard, name, email) + VALUES (?, 0, 1, ?, ?)", $user_id, $user_name, $user_email); @@ -479,7 +495,6 @@ function decrypt_passwd($cypher) function rcube_remote_response($js_code) { send_nocacheing_headers(); - //header('Content-Type: text/javascript'); header('Content-Type: application/x-javascript'); print '/** remote response ['.date('d/M/Y h:i:s O')."] **/\n"; @@ -530,6 +545,117 @@ function rcube_add_label() } +// remove temp files of a session +function rcmail_clear_session_temp($sess_id) + { + global $CONFIG; + + $temp_dir = $CONFIG['temp_dir'].(!eregi('\/$', $CONFIG['temp_dir']) ? '/' : ''); + $cache_dir = $temp_dir.$sess_id; + + if (is_dir($cache_dir)) + { + clear_directory($cache_dir); + rmdir($cache_dir); + } + } + + + +// replace specials characters to a specific encoding type +function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) + { + global $OUTPUT_TYPE, $CHARSET; + static $html_encode_arr, $js_rep_table, $rtf_rep_table, $xml_rep_table; + + if (!$enctype) + $enctype = $GLOBALS['OUTPUT_TYPE']; + + // convert nbsps back to normal spaces if not html + if ($enctype!='html') + $str = str_replace(chr(160), ' ', $str); + + + // encode for plaintext + if ($enctype=='text') + return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str); + + // encode for HTML output + if ($enctype=='html') + { + if (!$html_encode_arr) + { + if ($CHARSET=='ISO-8859-1') + { + $html_encode_arr = get_html_translation_table(HTML_ENTITIES); + $html_encode_arr[chr(128)] = '€'; + } + else + $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS); + + unset($html_encode_arr['?']); + unset($html_encode_arr['&']); + } + + $ltpos = strpos($str, '<'); + $encode_arr = $html_encode_arr; + + // don't replace quotes and html tags + if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false) + { + unset($encode_arr['"']); + unset($encode_arr['<']); + unset($encode_arr['>']); + } + else if ($mode=='remove') + $str = strip_tags($str); + + $out = strtr($str, $encode_arr); + + return $newlines ? nl2br($out) : $out; + } + + + if ($enctype=='url') + return rawurlencode($str); + + + // if the replace tables for RTF, XML and JS are not yet defined + if (!$js_rep_table) + { + $js_rep_table = $rtf_rep_table = $xml_rep_table = array(); + + for ($c=160; $c<256; $c++) // can be increased to support more charsets + { + $hex = dechex($c); + $rtf_rep_table[Chr($c)] = "\\'$hex"; + $xml_rep_table[Chr($c)] = "&#$c;"; + + if ($CHARSET=='ISO-8859-1') + $js_rep_table[Chr($c)] = sprintf("\u%s%s", str_repeat('0', 4-strlen($hex)), $hex); + } + + $js_rep_table['"'] = sprintf("\u%s%s", str_repeat('0', 4-strlen(dechex(34))), dechex(34)); + $xml_rep_table['"'] = '"'; + } + + // encode for RTF + if ($enctype=='xml') + return strtr($str, $xml_rep_table); + + // encode for javascript use + if ($enctype=='js') + return preg_replace(array("/\r\n/", '/"/', "/([^\\\])'/"), array('\n', '\"', "$1\'"), strtr($str, $js_rep_table)); + + // encode for RTF + if ($enctype=='rtf') + return preg_replace("/\r\n/", "\par ", strtr($str, $rtf_rep_table)); + + // no encoding given -> return original string + return $str; + } + + // ************** template parsing and gui functions ************** @@ -653,104 +779,53 @@ function rcube_xml_command($command, $str_attrib, $a_attrib=NULL) case 'object': $object = strtolower($attrib['name']); + $object_handlers = array( + // MAIL + 'mailboxlist' => 'rcmail_mailbox_list', + 'messages' => 'rcmail_message_list', + 'messagecountdisplay' => 'rcmail_messagecount_display', + 'messageheaders' => 'rcmail_message_headers', + 'messagebody' => 'rcmail_message_body', + 'messageattachments' => 'rcmail_message_attachments', + 'blockedobjects' => 'rcmail_remote_objects_msg', + 'messagecontentframe' => 'rcmail_messagecontent_frame', + 'messagepartframe' => 'rcmail_message_part_frame', + 'messagepartcontrols' => 'rcmail_message_part_controls', + 'composeheaders' => 'rcmail_compose_headers', + 'composesubject' => 'rcmail_compose_subject', + 'composebody' => 'rcmail_compose_body', + 'composeattachmentlist' => 'rcmail_compose_attachment_list', + 'composeattachmentform' => 'rcmail_compose_attachment_form', + 'composeattachment' => 'rcmail_compose_attachment_field', + 'priorityselector' => 'rcmail_priority_selector', + 'charsetselector' => 'rcmail_charset_selector', + + // ADDRESS BOOK + 'addresslist' => 'rcmail_contacts_list', + 'addressframe' => 'rcmail_contact_frame', + 'recordscountdisplay' => 'rcmail_rowcount_display', + 'contactdetails' => 'rcmail_contact_details', + 'contacteditform' => 'rcmail_contact_editform', + + // USER SETTINGS + 'userprefs' => 'rcmail_user_prefs_form', + 'itentitieslist' => 'rcmail_identities_list', + 'identityframe' => 'rcmail_identity_frame', + 'identityform' => 'rcube_identity_form', + 'foldersubscription' => 'rcube_subscription_form', + 'createfolder' => 'rcube_create_folder_form', + 'composebody' => 'rcmail_compose_body' + ); + if ($object=='loginform') return rcmail_login_form($attrib); else if ($object=='message') return rcmail_message_container($attrib); - - // MAIL - else if ($object=='mailboxlist' && function_exists('rcmail_mailbox_list')) - return rcmail_mailbox_list($attrib); - - else if ($object=='messages' && function_exists('rcmail_message_list')) - return rcmail_message_list($attrib); - - else if ($object=='messagecountdisplay' && function_exists('rcmail_messagecount_display')) - return rcmail_messagecount_display($attrib); - - else if ($object=='messageheaders' && function_exists('rcmail_message_headers')) - return rcmail_message_headers($attrib); - - else if ($object=='messageattachments' && function_exists('rcmail_message_attachments')) - return rcmail_message_attachments($attrib); - - else if ($object=='messagebody' && function_exists('rcmail_message_body')) - return rcmail_message_body($attrib); - - else if ($object=='blockedobjects' && function_exists('rcmail_remote_objects_msg')) - return rcmail_remote_objects_msg($attrib); - - else if ($object=='messagecontentframe' && function_exists('rcmail_messagecontent_frame')) - return rcmail_messagecontent_frame($attrib); - - else if ($object=='messagepartframe' && function_exists('rcmail_message_part_frame')) - return rcmail_message_part_frame($attrib); - - else if ($object=='messagepartcontrols' && function_exists('rcmail_message_part_controls')) - return rcmail_message_part_controls($attrib); - - else if ($object=='composeheaders' && function_exists('rcmail_compose_headers')) - return rcmail_compose_headers($attrib); - - else if ($object=='composesubject' && function_exists('rcmail_compose_subject')) - return rcmail_compose_subject($attrib); - - else if ($object=='composebody' && function_exists('rcmail_compose_body')) - return rcmail_compose_body($attrib); - - else if ($object=='composeattachmentlist' && function_exists('rcmail_compose_attachment_list')) - return rcmail_compose_attachment_list($attrib); - - else if ($object=='composeattachmentform' && function_exists('rcmail_compose_attachment_form')) - return rcmail_compose_attachment_form($attrib); - - else if ($object=='composeattachment' && function_exists('rcmail_compose_attachment_field')) - return rcmail_compose_attachment_field($attrib); - - else if ($object=='priorityselector' && function_exists('rcmail_priority_selector')) - return rcmail_priority_selector($attrib); - - else if ($object=='priorityselector' && function_exists('rcmail_priority_selector')) - return rcmail_priority_selector($attrib); - - - // ADDRESS BOOK - else if ($object=='addresslist' && function_exists('rcmail_contacts_list')) - return rcmail_contacts_list($attrib); - - else if ($object=='addressframe' && function_exists('rcmail_contact_frame')) - return rcmail_contact_frame($attrib); - - else if ($object=='recordscountdisplay' && function_exists('rcmail_rowcount_display')) - return rcmail_rowcount_display($attrib); - - else if ($object=='contactdetails' && function_exists('rcmail_contact_details')) - return rcmail_contact_details($attrib); - - else if ($object=='contacteditform' && function_exists('rcmail_contact_editform')) - return rcmail_contact_editform($attrib); - - - // USER SETTINGS - else if ($object=='userprefs' && function_exists('rcmail_user_prefs_form')) - return rcmail_user_prefs_form($attrib); - - else if ($object=='itentitieslist' && function_exists('rcmail_identities_list')) - return rcmail_identities_list($attrib); - - else if ($object=='identityframe' && function_exists('rcmail_identity_frame')) - return rcmail_identity_frame($attrib); - - else if ($object=='identityform' && function_exists('rcube_identity_form')) - return rcube_identity_form($attrib); - - else if ($object=='foldersubscription' && function_exists('rcube_subscription_form')) - return rcube_subscription_form($attrib); - - else if ($object=='createfolder' && function_exists('rcube_create_folder_form')) - return rcube_create_folder_form($attrib); - + + // execute object handler function + else if ($object_handlers[$object] && function_exists($object_handlers[$object])) + return call_user_func($object_handlers[$object], $attrib); else if ($object=='pagetitle') { @@ -878,7 +953,7 @@ function rcube_button($attrib) // generate image tag if ($attrib['type']=='image') { - $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'width', 'height', 'border', 'hspace', 'vspace', 'alt')); + $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'width', 'height', 'border', 'hspace', 'vspace', 'align', 'alt')); $img_tag = sprintf('', $attrib_str); $btn_content = sprintf($img_tag, $skin_path.$attrib['image']); if ($attrib['label']) @@ -1161,4 +1236,39 @@ EOF; } + +function rcmail_charset_selector($attrib) + { + // pass the following attributes to the form class + $field_attrib = array('name' => '_charset'); + foreach ($attrib as $attr => $value) + if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex'))) + $field_attrib[$attr] = $value; + + $charsets = array( + 'US-ASCII' => 'ASCII (English)', + 'X-EUC-JP' => 'EUC-JP (Japanese)', + 'EUC-KR' => 'EUC-KR (Korean)', + 'BIG5' => 'BIG5 (Chinese)', + 'GB2312' => 'GB2312 (Chinese)', + 'ISO-8859-1' => 'ISO-8859-1 (Latin-1)', + 'ISO-8859-2' => 'ISO-8895-2 (Central European)', + 'ISO-8859-7' => 'ISO-8859-7 (Greek)', + 'ISO-8859-9' => 'ISO-8859-9 (Turkish)', + 'Windows-1251' => 'Windows-1251 (Cyrillic)', + 'Windows-1252' => 'Windows-1252 (Western)', + 'Windows-1255' => 'Windows-1255 (Hebrew)', + 'Windows-1256' => 'Windows-1256 (Arabic)', + 'Windows-1257' => 'Windows-1257 (Baltic)', + 'UTF-8' => 'UTF-8' + ); + + $select = new select($field_attrib); + $select->add(array_values($charsets), array_keys($charsets)); + + $set = $_POST['_charset'] ? $_POST['_charset'] : $GLOBALS['CHARSET']; + return $select->show($set); + } + + ?> \ No newline at end of file diff --git a/program/include/rcube_db.inc b/program/include/rcube_db.inc index f13ab55c0..acb13ce37 100755 --- a/program/include/rcube_db.inc +++ b/program/include/rcube_db.inc @@ -23,299 +23,332 @@ require_once('DB.php'); class rcube_db -{ - var $db_dsnw; // DSN for write operations - var $db_dsnr; // DSN for read operations - var $db_connected=false; // Already connected ? - var $db_mode=''; // Connection mode - var $db_handle=0; // Connection handle - - var $a_query_results = array('dummy'); - var $last_res_id = 0; - - // PHP 5 constructor - function __construct($db_dsnw,$db_dsnr='') + { + var $db_dsnw; // DSN for write operations + var $db_dsnr; // DSN for read operations + var $db_connected = false; // Already connected ? + var $db_mode = ''; // Connection mode + var $db_handle = 0; // Connection handle + + var $a_query_results = array('dummy'); + var $last_res_id = 0; + + + // PHP 5 constructor + function __construct($db_dsnw,$db_dsnr='') { - if ($db_dsnr=='') $db_dsnr=$db_dsnw; + if ($db_dsnr=='') + $db_dsnr=$db_dsnw; - $this->db_dsnw = $db_dsnw; - $this->db_dsnr = $db_dsnr; + $this->db_dsnw = $db_dsnw; + $this->db_dsnr = $db_dsnr; - $dsn_array = DB::parseDSN($db_dsnw); - $this->db_provider = $dsn_array['phptype']; + $dsn_array = DB::parseDSN($db_dsnw); + $this->db_provider = $dsn_array['phptype']; } - // PHP 4 compatibility - function rcube_db($db_dsnw,$db_dsnr='') + + // PHP 4 compatibility + function rcube_db($db_dsnw,$db_dsnr='') { - $this->__construct($db_dsnw,$db_dsnr); + $this->__construct($db_dsnw,$db_dsnr); } - // Connect to specific database - function dsn_connect($dsn) + + // Connect to specific database + function dsn_connect($dsn) { - // Use persistent connections if available - $dbh = DB::connect($dsn, array('persistent' => $true)); + // Use persistent connections if available + $dbh = DB::connect($dsn, array('persistent' => TRUE)); - if (DB::isError($dbh)) - raise_error(array('code' => 500, + if (DB::isError($dbh)) + raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => $dbh->getMessage()), TRUE, FALSE); - else if ($this->db_provider=='sqlite') - { - $dsn_array = DB::parseDSN($dsn); - if (!filesize($dsn_array['database']) && !empty($this->sqlite_initials)) - $this->_sqlite_create_database($dbh, $this->sqlite_initials); - } + else if ($this->db_provider=='sqlite') + { + $dsn_array = DB::parseDSN($dsn); + if (!filesize($dsn_array['database']) && !empty($this->sqlite_initials)) + $this->_sqlite_create_database($dbh, $this->sqlite_initials); + } - return $dbh; + return $dbh; } - // Connect to appropiate databse - function db_connect ($mode) - { - $this->db_mode = $mode; - // Already connected - if ($this->db_connected) - { - // no replication, current connection is ok - if ($this->db_dsnw==$this->db_dsnr) return; + // Connect to appropiate databse + function db_connect ($mode) + { + $this->db_mode = $mode; + + // Already connected + if ($this->db_connected) + { + // no replication, current connection is ok + if ($this->db_dsnw==$this->db_dsnr) + return; - // connected to master, current connection is ok - if ($this->db_mode=='w') return; - - // Same mode, current connection is ok - if ($this->db_mode==$mode) return; - } + // connected to master, current connection is ok + if ($this->db_mode=='w') + return; + + // Same mode, current connection is ok + if ($this->db_mode==$mode) + return; + } - if ($mode=='r') - $dsn=$this->db_dsnr; - else - $dsn=$this->db_dsnw; + if ($mode=='r') + $dsn = $this->db_dsnr; + else + $dsn = $this->db_dsnw; - $this->db_handle = $this->dsn_connect($dsn); - $this->db_connected = true; + $this->db_handle = $this->dsn_connect($dsn); + $this->db_connected = true; } - // Query database - - function query() + + // Query database + function query() { - $params = func_get_args(); - $query = array_shift($params); - - return $this->_query($query, 0, 0, $params); + $params = func_get_args(); + $query = array_shift($params); + + return $this->_query($query, 0, 0, $params); } - - function limitquery() + + + // Query with limits + function limitquery() { - $params = func_get_args(); - $query = array_shift($params); - $offset = array_shift($params); - $numrows = array_shift($params); + $params = func_get_args(); + $query = array_shift($params); + $offset = array_shift($params); + $numrows = array_shift($params); - return $this->_query($query, $offset, $numrows, $params); + return $this->_query($query, $offset, $numrows, $params); } - - function _query($query, $offset, $numrows, $params) + + + function _query($query, $offset, $numrows, $params) { - // Read or write ? - if (strtolower(trim(substr($query,0,6)))=='select') - $mode='r'; - else - $mode='w'; + // Read or write ? + if (strtolower(trim(substr($query,0,6)))=='select') + $mode='r'; + else + $mode='w'; - $this->db_connect($mode); + $this->db_connect($mode); - if ($this->db_provider == 'sqlite') - $query = $this->_sqlite_prepare_query($query); - - if ($numrows || $offset) - { - $result = $this->db_handle->limitQuery($query,$offset,$numrows,$params); - } - else - $result = $this->db_handle->query($query, $params); + if ($this->db_provider == 'sqlite') + $this->_sqlite_prepare(); - if (DB::isError($result)) - { - raise_error(array('code' => 500, - 'type' => 'db', - 'line' => __LINE__, - 'file' => __FILE__, - 'message' => $result->getMessage().'; QUERY: '.$query), TRUE, FALSE); - return false; - } - - return $this->_add_result($result, $query); + if ($numrows || $offset) + $result = $this->db_handle->limitQuery($query,$offset,$numrows,$params); + else + $result = $this->db_handle->query($query, $params); + + // add result, even if it's an error + return $this->_add_result($result); } - - function num_rows($res_id=NULL) + + + function num_rows($res_id=NULL) { - if (!$this->db_handle) - return FALSE; + if (!$this->db_handle) + return FALSE; - $result = $this->_get_result($res_id); - - if ($result) - return $result->numRows(); - else - return FALSE; + if ($result = $this->_get_result($res_id)) + return $result->numRows(); + else + return FALSE; } - function affected_rows($res_id=NULL) + + function affected_rows($res_id=NULL) { - if (!$this->db_handle) - return FALSE; - - return $this->db_handle->affectedRows(); + if (!$this->db_handle) + return FALSE; + + return $this->db_handle->affectedRows(); } - function insert_id($sequence = '') + + function insert_id($sequence = '') { - if (!$this->db_handle || $this->db_mode=='r') - return FALSE; - - switch($this->db_provider) - { - case 'pgsql': - // PostgreSQL uses sequences - $result =& $this->db_handle->getOne("SELECT CURRVAL('$sequence')"); - if (DB::isError($result)) { - raise_error( array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, - 'message' => $result->getMessage()), TRUE, TRUE); - } - return $result; + if (!$this->db_handle || $this->db_mode=='r') + return FALSE; + + switch($this->db_provider) + { + case 'pgsql': + // PostgreSQL uses sequences + $result =& $this->db_handle->getOne("SELECT CURRVAL('$sequence')"); + if (DB::isError($result)) + raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, + 'message' => $result->getMessage()), TRUE, FALSE); + + return $result; - case 'mysql': // This is unfortuneate - return mysql_insert_id($this->db_handle->connection); + case 'mysql': // This is unfortuneate + return mysql_insert_id($this->db_handle->connection); - case 'mysqli': - return mysqli_insert_id($this->db_handle->connection); + case 'mysqli': + return mysqli_insert_id($this->db_handle->connection); - case 'sqlite': - return sqlite_last_insert_rowid($this->db_handle->connection); + case 'sqlite': + return sqlite_last_insert_rowid($this->db_handle->connection); - default: - die("portability issue with this database, please have the developer fix"); - } + default: + die("portability issue with this database, please have the developer fix"); + } } - function fetch_assoc($res_id=NULL) + function fetch_assoc($res_id=NULL) { - $result = $this->_get_result($res_id); - - if (DB::isError($result)) - { - raise_error( array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, - 'message' => $this->db_link->getMessage()), TRUE, FALSE); - return FALSE; - } + $result = $this->_get_result($res_id); + + if (DB::isError($result)) + { + raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, + 'message' => $this->db_link->getMessage()), TRUE, FALSE); + return FALSE; + } - return $result->fetchRow(DB_FETCHMODE_ASSOC); + return $result->fetchRow(DB_FETCHMODE_ASSOC); } - function quote($input, $type=null) + function quote($input, $type=null) { - if (!$this->db_handle) - $this->db_connect('r'); + if (!$this->db_handle) + $this->db_connect('r'); - return $this->db_handle->quote($input); + return $this->db_handle->quote($input); } - function quoteIdentifier($str) + function quoteIdentifier($str) { - if (!$this->db_handle) - $this->db_connect('r'); + if (!$this->db_handle) + $this->db_connect('r'); - return $this->db_handle->quoteIdentifier($str); - } - - function quote_identifier($str) - { - return $this->quoteIdentifier($str); + return $this->db_handle->quoteIdentifier($str); } - - function unixtimestamp($field) - { - switch($this->db_provider) - { - case 'pgsql': - return "EXTRACT (EPOCH FROM $field)"; - break; - default: - return "UNIX_TIMESTAMP($field)"; - } - } - - function _add_result($res, $query) + + function quote_identifier($str) + { + return $this->quoteIdentifier($str); + } + + + function unixtimestamp($field) { - // sql error occured - if (DB::isError($res)) - { - raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => $res->getMessage() . " Query: " . substr(preg_replace('/[\r\n]+\s*/', ' ', $query), 0, 1024)), TRUE, FALSE); - return FALSE; - } - else - { - $res_id = sizeof($this->a_query_results); - $this->a_query_results[$res_id] = $res; - $this->last_res_id = $res_id; - return $res_id; - } + switch($this->db_provider) + { + case 'pgsql': + return "EXTRACT (EPOCH FROM $field)"; + break; + + default: + return "UNIX_TIMESTAMP($field)"; + } } - function _get_result($res_id) + function fromunixtime($timestamp) { - if ($res_id==NULL) - $res_id = $this->last_res_id; + switch($this->db_provider) + { + case 'mysqli': + case 'mysql': + case 'sqlite': + return "FROM_UNIXTIME($timestamp)"; + + default: + return date("'Y-m-d H:i:s'", $timestamp); + } + } + + + function _add_result($res) + { + // sql error occured + if (DB::isError($res)) + { + raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, + 'message' => $res->getMessage() . " Query: " . substr(preg_replace('/[\r\n]+\s*/', ' ', $res->userinfo), 0, 1024)), TRUE, FALSE); + return FALSE; + } + else + { + $res_id = sizeof($this->a_query_results); + $this->a_query_results[$res_id] = $res; + $this->last_res_id = $res_id; + return $res_id; + } + } + + + function _get_result($res_id) + { + if ($res_id==NULL) + $res_id = $this->last_res_id; - if ($res_id && isset($this->a_query_results[$res_id])) - return $this->a_query_results[$res_id]; - else - return FALSE; + if ($res_id && isset($this->a_query_results[$res_id])) + return $this->a_query_results[$res_id]; + else + return FALSE; } - // create a sqlite database from a file - function _sqlite_create_database($dbh, $fileName) + // create a sqlite database from a file + function _sqlite_create_database($dbh, $fileName) { - if (empty($fileName) || !is_string($fileName)) - return ; - - $data = ''; - if ($fd = fopen($fileName, 'r')) - { - $data = fread($fd, filesize($fileName)); - fclose($fd); - } - - if (strlen($data)) - sqlite_exec($dbh->connection, $data); + if (empty($fileName) || !is_string($fileName)) + return ; + + $data = ''; + if ($fd = fopen($fileName, 'r')) + { + $data = fread($fd, filesize($fileName)); + fclose($fd); + } + + if (strlen($data)) + sqlite_exec($dbh->connection, $data); } - // transform a query so that it is sqlite2 compliant - function _sqlite_prepare_query($query) + function _sqlite_prepare() { - if (!is_string($query)) - return ($query); - - $search = array('/NOW\(\)/i', '/`/'); - $replace = array("datetime('now')", '"'); - $query = preg_replace($search, $replace, $query); + include_once('include/rcube_sqlite.inc'); - return ($query); + // we emulate via callback some missing MySQL function + sqlite_create_function($this->db_handle->connection, "from_unixtime", "rcube_sqlite_from_unixtime"); + sqlite_create_function($this->db_handle->connection, "unix_timestamp", "rcube_sqlite_unix_timestamp"); + sqlite_create_function($this->db_handle->connection, "now", "rcube_sqlite_now"); + sqlite_create_function($this->db_handle->connection, "md5", "rcube_sqlite_md5"); } - -} + +/* + // transform a query so that it is sqlite2 compliant + function _sqlite_prepare_query($query) + { + if (!is_string($query)) + return ($query); + + + $search = array('/NOW\(\)/i', '/`/'); + $replace = array("datetime('now')", '"'); + $query = preg_replace($search, $replace, $query); + + return ($query); + } +*/ + } // end class rcube_db ?> \ No newline at end of file diff --git a/program/include/rcube_imap.inc b/program/include/rcube_imap.inc index 2237b38f3..ed7c3edcd 100644 --- a/program/include/rcube_imap.inc +++ b/program/include/rcube_imap.inc @@ -28,6 +28,7 @@ require_once('lib/utf7.inc'); class rcube_imap { + var $db; var $conn; var $root_ns = ''; var $root_dir = ''; @@ -38,21 +39,23 @@ class rcube_imap var $caching_enabled = FALSE; var $default_folders = array('inbox', 'drafts', 'sent', 'junk', 'trash'); var $cache = array(); + var $cache_keys = array(); var $cache_changes = array(); var $uid_id_map = array(); var $msg_headers = array(); + var $capabilities = array(); // PHP 5 constructor - function __construct() + function __construct($db_conn) { - + $this->db = $db_conn; } // PHP 4 compatibility - function rcube_imap() + function rcube_imap($db_conn) { - $this->__construct(); + $this->__construct($db_conn); } @@ -95,6 +98,7 @@ class rcube_imap // get account namespace if ($this->conn) { + $this->_parse_capability($this->conn->capability); iil_C_NameSpace($this->conn); if (!empty($this->conn->delimiter)) @@ -185,6 +189,13 @@ class rcube_imap } + function get_capability($cap) + { + $cap = strtoupper($cap); + return $this->capabilities[$cap]; + } + + function get_hierarchy_delimiter() { if ($this->conn && empty($this->delimiter)) @@ -298,200 +309,131 @@ class rcube_imap // private method for listing message header - // by DrSlump - function __list_headers($mailbox='', $page=NULL, $sort_field='date', $sort_order='DESC') + function _list_headers($mailbox='', $page=NULL, $sort_field='date', $sort_order='DESC') { - $a_out = array(); - $cached_count = 0; - if (!strlen($mailbox)) - return $a_out; + return array(); + + $max = $this->_messagecount($mailbox); + $start_msg = ($this->list_page-1) * $this->page_size; + + if ($page=='all') + { + $begin = 0; + $end = $max; + } + else if ($sort_order=='DESC') + { + $begin = $max - $this->page_size - $start_msg; + $end = $max - $start_msg; + } + else + { + $begin = $start_msg; + $end = $start_msg + $this->page_size; + } - $mbox_count = $this->_messagecount($mailbox /*, 'ALL', TRUE*/); + if ($begin < 0) $begin = 0; + if ($end < 0) $end = $max; + if ($end > $max) $end = $max; - $revalidate = false; - if ($mbox_count) +//console("fetch headers $start_msg to ".($start_msg+$this->page_size)." (msg $begin to $end)"); + + $headers_sorted = FALSE; + $cache_key = $mailbox.'.msg'; + $cache_status = $this->check_cache_status($mailbox, $cache_key); + +//console("Cache status = $cache_status"); + + // cache is OK, we can get all messages from local cache + if ($cache_status>0) + { + $a_msg_headers = $this->get_message_cache($cache_key, $start_msg, $start_msg+$this->page_size, $sort_field, $sort_order); + $headers_sorted = TRUE; + } + else { - // get cached headers - $a_out = $this->get_cache($mailbox.'.msg'); - $a_out = is_array($a_out) ? $a_out : array(); // make sure we get an array - - $cached_count = count($a_out); - $a_new = array(); - $revalidate = true; // revalidate by default - - // if the cache count is greater then there have been changes for sure - if ($cached_count <= $mbox_count) + // retrieve headers from IMAP + if ($this->get_capability('sort') && ($msg_index = iil_C_Sort($this->conn, $mailbox, $sort_field))) { - $from = $cached_count?$cached_count:1; - - //get new headers (at least one is returned) - $a_temp = iil_C_FetchHeaders($this->conn, $mailbox, $from . ':' . $mbox_count); - $duplicated = $cached_count?true:false; - - foreach ($a_temp as $hdr) +//console("$mailbox: ".count($msg_index)); + + $msgs = $msg_index[$begin]; + for ($i=$begin; $i < $end; $i++) { - //skip the first one if duplicated - if ($duplicated) - { - //check for changes using the UID - $lastCacheHdr = end($a_out); - if ($hdr->uid === $lastCacheHdr->uid) - $revalidate = false; - - $duplicated = false; - continue; - } - - //skip deleted ones - if (! $hdr->deleted) - $a_new[ $hdr->uid ] = $hdr; + if ($sort_order == 'DESC') + $msgs = $msg_index[$i].','.$msgs; + else + $msgs = $msgs.','.$msg_index[$i]; } - } - //revalidate cache if needed - $to = $mbox_count - count($a_new); - if ($revalidate && $to !== 0) //we'll need to reindex the array so we have to make a copy + $sorted = TRUE; + } + else { - $a_dirty = $a_out; - $a_out = array(); - $a_buffers = array(); + $msgs = sprintf("%d:%d", $begin+1, $end); + $sorted = FALSE; + } - //fetch chunks of 20 headers - $step = 20; - $found = false; - - //fetch headers in blocks starting from new to old - do { - $from = $to-$step; - if ($from < 1) $from = 1; - //store the block in a temporal buffer - $a_buffers[$from] = iil_C_FetchHeaders($this->conn, $mailbox, $from . ':' . $to); + // cache is dirty, sync it + if ($this->caching_enabled && $cache_status==-1) + { + $this->sync_header_index($mailbox); + return $this->_list_headers($mailbox, $page, $sort_field, $sort_order); + } + - //compare the fetched headers with the ones in the cache - $idx = 0; - foreach ($a_buffers[$from] as $k=>$hdr) + // cache is incomplete + $cache_index = $this->get_message_cache_index($cache_key); + + // fetch reuested headers from server + $a_header_index = iil_C_FetchHeaders($this->conn, $mailbox, $msgs); + $a_msg_headers = array(); + + + if (!empty($a_header_index)) + { + foreach ($a_header_index as $i => $headers) + { + if ($headers->deleted) { - //if it's different the comparison ends - if (!isset($a_dirty[$hdr->uid]) || $a_dirty[$hdr->uid]->id !== $hdr->id) - break; + // delete from cache + if ($cache_index[$headers->id] && $cache_index[$headers->id] == $headers->uid) + $this->remove_message_cache($cache_key, $headers->id); - //if we arrive here then we know that the older messages in cache are ok - $found = $hdr->id; - $idx++; + continue; } - //remove from the buffer the headers which are already cached - if ($found) - $a_buffers[$from] = array_splice($a_buffers[$from], 0, $idx ); - - $to = $from-1; - } - while ($found===false && $from > 1); + // add message to cache + if ($this->caching_enabled && $cache_index[$headers->id] != $headers->uid) + $this->add_message_cache($cache_key, $headers->id, $headers); - //just keep the headers we are certain that didn't change in the cache - if ($found !== false) - { - foreach ($a_dirty as $hdr) - { - if ($hdr->id > $found) break; - $a_out[$hdr->uid] = $hdr; - } - } - - //we builded the block buffers from new to older, we process them in reverse order - ksort($a_buffers, SORT_NUMERIC); - foreach ($a_buffers as $a_buff) - { - foreach ($a_buff as $hdr) - { - if (! $hdr->deleted) - $a_out[$hdr->uid] = $hdr; - } + $a_msg_headers[$headers->uid] = $headers; } } - - //array_merge() would reindex the keys, so we use this 'hack' - $a_out += $a_new; - } - - //write headers list to cache if needed - if ($revalidate || count($a_out)!=$cached_count) { - $this->update_cache($mailbox.'.msg', $a_out); - } - - //sort headers by a specific col - $a_out = iil_SortHeaders( $a_out, $sort_field, $sort_order ); - - // return complete list of messages - if (strtolower($page)=='all') - return $a_out; - $start_msg = ($this->list_page-1) * $this->page_size; - return array_slice($a_out, $start_msg, $this->page_size); - } + // delete cached messages with a higher index than $max + $this->clear_message_cache($cache_key, $max); - // original function; replaced 2005/10/18 - // private method for listing message header - function _list_headers($mailbox='', $page=NULL, $sort_field='date', $sort_order='DESC') - { - $max = $this->_messagecount($mailbox); - - if (!strlen($mailbox)) - return array(); - - // get cached headers - $a_msg_headers = $this->get_cache($mailbox.'.msg'); - - // retrieve headers from IMAP - if (!is_array($a_msg_headers) || sizeof($a_msg_headers) != $max) - { - $a_header_index = iil_C_FetchHeaders($this->conn, $mailbox, "1:$max"); - $a_msg_headers = array(); - - if (!empty($a_header_index)) - foreach ($a_header_index as $i => $headers) - if (!$headers->deleted) - $a_msg_headers[$headers->uid] = $headers; - } - else - $headers_cached = TRUE; - - if (!is_array($a_msg_headers)) - return array(); - - // sort headers by a specific col - $a_headers = iil_SortHeaders($a_msg_headers, $sort_field, $sort_order); - $headers_count = count($a_headers); - - // free memory - unset($a_msg_headers); - - // write headers list to cache - if (!$headers_cached) - $this->update_cache($mailbox.'.msg', $a_headers); + // kick child process to sync cache - // update message count cache - $a_mailbox_cache = $this->get_cache('messagecount'); - if (isset($a_mailbox_cache[$mailbox]['ALL']) && $a_mailbox_cache[$mailbox]['ALL'] != $headers_count) - { - $a_mailbox_cache[$mailbox]['ALL'] = (int)$headers_count; - $this->update_cache('messagecount', $a_mailbox_cache); } - if (empty($a_headers)) + + // return empty array if no messages found + if (!is_array($a_msg_headers) || empty($a_msg_headers)) return array(); - - // return complete list of messages - if (strtolower($page)=='all') - return $a_headers; - $start_msg = ($this->list_page-1) * $this->page_size; - return array_slice($a_headers, $start_msg, $this->page_size); + + // if not already sorted + if (!$headers_sorted) + $a_msg_headers = iil_SortHeaders($a_msg_headers, $sort_field, $sort_order); + + return array_values($a_msg_headers); } - + // return sorted array of message UIDs function message_index($mbox='', $sort_field='date', $sort_order='DESC') @@ -510,9 +452,54 @@ class rcube_imap } - function sync_header_index($mbox=NULL) + function sync_header_index($mailbox) { - + $cache_key = $mailbox.'.msg'; + $cache_index = $this->get_message_cache_index($cache_key); + $msg_count = $this->_messagecount($mailbox); + + // fetch complete message index + $a_message_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, "1:$msg_count", 'UID'); + + foreach ($a_message_index as $id => $uid) + { + // message in cache at correct position + if ($cache_index[$id] == $uid) + { +// console("$id / $uid: OK"); + unset($cache_index[$id]); + continue; + } + + // message in cache but in wrong position + if (in_array((string)$uid, $cache_index, TRUE)) + { +// console("$id / $uid: Moved"); + unset($cache_index[$id]); + } + + // other message at this position + if (isset($cache_index[$id])) + { +// console("$id / $uid: Delete"); + $this->remove_message_cache($cache_key, $id); + unset($cache_index[$id]); + } + + +// console("$id / $uid: Add"); + + // fetch complete headers and add to cache + $headers = iil_C_FetchHeader($this->conn, $mailbox, $id); + $this->add_message_cache($cache_key, $headers->id, $headers); + } + + // those ids that are still in cache_index have been deleted + if (!empty($cache_index)) + { + foreach ($cache_index as $id => $uid) + $this->remove_message_cache($cache_key, $id); + } } @@ -527,22 +514,19 @@ class rcube_imap function get_headers($uid, $mbox=NULL) { $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox; - + // get cached headers - $a_msg_headers = $this->get_cache($mailbox.'.msg'); - - // return cached header - if ($a_msg_headers[$uid]) - return $a_msg_headers[$uid]; + if ($headers = $this->get_cached_message($mailbox.'.msg', $uid)) + return $headers; $msg_id = $this->_uid2id($uid); - $header = iil_C_FetchHeader($this->conn, $mailbox, $msg_id); + $headers = iil_C_FetchHeader($this->conn, $mailbox, $msg_id); // write headers cache - $a_msg_headers[$uid] = $header; - $this->update_cache($mailbox.'.msg', $a_msg_headers); + if ($headers) + $this->add_message_cache($mailbox.'.msg', $msg_id, $headers); - return $header; + return $headers; } @@ -595,19 +579,20 @@ class rcube_imap // reload message headers if cached $cache_key = $this->mailbox.'.msg'; - if ($this->caching_enabled && $result && ($a_cached_headers = $this->get_cache($cache_key))) + if ($this->caching_enabled) { - // close and re-open connection - $this->reconnect(); - - foreach ($uids as $uid) + foreach ($msg_ids as $id) { - if (isset($a_cached_headers[$uid])) + if ($cached_headers = $this->get_cached_message($cache_key, $id)) { - unset($this->cache[$cache_key][$uid]); - $this->get_headers($uid); + $this->remove_message_cache($cache_key, $id); + //$this->get_headers($uid); } } + + // close and re-open connection + // this prevents connection problems with Courier + $this->reconnect(); } // set nr of messages that were flaged @@ -633,7 +618,7 @@ class rcube_imap // make shure mailbox exists if (in_array($mailbox, $this->_list_mailboxes())) $saved = iil_C_Append($this->conn, $mailbox, $message); - + if ($saved) { // increase messagecount of the target mailbox @@ -672,20 +657,24 @@ class rcube_imap // really deleted from the source mailbox if ($moved) { - $this->expunge($from_mbox, FALSE); - $this->clear_cache($to_mbox.'.msg'); + $this->_expunge($from_mbox, FALSE); $this->_clear_messagecount($from_mbox); $this->_clear_messagecount($to_mbox); } // update cached message headers $cache_key = $from_mbox.'.msg'; - if ($moved && ($a_cached_headers = $this->get_cache($cache_key))) + if ($moved && ($a_cache_index = $this->get_message_cache_index($cache_key))) { + $start_index = 100000; foreach ($a_uids as $uid) - unset($a_cached_headers[$uid]); + { + $index = array_search($uid, $a_cache_index); + $start_index = min($index, $start_index); + } - $this->update_cache($cache_key, $a_cached_headers); + // clear cache from the lowest index on + $this->clear_message_cache($cache_key, $start_index); } return $moved; @@ -716,17 +705,23 @@ class rcube_imap // really deleted from the mailbox if ($deleted) { - $this->expunge($mailbox, FALSE); + $this->_expunge($mailbox, FALSE); $this->_clear_messagecount($mailbox); } // remove deleted messages from cache - if ($deleted && ($a_cached_headers = $this->get_cache($mailbox.'.msg'))) + $cache_key = $mailbox.'.msg'; + if ($deleted && ($a_cache_index = $this->get_message_cache_index($cache_key))) { + $start_index = 100000; foreach ($a_uids as $uid) - unset($a_cached_headers[$uid]); + { + $index = array_search($uid, $a_cache_index); + $start_index = min($index, $start_index); + } - $this->update_cache($mailbox.'.msg', $a_cached_headers); + // clear cache from the lowest index on + $this->clear_message_cache($cache_key, $start_index); } return $deleted; @@ -740,7 +735,10 @@ class rcube_imap $msg_count = $this->_messagecount($mailbox, 'ALL'); if ($msg_count>0) + { + $this->clear_message_cache($mailbox.'.msg'); return iil_C_ClearFolder($this->conn, $mailbox); + } else return 0; } @@ -750,12 +748,18 @@ class rcube_imap function expunge($mbox='', $clear_cache=TRUE) { $mailbox = $mbox ? $this->_mod_mailbox($mbox) : $this->mailbox; - + return $this->_expunge($mailbox, $clear_cache); + } + + + // send IMAP expunge command and clear cache + function _expunge($mailbox, $clear_cache=TRUE) + { $result = iil_C_Expunge($this->conn, $mailbox); if ($result>=0 && $clear_cache) { - $this->clear_cache($mailbox.'.msg'); + //$this->clear_message_cache($mailbox.'.msg'); $this->_clear_messagecount($mailbox); } @@ -763,7 +767,6 @@ class rcube_imap } - /* -------------------------------- * folder managment * --------------------------------*/ @@ -824,13 +827,18 @@ class rcube_imap function create_mailbox($name, $subscribe=FALSE) { $result = FALSE; + + // replace backslashes + $name = preg_replace('/[\\\]+/', '-', $name); + $name_enc = UTF7EncodeString($name); + + // reduce mailbox name to 100 chars + $name_enc = substr($name_enc, 0, 100); + $abs_name = $this->_mod_mailbox($name_enc); $a_mailbox_cache = $this->get_cache('mailboxes'); - - //if (strlen($this->root_ns)) - // $abs_name = $this->root_ns.$abs_name; - + if (strlen($abs_name) && (!is_array($a_mailbox_cache) || !in_array($abs_name, $a_mailbox_cache))) $result = iil_C_CreateFolder($this->conn, $abs_name); @@ -875,37 +883,41 @@ class rcube_imap // clear mailboxlist cache if ($deleted) + { + $this->clear_message_cache($mailbox.'.msg'); $this->clear_cache('mailboxes'); + } - return $updated; + return $deleted; } /* -------------------------------- - * internal caching functions + * internal caching methods * --------------------------------*/ function set_caching($set) { - if ($set && function_exists('rcube_read_cache')) + if ($set && is_object($this->db)) $this->caching_enabled = TRUE; else $this->caching_enabled = FALSE; } + function get_cache($key) { // read cache if (!isset($this->cache[$key]) && $this->caching_enabled) { - $cache_data = rcube_read_cache('IMAP.'.$key); + $cache_data = $this->_read_cache_record('IMAP.'.$key); $this->cache[$key] = strlen($cache_data) ? unserialize($cache_data) : FALSE; } - return $this->cache[$key]; + return $this->cache[$key]; } @@ -924,7 +936,7 @@ class rcube_imap foreach ($this->cache as $key => $data) { if ($this->cache_changes[$key]) - rcube_write_cache('IMAP.'.$key, serialize($data)); + $this->_write_cache_record('IMAP.'.$key, serialize($data)); } } } @@ -935,7 +947,7 @@ class rcube_imap if ($key===NULL) { foreach ($this->cache as $key => $data) - rcube_clear_cache('IMAP.'.$key); + $this->_clear_cache_record('IMAP.'.$key); $this->cache = array(); $this->cache_changed = FALSE; @@ -943,7 +955,7 @@ class rcube_imap } else { - rcube_clear_cache('IMAP.'.$key); + $this->_clear_cache_record('IMAP.'.$key); $this->cache_changes[$key] = FALSE; unset($this->cache[$key]); } @@ -951,8 +963,276 @@ class rcube_imap + function _read_cache_record($key) + { + $cache_data = FALSE; + + if ($this->db) + { + // get cached data from DB + $sql_result = $this->db->query( + "SELECT cache_id, data + FROM ".get_table_name('cache')." + WHERE user_id=? + AND cache_key=?", + $_SESSION['user_id'], + $key); + + if ($sql_arr = $this->db->fetch_assoc($sql_result)) + { + $cache_data = $sql_arr['data']; + $this->cache_keys[$key] = $sql_arr['cache_id']; + } + } + + return $cache_data; + } + + + function _write_cache_record($key, $data) + { + if (!$this->db) + return FALSE; + + // check if we already have a cache entry for this key + if (!isset($this->cache_keys[$key])) + { + $sql_result = $this->db->query( + "SELECT cache_id + FROM ".get_table_name('cache')." + WHERE user_id=? + AND cache_key=?", + $_SESSION['user_id'], + $key); + + if ($sql_arr = $this->db->fetch_assoc($sql_result)) + $this->cache_keys[$key] = $sql_arr['cache_id']; + else + $this->cache_keys[$key] = FALSE; + } + + // update existing cache record + if ($this->cache_keys[$key]) + { + $this->db->query( + "UPDATE ".get_table_name('cache')." + SET created=now(), + data=? + WHERE user_id=? + AND cache_key=?", + $data, + $_SESSION['user_id'], + $key); + } + // add new cache record + else + { + $this->db->query( + "INSERT INTO ".get_table_name('cache')." + (created, user_id, cache_key, data) + VALUES (now(), ?, ?, ?)", + $_SESSION['user_id'], + $key, + $data); + } + } + + + function _clear_cache_record($key) + { + $this->db->query( + "DELETE FROM ".get_table_name('cache')." + WHERE user_id=? + AND cache_key=?", + $_SESSION['user_id'], + $key); + } + + + + /* -------------------------------- + * message caching methods + * --------------------------------*/ + + + // checks if the cache is up-to-date + // return: -3 = off, -2 = incomplete, -1 = dirty + function check_cache_status($mailbox, $cache_key) + { + if (!$this->caching_enabled) + return -3; + + $cache_index = $this->get_message_cache_index($cache_key, TRUE); + $msg_count = $this->_messagecount($mailbox); + $cache_count = count($cache_index); + + // console("Cache check: $msg_count !== ".count($cache_index)); + + if ($cache_count==$msg_count) + { + // get highest index + $header = iil_C_FetchHeader($this->conn, $mailbox, "$msg_count"); + $cache_uid = array_pop($cache_index); + + // uids of highes message matches -> cache seems OK + if ($cache_uid == $header->uid) + return 1; + + // cache is dirty + return -1; + } + // if cache count differs less that 10% report as dirty + else if (abs($msg_count - $cache_count) < $msg_count/10) + return -1; + else + return -2; + } + + + + function get_message_cache($key, $from, $to, $sort_field, $sort_order) + { + $cache_key = "$key:$from:$to:$sort_field:$sort_order"; + $db_header_fields = array('idx', 'uid', 'subject', 'from', 'to', 'cc', 'date', 'size'); + + if (!in_array($sort_field, $db_header_fields)) + $sort_field = 'idx'; + + if ($this->caching_enabled && !isset($this->cache[$cache_key])) + { + $this->cache[$cache_key] = array(); + $sql_result = $this->db->limitquery( + "SELECT idx, uid, headers + FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=? + ORDER BY ".$this->db->quoteIdentifier($sort_field)." ". + strtoupper($sort_order), + $from, + $to-$from, + $_SESSION['user_id'], + $key); + + while ($sql_arr = $this->db->fetch_assoc($sql_result)) + { + $uid = $sql_arr['uid']; + $this->cache[$cache_key][$uid] = unserialize($sql_arr['headers']); + } + } + + return $this->cache[$cache_key]; + } + + + function get_cached_message($key, $uid, $body=FALSE) + { + if (!$this->caching_enabled) + return FALSE; + + $internal_key = '__single_msg'; + if ($this->caching_enabled && (!isset($this->cache[$internal_key][$uid]) || $body)) + { + $sql_select = "idx, uid, headers"; + if ($body) + $sql_select .= ", body"; + + $sql_result = $this->db->query( + "SELECT $sql_select + FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=? + AND uid=?", + $_SESSION['user_id'], + $key, + $uid); + + if ($sql_arr = $this->db->fetch_assoc($sql_result)) + { + $headers = unserialize($sql_arr['headers']); + if (is_object($headers) && !empty($sql_arr['body'])) + $headers->body = $sql_arr['body']; + + $this->cache[$internal_key][$uid] = $headers; + } + } + + return $this->cache[$internal_key][$uid]; + } + + + function get_message_cache_index($key, $force=FALSE) + { + static $sa_message_index = array(); + + if (!empty($sa_message_index[$key]) && !$force) + return $sa_message_index[$key]; + + $sa_message_index[$key] = array(); + $sql_result = $this->db->query( + "SELECT idx, uid + FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=? + ORDER BY idx ASC", + $_SESSION['user_id'], + $key); + + while ($sql_arr = $this->db->fetch_assoc($sql_result)) + $sa_message_index[$key][$sql_arr['idx']] = $sql_arr['uid']; + + return $sa_message_index[$key]; + } + + + function add_message_cache($key, $index, $headers) + { + $this->db->query( + "INSERT INTO ".get_table_name('messages')." + (user_id, del, cache_key, idx, uid, subject, ".$this->db->quoteIdentifier('from').", ".$this->db->quoteIdentifier('to').", cc, date, size, headers) + VALUES (?, 0, ?, ?, ?, ?, ?, ?, ?, ".$this->db->fromunixtime($headers->timestamp).", ?, ?)", + $_SESSION['user_id'], + $key, + $index, + $headers->uid, + $this->decode_header($headers->subject, TRUE), + $this->decode_header($headers->from, TRUE), + $this->decode_header($headers->to, TRUE), + $this->decode_header($headers->cc, TRUE), + $headers->size, + serialize($headers)); + } + + + function remove_message_cache($key, $index) + { + $this->db->query( + "DELETE FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=? + AND idx=?", + $_SESSION['user_id'], + $key, + $index); + } + + + function clear_message_cache($key, $start_index=1) + { + $this->db->query( + "DELETE FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=? + AND idx>=?", + $_SESSION['user_id'], + $key, + $start_index); + } + + + + /* -------------------------------- - * encoding/decoding functions + * encoding/decoding methods * --------------------------------*/ @@ -986,9 +1266,15 @@ class rcube_imap } - function decode_header($input) + function decode_header($input, $remove_quotes=FALSE) { - return $this->decode_mime_string($input); + $str = $this->decode_mime_string($input); + if ($str{0}=='"' && $remove_quotes) + { + $str = str_replace('"', '', $str); + } + + return $str; } @@ -1094,6 +1380,7 @@ class rcube_imap } + /* -------------------------------- * private methods * --------------------------------*/ @@ -1149,6 +1436,33 @@ class rcube_imap } + // parse string or array of server capabilities and put them in internal array + function _parse_capability($caps) + { + if (!is_array($caps)) + $cap_arr = explode(' ', $caps); + else + $cap_arr = $caps; + + foreach ($cap_arr as $cap) + { + if ($cap=='CAPABILITY') + continue; + + if (strpos($cap, '=')>0) + { + list($key, $value) = explode('=', $cap); + if (!is_array($this->capabilities[$key])) + $this->capabilities[$key] = array(); + + $this->capabilities[$key][] = $value; + } + else + $this->capabilities[$cap] = TRUE; + } + } + + // subscribe/unsubscribe a list of mailboxes and update local cache function _change_subscription($a_mboxes, $mode) { diff --git a/program/include/rcube_mdb2.inc b/program/include/rcube_mdb2.inc index 35973ad5d..54a40e796 100755 --- a/program/include/rcube_mdb2.inc +++ b/program/include/rcube_mdb2.inc @@ -238,6 +238,22 @@ class rcube_db } + function format_date($timestamp) + { + switch($this->db_provider) + { + case 'mysqli': + case 'mysql': + return "FROM_UNIXTIME($timestamp)"; + break; + case 'sqlite': + return "datetime('$timestamp')"; + break; + default: + return date("Y-m-d H:i:s", $timestamp); + } + } + function _add_result($res, $query) { // sql error occured diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc index 400e345c0..75db7603f 100644 --- a/program/include/rcube_shared.inc +++ b/program/include/rcube_shared.inc @@ -1185,99 +1185,6 @@ function send_future_expire_header() } -// replace specials characters to a specific encoding type -function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) - { - global $OUTPUT_TYPE, $CHARSET; - static $html_encode_arr, $js_rep_table, $rtf_rep_table, $xml_rep_table; - - if (!$enctype) - $enctype = $GLOBALS['OUTPUT_TYPE']; - - // convert nbsps back to normal spaces if not html - if ($enctype!='html') - $str = str_replace(chr(160), ' ', $str); - - - // encode for plaintext - if ($enctype=='text') - return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str); - - // encode for HTML output - if ($enctype=='html') - { - if (!$html_encode_arr) - { - if ($CHARSET=='ISO-8859-1') - { - $html_encode_arr = get_html_translation_table(HTML_ENTITIES); - $html_encode_arr[chr(128)] = '€'; - } - else - $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS); - - unset($html_encode_arr['?']); - unset($html_encode_arr['&']); - } - - $ltpos = strpos($str, '<'); - $encode_arr = $html_encode_arr; - - // don't replace quotes and html tags - if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false) - { - unset($encode_arr['"']); - unset($encode_arr['<']); - unset($encode_arr['>']); - } - else if ($mode=='remove') - $str = strip_tags($str); - - $out = strtr($str, $encode_arr); - - return $newlines ? nl2br($out) : $out; - } - - - if ($enctype=='url') - return rawurlencode($str); - - - // if the replace tables for RTF, XML and JS are not yet defined - if (!$js_rep_table) - { - $js_rep_table = $rtf_rep_table = $xml_rep_table = array(); - - for ($c=160; $c<256; $c++) // can be increased to support more charsets - { - $hex = dechex($c); - $rtf_rep_table[Chr($c)] = "\\'$hex"; - $xml_rep_table[Chr($c)] = "&#$c;"; - - if ($CHARSET=='ISO-8859-1') - $js_rep_table[Chr($c)] = sprintf("\u%s%s", str_repeat('0', 4-strlen($hex)), $hex); - } - - $js_rep_table['"'] = sprintf("\u%s%s", str_repeat('0', 4-strlen(dechex(34))), dechex(34)); - $xml_rep_table['"'] = '"'; - } - - // encode for RTF - if ($enctype=='xml') - return strtr($str, $xml_rep_table); - - // encode for javascript use - if ($enctype=='js') - return preg_replace(array("/\r\n/", '/"/', "/([^\\\])'/"), array('\n', '\"', "$1\'"), strtr($str, $js_rep_table)); - - // encode for RTF - if ($enctype=='rtf') - return preg_replace("/\r\n/", "\par ", strtr($str, $rtf_rep_table)); - - // no encoding given -> return original string - return $str; - } - function decode_specialchars($input, $charset='') { @@ -1462,7 +1369,21 @@ function abbrevate_string($str, $maxlength, $place_holder='...') return $str; } - + + +// delete all files within a folder +function clear_directory($dir_path) + { + $dir = @opendir($dir_path); + if(!$dir) return FALSE; + + while ($file = readdir($dir)) + if (strlen($file)>2) + unlink("$dir_path/$file"); + + closedir($dir); + return TRUE; + } ?> \ No newline at end of file diff --git a/program/include/rcube_sqlite.inc b/program/include/rcube_sqlite.inc new file mode 100644 index 000000000..78c671d38 --- /dev/null +++ b/program/include/rcube_sqlite.inc @@ -0,0 +1,71 @@ + | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +function rcube_sqlite_from_unixtime($timestamp) + { + $timestamp = trim($timestamp); + if (!preg_match("/^[0-9]+$/is", $timestamp)) + $ret = strtotime($timestamp); + else + $ret = $timestamp; + + $ret = date("Y-m-d H:i:s", $ret); + rcube_sqlite_debug("FROM_UNIXTIME ($timestamp) = $ret"); + return $ret; + } + + +function rcube_sqlite_unix_timestamp($timestamp="") + { + $timestamp = trim($timestamp); + if (!$timestamp) + $ret = time(); + else if (!preg_match("/^[0-9]+$/is", $timestamp)) + $ret = strtotime($timestamp); + else + $ret = $timestamp; + + rcube_sqlite_debug("UNIX_TIMESTAMP ($timestamp) = $ret"); + return $ret; + } + + +function rcube_sqlite_now() + { + rcube_sqlite_debug("NOW() = ".date("Y-m-d H:i:s")); + return date("Y-m-d H:i:s"); + } + + +function rcube_sqlite_md5($str) + { + return md5($str); + } + + +function rcube_sqlite_debug($str) + { + //console($str); + } + +?> \ No newline at end of file diff --git a/program/include/session.inc b/program/include/session.inc index 54ed798fc..f10a2b37e 100644 --- a/program/include/session.inc +++ b/program/include/session.inc @@ -45,7 +45,7 @@ function sess_read($key) if ($sql_arr = $DB->fetch_assoc($sql_result)) { - $SESS_CHANGED = $sql_arr['changed']; + $SESS_CHANGED = mktime(); //$sql_arr['changed']; if (strlen($sql_arr['vars'])) return $sql_arr['vars']; @@ -59,7 +59,7 @@ function sess_read($key) function sess_write($key, $vars) { global $DB; - + $sql_result = $DB->query("SELECT 1 FROM ".get_table_name('session')." WHERE sess_id=?", @@ -83,6 +83,8 @@ function sess_write($key, $vars) $key, $vars, $_SERVER['REMOTE_ADDR']); + + } return TRUE; @@ -102,7 +104,9 @@ function sess_destroy($key) $DB->query("DELETE FROM ".get_table_name('session')." WHERE sess_id=?", $key); - + + rcmail_clear_session_temp($key); + return TRUE; } @@ -115,7 +119,7 @@ function sess_gc($maxlifetime) // get all expired sessions $sql_result = $DB->query("SELECT sess_id FROM ".get_table_name('session')." - WHERE ".$DB->unixtimestamp('now()')."-".$DB->unixtimestamp('created')." > ?", + WHERE ".$DB->unixtimestamp('now()')."-".$DB->unixtimestamp('changed')." > ?", $maxlifetime); $a_exp_sessions = array(); @@ -134,6 +138,10 @@ function sess_gc($maxlifetime) WHERE sess_id IN ('".join("','", $a_exp_sessions)."')"); } + // remove session specific temp dirs + foreach ($a_exp_sessions as $key) + rcmail_clear_session_temp($key); + return TRUE; } diff --git a/program/js/app.js b/program/js/app.js index 095c89d3a..d65af420e 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -210,7 +210,7 @@ function rcube_webmail() 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 @@ function rcube_webmail() 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 @@ function rcube_webmail() 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,9 +466,18 @@ function rcube_webmail() // 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 @@ function rcube_webmail() 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 @@ function rcube_webmail() 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 @@ function rcube_webmail() 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 @@ function rcube_webmail() // 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 @@ function rcube_webmail() } 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 @@ function rcube_webmail() /*********************************************************/ /********* 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,19 +1900,23 @@ function rcube_webmail() { 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'; + }; + + this.subscribe_folder = function(folder) { var form; @@ -2491,6 +2541,21 @@ function rcube_webmail() 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 4bbb14c95..12ec06e8c 100644 --- a/program/localization/de/labels.inc +++ b/program/localization/de/labels.inc @@ -109,6 +109,7 @@ $labels['unread'] = 'Ungelesene'; $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['high'] = 'Hoch'; $labels['highest'] = 'Höchste'; $labels['nosubject'] = '(kein Betreff)'; - $labels['showimages'] = 'Bilder anzeigen'; @@ -165,7 +165,7 @@ $labels['setdefault'] = 'Als Standard'; $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 e8eabe10a..145c7b4a7 100644 --- a/program/localization/de/messages.inc +++ b/program/localization/de/messages.inc @@ -54,6 +54,8 @@ $messages['sendingfailed'] = 'Versand der Nachricht fehlgeschlagen'; $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 000000000..76050726f --- /dev/null +++ b/program/localization/ee/labels.inc @@ -0,0 +1,182 @@ + | + +-----------------------------------------------------------------------+ + + $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 000000000..6aeff2834 --- /dev/null +++ b/program/localization/ee/messages.inc @@ -0,0 +1,80 @@ + | + +-----------------------------------------------------------------------+ + + $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 35813cdb0..b37b48140 100644 --- a/program/localization/en/labels.inc +++ b/program/localization/en/labels.inc @@ -109,6 +109,7 @@ $labels['unread'] = 'Unread'; $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['high'] = 'High'; $labels['highest'] = 'Highest'; $labels['nosubject'] = '(no subject)'; - $labels['showimages'] = 'Display images'; @@ -165,7 +165,7 @@ $labels['setdefault'] = 'Set default'; $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 1ce6bba6d..d0286e229 100644 --- a/program/localization/en/messages.inc +++ b/program/localization/en/messages.inc @@ -60,13 +60,13 @@ $messages['errormoving'] = 'Could not move the message'; $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 d1a274971..cd9b5032d 100644 --- a/program/localization/en_GB/labels.inc +++ b/program/localization/en_GB/labels.inc @@ -120,6 +120,7 @@ $labels['normal'] = 'Normal'; $labels['high'] = 'High'; $labels['highest'] = 'Highest'; +$labels['nosubject'] = '(no subject)'; $labels['showimages'] = 'Display images'; @@ -163,7 +164,7 @@ $labels['setdefault'] = 'Set default'; $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 3386ebd68..82424e602 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 a3fcefbf7..b95988d12 100644 --- a/program/steps/addressbook/delete.inc +++ b/program/steps/addressbook/delete.inc @@ -24,7 +24,7 @@ $REMOTE_REQUEST = TRUE; 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 @@ if ($_GET['_cid']) // 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 @@ if ($_GET['_cid']) // 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 feb794f4d..a129d0094 100644 --- a/program/steps/addressbook/edit.inc +++ b/program/steps/addressbook/edit.inc @@ -26,7 +26,7 @@ if (($_GET['_cid'] || $_POST['_cid']) && $_action=='edit') $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 4cc79bad6..8065219b0 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -43,7 +43,7 @@ function rcmail_contacts_list($attrib) // 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 @@ function rcmail_contacts_list($attrib) // 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 @@ function rcmail_get_rowcount_text($max=NULL) 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 ecb634b6f..0aa4873b3 100644 --- a/program/steps/addressbook/list.inc +++ b/program/steps/addressbook/list.inc @@ -24,7 +24,7 @@ $REMOTE_REQUEST = TRUE; // 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 @@ $start_row = ($CONTACTS_LIST['page']-1) * $CONFIG['pagesize']; // 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 2f54e435b..f5ba139b9 100644 --- a/program/steps/addressbook/save.inc +++ b/program/steps/addressbook/save.inc @@ -52,7 +52,7 @@ if ($_POST['_cid']) 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 @@ if ($_POST['_cid']) $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 @@ else $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 @@ else 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 83db486cc..960ea1c45 100644 --- a/program/steps/addressbook/show.inc +++ b/program/steps/addressbook/show.inc @@ -26,7 +26,7 @@ if ($_GET['_cid'] || $_POST['_cid']) $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 ad1544e71..b1129ec9d 100644 --- a/program/steps/mail/addcontact.inc +++ b/program/steps/mail/addcontact.inc @@ -32,7 +32,7 @@ if ($_GET['_address']) $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 @@ if ($_GET['_address']) 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 9509b492b..45f111263 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -88,50 +88,20 @@ function rcmail_compose_headers($attrib) 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 rcmail_compose_headers($attrib) } -/*function rcube_compose_headers($attrib) - { - global $CONFIG, $OUTPUT; - - list($form_start, $form_end) = get_form_tags($attrib); - - // 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'); - - $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)); - - $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']); - - - $out = << - -$labels[from] -$fields[from] - - - -$labels[to] -$fields[to] - +function rcmail_compose_header_from($attrib) + { + 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; + + // 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']; + } -$labels[cc] -$fields[cc] + 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']; + } + } + } - + // 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']); -$labels[bcc] -$fields[bcc] + // 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']; -$labels[replyto] -$fields[replyto] + $out = $select_from->show($from_id); + - -$form_end -EOF; + // 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; - return $out; + return $out; } -*/ + function rcmail_compose_body($attrib) { @@ -360,6 +350,14 @@ function rcmail_create_reply_body($body) $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 @@ function rcmail_charset_pulldown($selected='ISO-8859-1') $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 ca72f7437..c81dd2fd2 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -305,26 +305,41 @@ function rcmail_message_list($attrib) // 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 = '  '; - - // asc link - if (!empty($attrib['sortascbutton'])) + // have buttons configured + if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton'])) { - $sort .= rcube_button(array('command' => 'sort', - 'prop' => $col.'_ASC', - 'image' => $attrib['sortascbutton'], - 'title' => 'sortasc')); - } + $sort = '  '; + + // 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('%s', + $JS_OBJECT_NAME, + $col, + rcube_label('sortby'), + $col_name); } } @@ -1128,20 +1143,76 @@ function rcmail_first_text_part($message_parts) // 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 + $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; + - // get message ID if uid is given - $headers = $IMAP->get_headers($uid); $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; + + // message is cached in temp dir + if (is_dir($cache_dir) && is_file($cache_path)) + { + 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; - // get message from server and cache it - if (!$msg_source) + // attempt to write a file with the message body + if ($dir && ($fp = fopen($cache_path, 'w'))) + { + fwrite($fp, $msg_source); + fclose($fp); + } + else { - $msg_source = $IMAP->get_raw_body($uid); - rcube_write_cache($message_id, $msg_source, TRUE); + 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 11fb559dd..8ec30b0a7 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -46,7 +46,7 @@ function rcmail_get_identity($id) 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 @@ if (empty($CHARSET)) $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 @@ if (is_array($_FILES['_attachments']['tmp_name'])) 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 3bca860b3..b8f9f526c 100644 --- a/program/steps/settings/delete_identity.inc +++ b/program/steps/settings/delete_identity.inc @@ -24,7 +24,7 @@ $REMOTE_REQUEST = $_GET['_remote'] ? TRUE : FALSE; 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 6649c209a..07cd8fa02 100644 --- a/program/steps/settings/edit_identity.inc +++ b/program/steps/settings/edit_identity.inc @@ -25,7 +25,7 @@ if (($_GET['_iid'] || $_POST['_iid']) && $_action=='edit-identity') $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 @@ function rcube_identity_form($attrib) '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 01b692395..b33f8d434 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -146,9 +146,9 @@ function rcmail_identities_list($attrib) // 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 04b2a461e..33d83dfff 100644 --- a/program/steps/settings/manage_folders.inc +++ b/program/steps/settings/manage_folders.inc @@ -69,10 +69,15 @@ else if ($_action=='create-folder') 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 @@ function rcube_create_folder_form($attrib) } +// 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 2e42987bf..dc61b7875 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 @@ if ($_POST['_iid']) 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 @@ if ($_POST['_iid']) // 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 @@ else (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 000000000..244db104c Binary files /dev/null and b/skins/default/images/sort_asc.gif differ diff --git a/skins/default/images/sort_desc.gif b/skins/default/images/sort_desc.gif new file mode 100644 index 000000000..2427311b4 Binary files /dev/null and b/skins/default/images/sort_desc.gif differ diff --git a/skins/default/mail.css b/skins/default/mail.css index f429407dd..9d507fe7b 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 @@ -332,6 +333,26 @@ body.messagelist 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 { height: 16px !important; @@ -645,9 +666,10 @@ div.message-part pre #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; @@ -702,4 +724,3 @@ div.message-part pre margin-top: 8px; } - diff --git a/skins/default/pngbehavior.htc b/skins/default/pngbehavior.htc index 553699a2f..24bfd4c79 100644 --- a/skins/default/pngbehavior.htc +++ b/skins/default/pngbehavior.htc @@ -40,7 +40,7 @@ function fixImage() { 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 aa28098c8..0a3f874ae 100644 --- a/skins/default/templates/compose.html +++ b/skins/default/templates/compose.html @@ -87,8 +87,9 @@ function rcmail_toggle_display(id) - - + +
+ diff --git a/skins/default/templates/mail.html b/skins/default/templates/mail.html index 61d0e6d52..0f87c635d 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" />
-- cgit v1.2.3