From 8bb10dbda276f4c5f1050a05786ba5a5a6cc787d Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=A9my=20Bobbio?= Date: Sat, 18 Jun 2011 16:57:50 +0200 Subject: [PATCH] Imported Upstream version 0.1~rc1~dfsg --- ._CHANGELOG | Bin 177 -> 0 bytes CHANGELOG | 361 ++- INSTALL | 16 +- README | 50 +- SQL/mssql.initial.sql | 206 ++ SQL/mysql.initial.sql | 9 +- SQL/mysql.update-0.1a.sql | 51 + SQL/mysql.update.sql | 72 +- SQL/mysql5.initial.sql | 32 +- SQL/postgres.initial.sql | 6 +- SQL/postgres.update.sql | 9 + SQL/sqlite.initial.sql | 7 +- SQL/sqlite.update.sql | 27 + UPGRADING | 184 +- bin/html2text.php | 16 + bin/quotaimg.php | 169 ++ config/db.inc.php.dist | 4 +- config/main.inc.php.dist | 61 +- index.php | 189 +- program/blocked.gif | Bin 0 -> 118 bytes program/include/bugs.inc | 5 +- program/include/main.inc | 1074 ++++---- program/include/rcmail_template.inc | 631 +++++ program/include/rcube_contacts.inc | 429 +++ program/include/rcube_db.inc | 46 +- program/include/rcube_imap.inc | 694 ++++- program/include/rcube_ldap.inc | 571 ++-- program/include/rcube_mdb2.inc | 36 +- program/include/rcube_shared.inc | 380 ++- program/include/rcube_smtp.inc | 59 +- program/include/session.inc | 53 +- program/js/app.js | 2307 +++++++---------- program/js/common.js | 199 +- program/js/googiespell.js | 2 +- program/js/list.js | 711 +++++ program/lib/Mail/mime.php | 437 ++-- program/lib/Mail/mimeDecode.php | 186 +- program/lib/Mail/mimePart.php | 180 +- program/lib/Net/SMTP.php | 7 +- program/lib/encoding/ISO-8859-10.map | 303 +++ program/lib/encoding/ISO-8859-11.map | 297 +++ program/lib/encoding/ISO-8859-13.map | 299 +++ program/lib/encoding/ISO-8859-14.map | 301 +++ program/lib/encoding/ISO-8859-15.map | 303 +++ program/lib/encoding/ISO-8859-16.map | 299 +++ program/lib/html2text.inc | 33 +- program/lib/imap.inc | 20 +- program/lib/mime.inc | 2 +- program/lib/rc_mail_mime.inc | 181 ++ program/lib/utf7.inc | 572 ++-- program/lib/utf8.class.php | 8 +- program/localization/am/labels.inc | 180 ++ program/localization/am/messages.inc | 77 + program/localization/bg/labels.inc | 180 ++ program/localization/bg/messages.inc | 77 + program/localization/ca/labels.inc | 181 ++ program/localization/ca/messages.inc | 78 + program/localization/cz/labels.inc | 244 ++ program/localization/cz/messages.inc | 80 + program/localization/da/labels.inc | 182 ++ program/localization/da/messages.inc | 79 + program/localization/de_CH/labels.inc | 35 +- program/localization/de_CH/messages.inc | 60 +- program/localization/de_DE/labels.inc | 267 +- program/localization/de_DE/messages.inc | 92 +- program/localization/en_GB/labels.inc | 16 +- program/localization/en_GB/messages.inc | 50 +- program/localization/en_US/labels.inc | 50 +- program/localization/en_US/messages.inc | 61 +- program/localization/es/labels.inc | 292 +-- program/localization/es/messages.inc | 107 +- program/localization/et_EE/labels.inc | 180 ++ program/localization/et_EE/messages.inc | 77 + program/localization/fr/labels.inc | 252 +- program/localization/fr/messages.inc | 80 +- program/localization/index.inc | 25 +- program/localization/it/labels.inc | 181 ++ program/localization/it/messages.inc | 78 + program/localization/lt/labels.inc | 180 ++ program/localization/lt/messages.inc | 77 + program/localization/lv/labels.inc | 181 ++ program/localization/lv/messages.inc | 78 + program/localization/nb_NO/labels.inc | 180 ++ program/localization/nb_NO/messages.inc | 77 + program/localization/nl_NL/labels.inc | 240 ++ program/localization/nl_NL/messages.inc | 77 + program/localization/pt_BR/labels.inc | 280 +- program/localization/pt_BR/messages.inc | 90 +- program/localization/pt_PT/labels.inc | 281 +- program/localization/pt_PT/messages.inc | 126 +- program/localization/ro/labels.inc | 181 ++ program/localization/ro/messages.inc | 78 + program/localization/ru/labels.inc | 181 ++ program/localization/ru/messages.inc | 78 + program/localization/se/labels.inc | 181 ++ program/localization/se/messages.inc | 78 + program/localization/si/labels.inc | 177 ++ program/localization/si/messages.inc | 73 + program/localization/sk/labels.inc | 181 ++ program/localization/sk/messages.inc | 78 + program/localization/sl/labels.inc | 180 ++ program/localization/sl/messages.inc | 77 + program/localization/zh_TW/labels.inc | 181 ++ program/localization/zh_TW/messages.inc | 78 + program/steps/addressbook/copy.inc | 44 + program/steps/addressbook/delete.inc | 75 +- program/steps/addressbook/edit.inc | 77 +- program/steps/addressbook/func.inc | 249 +- program/steps/addressbook/ldapsearchform.inc | 277 -- program/steps/addressbook/list.inc | 39 +- program/steps/addressbook/mailto.inc | 48 + program/steps/addressbook/save.inc | 270 +- program/steps/addressbook/search.inc | 53 + program/steps/addressbook/show.inc | 63 +- program/steps/error.inc | 17 +- program/steps/mail/addcontact.inc | 72 +- program/steps/mail/check_recent.inc | 21 +- program/steps/mail/compose.inc | 700 ++--- program/steps/mail/folders.inc | 48 +- program/steps/mail/func.inc | 919 ++++--- program/steps/mail/get.inc | 109 +- program/steps/mail/getunread.inc | 15 +- program/steps/mail/list.inc | 50 +- program/steps/mail/mark.inc | 30 +- program/steps/mail/move_del.inc | 83 +- program/steps/mail/quotadisplay.inc | 28 + program/steps/mail/rss.inc | 8 +- program/steps/mail/search.inc | 126 +- program/steps/mail/sendmail.inc | 227 +- program/steps/mail/show.inc | 168 +- program/steps/mail/spell.inc | 22 +- program/steps/mail/upload.inc | 57 +- program/steps/mail/viewsource.inc | 9 +- program/steps/settings/delete_identity.inc | 37 +- program/steps/settings/edit_identity.inc | 64 +- program/steps/settings/func.inc | 231 +- program/steps/settings/identities.inc | 13 +- program/steps/settings/manage_folders.inc | 131 +- program/steps/settings/save_identity.inc | 81 +- program/steps/settings/save_prefs.inc | 39 +- skins/default/addresses.css | 106 +- skins/default/common.css | 130 +- skins/default/images/buttons/first_act.png | Bin 0 -> 248 bytes skins/default/images/buttons/first_pas.png | Bin 0 -> 262 bytes skins/default/images/buttons/first_sel.png | Bin 0 -> 249 bytes skins/default/images/buttons/last_act.png | Bin 0 -> 255 bytes skins/default/images/buttons/last_pas.png | Bin 0 -> 252 bytes skins/default/images/buttons/last_sel.png | Bin 0 -> 256 bytes skins/default/images/dimple.png | Bin 0 -> 239 bytes skins/default/ldapsearchform.css | 54 - skins/default/mail.css | 213 +- skins/default/pngbehavior.htc | 4 +- skins/default/print.css | 29 +- skins/default/settings.css | 4 +- skins/default/templates/addcontact.html | 3 +- skins/default/templates/addressbook.html | 32 +- skins/default/templates/compose.html | 17 +- skins/default/templates/editcontact.html | 2 +- skins/default/templates/ldappublicsearch.html | 31 - skins/default/templates/login.html | 22 +- skins/default/templates/mail.html | 7 +- skins/default/templates/message.html | 6 +- skins/default/templates/messagepreview.html | 19 + skins/default/templates/showcontact.html | 4 +- 164 files changed, 16951 insertions(+), 7256 deletions(-) delete mode 100644 ._CHANGELOG create mode 100755 SQL/mssql.initial.sql create mode 100644 SQL/mysql.update-0.1a.sql create mode 100644 SQL/postgres.update.sql create mode 100644 SQL/sqlite.update.sql create mode 100644 bin/html2text.php create mode 100644 bin/quotaimg.php create mode 100644 program/blocked.gif create mode 100644 program/include/rcmail_template.inc create mode 100644 program/include/rcube_contacts.inc create mode 100644 program/js/list.js create mode 100644 program/lib/encoding/ISO-8859-10.map create mode 100644 program/lib/encoding/ISO-8859-11.map create mode 100644 program/lib/encoding/ISO-8859-13.map create mode 100644 program/lib/encoding/ISO-8859-14.map create mode 100644 program/lib/encoding/ISO-8859-15.map create mode 100644 program/lib/encoding/ISO-8859-16.map create mode 100644 program/lib/rc_mail_mime.inc create mode 100755 program/localization/am/labels.inc create mode 100755 program/localization/am/messages.inc create mode 100644 program/localization/bg/labels.inc create mode 100644 program/localization/bg/messages.inc create mode 100644 program/localization/ca/labels.inc create mode 100644 program/localization/ca/messages.inc create mode 100755 program/localization/cz/labels.inc create mode 100755 program/localization/cz/messages.inc create mode 100644 program/localization/da/labels.inc create mode 100644 program/localization/da/messages.inc create mode 100644 program/localization/et_EE/labels.inc create mode 100644 program/localization/et_EE/messages.inc create mode 100644 program/localization/it/labels.inc create mode 100644 program/localization/it/messages.inc create mode 100644 program/localization/lt/labels.inc create mode 100644 program/localization/lt/messages.inc create mode 100644 program/localization/lv/labels.inc create mode 100644 program/localization/lv/messages.inc create mode 100644 program/localization/nb_NO/labels.inc create mode 100644 program/localization/nb_NO/messages.inc create mode 100644 program/localization/nl_NL/labels.inc create mode 100644 program/localization/nl_NL/messages.inc create mode 100644 program/localization/ro/labels.inc create mode 100644 program/localization/ro/messages.inc create mode 100755 program/localization/ru/labels.inc create mode 100755 program/localization/ru/messages.inc create mode 100644 program/localization/se/labels.inc create mode 100644 program/localization/se/messages.inc create mode 100644 program/localization/si/labels.inc create mode 100644 program/localization/si/messages.inc create mode 100644 program/localization/sk/labels.inc create mode 100644 program/localization/sk/messages.inc create mode 100755 program/localization/sl/labels.inc create mode 100755 program/localization/sl/messages.inc create mode 100755 program/localization/zh_TW/labels.inc create mode 100755 program/localization/zh_TW/messages.inc create mode 100644 program/steps/addressbook/copy.inc delete mode 100644 program/steps/addressbook/ldapsearchform.inc create mode 100644 program/steps/addressbook/mailto.inc create mode 100644 program/steps/addressbook/search.inc create mode 100644 program/steps/mail/quotadisplay.inc create mode 100644 skins/default/images/buttons/first_act.png create mode 100644 skins/default/images/buttons/first_pas.png create mode 100644 skins/default/images/buttons/first_sel.png create mode 100644 skins/default/images/buttons/last_act.png create mode 100644 skins/default/images/buttons/last_pas.png create mode 100644 skins/default/images/buttons/last_sel.png create mode 100644 skins/default/images/dimple.png delete mode 100644 skins/default/ldapsearchform.css delete mode 100644 skins/default/templates/ldappublicsearch.html create mode 100644 skins/default/templates/messagepreview.html diff --git a/._CHANGELOG b/._CHANGELOG deleted file mode 100644 index 429e7b23c694257ab0f3accbd05fa31444fd4d4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDI}aXk=&_#1(k2;dkJ62u@FQ4CZH(lG;w zCDF7oBE&_L^K CREATE DATABASE 'roundcubemail'; +> CREATE DATABASE roundcubemail; > GRANT ALL PRIVILEGES ON roundcubemail.* TO roundcube@localhost - IDENTIFIED BY 'password'; + IDENTIFIED BY 'password'; > quit # mysql roundcubemail < SQL/mysql.initial.sql @@ -53,13 +53,17 @@ For MySQL version 4.1 and up, it's recommended to create the database for RoundCube with utf-8 charset. Here's an example of the init procedure: # mysql -> CREATE DATABASE 'roundcubemail' DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; +> CREATE DATABASE roundcubemail DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; > GRANT ALL PRIVILEGES ON roundcubemail.* TO roundcube@localhost - IDENTIFIED BY 'password'; + IDENTIFIED BY 'password'; > quit # mysql roundcubemail < SQL/mysql5.initial.sql +Note: 'password' is the master password for the roundcube user. It is strongly +recommended you replace this with a more secure password. Please keep in +mind: You need to specify this password later in 'config/db.inc.php'. + * SQLite -------- @@ -97,11 +101,13 @@ CONFIGURATION ============= Change the files in config/* according your to environment and your needs. -Details about the config paramaters can be found in the config files. +Details about the config parameters can be found in the config files +or in the wiki: http://trac.roundcube.net/trac.cgi/wiki/Howto_Config UPGRADING ========= + If you already have a previous version of RoundCube installed, please refer to the instructions in UPGRADING guide. diff --git a/README b/README index e8e065e..f89b21d 100644 --- a/README +++ b/README @@ -1,22 +1,14 @@ RoundCube Webmail (http://roundcube.net) -ATTENTION ---------- -This is just a snapshot of the current CVS repository and is NOT A STABLE -version of RoundCube. There have been major changes since the latest release -so please read the update instructions carefully. It's not recommended to -replace an existing installation of RoundCube with this version. Also using -a separate database or this installation is highly recommended. - - Introduction: ------------- RoundCube Webmail is a browser-based multilingual IMAP client with an -application-like user interface. It provides full functionality you expect from -an e-mail client, including MIME support, address book, folder manipulation and -message filters. RoundCube Webmail is written in PHP and requires the MySQL -database. The user interface is fully skinnable using XHTML and CSS 2. +application-like user interface. It provides full functionality you expect +from an e-mail client, including MIME support, address book, folder manipulation, +message searching and spell checking. RoundCube Webmail is written in PHP and +requires the MySQL or Postgres database. The user interface is fully skinnable +using XHTML and CSS 2. This project is meant to be a modern webmail solution which is easy to install/configure and that runs on a standard PHP plus MySQL or Postgres @@ -28,6 +20,18 @@ The current development skin uses icons designed by Stephen Horlander and Kevin Gerich for Mozilla.org. +Installation: +------------- +For detailed instructions on how to install RoundCube webmail on your server, +please refer to the INSTALL document in the same directory as this document. + + +Licensing: +---------- +This product is distributed under the GPL. Please read through the file +LICENSE for more information about our license. + + How it works: ------------- The main authority for the RoundCube access is the IMAP server. If @@ -41,38 +45,26 @@ will be allowed. Code Layout: ------------ - Basic sequence (index.php): - - index.php -> load_gui -> parse_template + - index.php -> rcmail_load_gui -> new rcmail_template -> rcmail_template::send - authentication details in this sequence Tasks - index.php limits tasks to set list - can see task in roundcube link when you mouse over it - task templates stored in skins/default/templates - - templates "roundcube:" tokens that get replaced in parse_template + - templates "roundcube:" tokens that get replaced in rcmail_template class program/include/rcube_shared.inc - defines rcube_html_page, class that lays out a roundcube web page - defines form control classes - - -Installation: -------------- -For detailed instructions on how to install RoundCube webmail on your server, -please refer to the INSTALL document in the same directory as this document. - - -Licensing: ----------- -This product is distributed under the GPL. Please read through the file -LICENSE for more information about our license. + - provides common functions Contact: -------- For any bug reports or feature requests please refer to the tracking system -at sourceforge.net (http://sourceforge.net/tracker/?group_id=139281) or +at trac.roundcube.net (http://trac.roundcube.net/trac.cgi/report) or subscribe to our mailing list. See http://www.roundcube.net/?p=mailinglists for details. diff --git a/SQL/mssql.initial.sql b/SQL/mssql.initial.sql new file mode 100755 index 0000000..59916e8 --- /dev/null +++ b/SQL/mssql.initial.sql @@ -0,0 +1,206 @@ +CREATE TABLE [dbo].[cache] ( + [cache_id] [int] IDENTITY (1, 1) NOT NULL , + [user_id] [int] NOT NULL , + [session_id] [varchar] (32) COLLATE Latin1_General_CI_AI NULL , + [cache_key] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [created] [datetime] NOT NULL , + [data] [text] COLLATE Latin1_General_CI_AI NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE TABLE [dbo].[contacts] ( + [contact_id] [int] IDENTITY (1, 1) NOT NULL , + [user_id] [int] NOT NULL , + [changed] [datetime] NOT NULL , + [del] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL , + [name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [email] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [firstname] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [surname] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [vcard] [text] COLLATE Latin1_General_CI_AI NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE TABLE [dbo].[identities] ( + [identity_id] [int] IDENTITY (1, 1) NOT NULL , + [user_id] [int] NOT NULL , + [del] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL , + [standard] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL , + [name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [organization] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [email] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [reply-to] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [bcc] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [signature] [text] COLLATE Latin1_General_CI_AI NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE TABLE [dbo].[messages] ( + [message_id] [int] IDENTITY (1, 1) NOT NULL , + [user_id] [int] NOT NULL , + [del] [tinyint] NOT NULL , + [cache_key] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [created] [datetime] NOT NULL , + [idx] [int] NOT NULL , + [uid] [int] NOT NULL , + [subject] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL , + [from] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL , + [to] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL , + [cc] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL , + [date] [datetime] NOT NULL , + [size] [int] NOT NULL , + [headers] [text] COLLATE Latin1_General_CI_AI NOT NULL , + [structure] [text] COLLATE Latin1_General_CI_AI NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE TABLE [dbo].[session] ( + [sess_id] [varchar] (32) COLLATE Latin1_General_CI_AI NOT NULL , + [created] [datetime] NOT NULL , + [changed] [datetime] NULL , + [ip] [varchar] (15) COLLATE Latin1_General_CI_AI NOT NULL , + [vars] [text] COLLATE Latin1_General_CI_AI NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +CREATE TABLE [dbo].[users] ( + [user_id] [int] IDENTITY (1, 1) NOT NULL , + [username] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [mail_host] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [alias] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL , + [created] [datetime] NOT NULL , + [last_login] [datetime] NULL , + [language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL , + [preferences] [text] COLLATE Latin1_General_CI_AI NOT NULL +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache] WITH NOCHECK ADD + PRIMARY KEY CLUSTERED + ( + [cache_id] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[contacts] WITH NOCHECK ADD + CONSTRAINT [PK_contacts_contact_id] PRIMARY KEY CLUSTERED + ( + [contact_id] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[identities] WITH NOCHECK ADD + PRIMARY KEY CLUSTERED + ( + [identity_id] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[messages] WITH NOCHECK ADD + PRIMARY KEY CLUSTERED + ( + [message_id] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[session] WITH NOCHECK ADD + CONSTRAINT [PK_session_sess_id] PRIMARY KEY CLUSTERED + ( + [sess_id] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[users] WITH NOCHECK ADD + CONSTRAINT [PK_users_user_id] PRIMARY KEY CLUSTERED + ( + [user_id] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[cache] ADD + CONSTRAINT [DF_cache_user_id] DEFAULT ('0') FOR [user_id], + CONSTRAINT [DF_cache_session_id] DEFAULT (null) FOR [session_id], + CONSTRAINT [DF_cache_cache_key] DEFAULT ('') FOR [cache_key], + CONSTRAINT [DF_cache_created] DEFAULT (getdate()) FOR [created] +GO + + CREATE INDEX [IX_cache_user_id] ON [dbo].[cache]([user_id]) ON [PRIMARY] +GO + + CREATE INDEX [IX_cache_cache_key] ON [dbo].[cache]([cache_key]) ON [PRIMARY] +GO + + CREATE INDEX [IX_cache_session_id] ON [dbo].[cache]([session_id]) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[contacts] ADD + CONSTRAINT [DF_contacts_user_id] DEFAULT (0) FOR [user_id], + CONSTRAINT [DF_contacts_changed] DEFAULT (getdate()) FOR [changed], + CONSTRAINT [DF_contacts_del] DEFAULT ('0') FOR [del], + CONSTRAINT [DF_contacts_name] DEFAULT ('') FOR [name], + CONSTRAINT [DF_contacts_email] DEFAULT ('') FOR [email], + CONSTRAINT [DF_contacts_firstname] DEFAULT ('') FOR [firstname], + CONSTRAINT [DF_contacts_surname] DEFAULT ('') FOR [surname], + CONSTRAINT [CK_contacts_del] CHECK ([del] = '1' or [del] = '0') +GO + + CREATE INDEX [IX_contacts_user_id] ON [dbo].[contacts]([user_id]) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[identities] ADD + CONSTRAINT [DF_identities_user] DEFAULT ('0') FOR [user_id], + CONSTRAINT [DF_identities_del] DEFAULT ('0') FOR [del], + CONSTRAINT [DF_identities_standard] DEFAULT ('0') FOR [standard], + CONSTRAINT [DF_identities_name] DEFAULT ('') FOR [name], + CONSTRAINT [DF_identities_organization] DEFAULT ('') FOR [organization], + CONSTRAINT [DF_identities_email] DEFAULT ('') FOR [email], + CONSTRAINT [DF_identities_reply] DEFAULT ('') FOR [reply-to], + CONSTRAINT [DF_identities_bcc] DEFAULT ('') FOR [bcc], + CHECK ([standard] = '1' or [standard] = '0'), + CHECK ([del] = '1' or [del] = '0') +GO + + CREATE INDEX [IX_identities_user_id] ON [dbo].[identities]([user_id]) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[messages] ADD + CONSTRAINT [DF_messages_user_id] DEFAULT (0) FOR [user_id], + CONSTRAINT [DF_messages_del] DEFAULT (0) FOR [del], + CONSTRAINT [DF_messages_cache_key] DEFAULT ('') FOR [cache_key], + CONSTRAINT [DF_messages_created] DEFAULT (getdate()) FOR [created], + CONSTRAINT [DF_messages_idx] DEFAULT (0) FOR [idx], + CONSTRAINT [DF_messages_uid] DEFAULT (0) FOR [uid], + CONSTRAINT [DF_messages_subject] DEFAULT ('') FOR [subject], + CONSTRAINT [DF_messages_from] DEFAULT ('') FOR [from], + CONSTRAINT [DF_messages_to] DEFAULT ('') FOR [to], + CONSTRAINT [DF_messages_cc] DEFAULT ('') FOR [cc], + CONSTRAINT [DF_messages_date] DEFAULT (getdate()) FOR [date], + CONSTRAINT [DF_messages_size] DEFAULT (0) FOR [size] +GO + + CREATE INDEX [IX_messages_user_id] ON [dbo].[messages]([user_id]) ON [PRIMARY] +GO + + CREATE INDEX [IX_messages_cache_key] ON [dbo].[messages]([cache_key]) ON [PRIMARY] +GO + + CREATE INDEX [IX_messages_idx] ON [dbo].[messages]([idx]) ON [PRIMARY] +GO + + CREATE INDEX [IX_messages_uid] ON [dbo].[messages]([uid]) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[session] ADD + CONSTRAINT [DF_session_sess_id] DEFAULT ('') FOR [sess_id], + CONSTRAINT [DF_session_created] DEFAULT (getdate()) FOR [created], + CONSTRAINT [DF_session_ip] DEFAULT ('') FOR [ip] +GO + +ALTER TABLE [dbo].[users] ADD + CONSTRAINT [DF_users_username] DEFAULT ('') FOR [username], + CONSTRAINT [DF_users_mail_host] DEFAULT ('') FOR [mail_host], + CONSTRAINT [DF_users_alias] DEFAULT ('') FOR [alias], + CONSTRAINT [DF_users_created] DEFAULT (getdate()) FOR [created], + CONSTRAINT [DF_users_language] DEFAULT ('en') FOR [language] +GO + diff --git a/SQL/mysql.initial.sql b/SQL/mysql.initial.sql index 7546e52..4a92734 100644 --- a/SQL/mysql.initial.sql +++ b/SQL/mysql.initial.sql @@ -1,5 +1,5 @@ -- RoundCube Webmail initial database structure --- Version 0.1beta2 +-- Version 0.1-rc1 -- -- -------------------------------------------------------- @@ -58,6 +58,7 @@ CREATE TABLE `identities` ( `reply-to` varchar(128) NOT NULL default '', `bcc` varchar(128) NOT NULL default '', `signature` text NOT NULL, + `html_signature` tinyint(1) NOT NULL default '0', PRIMARY KEY (`identity_id`), KEY `user_id` (`user_id`) ); @@ -116,12 +117,12 @@ CREATE TABLE `messages` ( `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, + `structure` text, PRIMARY KEY (`message_id`), KEY `user_id` (`user_id`), - KEY `cache_key` (`cache_key`), KEY `idx` (`idx`), - KEY `uid` (`uid`) + KEY `uid` (`uid`), + UNIQUE `uniqueness` (`user_id`, `cache_key`, `uid`) ); diff --git a/SQL/mysql.update-0.1a.sql b/SQL/mysql.update-0.1a.sql new file mode 100644 index 0000000..054814a --- /dev/null +++ b/SQL/mysql.update-0.1a.sql @@ -0,0 +1,51 @@ +-- RoundCube Webmail update script for MySQL databases +-- Updates from version 0.1-20051007 + + +ALTER TABLE `session` ADD `ip` VARCHAR(15) NOT NULL AFTER changed; +ALTER TABLE `users` ADD `alias` VARCHAR(128) NOT NULL AFTER mail_host; + + + +-- RoundCube Webmail update script for MySQL databases +-- Updates from 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 '', + `created` datetime NOT NULL default '0000-00-00 00:00:00', + `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, + `structure` text, + PRIMARY KEY (`message_id`), + KEY `user_id` (`user_id`), + KEY `idx` (`idx`), + KEY `uid` (`uid`), + UNIQUE `uniqueness` (`cache_key`, `uid`) +) TYPE=MyISAM; + diff --git a/SQL/mysql.update.sql b/SQL/mysql.update.sql index 7fa296b..b64f407 100644 --- a/SQL/mysql.update.sql +++ b/SQL/mysql.update.sql @@ -1,57 +1,17 @@ -- RoundCube Webmail update script for MySQL databases --- Updates from version 0.1-20051007 - - -ALTER TABLE `session` ADD `ip` VARCHAR(15) NOT NULL AFTER changed; -ALTER TABLE `users` ADD `alias` VARCHAR(128) NOT NULL AFTER mail_host; - - - --- RoundCube Webmail update script for MySQL databases --- Updates from 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 '', - `created` datetime NOT NULL default '0000-00-00 00:00:00', - `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; - - - --- RoundCube Webmail update script for MySQL databases --- Updates from version 0.1-20051216 - -ALTER TABLE `messages` ADD `created` DATETIME NOT NULL AFTER `cache_key` ; +-- Updates from version 0.1-beta and 0.1-beta2 + +ALTER TABLE `messages` + DROP `body`, + DROP INDEX `cache_key`, + ADD `structure` TEXT, + ADD UNIQUE `uniqueness` (`user_id`, `cache_key`, `uid`); + +ALTER TABLE `identities` + ADD `html_signature` tinyint(1) default 0 NOT NULL; + +-- Uncomment these lines if you're using MySQL 4.1 or higher +-- ALTER TABLE `users` +-- DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci, +-- CHANGE `username` `username` VARCHAR( 128 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, +-- CHANGE `alias` `alias` VARCHAR( 128 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL; diff --git a/SQL/mysql5.initial.sql b/SQL/mysql5.initial.sql index 0116468..0c0dcb1 100644 --- a/SQL/mysql5.initial.sql +++ b/SQL/mysql5.initial.sql @@ -1,6 +1,5 @@ -- RoundCube Webmail initial database structure --- Version 0.1beta2 --- +-- Version 0.1-rc1 -- -------------------------------------------------------- @@ -16,7 +15,7 @@ CREATE TABLE `session` ( `ip` varchar(15) NOT NULL, `vars` text NOT NULL, PRIMARY KEY(`sess_id`) -) TYPE=MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci; +) TYPE=MYISAM CHARACTER SET ascii COLLATE ascii_general_ci; -- Table structure for table `users` @@ -29,7 +28,7 @@ CREATE TABLE `users` ( `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `last_login` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `language` varchar(5) NOT NULL DEFAULT 'en', - `preferences` text NOT NULL, + `preferences` text NOT NULL DEFAULT '', PRIMARY KEY(`user_id`) ) TYPE=MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci; @@ -38,8 +37,9 @@ CREATE TABLE `users` ( CREATE TABLE `messages` ( `message_id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', `del` tinyint(1) NOT NULL DEFAULT '0', - `cache_key` varchar(128) NOT NULL, + `cache_key` varchar(128) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL, `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `idx` int(11) UNSIGNED NOT NULL DEFAULT '0', `uid` int(11) UNSIGNED NOT NULL DEFAULT '0', @@ -50,13 +50,12 @@ CREATE TABLE `messages` ( `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, - `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', + `structure` text, PRIMARY KEY(`message_id`), - INDEX `cache_key`(`cache_key`), INDEX `idx`(`idx`), INDEX `uid`(`uid`), - CONSTRAINT `User_ID_FK_messages` FOREIGN KEY (`user_id`) + UNIQUE `uniqueness` (`user_id`, `cache_key`, `uid`), + CONSTRAINT `user_id_fk_messages` FOREIGN KEY (`user_id`) REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE @@ -67,15 +66,15 @@ CREATE TABLE `messages` ( CREATE TABLE `cache` ( `cache_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `session_id` varchar(40), - `cache_key` varchar(128) NOT NULL, + `session_id` varchar(40) CHARACTER SET ascii COLLATE ascii_general_ci, + `cache_key` varchar(128) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL, `created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', `data` longtext NOT NULL, `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY(`cache_id`), INDEX `cache_key`(`cache_key`), INDEX `session_id`(`session_id`), - CONSTRAINT `User_ID_FK_cache` FOREIGN KEY (`user_id`) + CONSTRAINT `user_id_fk_cache` FOREIGN KEY (`user_id`) REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE @@ -92,10 +91,10 @@ CREATE TABLE `contacts` ( `email` varchar(128) NOT NULL, `firstname` varchar(128) NOT NULL, `surname` varchar(128) NOT NULL, - `vcard` text NOT NULL, + `vcard` text NULL, `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY(`contact_id`), - CONSTRAINT `User_ID_FK_contacts` FOREIGN KEY (`user_id`) + CONSTRAINT `user_id_fk_contacts` FOREIGN KEY (`user_id`) REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE @@ -114,13 +113,14 @@ CREATE TABLE `identities` ( `reply-to` varchar(128) NOT NULL, `bcc` varchar(128) NOT NULL, `signature` text NOT NULL, + `html_signature` tinyint(1) NOT NULL DEFAULT '0', `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY(`identity_id`), - CONSTRAINT `User_ID_FK_identities` FOREIGN KEY (`user_id`) + CONSTRAINT `user_id_fk_identities` FOREIGN KEY (`user_id`) REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE ) TYPE=MYISAM CHARACTER SET utf8 COLLATE utf8_general_ci; -SET FOREIGN_KEY_CHECKS=1; \ No newline at end of file +SET FOREIGN_KEY_CHECKS=1; diff --git a/SQL/postgres.initial.sql b/SQL/postgres.initial.sql index 55e1396..bf82b9b 100755 --- a/SQL/postgres.initial.sql +++ b/SQL/postgres.initial.sql @@ -69,7 +69,8 @@ CREATE TABLE identities ( email character varying(128) NOT NULL, "reply-to" character varying(128), bcc character varying(128), - signature text + signature text, + html_signature integer DEFAULT 0 NOT NULL ); @@ -163,6 +164,7 @@ CREATE TABLE "messages" ( date timestamp with time zone NOT NULL, size integer DEFAULT 0 NOT NULL, headers text NOT NULL, - body text + structure text ); +ALTER TABLE "messages" ADD UNIQUE (user_id, cache_key, uid); diff --git a/SQL/postgres.update.sql b/SQL/postgres.update.sql new file mode 100644 index 0000000..8f70548 --- /dev/null +++ b/SQL/postgres.update.sql @@ -0,0 +1,9 @@ +-- RoundCube Webmail update script for Postres databases +-- Updates from version 0.1-beta and older + +ALTER TABLE "messages" DROP body; +ALTER TABLE "messages" ADD structure TEXT; +ALTER TABLE "messages" ADD UNIQUE (user_id, cache_key, uid); + +ALTER TABLE "identities" ADD html_signature integer DEFAULT 0 NOT NULL; + diff --git a/SQL/sqlite.initial.sql b/SQL/sqlite.initial.sql index ae16a31..fd2a22f 100644 --- a/SQL/sqlite.initial.sql +++ b/SQL/sqlite.initial.sql @@ -1,5 +1,5 @@ -- RoundCube Webmail initial database structure --- Version 0.1a +-- Version 0.1-rc1 -- -- -------------------------------------------------------- @@ -58,7 +58,8 @@ CREATE TABLE identities ( email varchar(128) NOT NULL default '', "reply-to" varchar(128) NOT NULL default '', bcc varchar(128) NOT NULL default '', - signature text NOT NULL default '' + signature text NOT NULL default '', + html_signature tinyint NOT NULL default '0' ); CREATE INDEX ix_identities_user_id ON identities(user_id); @@ -118,7 +119,7 @@ CREATE TABLE messages ( date datetime NOT NULL default '0000-00-00 00:00:00', size integer NOT NULL default '0', headers text NOT NULL, - body text + structure text ); CREATE INDEX ix_messages_user_id ON messages(user_id); diff --git a/SQL/sqlite.update.sql b/SQL/sqlite.update.sql new file mode 100644 index 0000000..e725729 --- /dev/null +++ b/SQL/sqlite.update.sql @@ -0,0 +1,27 @@ +-- RoundCube Webmail update script for SQLite databases +-- Updates from version 0.1-beta2 and older + +DROP 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 '', + created datetime NOT NULL default '0000-00-00 00:00:00', + 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, + structure text +); + +CREATE INDEX ix_messages_user_id ON messages(user_id); +CREATE INDEX ix_messages_cache_key ON messages(cache_key); +CREATE INDEX ix_messages_idx ON messages(idx); +CREATE INDEX ix_messages_uid ON messages(uid); diff --git a/UPGRADING b/UPGRADING index bb8b36b..4f4d0da 100644 --- a/UPGRADING +++ b/UPGRADING @@ -5,64 +5,78 @@ Follow these instructions if upgrading from a previous version of RoundCube Webmail. -from versions 0.1-alpha and 0.1-20050811 +from version 1.0-beta2 ---------------------------------------- -- 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 line to /config/main.inc.php - $rcmail_config['trash_mbox'] = 'Trash'; - $rcmail_config['default_imap_folders'] = array('INBOX', 'Drafts', 'Sent', 'Junk', 'Trash'); - $rcmail_config['prefer_html'] = TRUE; - $rcmail_config['prettydate'] = TRUE; - $rcmail_config['smtp_port'] = 25; - $rcmail_config['default_port'] = 143; - $rcmail_config['session_lifetime'] = 20; - $rcmail_config['skip_deleted'] = FALSE; - $rcmail_config['message_sort_col'] = 'date'; - $rcmail_config['message_sort_order'] = 'DESC'; - $rcmail_config['log_dir'] = 'logs/'; - $rcmail_config['temp_dir'] = 'temp/'; - $rcmail_config['message_cache_lifetime'] = '10d'; -- 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; +* 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['draft_autosave'] = 300; + $rcmail_config['date_today'] = 'H:i'; +* If you have LDAP servers configured, change prop key 'mail_field' + to 'email_field' for each server confgured in /config/main.inc.php -from version 0.1-20050820 +form version 0.1-beta ---------------------------------------- -- 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 line to /config/main.inc.php - $rcmail_config['prettydate'] = TRUE; - $rcmail_config['smtp_port'] = 25; - $rcmail_config['default_port'] = 143; - $rcmail_config['session_lifetime'] = 20; +* 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 line to /config/db.inc.php + $rcmail_config['db_persistent'] = TRUE; +* add these lines to /config/main.inc.php + $rcmail_config['drafts_mbox'] = 'Drafts'; + $rcmail_config['junk_mbox'] = 'Junk'; + $rcmail_config['product_name'] = 'RoundCube Webmail'; + $rcmail_config['read_when_deleted'] = TRUE; + $rcmail_config['enable_spellcheck'] = TRUE; + $rcmail_config['protect_default_folders'] = TRUE; + $rcmail_config['draft_autosave'] = 300; + $rcmail_config['date_today'] = 'H:i'; +* replace the following line from /config/main.inc.php + @include($_SERVER['HTTP_HOST'].'.inc.php'); + with + $rcmail_config['include_host_config'] = TRUE; + + + +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-0.1a.sql or re-initalize database with *.initial.sql +* add these lines to /config/main.inc.php $rcmail_config['skip_deleted'] = FALSE; $rcmail_config['message_sort_col'] = 'date'; $rcmail_config['message_sort_order'] = 'DESC'; $rcmail_config['log_dir'] = 'logs/'; $rcmail_config['temp_dir'] = 'temp/'; $rcmail_config['message_cache_lifetime'] = '10d'; -- 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['drafts_mbox'] = 'Drafts'; + $rcmail_config['product_name'] = 'RoundCube Webmail'; + $rcmail_config['read_when_deleted'] = TRUE; + $rcmail_config['enable_spellcheck'] = TRUE; +* 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'; + $rcmail_config['db_persistent'] = TRUE; + 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 +* replace index.php +* replace all files in folder /program/ +* replace all files in folder /skins/default/ +* run all commands in SQL/*.update-0.1a.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['skip_deleted'] = FALSE; @@ -75,7 +89,7 @@ from version 0.1-20051007 $rcmail_config['product_name'] = 'RoundCube Webmail'; $rcmail_config['read_when_deleted'] = TRUE; $rcmail_config['enable_spellcheck'] = TRUE; -- add these lines to /config/db.inc.php +* 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'; @@ -84,50 +98,56 @@ from version 0.1-20051007 $rcmail_config['db_sequence_message_ids'] = 'message_ids'; $rcmail_config['db_persistent'] = TRUE; -from version 0.1-20051021 + + +from version 0.1-20050820 ---------------------------------------- -- 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 +* replace index.php +* replace all files in folder /program/ +* replace all files in folder /skins/default/ +* run all commands in SQL/*.update-0.1a.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; + $rcmail_config['default_port'] = 143; + $rcmail_config['session_lifetime'] = 20; $rcmail_config['skip_deleted'] = FALSE; $rcmail_config['message_sort_col'] = 'date'; $rcmail_config['message_sort_order'] = 'DESC'; $rcmail_config['log_dir'] = 'logs/'; $rcmail_config['temp_dir'] = 'temp/'; $rcmail_config['message_cache_lifetime'] = '10d'; - $rcmail_config['drafts_mbox'] = 'Drafts'; - $rcmail_config['product_name'] = 'RoundCube Webmail'; - $rcmail_config['read_when_deleted'] = TRUE; - $rcmail_config['enable_spellcheck'] = TRUE; -- add these lines to /config/db.inc.php +* 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; - $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'; - $rcmail_config['db_persistent'] = TRUE; - - -form version 0.1-beta + + + +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/ -- add these line to /config/db.inc.php - $rcmail_config['db_persistent'] = TRUE; -- add these lines to /config/main.inc.php - $rcmail_config['drafts_mbox'] = 'Drafts'; - $rcmail_config['junk_mbox'] = 'Junk'; - $rcmail_config['product_name'] = 'RoundCube Webmail'; - $rcmail_config['read_when_deleted'] = TRUE; - $rcmail_config['enable_spellcheck'] = TRUE; - $rcmail_config['protect_default_folders'] = TRUE; -- replace the following line from /config/main.inc.php - @include($_SERVER['HTTP_HOST'].'.inc.php'); - with - $rcmail_config['include_host_config'] = TRUE; - - +* replace index.php +* replace all files in folder /program/ +* replace all files in folder /skins/default/ +* run all commands in SQL/*.update-0.1a.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'); + $rcmail_config['prefer_html'] = TRUE; + $rcmail_config['prettydate'] = TRUE; + $rcmail_config['smtp_port'] = 25; + $rcmail_config['default_port'] = 143; + $rcmail_config['session_lifetime'] = 20; + $rcmail_config['skip_deleted'] = FALSE; + $rcmail_config['message_sort_col'] = 'date'; + $rcmail_config['message_sort_order'] = 'DESC'; + $rcmail_config['log_dir'] = 'logs/'; + $rcmail_config['temp_dir'] = 'temp/'; + $rcmail_config['message_cache_lifetime'] = '10d'; +* 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; + diff --git a/bin/html2text.php b/bin/html2text.php new file mode 100644 index 0000000..e0e2679 --- /dev/null +++ b/bin/html2text.php @@ -0,0 +1,16 @@ +get_text(); + +if (function_exists('html_entity_decode')) + print html_entity_decode($plaintext, ENT_COMPAT, 'UTF-8'); +else + print $plaintext; + +?> \ No newline at end of file diff --git a/bin/quotaimg.php b/bin/quotaimg.php new file mode 100644 index 0000000..47696ef --- /dev/null +++ b/bin/quotaimg.php @@ -0,0 +1,169 @@ + | + +-----------------------------------------------------------------------+ + + $Id: $ + +*/ + +$used = ((isset($_GET['u']) && !empty($_GET['u'])) || $_GET['u']=='0')?(int)$_GET['u']:'??'; +$quota = ((isset($_GET['q']) && !empty($_GET['q'])) || $_GET['q']=='0')?(int)$_GET['q']:'??'; +$width = empty($_GET['w']) ? 100 : (int)$_GET['w']; +$height = empty($_GET['h']) ? 14 : (int)$_GET['h']; + +function genQuota($used, $total, $width, $height) +{ + /** + * Quota Display + * + * Modify the following few elements to change the display of the image. + * Modifiable attributes are: + * bool border :: Defines whether you want to show a border around it or not. + * bool unknown :: Leave default; Defines whether quota is "unknown" + * + * int height :: Defines height of the image + * int width :: Defines width of the image + * int font :: Changes the font size & font used in the GD library. + * Available values are from 1 to 5. + * int padding :: Changes the offset (in pixels) from the top of the image to + * where the top of the text will be aligned. User greater than + * 0 to ensure text is off the border. + * array limit :: Holds the integer values of in an associative array as to what + * defines the upper and lower levels for quota display. + * High - Quota is nearing capacity. + * Mid - Quota is around the middle + * Low - Currently not used. + * array color :: An associative array of strings of comma separated values (R,G,B) + * for use in color creation. Define the RGB values you'd like to + * use. A list of colors (and their RGB values) can be found here: + * http://www.december.com/html/spec/colorcodes.html + **/ + + $unknown = false; + $border = 0; + + $font = 2; + $padding = 0; + + $limit['high'] = 70; + $limit['mid'] = 45; + $limit['low'] = 0; + + // Fill Colors + $color['fill']['high'] = '215, 13, 13'; // Near quota fill color + $color['fill']['mid'] = '126, 192, 238';// Mid-area of quota fill color + $color['fill']['low'] = '147, 225, 100'; // Far from quota fill color + + // Background colors + $color['bg']['OL'] = '215, 13, 13'; // Over limit bbackground + $color['bg']['Unknown'] = '238, 99, 99';// Unknown background + $color['bg']['quota'] = '255, 255, 255';// Normal quota background + + // Misc. Colors + $color['border'] = '0, 0, 0'; + $color['text'] = '102, 102, 102'; + + + /**************************** + ***** DO NOT EDIT BELOW HERE ***** + ****************************/ + + if (ereg("^[^0-9?]*$", $used) || ereg("^[^0-9?]*$", $total)) + return false; + + if (strpos($used, '?')!==false || strpos($total, '?')!==false && $used != 0) + $unknown = true; + + $im = imagecreate($width, $height); + + if ($border) + { + list($r, $g, $b) = explode(',', $color['border']); + $borderc = imagecolorallocate($im, $r, $g, $b); + imageline($im, 0, 0, $width, 0, $borderc); + imageline($im, 0, $height-$border, 0, 0, $borderc); + imageline($im, $width-1, 0, $width-$border, $height, $borderc); + imageline($im, $width, $height-$border, 0, $height-$border, $borderc); + } + + list($r, $g, $b) = explode(',', $color['text']); + $text = imagecolorallocate($im, $r, $g, $b); + + if ($unknown) + { + list($r, $g, $b) = explode(',', $color['bg']['Unknown']); + $background = imagecolorallocate($im, $r, $g, $b); + imagefilledrectangle($im, 0, 0, $width, $height, $background); + + $string = 'Unknown'; + $mid = floor(($width-(strlen($string)*imagefontwidth($font)))/2)+1; + imagestring($im, $font, $mid, $padding, $string, $text); + } + else if ($used > $total) + { + list($r, $g, $b) = explode(',', $color['bg']['OL']); + $background = imagecolorallocate($im, $r, $g, $b); + imagefilledrectangle($im, 0, 0, $width, $height, $background); + + $string = 'Over Limit'; + $mid = floor(($width-(strlen($string)*imagefontwidth($font)))/2)+1; + imagestring($im, $font, $mid, $padding, $string, $text); + } + else + { + list($r, $g, $b) = explode(',', $color['bg']['quota']); + $background = imagecolorallocate($im, $r, $b, $g); + imagefilledrectangle($im, 0, 0, $width, $height, $background); + + $quota = ($used==0)?0:(round($used/$total, 2)*100); + + if ($quota >= $limit['high']) + { + list($r, $g, $b) = explode(',', $color['fill']['high']); + $fill = imagecolorallocate($im, $r, $g, $b); + } + elseif($quota >= $limit['mid']) + { + list($r, $g, $b) = explode(',', $color['fill']['mid']); + $fill = imagecolorallocate($im, $r, $g, $b); + } + else // if($quota >= $limit['low']) + { + list($r, $g, $b) = explode(',', $color['fill']['low']); + $fill = imagecolorallocate($im, $r, $g, $b); + } + + $quota_width = $quota / 100 * $width; + imagefilledrectangle($im, $border, 0, $quota, $height-2*$border, $fill); + + $string = $quota.'%'; + $mid = floor(($width-(strlen($string)*imagefontwidth($font)))/2)+1; + imagestring($im, $font, $mid, $padding, $string, $text); // Print percent in black + } + + header('Content-Type: image/gif'); + header("Expires: ".gmdate("D, d M Y H:i:s", mktime()+86400)." GMT"); + header("Cache-Control: "); + header("Pragma: "); + + imagegif($im); + imagedestroy($im); +} + + +genQuota($used, $quota, $width, $height); +exit; +?> \ No newline at end of file diff --git a/config/db.inc.php.dist b/config/db.inc.php.dist index f9d3b2d..895aa45 100644 --- a/config/db.inc.php.dist +++ b/config/db.inc.php.dist @@ -5,7 +5,7 @@ | Configuration file for database access | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | +-----------------------------------------------------------------------+ @@ -16,7 +16,7 @@ $rcmail_config = array(); // PEAR database DSN for read/write operations // format is db_provider://user:password@host/databse -// currentyl suported db_providers: mysql, sqlite +// currentyl suported db_providers: mysql, pgsql, sqlite $rcmail_config['db_dsnw'] = 'mysql://roundcube:pass@localhost/roundcubemail'; // postgres example: 'pgsql://roundcube:pass@localhost/roundcubemail'; diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 2f79bd8..3b8e407 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -5,7 +5,7 @@ | Main configuration file | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | +-----------------------------------------------------------------------+ @@ -97,7 +97,11 @@ $rcmail_config['log_dir'] = 'logs/'; $rcmail_config['session_lifetime'] = 10; // check client IP in session athorization -$rcmail_config['ip_check'] = TRUE; +$rcmail_config['ip_check'] = false; + +// Use an additional frequently changing cookie to athenticate user sessions. +// There have been problems reported with this feature. +$rcmail_config['double_auth'] = false; // this key is used to encrypt the users imap password which is stored // in the session record (and the client cookie if remember password is enabled). @@ -113,8 +117,11 @@ $rcmail_config['date_short'] = 'D H:i'; // use this format for detailed date/time formatting $rcmail_config['date_long'] = 'd.m.Y H:i'; +// use this format for today's date display +$rcmail_config['date_today'] = 'H:i'; + // add this user-agent to message headers when sending -$rcmail_config['useragent'] = 'RoundCube Webmail/0.1b'; +$rcmail_config['useragent'] = 'RoundCube Webmail/0.1-rc1'; // use this name to compose page titles $rcmail_config['product_name'] = 'RoundCube Webmail'; @@ -157,10 +164,20 @@ $rcmail_config['read_when_deleted'] = TRUE; // false causes deleted messages to be permanantly removed if there is no Trash folder $rcmail_config['flag_for_deletion'] = TRUE; -// Make use of the built-in spell checker. It is based on GoogieSpell -// which means that the message content will be sent to Google in order to check spelling +// Make use of the built-in spell checker. It is based on GoogieSpell. $rcmail_config['enable_spellcheck'] = TRUE; +// For a locally installed Nox Spell Server, please specify the URI to call it. +// Get Nox Spell Server from http://orangoo.com/labs/?page_id=72 +// Leave empty to use the Google spell checking service, what means +// that the message content will be sent to Google in order to check spelling +$rcmail_config['spellcheck_uri'] = ''; + +// These languages can be selected for spell checking. +// Configure as a PHP style hash array: array('en'=>'English', 'de'=>'Deutsch'); +// Leave empty for default set of Google spell check languages +$rcmail_config['spellcheck_languages'] = NULL; + // path to a text file which will be added to each sent message // paths are relative to the RoundCube root folder $rcmail_config['generic_message_footer'] = ''; @@ -173,18 +190,29 @@ $rcmail_config['mail_header_delimiter'] = NULL; // like the Verisign example below. if you would like to test, // simply uncomment the Verisign example. /** - * example config for Verisign directory + * example config for Verisign directory * - * $rcmail_config['ldap_public']['Verisign'] = array('hosts' => array('directory.verisign.com'), - * 'port' => 389, - * 'base_dn' => '', - * 'search_fields' => array('Email' => 'mail', 'Name' => 'cn'), - * 'name_field' => 'cn', - * 'mail_field' => 'mail', - * 'scope' => 'sub', - * 'fuzzy_search' => 0); + * $rcmail_config['ldap_public']['Verisign'] = array( + * 'name' => 'Verisign.com', + * 'hosts' => array('directory.verisign.com'), + * 'port' => 389, + * 'base_dn' => '', + * 'bind_dn' => '', + * 'bind_pass' => '', + * 'search_fields' => array('mail', 'cn'), // fields to search in + * 'name_field' => 'cn', // this field represents the contact's name + * 'email_field' => 'mail', // this field represents the contact's e-mail + * 'scope' => 'sub', // search mode: sub|base|list + * 'filter' => '', // will be &'d with search field ex: (status=act) + * 'fuzzy_search' => true); // server allows wildcard search */ +// don't allow these settings to be overriden by the user +$rcmail_config['dont_override'] = array(); + +// list of configuration option names that need to be available in Javascript. +$rcmail_config['javascript_config'] = array('read_when_deleted', 'flag_for_deletion'); + // try to load host-specific configuration $rcmail_config['include_host_config'] = FALSE; @@ -212,9 +240,8 @@ $rcmail_config['message_sort_col'] = 'date'; // default sort order $rcmail_config['message_sort_order'] = 'DESC'; -// list of configuration option names that need to be available in Javascript. -$rcmail_config['javascript_config'] = array('read_when_deleted', 'flag_for_deletion'); - +// save compose message every 300 seconds (5min) +$rcmail_config['draft_autosave'] = 300; // end of config file ?> diff --git a/index.php b/index.php index 35184ff..c23494b 100644 --- a/index.php +++ b/index.php @@ -2,9 +2,9 @@ /* +-----------------------------------------------------------------------+ | RoundCube Webmail IMAP Client | - | Version 0.1-beta2 | + | Version 0.1-rc1 | | | - | Copyright (C) 2005-2006, RoundCube Dev. - Switzerland | + | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | | Redistribution and use in source and binary forms, with or without | @@ -36,16 +36,17 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: index.php 429 2006-12-22 22:26:24Z thomasb $ + $Id: index.php 579 2007-05-18 13:11:22Z thomasb $ */ -define('RCMAIL_VERSION', '0.1-beta2'); +// application constants +define('RCMAIL_VERSION', '0.1-rc1'); +define('RCMAIL_CHARSET', 'UTF-8'); +define('JS_OBJECT_NAME', 'rcmail'); // define global vars -$CHARSET = 'UTF-8'; $OUTPUT_TYPE = 'html'; -$JS_OBJECT_NAME = 'rcmail'; $INSTALL_PATH = dirname(__FILE__); $MAIN_TASKS = array('mail','settings','addressbook','logout'); @@ -73,7 +74,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); +if (!ini_get('safe_mode')) @set_time_limit(120); // include base files require_once('include/rcube_shared.inc'); @@ -87,12 +88,6 @@ require_once('PEAR.php'); // set PEAR error handling // PEAR::setErrorHandling(PEAR_ERROR_TRIGGER, E_USER_NOTICE); -// use gzip compression if supported -if (function_exists('ob_gzhandler') && !ini_get('zlib.output_compression')) - ob_start('ob_gzhandler'); -else - ob_start(); - // catch some url/post parameters $_task = strip_quotes(get_input_value('_task', RCUBE_INPUT_GPC)); @@ -103,8 +98,17 @@ $_framed = (!empty($_GET['_framed']) || !empty($_POST['_framed'])); if (empty($_task) || !in_array($_task, $MAIN_TASKS)) $_task = 'mail'; -if (!empty($_GET['_remote'])) - $REMOTE_REQUEST = TRUE; + +// set output buffering +if ($_action != 'get' && $_action != 'viewsource') +{ + // use gzip compression if supported + if (function_exists('ob_gzhandler') && ini_get('zlib.output_compression')) + ob_start('ob_gzhandler'); + else + ob_start(); +} + // start session with requested task rcmail_startup($_task); @@ -116,133 +120,136 @@ $SESS_HIDDEN_FIELD = ''; // add framed parameter if ($_framed) - { +{ $COMM_PATH .= '&_framed=1'; $SESS_HIDDEN_FIELD .= "\n".''; - } +} // init necessary objects for GUI -load_gui(); +rcmail_load_gui(); // check DB connections and exit on failure if ($err_str = $DB->is_error()) - { - raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, - 'message' => $err_str), FALSE, TRUE); - } +{ + raise_error(array( + 'code' => 603, + 'type' => 'db', + 'message' => $err_str), FALSE, TRUE); +} // error steps if ($_action=='error' && !empty($_GET['_code'])) - { raise_error(array('code' => hexdec($_GET['_code'])), FALSE, TRUE); - } // try to log in if ($_action=='login' && $_task=='mail') - { - $host = $_POST['_host'] ? $_POST['_host'] : $CONFIG['default_host']; +{ + $host = rcmail_autoselect_host(); // check if client supports cookies if (empty($_COOKIE)) - { - show_message("cookiesdisabled", 'warning'); - } - else if (isset($_POST['_user']) && isset($_POST['_pass']) && - rcmail_login(get_input_value('_user', RCUBE_INPUT_POST), $_POST['_pass'], $host)) - { + { + $OUTPUT->show_message("cookiesdisabled", 'warning'); + } + else if ($_SESSION['temp'] && !empty($_POST['_user']) && isset($_POST['_pass']) && + rcmail_login(get_input_value('_user', RCUBE_INPUT_POST), + get_input_value('_pass', RCUBE_INPUT_POST, true, 'ISO-8859-1'), $host)) + { + // create new session ID + unset($_SESSION['temp']); + sess_regenerate_id(); + + // send auth cookie if necessary + rcmail_authenticate_session(); + // send redirect header("Location: $COMM_PATH"); exit; - } + } else - { - show_message("loginfailed", 'warning'); + { + $OUTPUT->show_message("loginfailed", 'warning'); $_SESSION['user_id'] = ''; - } } +} // end session -else if ($_action=='logout' && isset($_SESSION['user_id'])) - { - show_message('loggedout'); +else if (($_task=='logout' || $_action=='logout') && isset($_SESSION['user_id'])) +{ + $OUTPUT->show_message('loggedout'); rcmail_kill_session(); - } +} // check session and auth cookie -else if ($_action!='login' && $_SESSION['user_id']) +else if ($_action != 'login' && $_SESSION['user_id'] && $_action != 'send') +{ + if (!rcmail_authenticate_session()) { - if (!rcmail_authenticate_session() || - ($CONFIG['session_lifetime'] && isset($SESS_CHANGED) && $SESS_CHANGED + $CONFIG['session_lifetime']*60 < mktime())) - { - $message = show_message('sessionerror', 'error'); + $OUTPUT->show_message('sessionerror', 'error'); rcmail_kill_session(); - } } +} // log in to imap server if (!empty($_SESSION['user_id']) && $_task=='mail') - { +{ $conn = $IMAP->connect($_SESSION['imap_host'], $_SESSION['username'], decrypt_passwd($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl']); if (!$conn) - { - show_message('imaperror', 'error'); + { + $OUTPUT->show_message('imaperror', 'error'); $_SESSION['user_id'] = ''; - } + } else rcmail_set_imap_prop(); - } +} // not logged in -> set task to 'login if (empty($_SESSION['user_id'])) - { - if ($REMOTE_REQUEST) - { - $message .= "setTimeout(\"location.href='\"+this.env.comm_path+\"'\", 2000);"; - rcube_remote_response($message); - } +{ + if ($OUTPUT->ajax_call) + $OUTPUT->remote_response("setTimeout(\"location.href='\"+this.env.comm_path+\"'\", 2000);"); $_task = 'login'; - } +} // set task and action to client -$script = sprintf("%s.set_env('task', '%s');", $JS_OBJECT_NAME, $_task); +$OUTPUT->set_env('task', $_task); if (!empty($_action)) - $script .= sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $_action); - -$OUTPUT->add_script($script); + $OUTPUT->set_env('action', $_action); // not logged in -> show login page if (!$_SESSION['user_id']) - { - parse_template('login'); +{ + $OUTPUT->task = 'login'; + $OUTPUT->send('login'); exit; - } +} // handle keep-alive signal if ($_action=='keep-alive') - { - rcube_remote_response(''); +{ + $OUTPUT->reset(); + $OUTPUT->send(''); exit; - } - +} // include task specific files if ($_task=='mail') - { +{ include_once('program/steps/mail/func.inc'); - if ($_action=='show' || $_action=='print') + if ($_action=='show' || $_action=='preview' || $_action=='print') include('program/steps/mail/show.inc'); if ($_action=='get') @@ -278,7 +285,7 @@ if ($_task=='mail') if ($_action=='getunread') include('program/steps/mail/getunread.inc'); - if ($_action=='list' && isset($_GET['_remote'])) + if ($_action=='list' && isset($_REQUEST['_remote'])) include('program/steps/mail/list.inc'); if ($_action=='search') @@ -289,16 +296,19 @@ if ($_task=='mail') if ($_action=='rss') include('program/steps/mail/rss.inc'); + + if ($_action=='quotadisplay') + include('program/steps/mail/quotadisplay.inc'); // make sure the message count is refreshed $IMAP->messagecount($_SESSION['mbox'], 'ALL', TRUE); - } +} // include task specific files if ($_task=='addressbook') - { +{ include_once('program/steps/addressbook/func.inc'); if ($_action=='save') @@ -313,17 +323,23 @@ if ($_task=='addressbook') if ($_action=='show') include('program/steps/addressbook/show.inc'); - if ($_action=='list' && $_GET['_remote']) + if ($_action=='list' && $_REQUEST['_remote']) include('program/steps/addressbook/list.inc'); - if ($_action=='ldappublicsearch') - include('program/steps/addressbook/ldapsearchform.inc'); - } + if ($_action=='search') + include('program/steps/addressbook/search.inc'); + + if ($_action=='copy') + include('program/steps/addressbook/copy.inc'); + + if ($_action=='mailto') + include('program/steps/addressbook/mailto.inc'); +} // include task specific files if ($_task=='settings') - { +{ include_once('program/steps/settings/func.inc'); if ($_action=='save-identity') @@ -345,18 +361,19 @@ if ($_task=='settings') $_action=='create-folder' || $_action=='rename-folder' || $_action=='delete-folder') include('program/steps/settings/manage_folders.inc'); - } +} // parse main template -parse_template($_task); +$OUTPUT->send($_task); // if we arrive here, something went wrong -raise_error(array('code' => 404, - 'type' => 'php', - 'line' => __LINE__, - 'file' => __FILE__, - 'message' => "Invalid request"), TRUE, TRUE); +raise_error(array( + 'code' => 404, + 'type' => 'php', + 'line' => __LINE__, + 'file' => __FILE__, + 'message' => "Invalid request"), TRUE, TRUE); ?> diff --git a/program/blocked.gif b/program/blocked.gif new file mode 100644 index 0000000000000000000000000000000000000000..7a4aa9f346fd7524519aaef53828d2a516fb13ac GIT binary patch literal 118 zcmZ?wbhEHbOkpr$XkcLYb?zJk1B2pE79h#MpaUX6G7L<;jF-siv0egDVL!_|Id(Z>!St?6f$edg@-T7G5K*RHc! V+wZLV&dt|-{K=-DIob>i)&S+hH!}bL literal 0 HcmV?d00001 diff --git a/program/include/bugs.inc b/program/include/bugs.inc index f1fa72b..46384ff 100644 --- a/program/include/bugs.inc +++ b/program/include/bugs.inc @@ -15,7 +15,7 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: bugs.inc 90 2005-12-11 23:19:48Z roundcube $ + $Id: bugs.inc 347 2006-09-16 22:58:51Z estadtherr $ */ @@ -115,5 +115,4 @@ function log_bug($arg_arr) } } - -?> \ No newline at end of file +?> diff --git a/program/include/main.inc b/program/include/main.inc index c594767..8a5017b 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -5,7 +5,7 @@ | program/include/main.inc | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005, RoundCube Dev, - Switzerland | + | Copyright (C) 2005-2007, RoundCube Dev, - Switzerland | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -15,13 +15,14 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: main.inc 429 2006-12-22 22:26:24Z thomasb $ + $Id: main.inc 567 2007-05-17 18:41:24Z thomasb $ */ require_once('lib/des.inc'); require_once('lib/utf7.inc'); require_once('lib/utf8.class.php'); +require_once('include/rcmail_template.inc'); // define constannts for input reading @@ -33,46 +34,18 @@ define('RCUBE_INPUT_GPC', 0x0103); // register session and connect to server function rcmail_startup($task='mail') { - global $sess_id, $sess_auth, $sess_user_lang; - global $CONFIG, $INSTALL_PATH, $BROWSER, $OUTPUT, $_SESSION, $IMAP, $DB, $JS_OBJECT_NAME; + global $sess_id, $sess_user_lang; + global $CONFIG, $INSTALL_PATH, $BROWSER, $OUTPUT, $_SESSION, $IMAP, $DB; // check client $BROWSER = rcube_browser(); - // load config file - include_once('config/main.inc.php'); - $CONFIG = is_array($rcmail_config) ? $rcmail_config : array(); - - // load host-specific configuration - rcmail_load_host_config($CONFIG); - - $CONFIG['skin_path'] = $CONFIG['skin_path'] ? unslashify($CONFIG['skin_path']) : 'skins/default'; - - // load db conf - include_once('config/db.inc.php'); - $CONFIG = array_merge($CONFIG, $rcmail_config); - - if (empty($CONFIG['log_dir'])) - $CONFIG['log_dir'] = $INSTALL_PATH.'logs'; - else - $CONFIG['log_dir'] = unslashify($CONFIG['log_dir']); - - // set PHP error logging according to config - if ($CONFIG['debug_level'] & 1) - { - ini_set('log_errors', 1); - ini_set('error_log', $CONFIG['log_dir'].'/errors'); - } - if ($CONFIG['debug_level'] & 4) - ini_set('display_errors', 1); - else - ini_set('display_errors', 0); - + // load configuration + $CONFIG = rcmail_load_config(); // set session garbage collecting time according to session_lifetime if (!empty($CONFIG['session_lifetime'])) - ini_set('session.gc_maxlifetime', ($CONFIG['session_lifetime']+2)*60); - + ini_set('session.gc_maxlifetime', ($CONFIG['session_lifetime']) * 120); // prepare DB connection require_once('include/rcube_'.(empty($CONFIG['db_backend']) ? 'db' : $CONFIG['db_backend']).'.inc'); @@ -80,10 +53,9 @@ function rcmail_startup($task='mail') $DB = new rcube_db($CONFIG['db_dsnw'], $CONFIG['db_dsnr'], $CONFIG['db_persistent']); $DB->sqlite_initials = $INSTALL_PATH.'SQL/sqlite.initial.sql'; $DB->db_connect('w'); - - // we can use the database for storing session data - if (!$DB->is_error()) - include_once('include/session.inc'); + + // use database for storing session data + include_once('include/session.inc'); // init session session_start(); @@ -93,8 +65,8 @@ function rcmail_startup($task='mail') if (!isset($_SESSION['auth_time'])) { $_SESSION['user_lang'] = rcube_language_prop($CONFIG['locale_string']); - $_SESSION['auth_time'] = mktime(); - setcookie('sessauth', rcmail_auth_hash($sess_id, $_SESSION['auth_time'])); + $_SESSION['auth_time'] = time(); + $_SESSION['temp'] = true; } // set session vars global @@ -129,6 +101,44 @@ function rcmail_startup($task='mail') } +// load roundcube configuration into global var +function rcmail_load_config() + { + global $INSTALL_PATH; + + // load config file + include_once('config/main.inc.php'); + $conf = is_array($rcmail_config) ? $rcmail_config : array(); + + // load host-specific configuration + rcmail_load_host_config($conf); + + $conf['skin_path'] = $conf['skin_path'] ? unslashify($conf['skin_path']) : 'skins/default'; + + // load db conf + include_once('config/db.inc.php'); + $conf = array_merge($conf, $rcmail_config); + + if (empty($conf['log_dir'])) + $conf['log_dir'] = $INSTALL_PATH.'logs'; + else + $conf['log_dir'] = unslashify($conf['log_dir']); + + // set PHP error logging according to config + if ($conf['debug_level'] & 1) + { + ini_set('log_errors', 1); + ini_set('error_log', $conf['log_dir'].'/errors'); + } + if ($conf['debug_level'] & 4) + ini_set('display_errors', 1); + else + ini_set('display_errors', 0); + + return $conf; + } + + // load a host-specific config file if configured function rcmail_load_host_config(&$config) { @@ -168,16 +178,30 @@ function rcmail_auth_hash($sess_id, $ts) // compare the auth hash sent by the client with the local session credentials function rcmail_authenticate_session() { - $now = mktime(); - $valid = ($_COOKIE['sessauth'] == rcmail_auth_hash(session_id(), $_SESSION['auth_time'])); + global $CONFIG, $SESS_CLIENT_IP, $SESS_CHANGED; + + // advanced session authentication + if ($CONFIG['double_auth']) + { + $now = time(); + $valid = ($_COOKIE['sessauth'] == rcmail_auth_hash(session_id(), $_SESSION['auth_time']) || + $_COOKIE['sessauth'] == rcmail_auth_hash(session_id(), $_SESSION['last_auth'])); - // renew auth cookie every 5 minutes (only for GET requests) - if (!$valid || ($_SERVER['REQUEST_METHOD']!='POST' && $now-$_SESSION['auth_time'] > 300)) + // renew auth cookie every 5 minutes (only for GET requests) + if (!$valid || ($_SERVER['REQUEST_METHOD']!='POST' && $now-$_SESSION['auth_time'] > 300)) { - $_SESSION['auth_time'] = $now; - setcookie('sessauth', rcmail_auth_hash(session_id(), $now)); + $_SESSION['last_auth'] = $_SESSION['auth_time']; + $_SESSION['auth_time'] = $now; + setcookie('sessauth', rcmail_auth_hash(session_id(), $now)); } - + } + else + $valid = $CONFIG['ip_check'] ? $_SERVER['REMOTE_ADDR'] == $SESS_CLIENT_IP : true; + + // check session filetime + if (!empty($CONFIG['session_lifetime']) && isset($SESS_CHANGED) && $SESS_CHANGED + $CONFIG['session_lifetime']*60 < time()) + $valid = false; + return $valid; } @@ -185,7 +209,7 @@ function rcmail_authenticate_session() // create IMAP object and connect to server function rcmail_imap_init($connect=FALSE) { - global $CONFIG, $DB, $IMAP; + global $CONFIG, $DB, $IMAP, $OUTPUT; $IMAP = new rcube_imap($DB); $IMAP->debug_level = $CONFIG['debug_level']; @@ -196,7 +220,7 @@ function rcmail_imap_init($connect=FALSE) if ($connect) { if (!($conn = $IMAP->connect($_SESSION['imap_host'], $_SESSION['username'], decrypt_passwd($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl']))) - show_message('imaperror', 'error'); + $OUTPUT->show_message('imaperror', 'error'); rcmail_set_imap_prop(); } @@ -263,8 +287,8 @@ function rcmail_kill_session() rcmail_save_user_prefs($a_user_prefs); } - $_SESSION = array(); - session_destroy(); + $_SESSION = array('user_lang' => $GLOBALS['sess_user_lang'], 'auth_time' => time(), 'temp' => true); + setcookie('sessauth', '-del-', time()-60); } @@ -337,30 +361,22 @@ function rcube_language_prop($lang, $prop='lang') // init output object for GUI and add common scripts -function load_gui() +function rcmail_load_gui() { - global $CONFIG, $OUTPUT, $COMM_PATH, $JS_OBJECT_NAME, $sess_user_lang; + global $CONFIG, $OUTPUT, $sess_user_lang; // init output page - $OUTPUT = new rcube_html_page(); - - // add common javascripts - $javascript = "var $JS_OBJECT_NAME = new rcube_webmail();\n"; - $javascript .= "$JS_OBJECT_NAME.set_env('comm_path', '$COMM_PATH');\n"; + $OUTPUT = new rcmail_template($CONFIG, $GLOBALS['_task']); + $OUTPUT->set_env('comm_path', $GLOBALS['COMM_PATH']); - if (isset($CONFIG['javascript_config'] )){ - foreach ($CONFIG['javascript_config'] as $js_config_var){ - $javascript .= "$JS_OBJECT_NAME.set_env('$js_config_var', '" . $CONFIG[$js_config_var] . "');\n"; - } + if (is_array($CONFIG['javascript_config'])) + { + foreach ($CONFIG['javascript_config'] as $js_config_var) + $OUTPUT->set_env($js_config_var, $CONFIG[$js_config_var]); } - + if (!empty($GLOBALS['_framed'])) - $javascript .= "$JS_OBJECT_NAME.set_env('framed', true);\n"; - - $OUTPUT->add_script($javascript); - $OUTPUT->include_script('common.js'); - $OUTPUT->include_script('app.js'); - $OUTPUT->scripts_path = 'program/js/'; + $OUTPUT->set_env('framed', true); // set locale setting rcmail_set_locale($sess_user_lang); @@ -368,49 +384,65 @@ function load_gui() // set user-selected charset if (!empty($CONFIG['charset'])) $OUTPUT->set_charset($CONFIG['charset']); + + // register common UI objects + $OUTPUT->add_handlers(array( + 'loginform' => 'rcmail_login_form', + 'username' => 'rcmail_current_username', + 'message' => 'rcmail_message_container', + 'charsetselector' => 'rcmail_charset_selector', + )); // add some basic label to client - rcube_add_label('loading','checkingmail'); + if (!$OUTPUT->ajax_call) + rcube_add_label('loading'); } // set localization charset based on the given language function rcmail_set_locale($lang) { - global $OUTPUT, $MBSTRING, $MBSTRING_ENCODING; + global $OUTPUT, $MBSTRING; static $s_mbstring_loaded = NULL; // settings for mbstring module (by Tadashi Jokagi) - if ($s_mbstring_loaded===NULL) + if (is_null($s_mbstring_loaded)) + $MBSTRING = $s_mbstring_loaded = extension_loaded("mbstring"); + else + $MBSTRING = $s_mbstring_loaded = FALSE; + + if ($MBSTRING) + mb_internal_encoding(RCMAIL_CHARSET); + + $OUTPUT->set_charset(rcube_language_prop($lang, 'charset')); + } + + +// auto-select IMAP host based on the posted login information +function rcmail_autoselect_host() + { + global $CONFIG; + + $host = isset($_POST['_host']) ? get_input_value('_host', RCUBE_INPUT_POST) : $CONFIG['default_host']; + if (is_array($host)) { - if ($s_mbstring_loaded = extension_loaded("mbstring")) + list($user, $domain) = explode('@', get_input_value('_user', RCUBE_INPUT_POST)); + if (!empty($domain)) { - $MBSTRING = TRUE; - if (function_exists("mb_mbstring_encodings")) - $MBSTRING_ENCODING = mb_mbstring_encodings(); - else - $MBSTRING_ENCODING = array("ISO-8859-1", "UTF-7", "UTF7-IMAP", "UTF-8", - "ISO-2022-JP", "EUC-JP", "EUCJP-WIN", - "SJIS", "SJIS-WIN"); - - $MBSTRING_ENCODING = array_map("strtoupper", $MBSTRING_ENCODING); - if (in_array("SJIS", $MBSTRING_ENCODING)) - $MBSTRING_ENCODING[] = "SHIFT_JIS"; - } - else - { - $MBSTRING = FALSE; - $MBSTRING_ENCODING = array(); + foreach ($host as $imap_host => $mail_domains) + if (is_array($mail_domains) && in_array($domain, $mail_domains)) + { + $host = $imap_host; + break; + } } - } - if ($MBSTRING && function_exists("mb_language")) - { - if (!@mb_language(strtok($lang, "_"))) - $MBSTRING = FALSE; // unsupport language + // take the first entry if $host is still an array + if (is_array($host)) + $host = array_shift($host); } - - $OUTPUT->set_charset(rcube_language_prop($lang, 'charset')); + + return $host; } @@ -423,26 +455,26 @@ function rcmail_login($user, $pass, $host=NULL) if (!$host) $host = $CONFIG['default_host']; - // Validate that selected host is in the list of configured hosts - if (is_array($CONFIG['default_host'])) - { - $allowed = FALSE; - foreach ($CONFIG['default_host'] as $key => $host_allowed) - { - if (!is_numeric($key)) - $host_allowed = $key; - if ($host == $host_allowed) - { - $allowed = TRUE; - break; - } - } - if (!$allowed) - return FALSE; - } - else if (!empty($CONFIG['default_host']) && $host != $CONFIG['default_host']) - return FALSE; - + // Validate that selected host is in the list of configured hosts + if (is_array($CONFIG['default_host'])) + { + $allowed = FALSE; + foreach ($CONFIG['default_host'] as $key => $host_allowed) + { + if (!is_numeric($key)) + $host_allowed = $key; + if ($host == $host_allowed) + { + $allowed = TRUE; + break; + } + } + if (!$allowed) + return FALSE; + } + else if (!empty($CONFIG['default_host']) && $host != $CONFIG['default_host']) + return FALSE; + // parse $host URL $a_host = parse_url($host); if ($a_host['host']) @@ -459,12 +491,12 @@ function rcmail_login($user, $pass, $host=NULL) Inspired by Marco */ // Check if we need to add domain - if ($CONFIG['username_domain'] && !strstr($user, '@')) + if (!empty($CONFIG['username_domain']) && !strstr($user, '@')) { if (is_array($CONFIG['username_domain']) && isset($CONFIG['username_domain'][$host])) $user .= '@'.$CONFIG['username_domain'][$host]; - else if (!empty($CONFIG['username_domain'])) - $user .= '@'.$CONFIG['username_domain']; + else if (is_string($CONFIG['username_domain'])) + $user .= '@'.$CONFIG['username_domain']; } @@ -510,7 +542,7 @@ function rcmail_login($user, $pass, $host=NULL) // update user's record $DB->query("UPDATE ".get_table_name('users')." - SET last_login=now() + SET last_login=".$DB->now()." WHERE user_id=?", $user_id); } @@ -529,6 +561,7 @@ function rcmail_login($user, $pass, $host=NULL) $_SESSION['username'] = $user; $_SESSION['user_lang'] = $sess_user_lang; $_SESSION['password'] = encrypt_passwd($pass); + $_SESSION['login_time'] = mktime(); // force reloading complete list of subscribed mailboxes rcmail_set_imap_prop(); @@ -555,22 +588,15 @@ function rcmail_create_user($user, $host) $DB->query("INSERT INTO ".get_table_name('users')." (created, last_login, username, mail_host, alias, language) - VALUES (now(), now(), ?, ?, ?, ?)", - $user, - $host, - $user_email, - $_SESSION['user_lang']); + VALUES (".$DB->now().", ".$DB->now().", ?, ?, ?, ?)", + strip_newlines($user), + strip_newlines($host), + strip_newlines($user_email), + $_SESSION['user_lang']); if ($user_id = $DB->insert_id(get_sequence_name('users'))) { - $mail_domain = $host; - if (is_array($CONFIG['mail_domain'])) - { - if (isset($CONFIG['mail_domain'][$host])) - $mail_domain = $CONFIG['mail_domain'][$host]; - } - else if (!empty($CONFIG['mail_domain'])) - $mail_domain = $CONFIG['mail_domain']; + $mail_domain = rcmail_mail_domain($host); if ($user_email=='') $user_email = strstr($user, '@') ? $user : sprintf('%s@%s', $user, $mail_domain); @@ -578,21 +604,28 @@ function rcmail_create_user($user, $host) $user_name = $user!=$user_email ? $user : ''; // try to resolve the e-mail address from the virtuser table - if (!empty($CONFIG['virtuser_query'])) - { - $sql_result = $DB->query(preg_replace('/%u/', $user, $CONFIG['virtuser_query'])); - if ($sql_arr = $DB->fetch_array($sql_result)) - $user_email = $sql_arr[0]; + if (!empty($CONFIG['virtuser_query']) && + ($sql_result = $DB->query(preg_replace('/%u/', $user, $CONFIG['virtuser_query']))) && + ($DB->num_rows()>0)) + while ($sql_arr = $DB->fetch_array($sql_result)) + { + $DB->query("INSERT INTO ".get_table_name('identities')." + (user_id, del, standard, name, email) + VALUES (?, 0, 1, ?, ?)", + $user_id, + strip_newlines($user_name), + preg_replace('/^@/', $user . '@', $sql_arr[0])); + } + else + { + // also create new identity records + $DB->query("INSERT INTO ".get_table_name('identities')." + (user_id, del, standard, name, email) + VALUES (?, 0, 1, ?, ?)", + $user_id, + strip_newlines($user_name), + strip_newlines($user_email)); } - - // also create new identity records - $DB->query("INSERT INTO ".get_table_name('identities')." - (user_id, del, standard, name, email) - VALUES (?, 0, 1, ?, ?)", - $user_id, - $user_name, - $user_email); - // get existing mailboxes $a_mailboxes = $IMAP->list_mailboxes(); @@ -714,44 +747,42 @@ function rcmail_save_user_prefs($a_user_prefs) // overwrite action variable function rcmail_overwrite_action($action) { - global $OUTPUT, $JS_OBJECT_NAME; + global $OUTPUT; $GLOBALS['_action'] = $action; - - $OUTPUT->add_script(sprintf("\n%s.set_env('action', '%s');", $JS_OBJECT_NAME, $action)); + $OUTPUT->set_env('action', $action); } -function show_message($message, $type='notice', $vars=NULL) - { - global $OUTPUT, $JS_OBJECT_NAME, $REMOTE_REQUEST; +/** + * Compose an URL for a specific action + * + * @param string Request action + * @param array More URL parameters + * @param string Request task (omit if the same) + * @return The application URL + */ +function rcmail_url($action, $p=array(), $task=null) +{ + global $MAIN_TASKS, $COMM_PATH; + $qstring = ''; + $base = $COMM_PATH; - $framed = $GLOBALS['_framed']; - $command = sprintf("display_message('%s', '%s');", - addslashes(rep_specialchars_output(rcube_label(array('name' => $message, 'vars' => $vars)))), - $type); - - if ($REMOTE_REQUEST) - return 'this.'.$command; + if ($task && in_array($task, $MAIN_TASKS)) + $base = ereg_replace('_task=[a-z]+', '_task='.$task, $COMM_PATH); - else - $OUTPUT->add_script(sprintf("%s%s.%s\n", - $framed ? sprintf('if(parent.%s)parent.', $JS_OBJECT_NAME) : '', - $JS_OBJECT_NAME, - $command)); + if (is_array($p)) + foreach ($p as $key => $val) + $qstring .= '&'.urlencode($key).'='.urlencode($val); - // console(rcube_label($message)); - } + return $base . ($action ? '&_action='.$action : '') . $qstring; +} -function console($msg, $type=1) +// @deprecated +function show_message($message, $type='notice', $vars=NULL) { - if ($GLOBALS['REMOTE_REQUEST']) - print "// $msg\n"; - else - { - print $msg; - print "\n
\n"; - } + global $OUTPUT; + $OUTPUT->show_message($message, $type, $vars); } @@ -787,43 +818,6 @@ function get_des_key() } -// send correct response on a remote request -function rcube_remote_response($js_code, $flush=FALSE) - { - global $OUTPUT, $CHARSET; - static $s_header_sent = FALSE; - - if (!$s_header_sent) - { - $s_header_sent = TRUE; - send_nocacheing_headers(); - header('Content-Type: application/x-javascript; charset='.$CHARSET); - print '/** remote response ['.date('d/M/Y h:i:s O')."] **/\n"; - } - - // send response code - print rcube_charset_convert($js_code, $CHARSET, $OUTPUT->get_charset()); - - if ($flush) // flush the output buffer - flush(); - else // terminate script - exit; - } - - -// send correctly formatted response for a request posted to an iframe -function rcube_iframe_response($js_code='') - { - global $OUTPUT, $JS_OBJECT_NAME; - - if (!empty($js_code)) - $OUTPUT->add_script("if(parent.$JS_OBJECT_NAME){\n" . $js_code . "\n}"); - - $OUTPUT->write(); - exit; - } - - // read directory program/localization/ and return a list of available languages function rcube_list_languages() { @@ -854,30 +848,33 @@ function rcube_list_languages() // add a localized label to the client environment function rcube_add_label() { - global $OUTPUT, $JS_OBJECT_NAME; + global $OUTPUT; $arg_list = func_get_args(); foreach ($arg_list as $i => $name) - $OUTPUT->add_script(sprintf("%s.add_label('%s', '%s');", - $JS_OBJECT_NAME, - $name, - rep_specialchars_output(rcube_label($name), 'js'))); + $OUTPUT->command('add_label', $name, rcube_label($name)); } -// remove temp files of a session -function rcmail_clear_session_temp($sess_id) +// remove temp files older than two day +function rcmail_temp_gc() { - global $CONFIG; + $tmp = unslashify($CONFIG['temp_dir']); + $expire = mktime() - 172800; // expire in 48 hours - $temp_dir = slashify($CONFIG['temp_dir']); - $cache_dir = $temp_dir.$sess_id; - - if (is_dir($cache_dir)) + if ($dir = opendir($tmp)) { - clear_directory($cache_dir); - rmdir($cache_dir); - } + while (($fname = readdir($dir)) !== false) + { + if ($fname{0} == '.') + continue; + + if (filemtime($tmp.'/'.$fname) < $expire) + @unlink($tmp.'/'.$fname); + } + + closedir($dir); + } } @@ -898,26 +895,34 @@ function rcmail_message_cache_gc() } -// convert a string from one charset to another -// this function is not complete and not tested well +/** + * Convert a string from one charset to another. + * Uses mbstring and iconv functions if possible + * + * @param string Input string + * @param string Suspected charset of the input string + * @param string Target charset to convert to; defaults to RCMAIL_CHARSET + * @return Converted string + */ function rcube_charset_convert($str, $from, $to=NULL) { - global $MBSTRING, $MBSTRING_ENCODING; + global $MBSTRING; $from = strtoupper($from); - $to = $to==NULL ? strtoupper($GLOBALS['CHARSET']) : strtoupper($to); + $to = $to==NULL ? strtoupper(RCMAIL_CHARSET) : strtoupper($to); - if ($from==$to) + if ($from==$to || $str=='' || empty($from)) return $str; - + // convert charset using mbstring module if ($MBSTRING) { $to = $to=="UTF-7" ? "UTF7-IMAP" : $to; $from = $from=="UTF-7" ? "UTF7-IMAP": $from; - - if (in_array($to, $MBSTRING_ENCODING) && in_array($from, $MBSTRING_ENCODING)) - return mb_convert_encoding($str, $to, $from); + + // return if convert succeeded + if (($out = mb_convert_encoding($str, $to, $from)) != '') + return $out; } // convert charset using iconv module @@ -928,8 +933,8 @@ function rcube_charset_convert($str, $from, $to=NULL) // convert string to UTF-8 if ($from=='UTF-7') - $str = rcube_charset_convert(UTF7DecodeString($str), 'ISO-8859-1'); - else if ($from=='ISO-8859-1' && function_exists('utf8_encode')) + $str = utf7_to_utf8($str); + else if (($from=='ISO-8859-1') && function_exists('utf8_encode')) $str = utf8_encode($str); else if ($from!='UTF-8') { @@ -939,7 +944,7 @@ function rcube_charset_convert($str, $from, $to=NULL) // encode string for output if ($to=='UTF-7') - return UTF7EncodeString($str); + return utf8_to_utf7($str); else if ($to=='ISO-8859-1' && function_exists('utf8_decode')) return utf8_decode($str); else if ($to!='UTF-8') @@ -953,12 +958,19 @@ function rcube_charset_convert($str, $from, $to=NULL) } - -// replace specials characters to a specific encoding type +/** + * Replacing specials characters to a specific encoding type + * + * @param string Input string + * @param string Encoding type: text|html|xml|js|url + * @param string Replace mode for tags: show|replace|remove + * @param boolean Convert newlines + * @return The quoted string + */ function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) { global $OUTPUT_TYPE, $OUTPUT; - static $html_encode_arr, $js_rep_table, $rtf_rep_table, $xml_rep_table; + static $html_encode_arr, $js_rep_table, $xml_rep_table; if (!$enctype) $enctype = $GLOBALS['OUTPUT_TYPE']; @@ -978,7 +990,6 @@ function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) { $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS); unset($html_encode_arr['?']); - unset($html_encode_arr['&']); } $ltpos = strpos($str, '<'); @@ -990,41 +1001,39 @@ function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) unset($encode_arr['"']); unset($encode_arr['<']); unset($encode_arr['>']); + unset($encode_arr['&']); } else if ($mode=='remove') $str = strip_tags($str); - - $out = strtr($str, $encode_arr); + + // avoid douple quotation of & + $out = preg_replace('/&([a-z]{2,5}|#[0-9]{2,4});/', '&\\1;', 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 the replace tables for XML and JS are not yet defined if (!$js_rep_table) { - $js_rep_table = $rtf_rep_table = $xml_rep_table = array(); + $js_rep_table = $xml_rep_table = array(); $xml_rep_table['&'] = '&'; 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 ($OUTPUT->get_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 + // encode for XML if ($enctype=='xml') return strtr($str, $xml_rep_table); @@ -1032,18 +1041,32 @@ function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) if ($enctype=='js') { if ($OUTPUT->get_charset()!='UTF-8') - $str = rcube_charset_convert($str, $GLOBALS['CHARSET'], $OUTPUT->get_charset()); + $str = rcube_charset_convert($str, RCMAIL_CHARSET, $OUTPUT->get_charset()); - return preg_replace(array("/\r\n/", '/"/', "/([^\\\])'/"), array('\n', '\"', "$1\'"), strtr($str, $js_rep_table)); + return preg_replace(array("/\r?\n/", "/\r/"), array('\n', '\n'), addslashes(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; } + +/** + * Quote a given string. Alias function for rep_specialchars_output + * @see rep_specialchars_output + */ +function Q($str, $mode='strict', $newlines=TRUE) + { + return rep_specialchars_output($str, 'html', $mode, $newlines); + } + +/** + * Quote a given string. Alias function for rep_specialchars_output + * @see rep_specialchars_output + */ +function JQ($str) + { + return rep_specialchars_output($str, 'js'); + } /** @@ -1098,14 +1121,19 @@ function strip_quotes($str) return preg_replace('/[\'"]/', '', $str); } - -// ************** template parsing and gui functions ************** +/** + * Remove new lines characters from given string + */ +function strip_newlines($str) +{ + return preg_replace('/[\r\n]/', '', $str); +} // return boolean if a specific template exists function template_exists($name) { - global $CONFIG, $OUTPUT; + global $CONFIG; $skin_path = $CONFIG['skin_path']; // check template file @@ -1113,352 +1141,11 @@ function template_exists($name) } -// get page template an replace variable -// similar function as used in nexImage -function parse_template($name='main', $exit=TRUE) - { - global $CONFIG, $OUTPUT; - $skin_path = $CONFIG['skin_path']; - - // read template file - $templ = ''; - $path = "$skin_path/templates/$name.html"; - - if($fp = @fopen($path, 'r')) - { - $templ = fread($fp, filesize($path)); - fclose($fp); - } - else - { - raise_error(array('code' => 500, - 'type' => 'php', - 'line' => __LINE__, - 'file' => __FILE__, - 'message' => "Error loading template for '$name'"), TRUE, TRUE); - return FALSE; - } - - - // parse for specialtags - $output = parse_rcube_xml($templ); - - $OUTPUT->write(trim(parse_with_globals($output)), $skin_path); - - if ($exit) - exit; - } - - - -// replace all strings ($varname) with the content of the according global variable -function parse_with_globals($input) - { - $GLOBALS['__comm_path'] = $GLOBALS['COMM_PATH']; - $output = preg_replace('/\$(__[a-z0-9_\-]+)/e', '$GLOBALS["\\1"]', $input); - return $output; - } - - - -function parse_rcube_xml($input) +// Wrapper for rcmail_template::parse() +// @deprecated +function parse_template($name='main', $exit=true) { - $output = preg_replace('/]+)>/Uie', "rcube_xml_command('\\1', '\\2')", $input); - return $output; - } - - -function rcube_xml_command($command, $str_attrib, $add_attrib=array()) - { - global $IMAP, $CONFIG, $OUTPUT; - - $command = strtolower($command); - $attrib = parse_attrib_string($str_attrib) + $add_attrib; - - // execute command - switch ($command) - { - // return a button - case 'button': - if ($attrib['command']) - return rcube_button($attrib); - break; - - // show a label - case 'label': - if ($attrib['name'] || $attrib['command']) - return rep_specialchars_output(rcube_label($attrib)); - break; - - // create a menu item - case 'menu': - if ($attrib['command'] && $attrib['group']) - rcube_menu($attrib); - break; - - // include a file - case 'include': - $path = realpath($CONFIG['skin_path'].$attrib['file']); - - if($fp = @fopen($path, 'r')) - { - $incl = fread($fp, filesize($path)); - fclose($fp); - return parse_rcube_xml($incl); - } - break; - - // return code for a specific application object - case 'object': - $object = strtolower($attrib['name']); - - $object_handlers = array( - // GENERAL - 'loginform' => 'rcmail_login_form', - 'username' => 'rcmail_current_username', - - // MAIL - 'mailboxlist' => 'rcmail_mailbox_list', - 'message' => 'rcmail_message_container', - 'messages' => 'rcmail_message_list', - 'messagecountdisplay' => 'rcmail_messagecount_display', - 'quotadisplay' => 'rcmail_quota_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', - 'searchform' => 'rcmail_search_form', - 'receiptcheckbox' => 'rcmail_receipt_checkbox', - - // ADDRESS BOOK - 'addresslist' => 'rcmail_contacts_list', - 'addressframe' => 'rcmail_contact_frame', - 'recordscountdisplay' => 'rcmail_rowcount_display', - 'contactdetails' => 'rcmail_contact_details', - 'contacteditform' => 'rcmail_contact_editform', - 'ldappublicsearch' => 'rcmail_ldap_public_search_form', - 'ldappublicaddresslist' => 'rcmail_ldap_public_list', - - // 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', - 'renamefolder' => 'rcube_rename_folder_form', - 'composebody' => 'rcmail_compose_body' - ); - - - // execute object handler function - if ($object_handlers[$object] && function_exists($object_handlers[$object])) - return call_user_func($object_handlers[$object], $attrib); - - else if ($object=='productname') - { - $name = !empty($CONFIG['product_name']) ? $CONFIG['product_name'] : 'RoundCube Webmail'; - return rep_specialchars_output($name, 'html', 'all'); - } - else if ($object=='version') - { - return (string)RCMAIL_VERSION; - } - else if ($object=='pagetitle') - { - $task = $GLOBALS['_task']; - $title = !empty($CONFIG['product_name']) ? $CONFIG['product_name'].' :: ' : ''; - - if ($task=='login') - $title = rcube_label(array('name' => 'welcome', 'vars' => array('product' => $CONFIG['product_name']))); - else if ($task=='mail' && isset($GLOBALS['MESSAGE']['subject'])) - $title .= $GLOBALS['MESSAGE']['subject']; - else if (isset($GLOBALS['PAGE_TITLE'])) - $title .= $GLOBALS['PAGE_TITLE']; - else if ($task=='mail' && ($mbox_name = $IMAP->get_mailbox_name())) - $title .= rcube_charset_convert($mbox_name, 'UTF-7', 'UTF-8'); - else - $title .= ucfirst($task); - - return rep_specialchars_output($title, 'html', 'all'); - } - - break; - } - - return ''; - } - - -// create and register a button -function rcube_button($attrib) - { - global $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $BROWSER, $COMM_PATH, $MAIN_TASKS; - static $sa_buttons = array(); - static $s_button_count = 100; - - // these commands can be called directly via url - $a_static_commands = array('compose', 'list'); - - $skin_path = $CONFIG['skin_path']; - - if (!($attrib['command'] || $attrib['name'])) - return ''; - - // try to find out the button type - if ($attrib['type']) - $attrib['type'] = strtolower($attrib['type']); - else - $attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $arg['imageact']) ? 'image' : 'link'; - - - $command = $attrib['command']; - - // take the button from the stack - if($attrib['name'] && $sa_buttons[$attrib['name']]) - $attrib = $sa_buttons[$attrib['name']]; - - // add button to button stack - else if($attrib['image'] || $arg['imageact'] || $attrib['imagepas'] || $attrib['class']) - { - if(!$attrib['name']) - $attrib['name'] = $command; - - if (!$attrib['image']) - $attrib['image'] = $attrib['imagepas'] ? $attrib['imagepas'] : $attrib['imageact']; - - $sa_buttons[$attrib['name']] = $attrib; - } - - // get saved button for this command/name - else if ($command && $sa_buttons[$command]) - $attrib = $sa_buttons[$command]; - - //else - // return ''; - - - // set border to 0 because of the link arround the button - if ($attrib['type']=='image' && !isset($attrib['border'])) - $attrib['border'] = 0; - - if (!$attrib['id']) - $attrib['id'] = sprintf('rcmbtn%d', $s_button_count++); - - // get localized text for labels and titles - if ($attrib['title']) - $attrib['title'] = rep_specialchars_output(rcube_label($attrib['title'])); - if ($attrib['label']) - $attrib['label'] = rep_specialchars_output(rcube_label($attrib['label'])); - - if ($attrib['alt']) - $attrib['alt'] = rep_specialchars_output(rcube_label($attrib['alt'])); - - // set title to alt attribute for IE browsers - if ($BROWSER['ie'] && $attrib['title'] && !$attrib['alt']) - { - $attrib['alt'] = $attrib['title']; - unset($attrib['title']); - } - - // add empty alt attribute for XHTML compatibility - if (!isset($attrib['alt'])) - $attrib['alt'] = ''; - - - // register button in the system - if ($attrib['command']) - { - $OUTPUT->add_script(sprintf("%s.register_button('%s', '%s', '%s', '%s', '%s', '%s');", - $JS_OBJECT_NAME, - $command, - $attrib['id'], - $attrib['type'], - $attrib['imageact'] ? $skin_path.$attrib['imageact'] : $attrib['classact'], - $attrib['imagesel'] ? $skin_path.$attrib['imagesel'] : $attrib['classsel'], - $attrib['imageover'] ? $skin_path.$attrib['imageover'] : '')); - - // make valid href to specific buttons - if (in_array($attrib['command'], $MAIN_TASKS)) - $attrib['href'] = htmlentities(ereg_replace('_task=[a-z]+', '_task='.$attrib['command'], $COMM_PATH)); - else if (in_array($attrib['command'], $a_static_commands)) - $attrib['href'] = htmlentities($COMM_PATH.'&_action='.$attrib['command']); - } - - // overwrite attributes - if (!$attrib['href']) - $attrib['href'] = '#'; - - if ($command) - $attrib['onclick'] = sprintf("return %s.command('%s','%s',this)", $JS_OBJECT_NAME, $command, $attrib['prop']); - - if ($command && $attrib['imageover']) - { - $attrib['onmouseover'] = sprintf("return %s.button_over('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']); - $attrib['onmouseout'] = sprintf("return %s.button_out('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']); - } - - if ($command && $attrib['imagesel']) - { - $attrib['onmousedown'] = sprintf("return %s.button_sel('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']); - $attrib['onmouseup'] = sprintf("return %s.button_out('%s','%s')", $JS_OBJECT_NAME, $command, $attrib['id']); - } - - $out = ''; - - // generate image tag - if ($attrib['type']=='image') - { - $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']) - $btn_content .= ' '.$attrib['label']; - - $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'title'); - } - else if ($attrib['type']=='link') - { - $btn_content = $attrib['label'] ? $attrib['label'] : $attrib['command']; - $link_attrib = array('href', 'onclick', 'title', 'id', 'class', 'style'); - } - else if ($attrib['type']=='input') - { - $attrib['type'] = 'button'; - - if ($attrib['label']) - $attrib['value'] = $attrib['label']; - - $attrib_str = create_attrib_string($attrib, array('type', 'value', 'onclick', 'id', 'class', 'style')); - $out = sprintf('', $attrib_str); - } - - // generate html code for button - if ($btn_content) - { - $attrib_str = create_attrib_string($attrib, $link_attrib); - $out = sprintf('%s', $attrib_str, $btn_content); - } - - return $out; - } - - -function rcube_menu($attrib) - { - - return ''; + $GLOBALS['OUTPUT']->parse($name, $exit); } @@ -1476,12 +1163,11 @@ function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col) $table .= "\n"; foreach ($a_show_cols as $col) - $table .= '' . rep_specialchars_output(rcube_label($col)) . "\n"; + $table .= '' . Q(rcube_label($col)) . "\n"; $table .= "\n\n"; $c = 0; - if (!is_array($table_data)) { while ($table_data && ($sql_arr = $DB->fetch_assoc($table_data))) @@ -1493,8 +1179,8 @@ function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col) // format each col foreach ($a_show_cols as $col) { - $cont = rep_specialchars_output($sql_arr[$col]); - $table .= '' . $cont . "\n"; + $cont = Q($sql_arr[$col]); + $table .= '' . $cont . "\n"; } $table .= "\n"; @@ -1512,8 +1198,8 @@ function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col) // format each col foreach ($a_show_cols as $col) { - $cont = rep_specialchars_output($row_data[$col]); - $table .= '' . $cont . "\n"; + $cont = Q($row_data[$col]); + $table .= '' . $cont . "\n"; } $table .= "\n"; @@ -1528,7 +1214,15 @@ function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col) } - +/** + * Create an edit field for inclusion on a form + * + * @param string col field name + * @param string value field value + * @param array attrib HTML element attributes for field + * @param string type HTML element type (default 'text') + * @return string HTML field definition + */ function rcmail_get_edit_field($col, $value, $attrib, $type='text') { $fname = '_'.$col; @@ -1557,6 +1251,24 @@ function rcmail_get_edit_field($col, $value, $attrib, $type='text') } +// return the mail domain configured for the given host +function rcmail_mail_domain($host) + { + global $CONFIG; + + $domain = $host; + if (is_array($CONFIG['mail_domain'])) + { + if (isset($CONFIG['mail_domain'][$host])) + $domain = $CONFIG['mail_domain'][$host]; + } + else if (!empty($CONFIG['mail_domain'])) + $domain = $CONFIG['mail_domain']; + + return $domain; + } + + // compose a valid attribute string for HTML tags function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style')) { @@ -1574,12 +1286,12 @@ function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'st function parse_attrib_string($str) { $attrib = array(); - preg_match_all('/\s*([-_a-z]+)=["]([^"]+)["]?/i', stripslashes($str), $regs, PREG_SET_ORDER); + preg_match_all('/\s*([-_a-z]+)=(["\'])([^"]+)\2/Ui', stripslashes($str), $regs, PREG_SET_ORDER); // convert attributes to an associative array (name => value) if ($regs) foreach ($regs as $attr) - $attrib[strtolower($attr[1])] = $attr[2]; + $attrib[strtolower($attr[1])] = $attr[3]; return $attrib; } @@ -1611,15 +1323,15 @@ function format_date($date, $format=NULL) $now = time(); // local time $now -= (int)date('Z'); // make GMT time $now += ($tz * 3600); // user's time - $now_date = getdate(); + $now_date = getdate($now); $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']); $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']); // define date format depending on current time - if ($CONFIG['prettydate'] && !$format && $timestamp > $today_limit) - return sprintf('%s %s', rcube_label('today'), date('H:i', $timestamp)); - else if ($CONFIG['prettydate'] && !$format && $timestamp > $week_limit) + if ($CONFIG['prettydate'] && !$format && $timestamp > $today_limit && $timestamp < $now) + return sprintf('%s %s', rcube_label('today'), date($CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i', $timestamp)); + else if ($CONFIG['prettydate'] && !$format && $timestamp > $week_limit && $timestamp < $now) $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i'; else if (!$format) $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i'; @@ -1656,13 +1368,23 @@ function format_date($date, $format=NULL) } +function format_email_recipient($email, $name='') + { + if ($name && $name != $email) + return sprintf('%s <%s>', strpos($name, ",") ? '"'.$name.'"' : $name, $email); + else + return $email; + } + + + // ************** functions delivering gui objects ************** function rcmail_message_container($attrib) { - global $OUTPUT, $JS_OBJECT_NAME; + global $OUTPUT; if (!$attrib['id']) $attrib['id'] = 'rcmMessageContainer'; @@ -1671,7 +1393,7 @@ function rcmail_message_container($attrib) $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id')); $out = '"; - $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('message', '$attrib[id]');"); + $OUTPUT->add_gui_object('message', $attrib['id']); return $out; } @@ -1712,14 +1434,14 @@ function rcmail_current_username($attrib) // return code for the webmail login form function rcmail_login_form($attrib) { - global $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $SESS_HIDDEN_FIELD; + global $CONFIG, $OUTPUT, $SESS_HIDDEN_FIELD; $labels = array(); $labels['user'] = rcube_label('username'); $labels['pass'] = rcube_label('password'); $labels['host'] = rcube_label('server'); - $input_user = new textfield(array('name' => '_user', 'id' => 'rcmloginuser', 'size' => 30)); + $input_user = new textfield(array('name' => '_user', 'id' => 'rcmloginuser', 'size' => 30, 'autocomplete' => 'off')); $input_pass = new passwordfield(array('name' => '_pass', 'id' => 'rcmloginpwd', 'size' => 30)); $input_action = new hiddenfield(array('name' => '_action', 'value' => 'login')); @@ -1733,14 +1455,22 @@ function rcmail_login_form($attrib) $select_host = new select(array('name' => '_host', 'id' => 'rcmloginhost')); foreach ($CONFIG['default_host'] as $key => $value) - $select_host->add($value, (is_numeric($key) ? $value : $key)); + { + if (!is_array($value)) + $select_host->add($value, (is_numeric($key) ? $value : $key)); + else + { + unset($select_host); + break; + } + } - $fields['host'] = $select_host->show($_POST['_host']); + $fields['host'] = isset($select_host) ? $select_host->show($_POST['_host']) : null; } else if (!strlen($CONFIG['default_host'])) { - $input_host = new textfield(array('name' => '_host', 'id' => 'rcmloginhost', 'size' => 30)); - $fields['host'] = $input_host->show($_POST['_host']); + $input_host = new textfield(array('name' => '_host', 'id' => 'rcmloginhost', 'size' => 30)); + $fields['host'] = $input_host->show($_POST['_host']); } $form_name = strlen($attrib['form']) ? $attrib['form'] : 'form'; @@ -1757,7 +1487,7 @@ function rcmail_login_form($attrib) EOF; - $OUTPUT->add_script("$JS_OBJECT_NAME.gui_object('loginform', '$form_name');"); + $OUTPUT->add_gui_object('loginform', $form_name); $out = <<show(); + + $OUTPUT->add_gui_object('qsearchbox', $attrib['id']); + + // add form tag around text field + if (empty($attrib['form'])) + $out = sprintf( + '
%s
', + JS_OBJECT_NAME, + $out); + + return $out; + } + + +/****** debugging functions ********/ + + +/** + * Print or write debug messages + * + * @param mixed Debug message or data + */ +function console($msg) + { + if (!is_string($msg)) + $msg = var_export($msg, true); + + if (!($GLOBALS['CONFIG']['debug_level'] & 4)) + write_log('console', $msg); + else if ($GLOBALS['REMOTE_REQUEST']) + print "/*\n $msg \n*/\n"; + else + { + print '
';
+    print $msg;
+    print "
\n"; + } + } + + +/** + * Append a line to a logfile in the logs directory. + * Date will be added automatically to the line. + * + * @param $name Name of logfile + * @param $line Line to append + */ +function write_log($name, $line) + { + global $CONFIG; + + if (!is_string($line)) + $line = var_export($line, true); + + $log_entry = sprintf("[%s]: %s\n", + date("d-M-Y H:i:s O", mktime()), + $line); + + if (empty($CONFIG['log_dir'])) + $CONFIG['log_dir'] = $INSTALL_PATH.'logs'; + + // try to open specific log file for writing + if ($fp = @fopen($CONFIG['log_dir'].'/'.$name, 'a')) + { + fwrite($fp, $log_entry); + fclose($fp); + } + } + function rcube_timer() { diff --git a/program/include/rcmail_template.inc b/program/include/rcmail_template.inc new file mode 100644 index 0000000..3609bec --- /dev/null +++ b/program/include/rcmail_template.inc @@ -0,0 +1,631 @@ + | + +-----------------------------------------------------------------------+ + + $Id: $ + +*/ + +require_once('include/rcube_shared.inc'); + + +class rcmail_template extends rcube_html_page +{ + var $config; + var $task = ''; + var $framed = false; + var $ajax_call = false; + var $pagetitle = ''; + var $env = array(); + var $js_env = array(); + var $js_commands = array(); + var $object_handlers = array(); + + + // PHP 5 constructor + function __construct(&$config, $task) + { + parent::__construct(); + + $this->task = $task; + $this->config = $config; + $this->ajax_call = !empty($_GET['_remote']) || !empty($_POST['_remote']); + + // add common javascripts + if (!$this->ajax_call) + { + $javascript = "var ".JS_OBJECT_NAME." = new rcube_webmail();"; + + // don't wait for page onload. Call init at the bottom of the page (delayed) + $javascript_foot = "if (window.call_init)\n call_init('".JS_OBJECT_NAME."');"; + + $this->add_script($javascript, 'head_top'); + $this->add_script($javascript_foot, 'foot'); + $this->scripts_path = 'program/js/'; + $this->include_script('common.js'); + $this->include_script('app.js'); + } + } + + // PHP 4 compatibility + function rcmail_template(&$config, $task) + { + $this->__construct($config, $task); + } + + + /** + * Set environment variable + */ + function set_env($name, $value, $addtojs=true) + { + $this->env[$name] = $value; + if ($addtojs || isset($this->js_env[$name])) + $this->js_env[$name] = $value; + } + + + /** + * Set page title variable + */ + function set_pagetitle($title) + { + $this->pagetitle = $title; + } + + + /** + * Register a template object handler + * + * @param string Object name + * @param string Function name to call + */ + function add_handler($obj, $func) + { + $this->object_handlers[$obj] = $func; + } + + /** + * Register a list of template object handlers + * + * @param array Hash array with object=>handler pairs + */ + function add_handlers($arr) + { + $this->object_handlers = array_merge($this->object_handlers, $arr); + } + + /** + * Register a GUI object to the client script + * + * @param string Object name + * @param string Object ID + */ + function add_gui_object($obj, $id) + { + $this->add_script(JS_OBJECT_NAME.".gui_object('$obj', '$id');"); + } + + + /** + * Call a client method + * + * @param string Method to call + * @param ... Additional arguments + */ + function command() + { + $this->js_commands[] = func_get_args(); + } + + + /** + * Invoke display_message command + */ + function show_message($message, $type='notice', $vars=NULL) + { + $this->command( + 'display_message', + rcube_label(array('name' => $message, 'vars' => $vars)), + $type); + } + + + /** + * Delete all stored env variables and commands + */ + function reset() + { + $this->env = array(); + $this->js_env = array(); + $this->js_commands = array(); + $this->object_handlers = array(); + parent::reset(); + } + + /** + * Send the request output to the client. + * This will either parse a skin tempalte or send an AJAX response + * + * @param string Template name + * @param boolean True if script should terminate (default) + */ + function send($templ=null, $exit=true) + { + if ($this->ajax_call) + $this->remote_response('', !$exit); + else if ($templ != 'iframe') + $this->parse($templ, false); + else + { + $this->framed = $templ == 'iframe' ? true : $this->framed; + $this->write(); + } + + if ($exit) + exit; + } + + + /** + * Send an AJAX response with executable JS code + * + * @param string Additional JS code + * @param boolean True if output buffer should be flushed + */ + function remote_response($add='', $flush=false) + { + static $s_header_sent = FALSE; + + if (!$s_header_sent) + { + $s_header_sent = TRUE; + send_nocacheing_headers(); + header('Content-Type: application/x-javascript; charset='.RCMAIL_CHARSET); + print '/** ajax response ['.date('d/M/Y h:i:s O')."] **/\n"; + } + + // unset default env vars + unset($this->js_env['task'], $this->js_env['action'], $this->js_env['comm_path']); + + // send response code + print rcube_charset_convert($this->get_js_commands() . $add, RCMAIL_CHARSET, $this->get_charset()); + + if ($flush) // flush the output buffer + flush(); + } + + + /** + * @override + */ + function write($template='') + { + // write all env variables to client + $js = $this->framed ? "if(window.parent) {\n" : ''; + $js .= $this->get_js_commands() . ($this->framed ? ' }' : ''); + $this->add_script($js, 'head_top'); + + // call super method + parent::write($template, $this->config['skin_path']); + } + + + /** + * Parse a specific skin template and deliver to stdout + * + * @param string Template name + * @param boolean Exit script + */ + function parse($name='main', $exit=true) + { + $skin_path = $this->config['skin_path']; + + // read template file + $templ = ''; + $path = "$skin_path/templates/$name.html"; + + if($fp = @fopen($path, 'r')) + { + $templ = fread($fp, filesize($path)); + fclose($fp); + } + else + { + raise_error(array( + 'code' => 501, + 'type' => 'php', + 'line' => __LINE__, + 'file' => __FILE__, + 'message' => "Error loading template for '$name'"), TRUE, TRUE); + return FALSE; + } + + // parse for specialtags + $output = $this->parse_xml($this->parse_conditions($templ)); + + // add debug console + if ($this->config['debug_level'] & 8) + $this->add_footer('
+ console +
'); + + $this->write(trim($this->parse_with_globals($output)), $skin_path); + + if ($exit) + exit; + } + + + /** + * Return executable javascript code for all registered commands + * @private + */ + function get_js_commands() + { + $out = ''; + if (!$this->framed) + $out .= ($this->ajax_call ? 'this' : JS_OBJECT_NAME) . '.set_env('.json_serialize($this->js_env).");\n"; + + foreach ($this->js_commands as $i => $args) + { + $method = array_shift($args); + foreach ($args as $i => $arg) + $args[$i] = json_serialize($arg); + + $parent = $this->framed || preg_match('/^parent\./', $method); + $out .= sprintf( + "%s.%s(%s);\n", + $this->ajax_call ? 'this' : ($parent ? 'parent.' : '') . JS_OBJECT_NAME, + preg_replace('/^parent\./', '', $method), + join(',', $args)); + } + + return $out; + } + + /** + * Make URLs starting with a slash point to skin directory + */ + function abs_url($str) + { + return preg_replace('/^\//', $this->config['skin_path'].'/', $str); + } + + + + /***** Template parsing methods *****/ + + /** + * Replace all strings ($varname) with the content + * of the according global variable. + */ + function parse_with_globals($input) + { + $GLOBALS['__comm_path'] = $GLOBALS['COMM_PATH']; + return preg_replace('/\$(__[a-z0-9_\-]+)/e', '$GLOBALS["\\1"]', $input); + } + + + /** + * Parse for conditional tags + */ + function parse_conditions($input) + { + if (($matches = preg_split('/]+)>/is', $input, 2, PREG_SPLIT_DELIM_CAPTURE)) && count($matches)==4) + { + if (preg_match('/^(else|endif)$/i', $matches[1])) + return $matches[0] . $this->parse_conditions($matches[3]); + else + { + $attrib = parse_attrib_string($matches[2]); + if (isset($attrib['condition'])) + { + $condmet = $this->check_condition($attrib['condition']); + $submatches = preg_split('/]+)>/is', $matches[3], 2, PREG_SPLIT_DELIM_CAPTURE); + + if ($condmet) + $result = $submatches[0] . ($submatches[1] != 'endif' ? preg_replace('/.*]+>/Uis', '', $submatches[3], 1) : $submatches[3]); + else + $result = "" . $submatches[3]; + + return $matches[0] . $this->parse_conditions($result); + } + else + { + raise_error(array('code' => 500, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__, + 'message' => "Unable to parse conditional tag " . $matches[2]), TRUE, FALSE); + } + } + } + + return $input; + } + + + /** + * Determines if a given condition is met + * + * @return True if condition is valid, False is not + */ + function check_condition($condition) + { + $condition = preg_replace( + array('/session:([a-z0-9_]+)/i', '/config:([a-z0-9_]+)/i', '/env:([a-z0-9_]+)/i', '/request:([a-z0-9_]+)/ie'), + array("\$_SESSION['\\1']", "\$this->config['\\1']", "\$this->env['\\1']", "get_input_value('\\1', RCUBE_INPUT_GPC)"), + $condition); + + return @eval("return (".$condition.");"); + } + + + /** + * Search for special tags in input and replace them + * with the appropriate content + * + * @param string Input string to parse + * @return Altered input string + */ + function parse_xml($input) + { + return preg_replace('/]+)>/Uie', "\$this->xml_command('\\1', '\\2')", $input); + } + + + /** + * Convert a xml command tag into real content + * + * @param string Tag command: object,button,label, etc. + * @param string Attribute string + * @return Tag/Object content string + */ + function xml_command($command, $str_attrib, $add_attrib=array()) + { + $command = strtolower($command); + $attrib = parse_attrib_string($str_attrib) + $add_attrib; + + // empty output if required condition is not met + if (!empty($attrib['condition']) && !$this->check_condition($attrib['condition'])) + return ''; + + // execute command + switch ($command) + { + // return a button + case 'button': + if ($attrib['command']) + return $this->button($attrib); + break; + + // show a label + case 'label': + if ($attrib['name'] || $attrib['command']) + return Q(rcube_label($attrib + array('vars' => array('product' => $this->config['product_name'])))); + break; + + // include a file + case 'include': + $path = realpath($this->config['skin_path'].$attrib['file']); + if ($fp = @fopen($path, 'r')) + { + $incl = fread($fp, filesize($path)); + fclose($fp); + return $this->parse_xml($incl); + } + break; + + // return code for a specific application object + case 'object': + $object = strtolower($attrib['name']); + + // execute object handler function + if ($this->object_handlers[$object] && function_exists($this->object_handlers[$object])) + return call_user_func($this->object_handlers[$object], $attrib); + + else if ($object=='productname') + { + $name = !empty($this->config['product_name']) ? $this->config['product_name'] : 'RoundCube Webmail'; + return Q($name); + } + else if ($object=='version') + { + return (string)RCMAIL_VERSION; + } + else if ($object=='pagetitle') + { + $task = $this->task; + $title = !empty($this->config['product_name']) ? $this->config['product_name'].' :: ' : ''; + + if (!empty($this->pagetitle)) + $title .= $this->pagetitle; + else if ($task == 'login') + $title = rcube_label(array('name' => 'welcome', 'vars' => array('product' => $this->config['product_name']))); + else + $title .= ucfirst($task); + + return Q($title); + } + + break; + } + + return ''; + } + + + /** + * Create and register a button + * + * @param array Button attributes + * @return HTML button + */ + function button($attrib) + { + global $CONFIG, $OUTPUT, $BROWSER, $MAIN_TASKS; + static $sa_buttons = array(); + static $s_button_count = 100; + + // these commands can be called directly via url + $a_static_commands = array('compose', 'list'); + + $skin_path = $this->config['skin_path']; + + if (!($attrib['command'] || $attrib['name'])) + return ''; + + // try to find out the button type + if ($attrib['type']) + $attrib['type'] = strtolower($attrib['type']); + else + $attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $attrib['imageact']) ? 'image' : 'link'; + + $command = $attrib['command']; + + // take the button from the stack + if($attrib['name'] && $sa_buttons[$attrib['name']]) + $attrib = $sa_buttons[$attrib['name']]; + + // add button to button stack + else if($attrib['image'] || $attrib['imageact'] || $attrib['imagepas'] || $attrib['class']) + { + if (!$attrib['name']) + $attrib['name'] = $command; + + if (!$attrib['image']) + $attrib['image'] = $attrib['imagepas'] ? $attrib['imagepas'] : $attrib['imageact']; + + $sa_buttons[$attrib['name']] = $attrib; + } + + // get saved button for this command/name + else if ($command && $sa_buttons[$command]) + $attrib = $sa_buttons[$command]; + + //else + // return ''; + + + // set border to 0 because of the link arround the button + if ($attrib['type']=='image' && !isset($attrib['border'])) + $attrib['border'] = 0; + + if (!$attrib['id']) + $attrib['id'] = sprintf('rcmbtn%d', $s_button_count++); + + // get localized text for labels and titles + if ($attrib['title']) + $attrib['title'] = Q(rcube_label($attrib['title'])); + if ($attrib['label']) + $attrib['label'] = Q(rcube_label($attrib['label'])); + + if ($attrib['alt']) + $attrib['alt'] = Q(rcube_label($attrib['alt'])); + + // set title to alt attribute for IE browsers + if ($BROWSER['ie'] && $attrib['title'] && !$attrib['alt']) + { + $attrib['alt'] = $attrib['title']; + unset($attrib['title']); + } + + // add empty alt attribute for XHTML compatibility + if (!isset($attrib['alt'])) + $attrib['alt'] = ''; + + + // register button in the system + if ($attrib['command']) + { + $this->add_script(sprintf( + "%s.register_button('%s', '%s', '%s', '%s', '%s', '%s');", + JS_OBJECT_NAME, + $command, + $attrib['id'], + $attrib['type'], + $attrib['imageact'] ? $skin_path.$attrib['imageact'] : $attrib['classact'], + $attrib['imagesel'] ? $skin_path.$attrib['imagesel'] : $attrib['classsel'], + $attrib['imageover'] ? $skin_path.$attrib['imageover'] : '') + ); + + // make valid href to specific buttons + if (in_array($attrib['command'], $MAIN_TASKS)) + $attrib['href'] = Q(rcmail_url(null, null, $attrib['command'])); + else if (in_array($attrib['command'], $a_static_commands)) + $attrib['href'] = Q(rcmail_url($attrib['command'])); + } + + // overwrite attributes + if (!$attrib['href']) + $attrib['href'] = '#'; + + if ($command) + $attrib['onclick'] = sprintf("return %s.command('%s','%s',this)", JS_OBJECT_NAME, $command, $attrib['prop']); + + if ($command && $attrib['imageover']) + { + $attrib['onmouseover'] = sprintf("return %s.button_over('%s','%s')", JS_OBJECT_NAME, $command, $attrib['id']); + $attrib['onmouseout'] = sprintf("return %s.button_out('%s','%s')", JS_OBJECT_NAME, $command, $attrib['id']); + } + + if ($command && $attrib['imagesel']) + { + $attrib['onmousedown'] = sprintf("return %s.button_sel('%s','%s')", JS_OBJECT_NAME, $command, $attrib['id']); + $attrib['onmouseup'] = sprintf("return %s.button_out('%s','%s')", JS_OBJECT_NAME, $command, $attrib['id']); + } + + $out = ''; + + // generate image tag + if ($attrib['type']=='image') + { + $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']) + $btn_content .= ' '.$attrib['label']; + + $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'title'); + } + else if ($attrib['type']=='link') + { + $btn_content = $attrib['label'] ? $attrib['label'] : $attrib['command']; + $link_attrib = array('href', 'onclick', 'title', 'id', 'class', 'style'); + } + else if ($attrib['type']=='input') + { + $attrib['type'] = 'button'; + + if ($attrib['label']) + $attrib['value'] = $attrib['label']; + + $attrib_str = create_attrib_string($attrib, array('type', 'value', 'onclick', 'id', 'class', 'style')); + $out = sprintf('', $attrib_str); + } + + // generate html code for button + if ($btn_content) + { + $attrib_str = create_attrib_string($attrib, $link_attrib); + $out = sprintf('%s', $attrib_str, $btn_content); + } + + return $out; + } + +} + +?> \ No newline at end of file diff --git a/program/include/rcube_contacts.inc b/program/include/rcube_contacts.inc new file mode 100644 index 0000000..3902e2a --- /dev/null +++ b/program/include/rcube_contacts.inc @@ -0,0 +1,429 @@ + | + +-----------------------------------------------------------------------+ + + $Id: rcube_contacts.inc 328 2006-08-30 17:41:21Z thomasb $ + +*/ + +class rcube_contacts +{ + var $db = null; + var $db_name = ''; + var $user_id = 0; + var $filter = null; + var $result = null; + var $search_fields; + var $search_string; + var $table_cols = array('name', 'email', 'firstname', 'surname'); + + /** public properties */ + var $primary_key = 'contact_id'; + var $readonly = false; + var $list_page = 1; + var $page_size = 10; + var $ready = false; + + + /** + * Object constructor + * + * @param object Instance of the rcube_db class + * @param integer User-ID + */ + function __construct($dbconn, $user) + { + $this->db = $dbconn; + $this->db_name = get_table_name('contacts'); + $this->user_id = $user; + $this->ready = $this->db && !$this->db->is_error(); + } + + /** + * PHP 4 object constructor + * + * @see rcube_contacts::__construct + */ + function rcube_contacts($dbconn, $user) + { + $this->__construct($dbconn, $user); + } + + + /** + * Set internal list page + * + * @param number Page number to list + * @access public + */ + function set_page($page) + { + $this->list_page = (int)$page; + } + + + /** + * Set internal page size + * + * @param number Number of messages to display on one page + * @access public + */ + function set_pagesize($size) + { + $this->page_size = (int)$size; + } + + + /** + * Save a search string for future listings + * + * @param string SQL params to use in listing method + */ + function set_search_set($filter) + { + $this->filter = $filter; + } + + + /** + * Getter for saved search properties + * + * @return mixed Search properties used by this class + */ + function get_search_set() + { + return $this->filter; + } + + + /** + * Reset all saved results and search parameters + */ + function reset() + { + $this->result = null; + $this->filter = null; + $this->search_fields = null; + $this->search_string = null; + } + + + /** + * List the current set of contact records + * + * @param array List of cols to show + * @return array Indexed list of contact records, each a hash array + */ + function list_records($cols=null, $subset=0) + { + // count contacts for this user + $this->result = $this->count(); + $sql_result = NULL; + + // get contacts from DB + if ($this->result->count) + { + $start_row = $subset < 0 ? $this->result->first + $this->page_size + $subset : $this->result->first; + $length = $subset != 0 ? abs($subset) : $this->page_size; + + $sql_result = $this->db->limitquery( + "SELECT * FROM ".$this->db_name." + WHERE del<>1 + AND user_id=?" . + ($this->filter ? " AND (".$this->filter.")" : "") . + " ORDER BY name", + $start_row, + $length, + $this->user_id); + } + + while ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) + { + $sql_arr['ID'] = $sql_arr[$this->primary_key]; + // make sure we have a name to display + if (empty($sql_arr['name'])) + $sql_arr['name'] = $sql_arr['email']; + $this->result->add($sql_arr); + } + + return $this->result; + } + + + /** + * Search contacts + * + * @param array List of fields to search in + * @param string Search value + * @param boolean True if results are requested, False if count only + * @return Indexed list of contact records and 'count' value + */ + function search($fields, $value, $select=true) + { + if (!is_array($fields)) + $fields = array($fields); + + $add_where = array(); + foreach ($fields as $col) + { + if ($col == 'ID' || $col == $this->primary_key) + { + $ids = !is_array($value) ? split(',', $value) : $value; + $add_where[] = $this->primary_key." IN (".join(',', $ids).")"; + } + else + $add_where[] = $this->db->quoteIdentifier($col)." LIKE ".$this->db->quote(strlen($value)>2 ? "%$value%" : "$value%"); + } + + if (!empty($add_where)) + { + $this->set_search_set(join(' OR ', $add_where)); + if ($select) + $this->list_records(); + else + $this->result = $this->count(); + } + + return $this->result; + } + + + /** + * Count number of available contacts in database + * + * @return Result array with values for 'count' and 'first' + */ + function count() + { + // count contacts for this user + $sql_result = $this->db->query( + "SELECT COUNT(contact_id) AS rows + FROM ".$this->db_name." + WHERE del<>1 + AND user_id=?". + ($this->filter ? " AND (".$this->filter.")" : ""), + $this->user_id); + + $sql_arr = $this->db->fetch_assoc($sql_result); + return new rcube_result_set($sql_arr['rows'], ($this->list_page-1) * $this->page_size);; + } + + + /** + * Return the last result set + * + * @return Result array or NULL if nothing selected yet + */ + function get_result($as_res=true) + { + return $this->result; + } + + + /** + * Get a specific contact record + * + * @param mixed record identifier(s) + * @return Result object with all record fields or False if not found + */ + function get_record($id, $assoc=false) + { + // return cached result + if ($this->result && ($first = $this->result->first()) && $first[$this->primary_key] == $id) + return $assoc ? $first : $this->result; + + $this->db->query( + "SELECT * FROM ".$this->db_name." + WHERE contact_id=? + AND user_id=? + AND del<>1", + $id, + $this->user_id); + + if ($sql_arr = $this->db->fetch_assoc()) + { + $sql_arr['ID'] = $sql_arr[$this->primary_key]; + $this->result = new rcube_result_set(1); + $this->result->add($sql_arr); + } + + return $assoc && $sql_arr ? $sql_arr : $this->result; + } + + + /** + * Create a new contact record + * + * @param array Assoziative array with save data + * @return The created record ID on success, False on error + */ + function insert($save_data, $check=false) + { + if (is_object($save_data) && is_a($save_data, rcube_result_set)) + return $this->insert_recset($save_data, $check); + + $insert_id = $existing = false; + + if ($check) + $existing = $this->search('email', $save_data['email'], false); + + $a_insert_cols = $a_insert_values = array(); + foreach ($this->table_cols as $col) + if (isset($save_data[$col])) + { + $a_insert_cols[] = $this->db->quoteIdentifier($col); + $a_insert_values[] = $this->db->quote($save_data[$col]); + } + + if (!$existing->count && !empty($a_insert_cols)) + { + $this->db->query( + "INSERT INTO ".$this->db_name." + (user_id, changed, del, ".join(', ', $a_insert_cols).") + VALUES (?, ".$this->db->now().", 0, ".join(', ', $a_insert_values).")", + $this->user_id); + + $insert_id = $this->db->insert_id(get_sequence_name('contacts')); + } + + return $insert_id; + } + + + /** + * Insert new contacts for each row in set + */ + function insert_recset($result, $check=false) + { + $ids = array(); + while ($row = $result->next()) + { + if ($insert = $this->insert($row, $check)) + $ids[] = $insert; + } + return $ids; + } + + + /** + * Update a specific contact record + * + * @param mixed Record identifier + * @param array Assoziative array with save data + * @return True on success, False on error + */ + function update($id, $save_cols) + { + $updated = false; + $write_sql = array(); + foreach ($this->table_cols as $col) + if (isset($save_cols[$col])) + $write_sql[] = sprintf("%s=%s", $this->db->quoteIdentifier($col), $this->db->quote($save_cols[$col])); + + if (!empty($write_sql)) + { + $this->db->query( + "UPDATE ".$this->db_name." + SET changed=".$this->db->now().", ".join(', ', $write_sql)." + WHERE contact_id=? + AND user_id=? + AND del<>1", + $id, + $this->user_id); + + $updated = $this->db->affected_rows(); + } + + return $updated; + } + + + /** + * Mark one or more contact records as deleted + * + * @param array Record identifiers + */ + function delete($ids) + { + if (is_array($ids)) + $ids = join(',', $ids); + + $this->db->query( + "UPDATE ".$this->db_name." + SET del=1 + WHERE user_id=? + AND contact_id IN (".$ids.")", + $this->user_id); + + return $this->db->affected_rows(); + } + +} + + +/** + * RoundCube result set class. + * Representing an address directory result set. + */ +class rcube_result_set +{ + var $count = 0; + var $first = 0; + var $current = 0; + var $records = array(); + + function __construct($c=0, $f=0) + { + $this->count = (int)$c; + $this->first = (int)$f; + } + + function rcube_result_set($c=0, $f=0) + { + $this->__construct($c, $f); + } + + function add($rec) + { + $this->records[] = $rec; + } + + function iterate() + { + return $this->records[$this->current++]; + } + + function first() + { + $this->current = 0; + return $this->records[$this->current++]; + } + + // alias + function next() + { + return $this->iterate(); + } + + function seek($i) + { + $this->current = $i; + } + +} + + +?> \ No newline at end of file diff --git a/program/include/rcube_db.inc b/program/include/rcube_db.inc index 8c2abe2..6510483 100755 --- a/program/include/rcube_db.inc +++ b/program/include/rcube_db.inc @@ -5,7 +5,7 @@ | program/include/rcube_db.inc | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -17,7 +17,7 @@ | Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: rcube_db.inc 262 2006-06-25 09:17:07Z thomasb $ + $Id: rcube_db.inc 543 2007-04-28 18:07:12Z thomasb $ */ @@ -102,7 +102,7 @@ class rcube_db $this->db_error = TRUE; $this->db_error_msg = $dbh->getMessage(); - raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, + raise_error(array('code' => 603, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => $this->db_error_msg), TRUE, FALSE); return FALSE; @@ -292,14 +292,17 @@ class rcube_db 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 'mssql': + $result = &$this->db_handle->getOne("SELECT @@IDENTITY"); + 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 @@ -307,7 +310,7 @@ class rcube_db case 'mysqli': return mysqli_insert_id($this->db_handle->connection); - + case 'sqlite': return sqlite_last_insert_rowid($this->db_handle->connection); @@ -357,12 +360,14 @@ class rcube_db */ function _fetch_row($result, $mode) { - if (DB::isError($result)) + if (!$result || DB::isError($result)) { raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => $this->db_link->getMessage()), TRUE, FALSE); return FALSE; } + elseif (!is_object($result)) + return FALSE; return $result->fetchRow($mode); } @@ -421,6 +426,25 @@ class rcube_db } + /* + * Return SQL function for current time and date + * + * @return string SQL function to use in query + * @access public + */ + function now() + { + switch($this->db_provider) + { + case 'mssql': + return "getdate()"; + + default: + return "now()"; + } + } + + /** * Return SQL statement to convert a field value into a unix timestamp * @@ -434,7 +458,9 @@ class rcube_db { case 'pgsql': return "EXTRACT (EPOCH FROM $field)"; - break; + + case 'mssql': + return "datediff(s, '1970-01-01 00:00:00', $field)"; default: return "UNIX_TIMESTAMP($field)"; @@ -456,7 +482,7 @@ class rcube_db case 'mysqli': case 'mysql': case 'sqlite': - return "FROM_UNIXTIME($timestamp)"; + return sprintf("FROM_UNIXTIME(%d)", $timestamp); default: return date("'Y-m-d H:i:s'", $timestamp); diff --git a/program/include/rcube_imap.inc b/program/include/rcube_imap.inc index 5ce8b15..a777567 100644 --- a/program/include/rcube_imap.inc +++ b/program/include/rcube_imap.inc @@ -5,7 +5,7 @@ | program/include/rcube_imap.inc | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | Copyright (C) 2005-2006, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -16,7 +16,7 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: rcube_imap.inc 293 2006-08-04 13:56:08Z thomasb $ + $Id: rcube_imap.inc 561 2007-05-17 15:18:12Z thomasb $ */ @@ -26,7 +26,6 @@ */ require_once('lib/imap.inc'); require_once('lib/mime.inc'); -require_once('lib/utf7.inc'); /** @@ -36,7 +35,7 @@ require_once('lib/utf7.inc'); * * @package RoundCube Webmail * @author Thomas Bruederli - * @version 1.31 + * @version 1.36 * @link http://ilohamail.org */ class rcube_imap @@ -61,6 +60,10 @@ class rcube_imap var $msg_headers = array(); var $capabilities = array(); var $skip_deleted = FALSE; + var $search_set = NULL; + var $search_subject = ''; + var $search_string = ''; + var $search_charset = ''; var $debug_level = 1; @@ -133,11 +136,10 @@ class rcube_imap 'message' => $GLOBALS['iil_error']), TRUE, FALSE); } - // get account namespace + // get server properties if ($this->conn) { $this->_parse_capability($this->conn->capability); - iil_C_NameSpace($this->conn); if (!empty($this->conn->delimiter)) $this->delimiter = $this->conn->delimiter; @@ -267,6 +269,36 @@ class rcube_imap { $this->page_size = (int)$size; } + + + /** + * Save a set of message ids for future message listing methods + * + * @param array List of IMAP fields to search in + * @param string Search string + * @param array List of message ids or NULL if empty + */ + function set_search_set($subject, $str=null, $msgs=null, $charset=null) + { + if (is_array($subject) && $str == null && $msgs == null) + list($subject, $str, $msgs, $charset) = $subject; + if ($msgs != null && !is_array($msgs)) + $msgs = split(',', $msgs); + + $this->search_subject = $subject; + $this->search_string = $str; + $this->search_set = is_array($msgs) ? $msgs : NULL; + $this->search_charset = $charset; + } + + + /** + * Return the saved search set as hash array + */ + function get_search_set() + { + return array($this->search_subject, $this->search_string, $this->search_set, $this->search_charset); + } /** @@ -404,6 +436,10 @@ class rcube_imap if (empty($mailbox)) $mailbox = $this->mailbox; + + // count search set + if ($this->search_set && $mailbox == $this->mailbox && $mode == 'ALL') + return count($this->search_set); $a_mailbox_cache = $this->get_cache('messagecount'); @@ -483,7 +519,11 @@ class rcube_imap { if (!strlen($mailbox)) return array(); - + + // use saved message set + if ($this->search_set && $mailbox == $this->mailbox) + return $this->_list_header_set($mailbox, $this->search_set, $page, $sort_field, $sort_order); + if ($sort_field!=NULL) $this->sort_field = $sort_field; if ($sort_order!=NULL) @@ -494,10 +534,10 @@ class rcube_imap list($begin, $end) = $this->_get_message_range($max, $page); - // mailbox is empty + // mailbox is empty if ($begin >= $end) return array(); - + $headers_sorted = FALSE; $cache_key = $mailbox.'.msg'; $cache_status = $this->check_cache_status($mailbox, $cache_key); @@ -540,8 +580,9 @@ class rcube_imap $a_msg_headers = array(); $deleted_count = $this->_fetch_headers($mailbox, $msgs, $a_msg_headers, $cache_key); - // delete cached messages with a higher index than $max - $this->clear_message_cache($cache_key, $max); + // delete cached messages with a higher index than $max+1 + // Changed $max to $max+1 to fix this bug : #1484295 + $this->clear_message_cache($cache_key, $max + 1); // kick child process to sync cache @@ -616,14 +657,14 @@ class rcube_imap $this->_fetch_headers($mailbox, join(',', $msgs), $a_msg_headers, NULL); // return empty array if no messages found - if (!is_array($a_msg_headers) || empty($a_msg_headers)) - return array(); + if (!is_array($a_msg_headers) || empty($a_msg_headers)) + return array(); // if not already sorted $a_msg_headers = iil_SortHeaders($a_msg_headers, $this->sort_field, $this->sort_order); - // only return the requested part of the set - return array_slice(array_values($a_msg_headers), $start_msg, min($max-$start_msg, $this->page_size)); + // only return the requested part of the set + return array_slice(array_values($a_msg_headers), $start_msg, min($max-$start_msg, $this->page_size)); } @@ -710,7 +751,14 @@ class rcube_imap } - // return sorted array of message UIDs + /** + * Return sorted array of message UIDs + * + * @param string Mailbox to get index from + * @param string Sort column + * @param string Sort order [ASC, DESC] + * @return array Indexed array with message ids + */ function message_index($mbox_name='', $sort_field=NULL, $sort_order=NULL) { if ($sort_field!=NULL) @@ -781,7 +829,6 @@ class rcube_imap // message in cache at correct position if ($cache_index[$id] == $uid) { -// console("$id / $uid: OK"); unset($cache_index[$id]); continue; } @@ -789,21 +836,17 @@ class rcube_imap // 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); @@ -830,15 +873,29 @@ class rcube_imap function search($mbox_name='', $criteria='ALL', $str=NULL, $charset=NULL) { $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox; - if ($str && $criteria) + + // have an array of criterias => execute multiple searches + if (is_array($criteria) && $str) + { + $results = array(); + foreach ($criteria as $crit) + if ($search_result = $this->search($mbox_name, $crit, $str, $charset)) + $results = array_merge($results, $search_result); + + $results = array_unique($results); + $this->set_search_set($criteria, $str, $results, $charset); + return $results; + } + else if ($str && $criteria) { $search = (!empty($charset) ? "CHARSET $charset " : '') . sprintf("%s {%d}\r\n%s", $criteria, strlen($str), $str); $results = $this->_search_index($mailbox, $search); - // try search without charset (probably not supported by server) - if (empty($results)) - $results = $this->_search_index($mailbox, "$criteria $str"); + // try search with ISO charset (should be supported by server) + if (empty($results) && !empty($charset) && $charset!='ISO-8859-1') + $results = $this->search($mbox_name, $criteria, rcube_charset_convert($str, $charset, 'ISO-8859-1'), 'ISO-8859-1'); + $this->set_search_set($criteria, $str, $results, $charset); return $results; } else @@ -866,46 +923,322 @@ class rcube_imap return $a_messages; } + + + /** + * Refresh saved search set + */ + function refresh_search() + { + if (!empty($this->search_subject) && !empty($this->search_string)) + $this->search_set = $this->search('', $this->search_subject, $this->search_string, $this->search_charset); + + return $this->get_search_set(); + } + /** + * Return message headers object of a specific message + * + * @param int Message ID + * @param string Mailbox to read from + * @param boolean True if $id is the message UID + * @return object Message headers representation + */ function get_headers($id, $mbox_name=NULL, $is_uid=TRUE) { $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox; + $uid = $is_uid ? $id : $this->_id2uid($id); // get cached headers - if ($is_uid && ($headers = $this->get_cached_message($mailbox.'.msg', $id))) + if ($uid && ($headers = &$this->get_cached_message($mailbox.'.msg', $uid))) return $headers; - $msg_id = $is_uid ? $this->_uid2id($id) : $id; - $headers = iil_C_FetchHeader($this->conn, $mailbox, $msg_id); + $headers = iil_C_FetchHeader($this->conn, $mailbox, $id, $is_uid); // write headers cache if ($headers) - $this->add_message_cache($mailbox.'.msg', $msg_id, $headers); + { + if ($is_uid) + $this->uid_id_map[$mbox_name][$uid] = $headers->id; + + $this->add_message_cache($mailbox.'.msg', $headers->id, $headers); + } return $headers; } - function get_body($uid, $part=1) + /** + * Fetch body structure from the IMAP server and build + * an object structure similar to the one generated by PEAR::Mail_mimeDecode + * + * @param Int Message UID to fetch + * @return object Standard object tree or False on failure + */ + function &get_structure($uid) { + $cache_key = $this->mailbox.'.msg'; + $headers = &$this->get_cached_message($cache_key, $uid, true); + + // return cached message structure + if (is_object($headers) && is_object($headers->structure)) + return $headers->structure; + + // resolve message sequence number if (!($msg_id = $this->_uid2id($uid))) return FALSE; - $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id); - $structure = iml_GetRawStructureArray($structure_str); - $body = iil_C_FetchPartBody($this->conn, $this->mailbox, $msg_id, $part); + $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id); + $structure = iml_GetRawStructureArray($structure_str); + $struct = false; + + // parse structure and add headers + if (!empty($structure)) + { + $this->_msg_id = $msg_id; + $headers = $this->get_headers($msg_id, NULL, FALSE); + + $struct = &$this->_structure_part($structure); + $struct->headers = get_object_vars($headers); + + // don't trust given content-type + if (empty($struct->parts) && !empty($struct->headers['ctype'])) + { + $struct->mime_id = '1'; + $struct->mimetype = strtolower($struct->headers['ctype']); + list($struct->ctype_primary, $struct->ctype_secondary) = explode('/', $struct->mimetype); + } + + // write structure to cache + if ($this->caching_enabled) + $this->add_message_cache($cache_key, $msg_id, $headers, $struct); + } + + return $struct; + } + + + /** + * Build message part object + * + * @access private + */ + function &_structure_part($part, $count=0, $parent='') + { + $struct = new rcube_message_part; + $struct->mime_id = empty($parent) ? (string)$count : "$parent.$count"; + + // multipart + if (is_array($part[0])) + { + $struct->ctype_primary = 'multipart'; + + // find first non-array entry + for ($i=1; count($part); $i++) + if (!is_array($part[$i])) + { + $struct->ctype_secondary = strtolower($part[$i]); + break; + } + + $struct->mimetype = 'multipart/'.$struct->ctype_secondary; + + $struct->parts = array(); + for ($i=0, $count=0; $i 5) + $struct->parts[] = $this->_structure_part($part[$i], ++$count, $struct->mime_id); + + return $struct; + } + + + // regular part + $struct->ctype_primary = strtolower($part[0]); + $struct->ctype_secondary = strtolower($part[1]); + $struct->mimetype = $struct->ctype_primary.'/'.$struct->ctype_secondary; + + // read content type parameters + if (is_array($part[2])) + { + $struct->ctype_parameters = array(); + for ($i=0; $ictype_parameters[strtolower($part[2][$i])] = $part[2][$i+1]; + + if (isset($struct->ctype_parameters['charset'])) + $struct->charset = $struct->ctype_parameters['charset']; + } + + // read content encoding + if (!empty($part[5]) && $part[5]!='NIL') + { + $struct->encoding = strtolower($part[5]); + $struct->headers['content-transfer-encoding'] = $struct->encoding; + } + + // get part size + if (!empty($part[6]) && $part[6]!='NIL') + $struct->size = intval($part[6]); + + // read part disposition + $di = count($part) - 2; + if ((is_array($part[$di]) && count($part[$di]) == 2 && is_array($part[$di][1])) || + (is_array($part[--$di]) && count($part[$di]) == 2)) + { + $struct->disposition = strtolower($part[$di][0]); + + if (is_array($part[$di][1])) + for ($n=0; $nd_parameters[strtolower($part[$di][1][$n])] = $part[$di][1][$n+1]; + } + + // get child parts + if (is_array($part[8]) && $di != 8) + { + $struct->parts = array(); + for ($i=0, $count=0; $i 5) + $struct->parts[] = $this->_structure_part($part[8][$i], ++$count, $struct->mime_id); + } + + // get part ID + if (!empty($part[3]) && $part[3]!='NIL') + { + $struct->content_id = $part[3]; + $struct->headers['content-id'] = $part[3]; + + if (empty($struct->disposition)) + $struct->disposition = 'inline'; + } + + // fetch message headers if message/rfc822 + if ($struct->ctype_primary=='message') + { + $headers = iil_C_FetchPartBody($this->conn, $this->mailbox, $this->_msg_id, $struct->mime_id.'.HEADER'); + $struct->headers = $this->_parse_headers($headers); + + if (is_array($part[8]) && empty($struct->parts)) + $struct->parts[] = $this->_structure_part($part[8], ++$count, $struct->mime_id); + } + + // normalize filename property + if (!empty($struct->d_parameters['filename'])) + $struct->filename = $this->decode_mime_string($struct->d_parameters['filename']); + else if (!empty($struct->ctype_parameters['name'])) + $struct->filename = $this->decode_mime_string($struct->ctype_parameters['name']); + else if (!empty($struct->headers['content-description'])) + $struct->filename = $this->decode_mime_string($struct->headers['content-description']); + + return $struct; + } + + + /** + * Return a flat array with references to all parts, indexed by part numbers + * + * @param object Message body structure + * @return Array with part number -> object pairs + */ + function get_mime_numbers(&$structure) + { + $a_parts = array(); + $this->_get_part_numbers($structure, $a_parts); + return $a_parts; + } + + + /** + * Helper method for recursive calls + * + * @access + */ + function _get_part_numbers(&$part, &$a_parts) + { + if ($part->mime_id) + $a_parts[$part->mime_id] = &$part; + + if (is_array($part->parts)) + for ($i=0; $iparts); $i++) + $this->_get_part_numbers($part->parts[$i], $a_parts); + } + - $encoding = iml_GetPartEncodingCode($structure, $part); + /** + * Fetch message body of a specific message from the server + * + * @param int Message UID + * @param string Part number + * @param object Part object created by get_structure() + * @param mixed True to print part, ressource to write part contents in + * @return Message/part body if not printed + */ + function &get_message_part($uid, $part=1, $o_part=NULL, $print=NULL) + { + if (!($msg_id = $this->_uid2id($uid))) + return FALSE; - if ($encoding==3) $body = $this->mime_decode($body, 'base64'); - else if ($encoding==4) $body = $this->mime_decode($body, 'quoted-printable'); + // get part encoding if not provided + if (!is_object($o_part)) + { + $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id); + $structure = iml_GetRawStructureArray($structure_str); + $part_type = iml_GetPartTypeCode($structure, $part); + $o_part = new rcube_message_part; + $o_part->ctype_primary = $part_type==0 ? 'text' : ($part_type==2 ? 'message' : 'other'); + $o_part->encoding = strtolower(iml_GetPartEncodingString($structure, $part)); + $o_part->charset = iml_GetPartCharset($structure, $part); + } + + // TODO: Add caching for message parts + + if ($print) + { + iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, $part, ($o_part->encoding=='base64'?3:2)); + $body = TRUE; + } + else + { + $body = iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, $part, 1); + + // decode part body + if ($o_part->encoding=='base64' || $o_part->encoding=='quoted-printable') + $body = $this->mime_decode($body, $o_part->encoding); + + // convert charset (if text or message part) + if ($o_part->ctype_primary=='text' || $o_part->ctype_primary=='message') + { + // assume ISO-8859-1 if no charset specified + if (empty($o_part->charset)) + $o_part->charset = 'ISO-8859-1'; + + $body = rcube_charset_convert($body, $o_part->charset); + } + } return $body; } - function get_raw_body($uid) + /** + * Fetch message body of a specific message from the server + * + * @param int Message UID + * @return Message/part body + * @see ::get_message_part() + */ + function &get_body($uid, $part=1) + { + return $this->get_message_part($uid, $part); + } + + + /** + * Returns the whole message source as string + * + * @param int Message UID + * @return Message source string + */ + function &get_raw_body($uid) { if (!($msg_id = $this->_uid2id($uid))) return FALSE; @@ -915,10 +1248,31 @@ class rcube_imap return $body; } + + + /** + * Sends the whole message source to stdout + * + * @param int Message UID + */ + function print_raw_body($uid) + { + if (!($msg_id = $this->_uid2id($uid))) + return FALSE; + + print iil_C_FetchPartHeader($this->conn, $this->mailbox, $msg_id, NULL); + flush(); + iil_C_HandlePartBody($this->conn, $this->mailbox, $msg_id, NULL, 2); + } - // set message flag to one or several messages - // possible flags are: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT + /** + * Set message flag to one or several messages + * + * @param mixed Message UIDs as array or as comma-separated string + * @param string Flag to set: SEEN, UNDELETED, DELETED, RECENT, ANSWERED, DRAFT + * @return True on success, False on failure + */ function set_flag($uids, $flag) { $flag = strtoupper($flag); @@ -1019,7 +1373,8 @@ class rcube_imap foreach ($a_uids as $uid) $a_mids[] = $this->_uid2id($uid, $from_mbox); - $moved = iil_C_Move($this->conn, join(',', $a_mids), $from_mbox, $to_mbox); + $iil_move = iil_C_Move($this->conn, join(',', $a_mids), $from_mbox, $to_mbox); + $moved = !($iil_move === false || $iil_move < 0); // send expunge command in order to have the moved message // really deleted from the source mailbox @@ -1029,6 +1384,10 @@ class rcube_imap $this->_clear_messagecount($from_mbox); $this->_clear_messagecount($to_mbox); } + + // remove message ids from search set + if ($moved && $this->search_set && $from_mbox == $this->mailbox) + $this->search_set = array_diff($this->search_set, $a_mids); // update cached message headers $cache_key = $from_mbox.'.msg'; @@ -1037,8 +1396,8 @@ class rcube_imap $start_index = 100000; foreach ($a_uids as $uid) { - if(($index = array_search($uid, $a_cache_index)) !== FALSE) - $start_index = min($index, $start_index); + if (($index = array_search($uid, $a_cache_index)) !== FALSE) + $start_index = min($index, $start_index); } // clear cache from the lowest index on @@ -1078,6 +1437,10 @@ class rcube_imap $this->_clear_messagecount($mailbox); } + // remove message ids from search set + if ($moved && $this->search_set && $mailbox == $this->mailbox) + $this->search_set = array_diff($this->search_set, $a_mids); + // remove deleted messages from cache $cache_key = $mailbox.'.msg'; if ($deleted && ($a_cache_index = $this->get_message_cache_index($cache_key))) @@ -1085,8 +1448,8 @@ class rcube_imap $start_index = 100000; foreach ($a_uids as $uid) { - $index = array_search($uid, $a_cache_index); - $start_index = min($index, $start_index); + if (($index = array_search($uid, $a_cache_index)) !== FALSE) + $start_index = min($index, $start_index); } // clear cache from the lowest index on @@ -1190,12 +1553,8 @@ class rcube_imap function get_quota() { if ($this->get_capability('QUOTA')) - { - $result = iil_C_GetQuota($this->conn); - if ($result["total"]) - return sprintf("%.2fMB / %.2fMB (%.0f%%)", $result["used"] / 1000.0, $result["total"] / 1000.0, $result["percent"]); - } - + return iil_C_GetQuota($this->conn); + return FALSE; } @@ -1231,7 +1590,11 @@ class rcube_imap /** - * create a new mailbox on the server and register it in local cache + * Create a new mailbox on the server and register it in local cache + * + * @param string New mailbox name (as utf-7 string) + * @param boolean True if the new mailbox should be subscribed + * @param string Name of the created mailbox, false on error */ function create_mailbox($name, $subscribe=FALSE) { @@ -1240,12 +1603,10 @@ class rcube_imap // replace backslashes $name = preg_replace('/[\\\]+/', '-', $name); - $name_enc = UTF7EncodeString($name); - // reduce mailbox name to 100 chars - $name_enc = substr($name_enc, 0, 100); + $name = substr($name, 0, 100); - $abs_name = $this->_mod_mailbox($name_enc); + $abs_name = $this->_mod_mailbox($name); $a_mailbox_cache = $this->get_cache('mailboxes'); if (strlen($abs_name) && (!is_array($a_mailbox_cache) || !in_array_nocase($abs_name, $a_mailbox_cache))) @@ -1253,14 +1614,18 @@ class rcube_imap // try to subscribe it if ($subscribe) - $this->subscribe($name_enc); + $this->subscribe($name); return $result ? $name : FALSE; } /** - * set a new name to an existing mailbox + * Set a new name to an existing mailbox + * + * @param string Mailbox to rename (as utf-7 string) + * @param string New mailbox name (as utf-7 string) + * @param string Name of the renames mailbox, false on error */ function rename_mailbox($mbox_name, $new_name) { @@ -1270,22 +1635,34 @@ class rcube_imap $name = preg_replace('/[\\\]+/', '-', $new_name); // encode mailbox name and reduce it to 100 chars - $name_enc = substr(UTF7EncodeString($new_name), 0, 100); + $name = substr($new_name, 0, 100); // make absolute path $mailbox = $this->_mod_mailbox($mbox_name); - $abs_name = $this->_mod_mailbox($name_enc); + $abs_name = $this->_mod_mailbox($name); + + // check if mailbox is subscribed + $a_subscribed = $this->_list_mailboxes(); + $subscribed = in_array($mailbox, $a_subscribed); + // unsubscribe folder + if ($subscribed) + iil_C_UnSubscribe($this->conn, $mailbox); + if (strlen($abs_name)) $result = iil_C_RenameFolder($this->conn, $mailbox, $abs_name); - + // clear cache if ($result) { $this->clear_message_cache($mailbox.'.msg'); - $this->clear_cache('mailboxes'); + $this->clear_cache('mailboxes'); } + // try to subscribe it + if ($result && $subscribed) + iil_C_Subscribe($this->conn, $abs_name); + return $result ? $name : FALSE; } @@ -1346,6 +1723,10 @@ class rcube_imap else $this->subscribe($folder); } + else if (!in_array_nocase($abs_name, $a_folders)) + { + $this->create_mailbox($folder, FALSE); + } } } @@ -1473,7 +1854,7 @@ class rcube_imap { $this->db->query( "UPDATE ".get_table_name('cache')." - SET created=now(), + SET created=".$this->db->now().", data=? WHERE user_id=? AND cache_key=?", @@ -1487,7 +1868,7 @@ class rcube_imap $this->db->query( "INSERT INTO ".get_table_name('cache')." (created, user_id, cache_key, data) - VALUES (now(), ?, ?, ?)", + VALUES (".$this->db->now().", ?, ?, ?)", $_SESSION['user_id'], $key, $data); @@ -1581,18 +1962,16 @@ class rcube_imap } - function get_cached_message($key, $uid, $body=FALSE) + function &get_cached_message($key, $uid, $struct=false) { if (!$this->caching_enabled) return FALSE; $internal_key = '__single_msg'; - if ($this->caching_enabled && (!isset($this->cache[$internal_key][$uid]) || $body)) + if ($this->caching_enabled && (!isset($this->cache[$internal_key][$uid]) || + ($struct && empty($this->cache[$internal_key][$uid]->structure)))) { - $sql_select = "idx, uid, headers"; - if ($body) - $sql_select .= ", body"; - + $sql_select = "idx, uid, headers" . ($struct ? ", structure" : ''); $sql_result = $this->db->query( "SELECT $sql_select FROM ".get_table_name('messages')." @@ -1602,14 +1981,12 @@ class rcube_imap $_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; + $this->cache[$internal_key][$uid] = unserialize($sql_arr['headers']); + if (is_object($this->cache[$internal_key][$uid]) && !empty($sql_arr['structure'])) + $this->cache[$internal_key][$uid]->structure = unserialize($sql_arr['structure']); } } @@ -1622,7 +1999,7 @@ class rcube_imap static $sa_message_index = array(); // empty key -> empty array - if (empty($key)) + if (!$this->caching_enabled || empty($key)) return array(); if (!empty($sa_message_index[$key]) && !$force) @@ -1645,25 +2022,55 @@ class rcube_imap } - function add_message_cache($key, $index, $headers) + function add_message_cache($key, $index, $headers, $struct=null) { - if (!$key || !is_object($headers) || empty($headers->uid)) + if (!$this->caching_enabled || empty($key) || !is_object($headers) || empty($headers->uid)) return; + + // check for an existing record (probly headers are cached but structure not) + $sql_result = $this->db->query( + "SELECT message_id + FROM ".get_table_name('messages')." + WHERE user_id=? + AND cache_key=? + AND uid=? + AND del<>1", + $_SESSION['user_id'], + $key, + $headers->uid); - $this->db->query( - "INSERT INTO ".get_table_name('messages')." - (user_id, del, cache_key, created, idx, uid, subject, ".$this->db->quoteIdentifier('from').", ".$this->db->quoteIdentifier('to').", cc, date, size, headers) - VALUES (?, 0, ?, now(), ?, ?, ?, ?, ?, ?, ".$this->db->fromunixtime($headers->timestamp).", ?, ?)", - $_SESSION['user_id'], - $key, - $index, - $headers->uid, - (string)substr($this->decode_header($headers->subject, TRUE), 0, 128), - (string)substr($this->decode_header($headers->from, TRUE), 0, 128), - (string)substr($this->decode_header($headers->to, TRUE), 0, 128), - (string)substr($this->decode_header($headers->cc, TRUE), 0, 128), - (int)$headers->size, - serialize($headers)); + // update cache record + if ($sql_arr = $this->db->fetch_assoc($sql_result)) + { + $this->db->query( + "UPDATE ".get_table_name('messages')." + SET idx=?, headers=?, structure=? + WHERE message_id=?", + $index, + serialize($headers), + is_object($struct) ? serialize($struct) : NULL, + $sql_arr['message_id'] + ); + } + else // insert new record + { + $this->db->query( + "INSERT INTO ".get_table_name('messages')." + (user_id, del, cache_key, created, idx, uid, subject, ".$this->db->quoteIdentifier('from').", ".$this->db->quoteIdentifier('to').", cc, date, size, headers, structure) + VALUES (?, 0, ?, ".$this->db->now().", ?, ?, ?, ?, ?, ?, ".$this->db->fromunixtime($headers->timestamp).", ?, ?, ?)", + $_SESSION['user_id'], + $key, + $index, + $headers->uid, + (string)substr($this->decode_header($headers->subject, TRUE), 0, 128), + (string)substr($this->decode_header($headers->from, TRUE), 0, 128), + (string)substr($this->decode_header($headers->to, TRUE), 0, 128), + (string)substr($this->decode_header($headers->cc, TRUE), 0, 128), + (int)$headers->size, + serialize($headers), + is_object($struct) ? serialize($struct) : NULL + ); + } } @@ -1700,9 +2107,9 @@ class rcube_imap * --------------------------------*/ - function decode_address_list($input, $max=NULL) + function decode_address_list($input, $max=null, $decode=true) { - $a = $this->_parse_address_list($input); + $a = $this->_parse_address_list($input, $decode); $out = array(); if (!is_array($a)) @@ -1716,7 +2123,12 @@ class rcube_imap $j++; $address = $val['address']; $name = preg_replace(array('/^[\'"]/', '/[\'"]$/'), '', trim($val['name'])); - $string = $name!==$address ? sprintf('%s <%s>', strpos($name, ',')!==FALSE ? '"'.$name.'"' : $name, $address) : $address; + if ($name && $address && $name != $address) + $string = sprintf('%s <%s>', strpos($name, ',')!==FALSE ? '"'.$name.'"' : $name, $address); + else if ($address) + $string = $address; + else if ($name) + $string = $name; $out[$j] = array('name' => $name, 'mailto' => $address, @@ -1734,9 +2146,7 @@ class rcube_imap { $str = $this->decode_mime_string((string)$input); if ($str{0}=='"' && $remove_quotes) - { $str = str_replace('"', '', $str); - } return $str; } @@ -1747,7 +2157,7 @@ class rcube_imap * * @access static */ - function decode_mime_string($input, $recursive=false) + function decode_mime_string($input, $fallback=null) { $out = ''; @@ -1764,13 +2174,13 @@ class rcube_imap $rest = substr($input, $end_pos+2); $out .= rcube_imap::_decode_mime_string_part($encstr); - $out .= rcube_imap::decode_mime_string($rest); + $out .= rcube_imap::decode_mime_string($rest, $fallback); return $out; } - // no encoding information, defaults to what is specified in the class header - return rcube_charset_convert($input, 'ISO-8859-1'); + // no encoding information, use fallback + return rcube_charset_convert($input, !empty($fallback) ? $fallback : 'ISO-8859-1'); } @@ -1865,7 +2275,7 @@ class rcube_imap function _mod_mailbox($mbox_name, $mode='in') { - if ((!empty($this->root_ns) && $this->root_ns == $mbox_name) || $mbox_name == 'INBOX') + if (empty($mbox_name) || (!empty($this->root_ns) && $this->root_ns == $mbox_name) || $mbox_name == 'INBOX') return $mbox_name; if (!empty($this->root_dir) && $mode=='in') @@ -1902,12 +2312,12 @@ class rcube_imap function get_id($uid, $mbox_name=NULL) { - return $this->_uid2id($uid, $mbox_name); + return $this->_uid2id($uid, $this->_mod_mailbox($mbox_name)); } function get_uid($id,$mbox_name=NULL) { - return $this->_id2uid($id, $mbox_name); + return $this->_id2uid($id, $this->_mod_mailbox($mbox_name)); } function _uid2id($uid, $mbox_name=NULL) @@ -2040,20 +2450,42 @@ class rcube_imap } - function _parse_address_list($str) + // split RFC822 header string into an associative array + function _parse_headers($headers) + { + $a_headers = array(); + $lines = explode("\n", $headers); + $c = count($lines); + for ($i=0; $i<$c; $i++) + { + if ($p = strpos($lines[$i], ': ')) + { + $field = strtolower(substr($lines[$i], 0, $p)); + $value = trim(substr($lines[$i], $p+1)); + if (!empty($value)) + $a_headers[$field] = $value; + } + } + + return $a_headers; + } + + + function _parse_address_list($str, $decode=true) { - $a = $this->_explode_quoted_string(',', $str); + // remove any newlines and carriage returns before + $a = $this->_explode_quoted_string('[,;]', preg_replace( "/[\r\n]/", " ", $str)); $result = array(); foreach ($a as $key => $val) { - $val = str_replace("\"<", "\" <", $val); - $sub_a = $this->_explode_quoted_string(' ', $this->decode_header($val)); + $val = preg_replace("/([\"\w])_explode_quoted_string(' ', $decode ? $this->decode_header($val) : $val); $result[$key]['name'] = ''; foreach ($sub_a as $k => $v) { - if ((strpos($v, '@') > 0) && (strpos($v, '.') > 0)) + if (strpos($v, '@') > 0) $result[$key]['address'] = str_replace('<', '', str_replace('>', '', $v)); else $result[$key]['name'] .= (empty($result[$key]['name'])?'':' ').str_replace("\"",'',stripslashes($v)); @@ -2069,22 +2501,45 @@ class rcube_imap function _explode_quoted_string($delimiter, $string) { - $quotes = explode("\"", $string); - foreach ($quotes as $key => $val) - if (($key % 2) == 1) - $quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]); - - $string = implode("\"", $quotes); - - $result = explode($delimiter, $string); - foreach ($result as $key => $val) - $result[$key] = str_replace("_!@!_", $delimiter, $result[$key]); + $result = array(); + $strlen = strlen($string); + for ($q=$p=$i=0; $i < $strlen; $i++) + { + if ($string{$i} == "\"" && $string{$i-1} != "\\") + $q = $q ? false : true; + else if (!$q && preg_match("/$delimiter/", $string{$i})) + { + $result[] = substr($string, $p, $i - $p); + $p = $i + 1; + } + } + $result[] = substr($string, $p); return $result; } } +/** + * Class representing a message part + */ +class rcube_message_part +{ + var $mime_id = ''; + var $ctype_primary = 'text'; + var $ctype_secondary = 'plain'; + var $mimetype = 'text/plain'; + var $disposition = ''; + var $filename = ''; + var $encoding = '8bit'; + var $charset = ''; + var $size = 0; + var $headers = array(); + var $d_parameters = array(); + var $ctype_parameters = array(); + +} + /** * rcube_header_sorter @@ -2225,4 +2680,5 @@ function quoted_printable_encode($input, $line_max=76, $space_conv=false) return trim($output); } + ?> diff --git a/program/include/rcube_ldap.inc b/program/include/rcube_ldap.inc index 65857b7..074ca79 100644 --- a/program/include/rcube_ldap.inc +++ b/program/include/rcube_ldap.inc @@ -5,255 +5,428 @@ | program/include/rcube_ldap.inc | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | Copyright (C) 2006-2007, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | | PURPOSE: | - | Manage an LDAP connection | + | Interface to an LDAP address directory | | | +-----------------------------------------------------------------------+ - | Author: Jeremy Jongsma | + | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: rcube_ldap.inc 95 2006-01-08 07:15:44Z justinrandell $ + $Id: rcube_ldap.inc 563 2007-05-17 15:58:51Z thomasb $ */ -require_once("bugs.inc"); - class rcube_ldap - { +{ var $conn; - var $host; - var $port; - var $protocol; - var $base_dn; - var $bind_dn; - var $bind_pass; - - // PHP 5 constructor - function __construct() - { - } + var $prop = array(); + var $fieldmap = array(); + + var $filter = ''; + var $result = null; + var $ldap_result = null; + var $sort_col = ''; + + /** public properties */ + var $primary_key = 'ID'; + var $readonly = true; + var $list_page = 1; + var $page_size = 10; + var $ready = false; + + + /** + * Object constructor + * + * @param array LDAP connection properties + * @param integer User-ID + */ + function __construct($p) + { + $this->prop = $p; + + foreach ($p as $prop => $value) + if (preg_match('/^(.+)_field$/', $prop, $matches)) + $this->fieldmap[$matches[1]] = $value; + + // $this->filter = "(dn=*)"; + $this->connect(); + } - // PHP 4 constructor - function rcube_ldap() - { - $this->__construct(); - } + /** + * PHP 4 object constructor + * + * @see rcube_ldap::__construct + */ + function rcube_ldap($p) + { + $this->__construct($p); + } + - function connect($hosts, $port=389, $protocol=3) - { + /** + * Establish a connection to the LDAP server + */ + function connect() + { if (!function_exists('ldap_connect')) - raise_error(array("type" => "ldap", - "message" => "No ldap support in this installation of php."), - TRUE); + raise_error(array('type' => 'ldap', 'message' => "No ldap support in this installation of PHP"), true); if (is_resource($this->conn)) - return TRUE; + return true; - if (!is_array($hosts)) - $hosts = array($hosts); + if (!is_array($this->prop['hosts'])) + $this->prop['hosts'] = array($this->prop['hosts']); - foreach ($hosts as $host) + foreach ($this->prop['hosts'] as $host) + { + if ($lc = @ldap_connect($host, $this->prop['port'])) { - if ($lc = @ldap_connect($host, $port)) - { - @ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, $protocol); - $this->host = $host; - $this->port = $port; - $this->protocol = $protocol; + ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, $this->prop['port']); + $this->prop['host'] = $host; $this->conn = $lc; - return TRUE; - } + break; } + } - if (!is_resource($this->conn)) - raise_error(array("type" => "ldap", - "message" => "Could not connect to any LDAP server, tried $host:$port last"), - TRUE); + if (is_resource($this->conn)) + { + $this->ready = true; + if (!empty($this->prop['bind_dn']) && !empty($this->prop['bind_pass'])) + $this->ready = $this->bind($this->prop['bind_dn'], $this->prop['bind_pass']); } + else + raise_error(array('type' => 'ldap', 'message' => "Could not connect to any LDAP server, tried $host:{$this->prop[port]} last"), true); + } - function close() + + /** + * Bind connection with DN and password + */ + function bind($dn, $pass) + { + if (!$this->conn) + return false; + + if (@ldap_bind($this->conn, $dn, $pass)) + return true; + else { - if ($this->conn) - { - if (@ldap_unbind($this->conn)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Could not close connection to LDAP server: ".ldap_error($this->conn)), - TRUE); - } - return FALSE; + raise_error(array( + 'code' => ldap_errno($this->conn), + 'type' => 'ldap', + 'message' => "Bind failed for dn=$dn: ".ldap_error($this->conn)), + true); } + + return false; + } - // Merge with connect()? - function bind($dn=null, $pass=null) - { + + /** + * Close connection to LDAP server + */ + function close() + { if ($this->conn) - { - if ($dn) - if (@ldap_bind($this->conn, $dn, $pass)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Bind failed for dn=$dn: ".ldap_error($this->conn)), - TRUE); - else - if (@ldap_bind($this->conn)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Anonymous bind failed: ".ldap_error($this->conn)), - TRUE); - } - else - raise_error(array("type" => "ldap", - "message" => "Attempted bind on nonexistent connection"), TRUE); - return FALSE; - } + @ldap_unbind($this->conn); + } + + + /** + * Set internal list page + * + * @param number Page number to list + * @access public + */ + function set_page($page) + { + $this->list_page = (int)$page; + } + + + /** + * Set internal page size + * + * @param number Number of messages to display on one page + * @access public + */ + function set_pagesize($size) + { + $this->page_size = (int)$size; + } - function count($base, $filter=null, $attributes=null, $scope="sub") + + /** + * Save a search string for future listings + * + * @param string ?? + */ + function set_search_set($filter) + { + $this->filter = $filter; + } + + + /** + * Getter for saved search properties + * + * @return mixed Search properties used by this class + */ + function get_search_set() + { + return $this->filter; + } + + + /** + * Reset all saved results and search parameters + */ + function reset() + { + $this->result = null; + $this->ldap_result = null; + $this->filter = ''; + } + + + /** + * List the current set of contact records + * + * @param array List of cols to show + * @return array Indexed list of contact records, each a hash array + */ + function list_records($cols=null, $subset=0) + { + // exec LDAP search if no result resource is stored + if ($this->conn && !$this->ldap_result) + $this->_exec_search(); + + // count contacts for this user + $this->result = $this->count(); + + // we have a search result resource + if ($this->ldap_result && $this->result->count > 0) { - if ($this->conn) - { - if ($scope === 'sub') - $sr = @ldap_search($this->conn, $base, $filter, $attributes, 0, $limit); - else if ($scope === 'one') - $sr = @ldap_list($this->conn, $base, $filter, $attributes, 0, $limit); - else if ($scope === 'base') - $sr = @ldap_read($this->conn, $base, $filter, $attributes, 0, $limit); - if ($sr) - return @ldap_count_entries($this->conn, $sr); - } - else - raise_error(array("type" => "ldap", - "message" => "Attempted count search on nonexistent connection"), TRUE); - return FALSE; + if ($this->sort_col && $this->prop['scope'] !== "base") + @ldap_sort($this->conn, $this->ldap_result, $this->sort_col); + + $entries = ldap_get_entries($this->conn, $this->ldap_result); + for ($i = $this->result->first; $i < min($entries['count'], $this->result->first + $this->page_size); $i++) + $this->result->add($this->_ldap2result($entries[$i])); } - function search($base, $filter=null, $attributes=null, $scope='sub', $sort=null, $limit=0) + return $this->result; + } + + + /** + * Search contacts + * + * @param array List of fields to search in + * @param string Search value + * @param boolean True if results are requested, False if count only + * @return Indexed list of contact records and 'count' value + */ + function search($fields, $value, $select=true) + { + // special treatment for ID-based search + if ($fields == 'ID' || $fields == $this->primary_key) { - if ($this->conn) - { - if ($scope === 'sub') - $sr = @ldap_search($this->conn, $base, $filter, $attributes, 0, $limit); - else if ($scope === 'one') - $sr = @ldap_list($this->conn, $base, $filter, $attributes, 0, $limit); - else if ($scope === 'base') - $sr = @ldap_read($this->conn, $base, $filter, $attributes, 0, $limit); - if ($sr) + $ids = explode(',', $value); + $result = new rcube_result_set(); + foreach ($ids as $id) + if ($rec = $this->get_record($id, true)) { - if ($sort && $scope !== "base") - { - if (is_array($sort)) - { - // Start from the end so first sort field has highest priority - $sortfields = array_reverse($sort); - foreach ($sortfields as $sortfield) - @ldap_sort($this->conn, $sr, $sortfield); - } - else - @ldap_sort($this->conn, $sr, $sort); - } - return @ldap_get_entries($this->conn, $sr); + $result->add($rec); + $result->count++; } - } - else - raise_error(array("type" => "ldap", - "message" => "Attempted search on nonexistent connection"), TRUE); - return FALSE; + + return $result; } - - function add($dn, $object) + + $filter = '(|'; + $wc = $this->prop['fuzzy_search'] ? '*' : ''; + if (is_array($this->prop['search_fields'])) { - if ($this->conn) - { - if (@ldap_add($this->conn, $dn, $object)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Add object failed: ".ldap_error($this->conn)), - TRUE); - } + foreach ($this->prop['search_fields'] as $k => $field) + $filter .= "($field=$wc" . rcube_ldap::quote_string($value) . "$wc)"; + } else - raise_error(array("type" => "ldap", - "message" => "Add object faile: no connection"), - TRUE); - return FALSE; + { + foreach ((array)$fields as $field) + if ($f = $this->_map_field($field)) + $filter .= "($f=$wc" . rcube_ldap::quote_string($value) . "$wc)"; } + $filter .= ')'; + + // add general filter to query + if (!empty($this->prop['filter'])) + $filter = '(&'.$this->prop['filter'] . $filter . ')'; - function modify($dn, $object) - { - if ($this->conn) - { - if (@ldap_modify($this->conn, $dn, $object)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Modify object failed: ".ldap_error($this->conn)), - TRUE); - } + // set filter string and execute search + $this->set_search_set($filter); + $this->_exec_search(); + + if ($select) + $this->list_records(); else - raise_error(array("type" => "ldap", - "message" => "Modify object failed: no connection"), - TRUE); - return FALSE; - } + $this->result = $this->count(); + + return $this->result; + } - function rename($dn, $newrdn, $parentdn) - { - if ($this->protocol < 3) - { - raise_error(array("type" => "ldap", - "message" => "rename() support requires LDAPv3 or above "), - TRUE); - return FALSE; - } - if ($this->conn) + /** + * Count number of available contacts in database + * + * @return Result array with values for 'count' and 'first' + */ + function count() + { + $count = 0; + if ($this->conn && $this->ldap_result) + $count = ldap_count_entries($this->conn, $this->ldap_result); + + return new rcube_result_set($count, ($this->list_page-1) * $this->page_size); + } + + + /** + * Return the last result set + * + * @return Result array or NULL if nothing selected yet + */ + function get_result() + { + return $this->result; + } + + + /** + * Get a specific contact record + * + * @param mixed record identifier + * @return Hash array with all record fields or False if not found + */ + function get_record($dn, $assoc=false) + { + $res = null; + if ($this->conn && $dn) + { + $this->ldap_result = @ldap_read($this->conn, base64_decode($dn), "(objectclass=*)", array_values($this->fieldmap)); + $entry = @ldap_first_entry($this->conn, $this->ldap_result); + + if ($entry && ($rec = ldap_get_attributes($this->conn, $entry))) { - if (@ldap_rename($this->conn, $dn, $newrdn, $parentdn, TRUE)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Rename object failed: ".ldap_error($this->conn)), - TRUE); + $res = $this->_ldap2result($rec); + $this->result = new rcube_result_set(1); + $this->result->add($res); } - else - raise_error(array("type" => "ldap", - "message" => "Rename object failed: no connection"), - TRUE); - return FALSE; } - function delete($dn) + return $assoc ? $res : $this->result; + } + + + /** + * Create a new contact record + * + * @param array Assoziative array with save data + * @return The create record ID on success, False on error + */ + function insert($save_cols) + { + // TODO + return false; + } + + + /** + * Update a specific contact record + * + * @param mixed Record identifier + * @param array Assoziative array with save data + * @return True on success, False on error + */ + function update($id, $save_cols) + { + // TODO + return false; + } + + + /** + * Mark one or more contact records as deleted + * + * @param array Record identifiers + */ + function delete($ids) + { + // TODO + return false; + } + + + /** + * Execute the LDAP search based on the stored credentials + * + * @private + */ + function _exec_search() + { + if ($this->conn && $this->filter) { - if ($this->conn) - { - if (@ldap_delete($this->conn, $dn)) - return TRUE; - else - raise_error(array("code" => ldap_errno($this->conn), - "type" => "ldap", - "message" => "Delete object failed: ".ldap_error($this->conn)), - TRUE); - } + $function = $this->prop['scope'] == 'sub' ? 'ldap_search' : ($this->prop['scope'] == 'base' ? 'ldap_read' : 'ldap_list'); + $this->ldap_result = $function($this->conn, $this->prop['base_dn'], $this->filter, array_values($this->fieldmap), 0, 0); + return true; + } else - raise_error(array("type" => "ldap", - "message" => "Delete object failed: no connection"), - TRUE); - return FALSE; + return false; + } + + + /** + * @private + */ + function _ldap2result($rec) + { + $out = array(); + + if ($rec['dn']) + $out[$this->primary_key] = base64_encode($rec['dn']); + + foreach ($this->fieldmap as $rf => $lf) + { + if ($rec[$lf]['count']) + $out[$rf] = $rec[$lf][0]; } - + + return $out; + } + + + /** + * @private + */ + function _map_field($field) + { + return $this->fieldmap[$field]; } + + + /** + * @static + */ + function quote_string($str) + { + return strtr($str, array('*'=>'\2a', '('=>'\28', ')'=>'\29', '\\'=>'\5c')); + } + + +} -// vi: et ts=2 sw=2 -?> +?> \ No newline at end of file diff --git a/program/include/rcube_mdb2.inc b/program/include/rcube_mdb2.inc index f37c269..5f81aea 100755 --- a/program/include/rcube_mdb2.inc +++ b/program/include/rcube_mdb2.inc @@ -16,7 +16,7 @@ | Author: Lukas Kahwe Smith | +-----------------------------------------------------------------------+ - $Id: rcube_mdb2.inc 239 2006-05-18 21:25:11Z roundcube $ + $Id: rcube_mdb2.inc 420 2006-12-20 14:07:46Z thomasb $ */ @@ -59,14 +59,15 @@ class rcube_db * @param string DSN for read/write operations * @param string Optional DSN for read only operations */ - function __construct($db_dsnw, $db_dsnr='') + function __construct($db_dsnw, $db_dsnr='', $pconn=false) { if ($db_dsnr=='') $db_dsnr=$db_dsnw; $this->db_dsnw = $db_dsnw; $this->db_dsnr = $db_dsnr; - + $this->db_pconn = $pconn; + $dsn_array = MDB2::parseDSN($db_dsnw); $this->db_provider = $dsn_array['phptype']; } @@ -93,7 +94,7 @@ class rcube_db function dsn_connect($dsn) { // Use persistent connections if available - $dbh = MDB2::connect($dsn, array('persistent' => TRUE, 'portability' => MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_EMPTY_TO_NULL)); + $dbh = MDB2::connect($dsn, array('persistent' => $this->db_pconn, 'portability' => MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_EMPTY_TO_NULL)); if (PEAR::isError($dbh)) { @@ -103,13 +104,14 @@ class rcube_db raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => $dbh->getMessage()), TRUE, FALSE); } - else if ($this->db_provider=='sqlite') { $dsn_array = MDB2::parseDSN($dsn); if (!filesize($dsn_array['database']) && !empty($this->sqlite_initials)) $this->_sqlite_create_database($dbh, $this->sqlite_initials); } + else + $dbh->setCharset('utf8'); return $dbh; } @@ -285,7 +287,7 @@ class rcube_db if (!$this->db_handle) return FALSE; - return $result; + return $this->_get_result($result); } @@ -410,6 +412,25 @@ class rcube_db } + /** + * Return SQL function for current time and date + * + * @return string SQL function to use in query + * @access public + */ + function now() + { + switch($this->db_provider) + { + case 'mssql': + return "getdate()"; + + default: + return "now()"; + } + } + + /** * Return SQL statement to convert a field value into a unix timestamp * @@ -425,6 +446,9 @@ class rcube_db return "EXTRACT (EPOCH FROM $field)"; break; + case 'mssql': + return "datediff(s, '1970-01-01 00:00:00', $field)"; + default: return "UNIX_TIMESTAMP($field)"; } diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc index ee95965..737ebaf 100644 --- a/program/include/rcube_shared.inc +++ b/program/include/rcube_shared.inc @@ -5,7 +5,7 @@ | rcube_shared.inc | | | | This file is part of the RoundCube PHP suite | - | Copyright (C) 2005, RoundCube Dev. - Switzerland | + | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | | CONTENTS: | @@ -15,7 +15,7 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: rcube_shared.inc 288 2006-07-31 22:51:23Z thomasb $ + $Id: rcube_shared.inc 543 2007-04-28 18:07:12Z thomasb $ */ @@ -28,13 +28,15 @@ class rcube_html_page var $scripts_path = ''; var $script_files = array(); + var $external_scripts = array(); var $scripts = array(); var $charset = 'ISO-8859-1'; var $script_tag_file = "\n"; var $script_tag = "\n"; - var $default_template = "\n\n"; - + var $default_template = "\n\n\n"; + var $tag_format_external_script = "\n"; + var $title = ''; var $header = ''; var $footer = ''; @@ -69,15 +71,33 @@ class rcube_html_page $this->script_files[$position][] = $file; } - + function include_external_script($script_location, $position='head') + { + if (!is_array($this->external_scripts[$position])) + { + $this->external_scripts[$position] = array(); + } + + $this->external_scripts[$position][] = $script_location; + } + function add_script($script, $position='head') { if (!isset($this->scripts[$position])) - $this->scripts[$position] = ''; + $this->scripts[$position] = "\n".rtrim($script); + else + $this->scripts[$position] .= "\n".rtrim($script); + } - $this->scripts[$position] .= "\n$script"; + function add_header($str) + { + $this->header .= "\n".$str; } + function add_footer($str) + { + $this->footer .= "\n".$str; + } function set_title($t) { @@ -110,19 +130,21 @@ class rcube_html_page $this->script_files = array(); $this->scripts = array(); $this->title = ''; + $this->header = ''; + $this->footer = ''; } function write($templ='', $base_path='') { $output = empty($templ) ? $this->default_template : trim($templ); - + // set default page title - if (!strlen($this->title)) + if (empty($this->title)) $this->title = 'RoundCube Mail'; // replace specialchars in content - $__page_title = rep_specialchars_output($this->title, 'html', 'show', FALSE); + $__page_title = Q($this->title, 'show', FALSE); $__page_header = $__page_body = $__page_footer = ''; @@ -139,19 +161,28 @@ class rcube_html_page foreach ($this->script_files['head'] as $file) $__page_header .= sprintf($this->script_tag_file, $this->scripts_path, $file); - if (strlen($this->scripts['head'])) - $__page_header .= sprintf($this->script_tag, $this->scripts['head']); - + if (is_array($this->external_scripts['head'])) + foreach ($this->external_scripts['head'] as $xscript) + $__page_header .= sprintf($this->tag_format_external_script, $xscript); + + $head_script = $this->scripts['head_top'] . $this->scripts['head']; + if (!empty($head_script)) + $__page_header .= sprintf($this->script_tag, $head_script); + + if (!empty($this->header)) + $__page_header .= $this->header; + if (is_array($this->script_files['foot'])) foreach ($this->script_files['foot'] as $file) $__page_footer .= sprintf($this->script_tag_file, $this->scripts_path, $file); - if (strlen($this->scripts['foot'])) + if (!empty($this->scripts['foot'])) $__page_footer .= sprintf($this->script_tag, $this->scripts['foot']); - + + if (!empty($this->footer)) + $__page_footer .= $this->footer; $__page_header .= $this->css->show(); - // find page header if($hpos = strpos(strtolower($output), '')) @@ -192,8 +223,10 @@ class rcube_html_page // find and add page footer - if(($fpos = strpos(strtolower($output), '')) || ($fpos = strpos(strtolower($output), ''))) - $output = substr($output,0,$fpos) . "$__page_footer\n" . substr($output,$fpos,strlen($output)); + $output_lc = strtolower($output); + if(($fpos = strrstr($output_lc, '')) || + ($fpos = strrstr($output_lc, ''))) + $output = substr($output, 0, $fpos) . "$__page_footer\n" . substr($output, $fpos); else $output .= "\n$__page_footer"; @@ -202,7 +235,7 @@ class rcube_html_page $__page_header = $__page_footer = ''; - // correct absolute pathes in images and other tags + // correct absolute paths in images and other tags $output = preg_replace('/(src|href|background)=(["\']?)(\/[a-z0-9_\-]+)/Ui', "\\1=\\2$base_path\\3", $output); $output = str_replace('$__skin_path', $base_path, $output); @@ -701,7 +734,7 @@ class base_form_element // encode textarea content if ($key=='value') - $value = rep_specialchars_output($value, 'html', 'replace', FALSE); + $value = Q($value, 'strict', FALSE); // attributes with no value if (in_array($key, array('checked', 'multiple', 'disabled', 'selected'))) @@ -854,9 +887,9 @@ class textarea extends base_form_element if (isset($this->attrib['value'])) unset($this->attrib['value']); - if (strlen($value)) - $value = rep_specialchars_output($value, 'html', 'replace', FALSE); - + if (!empty($value) && !isset($this->attrib['mce_editable'])) + $value = Q($value, 'strict', FALSE); + // return final tag return sprintf('<%s%s>%s%s', $this->_conv_case('textarea', 'tag'), @@ -985,20 +1018,22 @@ class select extends base_form_element if (!is_array($select)) $select = array((string)$select); - + foreach ($this->options as $option) { - $selected = ((strlen($option['value']) && in_array($option['value'], $select, TRUE)) || - (in_array($option['text'], $select, TRUE))) ? $this->_conv_case(' selected', 'attrib') : ''; - + $selected = ((isset($option['value']) && + in_array($option['value'], $select, TRUE)) || + (in_array($option['text'], $select, TRUE))) ? + $this->_conv_case(' selected', 'attrib') : ''; + $options_str .= sprintf("<%s%s%s>%s\n", $this->_conv_case('option', 'tag'), - strlen($option['value']) ? sprintf($value_str, $option['value']) : '', + !empty($option['value']) ? sprintf($value_str, Q($option['value'])) : '', $selected, - rep_specialchars_output($option['text'], 'html', 'replace', FALSE), + Q($option['text'], 'strict', FALSE), $this->_conv_case('option', 'tag')); } - + // return final tag return sprintf('<%s%s>%s%s', $this->_conv_case('select', 'tag'), @@ -1080,7 +1115,7 @@ function rcube_label($attrib) $nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1; $vars = isset($attrib['vars']) ? $attrib['vars'] : ''; - $command_name = strlen($attrib['command']) ? $attrib['command'] : NULL; + $command_name = !empty($attrib['command']) ? $attrib['command'] : NULL; $alias = $attrib['name'] ? $attrib['name'] : ($command_name && $command_label_map[$command_name] ? $command_label_map[$command_name] : ''); @@ -1171,8 +1206,6 @@ EOF; return strtoupper($text); else if ($attrib['lowercase']) return strtolower($text); - else - return $text; return $text; } @@ -1192,89 +1225,113 @@ function send_nocacheing_headers() // send header with expire date 30 days in future -function send_future_expire_header() +function send_future_expire_header($offset=2600000) { - if (!headers_sent()) - header("Expires: ".gmdate("D, d M Y H:i:s", mktime()+2600000)." GMT"); + if (headers_sent()) + return; + + header("Expires: ".gmdate("D, d M Y H:i:s", mktime()+$offset)." GMT"); + header("Cache-Control: max-age=$offset"); + header("Pragma: "); } -// function to convert an array to a javascript array -function array2js($arr, $type='') +// check request for If-Modified-Since and send an according response +function send_modified_header($mdate, $etag=null) +{ + if (headers_sent()) + return; + + $iscached = false; + if ($_SERVER['HTTP_IF_MODIFIED_SINCE'] && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $mdate) + $iscached = true; + + $etag = $etag ? "\"$etag\"" : null; + if ($etag && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag) + $iscached = true; + + if ($iscached) + header("HTTP/1.x 304 Not Modified"); + else + header("Last-Modified: ".gmdate("D, d M Y H:i:s", $mdate)." GMT"); + + header("Cache-Control: max-age=0"); + header("Expires: "); + header("Pragma: "); + + if ($etag) + header("Etag: $etag"); + + if ($iscached) + exit; +} + + +/** + * Convert a variable into a javascript notation string + */ +function json_serialize($var) { - if (!$type) - $type = 'mixed'; + if (is_object($var)) + $var = get_object_vars($var); - if (is_array($arr)) + if (is_array($var)) { - // no items in array - if (!sizeof($arr)) - return 'new Array()'; - else + // empty array + if (!sizeof($var)) + return '[]'; + else { - $a_pairs = array(); - $keys_arr = array_keys($arr); - $is_assoc = $have_numeric = 0; + $keys_arr = array_keys($var); + $is_assoc = $have_numeric = 0; - for ($i=0; $i $value) { - // enclose key with quotes if it is not variable-name conform - if (!ereg("^[_a-zA-Z]{1}[_a-zA-Z0-9]*$", $key) /* || is_js_reserved_word($key) */) - $key = "'$key'"; - - if (!is_array($value)) - { - $value = str_replace("\r\n", '\n', $value); - $value = str_replace("\n", '\n', $value); - } - - $is_string = false; - if (!is_array($value)) - { - if ($type=='string') - $is_string = true; - else if ((($type=='mixed' && is_numeric($value)) || $type=='int') && strlen($value)<16) // js interprets numbers with digits >15 as ...e+... - $is_string = FALSE; - else - $is_string = TRUE; - } - - if ($is_string) - $value = "'".preg_replace("/(? 1024) - return sprintf('%d KB', round($numbytes/1024)); + if ($bytes > 1073741824) + { + $gb = $bytes/1073741824; + $str = sprintf($gb>=10 ? "%d GB" : "%.1f GB", $gb); + } + else if ($bytes > 1048576) + { + $mb = $bytes/1048576; + $str = sprintf($mb>=10 ? "%d MB" : "%.1f MB", $mb); + } + else if ($bytes > 1024) + $str = sprintf("%d KB", round($bytes/1024)); else - return sprintf('%d B', $numbytes); + $str = sprintf('%d B', $bytes); + + return $str; } @@ -1345,17 +1442,63 @@ function make_absolute_url($path, $base_url) } +// wrapper function for strlen +function rc_strlen($str) + { + if (function_exists('mb_strlen')) + return mb_strlen($str); + else + return strlen($str); + } + +// wrapper function for strtolower +function rc_strtolower($str) + { + if (function_exists('mb_strtolower')) + return mb_strtolower($str); + else + return strtolower($str); + } + +// wrapper function for substr +function rc_substr($str, $start, $len=null) + { + if (function_exists('mb_substr')) + return mb_substr($str, $start, $len); + else + return substr($str, $start, $len); + } + +// wrapper function for strpos +function rc_strpos($haystack, $needle, $offset=0) + { + if (function_exists('mb_strpos')) + return mb_strpos($haystack, $needle, $offset); + else + return strpos($haystack, $needle, $offset); + } + +// wrapper function for strrpos +function rc_strrpos($haystack, $needle, $offset=0) + { + if (function_exists('mb_strrpos')) + return mb_strrpos($haystack, $needle, $offset); + else + return strrpos($haystack, $needle, $offset); + } + + // replace the middle part of a string with ... // if it is longer than the allowed length function abbrevate_string($str, $maxlength, $place_holder='...') { - $length = strlen($str); - $first_part_length = floor($maxlength/2) - strlen($place_holder); + $length = rc_strlen($str); + $first_part_length = floor($maxlength/2) - rc_strlen($place_holder); if ($length > $maxlength) { $second_starting_location = $length - $maxlength + $first_part_length + 1; - $str = substr($str, 0, $first_part_length) . $place_holder . substr($str, $second_starting_location, $length); + $str = rc_substr($str, 0, $first_part_length) . $place_holder . rc_substr($str, $second_starting_location, $length); } return $str; @@ -1414,7 +1557,7 @@ function get_offset_time($offset_str, $factor=1) $amount *= 24; case 'h': $amount *= 60; - case 'h': + case 'm': $amount *= 60; case 's': $ts += $amount * $factor; @@ -1424,4 +1567,31 @@ function get_offset_time($offset_str, $factor=1) } +/** + * strrstr + * + * return the last occurence of a string in another string + * @param haystack string string in which to search + * @param needle string string for which to search + * @return index of needle within haystack, or false if not found + */ +function strrstr($haystack, $needle) + { + $pver = phpversion(); + if ($pver[0] >= 5) + { + return strrpos($haystack, $needle); + } + else + { + $index = strpos(strrev($haystack), strrev($needle)); + if($index === false) { + return false; + } + $index = strlen($haystack) - strlen($needle) - $index; + return $index; + } + } + + ?> diff --git a/program/include/rcube_smtp.inc b/program/include/rcube_smtp.inc index 5977de5..99b99e7 100644 --- a/program/include/rcube_smtp.inc +++ b/program/include/rcube_smtp.inc @@ -15,7 +15,7 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: rcube_smtp.inc 266 2006-06-26 18:38:03Z thomasb $ + $Id: rcube_smtp.inc 399 2006-12-06 21:37:37Z thomasb $ */ @@ -49,9 +49,9 @@ $SMTP_CONN = null; * @return bool Returns TRUE on success, or FALSE on error * @access public */ -function smtp_mail($from, $recipients, &$headers, &$body) +function smtp_mail($from, $recipients, &$headers, &$body, &$response) { - global $SMTP_CONN, $CONFIG, $SMTP_ERROR; + global $SMTP_CONN, $CONFIG; $smtp_timeout = null; $smtp_host = $CONFIG['smtp_server']; $smtp_port = is_numeric($CONFIG['smtp_port']) ? $CONFIG['smtp_port'] : 25; @@ -85,7 +85,7 @@ function smtp_mail($from, $recipients, &$headers, &$body) if (PEAR::isError($result)) { $SMTP_CONN = null; - $SMTP_ERROR .= "Connection failed: ".$result->getMessage()."\n"; + $response[] = "Connection failed: ".$result->getMessage(); return FALSE; } @@ -93,22 +93,22 @@ function smtp_mail($from, $recipients, &$headers, &$body) if ($CONFIG['smtp_user'] && $CONFIG['smtp_pass']) { if (strstr($CONFIG['smtp_user'], '%u')) - $smtp_user = str_replace('%u', $_SESSION['username'], $CONFIG['smtp_user']); + $smtp_user = str_replace('%u', $_SESSION['username'], $CONFIG['smtp_user']); else - $smtp_user = $CONFIG['smtp_user']; + $smtp_user = $CONFIG['smtp_user']; - if (strstr($CONFIG['smtp_pass'], '%p')) - $smtp_pass = str_replace('%p', decrypt_passwd($_SESSION['password']), $CONFIG['smtp_pass']); + if (strstr($CONFIG['smtp_pass'], '%p')) + $smtp_pass = str_replace('%p', decrypt_passwd($_SESSION['password']), $CONFIG['smtp_pass']); else - $smtp_pass = $CONFIG['smtp_pass']; + $smtp_pass = $CONFIG['smtp_pass']; - $smtp_auth_type = empty($CONFIG['smtp_auth_type']) ? NULL : $CONFIG['smtp_auth_type']; - $result = $SMTP_CONN->auth($smtp_user, $smtp_pass, $smtp_auth_type); - + $smtp_auth_type = empty($CONFIG['smtp_auth_type']) ? NULL : $CONFIG['smtp_auth_type']; + $result = $SMTP_CONN->auth($smtp_user, $smtp_pass, $smtp_auth_type); + if (PEAR::isError($result)) { smtp_reset(); - $SMTP_ERROR .= "Authentication failure: ".$result->getMessage()."\n"; + $response[] .= "Authentication failure: ".$result->getMessage(); return FALSE; } } @@ -132,7 +132,7 @@ function smtp_mail($from, $recipients, &$headers, &$body) else { smtp_reset(); - $SMTP_ERROR .= "Invalid message headers\n"; + $response[] .= "Invalid message headers"; return FALSE; } @@ -140,7 +140,7 @@ function smtp_mail($from, $recipients, &$headers, &$body) if (!isset($from)) { smtp_reset(); - $SMTP_ERROR .= "No From address has been provided\n"; + $response[] .= "No From address has been provided"; return FALSE; } @@ -149,7 +149,7 @@ function smtp_mail($from, $recipients, &$headers, &$body) if (PEAR::isError($SMTP_CONN->mailFrom($from))) { smtp_reset(); - $SMTP_ERROR .= "Failed to set sender '$from'\n"; + $response[] .= "Failed to set sender '$from'"; return FALSE; } @@ -169,21 +169,30 @@ function smtp_mail($from, $recipients, &$headers, &$body) if (PEAR::isError($SMTP_CONN->rcptTo($recipient))) { smtp_reset(); - $SMTP_ERROR .= "Failed to add recipient '$recipient'\n"; + $response[] .= "Failed to add recipient '$recipient'"; return FALSE; } } + // Concatenate headers and body so it can be passed by reference to SMTP_CONN->data + // so preg_replace in SMTP_CONN->quotedata will store a reference instead of a copy. + // We are still forced to make another copy here for a couple ticks so we don't really + // get to save a copy in the method call. + $data = $text_headers . "\r\n" . $body; + + // unset old vars to save data and so we can pass into SMTP_CONN->data by reference. + unset($text_headers, $body); + // Send the message's headers and the body as SMTP data. - if (PEAR::isError($SMTP_CONN->data("$text_headers\r\n$body"))) + if (PEAR::isError($SMTP_CONN->data($data))) { smtp_reset(); - $SMTP_ERROR .= "Failed to send data\n"; + $response[] .= "Failed to send data"; return FALSE; } - + $response[] = join(': ', $SMTP_CONN->getResponse()); return TRUE; } @@ -318,9 +327,9 @@ function smtp_parse_rfc822($recipients) reset($recipients); while (list($k, $recipient) = each($recipients)) { - $a = explode(" ", $recipient); - while (list($k2, $word) = each($a)) - { + $a = explode(" ", $recipient); + while (list($k2, $word) = each($a)) + { if ((strpos($word, "@") > 0) && (strpos($word, "\"")===false)) { $word = ereg_replace('^<|>$', '', trim($word)); @@ -340,13 +349,13 @@ function smtp_explode_quoted_str($delimiter, $string) if (($key % 2) == 1) $quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]); $string=implode("\"", $quotes); - + $result=explode($delimiter, $string); while (list($key, $val) = each($result)) $result[$key] = str_replace("_!@!_", $delimiter, $result[$key]); return $result; - } + } ?> diff --git a/program/include/session.inc b/program/include/session.inc index 822237c..81752d7 100644 --- a/program/include/session.inc +++ b/program/include/session.inc @@ -15,7 +15,7 @@ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: session.inc 132 2006-02-05 15:38:51Z roundcube $ + $Id: session.inc 521 2007-03-27 09:34:30Z thomasb $ */ @@ -36,7 +36,10 @@ function sess_close() // read session data function sess_read($key) { - global $DB, $SESS_CHANGED; + global $DB, $SESS_CHANGED, $SESS_CLIENT_IP; + + if ($DB->is_error()) + return FALSE; $sql_result = $DB->query("SELECT vars, ip, ".$DB->unixtimestamp('changed')." AS changed FROM ".get_table_name('session')." @@ -45,7 +48,8 @@ function sess_read($key) if ($sql_arr = $DB->fetch_assoc($sql_result)) { - $SESS_CHANGED = mktime(); //$sql_arr['changed']; + $SESS_CHANGED = $sql_arr['changed']; + $SESS_CLIENT_IP = $sql_arr['ip']; if (strlen($sql_arr['vars'])) return $sql_arr['vars']; @@ -59,6 +63,9 @@ function sess_read($key) function sess_write($key, $vars) { global $DB; + + if ($DB->is_error()) + return FALSE; $sql_result = $DB->query("SELECT 1 FROM ".get_table_name('session')." @@ -70,7 +77,7 @@ function sess_write($key, $vars) session_decode($vars); $DB->query("UPDATE ".get_table_name('session')." SET vars=?, - changed=now() + changed=".$DB->now()." WHERE sess_id=?", $vars, $key); @@ -79,7 +86,7 @@ function sess_write($key, $vars) { $DB->query("INSERT INTO ".get_table_name('session')." (sess_id, vars, ip, created, changed) - VALUES (?, ?, ?, now(), now())", + VALUES (?, ?, ?, ".$DB->now().", ".$DB->now().")", $key, $vars, $_SERVER['REMOTE_ADDR']); @@ -96,6 +103,9 @@ function sess_destroy($key) { global $DB; + if ($DB->is_error()) + return FALSE; + // delete session entries in cache table $DB->query("DELETE FROM ".get_table_name('cache')." WHERE session_id=?", @@ -105,7 +115,6 @@ function sess_destroy($key) WHERE sess_id=?", $key); - rcmail_clear_session_temp($key); return TRUE; } @@ -115,10 +124,13 @@ function sess_gc($maxlifetime) { global $DB; + if ($DB->is_error()) + return FALSE; + // get all expired sessions $sql_result = $DB->query("SELECT sess_id FROM ".get_table_name('session')." - WHERE ".$DB->unixtimestamp('now()')."-".$DB->unixtimestamp('changed')." > ?", + WHERE ".$DB->unixtimestamp($DB->now())."-".$DB->unixtimestamp('changed')." > ?", $maxlifetime); $a_exp_sessions = array(); @@ -137,17 +149,36 @@ 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); - // also run message cache GC rcmail_message_cache_gc(); + rcmail_temp_gc(); return TRUE; } +function sess_regenerate_id() + { + $randlen = 32; + $randval = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + $random = ""; + for ($i=1; $i <= $randlen; $i++) + $random .= substr($randval, rand(0,(strlen($randval) - 1)), 1); + + // use md5 value for id or remove capitals from string $randval + $random = md5($random); + + // delete old session record + sess_destroy(session_id()); + + session_id($random); + $cookie = session_get_cookie_params(); + setcookie(session_name(), $random, $cookie['lifetime'], $cookie['path']); + + return true; + } + + // set custom functions for PHP session management session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy', 'sess_gc'); diff --git a/program/js/app.js b/program/js/app.js index 7622f1f..5ae6710 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -3,21 +3,19 @@ | RoundCube Webmail Client Script | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005, RoundCube Dev, - Switzerland | + | Copyright (C) 2005-2007, RoundCube Dev, - Switzerland | | Licensed under the GNU GPL | | | +-----------------------------------------------------------------------+ | Authors: Thomas Bruederli | | Charles McNulty | +-----------------------------------------------------------------------+ - - $Id: app.js 431 2006-12-23 10:44:16Z thomasb $ + | Requires: common.js, list.js | + +-----------------------------------------------------------------------+ + + $Id: app.js 575 2007-05-18 12:35:28Z thomasb $ */ -// Constants -var CONTROL_KEY = 1; -var SHIFT_KEY = 2; -var CONTROL_SHIFT_KEY = 3; var rcube_webmail_client; @@ -28,19 +26,18 @@ function rcube_webmail() this.buttons = new Object(); this.gui_objects = new Object(); this.commands = new Object(); - this.selection = new Array(); - this.last_selected = 0; - this.in_message_list = false; + this.onloads = new Array(); - // create public reference to myself + // create protected reference to myself rcube_webmail_client = this; this.ref = 'rcube_webmail_client'; + var ref = this; // webmail client settings - this.dblclick_time = 600; + this.dblclick_time = 500; this.message_time = 5000; - this.mbox_expression = new RegExp('[^0-9a-z\-_]', 'gi'); + this.identifier_expr = new RegExp('[^0-9a-z\-_]', 'gi'); // mimetypes supported by the browser (default settings) this.mimetypes = new Array('text/plain', 'text/html', 'text/xml', @@ -51,13 +48,20 @@ function rcube_webmail() // default environment vars this.env.keep_alive = 60; // seconds this.env.request_timeout = 180; // seconds - this.env.draft_autosave = 300; // seconds + this.env.draft_autosave = 0; // seconds + this.env.comm_path = './'; + this.env.bin_path = './bin/'; + this.env.blankpage = 'program/blank.gif'; - // set environment variable - this.set_env = function(name, value) + // set environment variable(s) + this.set_env = function(p, value) { - this.env[name] = value; + if (p != null && typeof(p) == 'object' && !value) + for (var n in p) + this.env[n] = p[n]; + else + this.env[p] = value; }; @@ -82,30 +86,35 @@ function rcube_webmail() this.buttons[command][this.buttons[command].length] = button_prop; }; - // register a specific gui object this.gui_object = function(name, id) { this.gui_objects[name] = id; }; - + + // execute the given script on load + this.add_onload = function(f) + { + this.onloads[this.onloads.length] = f; + }; // initialize webmail client this.init = function() { + var p = this; this.task = this.env.task; // check browser if (!bw.dom || !bw.xmlhttp_test()) { - location.href = this.env.comm_path+'&_action=error&_code=0x199'; + this.goto_url('error', '_code=0x199'); return; } // find all registered gui objects for (var n in this.gui_objects) this.gui_objects[n] = rcube_find_object(this.gui_objects[n]); - + // tell parent window that this frame is loaded if (this.env.framed && parent.rcmail && parent.rcmail.set_busy) parent.rcmail.set_busy(false); @@ -116,43 +125,73 @@ function rcube_webmail() switch (this.task) { case 'mail': - var msg_list_frame = this.gui_objects.mailcontframe; - var msg_list = this.gui_objects.messagelist; - if (msg_list) + if (this.gui_objects.messagelist) { - msg_list_frame.onmousedown = function(e){return rcube_webmail_client.click_on_list(e);}; - this.init_messagelist(msg_list); + this.message_list = new rcube_list_widget(this.gui_objects.messagelist, {multiselect:true, draggable:true, keyboard:true, dblclick_time:this.dblclick_time}); + this.message_list.row_init = function(o){ p.init_message_row(o); }; + this.message_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); }); + this.message_list.addEventListener('keypress', function(o){ p.msglist_keypress(o); }); + this.message_list.addEventListener('select', function(o){ p.msglist_select(o); }); + this.message_list.addEventListener('dragstart', function(o){ p.drag_active = true; }); + this.message_list.addEventListener('dragend', function(o){ p.drag_active = false; }); + + this.message_list.init(); this.enable_command('toggle_status', true); + + if (this.gui_objects.mailcontframe) + { + this.gui_objects.mailcontframe.onmousedown = function(e){ return p.click_on_list(e); }; + document.onmouseup = function(e){ return p.doc_mouse_up(e); }; + } + else + this.message_list.focus(); } // enable mail commands this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', true); - if (this.env.action=='show') + if (this.env.action=='show' || this.env.action=='preview') { this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 'viewsource', 'print', 'load-attachment', true); if (this.env.next_uid) + { this.enable_command('nextmessage', true); + this.enable_command('lastmessage', true); + } if (this.env.prev_uid) + { this.enable_command('previousmessage', true); + this.enable_command('firstmessage', true); + } + } + + // make preview/message frame visible + if (this.env.action == 'preview' && this.env.framed && parent.rcmail) + { + this.enable_command('compose', 'add-contact', false); + parent.rcmail.show_contentframe(true); + parent.rcmail.mark_message('read', this.env.uid); } - if (this.env.action=='show' && this.env.blockedobjects) + if ((this.env.action=='show' || this.env.action=='preview') && this.env.blockedobjects) { if (this.gui_objects.remoteobjectsmsg) this.gui_objects.remoteobjectsmsg.style.display = 'block'; this.enable_command('load-images', true); - } + } if (this.env.action=='compose') { this.enable_command('add-attachment', 'send-attachment', 'remove-attachment', 'send', true); if (this.env.spellcheck) - this.enable_command('spellcheck', true); + { + this.env.spellcheck.spelling_state_observer = function(s){ ref.set_spellcheck_state(s); }; + this.set_spellcheck_state('ready'); + } if (this.env.drafts_mailbox) this.enable_command('savedraft', true); } - + if (this.env.messagecount) this.enable_command('select-all', 'select-none', 'sort', 'expunge', true); @@ -171,36 +210,53 @@ function rcube_webmail() // show printing dialog if (this.env.action=='print') window.print(); - + // get unread count for each mailbox if (this.gui_objects.mailboxlist) + { + this.gui_objects.folderlist = this.gui_objects.mailboxlist; this.http_request('getunread', ''); + } break; case 'addressbook': - var contacts_list = this.gui_objects.contactslist; - var ldap_contacts_list = this.gui_objects.ldapcontactslist; + if (this.gui_objects.contactslist) + { + this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, {multiselect:true, draggable:true, keyboard:true}); + this.contact_list.addEventListener('keypress', function(o){ p.contactlist_keypress(o); }); + this.contact_list.addEventListener('select', function(o){ p.contactlist_select(o); }); + this.contact_list.addEventListener('dragstart', function(o){ p.drag_active = true; }); + this.contact_list.addEventListener('dragend', function(o){ p.drag_active = false; }); + this.contact_list.init(); - if (contacts_list) - this.init_contactslist(contacts_list); - - if (ldap_contacts_list) - this.init_ldapsearchlist(ldap_contacts_list); + if (this.env.cid) + this.contact_list.highlight_row(this.env.cid); + + if (this.gui_objects.contactslist.parentNode) + { + this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return p.click_on_list(e); }; + document.onmouseup = function(e){ return p.doc_mouse_up(e); }; + } + else + this.contact_list.focus(); + } this.set_page_buttons(); - + + if (this.env.address_sources && !this.env.address_sources[this.env.source].readonly) + this.enable_command('add', true); + if (this.env.cid) this.enable_command('show', 'edit', true); if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform) this.enable_command('save', true); - - this.enable_command('list', 'add', true); - - this.enable_command('ldappublicsearch', this.env.ldappublicsearch); + else + this.enable_command('search', 'reset-search', 'moveto', true); + this.enable_command('list', true); break; @@ -215,16 +271,25 @@ function rcube_webmail() if (this.env.action=='folders') this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', true); - - var identities_list = this.gui_objects.identitieslist; - if (identities_list) - this.init_identitieslist(identities_list); + + if (this.gui_objects.identitieslist) + { + this.identity_list = new rcube_list_widget(this.gui_objects.identitieslist, {multiselect:false, draggable:false, keyboard:false}); + this.identity_list.addEventListener('select', function(o){ p.identity_select(o); }); + this.identity_list.init(); + this.identity_list.focus(); + + if (this.env.iid) + this.identity_list.highlight_row(this.env.iid); + } break; case 'login': var input_user = rcube_find_object('_user'); var input_pass = rcube_find_object('_pass'); + if (input_user) + input_user.onkeypress = function(e){ return rcmail.login_user_keypress(e); }; if (input_user && input_user.value=='') input_user.focus(); else if (input_pass) @@ -241,13 +306,6 @@ function rcube_webmail() // enable basic commands this.enable_command('logout', true); - // disable browser's contextmenus - // document.oncontextmenu = function(){ return false; } - - // load body click event - document.onmousedown = function(){ return rcube_webmail_client.reset_click(); }; - document.onkeydown = function(e){ return rcube_webmail_client.key_pressed(e, msg_list_frame); }; - // flag object as complete this.loaded = true; @@ -257,167 +315,48 @@ function rcube_webmail() // start keep-alive interval this.start_keepalive(); + + + // execute all foreign onload scripts + for (var i=0; i (Number(msg_list_frame.scrollTop) + Number(msg_list_frame.offsetHeight)))) { - msg_list_frame.scrollTop = scroll_to; - } - return false; - }; - - this.use_delete_key = function(keyCode, mod_key, msg_list_frame){ - this.command('delete','',this); - return false; - } - - // get all message rows from HTML table and init each row - this.init_messagelist = function(msg_list) - { - if (msg_list && msg_list.tBodies[0]) + // set eventhandler to message icon + if ((row.icon = row.obj.cells[0].childNodes[0]) && row.icon.nodeName=='IMG') { - - this.message_rows = new Array(); - - var row; - for(var r=0; r=0) { - this.attachment_win = window.open(url+'&_frame=1', 'rcubemailattachment'); + if (props.mimetype == 'text/html') + qstring += '&_safe=1'; + this.attachment_win = window.open(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1', 'rcubemailattachment'); if (this.attachment_win) { - setTimeout(this.ref+'.attachment_win.focus()', 10); + setTimeout(function(){ ref.attachment_win.focus(); }, 10); break; } } - location.href = url; + this.goto_url('get', qstring+'&_download=1'); break; case 'select-all': - this.select_all(props); + this.message_list.select_all(props); break; case 'select-none': - this.clear_selection(); + this.message_list.clear_selection(); break; case 'nextmessage': if (this.env.next_uid) - this.show_message(this.env.next_uid); - //location.href = this.env.comm_path+'&_action=show&_uid='+this.env.next_uid+'&_mbox='+this.env.mailbox; + this.show_message(this.env.next_uid, false, this.env.action=='preview'); + break; + + case 'lastmessage': + if (this.env.last_uid) + this.show_message(this.env.last_uid); break; case 'previousmessage': if (this.env.prev_uid) - this.show_message(this.env.prev_uid); - //location.href = this.env.comm_path+'&_action=show&_uid='+this.env.prev_uid+'&_mbox='+this.env.mailbox; + this.show_message(this.env.prev_uid, false, this.env.action=='preview'); + break; + + case 'firstmessage': + if (this.env.first_uid) + this.show_message(this.env.first_uid); break; case 'checkmail': @@ -880,58 +736,51 @@ function rcube_webmail() { var uid; if (uid = this.get_single_uid()) - url += '&_draft_uid='+uid+'&_mbox='+escape(this.env.mailbox); - } + url += '&_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox); + } // modify url if we're in addressbook else if (this.task=='addressbook') { - url = this.get_task_url('mail', url); - var a_cids = new Array(); + // switch to mail compose step directly + if (props && props.indexOf('@') > 0) + { + url = this.get_task_url('mail', url); + this.redirect(url + '&_to='+urlencode(props)); + break; + } // use contact_id passed as command parameter + var a_cids = new Array(); if (props) a_cids[a_cids.length] = props; - // get selected contacts - else + else if (this.contact_list) { - if (!window.frames[this.env.contentframe].rcmail.selection.length) - { - for (var n=0; n0 ? true : false); + } + else { - this.drag_start = true; - document.onmousemove = function(e){ return rcube_webmail_client.drag_mouse_move(e); }; - document.onmouseup = function(e){ return rcube_webmail_client.drag_mouse_up(e); }; + this.enable_command('show', 'reply', 'reply-all', 'forward', 'print', selected); + this.enable_command('delete', 'moveto', list.selection.length>0 ? true : false); } - return false; + // start timer for message preview (wait for double click) + if (selected && this.env.contentframe) + this.preview_timer = setTimeout(function(){ ref.msglist_get_preview(); }, this.dblclick_time + 10); + else if (this.env.contentframe) + this.show_contentframe(false); }; - // onmouseup-handler of message list row - this.click_row = function(e, id) + this.msglist_dbl_click = function(list) { - var mod_key = this.get_modifier(e); + if (this.preview_timer) + clearTimeout(this.preview_timer); - // don't do anything (another action processed before) - if (this.dont_select) - { - this.dont_select = false; - return false; - } - - // unselects currently selected row - if (!this.drag_active && this.in_selection_before==id && !this.list_rows[id].clicked) - this.select_row(id,mod_key,false); + var uid = list.get_single_selection(); + if (uid && this.env.mailbox == this.env.drafts_mailbox) + this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true); + else if (uid) + this.show_message(uid, false, false); + }; - 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 && this.in_selection(id)) - { - if (this.env.mailbox==this.env.drafts_mailbox) - { - this.set_busy(true); - location.href = this.env.comm_path+'&_action=compose&_draft_uid='+id+'&_mbox='+escape(this.env.mailbox); - } - else - { - this.show_message(id); - } - return false; - } - else if (this.task=='addressbook') - { - if (this.contact_rows && this.selection.length==1) - { - this.load_contact(this.selection[0], 'show', true); - // change the text for the add contact button - var links = parent.document.getElementById('abooktoolbar').getElementsByTagName('A'); - for (i = 0; i < links.length; i++) - { - var onclickstring = new String(links[i].onclick); - if (onclickstring.search('\"add\"') != -1) - links[i].title = this.env.newcontact; - } - } - else if (this.contact_rows && this.contact_rows[id].clicked) - { - this.load_contact(id, 'show'); - return false; - } - else if (this.ldap_contact_rows && !this.ldap_contact_rows[id].clicked) - { - // clear selection - parent.rcmail.clear_selection(); - // disable delete - parent.rcmail.set_button('delete', 'pas'); - - // change the text for the add contact button - var links = parent.document.getElementById('abooktoolbar').getElementsByTagName('A'); - for (i = 0; i < links.length; i++) - { - var onclickstring = new String(links[i].onclick); - if (onclickstring.search('\"add\"') != -1) - links[i].title = this.env.addcontact; - } - } - // handle double click event - else if (this.ldap_contact_rows && this.selection.length==1 && this.ldap_contact_rows[id].clicked) - this.command('compose', this.ldap_contact_rows[id].obj.cells[1].innerHTML); - else if (this.env.contentframe) - { - var elm = document.getElementById(this.env.contentframe); - elm.style.visibility = 'hidden'; - } - } - else if (this.task=='settings') - { - if (this.selection.length==1) - this.command('edit', this.selection[0]); - } - - this.list_rows[id].clicked = true; - setTimeout(this.ref+'.list_rows['+id+'].clicked=false;', this.dblclick_time); - - return false; - }; - - - - /*********************************************************/ - /********* (message) list functionality *********/ - /*********************************************************/ - - // get next and previous rows that are not hidden - this.get_next_row = function(){ - if (!this.list_rows) return false; - var last_selected_row = this.list_rows[this.last_selected]; - var new_row = last_selected_row.obj.nextSibling; - while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none')) { - new_row = new_row.nextSibling; - } - return new_row; - } - - this.get_prev_row = function(){ - if (!this.list_rows) return false; - var last_selected_row = this.list_rows[this.last_selected]; - var new_row = last_selected_row.obj.previousSibling; - while (new_row && (new_row.nodeType != 1 || new_row.style.display == 'none')) { - new_row = new_row.previousSibling; - } - return new_row; - } - - // highlight/unhighlight a row - this.highlight_row = function(id, multiple) + this.msglist_keypress = function(list) { - var selected = false - - if (this.list_rows[id] && !multiple) - { - this.clear_selection(); - this.selection[0] = id; - this.list_rows[id].obj.className += ' selected'; - selected = true; - } - - else if (this.list_rows[id]) - { - if (!this.in_selection(id)) // select row - { - this.selection[this.selection.length] = id; - this.set_classname(this.list_rows[id].obj, 'selected', true); - } - else // unselect row - { - var p = find_in_array(id, this.selection); - var a_pre = this.selection.slice(0, p); - var a_post = this.selection.slice(p+1, this.selection.length); - this.selection = a_pre.concat(a_post); - this.set_classname(this.list_rows[id].obj, 'selected', false); - this.set_classname(this.list_rows[id].obj, 'unfocused', false); - } - selected = (this.selection.length==1); - } - - // enable/disable commands for message - if (this.task=='mail') - { - if (this.env.mailbox==this.env.drafts_mailbox) - { - this.enable_command('show', selected); - this.enable_command('delete', 'moveto', this.selection.length>0 ? true : false); - } - else - { - this.enable_command('show', 'reply', 'reply-all', 'forward', 'print', selected); - this.enable_command('delete', 'moveto', this.selection.length>0 ? true : false); - } - } - else if (this.task=='addressbook') - { - this.enable_command('edit', /*'print',*/ selected); - this.enable_command('delete', 'compose', this.selection.length>0 ? true : false); - } + if (list.key_pressed == list.ENTER_KEY) + this.command('show'); + else if (list.key_pressed == list.DELETE_KEY) + this.command('delete'); }; -// selects or unselects the proper row depending on the modifier key pressed - this.select_row = function(id,mod_key,with_mouse) { - if (!mod_key) { - this.shift_start = id; - this.highlight_row(id, false); - } else { - switch (mod_key) { - case SHIFT_KEY: { - this.shift_select(id,false); - break; } - case CONTROL_KEY: { - this.shift_start = id; - if (!with_mouse) - this.highlight_row(id, true); - break; - } - case CONTROL_SHIFT_KEY: { - this.shift_select(id,true); - break; - } - default: { - this.highlight_row(id, false); - break; - } - } - } - if (this.last_selected != 0 && this.list_rows[this.last_selected]) - this.set_classname(this.list_rows[this.last_selected].obj, 'focused', false); - - this.last_selected = id; - this.set_classname(this.list_rows[id].obj, 'focused', true); - }; - - this.shift_select = function(id, control) { - var from_rowIndex = this.list_rows[this.shift_start].obj.rowIndex; - var to_rowIndex = this.list_rows[id].obj.rowIndex; - - var i = ((from_rowIndex < to_rowIndex)? from_rowIndex : to_rowIndex); - var j = ((from_rowIndex > to_rowIndex)? from_rowIndex : to_rowIndex); - - // iterate through the entire message list - for (var n in this.list_rows) { - if ((this.list_rows[n].obj.rowIndex >= i) && (this.list_rows[n].obj.rowIndex <= j)) { - if (!this.in_selection(n)) - this.highlight_row(n, true); - } else { - if (this.in_selection(n) && !control) - this.highlight_row(n, true); - } - } + this.msglist_get_preview = function() + { + var uid = this.get_single_uid(); + if (uid && this.env.contentframe && !this.drag_active) + this.show_message(uid, false, true); + else if (this.env.contentframe) + this.show_contentframe(false); }; + + this.check_droptarget = function(id) + { + if (this.task == 'mail') + return (id != this.env.mailbox); + else if (this.task == 'addressbook') + return (id != this.env.source && this.env.address_sources[id] && !this.env.address_sources[id].readonly); + }; - this.clear_selection = function() - { - for(var n=0; n= 0) + this.show_contentframe(true); + else + { + this.set_busy(true, 'loading'); + target.location.href = this.env.comm_path+url; + } } }; + this.show_contentframe = function(show) + { + var frm; + if (this.env.contentframe && (frm = rcube_find_object(this.env.contentframe))) + { + if (!show && window.frames[this.env.contentframe] && frames[this.env.contentframe].location.href.indexOf(this.env.blankpage)<0) + frames[this.env.contentframe].location.href = this.env.blankpage; + if (!bw.safari) + frm.style.display = show ? 'block' : 'none'; + } + + if (!show && this.busy) + this.set_busy(false); + }; + // list a specific page this.list_page = function(page) { if (page=='next') page = this.env.current_page+1; + if (page=='last') + page = this.env.pagecount; if (page=='prev' && this.env.current_page>1) page = this.env.current_page-1; + if (page=='first' && this.env.current_page>1) + page = 1; if (page > 0 && page <= this.env.pagecount) { @@ -1542,7 +1217,7 @@ function rcube_webmail() if (this.task=='mail') this.list_mailbox(this.env.mailbox, page); else if (this.task=='addressbook') - this.list_contacts(page); + this.list_contacts(this.env.source, page); } }; @@ -1560,21 +1235,26 @@ function rcube_webmail() // add sort to url if set if (sort) add_url += '&_sort=' + sort; + + // also send search request to get the right messages + if (this.env.search_request) + add_url += '&_search='+this.env.search_request; // set page=1 if changeing to another mailbox if (!page && mbox != this.env.mailbox) { page = 1; - add_url += '&_refresh=1'; this.env.current_page = page; - this.clear_selection(); + if (this.message_list) + this.message_list.clear_selection(); + this.show_contentframe(false); } - // also send search request to get the right messages - if (this.env.search_request) - add_url += '&_search='+this.env.search_request; - - this.select_mailbox(mbox); + if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort)) + add_url += '&_refresh=1'; + + this.select_folder(mbox, this.env.mailbox); + this.env.mailbox = mbox; // load message list remotely if (this.gui_objects.messagelist) @@ -1593,7 +1273,7 @@ function rcube_webmail() if (mbox) { this.set_busy(true, 'loading'); - target.location.href = this.env.comm_path+'&_mbox='+escape(mbox)+(page ? '&_page='+page : '')+add_url; + target.location.href = this.env.comm_path+'&_mbox='+urlencode(mbox)+(page ? '&_page='+page : '')+add_url; } }; @@ -1602,47 +1282,15 @@ function rcube_webmail() this.list_mailbox_remote = function(mbox, page, add_url) { // clear message list first - this.clear_message_list(); + this.message_list.clear(); // send request to server - var url = '_mbox='+escape(mbox)+(page ? '&_page='+page : ''); + var url = '_mbox='+urlencode(mbox)+(page ? '&_page='+page : ''); this.set_busy(true, 'loading'); this.http_request('list', url+add_url, true); }; - this.clear_message_list = function() - { - var table = this.gui_objects.messagelist; - - var tbody = document.createElement('TBODY'); - table.insertBefore(tbody, table.tBodies[0]); - table.removeChild(table.tBodies[1]); - - this.message_rows = new Array(); - this.list_rows = this.message_rows; - - }; - - - this.clear_message_list_header = function() - { - var table; - if (table = this.gui_objects.messagelist) - { - if (table.colgroup) - table.removeChild(table.colgroup); - if (table.tHead) - table.removeChild(table.tHead); - - var colgroup = document.createElement('COLGROUP'); - var thead = document.createElement('THEAD'); - table.insertBefore(colgroup, table.tBodies[0]); - table.insertBefore(thead, table.tBodies[0]); - } - }; - - this.expunge_mailbox = function(mbox) { var lock = false; @@ -1657,8 +1305,8 @@ function rcube_webmail() } // send request to server - var url = '_mbox='+escape(mbox); - this.http_request('expunge', url+add_url, lock); + var url = '_mbox='+urlencode(mbox); + this.http_post('expunge', url+add_url, lock); }; @@ -1679,55 +1327,24 @@ function rcube_webmail() } // send request to server - var url = '_mbox='+escape(mbox); - this.http_request('purge', url+add_url, lock); + var url = '_mbox='+urlencode(mbox); + this.http_post('purge', url+add_url, lock); return true; }; - - this.focus_mailbox = function(mbox) - { - var mbox_li; - if (this.drag_active && mbox != this.env.mailbox && (mbox_li = this.get_mailbox_li(mbox))) - this.set_classname(mbox_li, 'droptarget', true); - } - - this.unfocus_mailbox = function(mbox) - { - var mbox_li; - if (this.drag_active && (mbox_li = this.get_mailbox_li(mbox))) - this.set_classname(mbox_li, 'droptarget', false); - } + // move selected messages to the specified mailbox this.move_messages = function(mbox) { - // exit if no mailbox specified or if selection is empty - if (!mbox || !(this.selection.length || this.env.uid) || mbox==this.env.mailbox) - return; - - var a_uids = new Array(); - - if (this.env.uid) - a_uids[a_uids.length] = this.env.uid; - else + // exit if current or no mailbox specified or if selection is empty + if (!mbox || !this.env.uid || mbox==this.env.mailbox) { - var id; - for (var n=0; n 0) { - this.message_rows[uid].classname = this.message_rows[uid].classname.replace(/\s*deleted/, ''); - this.set_classname(this.message_rows[uid].obj, 'deleted', false); + if (rows[uid].classname.indexOf('deleted') > 0) + { + rows[uid].classname = rows[uid].classname.replace(/\s*deleted/, ''); + this.set_classname(rows[uid].obj, 'deleted', false); } - if (this.message_rows[uid].unread && this.env.unreadicon) + if (rows[uid].unread && this.env.unreadicon) icn_src = this.env.unreadicon; - else if (this.message_rows[uid].replied && this.env.repliedicon) + else if (rows[uid].replied && this.env.repliedicon) icn_src = this.env.repliedicon; else if (this.env.messageicon) icn_src = this.env.messageicon; - if (this.message_rows[uid].icon && icn_src) - this.message_rows[uid].icon.src = icn_src; + if (rows[uid].icon && icn_src) + rows[uid].icon.src = icn_src; } } - this.http_request('mark', '_uid='+a_uids.join(',')+'&_flag=undelete'); + + this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag=undelete'); return true; - } + }; + - this.flag_as_deleted = function(a_uids) { + this.flag_as_deleted = function(a_uids) + { // if deleting message from "view message" don't bother with delete icon if (this.env.action == "show") return false; - for (var i=0; i=0) - message = message.substring(0, p-1) + message.substring(p+sig.length, message.length); - } + p = message.lastIndexOf(sig); + if (p>=0) + 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]) + // add the new signature string + if (this.env.signatures && this.env.signatures[id]) + { + sig = this.env.signatures[id]['text']; + if (this.env.signatures[id]['is_html']) + { + sig = this.env.signatures[id]['plain_text']; + } + if (sig.indexOf('-- ')!=0) + sig = '-- \n'+sig; + message += '\n'+sig; + } + } + else { - sig = this.env.signatures[id]; - if (sig.indexOf('--')!=0) - sig = '--\n'+sig; - message += '\n'+sig; + var eid = tinyMCE.getEditorId('_message'); + // editor is a TinyMCE_Control object + var editor = tinyMCE.getInstanceById(eid); + var msgDoc = editor.getDoc(); + var msgBody = msgDoc.body; + + if (this.env.signatures && this.env.signatures[id]) + { + // Append the signature as a span within the body + var sigElem = msgDoc.getElementById("_rc_sig"); + if (!sigElem) + { + sigElem = msgDoc.createElement("span"); + sigElem.setAttribute("id", "_rc_sig"); + msgBody.appendChild(sigElem); + } + if (this.env.signatures[id]['is_html']) + { + sigElem.innerHTML = this.env.signatures[id]['text']; + } + else + { + sigElem.innerHTML = '
' + this.env.signatures[id]['text'] + '
'; + } + } } if (input_message) input_message.value = message; - + this.env.identity = id; return true; }; @@ -2185,7 +1906,7 @@ function rcube_webmail() { if (!this.gui_objects.attachmentlist) return false; - + var li = document.createElement('LI'); li.id = name; li.innerHTML = content; @@ -2207,7 +1928,7 @@ function rcube_webmail() this.remove_attachment = function(name) { if (name) - this.http_request('remove-attachment', '_file='+escape(name)); + this.http_post('remove-attachment', '_file='+urlencode(name)); return true; }; @@ -2216,19 +1937,27 @@ function rcube_webmail() this.add_contact = function(value) { if (value) - this.http_request('addcontact', '_address='+value); + this.http_post('addcontact', '_address='+value); return true; }; - // send remote request to search mail - this.qsearch = function(value, mbox) + // send remote request to search mail or contacts + this.qsearch = function(value) { - if (value && mbox) + if (value != '') { - this.clear_message_list(); + if (this.message_list) + this.message_list.clear(); + else if (this.contact_list) { + this.contact_list.clear(true); + this.show_contentframe(false); + } + + // reset vars + this.env.current_page = 1; this.set_busy(true, 'searching'); - this.http_request('search', '_search='+value+'&_mbox='+mbox, true); + this.http_request('search', '_q='+urlencode(value)+(this.env.mailbox ? '&_mbox='+this.env.mailbox : '')+(this.env.source ? '&_source='+urlencode(this.env.source) : ''), true); } return true; }; @@ -2265,11 +1994,9 @@ function rcube_webmail() if (this.ksearch_timer) clearTimeout(this.ksearch_timer); - if (!e) - e = window.event; - var highlight; - var key = e.keyCode ? e.keyCode : e.which; + var key = rcube_event.get_keycode(e); + var mod = rcube_event.get_modifier(e); switch (key) { @@ -2288,7 +2015,6 @@ function rcube_webmail() if (highlight && (next = dir ? highlight.previousSibling : highlight.nextSibling)) { highlight.removeAttribute('id'); - //highlight.removeAttribute('class'); this.set_classname(highlight, 'selected', false); } @@ -2299,42 +2025,21 @@ function rcube_webmail() this.ksearch_selected = next._rcm_id; } - if (e.preventDefault) - e.preventDefault(); - return false; + return rcube_event.cancel(e); case 9: // tab - if(e.shiftKey) + if(mod == SHIFT_KEY) break; case 13: // enter if (this.ksearch_selected===null || !this.ksearch_input || !this.ksearch_value) break; - // get cursor pos - var inp_value = this.ksearch_input.value.toLowerCase(); - var cpos = this.get_caret_pos(this.ksearch_input); - var p = inp_value.lastIndexOf(this.ksearch_value, cpos); - - // replace search string with full address - var pre = this.ksearch_input.value.substring(0, p); - var end = this.ksearch_input.value.substring(p+this.ksearch_value.length, this.ksearch_input.value.length); - var insert = this.env.contacts[this.ksearch_selected]+', '; - this.ksearch_input.value = pre + insert + end; - - //this.ksearch_input.value = this.ksearch_input.value.substring(0, p)+insert; - - // set caret to insert pos - cpos = p+insert.length; - if (this.ksearch_input.setSelectionRange) - this.ksearch_input.setSelectionRange(cpos, cpos); - - // hide ksearch pane + // insert selected address and hide ksearch pane + this.insert_recipient(this.ksearch_selected); this.ksearch_hide(); - - if (e.preventDefault) - e.preventDefault(); - return false; + + return rcube_event.cancel(e); case 27: // escape this.ksearch_hide(); @@ -2343,13 +2048,37 @@ function rcube_webmail() } // start timer - this.ksearch_timer = setTimeout(this.ref+'.ksearch_get_results()', 200); + this.ksearch_timer = setTimeout(function(){ ref.ksearch_get_results(); }, 200); this.ksearch_input = obj; return true; }; + this.insert_recipient = function(id) + { + if (!this.env.contacts[id] || !this.ksearch_input) + return; + + // get cursor pos + var inp_value = this.ksearch_input.value.toLowerCase(); + var cpos = this.get_caret_pos(this.ksearch_input); + var p = inp_value.lastIndexOf(this.ksearch_value, cpos); + + // replace search string with full address + var pre = this.ksearch_input.value.substring(0, p); + var end = this.ksearch_input.value.substring(p+this.ksearch_value.length, this.ksearch_input.value.length); + var insert = this.env.contacts[id]+', '; + this.ksearch_input.value = pre + insert + end; + + // set caret to insert pos + cpos = p+insert.length; + if (this.ksearch_input.setSelectionRange) + this.ksearch_input.setSelectionRange(cpos, cpos); + + }; + + // address search processor this.ksearch_get_results = function() { @@ -2415,6 +2144,8 @@ function rcube_webmail() { li = document.createElement('LI'); li.innerHTML = a_results[i].replace(//, '>'); + li.onmousedown = function(e){ ref.insert_recipient(this._rcm_id); ref.ksearch_pane.show(0); return rcube_event.cancel(e); }; + li.style.cursor = 'pointer'; li._rcm_id = a_result_ids[i]; ul.appendChild(li); } @@ -2440,9 +2171,6 @@ function rcube_webmail() this.ksearch_selected = a_result_ids[0]; } - // resize the containing layer to fit the list - //this.ksearch_pane.resize(ul.offsetWidth, ul.offsetHeight); - // move the results pane right under the input box and make it visible var pos = rcube_get_object_pos(this.ksearch_input); this.ksearch_pane.move(pos.x, pos.y+this.ksearch_input.offsetHeight); @@ -2481,18 +2209,56 @@ function rcube_webmail() /*********************************************************/ - this.list_contacts = function(page) + this.contactlist_keypress = function(list) + { + if (list.key_pressed == list.DELETE_KEY) + this.command('delete'); + }; + + + this.contactlist_select = function(list) + { + if (this.preview_timer) + clearTimeout(this.preview_timer); + + var id, frame, ref = this; + if (id = list.get_single_selection()) + this.preview_timer = setTimeout(function(){ ref.load_contact(id, 'show'); }, this.dblclick_time + 10); + else if (this.env.contentframe) + this.show_contentframe(false); + + this.enable_command('edit', id?true:false); + this.enable_command('compose', list.selection.length > 0); + this.enable_command('delete', list.selection.length && this.env.address_sources && !this.env.address_sources[this.env.source].readonly); + + return false; + }; + + + this.list_contacts = function(src, page) { var add_url = ''; var target = window; - if (page && this.current_page==page) + if (!src) + src = this.env.source; + + if (page && this.current_page==page && src == this.env.source) return false; + + if (src != this.env.source) + { + page = 1; + this.env.current_page = page; + } + + this.select_folder(src, this.env.source); + this.env.source = src; // load contacts remotely if (this.gui_objects.contactslist) { - this.list_contacts_remote(page); + this.list_contacts_remote(src, page); return; } @@ -2502,25 +2268,31 @@ function rcube_webmail() add_url = '&_framed=1'; } + // also send search request to get the correct listing + if (this.env.search_request) + add_url += '&_search='+this.env.search_request; + this.set_busy(true, 'loading'); - location.href = this.env.comm_path+(page ? '&_page='+page : '')+add_url; + target.location.href = this.env.comm_path+(src ? '&_source='+urlencode(src) : '')+(page ? '&_page='+page : '')+add_url; }; // send remote request to load contacts list - this.list_contacts_remote = function(page) + this.list_contacts_remote = function(src, page) { - // clear list - var table = this.gui_objects.contactslist; - var tbody = document.createElement('TBODY'); - table.insertBefore(tbody, table.tBodies[0]); - table.tBodies[1].style.display = 'none'; - - this.contact_rows = new Array(); - this.list_rows = this.contact_rows; + // clear message list first + this.contact_list.clear(true); + this.show_contentframe(false); + this.enable_command('delete', 'compose', false); // send request to server - var url = page ? '&_page='+page : ''; + var url = (src ? '&_source='+urlencode(src) : '') + (page ? '&_page='+page : ''); + this.env.source = src; + + // also send search request to get the right messages + if (this.env.search_request) + url += '&_search='+this.env.search_request; + this.set_busy(true, 'loading'); this.http_request('list', url, true); }; @@ -2535,28 +2307,35 @@ function rcube_webmail() { add_url = '&_framed=1'; target = window.frames[this.env.contentframe]; - document.getElementById(this.env.contentframe).style.visibility = 'inherit'; + this.show_contentframe(true); } else if (framed) return false; - //if (this.env.framed && add_url=='') - - // add_url = '&_framed=1'; - - if (action && (cid || action=='add')) + if (action && (cid || action=='add') && !this.drag_active) { this.set_busy(true); - target.location.href = this.env.comm_path+'&_action='+action+'&_cid='+cid+add_url; + target.location.href = this.env.comm_path+'&_action='+action+'&_source='+urlencode(this.env.source)+'&_cid='+urlencode(cid) + add_url; } return true; }; + // copy a contact to the specified target (group or directory) + this.copy_contact = function(cid, to) + { + if (!cid) + cid = this.contact_list.get_selection().join(','); + + if (to != this.env.source && cid && this.env.address_sources[to] && !this.env.address_sources[to].readonly) + this.http_post('copy', '_cid='+urlencode(cid)+'&_source='+urlencode(this.env.source)+'&_to='+urlencode(to)); + }; + this.delete_contacts = function() { // exit if no mailbox specified or if selection is empty - if (!(this.selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm'))) + var selection = this.contact_list.get_selection(); + if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm'))) return; var a_cids = new Array(); @@ -2566,26 +2345,20 @@ function rcube_webmail() else { var id; - for (var n=0; n + this.env.subscriptionrows[tbody.childNodes[index[i+1]].id][0]) + { + var swap = tbody.replaceChild(tbody.childNodes[index[i]], tbody.childNodes[index[i+1]]); + if (typeof(tbody.childNodes[index[i]]) != 'undefined') + tbody.insertBefore(swap, tbody.childNodes[index[i]]) + else + tbody.appendChild(swap); + swapped = true; + } + } + if (swapped) + this.sort_subscription_list(); + }; + /*********************************************************/ /********* GUI functionality *********/ @@ -3033,7 +2782,9 @@ function rcube_webmail() this.set_page_buttons = function() { this.enable_command('nextpage', (this.env.pagecount > this.env.current_page)); + this.enable_command('lastpage', (this.env.pagecount > this.env.current_page)); this.enable_command('previouspage', (this.env.current_page > 1)); + this.enable_command('firstpage', (this.env.current_page > 1)); } @@ -3164,6 +2915,10 @@ function rcube_webmail() // display a system message this.display_message = function(msg, type, hold) { + // pass command to parent window + if (this.env.framed && parent.rcmail ) + return parent.rcmail.display_message(msg, type, hold); + this.set_busy(false); if (!this.loaded) // save message in order to display after page loaded { @@ -3189,7 +2944,7 @@ function rcube_webmail() this.gui_objects.message.onmousedown = function(){ this._rcube.hide_message(); return true; }; if (!hold) - this.message_timer = setTimeout(this.ref+'.hide_message()', this.message_time); + this.message_timer = setTimeout(function(){ ref.hide_message(); }, this.message_time); }; @@ -3205,38 +2960,37 @@ function rcube_webmail() // mark a mailbox as selected and set environment variable - this.select_mailbox = function(mbox) + this.select_folder = function(name, old) + { + if (this.gui_objects.folderlist) { - if (this.gui_objects.mailboxlist ) - { - var item, reg, text_obj; - var current_li = this.get_mailbox_li(); - var mbox_li = this.get_mailbox_li(mbox); + var current_li, target_li; - if (current_li) - { + if ((current_li = this.get_folder_li(old))) + { this.set_classname(current_li, 'selected', false); this.set_classname(current_li, 'unfocused', false); - } - - if (mbox_li || this.env.mailbox == mbox) - { - this.set_classname(mbox_li, 'unfocused', false); - this.set_classname(mbox_li, 'selected', true); - } } - - // also update mailbox name in window title - if (document.title) + + if ((target_li = this.get_folder_li(name))) { - var doc_title = String(document.title); - var reg = new RegExp(this.env.mailbox.toLowerCase(), 'i'); - if (this.env.mailbox && doc_title.match(reg)) - document.title = doc_title.replace(reg, mbox).replace(/^\([0-9]+\)\s+/i, ''); + this.set_classname(target_li, 'unfocused', false); + this.set_classname(target_li, 'selected', true); } - - this.env.mailbox = mbox; - }; + } + }; + + // helper method to find a folder list item + this.get_folder_li = function(name) + { + if (this.gui_objects.folderlist) + { + name = String(name).replace(this.identifier_expr, ''); + return document.getElementById('rcmli'+name); + } + + return null; + }; // for reordering column array, Konqueror workaround @@ -3271,9 +3025,9 @@ function rcube_webmail() // create a table row in the message list this.add_message_row = function(uid, cols, flags, attachment, attop) { - if (!this.gui_objects.messagelist || !this.gui_objects.messagelist.tBodies[0]) + if (!this.gui_objects.messagelist || !this.message_list) return false; - + var tbody = this.gui_objects.messagelist.tBodies[0]; var rowcount = tbody.rows.length; var even = rowcount%2; @@ -3285,8 +3039,8 @@ function rcube_webmail() var row = document.createElement('TR'); row.id = 'rcmrow'+uid; row.className = 'message '+(even ? 'even' : 'odd')+(flags.unread ? ' unread' : '')+(flags.deleted ? ' deleted' : ''); - - if (this.in_selection(uid)) + + if (this.message_list.in_selection(uid)) row.className += ' selected'; var icon = flags.deleted && this.env.deletedicon ? this.env.deletedicon: @@ -3312,13 +3066,8 @@ function rcube_webmail() col.className = 'icon'; col.innerHTML = attachment && this.env.attachmenticon ? '' : ''; row.appendChild(col); - - if (attop && tbody.rows.length) - tbody.insertBefore(row, tbody.firstChild); - else - tbody.appendChild(row); - - this.init_message_row(row); + + this.message_list.insert_row(row, attop); }; @@ -3333,12 +3082,16 @@ function rcube_webmail() }; // replace content of quota display - this.set_quota = function(text) - { - if (this.gui_objects.quotadisplay) - this.gui_objects.quotadisplay.innerHTML = text; + this.set_quota = function() + { + if (this.gui_objects.quotadisplay && + this.gui_objects.quotadisplay.attributes.getNamedItem('display') && + this.gui_objects.quotadisplay.attributes.getNamedItem('id')) + this.http_request('quotadisplay', '_display='+ + this.gui_objects.quotadisplay.attributes.getNamedItem('display').nodeValue+ + '&_id='+this.gui_objects.quotadisplay.attributes.getNamedItem('id').nodeValue, false); }; - + // update the mailboxlist this.set_unread_count = function(mbox, count, set_title) @@ -3350,8 +3103,8 @@ function rcube_webmail() set_title = true; var reg, text_obj; - var item = this.get_mailbox_li(mbox); - mbox = String(mbox).toLowerCase().replace(this.mbox_expression, ''); + var item = this.get_folder_li(mbox); + mbox = String(mbox).toLowerCase().replace(this.identifier_expr, ''); if (item && item.className && item.className.indexOf('mailbox '+mbox)>=0) { @@ -3387,7 +3140,7 @@ function rcube_webmail() // add row to contacts list - this.add_contact_row = function(cid, cols) + this.add_contact_row = function(cid, cols, select) { if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0]) return false; @@ -3400,7 +3153,7 @@ function rcube_webmail() row.id = 'rcmrow'+cid; row.className = 'contact '+(even ? 'even' : 'odd'); - if (this.in_selection(cid)) + if (this.contact_list.in_selection(cid)) row.className += ' selected'; // add each submitted col @@ -3412,73 +3165,21 @@ function rcube_webmail() row.appendChild(col); } - tbody.appendChild(row); - this.init_table_row(row, 'contact_rows'); + this.contact_list.insert_row(row); }; - - /********************************************************/ - /********* drag & drop methods *********/ - /********************************************************/ - - - this.drag_mouse_move = function(e) + this.toggle_editor = function(checkbox, textElementName) { - if (this.drag_start) + var ischecked = checkbox.checked; + if (ischecked) { - if (!this.draglayer) - this.draglayer = new rcube_layer('rcmdraglayer', {x:0, y:0, width:300, vis:0, zindex:2000}); - - // get subjects of selectedd messages - var names = ''; - var c, subject, obj; - for(var n=0; n12) // only show 12 lines - { - names += '...'; - break; - } - - if (this.message_rows[this.selection[n]].obj) - { - obj = this.message_rows[this.selection[n]].obj; - subject = ''; - - for(c=0; c 50 ? subject.substring(0, 50)+'...' : subject) + '
'; - } - } - } - - this.draglayer.write(names); - this.draglayer.show(1); + tinyMCE.execCommand('mceAddControl', true, textElementName); + } + else + { + tinyMCE.execCommand('mceRemoveControl', true, textElementName); } - - var pos = this.get_mouse_pos(e); - this.draglayer.move(pos.x+20, pos.y-5); - - this.drag_start = false; - this.drag_active = true; - - return false; - }; - - - this.drag_mouse_up = function() - { - document.onmousemove = null; - - if (this.draglayer && this.draglayer.visible) - this.draglayer.show(0); - - this.drag_active = false; - - return false; }; @@ -3487,6 +3188,24 @@ function rcube_webmail() /********* remote request methods *********/ /********************************************************/ + this.redirect = function(url) + { + this.set_busy(true); + if (this.env.framed && window.parent) + parent.location.href = url; + else + location.href = url; + }; + + this.goto_url = function(action, query, lock) + { + if (lock) + this.set_busy(true); + + var querystring = query ? '&'+query : ''; + this.redirect(this.env.comm_path+'&_action='+action+querystring); + }; + this.http_sockets = new Array(); @@ -3511,7 +3230,7 @@ function rcube_webmail() this.http_request = function(action, querystring, lock) { var request_obj = this.get_request_obj(); - querystring += '&_remote=1'; + querystring += (querystring ? '&' : '') + '_remote=1'; // add timestamp to request url to avoid cacheing problems in Safari if (bw.safari) @@ -3520,20 +3239,45 @@ function rcube_webmail() // send request if (request_obj) { - // prompt('request', this.env.comm_path+'&_action='+escape(action)+'&'+querystring); - console('HTTP request: '+this.env.comm_path+'&_action='+escape(action)+'&'+querystring); + console.log('HTTP request: '+this.env.comm_path+'&_action='+action+'&'+querystring); if (lock) this.set_busy(true); + var rcm = this; request_obj.__lock = lock ? true : false; request_obj.__action = action; - request_obj.onerror = function(o){ rcube_webmail_client.http_error(o); }; - request_obj.oncomplete = function(o){ rcube_webmail_client.http_response(o); }; - request_obj.GET(this.env.comm_path+'&_action='+escape(action)+'&'+querystring); + request_obj.onerror = function(o){ ref.http_error(o); }; + request_obj.oncomplete = function(o){ ref.http_response(o); }; + request_obj.GET(this.env.comm_path+'&_action='+action+'&'+querystring); } }; + // send a http POST request to the server + this.http_post = function(action, postdata, lock) + { + var request_obj; + if (postdata && typeof(postdata) == 'object') + postdata._remote = 1; + else + postdata += (postdata ? '&' : '') + '_remote=1'; + + // send request + if (request_obj = this.get_request_obj()) + { + console.log('HTTP POST: '+this.env.comm_path+'&_action='+action); + + if (lock) + this.set_busy(true); + + var rcm = this; + request_obj.__lock = lock ? true : false; + request_obj.__action = action; + request_obj.onerror = function(o){ rcm.http_error(o); }; + request_obj.oncomplete = function(o){ rcm.http_response(o); }; + request_obj.POST(this.env.comm_path+'&_action='+action, postdata); + } + }; // handle HTTP response this.http_response = function(request_obj) @@ -3547,7 +3291,7 @@ function rcube_webmail() this.set_busy(false); - console(request_obj.get_text()); + console.log(request_obj.get_text()); // if we get javascript code from server -> execute it if (request_obj.get_text() && (ctype=='text/javascript' || ctype=='application/x-javascript')) @@ -3560,15 +3304,17 @@ function rcube_webmail() case 'moveto': if (this.env.action=='show') this.command('list'); + else if (this.message_list) + this.message_list.init(); break; case 'list': if (this.env.messagecount) - this.enable_command('purge', (this.env.mailbox==this.env.trash_mailbox)); + this.enable_command('purge', (this.env.mailbox==this.env.trash_mailbox || this.env.mailbox==this.env.junk_mailbox)); case 'expunge': this.enable_command('select-all', 'select-none', 'expunge', this.env.messagecount ? true : false); - break; + break; } request_obj.reset(); @@ -3619,91 +3365,16 @@ function rcube_webmail() // and return the message uid this.get_single_uid = function() { - return this.env.uid ? this.env.uid : (this.selection.length==1 ? this.selection[0] : null); + return this.env.uid ? this.env.uid : (this.message_list ? this.message_list.get_single_selection() : null); }; // same as above but for contacts this.get_single_cid = function() { - return this.env.cid ? this.env.cid : (this.selection.length==1 ? this.selection[0] : null); + return this.env.cid ? this.env.cid : (this.contact_list ? this.contact_list.get_single_selection() : null); }; -/* deprecated methods - - // check if Shift-key is pressed on event - this.check_shiftkey = function(e) - { - if(!e && window.event) - e = window.event; - - if(bw.linux && bw.ns4 && e.modifiers) - return true; - else if((bw.ns4 && e.modifiers & Event.SHIFT_MASK) || (e && e.shiftKey)) - return true; - else - 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; - } -*/ - - // returns modifier key (constants defined at top of file) - this.get_modifier = function(e) - { - var opcode = 0; - e = e || window.event; - - if (bw.mac && e) - { - opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY); - return opcode; - } - if (e) - { - opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY); - return opcode; - } - if (e.cancelBubble) - { - e.cancelBubble = true; - e.returnValue = false; - } - else if (e.preventDefault) - e.preventDefault(); - } - - - this.get_mouse_pos = function(e) - { - if(!e) e = window.event; - var mX = (e.pageX) ? e.pageX : e.clientX; - var mY = (e.pageY) ? e.pageY : e.clientY; - - if(document.body && document.all) - { - mX += document.body.scrollLeft; - mY += document.body.scrollTop; - } - - return { x:mX, y:mY }; - }; - - this.get_caret_pos = function(obj) { if (typeof(obj.selectionEnd)!='undefined') @@ -3768,7 +3439,10 @@ function rcube_webmail() -// class for HTTP requests +/** + * Class for sending HTTP requests + * @constructor + */ function rcube_http_request() { this.url = ''; @@ -3799,14 +3473,17 @@ function rcube_http_request() if (window.XMLHttpRequest) this.xmlhttp = new XMLHttpRequest(); else if (window.ActiveXObject) - this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); + { + try { this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); } + catch(e) { this.xmlhttp = null; } + } else { } } - // sedn GET request + // send GET request this.GET = function(url) { this.build(); @@ -3827,9 +3504,36 @@ function rcube_http_request() }; - this.POST = function(url, a_param) + this.POST = function(url, body, contentType) + { + // default value for contentType if not provided + if (typeof(contentType) == 'undefined') + contentType = 'application/x-www-form-urlencoded'; + + this.build(); + + if (!this.xmlhttp) + { + this.onerror(this); + return false; + } + + var req_body = body; + if (typeof(body) == 'object') { - // not implemented yet + req_body = ''; + for (var p in body) + req_body += (req_body ? '&' : '') + p+'='+urlencode(body[p]); + } + + var ref = this; + this.url = url; + this.busy = true; + + this.xmlhttp.onreadystatechange = function() { ref.xmlhttp_onreadystatechange(); }; + this.xmlhttp.open('POST', url, true); + this.xmlhttp.setRequestHeader('Content-Type', contentType); + this.xmlhttp.send(req_body); }; @@ -3886,17 +3590,10 @@ function rcube_http_request() } // end class rcube_http_request - -function console(str) +// helper function to call the init method with a delay +function call_init(o) { - if (document.debugform && document.debugform.console) - document.debugform.console.value += str+'\n--------------------------------------\n'; + if (window[o] && window[o].init) + setTimeout(o+'.init()', 200); } - -// set onload handler -window.onload = function(e) - { - if (window.rcube_webmail_client) - rcube_webmail_client.init(); - }; diff --git a/program/js/common.js b/program/js/common.js index 2058635..1e89be2 100644 --- a/program/js/common.js +++ b/program/js/common.js @@ -3,18 +3,26 @@ | RoundCube common js library | | | | This file is part of the RoundCube web development suite | - | Copyright (C) 2005, RoundCube Dev, - Switzerland | + | Copyright (C) 2005-2006, RoundCube Dev, - Switzerland | | Licensed under the GNU GPL | | | +-----------------------------------------------------------------------+ | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ - $Id: common.js 289 2006-08-02 03:42:08Z cmcnulty $ + $Id: common.js 543 2007-04-28 18:07:12Z thomasb $ */ +// Constants +var CONTROL_KEY = 1; +var SHIFT_KEY = 2; +var CONTROL_SHIFT_KEY = 3; -// default browsercheck + +/** + * Default browser check class + * @construcotr + */ function roundcube_browser() { this.ver = parseFloat(navigator.appVersion); @@ -92,10 +100,127 @@ function roundcube_browser() } +// static functions for event handling +var rcube_event = { + + /** + * returns the event key code + */ + get_keycode: function(e) + { + e = e || window.event; + return e && e.keyCode ? e.keyCode : (e && e.which ? e.which : 0); + }, + +/** + * returns modifier key (constants defined at top of file) + */ +get_modifier: function(e) +{ + var opcode = 0; + e = e || window.event; + + if (bw.mac && e) + { + opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY); + return opcode; + } + if (e) + { + opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY); + return opcode; + } +}, + +/** + * Return absolute mouse position of an event + */ +get_mouse_pos: function(e) +{ + if (!e) e = window.event; + var mX = (e.pageX) ? e.pageX : e.clientX; + var mY = (e.pageY) ? e.pageY : e.clientY; + + if (document.body && document.all) + { + mX += document.body.scrollLeft; + mY += document.body.scrollTop; + } + + return { x:mX, y:mY }; +}, + +/** + * Add an object method as event listener to a certain element + */ +add_listener: function(p) +{ + if (!p.object || !p.method) // not enough arguments + return; + if (!p.element) + p.element = document; + + if (!p.object._rc_events) + p.object._rc_events = []; + + var key = p.event + '*' + p.method; + if (!p.object._rc_events[key]) + p.object._rc_events[key] = function(e){ return p.object[p.method](e); }; + + if (p.element.addEventListener) + p.element.addEventListener(p.event, p.object._rc_events[key], false); + else if (p.element.attachEvent) + p.element.attachEvent('on'+p.event, p.object._rc_events[key]); + else + p.element['on'+p.event] = p.object._rc_events[key]; +}, + +/** + * Remove event listener + */ +remove_listener: function(p) +{ + if (!p.element) + p.element = document; + + var key = p.event + '*' + p.method; + if (p.object && p.object._rc_events && p.object._rc_events[key]) { + if (p.element.removeEventListener) + p.element.removeEventListener(p.event, p.object._rc_events[key], false); + else if (p.element.detachEvent) + p.element.detachEvent('on'+p.event, p.object._rc_events[key]); + else + p.element['on'+p.event] = null; + } +}, + +/** + * Prevent event propagation and bubbeling + */ +cancel: function(evt) +{ + var e = evt ? evt : window.event; + if (e.preventDefault) + e.preventDefault(); + if (e.stopPropagation) + e.stopPropagation(); + + e.cancelBubble = true; + e.returnValue = false; + return false; +} + +}; var rcube_layer_objects = new Array(); + +/** + * RoundCube generic layer (floating box) class + * + * @constructor + */ function rcube_layer(id, attributes) { this.name = id; @@ -263,6 +388,7 @@ function rcube_layer(id, attributes) } } + // check if input is a valid email address // By Cal Henderson // http://code.iamcal.com/php/rfc822/ @@ -343,6 +469,13 @@ function find_in_array() } +// make a string URL safe +function urlencode(str) +{ + return window.encodeURIComponent ? encodeURIComponent(str) : escape(str); +} + + // get any type of html objects by id/name function rcube_find_object(id, d) { @@ -372,7 +505,7 @@ function rcube_find_object(id, d) { if(d.layers[id]) obj = d.layers[id]; for(n=0; !obj && n | + | Charles McNulty | + +-----------------------------------------------------------------------+ + | Requires: common.js | + +-----------------------------------------------------------------------+ + + $Id: list.js 344 2006-09-18 03:49:28Z thomasb $ +*/ + + +/** + * RoundCube List Widget class + * @contructor + */ +function rcube_list_widget(list, p) + { + // static contants + this.ENTER_KEY = 13; + this.DELETE_KEY = 46; + + this.list = list ? list : null; + this.frame = null; + this.rows = []; + this.selection = []; + + this.shiftkey = false; + + this.multiselect = false; + this.draggable = false; + this.keyboard = false; + + this.dont_select = false; + this.drag_active = false; + this.last_selected = 0; + this.in_selection_before = false; + this.focused = false; + this.drag_mouse_start = null; + this.dblclick_time = 600; + this.row_init = function(){}; + this.events = { click:[], dblclick:[], select:[], keypress:[], dragstart:[], dragend:[] }; + + // overwrite default paramaters + if (p && typeof(p)=='object') + for (var n in p) + this[n] = p[n]; + } + + +rcube_list_widget.prototype = { + + +/** + * get all message rows from HTML table and init each row + */ +init: function() +{ + if (this.list && this.list.tBodies[0]) + { + this.rows = new Array(); + + var row; + for(var r=0; r