]> git.donarmstrong.com Git - roundcube.git/commitdiff
Merge commit 'upstream/0.7'
authorVincent Bernat <bernat@luffy.cx>
Fri, 23 Dec 2011 21:03:14 +0000 (22:03 +0100)
committerVincent Bernat <bernat@luffy.cx>
Fri, 23 Dec 2011 21:03:14 +0000 (22:03 +0100)
240 files changed:
CHANGELOG
INSTALL
INSTALL.orig [new file with mode: 0644]
SQL/mssql.initial.sql
SQL/mssql.upgrade.sql
SQL/mysql.initial.sql
SQL/mysql.update.sql
SQL/postgres.initial.sql
SQL/postgres.update.sql
SQL/sqlite.initial.sql
SQL/sqlite.update.sql
bin/indexcontacts.sh
bin/installto.sh
bin/jsshrink.sh
config/db.inc.php.dist
config/main.inc.php.dist
index.php
installer/rcube_install.php
installer/test.php
plugins/acl/acl.js
plugins/acl/acl.php
plugins/acl/skins/default/acl.css
plugins/archive/archive.js
plugins/archive/archive.php
plugins/archive/localization/fr_FR.inc
plugins/archive/package.xml
plugins/enigma/README
plugins/enigma/config.inc.php [deleted file]
plugins/enigma/config.inc.php.dist [new file with mode: 0644]
plugins/enigma/lib/enigma_ui.php
plugins/http_authentication/http_authentication.php
plugins/managesieve/Changelog
plugins/managesieve/config.inc.php.dist
plugins/managesieve/lib/rcube_sieve.php
plugins/managesieve/lib/rcube_sieve_script.php
plugins/managesieve/localization/de_CH.inc
plugins/managesieve/localization/de_DE.inc
plugins/managesieve/localization/en_US.inc
plugins/managesieve/localization/es_ES.inc
plugins/managesieve/localization/lv_LV.inc [new file with mode: 0644]
plugins/managesieve/localization/pl_PL.inc
plugins/managesieve/localization/pt_BR.inc
plugins/managesieve/managesieve.js
plugins/managesieve/managesieve.php
plugins/managesieve/package.xml [new file with mode: 0644]
plugins/managesieve/skins/default/images/add.png [new file with mode: 0644]
plugins/managesieve/skins/default/images/del.png [new file with mode: 0644]
plugins/managesieve/skins/default/images/down_small.gif [new file with mode: 0644]
plugins/managesieve/skins/default/images/filter.png [new file with mode: 0644]
plugins/managesieve/skins/default/images/up_small.gif [new file with mode: 0644]
plugins/managesieve/skins/default/managesieve.css
plugins/managesieve/skins/default/managesieve_mail.css [new file with mode: 0644]
plugins/managesieve/skins/default/managesieve_toolbar.png [deleted file]
plugins/managesieve/skins/default/templates/filteredit.html
plugins/managesieve/skins/default/templates/managesieve.html
plugins/managesieve/tests/parser.phpt
plugins/managesieve/tests/parser_body.phpt [new file with mode: 0644]
plugins/managesieve/tests/parser_imapflags.phpt [new file with mode: 0644]
plugins/managesieve/tests/parser_include.phpt [new file with mode: 0644]
plugins/managesieve/tests/parser_kep14.phpt [new file with mode: 0644]
plugins/managesieve/tests/parser_prefix.phpt [new file with mode: 0644]
plugins/managesieve/tests/parser_relational.phpt [new file with mode: 0644]
plugins/managesieve/tests/parser_vacation.phpt [new file with mode: 0644]
plugins/managesieve/tests/parser_variables.phpt [new file with mode: 0644]
plugins/managesieve/tests/parset_subaddress.phpt [new file with mode: 0644]
plugins/new_user_dialog/localization/bg_BG.inc [new file with mode: 0644]
plugins/newmail_notifier/config.inc.php.dist
plugins/newmail_notifier/localization/de_CH.inc [new file with mode: 0644]
plugins/newmail_notifier/localization/de_DE.inc [new file with mode: 0644]
plugins/newmail_notifier/localization/en_US.inc
plugins/newmail_notifier/localization/lv_LV.inc [new file with mode: 0644]
plugins/newmail_notifier/localization/pl_PL.inc
plugins/newmail_notifier/localization/pt_BR.inc [new file with mode: 0644]
plugins/newmail_notifier/localization/ru_RU.inc [new file with mode: 0644]
plugins/newmail_notifier/mail.png [new file with mode: 0644]
plugins/newmail_notifier/newmail_notifier.js
plugins/newmail_notifier/newmail_notifier.php
plugins/password/config.inc.php.dist
plugins/password/drivers/ldap.php
plugins/password/drivers/ldap_simple.php
plugins/password/drivers/sql.php
plugins/password/package.xml
plugins/password/password.php
plugins/userinfo/localization/fr_FR.inc [new file with mode: 0644]
plugins/userinfo/localization/ro_RO.inc [new file with mode: 0644]
program/include/clisetup.php
program/include/iniset.php
program/include/main.inc
program/include/rcmail.php
program/include/rcmail.php.orig [new file with mode: 0644]
program/include/rcube_addressbook.php
program/include/rcube_browser.php
program/include/rcube_cache.php
program/include/rcube_config.php
program/include/rcube_contacts.php
program/include/rcube_html_page.php
program/include/rcube_imap.php
program/include/rcube_imap_cache.php [new file with mode: 0644]
program/include/rcube_imap_generic.php
program/include/rcube_json_output.php
program/include/rcube_ldap.php
program/include/rcube_mdb2.php
program/include/rcube_message.php
program/include/rcube_mime_struct.php
program/include/rcube_plugin.php
program/include/rcube_plugin_api.php
program/include/rcube_result_set.php
program/include/rcube_session.php
program/include/rcube_shared.inc
program/include/rcube_smtp.php
program/include/rcube_spellchecker.php
program/include/rcube_string_replacer.php
program/include/rcube_template.php
program/include/rcube_user.php
program/include/rcube_vcard.php
program/js/app.js
program/js/app.js.src
program/js/common.js
program/js/common.js.src
program/js/editor.js
program/js/googiespell.js
program/js/googiespell.js.src
program/js/list.js
program/js/list.js.src
program/lib/html2text.php
program/lib/washtml.php
program/localization/ar_SA/labels.inc
program/localization/ar_SA/messages.inc
program/localization/bg_BG/labels.inc
program/localization/bg_BG/messages.inc
program/localization/ca_ES/labels.inc
program/localization/ca_ES/messages.inc
program/localization/cs_CZ/labels.inc
program/localization/cs_CZ/messages.inc
program/localization/cy_GB/labels.inc
program/localization/cy_GB/messages.inc
program/localization/da_DK/labels.inc
program/localization/da_DK/messages.inc
program/localization/de_CH/labels.inc
program/localization/de_CH/messages.inc
program/localization/de_DE/labels.inc
program/localization/de_DE/messages.inc
program/localization/en_GB/labels.inc
program/localization/en_GB/messages.inc
program/localization/en_US/labels.inc
program/localization/en_US/messages.inc
program/localization/es_ES/labels.inc
program/localization/es_ES/messages.inc
program/localization/et_EE/messages.inc
program/localization/fr_FR/labels.inc
program/localization/fr_FR/messages.inc
program/localization/gl_ES/labels.inc
program/localization/gl_ES/messages.inc
program/localization/he_IL/labels.inc
program/localization/he_IL/messages.inc
program/localization/hr_HR/messages.inc
program/localization/hu_HU/labels.inc
program/localization/hu_HU/messages.inc
program/localization/id_ID/messages.inc
program/localization/it_IT/labels.inc
program/localization/it_IT/messages.inc
program/localization/ja_JP/labels.inc
program/localization/ja_JP/messages.inc
program/localization/ka_GE/labels.inc
program/localization/ka_GE/messages.inc
program/localization/lt_LT/labels.inc
program/localization/lt_LT/messages.inc
program/localization/lv_LV/labels.inc
program/localization/lv_LV/messages.inc
program/localization/nl_NL/labels.inc
program/localization/nl_NL/messages.inc
program/localization/nn_NO/labels.inc
program/localization/pl_PL/labels.inc
program/localization/pl_PL/messages.inc
program/localization/pt_BR/labels.inc
program/localization/pt_BR/messages.inc
program/localization/pt_PT/labels.inc
program/localization/pt_PT/messages.inc
program/localization/ru_RU/labels.inc
program/localization/ru_RU/messages.inc
program/localization/sk_SK/labels.inc
program/localization/sk_SK/messages.inc
program/localization/sl_SI/labels.inc
program/localization/sl_SI/messages.inc
program/localization/sv_SE/labels.inc
program/localization/sv_SE/messages.inc
program/localization/tr_TR/labels.inc
program/localization/tr_TR/messages.inc
program/localization/uk_UA/labels.inc
program/localization/uk_UA/messages.inc
program/localization/zh_CN/labels.inc
program/localization/zh_CN/messages.inc
program/steps/addressbook/copy.inc
program/steps/addressbook/export.inc
program/steps/addressbook/func.inc
program/steps/addressbook/import.inc
program/steps/addressbook/list.inc
program/steps/addressbook/mailto.inc
program/steps/addressbook/save.inc
program/steps/addressbook/search.inc
program/steps/addressbook/upload_photo.inc
program/steps/mail/addcontact.inc
program/steps/mail/attachments.inc
program/steps/mail/autocomplete.inc
program/steps/mail/check_recent.inc
program/steps/mail/compose.inc
program/steps/mail/folders.inc
program/steps/mail/func.inc
program/steps/mail/get.inc
program/steps/mail/list.inc
program/steps/mail/mark.inc
program/steps/mail/move_del.inc
program/steps/mail/search.inc
program/steps/mail/sendmail.inc
program/steps/mail/show.inc
program/steps/settings/edit_folder.inc
program/steps/settings/edit_identity.inc
program/steps/settings/folders.inc
program/steps/settings/func.inc
program/steps/settings/save_folder.inc
program/steps/settings/save_prefs.inc
program/steps/utils/killcache.inc
program/steps/utils/spell.inc
program/steps/utils/spell_html.inc
skins/default/addressbook.css
skins/default/common.css
skins/default/editor_content.css
skins/default/functions.js
skins/default/ie6hacks.css
skins/default/images/icons/folders.gif
skins/default/images/icons/folders.png
skins/default/images/messageicons.gif
skins/default/images/messageicons.png
skins/default/includes/messagetoolbar.html
skins/default/mail.css
skins/default/print.css
skins/default/templates/addressbook.html
skins/default/templates/mail.html
skins/default/templates/message.html
skins/default/templates/messagepreview.html

index f4daec427835436b174b8fcb134b1d0f5b2972c3..c872df37846db35d22b7a07b7bd53cdc5053e6b6 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,96 +1,80 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
-- Fix bug where the last identity is used on reply (#1488101)
-- Fix locked folder rename option on servers supporting RFC2086 only (#1488089)
-- Fix encoding of LDAP contacts identifiers (#1488079)
-- Fix session race conditions when composing new messages
-- jQuery 1.6.4
-- Fix handling of binary attachments encoded with quoted-printable (#1488065)
-- Fix text-overflow:ellipsis issues on messages list in FF7 and Webkit (#1488061)
-- Fix handling of links with IP address
-- Fix bug where message list filter was reset on folder compacting (#1488076)
+RELEASE 0.7
+-----------
+- Make Roundcube render the Email Standards Project Acid Test correctly
+- Replace prompt() with jQuery UI dialog (#1485135)
+- Fix navigation in messages search results
+- Improved handling of some malformed values encoded with quoted-printable (#1488232)
+- Add possibility to do LDAP bind before searching for bind DN
+- Fix handling of empty <U> tags in HTML messages (#1488225)
+- Add content filter for embedded attachments to protect from XSS on IE (#1487895)
+- Use strpos() instead of strstr() when possible (#1488211)
+- Fix handling HTML entities when converting HTML to text (#1488212)
+- Fix fit_string_to_size() renders browser and ui unresponsive (#1488207)
+- Fix handling of invalid characters in request (#1488124)
+- Fix merging some configuration options in update.sh script (#1485864)
+- Fix so TEXT key will remove all HEADER keys in IMAP SEARCH (#1488208)
+- Fix handling contact photo url with https:// prefix (#1488202)
+- Fix possible infinite redirect on attachment preview (#1488199)
+- Improved clickjacking protection for browsers which don't support X-Frame-Options headers
+- Fixed bug where similiar folder names were highlighted wrong (#1487860)
+- Fixed bug in handling link with '!' character in it (#1488195)
+- Fixed bug where session ID's length was limited to 40 characters (#1488196)
+- TinyMCE security issue: removed moxieplayer (embedding flv and mp4 is not supported anymore)
 
-RELEASE 0.6-RC
---------------
-- jQuery 1.6.3
-- Fallback to mail_domain in LDAP variable replacements; added 'host' to 'user_create' hook arguments (#1488024)
-- Fixed wrong vCard type parameter mobile (#1488067)
-- Fixed vCard WORKFAX issue (#1488046)
-- Add vCard's Profile URL support (#1488062)
-- Fix imap_cache setting to values other than 'db' (#1488060)
-- Fix handling of attachments inside message/rfc822 parts (#1488026)
-- Make list of mimetypes that open in preview window configurable (#1487625)
-- Added plugin hook 'message_part_get' for attachment downloads
-- Fixed selecting identity on reply/forward (#1487981)
-- Fix image type check for contact photo uploads
-
-RELEASE 0.6-beta
+RELEASE 0.7-beta
 ----------------
-- Added unique connection identifier to IMAP debug messages
-- Add option to hide selected LDAP addressbook on the list
-- Add client-side checking of uploaded files size
-- Add newlines between organization, department, jobtitle (#1488028)
-- Recalculate date when replying to a message and localize the cite header (#1487675)
-- Fix handling of email addresses with quoted local part (#1487939)
-- Fix EOL character in vCard exports (#1487873)
-- Added optional "multithreading" autocomplete feature
-- Plugin API: Added 'config_get' hook
-- Fixed new_user_identity plugin to work with updated rcube_ldap class (#1487994)
-- Plugin API: added folder_delete and folder_rename hooks
-- Added possibility to undo last contact delete operation
-- Fix sorting of contact groups after group create (#1487747)
-- Add optional textual upload progress indicator (#1486039)
-- Fix parsing URLs containing commas (#1487970)
-- Added vertical splitter for books/groups list in addressbook (#1487923)
-- Improved namespace roots handling in folder manager
-- Added searching in all addressbook sources
-- Added addressbook source selection in contacts import
-- Implement LDAPv3 Virtual List View (VLV) for paged results listing
-- Use 'address_template' config option when adding a new address block (#1487944)
-- Added addressbook advanced search
-- Add popup with basic fields selection for addressbook search
-- Case-insensitive matching in autocompletion (#1487933)
-- Added option to force spellchecking before sending a message (#1485458)
-- Fix handling of "<" character in contact data, search fields and folder names (#1487864)
-- Fix saving "<" character in identity name and organization fields (#1487864)
-- Added option to specify to which address book add new contacts
-- Added plugin hook for keep-alive requests
-- Store user preferences in session when write-master is not available and session is stored in memcache, write them later
-- Improve performence of folder manager operations
-- Fix default_port option handling in Installer when config.inc.php file exists (#1487925)
-- Removed option focus_on_new_message, added newmail_notifier plugin
-- Added general rcube_cache class with Memcache and APC support
-- Improved caching performance by skipping writes of unchanged data
-- Option enable_caching replaced by imap_cache and messages_cache options
-- Fix WORKFAX saving in address book (#1487910)
-- Add forward-as-attachment feature
-- jQuery-1.6.2 (#1487913, #1487144)
-- Improve display name composition when saving contacts (#1487143)
-- Fix problems with subfolders of INBOX folder on some IMAP servers (#1487725)
-- Fix handling of folders that doesn't belong to any namespace (#1487637)
-- Enable multiselection for attachments uploading in capable browsers (#1485969)
-- Add possibility to change HTML editor configuration by skin
-- Fix a bug where selecting too many contacts would produce too large URI request (#1487892)
-- Improve performance by including files with absolute path (#1487849)
-- Move folder name truncation to client/skin (#1485412)
-- Added plugin hook for request token creation
-- Replace LDAP vars in group queries (#1487837)
-- Fix vcard folding with uncode characters (#1487868)
-- Keep all submitted data if contact form validation fails (#1487865)
-- Handle uncode strings in rcube_addressbook::normalize_string() (#1487866)
-- Fix handling of debug_level=4 in ajax requests (#1487831)
-- Enable TinyMCE's contextmenu (#1487014)
-- Allow multiple concurrent compose sessions
-- New config option for custom logo
-- Allow skins to define/override texts with <roundcube:label />
-- Add simple ACL rights/namespace handling in folder manager
-- Force IE to send referers (#1487806)
-- Better display of vcard import results (#1485457)
-- Improved vcard import
-- Interactive update script with improved DB schema check
-- Fix problem with contactgroupmembers table creation on MySQL 4.x, add index on contact_id column
-- Add LDAP SASL bind and proxy authentication (#1486692)
-- Replying to a sent message puts the old recipient as the new recipient (#1487074)
-- Fulltext search over (almost) all data for contacts
-- Extend address book with rich contact information
+- Fix handling of HTML form elements in messages (#1485137)
+- Fix regression in setting recipient to self when replying to a Sent message (#1487074)
+- Fix listing of folders in hidden namespaces (#1486796)
+- Don't consider \Noselect flag when building folders tree (#1488004)
+- Fix sorting autocomplete results (#1488084)
+- Add option to set session name (#1486433)
+- Add option to skip alternative email addresses in autocompletion
+- Fix inconsistent behaviour of Compose button in Drafts folder, add Edit button for drafts
+- Fix problem with parsing HTML message body with non-unicode characters (#1487813)
+- Add option to define matching method for addressbook search (#1486564, #1487907)
+- Make email recipients separator configurable
+- Fix so folders with \Noinferiors attribute aren't listed in parent selector
+- Fix handling of curly brackets in URLs (#1488168)
+- Fix handling of dates (birthday/anniversary) in contact data (#1488147)
+- Fix error on opening searched LDAP contact (#1488144)
+- Fix redundant line break in flowed format (#1488146)
+- Fix IDN address validation issue (#1488137)
+- Fix JS error when dst_active checkbox doesn't exist (#1488133)
+- Autocomplete LDAP records when adding contacts from mail (#1488073)
+- Plugin API: added 'ready' hook (#1488063)
+- Ignore DSN request when it isn't supported by SMTP server (#1487800)
+- Make sure LDAP name fields aren't arrays (#1488108)
+- Fixed imap test to non-default port when using ssl (#1488118)
+- Force all files to be overwritten when updating (#1488117)
+- Fix issue where it wasn't possible to change list view mode in folder manager for INBOX (#1488107)
+- Fix namespace handling in special folders settings (#1488112)
+- Disable time limit for CLI scripts (#1488109)
+- Fix misleading display when chaning editor type (#1488104)
+- Add loading indicator on contact delete
+- Fix bug where after delete message rows can be added to the list of another folder (#1487752)
+- Add notice on autocompletion that not all records were displayed
+- Add option 'searchonly' for LDAP address books
+- Add Priority filter to the messages list
+- Cache synchronization using QRESYNC/CONDSTORE
+- Trigger 'new_messages' hook for all checked folders (#1488083)
+- Make date/time format user configurable; drop 'date_today' config option
+- Fix setting title for truncated subject in IE (#1487128)
+- Fix displaying multipart/alternative messages with only one part (#1487938)
+- Rewritten messages caching:
+  Indexes are stored in a separate table, so there's no need to store all messages in a folder
+  Added threads data caching
+  Flags are stored separately, so flag change doesn't cause DELETE+INSERT, just UPDATE
+- Improved FETCH response handling
+- Improvements in response tokenization method
+- Use 'From' and 'To' labels instead of 'Sender' and 'Recipient'
+- Fix username case-insensitivity issue in MySQL (#1488021)
+- Addressbook Saved Searches
+- Added spellchecker exceptions dictionary (shared or per-user)
+- Added possibility to ignore words containing caps, numbers, symbols (spellcheck_ignore_* options)
+- Added 'priority' column on messages list (#1486782)
+- Localize forwarded message header (#1488058)
+
diff --git a/INSTALL b/INSTALL
index 9b07b2bcba5a3b21b497854d71275efb7d9a44f9..3fc6f5dfef03db456be5b70ead9a7105a6dbacd9 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -9,7 +9,7 @@ wiki page at http://trac.roundcube.net/wiki
 REQUIREMENTS
 ============
 
-* The Apache or Lighttpd Webserver
+* The Apache, Lighttpd, Cherokee or Hiawatha web server
 * .htaccess support allowing overrides for DirectoryIndex
 * PHP Version 5.2.1 or greater including
    - PCRE, DOM, JSON, XML, Session, Sockets (required)
diff --git a/INSTALL.orig b/INSTALL.orig
new file mode 100644 (file)
index 0000000..493baf7
--- /dev/null
@@ -0,0 +1,233 @@
+INTRODUCTION
+============
+
+This file describes the basic steps to install Roundcube Webmail on your
+web server. For additional information, please also consult the project's
+wiki page at http://trac.roundcube.net/wiki
+
+
+REQUIREMENTS
+============
+
+* The Apache, Lighttpd, Cherokee or Hiawatha web server
+* .htaccess support allowing overrides for DirectoryIndex
+* PHP Version 5.2.1 or greater including
+   - PCRE, DOM, JSON, XML, Session, Sockets (required)
+   - libiconv (recommended)
+   - mbstring, fileinfo, mcrypt (optional)
+* PEAR packages distributed with Roundcube or external:
+   - MDB2 2.5.0 or newer
+   - Mail_Mime 1.8.1 or newer
+   - Net_SMTP 1.4.2 or newer
+   - Net_IDNA2 0.1.1 or newer
+   - Auth_SASL 1.0.3 or newer
+* php.ini options (see .htaccess file):
+   - error_reporting E_ALL & ~E_NOTICE (or lower)
+   - memory_limit > 16MB (increase as suitable to support large attachments)
+   - file_uploads enabled (for attachment upload features)
+   - session.auto_start disabled
+   - zend.ze1_compatibility_mode disabled
+   - suhosin.session.encrypt disabled
+   - mbstring.func_overload disabled
+   - magic_quotes_runtime disabled
+* PHP compiled with OpenSSL to connect to IMAPS and to use the spell checker
+* A MySQL (4.0.8 or newer), PostgreSQL, MSSQL database engine
+  or the SQLite extension for PHP
+* One of the above databases with permission to create tables
+* An SMTP server (recommended) or PHP configured for mail delivery
+
+
+INSTALLATION
+============
+
+1. Decompress and put this folder somewhere inside your document root
+2. Make sure that the following directories (and the files within)
+   are writable by the webserver
+   - /temp
+   - /logs
+3. Create a new database and a database user for Roundcube (see DATABASE SETUP)
+4. Point your browser to http://url-to-roundcube/installer/
+5. Follow the instructions of the install script (or see MANUAL CONFIGURATION)
+6. After creating and testing the configuration, remove the installer directory
+7. Done!
+
+
+CONFIGURATION HINTS
+===================
+
+Roundcube writes internal errors to the 'errors' log file located in the logs
+directory which can be configured in config/main.inc.php. If you want ordinary
+PHP errors to be logged there as well, enable the 'php_value error_log' line
+in the .htaccess file and set the path to the log file accordingly.
+
+By default the session_path settings of PHP are not modified by Roundcube.
+However if you want to limit the session cookies to the directory where
+Roundcube resides you can uncomment and configure the according line
+in the .htaccess file.
+
+
+DATABASE SETUP
+==============
+
+Note: Database for Roundcube must use UTF-8 character set.
+
+* MySQL
+-------
+Setting up the mysql database can be done by creating an empty database,
+importing the table layout and granting the proper permissions to the
+roundcube user. Here is an example of that procedure:
+
+# mysql
+> CREATE DATABASE roundcubemail /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+> GRANT ALL PRIVILEGES ON roundcubemail.* TO roundcube@localhost
+    IDENTIFIED BY 'password';
+> quit
+
+# mysql roundcubemail < SQL/mysql.initial.sql
+
+Note 1: '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
+--------
+You need sqlite 2 (preferably 2.8) to setup the sqlite db 
+(sqlite 3.x also doesn't work at the moment). Here is
+an example how you can setup the sqlite.db for roundcube:
+
+# sqlite -init SQL/sqlite.initial.sql sqlite.db
+Loading resources from SQL/sqlite.initial.sql
+SQLite version 2.8.16
+Enter ".help" for instructions
+sqlite> .exit
+# chmod o+rw sqlite.db
+
+Make sure your configuration points to the sqlite.db file and that the
+webserver can write to the file and the directory containing the file.
+
+
+* PostgreSQL
+------------
+To use Roundcube with PostgreSQL support you have to follow these
+simple steps, which have to be done as the postgres system user (or
+which ever is the database superuser):
+
+$ createuser roundcube
+$ createdb -O roundcube -E UNICODE roundcubemail
+$ psql roundcubemail
+
+roundcubemail =# ALTER USER roundcube WITH PASSWORD 'the_new_password';
+roundcubemail =# \c - roundcube
+roundcubemail => \i SQL/postgres.initial.sql
+
+All this has been tested with PostgreSQL 8.x and 7.4.x. Older
+versions don't have a -O option for the createdb, so if you are
+using that version you'll have to change ownership of the DB later.
+
+
+Database cleaning
+-----------------
+Do keep your database slick and clean we recommend to periodically execute
+bin/cleandb.sh which finally removes all records that are marked as deleted.
+Best solution is to install a cronjob running this script daily.
+
+
+
+MANUAL CONFIGURATION
+====================
+
+First of all, rename the files config/*.inc.php.dist to config/*.inc.php.
+You can then change these files according to your environment and your needs.
+Details about the config parameters can be found in the config files.
+See http://trac.roundcube.net/wiki/Howto_Install for even more guidance.
+
+You can also modify the default .htaccess file. This is necessary to
+increase the allowed size of file attachments, for example:
+       php_value       upload_max_filesize     2M
+
+
+UPGRADING
+=========
+
+If you already have a previous version of Roundcube installed,
+please refer to the instructions in UPGRADING guide.
+
+
+OPTIMISING
+==========
+
+There are two forms of optimisation here, compression and caching, both aimed
+at increasing an end user's experience using Roundcube Webmail. Compression
+allows the static web pages to be delivered with less bandwidth. The index.php
+of Roundcube Webmail already enables compression on its output. The settings
+below allow compression to occur for all static files. Caching sets HTTP 
+response headers that enable a user's web client to understand what is static
+and how to cache it.
+
+The caching directives used are:
+ * Etags - sets at tag so the client can request is the page has changed
+ * Cache-control - defines the age of the page and that the page is 'public'
+   This enables clients to cache javascript files that don't have private 
+   information between sessions even if using HTTPS. It also allows proxies
+   to share the same cached page between users.
+ * Expires - provides another hint to increase the lifetime of static pages.
+
+For more information refer to RFC 2616.
+
+Side effects:
+-------------
+These directives are designed for production use. If you are using this in
+a development environment you may get horribly confused if your webclient
+is caching stuff that you changed on the server. Disabling the expires 
+parts below should save you some grief.
+
+If you are changing the skins, it is recommended that you copy content to 
+a different directory apart from 'default'.
+
+Apache:
+-------
+To enable these features in apache the following modules need to be enabled:
+ * mod_deflate
+ * mod_expires
+ * mod_headers
+
+The optimisation is already included in the .htaccess file in the top 
+directory of your installation.
+
+If you are using Apache version 2.2.9 and later, in the .htaccess file
+change the 'append' word to 'merge' for a more correct response. Keeping
+as 'append' shouldn't cause any problems though changing to merge will 
+eliminate the possibility of duplicate 'public' headers in Cache-control.
+
+Lighttpd:
+---------
+With Lightty the addition of Expire: tags by mod_expire is incompatible with
+the addition of "Cache-control: public". Using Cache-control 'public' is 
+used below as it is assumed to give a better caching result.
+
+Enable modules in server.modules:
+    "mod_setenv"
+    "mod_compress"
+
+Mod_compress is a server side cache of compressed files to improve its performance.
+
+$HTTP["host"] == "www.example.com" {
+
+    static-file.etags = "enable"
+    # http://redmine.lighttpd.net/projects/lighttpd/wiki/Etag.use-mtimeDetails
+    etag.use-mtime = "enable"
+
+    # http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModSetEnv
+    $HTTP["url"] =~ "^/roundcubemail/(plugins|skins|program)" {
+        setenv.add-response-header  = ( "Cache-Control" => "public, max-age=2592000")
+    }
+
+    # http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ModCompress
+    # set compress.cache-dir to somewhere outside the docroot.
+    compress.cache-dir   = var.statedir + "/cache/compress"
+
+    compress.filetype = ("text/plain", "text/html", "text/javascript", "text/css", "text/xml", "image/gif", "image/png")
+}
+
+
index 4aa6fc9f7516112bc5fb5e08184ff786321276cf..c14114100993d3d0aec6516da4a7528cbf82f777 100644 (file)
@@ -7,6 +7,33 @@ CREATE TABLE [dbo].[cache] (
 ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
 GO\r
 \r
+CREATE TABLE [dbo].[cache_index] (\r
+       [user_id] [int] NOT NULL ,\r
+       [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [changed] [datetime] NOT NULL ,\r
+       [valid] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [data] [text] COLLATE Latin1_General_CI_AI NOT NULL \r
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
+GO\r
+\r
+CREATE TABLE [dbo].[cache_thread] (\r
+       [user_id] [int] NOT NULL ,\r
+       [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [changed] [datetime] NOT NULL ,\r
+       [data] [text] COLLATE Latin1_General_CI_AI NOT NULL \r
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
+GO\r
+\r
+CREATE TABLE [dbo].[cache_messages] (\r
+       [user_id] [int] NOT NULL ,\r
+       [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [uid] [int] NOT NULL ,\r
+       [changed] [datetime] NOT NULL ,\r
+       [data] [text] COLLATE Latin1_General_CI_AI NOT NULL \r
+       [flags] [int](1) NOT NULL ,\r
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
+GO\r
+\r
 CREATE TABLE [dbo].[contacts] (\r
        [contact_id] [int] IDENTITY (1, 1) NOT NULL ,\r
        [user_id] [int] NOT NULL ,\r
@@ -53,27 +80,8 @@ CREATE TABLE [dbo].[identities] (
 ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
 GO\r
 \r
-CREATE TABLE [dbo].[messages] (\r
-       [message_id] [int] IDENTITY (1, 1) NOT NULL ,\r
-       [user_id] [int] NOT NULL ,\r
-       [del] [tinyint] NOT NULL ,\r
-       [cache_key] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,\r
-       [created] [datetime] NOT NULL ,\r
-       [idx] [int] NOT NULL ,\r
-       [uid] [int] NOT NULL ,\r
-       [subject] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL ,\r
-       [from] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL ,\r
-       [to] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL ,\r
-       [cc] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL ,\r
-       [date] [datetime] NOT NULL ,\r
-       [size] [int] NOT NULL ,\r
-       [headers] [text] COLLATE Latin1_General_CI_AI NOT NULL ,\r
-       [structure] [text] COLLATE Latin1_General_CI_AI NULL \r
-) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
-GO\r
-\r
 CREATE TABLE [dbo].[session] (\r
-       [sess_id] [varchar] (32) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [sess_id] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,\r
        [created] [datetime] NOT NULL ,\r
        [changed] [datetime] NULL ,\r
        [ip] [varchar] (40) COLLATE Latin1_General_CI_AI NOT NULL ,\r
@@ -93,6 +101,22 @@ CREATE TABLE [dbo].[users] (
 ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
 GO\r
 \r
+CREATE TABLE [dbo].[dictionary] (\r
+       [user_id] [int] ,\r
+       [language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [data] [text] COLLATE Latin1_General_CI_AI NOT NULL \r
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
+GO\r
+\r
+CREATE TABLE [dbo].[searches] (\r
+       [search_id] [int] IDENTITY (1, 1) NOT NULL ,\r
+       [user_id] [int] NOT NULL ,\r
+       [type] [tinyint] NOT NULL ,\r
+       [name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [data] [text] COLLATE Latin1_General_CI_AI NOT NULL \r
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
+GO\r
+\r
 ALTER TABLE [dbo].[cache] WITH NOCHECK ADD \r
         PRIMARY KEY  CLUSTERED \r
        (\r
@@ -100,6 +124,27 @@ ALTER TABLE [dbo].[cache] WITH NOCHECK ADD
        )  ON [PRIMARY] \r
 GO\r
 \r
+ALTER TABLE [dbo].[cache_index] WITH NOCHECK ADD \r
+        PRIMARY KEY CLUSTERED \r
+       (\r
+               [user_id],[mailbox]\r
+       ) ON [PRIMARY] \r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_thread] WITH NOCHECK ADD \r
+        PRIMARY KEY CLUSTERED \r
+       (\r
+               [user_id],[mailbox]\r
+       ) ON [PRIMARY] \r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_messages] WITH NOCHECK ADD \r
+        PRIMARY KEY CLUSTERED \r
+       (\r
+               [user_id],[mailbox],[uid]\r
+       ) ON [PRIMARY] \r
+GO\r
+\r
 ALTER TABLE [dbo].[contacts] WITH NOCHECK ADD \r
        CONSTRAINT [PK_contacts_contact_id] PRIMARY KEY  CLUSTERED \r
        (\r
@@ -128,13 +173,6 @@ ALTER TABLE [dbo].[identities] WITH NOCHECK ADD
        )  ON [PRIMARY] \r
 GO\r
 \r
-ALTER TABLE [dbo].[messages] WITH NOCHECK ADD \r
-        PRIMARY KEY  CLUSTERED \r
-       (\r
-               [message_id]\r
-       )  ON [PRIMARY] \r
-GO\r
-\r
 ALTER TABLE [dbo].[session] WITH NOCHECK ADD \r
        CONSTRAINT [PK_session_sess_id] PRIMARY KEY  CLUSTERED \r
        (\r
@@ -149,6 +187,13 @@ ALTER TABLE [dbo].[users] WITH NOCHECK ADD
        )  ON [PRIMARY] \r
 GO\r
 \r
+ALTER TABLE [dbo].[searches] WITH NOCHECK ADD \r
+       CONSTRAINT [PK_searches_search_id] PRIMARY KEY CLUSTERED \r
+       (\r
+               [search_id]\r
+       ) ON [PRIMARY] \r
+GO\r
+\r
 ALTER TABLE [dbo].[cache] ADD \r
        CONSTRAINT [DF_cache_user_id] DEFAULT ('0') FOR [user_id],\r
        CONSTRAINT [DF_cache_cache_key] DEFAULT ('') FOR [cache_key],\r
@@ -164,6 +209,29 @@ GO
 CREATE  INDEX [IX_cache_created] ON [dbo].[cache]([created]) ON [PRIMARY]\r
 GO\r
 \r
+ALTER TABLE [dbo].[cache_index] ADD \r
+       CONSTRAINT [DF_cache_index_changed] DEFAULT (getdate()) FOR [changed],\r
+       CONSTRAINT [DF_cache_index_valid] DEFAULT ('0') FOR [valid]\r
+GO\r
+\r
+CREATE  INDEX [IX_cache_index_user_id] ON [dbo].[cache_index]([user_id]) ON [PRIMARY]\r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_thread] ADD \r
+       CONSTRAINT [DF_cache_thread_changed] DEFAULT (getdate()) FOR [changed]\r
+GO\r
+\r
+CREATE  INDEX [IX_cache_thread_user_id] ON [dbo].[cache_thread]([user_id]) ON [PRIMARY]\r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_messages] ADD \r
+       CONSTRAINT [DF_cache_messages_changed] DEFAULT (getdate()) FOR [changed],\r
+       CONSTRAINT [DF_cache_messages_flags] DEFAULT (0) FOR [flags],\r
+GO\r
+\r
+CREATE  INDEX [IX_cache_messages_user_id] ON [dbo].[cache_messages]([user_id]) ON [PRIMARY]\r
+GO\r
+\r
 ALTER TABLE [dbo].[contacts] ADD \r
        CONSTRAINT [DF_contacts_user_id] DEFAULT (0) FOR [user_id],\r
        CONSTRAINT [DF_contacts_changed] DEFAULT (getdate()) FOR [changed],\r
@@ -215,33 +283,6 @@ GO
 CREATE  INDEX [IX_identities_user_id] ON [dbo].[identities]([user_id]) ON [PRIMARY]\r
 GO\r
 \r
-ALTER TABLE [dbo].[messages] ADD \r
-       CONSTRAINT [DF_messages_user_id] DEFAULT (0) FOR [user_id],\r
-       CONSTRAINT [DF_messages_del] DEFAULT (0) FOR [del],\r
-       CONSTRAINT [DF_messages_cache_key] DEFAULT ('') FOR [cache_key],\r
-       CONSTRAINT [DF_messages_created] DEFAULT (getdate()) FOR [created],\r
-       CONSTRAINT [DF_messages_idx] DEFAULT (0) FOR [idx],\r
-       CONSTRAINT [DF_messages_uid] DEFAULT (0) FOR [uid],\r
-       CONSTRAINT [DF_messages_subject] DEFAULT ('') FOR [subject],\r
-       CONSTRAINT [DF_messages_from] DEFAULT ('') FOR [from],\r
-       CONSTRAINT [DF_messages_to] DEFAULT ('') FOR [to],\r
-       CONSTRAINT [DF_messages_cc] DEFAULT ('') FOR [cc],\r
-       CONSTRAINT [DF_messages_date] DEFAULT (getdate()) FOR [date],\r
-       CONSTRAINT [DF_messages_size] DEFAULT (0) FOR [size]\r
-GO\r
-\r
-CREATE  INDEX [IX_messages_user_id] ON [dbo].[messages]([user_id]) ON [PRIMARY]\r
-GO\r
-\r
-CREATE  INDEX [IX_messages_cache_key] ON [dbo].[messages]([cache_key]) ON [PRIMARY]\r
-GO\r
-\r
-CREATE  INDEX [IX_messages_uid] ON [dbo].[messages]([uid]) ON [PRIMARY]\r
-GO\r
-\r
-CREATE  INDEX [IX_messages_created] ON [dbo].[messages]([created]) ON [PRIMARY]\r
-GO\r
-\r
 ALTER TABLE [dbo].[session] ADD \r
        CONSTRAINT [DF_session_sess_id] DEFAULT ('') FOR [sess_id],\r
        CONSTRAINT [DF_session_created] DEFAULT (getdate()) FOR [created],\r
@@ -264,6 +305,17 @@ GO
 CREATE  INDEX [IX_users_alias] ON [dbo].[users]([alias]) ON [PRIMARY]\r
 GO\r
 \r
+CREATE  UNIQUE INDEX [IX_dictionary_user_language] ON [dbo].[dictionary]([user_id],[language]) ON [PRIMARY]\r
+GO\r
+\r
+ALTER TABLE [dbo].[searches] ADD \r
+       CONSTRAINT [DF_searches_user] DEFAULT (0) FOR [user_id],\r
+       CONSTRAINT [DF_searches_type] DEFAULT (0) FOR [type],\r
+GO\r
+\r
+CREATE UNIQUE INDEX [IX_searches_user_type_name] ON [dbo].[searches]([user_id],[type],[name]) ON [PRIMARY]\r
+GO\r
+\r
 ALTER TABLE [dbo].[identities] ADD CONSTRAINT [FK_identities_user_id] \r
     FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])\r
     ON DELETE CASCADE ON UPDATE CASCADE\r
@@ -284,7 +336,17 @@ ALTER TABLE [dbo].[cache] ADD CONSTRAINT [FK_cache_user_id]
     ON DELETE CASCADE ON UPDATE CASCADE\r
 GO\r
 \r
-ALTER TABLE [dbo].[messages] ADD CONSTRAINT [FK_messages_user_id]\r
+ALTER TABLE [dbo].[cache_index] ADD CONSTRAINT [FK_cache_index_user_id]\r
+    FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])\r
+    ON DELETE CASCADE ON UPDATE CASCADE\r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_thread] ADD CONSTRAINT [FK_cache_thread_user_id]\r
+    FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])\r
+    ON DELETE CASCADE ON UPDATE CASCADE\r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_messages] ADD CONSTRAINT [FK_cache_messages_user_id]\r
     FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])\r
     ON DELETE CASCADE ON UPDATE CASCADE\r
 GO\r
@@ -294,6 +356,11 @@ ALTER TABLE [dbo].[contactgroupmembers] ADD CONSTRAINT [FK_contactgroupmembers_c
     ON DELETE CASCADE ON UPDATE CASCADE\r
 GO\r
 \r
+ALTER TABLE [dbo].[searches] ADD CONSTRAINT [FK_searches_user_id]\r
+    FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])\r
+    ON DELETE CASCADE ON UPDATE CASCADE\r
+GO\r
+\r
 -- Use trigger instead of foreign key (#1487112)\r
 -- "Introducing FOREIGN KEY constraint ... may cause cycles or multiple cascade paths."\r
 CREATE TRIGGER [contact_delete_member] ON [dbo].[contacts]\r
index 606db60466f71e108e8e13c921b49511493a31c8..eee5ae560a5ce3bb3af34d043fe257788c48e466 100644 (file)
@@ -110,3 +110,137 @@ DELETE FROM [dbo].[messages]
 GO\r
 DELETE FROM [dbo].[cache]\r
 GO\r
+\r
+-- Updates from version 0.6\r
+\r
+CREATE TABLE [dbo].[dictionary] (\r
+    [user_id] [int] ,\r
+    [language] [varchar] (5) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+    [data] [text] COLLATE Latin1_General_CI_AI NOT NULL \r
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
+GO\r
+CREATE  UNIQUE INDEX [IX_dictionary_user_language] ON [dbo].[dictionary]([user_id],[language]) ON [PRIMARY]\r
+GO\r
+\r
+CREATE TABLE [dbo].[searches] (\r
+       [search_id] [int] IDENTITY (1, 1) NOT NULL ,\r
+       [user_id] [int] NOT NULL ,\r
+       [type] [tinyint] NOT NULL ,\r
+       [name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [data] [text] COLLATE Latin1_General_CI_AI NOT NULL \r
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
+GO\r
+\r
+ALTER TABLE [dbo].[searches] WITH NOCHECK ADD \r
+       CONSTRAINT [PK_searches_search_id] PRIMARY KEY CLUSTERED \r
+       (\r
+               [search_id]\r
+       ) ON [PRIMARY] \r
+GO\r
+\r
+ALTER TABLE [dbo].[searches] ADD \r
+       CONSTRAINT [DF_searches_user] DEFAULT (0) FOR [user_id],\r
+       CONSTRAINT [DF_searches_type] DEFAULT (0) FOR [type],\r
+GO\r
+\r
+CREATE UNIQUE INDEX [IX_searches_user_type_name] ON [dbo].[searches]([user_id],[type],[name]) ON [PRIMARY]\r
+GO\r
+\r
+ALTER TABLE [dbo].[searches] ADD CONSTRAINT [FK_searches_user_id]\r
+    FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])\r
+    ON DELETE CASCADE ON UPDATE CASCADE\r
+GO\r
+\r
+DROP TABLE [dbo].[messages]\r
+GO\r
+CREATE TABLE [dbo].[cache_index] (\r
+       [user_id] [int] NOT NULL ,\r
+       [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [changed] [datetime] NOT NULL ,\r
+       [valid] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [data] [text] COLLATE Latin1_General_CI_AI NOT NULL \r
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
+GO\r
+\r
+CREATE TABLE [dbo].[cache_thread] (\r
+       [user_id] [int] NOT NULL ,\r
+       [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [changed] [datetime] NOT NULL ,\r
+       [data] [text] COLLATE Latin1_General_CI_AI NOT NULL \r
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
+GO\r
+\r
+CREATE TABLE [dbo].[cache_messages] (\r
+       [user_id] [int] NOT NULL ,\r
+       [mailbox] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,\r
+       [uid] [int] NOT NULL ,\r
+       [changed] [datetime] NOT NULL ,\r
+       [data] [text] COLLATE Latin1_General_CI_AI NOT NULL \r
+       [flags] [int] NOT NULL ,\r
+) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]\r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_index] WITH NOCHECK ADD \r
+        PRIMARY KEY CLUSTERED \r
+       (\r
+               [user_id],[mailbox]\r
+       ) ON [PRIMARY] \r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_thread] WITH NOCHECK ADD \r
+        PRIMARY KEY CLUSTERED \r
+       (\r
+               [user_id],[mailbox]\r
+       ) ON [PRIMARY] \r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_messages] WITH NOCHECK ADD \r
+        PRIMARY KEY CLUSTERED \r
+       (\r
+               [user_id],[mailbox],[uid]\r
+       ) ON [PRIMARY] \r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_index] ADD \r
+       CONSTRAINT [DF_cache_index_changed] DEFAULT (getdate()) FOR [changed],\r
+       CONSTRAINT [DF_cache_index_valid] DEFAULT ('0') FOR [valid]\r
+GO\r
+\r
+CREATE  INDEX [IX_cache_index_user_id] ON [dbo].[cache_index]([user_id]) ON [PRIMARY]\r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_thread] ADD \r
+       CONSTRAINT [DF_cache_thread_changed] DEFAULT (getdate()) FOR [changed]\r
+GO\r
+\r
+CREATE  INDEX [IX_cache_thread_user_id] ON [dbo].[cache_thread]([user_id]) ON [PRIMARY]\r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_messages] ADD \r
+       CONSTRAINT [DF_cache_messages_changed] DEFAULT (getdate()) FOR [changed],\r
+       CONSTRAINT [DF_cache_messages_flags] DEFAULT (0) FOR [flags]\r
+GO\r
+\r
+CREATE  INDEX [IX_cache_messages_user_id] ON [dbo].[cache_messages]([user_id]) ON [PRIMARY]\r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_index] ADD CONSTRAINT [FK_cache_index_user_id]\r
+    FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])\r
+    ON DELETE CASCADE ON UPDATE CASCADE\r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_thread] ADD CONSTRAINT [FK_cache_thread_user_id]\r
+    FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])\r
+    ON DELETE CASCADE ON UPDATE CASCADE\r
+GO\r
+\r
+ALTER TABLE [dbo].[cache_messages] ADD CONSTRAINT [FK_cache_messages_user_id]\r
+    FOREIGN KEY ([user_id]) REFERENCES [dbo].[users] ([user_id])\r
+    ON DELETE CASCADE ON UPDATE CASCADE\r
+GO\r
+\r
+-- Updates from version 0.7-beta
+
+ALTER TABLE [dbo].[session] ALTER COLUMN [sess_id] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL
+GO
+
index 14bbb968acfcf2df80cd536a0869baa84d456801..94679f20288346ad6e950fbf08380df0e537f40e 100644 (file)
@@ -6,7 +6,7 @@
 -- Table structure for table `session`
 
 CREATE TABLE `session` (
- `sess_id` varchar(40) NOT NULL,
+ `sess_id` varchar(128) NOT NULL,
  `created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
  `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
  `ip` varchar(40) NOT NULL,
@@ -20,9 +20,9 @@ CREATE TABLE `session` (
 
 CREATE TABLE `users` (
  `user_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
- `username` varchar(128) NOT NULL,
+ `username` varchar(128) BINARY NOT NULL,
  `mail_host` varchar(128) NOT NULL,
- `alias` varchar(128) NOT NULL,
+ `alias` varchar(128) BINARY NOT NULL,
  `created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
  `last_login` datetime DEFAULT NULL,
  `language` varchar(5),
@@ -33,33 +33,6 @@ CREATE TABLE `users` (
 ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
 
 
--- Table structure for table `messages`
-
-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) /*!40101 CHARACTER SET ascii COLLATE ascii_general_ci */ NOT NULL,
- `created` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
- `idx` int(11) UNSIGNED NOT NULL DEFAULT '0',
- `uid` int(11) UNSIGNED NOT NULL DEFAULT '0',
- `subject` varchar(255) NOT NULL,
- `from` varchar(255) NOT NULL,
- `to` varchar(255) NOT NULL,
- `cc` varchar(255) NOT NULL,
- `date` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
- `size` int(11) UNSIGNED NOT NULL DEFAULT '0',
- `headers` text NOT NULL,
- `structure` text,
- PRIMARY KEY(`message_id`),
- CONSTRAINT `user_id_fk_messages` FOREIGN KEY (`user_id`)
-   REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
- INDEX `created_index` (`created`),
- INDEX `index_index` (`user_id`, `cache_key`, `idx`),
- UNIQUE `uniqueness` (`user_id`, `cache_key`, `uid`)
-) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
-
-
 -- Table structure for table `cache`
 
 CREATE TABLE `cache` (
@@ -76,6 +49,51 @@ CREATE TABLE `cache` (
 ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
 
 
+-- Table structure for table `cache_index`
+
+CREATE TABLE `cache_index` (
+ `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ `mailbox` varchar(255) BINARY NOT NULL,
+ `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
+ `valid` tinyint(1) NOT NULL DEFAULT '0',
+ `data` longtext NOT NULL,
+ CONSTRAINT `user_id_fk_cache_index` FOREIGN KEY (`user_id`)
+   REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ INDEX `changed_index` (`changed`),
+ PRIMARY KEY (`user_id`, `mailbox`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+
+
+-- Table structure for table `cache_thread`
+
+CREATE TABLE `cache_thread` (
+ `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ `mailbox` varchar(255) BINARY NOT NULL,
+ `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
+ `data` longtext NOT NULL,
+ CONSTRAINT `user_id_fk_cache_thread` FOREIGN KEY (`user_id`)
+   REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ INDEX `changed_index` (`changed`),
+ PRIMARY KEY (`user_id`, `mailbox`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+
+
+-- Table structure for table `cache_messages`
+
+CREATE TABLE `cache_messages` (
+ `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ `mailbox` varchar(255) BINARY NOT NULL,
+ `uid` int(11) UNSIGNED NOT NULL DEFAULT '0',
+ `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
+ `data` longtext NOT NULL,
+ `flags` int(11) NOT NULL DEFAULT '0',
+ CONSTRAINT `user_id_fk_cache_messages` FOREIGN KEY (`user_id`)
+   REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ INDEX `changed_index` (`changed`),
+ PRIMARY KEY (`user_id`, `mailbox`, `uid`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+
+
 -- Table structure for table `contacts`
 
 CREATE TABLE `contacts` (
@@ -144,4 +162,31 @@ CREATE TABLE `identities` (
 ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
 
 
+-- Table structure for table `dictionary`
+
+CREATE TABLE `dictionary` (
+  `user_id` int(10) UNSIGNED DEFAULT NULL,
+  `language` varchar(5) NOT NULL,
+  `data` longtext NOT NULL,
+  CONSTRAINT `user_id_fk_dictionary` FOREIGN KEY (`user_id`)
+    REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+  UNIQUE `uniqueness` (`user_id`, `language`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+
+
+-- Table structure for table `searches`
+
+CREATE TABLE `searches` (
+ `search_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ `type` int(3) NOT NULL DEFAULT '0',
+ `name` varchar(128) NOT NULL,
+ `data` text,
+ PRIMARY KEY(`search_id`),
+ CONSTRAINT `user_id_fk_searches` FOREIGN KEY (`user_id`)
+   REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ UNIQUE `uniqueness` (`user_id`, `type`, `name`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+
+
 /*!40014 SET FOREIGN_KEY_CHECKS=1 */;
index ed21bda2ecf191d626110d30b71842365dbaea00..07617316b3cf7b26f9315f83140938bd5bdc645f 100644 (file)
@@ -144,3 +144,71 @@ ALTER TABLE `contactgroupmembers` ADD INDEX `contactgroupmembers_contact_index`
 
 TRUNCATE TABLE `messages`;
 TRUNCATE TABLE `cache`;
+
+-- Updates from version 0.6
+
+ALTER TABLE `users` CHANGE `alias` `alias` varchar(128) BINARY NOT NULL;
+ALTER TABLE `users` CHANGE `username` `username` varchar(128) BINARY NOT NULL;
+
+CREATE TABLE `dictionary` (
+  `user_id` int(10) UNSIGNED DEFAULT NULL,
+  `language` varchar(5) NOT NULL,
+  `data` longtext NOT NULL,
+  CONSTRAINT `user_id_fk_dictionary` FOREIGN KEY (`user_id`)
+    REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+  UNIQUE `uniqueness` (`user_id`, `language`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+
+CREATE TABLE `searches` (
+  `search_id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+  `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+  `type` int(3) NOT NULL DEFAULT '0',
+  `name` varchar(128) NOT NULL,
+  `data` text,
+  PRIMARY KEY(`search_id`),
+  CONSTRAINT `user_id_fk_searches` FOREIGN KEY (`user_id`)
+    REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+  UNIQUE `uniqueness` (`user_id`, `type`, `name`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+
+DROP TABLE `messages`;
+
+CREATE TABLE `cache_index` (
+ `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ `mailbox` varchar(255) BINARY NOT NULL,
+ `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
+ `valid` tinyint(1) NOT NULL DEFAULT '0',
+ `data` longtext NOT NULL,
+ CONSTRAINT `user_id_fk_cache_index` FOREIGN KEY (`user_id`)
+   REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ INDEX `changed_index` (`changed`),
+ PRIMARY KEY (`user_id`, `mailbox`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+
+CREATE TABLE `cache_thread` (
+ `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ `mailbox` varchar(255) BINARY NOT NULL,
+ `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
+ `data` longtext NOT NULL,
+ CONSTRAINT `user_id_fk_cache_thread` FOREIGN KEY (`user_id`)
+   REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ INDEX `changed_index` (`changed`),
+ PRIMARY KEY (`user_id`, `mailbox`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+
+CREATE TABLE `cache_messages` (
+ `user_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
+ `mailbox` varchar(255) BINARY NOT NULL,
+ `uid` int(11) UNSIGNED NOT NULL DEFAULT '0',
+ `changed` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
+ `data` longtext NOT NULL,
+ `flags` int(11) NOT NULL DEFAULT '0',
+ CONSTRAINT `user_id_fk_cache_messages` FOREIGN KEY (`user_id`)
+   REFERENCES `users`(`user_id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ INDEX `changed_index` (`changed`),
+ PRIMARY KEY (`user_id`, `mailbox`, `uid`)
+) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
+
+-- Updates from version 0.7-beta
+
+ALTER TABLE `session` CHANGE `sess_id` `sess_id` varchar(128) NOT NULL;
index 5350e791f71dc7904cc8ddac9fa20032999fae70..3710dac4a7f70c7bf2c40827bdf1b01b5dba54d0 100644 (file)
@@ -37,7 +37,7 @@ CREATE INDEX users_alias_id_idx ON users (alias);
 --
 
 CREATE TABLE "session" (
-    sess_id varchar(40) DEFAULT '' PRIMARY KEY,
+    sess_id varchar(128) DEFAULT '' PRIMARY KEY,
     created timestamp with time zone DEFAULT now() NOT NULL,
     changed timestamp with time zone DEFAULT now() NOT NULL,
     ip varchar(41) NOT NULL,
@@ -67,7 +67,7 @@ CREATE SEQUENCE identity_ids
 CREATE TABLE identities (
     identity_id integer DEFAULT nextval('identity_ids'::text) PRIMARY KEY,
     user_id integer NOT NULL
-       REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+        REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
     changed timestamp with time zone DEFAULT now() NOT NULL,
     del smallint DEFAULT 0 NOT NULL,
     standard smallint DEFAULT 0 NOT NULL,
@@ -178,7 +178,7 @@ CREATE SEQUENCE cache_ids
 CREATE TABLE "cache" (
     cache_id integer DEFAULT nextval('cache_ids'::text) PRIMARY KEY,
     user_id integer NOT NULL
-       REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+       REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
     cache_key varchar(128) DEFAULT '' NOT NULL,
     created timestamp with time zone DEFAULT now() NOT NULL,
     data text NOT NULL
@@ -188,40 +188,91 @@ CREATE INDEX cache_user_id_idx ON "cache" (user_id, cache_key);
 CREATE INDEX cache_created_idx ON "cache" (created);
 
 --
--- Sequence "message_ids"
--- Name: message_ids; Type: SEQUENCE; Schema: public; Owner: postgres
+-- Table "cache_index"
+-- Name: cache_index; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE cache_index (
+    user_id integer NOT NULL
+       REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+    mailbox varchar(255) NOT NULL,
+    changed timestamp with time zone DEFAULT now() NOT NULL,
+    valid smallint NOT NULL DEFAULT 0,
+    data text NOT NULL,
+    PRIMARY KEY (user_id, mailbox)
+);
+
+CREATE INDEX cache_index_changed_idx ON cache_index (changed);
+
+--
+-- Table "cache_thread"
+-- Name: cache_thread; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE cache_thread (
+    user_id integer NOT NULL
+       REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+    mailbox varchar(255) NOT NULL,
+    changed timestamp with time zone DEFAULT now() NOT NULL,
+    data text NOT NULL,
+    PRIMARY KEY (user_id, mailbox)
+);
+
+CREATE INDEX cache_thread_changed_idx ON cache_thread (changed);
+
+--
+-- Table "cache_messages"
+-- Name: cache_messages; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE cache_messages (
+    user_id integer NOT NULL
+       REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+    mailbox varchar(255) NOT NULL,
+    uid integer NOT NULL,
+    changed timestamp with time zone DEFAULT now() NOT NULL,
+    data text NOT NULL,
+    flags integer NOT NULL DEFAULT 0,
+    PRIMARY KEY (user_id, mailbox, uid)
+);
+
+CREATE INDEX cache_messages_changed_idx ON cache_messages (changed);
+
+--
+-- Table "dictionary"
+-- Name: dictionary; Type: TABLE; Schema: public; Owner: postgres
+--
+
+CREATE TABLE dictionary (
+    user_id integer DEFAULT NULL
+       REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+   "language" varchar(5) NOT NULL,
+    data text NOT NULL,
+    CONSTRAINT dictionary_user_id_language_key UNIQUE (user_id, "language")
+);
+
+--
+-- Sequence "searches_ids"
+-- Name: searches_ids; Type: SEQUENCE; Schema: public; Owner: postgres
 --
 
-CREATE SEQUENCE message_ids
+CREATE SEQUENCE search_ids
     INCREMENT BY 1
     NO MAXVALUE
     NO MINVALUE
     CACHE 1;
 
 --
--- Table "messages"
--- Name: messages; Type: TABLE; Schema: public; Owner: postgres
+-- Table "searches"
+-- Name: searches; Type: TABLE; Schema: public; Owner: postgres
 --
 
-CREATE TABLE messages (
-    message_id integer DEFAULT nextval('message_ids'::text) PRIMARY KEY,
+CREATE TABLE searches (
+    search_id integer DEFAULT nextval('search_ids'::text) PRIMARY KEY,
     user_id integer NOT NULL
-       REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
-    del smallint DEFAULT 0 NOT NULL,
-    cache_key varchar(128) DEFAULT '' NOT NULL,
-    created timestamp with time zone DEFAULT now() NOT NULL,
-    idx integer DEFAULT 0 NOT NULL,
-    uid integer DEFAULT 0 NOT NULL,
-    subject varchar(128) DEFAULT '' NOT NULL,
-    "from" varchar(128) DEFAULT '' NOT NULL,
-    "to" varchar(128) DEFAULT '' NOT NULL,
-    cc varchar(128) DEFAULT '' NOT NULL,
-    date timestamp with time zone NOT NULL,
-    size integer DEFAULT 0 NOT NULL,
-    headers text NOT NULL,
-    structure text,
-    CONSTRAINT messages_user_id_key UNIQUE (user_id, cache_key, uid)
+        REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+    "type" smallint DEFAULT 0 NOT NULL,
+    name varchar(128) NOT NULL,
+    data text NOT NULL,
+    CONSTRAINT searches_user_id_key UNIQUE (user_id, "type", name)
 );
-
-CREATE INDEX messages_index_idx ON messages (user_id, cache_key, idx);
-CREATE INDEX messages_created_idx ON messages (created);
index 94513c53fef603c60718f8d3f4578d6a5c5686ec..c96669dc8ee1ccbc4dd30ad6904e0454b9cc3dbe 100644 (file)
@@ -100,3 +100,72 @@ CREATE INDEX contactgroupmembers_contact_id_idx ON contactgroupmembers (contact_
 
 TRUNCATE messages;
 TRUNCATE cache;
+
+-- Updates from version 0.6
+
+CREATE TABLE dictionary (
+    user_id integer DEFAULT NULL
+        REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+   "language" varchar(5) NOT NULL,
+    data text NOT NULL,
+    CONSTRAINT dictionary_user_id_language_key UNIQUE (user_id, "language")
+);
+
+CREATE SEQUENCE search_ids
+    INCREMENT BY 1
+    NO MAXVALUE
+    NO MINVALUE
+    CACHE 1;
+
+CREATE TABLE searches (
+    search_id integer DEFAULT nextval('search_ids'::text) PRIMARY KEY,
+    user_id integer NOT NULL
+        REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+    "type" smallint DEFAULT 0 NOT NULL,
+    name varchar(128) NOT NULL,
+    data text NOT NULL,
+    CONSTRAINT searches_user_id_key UNIQUE (user_id, "type", name)
+);
+
+DROP SEQUENCE messages_ids;
+DROP TABLE messages;
+
+CREATE TABLE cache_index (
+    user_id integer NOT NULL
+       REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+    mailbox varchar(255) NOT NULL,
+    changed timestamp with time zone DEFAULT now() NOT NULL,
+    valid smallint NOT NULL DEFAULT 0,
+    data text NOT NULL,
+    PRIMARY KEY (user_id, mailbox)
+);
+
+CREATE INDEX cache_index_changed_idx ON cache_index (changed);
+
+CREATE TABLE cache_thread (
+    user_id integer NOT NULL
+       REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+    mailbox varchar(255) NOT NULL,
+    changed timestamp with time zone DEFAULT now() NOT NULL,
+    data text NOT NULL,
+    PRIMARY KEY (user_id, mailbox)
+);
+
+CREATE INDEX cache_thread_changed_idx ON cache_thread (changed);
+
+CREATE TABLE cache_messages (
+    user_id integer NOT NULL
+       REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
+    mailbox varchar(255) NOT NULL,
+    uid integer NOT NULL,
+    changed timestamp with time zone DEFAULT now() NOT NULL,
+    data text NOT NULL,
+    flags integer NOT NULL DEFAULT 0,
+    PRIMARY KEY (user_id, mailbox, uid)
+);
+
+CREATE INDEX cache_messages_changed_idx ON cache_messages (changed);
+
+-- Updates from version 0.7-beta
+
+ALTER TABLE "session" ALTER sess_id TYPE varchar(128);
index d2885e9686d01b0e999d3d7d0d04a26da9faf1f8..8c8da5c0f6b0c9b906455689eccc2b73587e04b2 100644 (file)
@@ -1,7 +1,7 @@
 -- Roundcube Webmail initial database structure
 
 -- 
--- Table structure for table `cache`
+-- Table structure for table cache
 -- 
 
 CREATE TABLE cache (
@@ -9,7 +9,7 @@ CREATE TABLE cache (
   user_id integer NOT NULL default 0,
   cache_key varchar(128) NOT NULL default '',
   created datetime NOT NULL default '0000-00-00 00:00:00',
-  data longtext NOT NULL
+  data text NOT NULL
 );
 
 CREATE INDEX ix_cache_user_cache_key ON cache(user_id, cache_key);
@@ -110,7 +110,7 @@ CREATE INDEX ix_users_alias ON users(alias);
 -- 
 
 CREATE TABLE session (
-  sess_id varchar(40) NOT NULL PRIMARY KEY,
+  sess_id varchar(128) NOT NULL PRIMARY KEY,
   created datetime NOT NULL default '0000-00-00 00:00:00',
   changed datetime NOT NULL default '0000-00-00 00:00:00',
   ip varchar(40) NOT NULL default '',
@@ -121,28 +121,81 @@ CREATE INDEX ix_session_changed ON session (changed);
 
 -- --------------------------------------------------------
 
--- 
--- Table structure for table messages
--- 
+--
+-- Table structure for table dictionary
+--
 
-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 TABLE dictionary (
+    user_id integer DEFAULT NULL,
+   "language" varchar(5) NOT NULL,
+    data text NOT NULL
+);
+
+CREATE UNIQUE INDEX ix_dictionary_user_language ON dictionary (user_id, "language");
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table searches
+--
+
+CREATE TABLE searches (
+  search_id integer NOT NULL PRIMARY KEY,
+  user_id integer NOT NULL DEFAULT '0',
+  "type" smallint NOT NULL DEFAULT '0',
+  name varchar(128) NOT NULL,
+  data text NOT NULL
+);
+
+CREATE UNIQUE INDEX ix_searches_user_type_name (user_id, type, name);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table cache_index
+--
+
+CREATE TABLE cache_index (
+    user_id integer NOT NULL,
+    mailbox varchar(255) NOT NULL,
+    changed datetime NOT NULL default '0000-00-00 00:00:00',
+    valid smallint NOT NULL DEFAULT '0',
+    data text NOT NULL,
+    PRIMARY KEY (user_id, mailbox)
+);
+
+CREATE INDEX ix_cache_index_changed ON cache_index (changed);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table cache_thread
+--
+
+CREATE TABLE cache_thread (
+    user_id integer NOT NULL,
+    mailbox varchar(255) NOT NULL,
+    changed datetime NOT NULL default '0000-00-00 00:00:00',
+    data text NOT NULL,
+    PRIMARY KEY (user_id, mailbox)
+);
+
+CREATE INDEX ix_cache_thread_changed ON cache_thread (changed);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table cache_messages
+--
+
+CREATE TABLE cache_messages (
+    user_id integer NOT NULL,
+    mailbox varchar(255) NOT NULL,
+    uid integer NOT NULL,
+    changed datetime NOT NULL default '0000-00-00 00:00:00',
+    data text NOT NULL,
+    flags integer NOT NULL DEFAULT '0',
+    PRIMARY KEY (user_id, mailbox, uid)
 );
 
-CREATE UNIQUE INDEX ix_messages_user_cache_uid ON messages (user_id,cache_key,uid);
-CREATE INDEX ix_messages_index ON messages (user_id,cache_key,idx);
-CREATE INDEX ix_messages_created ON messages (created);
+CREATE INDEX ix_cache_messages_changed ON cache_messages (changed);
index 30c3ae90baac816dccf46f3decf104f941bab844..92f6da1423ac033cf97b4699af249ef6f7abc823 100644 (file)
@@ -223,6 +223,75 @@ INSERT INTO contacts (contact_id, user_id, changed, del, name, email, firstname,
 CREATE INDEX ix_contacts_user_id ON contacts(user_id, email);
 DROP TABLE contacts_tmp;
 
+
 DELETE FROM messages;
 DELETE FROM cache;
 CREATE INDEX ix_contactgroupmembers_contact_id ON contactgroupmembers (contact_id);
+
+-- Updates from version 0.6
+
+CREATE TABLE dictionary (
+    user_id integer DEFAULT NULL,
+   "language" varchar(5) NOT NULL,
+    data text NOT NULL
+);
+
+CREATE UNIQUE INDEX ix_dictionary_user_language ON dictionary (user_id, "language");
+
+CREATE TABLE searches (
+  search_id integer NOT NULL PRIMARY KEY,
+  user_id integer NOT NULL DEFAULT '0',
+  "type" smallint NOT NULL DEFAULT '0',
+  name varchar(128) NOT NULL,
+  data text NOT NULL
+);
+
+CREATE UNIQUE INDEX ix_searches_user_type_name (user_id, type, name);
+
+DROP TABLE messages;
+
+CREATE TABLE cache_index (
+    user_id integer NOT NULL,
+    mailbox varchar(255) NOT NULL,
+    changed datetime NOT NULL default '0000-00-00 00:00:00',
+    valid smallint NOT NULL DEFAULT '0',
+    data text NOT NULL,
+    PRIMARY KEY (user_id, mailbox)
+);
+
+CREATE INDEX ix_cache_index_changed ON cache_index (changed);
+
+CREATE TABLE cache_thread (
+    user_id integer NOT NULL,
+    mailbox varchar(255) NOT NULL,
+    changed datetime NOT NULL default '0000-00-00 00:00:00',
+    data text NOT NULL,
+    PRIMARY KEY (user_id, mailbox)
+);
+
+CREATE INDEX ix_cache_thread_changed ON cache_thread (changed);
+
+CREATE TABLE cache_messages (
+    user_id integer NOT NULL,
+    mailbox varchar(255) NOT NULL,
+    uid integer NOT NULL,
+    changed datetime NOT NULL default '0000-00-00 00:00:00',
+    data text NOT NULL,
+    flags integer NOT NULL DEFAULT '0',
+    PRIMARY KEY (user_id, mailbox, uid)
+);
+
+CREATE INDEX ix_cache_messages_changed ON cache_messages (changed);
+
+-- Updates from version 0.7-beta
+
+DROP TABLE session;
+CREATE TABLE session (
+  sess_id varchar(128) NOT NULL PRIMARY KEY,
+  created datetime NOT NULL default '0000-00-00 00:00:00',
+  changed datetime NOT NULL default '0000-00-00 00:00:00',
+  ip varchar(40) NOT NULL default '',
+  vars text NOT NULL
+);
+
+CREATE INDEX ix_session_changed ON session (changed);
index d552be61f8f7b7bf30d6a3b18ca4902bc6eb21f3..cbeffe96b5353814d5f5f37665ebd6c82bd7ba41 100755 (executable)
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: indexcontacts.sh 4623 2011-03-28 06:49:02Z alec $
+ $Id: indexcontacts.sh 5307 2011-10-05 09:28:25Z alec $
 
 */
 
 define('INSTALL_PATH', realpath(dirname(__FILE__) . '/..') . '/' );
 
 require_once INSTALL_PATH.'program/include/clisetup.php';
-
+ini_set('memory_limit', -1);
 
 // connect to DB
 $RCMAIL = rcmail::get_instance();
@@ -47,7 +47,7 @@ while ($sql_result && ($sql_arr = $db->fetch_assoc($sql_result))) {
         unset($row['words']);
         $contacts->update($row['ID'], $row);
     }
-    
+
     echo "done.\n";
 }
 
index 33652dcc720b6c22b15a8965a5a6c6de212c53b2..bcba57c7586422477d9959877ac7da4803e1d1ec 100755 (executable)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: installto.sh 4677 2011-04-20 13:10:45Z alec $
+ $Id: installto.sh 5311 2011-10-06 08:20:11Z thomasb $
 
 */
 
@@ -45,13 +45,13 @@ if (strtolower($input) == 'y') {
   $err = false;
   echo "Copying files to target location...";
   foreach (array('program','installer','bin','SQL','plugins','skins/default') as $dir) {
-    if (!system("rsync -avuC " . INSTALL_PATH . "$dir/* $target_dir/$dir/")) {
+    if (!system("rsync -avC " . INSTALL_PATH . "$dir/* $target_dir/$dir/")) {
       $err = true;
       break;
     }
   }
   foreach (array('index.php','.htaccess','config/main.inc.php.dist','config/db.inc.php.dist','CHANGELOG','README','UPGRADING') as $file) {
-    if (!system("rsync -avu " . INSTALL_PATH . "$file $target_dir/$file")) {
+    if (!system("rsync -av " . INSTALL_PATH . "$file $target_dir/$file")) {
       $err = true;
       break;
     }
index be5aad16d1d96bab1421b230c5654e7914012561..9cfd660bb1446900c2c2b4710afff138a4599f4e 100755 (executable)
@@ -1,10 +1,11 @@
 #!/bin/sh
 JS_DIR=`dirname "$0"`/../program/js
+JAR_DIR='/tmp'
 CLOSURE_COMPILER_URL='http://closure-compiler.googlecode.com/files/compiler-latest.zip'
 
 do_shrink() {
        rm -f "$2"
-       java -jar compiler.jar --compilation_level=SIMPLE_OPTIMIZATIONS --js="$1" --js_output_file="$2"
+       java -jar $JAR_DIR/compiler.jar --compilation_level=SIMPLE_OPTIMIZATIONS --js="$1" --js_output_file="$2"
 }
 
 if [ ! -d "$JS_DIR" ]; then
@@ -12,6 +13,10 @@ if [ ! -d "$JS_DIR" ]; then
        exit 1
 fi
 
+if [ ! -w "$JAR_DIR" ]; then
+       JAR_DIR=`dirname "$0"`
+fi
+
 if java -version >/dev/null 2>&1; then
        :
 else
@@ -19,16 +24,16 @@ else
        exit 1
 fi
 
-if [ ! -r "compiler.jar" ]; then
+if [ ! -r "$JAR_DIR/compiler.jar" ]; then
        if which wget >/dev/null 2>&1 && which unzip >/dev/null 2>&1; then
                wget "$CLOSURE_COMPILER_URL" -O "/tmp/$$.zip"
        elif which curl >/dev/null 2>&1 && which unzip >/dev/null 2>&1; then
                curl "$CLOSURE_COMPILER_URL" -o "/tmp/$$.zip"
        else
-               echo "Please download $CLOSURE_COMPILER_URL and extract compiler.jar to this directory."
+               echo "Please download $CLOSURE_COMPILER_URL and extract compiler.jar to $JAR_DIR/."
                exit 1
        fi
-       unzip "/tmp/$$.zip" "compiler.jar"
+       (cd $JAR_DIR && unzip "/tmp/$$.zip" "compiler.jar")
        rm -f "/tmp/$$.zip"
 fi
 
index 78cd96882d9f00019d8a91c68f7dad2e3e1e740c..c1464f91470bcef76ae7b11ddba0e2aa660ea502 100644 (file)
@@ -39,34 +39,24 @@ $rcmail_config['db_persistent'] = FALSE;
 
 // you can define specific table names used to store webmail data
 $rcmail_config['db_table_users'] = 'users';
-
 $rcmail_config['db_table_identities'] = 'identities';
-
 $rcmail_config['db_table_contacts'] = 'contacts';
-
 $rcmail_config['db_table_contactgroups'] = 'contactgroups';
-
 $rcmail_config['db_table_contactgroupmembers'] = 'contactgroupmembers';
-
 $rcmail_config['db_table_session'] = 'session';
-
 $rcmail_config['db_table_cache'] = 'cache';
-
-$rcmail_config['db_table_messages'] = 'messages';
+$rcmail_config['db_table_cache_index'] = 'cache_index';
+$rcmail_config['db_table_cache_thread'] = 'cache_thread';
+$rcmail_config['db_table_cache_messages'] = 'cache_messages';
 
 
 // you can define specific sequence names used in PostgreSQL
 $rcmail_config['db_sequence_users'] = 'user_ids';
-
 $rcmail_config['db_sequence_identities'] = 'identity_ids';
-
 $rcmail_config['db_sequence_contacts'] = 'contact_ids';
-
 $rcmail_config['db_sequence_contactgroups'] = 'contactgroups_ids';
-
 $rcmail_config['db_sequence_cache'] = 'cache_ids';
-
-$rcmail_config['db_sequence_messages'] = 'message_ids';
+$rcmail_config['db_sequence_searches'] = 'search_ids';
 
 
 // end db config file
index 824085c14ebb2d5302423ffaf3923e6570dcbad4..6957577b15009c362140e549cc7e50cf0e80de99 100644 (file)
@@ -5,7 +5,7 @@
  | Main configuration file                                               |
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2005-2010, The Roundcube Dev Team                       |
+ | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  +-----------------------------------------------------------------------+
@@ -222,6 +222,9 @@ $rcmail_config['session_lifetime'] = 10;
 // session domain: .example.org
 $rcmail_config['session_domain'] = '';
 
+// session name. Default: 'roundcube_sessid'
+$rcmail_config['session_name'] = null;
+
 // Backend to use for session storage. Can either be 'db' (default) or 'memcache'
 // If set to memcache, a list of servers need to be specified in 'memcache_hosts'
 // Make sure the Memcache extension (http://pecl.php.net/package/memcache) version >= 2.0.0 is installed
@@ -369,25 +372,30 @@ $rcmail_config['message_sort_col'] = '';
 $rcmail_config['message_sort_order'] = 'DESC';
 
 // These cols are shown in the message list. Available cols are:
-// subject, from, to, cc, replyto, date, size, status, flag, attachment
+// subject, from, to, cc, replyto, date, size, status, flag, attachment, 'priority'
 $rcmail_config['list_cols'] = array('subject', 'status', 'from', 'date', 'size', 'flag', 'attachment');
 
 // the default locale setting (leave empty for auto-detection)
 // RFC1766 formatted language name like en_US, de_DE, de_CH, fr_FR, pt_BR
 $rcmail_config['language'] = null;
 
-// use this format for short date display (date or strftime format)
-$rcmail_config['date_short'] = 'D H:i';
+// use this format for date display (date or strftime format)
+$rcmail_config['date_format'] = 'Y-m-d';
 
-// use this format for detailed date/time formatting (date or strftime format)
-$rcmail_config['date_long'] = 'd.m.Y H:i';
+// give this choice of date formats to the user to select from
+$rcmail_config['date_formats'] = array('Y-m-d', 'd-m-Y', 'Y/m/d', 'm/d/Y', 'd/m/Y', 'd.m.Y', 'j.n.Y');
 
-// use this format for today's date display (date or strftime format)
-// Note: $ character will be replaced with 'Today' label
-$rcmail_config['date_today'] = 'H:i';
+// use this format for time display (date or strftime format)
+$rcmail_config['time_format'] = 'H:i';
 
-// use this format for date display without time (date or strftime format)
-$rcmail_config['date_format'] = 'Y-m-d';
+// give this choice of time formats to the user to select from
+$rcmail_config['time_formats'] = array('G:i', 'H:i', 'g:i a', 'h:i A');
+
+// use this format for short date display (derived from date_format and time_format)
+$rcmail_config['date_short'] = 'D H:i';
+
+// use this format for detailed date/time formatting (derived from date_format and time_format)
+$rcmail_config['date_long'] = 'Y-m-d H:i';
 
 // store draft message is this mailbox
 // leave blank if draft messages should not be stored
@@ -413,7 +421,7 @@ $rcmail_config['trash_mbox'] = 'Trash';
 // NOTE: Use folder names with namespace prefix (INBOX. on Courier-IMAP)
 $rcmail_config['default_imap_folders'] = array('INBOX', 'Drafts', 'Sent', 'Junk', 'Trash');
 
-// automatically create the above listed default folders on login
+// automatically create the above listed default folders on first login
 $rcmail_config['create_default_folders'] = false;
 
 // protect the default folders from renames, deletes, and subscription changes
@@ -427,6 +435,10 @@ $rcmail_config['quota_zero_as_unlimited'] = false;
 // requires to be compiled with Open SSL support
 $rcmail_config['enable_spellcheck'] = true;
 
+// Enables spellchecker exceptions dictionary.
+// Setting it to 'shared' will make the dictionary shared by all users.
+$rcmail_config['spellcheck_dictionary'] = false;
+
 // Set the spell checking engine. 'googie' is the default. 'pspell' is also available,
 // but requires the Pspell extensions. When using Nox Spell Server, also set 'googie' here.
 $rcmail_config['spellcheck_engine'] = 'googie';
@@ -442,6 +454,18 @@ $rcmail_config['spellcheck_uri'] = '';
 // Leave empty for default set of available language.
 $rcmail_config['spellcheck_languages'] = NULL;
 
+// Makes that words with all letters capitalized will be ignored (e.g. GOOGLE)
+$rcmail_config['spellcheck_ignore_caps'] = false;
+
+// Makes that words with numbers will be ignored (e.g. g00gle)
+$rcmail_config['spellcheck_ignore_nums'] = false;
+
+// Makes that words with symbols will be ignored (e.g. g@@gle)
+$rcmail_config['spellcheck_ignore_syms'] = false;
+
+// Use this char/string to separate recipients when composing a new message
+$rcmail_config['recipients_separator'] = ',';
+
 // don't let users set pagesize to more than this value if set
 $rcmail_config['max_pagesize'] = 200;
 
@@ -517,14 +541,21 @@ $rcmail_config['ldap_public']['Verisign'] = array(
   // The login name is used to search for the DN to bind with
   'search_base_dn' => '',
   'search_filter'  => '',   // e.g. '(&(objectClass=posixAccount)(uid=%u))'
+  // DN and password to bind as before searching for bind DN, if anonymous search is not allowed
+  'search_bind_dn' => '',
+  'search_bind_pw' => '',
+  // Default for %dn variable if search doesn't return DN value
+  'search_dn_default' => '',
   // Optional authentication identifier to be used as SASL authorization proxy
   // bind_dn need to be empty
   'auth_cid'       => '',
   // SASL authentication method (for proxy auth), e.g. DIGEST-MD5
   'auth_method'    => '',
-  // Indicates if the addressbook shall be displayed on the list.
+  // Indicates if the addressbook shall be hidden from the list.
   // With this option enabled you can still search/view contacts.
   'hidden'        => false,
+  // Indicates if the addressbook shall not list contacts but only allows searching.
+  'searchonly'    => false,
   // Indicates if we can write to the LDAP directory or not.
   // If writable is true then these fields need to be populated:
   // LDAP_Object_Classes, required_fields, LDAP_rdn
@@ -564,6 +595,7 @@ $rcmail_config['ldap_public']['Verisign'] = array(
   'numsub_filter' => '(objectClass=organizationalUnit)',   // with VLV, we also use numSubOrdinates to query the total number of records. Set this filter to get all numSubOrdinates attributes for counting
   'sizelimit'     => '0',     // Enables you to limit the count of entries fetched. Setting this to 0 means no limit.
   'timelimit'     => '0',     // Sets the number of seconds how long is spend on the search. Setting this to 0 means no limit.
+  'referrals'     => true|false,  // Sets the LDAP_OPT_REFERRALS option. Mostly used in multi-domain Active Directory setups
 
   // definition for contact groups (uncomment if no groups are supported)
   // for the groups base_dn, the user replacements %fu, %u, $d and %dc work as for base_dn (see above)
@@ -573,8 +605,8 @@ $rcmail_config['ldap_public']['Verisign'] = array(
     'base_dn'     => '',
     'filter'      => '(objectClass=groupOfNames)',
     'object_classes' => array("top", "groupOfNames"),
-    // name of the member attribute, e.g. uniqueMember
-    'member_attr'    => 'member',
+    'member_attr'  => 'member',   // name of the member attribute, e.g. uniqueMember
+    'name_attr'    => 'cn',       // attribute to be used as group name
   ),
 );
 */
@@ -601,6 +633,13 @@ $rcmail_config['autocomplete_max'] = 15;
 // available placeholders: {street}, {locality}, {zipcode}, {country}, {region}
 $rcmail_config['address_template'] = '{street}<br/>{locality} {zipcode}<br/>{country} {region}';
 
+// Matching mode for addressbook search (including autocompletion)
+// 0 - partial (*abc*), default
+// 1 - strict (abc)
+// 2 - prefix (abc*)
+// Note: For LDAP sources fuzzy_search must be enabled to use 'partial' or 'prefix' mode
+$rcmail_config['addressbook_search_mode'] = 0;
+
 // ----------------------------------
 // USER PREFERENCES
 // ----------------------------------
@@ -617,8 +656,8 @@ $rcmail_config['pagesize'] = 40;
 // use this timezone to display date/time
 $rcmail_config['timezone'] = 'auto';
 
-// is daylight saving On?
-$rcmail_config['dst_active'] = (bool)date('I');
+// is daylight saving On? Default: (bool)date('I');
+$rcmail_config['dst_active'] = null;
 
 // prefer displaying HTML messages
 $rcmail_config['prefer_html'] = true;
@@ -748,4 +787,7 @@ $rcmail_config['default_addressbook'] = null;
 // Enables spell checking before sending a message.
 $rcmail_config['spellcheck_before_send'] = false;
 
+// Skip alternative email addresses in autocompletion (show one address per contact)
+$rcmail_config['autocomplete_single'] = false;
+
 // end of config file
index e3e55d74b719c8ad3f92780b902d7a49b8364939..1a7fcebf3fddabc8acbbcf4969ffadcd821eff87 100644 (file)
--- a/index.php
+++ b/index.php
@@ -2,7 +2,7 @@
 /*
  +-------------------------------------------------------------------------+
  | Roundcube Webmail IMAP Client                                           |
- | Version 0.6                                                             |
+ | Version 0.7                                                             |
  |                                                                         |
  | Copyright (C) 2005-2011, The Roundcube Dev Team                         |
  |                                                                         |
@@ -23,7 +23,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                          |
  +-------------------------------------------------------------------------+
 
- $Id: index.php 5292 2011-09-28 19:16:41Z thomasb $
+ $Id: index.php 5582 2011-12-09 08:55:40Z thomasb $
 
 */
 
@@ -33,6 +33,9 @@ require_once 'program/include/iniset.php';
 // init application, start session, init output class, etc.
 $RCMAIL = rcmail::get_instance();
 
+// Make the whole PHP output non-cacheable (#1487797)
+send_nocacheing_headers();
+
 // turn on output buffering
 ob_start();
 
@@ -177,7 +180,7 @@ if (empty($RCMAIL->user->ID)) {
       )
     );
   }
-  
+
   if ($session_error || $_REQUEST['_err'] == 'session')
     $OUTPUT->show_message('sessionerror', 'error', null, true, -1);
 
@@ -192,7 +195,7 @@ else {
   // check client X-header to verify request origin
   if ($OUTPUT->ajax_call) {
     if (rc_request_header('X-Roundcube-Request') != $RCMAIL->get_request_token() && !$RCMAIL->config->get('devel_mode')) {
-      header('HTTP/1.1 404 Not Found');
+      header('HTTP/1.1 403 Forbidden');
       die("Invalid Request");
     }
   }
@@ -211,6 +214,12 @@ else {
   }
 }
 
+// we're ready, user is authenticated and the request is safe
+$plugin = $RCMAIL->plugins->exec_hook('ready', array('task' => $RCMAIL->task, 'action' => $RCMAIL->action));
+$RCMAIL->set_task($plugin['task']);
+$RCMAIL->action = $plugin['action'];
+
+
 // handle special actions
 if ($RCMAIL->action == 'keep-alive') {
   $OUTPUT->reset();
index ff3f7a4c3c5ec00897d076656be27131722bf30e..dbe662acff869750ea29244a98d22a848382ff32 100644 (file)
@@ -142,20 +142,22 @@ class rcube_install
 
     foreach ($this->config as $prop => $default) {
 
-      $value = (isset($_POST["_$prop"]) || $this->bool_config_props[$prop]) ? $_POST["_$prop"] : $default;
+      $is_default = !isset($_POST["_$prop"]);
+      $value      = !$is_default || $this->bool_config_props[$prop] ? $_POST["_$prop"] : $default;
 
       // convert some form data
-      if ($prop == 'debug_level') {
-        $val = 0;
-        if (is_array($value))
+      if ($prop == 'debug_level' && !$is_default) {
+        if (is_array($value)) {
+          $val = 0;
           foreach ($value as $dbgval)
             $val += intval($dbgval);
-        $value = $val;
+          $value = $val;
+        }
       }
       else if ($which == 'db' && $prop == 'db_dsnw' && !empty($_POST['_dbtype'])) {
         if ($_POST['_dbtype'] == 'sqlite')
           $value = sprintf('%s://%s?mode=0646', $_POST['_dbtype'], $_POST['_dbname']{0} == '/' ? '/' . $_POST['_dbname'] : $_POST['_dbname']);
-        else
+        else if ($_POST['_dbtype'])
           $value = sprintf('%s://%s:%s@%s/%s', $_POST['_dbtype'], 
             rawurlencode($_POST['_dbuser']), rawurlencode($_POST['_dbpass']), $_POST['_dbhost'], $_POST['_dbname']);
       }
@@ -177,9 +179,9 @@ class rcube_install
         $value = '%p';
       }
       else if ($prop == 'default_imap_folders') {
-           $value = Array();
+           $value = array();
            foreach ($this->config['default_imap_folders'] as $_folder) {
-             switch($_folder) {
+             switch ($_folder) {
              case 'Drafts': $_folder = $this->config['drafts_mbox']; break;
              case 'Sent':   $_folder = $this->config['sent_mbox']; break;
              case 'Junk':   $_folder = $this->config['junk_mbox']; break;
@@ -206,7 +208,7 @@ class rcube_install
       // replace the matching line in config file
       $out = preg_replace(
         '/(\$rcmail_config\[\''.preg_quote($prop).'\'\])\s+=\s+(.+);/Uie',
-        "'\\1 = ' . rcube_install::_dump_var(\$value) . ';'",
+        "'\\1 = ' . rcube_install::_dump_var(\$value, \$prop) . ';'",
         $out);
     }
 
@@ -299,7 +301,7 @@ class rcube_install
     $current = $this->config;
     $this->config = array();
     $this->load_defaults();
-    
+
     foreach ($this->replaced_config as $prop => $replacement) {
       if (isset($current[$prop])) {
         if ($prop == 'skin_path')
@@ -328,9 +330,9 @@ class rcube_install
     
     if ($current['keep_alive'] && $current['session_lifetime'] < $current['keep_alive'])
       $current['session_lifetime'] = max(10, ceil($current['keep_alive'] / 60) * 2);
-    
+
     $this->config  = array_merge($this->config, $current);
-    
+
     foreach ((array)$current['ldap_public'] as $key => $values) {
       $this->config['ldap_public'][$key] = $current['ldap_public'][$key];
     }
@@ -614,7 +616,22 @@ class rcube_install
   }
   
   
-  static function _dump_var($var) {
+  static function _dump_var($var, $name=null) {
+    // special values
+    switch ($name) {
+    case 'syslog_facility':
+      $list = array(32 => 'LOG_AUTH', 80 => 'LOG_AUTHPRIV', 72 => ' LOG_CRON',
+                    24 => 'LOG_DAEMON', 0 => 'LOG_KERN', 128 => 'LOG_LOCAL0',
+                    136 => 'LOG_LOCAL1', 144 => 'LOG_LOCAL2', 152 => 'LOG_LOCAL3',
+                    160 => 'LOG_LOCAL4', 168 => 'LOG_LOCAL5', 176 => 'LOG_LOCAL6',
+                    184 => 'LOG_LOCAL7', 48 => 'LOG_LPR', 16 => 'LOG_MAIL',
+                    56 => 'LOG_NEWS', 40 => 'LOG_SYSLOG', 8 => 'LOG_USER', 64 => 'LOG_UUCP');
+      if ($val = $list[$var])
+        return $val;
+      break;
+    }
+
+
     if (is_array($var)) {
       if (empty($var)) {
         return 'array()';
index 02a1cebe27c40431971b7fa7dee54d6c81b9c1dd..2dd330531e5c36837fa7ba06a8e199dedef8a15d 100644 (file)
@@ -382,18 +382,20 @@ $pass_field = new html_passwordfield(array('name' => '_pass', 'id' => 'imappass'
 <?php
 
 if (isset($_POST['imaptest']) && !empty($_POST['_host']) && !empty($_POST['_user'])) {
-  
+
   echo '<p>Connecting to ' . Q($_POST['_host']) . '...<br />';
-  
-  $a_host = parse_url($_POST['_host']);
+
+  $imap_host = trim($_POST['_host']);
+  $imap_port = $RCI->getprop('default_port');
+  $a_host    = parse_url($imap_host);
+
   if ($a_host['host']) {
     $imap_host = $a_host['host'];
-    $imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
-    $imap_port = isset($a_host['port']) ? $a_host['port'] : ($imap_ssl ? 993 : $CONFIG['default_port']);
-  }
-  else {
-    $imap_host = trim($_POST['_host']);
-    $imap_port = $RCI->getprop('default_port');
+    $imap_ssl  = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
+    if (isset($a_host['port']))
+      $imap_port = $a_host['port'];
+    else if ($imap_ssl && $imap_ssl != 'tls' && (!$imap_port || $imap_port == 143))
+      $imap_port = 993;
   }
 
   $imap_host = idn_to_ascii($imap_host);
index 4b1431ac3106cef0e2baaa1bf70f814114582773..c4011a81c5af16faf1dc8d0c0013ce9940ccafc6 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * ACL plugin script
  *
- * @version 0.6.1
+ * @version 0.6.3
  * @author Aleksander Machniak <alec@alec.pl>
  */
 
@@ -315,6 +315,9 @@ rcube_webmail.prototype.acl_init_form = function(id)
     this.acl_form.show();
     if (type == 'user')
         name_input.focus();
+
+    // unfocus the list, make backspace key in name input field working
+    this.acl_list.blur();
 }
 
 // Returns class name according to ACL comparision result
index 976b362608ea50c517264324b9fd6a8a3c5e0ae8..3a5fd1ac1f822264a441d6b14381746bbb2706a1 100644 (file)
@@ -3,7 +3,7 @@
 /**
  * Folders Access Control Lists Management (RFC4314, RFC2086)
  *
- * @version 0.6.1
+ * @version @package_version@
  * @author Aleksander Machniak <alec@alec.pl>
  *
  *
@@ -87,11 +87,15 @@ class acl extends rcube_plugin
         $this->load_config();
 
         $search = get_input_value('_search', RCUBE_INPUT_GPC, true);
+        $sid    = get_input_value('_id', RCUBE_INPUT_GPC);
         $users  = array();
 
         if ($this->init_ldap()) {
-            $this->ldap->set_pagesize(15);
-            $result = $this->ldap->search('*', $search);
+            $max  = (int) $this->rc->config->get('autocomplete_max', 15);
+            $mode = (int) $this->rc->config->get('addressbook_search_mode');
+
+            $this->ldap->set_pagesize($max);
+            $result = $this->ldap->search('*', $search, $mode);
 
             foreach ($result->records as $record) {
                 $user = $record['uid'];
@@ -112,7 +116,7 @@ class acl extends rcube_plugin
 
         sort($users, SORT_LOCALE_STRING);
 
-        $this->rc->output->command('ksearch_query_results', $users, $search);
+        $this->rc->output->command('ksearch_query_results', $users, $search, $sid);
         $this->rc->output->send();
     }
 
@@ -186,6 +190,10 @@ class acl extends rcube_plugin
             'aclrights' => array($this, 'templ_rights'),
         ));
 
+        $this->rc->output->set_env('autocomplete_max', (int)$this->rc->config->get('autocomplete_max', 15));
+        $this->rc->output->set_env('autocomplete_min_length', $this->rc->config->get('autocomplete_min_length'));
+        $this->rc->output->add_label('autocompletechars', 'autocompletemore');
+
         $args['form']['sharing'] = array(
             'name'    => Q($this->gettext('sharing')),
             'content' => $this->rc->output->parse('acl.table', false, false),
@@ -619,7 +627,6 @@ class acl extends rcube_plugin
             $acl = $this->rc->imap->get_acl('INBOX');
             if (is_array($acl)) {
                 $regexp = '/^' . preg_quote($_SESSION['username'], '/') . '@(.*)$/';
-                $regexp = '/^' . preg_quote('aleksander.machniak', '/') . '@(.*)$/';
                 foreach (array_keys($acl) as $name) {
                     if (preg_match($regexp, $name, $matches)) {
                         $domain = $matches[1];
index e46a1d00b2ae977441d0c0f7c637df3f0e3e6d7c..cf3391f4934cc5d018e3247bb0547280311570de 100644 (file)
   background-color: #CC3333;
 }
 
+#acltable tr.unfocused td
+{
+  color: #FFFFFF;
+  background-color: #929292;
+}
+
 #acladvswitch
 {
   position: absolute;
index a837508192663c2640b48ca4bb836f7f917e8d70..5c576e1009444c23151fc0a33c971b0c85d460b4 100644 (file)
@@ -27,7 +27,9 @@ if (window.rcmail) {
     
     // set css style for archive folder
     var li;
-    if (rcmail.env.archive_folder && rcmail.env.archive_folder_icon && (li = rcmail.get_folder_li(rcmail.env.archive_folder)))
+    if (rcmail.env.archive_folder && rcmail.env.archive_folder_icon
+      && (li = rcmail.get_folder_li(rcmail.env.archive_folder, '', true))
+    )
       $(li).css('background-image', 'url(' + rcmail.env.archive_folder_icon + ')');
   })
 }
index 843b61217612e611a96a4e410b7fd0fe30d28a38..a56806263c2fa2ed9af494a363015ff0d659d879 100644 (file)
@@ -100,7 +100,7 @@ class archive extends rcube_plugin
       // load folders list when needed
       if ($CURR_SECTION)
         $select = rcmail_mailbox_select(array('noselection' => '---', 'realnames' => true,
-          'maxlength' => 30, 'exceptions' => array('INBOX')));
+          'maxlength' => 30, 'exceptions' => array('INBOX'), 'folder_filter' => 'mail', 'folder_rights' => 'w'));
       else
         $select = new html_select();
 
index f44f30f44e17696fa63996304d0a8a5d3452c67a..498a091fe82810c6af162bce0d5beb04021ded5d 100644 (file)
@@ -2,7 +2,7 @@
 
 $labels = array();
 $labels['buttontitle'] = 'Archiver ce message';
-$labels['archived'] = 'Message archivé avec success';
+$labels['archived'] = 'Message archivé avec success';
 $labels['archivefolder'] = 'Archive';
 
 ?>
index c442a5c4d2233e938e0d9d84f966e9acecf0b16e..c549fc9b7b516c67b4ee4870173050483169949c 100644 (file)
                <email>roundcube@gmail.com</email>
                <active>yes</active>
        </lead>
-       <date>2010-02-06</date>
-       <time>12:12:00</time>
+       <date>2011-11-23</date>
        <version>
-               <release>1.4</release>
+               <release>1.5</release>
                <api>1.4</api>
        </version>
        <stability>
                                <tasks:replace from="@name@" to="name" type="package-info"/>
                                <tasks:replace from="@package_version@" to="version" type="package-info"/>
                        </file>
-                       <file name="localization/en_US.inc" role="data"></file>
                        <file name="localization/cs_CZ.inc" role="data"></file>
                        <file name="localization/de_CH.inc" role="data"></file>
                        <file name="localization/de_DE.inc" role="data"></file>
+                       <file name="localization/en_US.inc" role="data"></file>
+                       <file name="localization/es_AR.inc" role="data"></file>
+                       <file name="localization/es_ES.inc" role="data"></file>
                        <file name="localization/et_EE.inc" role="data"></file>
                        <file name="localization/fr_FR.inc" role="data"></file>
+                       <file name="localization/gl_ES.inc" role="data"></file>
+                       <file name="localization/ja_JP.inc" role="data"></file>
+                       <file name="localization/nl_NL.inc" role="data"></file>
                        <file name="localization/pl_PL.inc" role="data"></file>
+                       <file name="localization/pt_BR.inc" role="data"></file>
                        <file name="localization/ru_RU.inc" role="data"></file>
+                       <file name="localization/sv_SE.inc" role="data"></file>
                        <file name="localization/zh_TW.inc" role="data"></file>
                        <file name="skins/default/archive_act.png" role="data"></file>
                        <file name="skins/default/archive_pas.png" role="data"></file>
index afb23224ca7998cc4031010b481ab9d3ba0899db..22d6e513a2de7a25e50b2a3d68503d21cb4d5baf 100644 (file)
@@ -18,7 +18,7 @@ Enigma Plugin Status:
 - Parsing of decrypted messages into array (see rcube_mime_struct) and then into rcube_message_part structure
   (create core class rcube_mime_parser or take over PEAR::Mail_mimeDecode package and improve it)
 - Sending encrypted/signed messages (probably some changes in core will be needed)
-- Per-Identity settings (including keys/certs) (+ split Identities details page into tabs)
+- Per-Identity settings (including keys/certs)
 - Handling big messages with temp files (including changes in Roundcube core)
 - Performance improvements (some caching, code review)
 - better (and more) icons
diff --git a/plugins/enigma/config.inc.php b/plugins/enigma/config.inc.php
deleted file mode 100644 (file)
index ca841d0..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-// Enigma Plugin options
-// --------------------
-
-// A driver to use for PGP. Default: "gnupg".
-$rcmail_config['enigma_pgp_driver'] = 'gnupg';
-
-// A driver to use for S/MIME. Default: "phpssl".
-$rcmail_config['enigma_smime_driver'] = 'phpssl';
-
-// Keys directory for all users. Default 'enigma/home'.
-// Must be writeable by PHP process
-$rcmail_config['enigma_pgp_homedir'] = null;
diff --git a/plugins/enigma/config.inc.php.dist b/plugins/enigma/config.inc.php.dist
new file mode 100644 (file)
index 0000000..ca841d0
--- /dev/null
@@ -0,0 +1,14 @@
+<?php
+
+// Enigma Plugin options
+// --------------------
+
+// A driver to use for PGP. Default: "gnupg".
+$rcmail_config['enigma_pgp_driver'] = 'gnupg';
+
+// A driver to use for S/MIME. Default: "phpssl".
+$rcmail_config['enigma_smime_driver'] = 'phpssl';
+
+// Keys directory for all users. Default 'enigma/home'.
+// Must be writeable by PHP process
+$rcmail_config['enigma_pgp_homedir'] = null;
index b9ccff53db9b0e3378445dec8f193f8bcbc6eaa9..5901b58d9a1f6d68fd232db8e3a18ce0e4ecf044 100644 (file)
@@ -412,9 +412,6 @@ class enigma_ui
 
     private function compose_ui()
     {
-        if (!is_array($_SESSION['compose']) || $_SESSION['compose']['id'] != get_input_value('_id', RCUBE_INPUT_GET))
-            return;
-
         // Options menu button
         // @TODO: make this work with non-default skins
         $this->enigma->add_button(array(
index fa074f09accdb243120b1245f0f3cde959ab4262..be49b52be0c70fe3a6b0b4fbd29d7c1fb0b7829e 100644 (file)
@@ -53,7 +53,7 @@ class http_authentication extends rcube_plugin
 
     return $args;
   }
-
+  
   function logout($args)
   {
     // redirect to configured URL in order to clear HTTP auth credentials
index e35406459bc102c5f323847e2850dbfa5e905e9d..05506120e192ab9f0df28e651ca8a5d90db383cc 100644 (file)
@@ -1,4 +1,36 @@
+- Fixed setting test type to :is when none is specified
+- Fixed javascript error in IE8
+
+* version 5.0-rc1 [2011-11-17]
+-----------------------------------------------------------
+- Fixed sorting of scripts, scripts including aware of the sort order
+- Fixed import of rules with unsupported tests
+- Added 'address' and 'envelope' tests support
+- Added 'body' extension support (RFC5173)
+- Added 'subaddress' extension support (RFC5233)
+- Added comparators support
+- Changed Sender/Recipient labels to From/To
+- Fixed importing rule names from Ingo
+- Fixed handling of extensions disabled in config
+
+* version 5.0-beta [2011-10-17]
+-----------------------------------------------------------
+- Added possibility to create a filter based on selected message "in-place"
 - Fixed import from Horde-INGO (#1488064)
+- Add managesieve_script_name option for default name of the script (#1487956)
+- Fixed handling of enabled magic_quotes_gpc setting
+- Fixed PHP warning on connection error when submitting filter form
+- Fixed bug where new action row with flags wasn't handled properly
+- Added managesieve_connect hook for plugins
+- Fixed doubled Filter tab on page refresh
+- Added filters set selector in filter form when invoked in mail task
+- Improved script parser, added support for include and variables extensions
+- Added Kolab's KEP:14 support (http://wiki.kolab.org/User:Greve/Drafts/KEP:14)
+- Use smaller action/rule buttons
+- UI redesign: added possibility to move filter to any place using drag&drop
+  (instead of up/down buttons), added filter sets list object, added more
+  'loading' messages
+- Added option to hide some scripts (managesieve_filename_exceptions)
 
 * version 4.3 [2011-07-28]
 -----------------------------------------------------------
index 905cfef18c5da255cbd2997ce58ddac5aa625a85..cb9b2a97ffa36c41d61261605768449fd53b2445 100644 (file)
@@ -31,6 +31,9 @@ $rcmail_config['managesieve_usetls'] = false;
 // default contents of filters script (eg. default spam filter)
 $rcmail_config['managesieve_default'] = '/etc/dovecot/sieve/global';
 
+// The name of the script which will be used when there's no user script
+$rcmail_config['managesieve_script_name'] = 'managesieve';
+
 // Sieve RFC says that we should use UTF-8 endcoding for mailbox names,
 // but some implementations does not covert UTF-8 to modified UTF-7.
 // Defaults to UTF7-IMAP
@@ -50,4 +53,15 @@ $rcmail_config['managesieve_disabled_extensions'] = array();
 // Enables debugging of conversation with sieve server. Logs it into <log_dir>/sieve
 $rcmail_config['managesieve_debug'] = false;
 
+// Enables features described in http://wiki.kolab.org/KEP:14
+$rcmail_config['managesieve_kolab_master'] = false;
+
+// Script name extension used for scripts including. Dovecot uses '.sieve',
+// Cyrus uses '.siv'. Doesn't matter if you have managesieve_kolab_master disabled.
+$rcmail_config['managesieve_filename_extension'] = '.sieve';
+
+// List of reserved script names (without extension).
+// Scripts listed here will be not presented to the user.
+$rcmail_config['managesieve_filename_exceptions'] = array();
+
 ?>
index 3e52809137b320e5d8cbab18cfd270b59e153cf5..7c4f0aa31a20f8d7e257b6cced9688a59f0aa26e 100644 (file)
@@ -1,13 +1,27 @@
 <?php
 
 /**
-  Classes for managesieve operations (using PEAR::Net_Sieve)
-
-  Author: Aleksander Machniak <alec@alec.pl>
-
-  $Id: rcube_sieve.php 5203 2011-09-12 06:44:56Z alec $
-
-*/
+ *  Classes for managesieve operations (using PEAR::Net_Sieve)
+ *
+ * Copyright (C) 2008-2011, The Roundcube Dev Team
+ * Copyright (C) 2011, Kolab Systems AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * $Id: rcube_sieve.php 5452 2011-11-18 14:44:48Z alec $
+ *
+ */
 
 // Managesieve Protocol: RFC5804
 
@@ -30,7 +44,6 @@ class rcube_sieve
 
     public $script;                 // rcube_sieve_script object
     public $current;                // name of currently loaded script
-    private $disabled;              // array of disabled extensions
     private $exts;                  // array of supported extensions
 
 
@@ -75,7 +88,17 @@ class rcube_sieve
         }
 
         $this->exts     = $this->get_extensions();
-        $this->disabled = $disabled;
+
+        // disable features by config
+        if (!empty($disabled)) {
+            // we're working on lower-cased names
+            $disabled = array_map('strtolower', (array) $disabled);
+            foreach ($disabled as $ext) {
+                if (($idx = array_search($ext, $this->exts)) !== false) {
+                    unset($this->exts[$idx]);
+                }
+            }
+        }
     }
 
     public function __destruct() {
@@ -195,7 +218,7 @@ class rcube_sieve
     {
         if ($this->exts)
             return $this->exts;
-    
+
         if (!$this->sieve)
             return $this->_set_error(SIEVE_ERROR_INTERNAL);
 
@@ -286,23 +309,28 @@ class rcube_sieve
      */
     private function _parse($txt)
     {
-        // try to parse from Roundcube format
-        $script = new rcube_sieve_script($txt, $this->disabled, $this->exts);
-
-        // ... else try to import from different formats
-        if (empty($script->content)) {
-            $script = $this->_import_rules($txt);
-            $script = new rcube_sieve_script($script, $this->disabled, $this->exts);
+        // parse
+        $script = new rcube_sieve_script($txt, $this->exts);
 
+        // fix/convert to Roundcube format
+        if (!empty($script->content)) {
             // replace all elsif with if+stop, we support only ifs
             foreach ($script->content as $idx => $rule) {
+                if (empty($rule['type']) || !preg_match('/^(if|elsif|else)$/', $rule['type'])) {
+                    continue;
+                }
+
+                $script->content[$idx]['type'] = 'if';
+
                 // 'stop' not found?
                 foreach ($rule['actions'] as $action) {
                     if (preg_match('/^(stop|vacation)$/', $action['type'])) {
                         continue 2;
                     }
                 }
-                $script->content[$idx]['actions'][] = array('type' => 'stop');
+                if (empty($script->content[$idx+1]) || $script->content[$idx+1]['type'] != 'if') {
+                    $script->content[$idx]['actions'][] = array('type' => 'stop');
+                }
             }
         }
 
@@ -343,48 +371,6 @@ class rcube_sieve
         return $this->save_script($name, $content);
     }
 
-    private function _import_rules($script)
-    {
-        $i = 0;
-        $name = array();
-
-        // Squirrelmail (Avelsieve)
-        if (preg_match('/(#START_SIEVE_RULE.*END_SIEVE_RULE)\r?\n/', $script)) {
-            $tokens = preg_split('/(#START_SIEVE_RULE.*END_SIEVE_RULE)\r?\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE);
-            foreach ($tokens as $token) {
-                if (preg_match('/^#START_SIEVE_RULE.*/', $token, $matches)) {
-                    $name[$i] = "unnamed rule ".($i+1);
-                    $content .= "# rule:[".$name[$i]."]\n";
-                }
-                elseif (isset($name[$i])) {
-                    // This preg_replace is added because I've found some Avelsieve scripts
-                    // with rules containing "if" here. I'm not sure it was working
-                    // before without this or not.
-                    $token = preg_replace('/^if\s+/', '', trim($token));
-                    $content .= "if $token\n";
-                    $i++;
-                }
-            }
-        }
-        // Horde (INGO)
-        else if (preg_match('/(# .+)\r?\n/', $script)) {
-            $tokens = preg_split('/(# .+)\r?\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE);
-            foreach($tokens as $token) {
-                if (preg_match('/^# (.+)/', $token, $matches)) {
-                    $name[$i] = $matches[1];
-                    $content .= "# rule:[" . $name[$i] . "]\n";
-                }
-                elseif (isset($name[$i])) {
-                    $token = str_replace(":comparator \"i;ascii-casemap\" ", "", $token);
-                    $content .= $token . "\n";
-                    $i++;
-                }
-            }
-        }
-
-        return $content;
-    }
-
     private function _set_error($error)
     {
         $this->error = $error;
index 871fb1401623c94110e175b22dc3e7944478e8fa..04bcc4c139428939b99a5f4c09388fd03e707481 100644 (file)
@@ -1,20 +1,37 @@
 <?php
 
 /**
-  Class for operations on Sieve scripts
-
-  Author: Aleksander Machniak <alec@alec.pl>
-
-  $Id: rcube_sieve_script.php 4806 2011-05-24 08:32:01Z alec $
-
-*/
+ *  Class for operations on Sieve scripts
+ *
+ * Copyright (C) 2008-2011, The Roundcube Dev Team
+ * Copyright (C) 2011, Kolab Systems AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * $Id: rcube_sieve_script.php 5452 2011-11-18 14:44:48Z alec $
+ *
+ */
 
 class rcube_sieve_script
 {
     public $content = array();      // script rules array
 
-    private $supported = array(     // extensions supported by class
-        'fileinto',                 // RFC3028
+    private $vars = array();        // "global" variables
+    private $prefix = '';           // script header (comments)
+    private $supported = array(     // Sieve extensions supported by class
+        'fileinto',                 // RFC5228
+        'envelope',                 // RFC5228
         'reject',                   // RFC5429
         'ereject',                  // RFC5429
         'copy',                     // RFC3894
@@ -23,57 +40,34 @@ class rcube_sieve_script
         'regex',                    // draft-ietf-sieve-regex-01
         'imapflags',                // draft-melnikov-sieve-imapflags-06
         'imap4flags',               // RFC5232
-        // TODO: body, notify
+        'include',                  // draft-ietf-sieve-include-12
+        'variables',                // RFC5229
+        'body',                     // RFC5173
+        'subaddress',               // RFC5233
+        // @TODO: enotify/notify, spamtest+virustest, mailbox, date
     );
 
-    private $capabilities;
-
     /**
      * Object constructor
      *
      * @param  string  Script's text content
-     * @param  array   List of disabled extensions
      * @param  array   List of capabilities supported by server
      */
-    public function __construct($script, $disabled=null, $capabilities=null)
+    public function __construct($script, $capabilities=array())
     {
-        if (!empty($disabled)) {
-            // we're working on lower-cased names
-            $disabled = array_map('strtolower', (array) $disabled);
-            foreach ($disabled as $ext) {
-                if (($idx = array_search($ext, $this->supported)) !== false) {
+        $capabilities = array_map('strtolower', (array) $capabilities);
+
+        // disable features by server capabilities
+        if (!empty($capabilities)) {
+            foreach ($this->supported as $idx => $ext) {
+                if (!in_array($ext, $capabilities)) {
                     unset($this->supported[$idx]);
                 }
             }
         }
 
-        $this->capabilities = $capabilities;
-        $this->content      = $this->_parse_text($script);
-    }
-
-    /**
-     * Adds script contents as text to the script array (at the end)
-     *
-     * @param    string    Text script contents
-     */
-    public function add_text($script)
-    {
-        $content = $this->_parse_text($script);
-        $result = false;
-
-        // check existsing script rules names
-        foreach ($this->content as $idx => $elem) {
-            $names[$elem['name']] = $idx;
-        }
-
-        foreach ($content as $elem) {
-            if (!isset($names[$elem['name']])) {
-                array_push($this->content, $elem);
-                $result = true;
-            }
-        }
-
-        return $result;
+        // Parse text content of the script
+        $this->_parse_text($script);
     }
 
     /**
@@ -81,6 +75,8 @@ class rcube_sieve_script
      *
      * @param string Rule name
      * @param array  Rule content (as array)
+     *
+     * @return int The index of the new rule
      */
     public function add_rule($content)
     {
@@ -113,162 +109,344 @@ class rcube_sieve_script
         return false;
     }
 
+    /**
+     * Sets "global" variable
+     *
+     * @param string $name  Variable name
+     * @param string $value Variable value
+     * @param array  $mods  Variable modifiers
+     */
+    public function set_var($name, $value, $mods = array())
+    {
+        // Check if variable exists
+        for ($i=0, $len=count($this->vars); $i<$len; $i++) {
+            if ($this->vars[$i]['name'] == $name) {
+                break;
+            }
+        }
+
+        $var = array_merge($mods, array('name' => $name, 'value' => $value));
+        $this->vars[$i] = $var;
+    }
+
+    /**
+     * Unsets "global" variable
+     *
+     * @param string $name  Variable name
+     */
+    public function unset_var($name)
+    {
+        // Check if variable exists
+        foreach ($this->vars as $idx => $var) {
+            if ($var['name'] == $name) {
+                unset($this->vars[$idx]);
+                break;
+            }
+        }
+    }
+
+    /**
+     * Gets the value of  "global" variable
+     *
+     * @param string $name  Variable name
+     *
+     * @return string Variable value
+     */
+    public function get_var($name)
+    {
+        // Check if variable exists
+        for ($i=0, $len=count($this->vars); $i<$len; $i++) {
+            if ($this->vars[$i]['name'] == $name) {
+                return $this->vars[$i]['name'];
+            }
+        }
+    }
+
+    /**
+     * Sets script header content
+     *
+     * @param string $text  Header content
+     */
+    public function set_prefix($text)
+    {
+        $this->prefix = $text;
+    }
+
     /**
      * Returns script as text
      */
     public function as_text()
     {
-        $script = '';
-        $exts = array();
-        $idx = 0;
+        $output = '';
+        $exts   = array();
+        $idx    = 0;
+
+        if (!empty($this->vars)) {
+            if (in_array('variables', (array)$this->supported)) {
+                $has_vars = true;
+                array_push($exts, 'variables');
+            }
+            foreach ($this->vars as $var) {
+                if (empty($has_vars)) {
+                    // 'variables' extension not supported, put vars in comments
+                    $output .= sprintf("# %s %s\n", $var['name'], $var['value']);
+                }
+                else {
+                    $output .= 'set ';
+                    foreach (array_diff(array_keys($var), array('name', 'value')) as $opt) {
+                        $output .= ":$opt ";
+                    }
+                    $output .= self::escape_string($var['name']) . ' ' . self::escape_string($var['value']) . ";\n";
+                }
+            }
+        }
 
         // rules
         foreach ($this->content as $rule) {
             $extension = '';
-            $tests = array();
-            $i = 0;
+            $script    = '';
+            $tests     = array();
+            $i         = 0;
 
             // header
-            $script .= '# rule:[' . $rule['name'] . "]\n";
+            if (!empty($rule['name']) && strlen($rule['name'])) {
+                $script .= '# rule:[' . $rule['name'] . "]\n";
+            }
 
             // constraints expressions
-            foreach ($rule['tests'] as $test) {
-                $tests[$i] = '';
-                switch ($test['test']) {
-                case 'size':
-                    $tests[$i] .= ($test['not'] ? 'not ' : '');
-                    $tests[$i] .= 'size :' . ($test['type']=='under' ? 'under ' : 'over ') . $test['arg'];
-                    break;
-                case 'true':
-                    $tests[$i] .= ($test['not'] ? 'false' : 'true');
-                    break;
-                case 'exists':
-                    $tests[$i] .= ($test['not'] ? 'not ' : '');
-                    $tests[$i] .= 'exists ' . self::escape_string($test['arg']);
-                    break;
-                case 'header':
-                    $tests[$i] .= ($test['not'] ? 'not ' : '');
+            if (!empty($rule['tests'])) {
+                foreach ($rule['tests'] as $test) {
+                    $tests[$i] = '';
+                    switch ($test['test']) {
+                    case 'size':
+                        $tests[$i] .= ($test['not'] ? 'not ' : '');
+                        $tests[$i] .= 'size :' . ($test['type']=='under' ? 'under ' : 'over ') . $test['arg'];
+                        break;
 
-                    // relational operator + comparator
-                                       if (preg_match('/^(value|count)-([gteqnl]{2})/', $test['type'], $m)) {
-                                               array_push($exts, 'relational');
-                                               array_push($exts, 'comparator-i;ascii-numeric');
+                    case 'true':
+                        $tests[$i] .= ($test['not'] ? 'false' : 'true');
+                        break;
 
-                        $tests[$i] .= 'header :' . $m[1] . ' "' . $m[2] . '" :comparator "i;ascii-numeric"';
-                    }
-                    else {
-                                           if ($test['type'] == 'regex') {
-                                                   array_push($exts, 'regex');
+                    case 'exists':
+                        $tests[$i] .= ($test['not'] ? 'not ' : '');
+                        $tests[$i] .= 'exists ' . self::escape_string($test['arg']);
+                        break;
+
+                    case 'header':
+                        $tests[$i] .= ($test['not'] ? 'not ' : '');
+                        $tests[$i] .= 'header';
+
+                        if (!empty($test['type'])) {
+                            // relational operator + comparator
+                            if (preg_match('/^(value|count)-([gteqnl]{2})/', $test['type'], $m)) {
+                                array_push($exts, 'relational');
+                                array_push($exts, 'comparator-i;ascii-numeric');
+
+                                $tests[$i] .= ' :' . $m[1] . ' "' . $m[2] . '" :comparator "i;ascii-numeric"';
+                            }
+                            else {
+                                $this->add_comparator($test, $tests[$i], $exts);
+
+                                if ($test['type'] == 'regex') {
+                                    array_push($exts, 'regex');
+                                }
+
+                                $tests[$i] .= ' :' . $test['type'];
+                            }
                         }
 
-                        $tests[$i] .= 'header :' . $test['type'];
-                    }
+                        $tests[$i] .= ' ' . self::escape_string($test['arg1']);
+                        $tests[$i] .= ' ' . self::escape_string($test['arg2']);
+                        break;
 
-                    $tests[$i] .= ' ' . self::escape_string($test['arg1']);
-                    $tests[$i] .= ' ' . self::escape_string($test['arg2']);
-                    break;
+                    case 'address':
+                    case 'envelope':
+                        if ($test['test'] == 'envelope') {
+                            array_push($exts, 'envelope');
+                        }
+
+                        $tests[$i] .= ($test['not'] ? 'not ' : '');
+                        $tests[$i] .= $test['test'];
+
+                        if (!empty($test['part'])) {
+                            $tests[$i] .= ' :' . $test['part'];
+                            if ($test['part'] == 'user' || $test['part'] == 'detail') {
+                                array_push($exts, 'subaddress');
+                            }
+                        }
+
+                        $this->add_comparator($test, $tests[$i], $exts);
+
+                        if (!empty($test['type'])) {
+                            if ($test['type'] == 'regex') {
+                                array_push($exts, 'regex');
+                            }
+                            $tests[$i] .= ' :' . $test['type'];
+                        }
+
+                        $tests[$i] .= ' ' . self::escape_string($test['arg1']);
+                        $tests[$i] .= ' ' . self::escape_string($test['arg2']);
+                        break;
+
+                    case 'body':
+                        array_push($exts, 'body');
+
+                        $tests[$i] .= ($test['not'] ? 'not ' : '') . 'body';
+
+                        $this->add_comparator($test, $tests[$i], $exts);
+
+                        if (!empty($test['part'])) {
+                            $tests[$i] .= ' :' . $test['part'];
+
+                            if (!empty($test['content']) && $test['part'] == 'content') {
+                                $tests[$i] .= ' ' . self::escape_string($test['content']);
+                            }
+                        }
+
+                        if (!empty($test['type'])) {
+                            if ($test['type'] == 'regex') {
+                                array_push($exts, 'regex');
+                            }
+                            $tests[$i] .= ' :' . $test['type'];
+                        }
+
+                        $tests[$i] .= ' ' . self::escape_string($test['arg']);
+                        break;
+                    }
+                    $i++;
                 }
-                $i++;
             }
 
             // disabled rule: if false #....
-            $script .= 'if ' . ($rule['disabled'] ? 'false # ' : '');
+            if (!empty($tests)) {
+                $script .= 'if ' . ($rule['disabled'] ? 'false # ' : '');
 
-            if (empty($tests)) {
-                $tests_str = 'true';
-            }
-            else if (count($tests) > 1) {
-                $tests_str = implode(', ', $tests);
-            }
-            else {
-                $tests_str = $tests[0];
-            }
+                if (count($tests) > 1) {
+                    $tests_str = implode(', ', $tests);
+                }
+                else {
+                    $tests_str = $tests[0];
+                }
 
-            if ($rule['join'] || count($tests) > 1) {
-                $script .= sprintf('%s (%s)', $rule['join'] ? 'allof' : 'anyof', $tests_str);
-            }
-            else {
-                $script .= $tests_str;
+                if ($rule['join'] || count($tests) > 1) {
+                    $script .= sprintf('%s (%s)', $rule['join'] ? 'allof' : 'anyof', $tests_str);
+                }
+                else {
+                    $script .= $tests_str;
+                }
+                $script .= "\n{\n";
             }
-            $script .= "\n{\n";
 
             // action(s)
-            foreach ($rule['actions'] as $action) {
-                switch ($action['type']) {
+            if (!empty($rule['actions'])) {
+                foreach ($rule['actions'] as $action) {
+                    $action_script = '';
+
+                    switch ($action['type']) {
+
+                    case 'fileinto':
+                        array_push($exts, 'fileinto');
+                        $action_script .= 'fileinto ';
+                        if ($action['copy']) {
+                            $action_script .= ':copy ';
+                            array_push($exts, 'copy');
+                        }
+                        $action_script .= self::escape_string($action['target']);
+                        break;
 
-                case 'fileinto':
-                    array_push($exts, 'fileinto');
-                    $script .= "\tfileinto ";
-                    if ($action['copy']) {
-                        $script .= ':copy ';
-                        array_push($exts, 'copy');
-                    }
-                    $script .= self::escape_string($action['target']) . ";\n";
-                    break;
+                    case 'redirect':
+                        $action_script .= 'redirect ';
+                        if ($action['copy']) {
+                            $action_script .= ':copy ';
+                            array_push($exts, 'copy');
+                        }
+                        $action_script .= self::escape_string($action['target']);
+                        break;
 
-                case 'redirect':
-                    $script .= "\tredirect ";
-                    if ($action['copy']) {
-                        $script .= ':copy ';
-                        array_push($exts, 'copy');
-                    }
-                    $script .= self::escape_string($action['target']) . ";\n";
-                    break;
+                    case 'reject':
+                    case 'ereject':
+                        array_push($exts, $action['type']);
+                        $action_script .= $action['type'].' '
+                            . self::escape_string($action['target']);
+                        break;
 
-                case 'reject':
-                case 'ereject':
-                    array_push($exts, $action['type']);
-                    $script .= "\t".$action['type']." "
-                        . self::escape_string($action['target']) . ";\n";
-                    break;
+                    case 'addflag':
+                    case 'setflag':
+                    case 'removeflag':
+                        if (in_array('imap4flags', $this->supported))
+                            array_push($exts, 'imap4flags');
+                        else
+                            array_push($exts, 'imapflags');
 
-                case 'addflag':
-                case 'setflag':
-                case 'removeflag':
-                    if (is_array($this->capabilities) && in_array('imap4flags', $this->capabilities))
-                        array_push($exts, 'imap4flags');
-                    else
-                        array_push($exts, 'imapflags');
+                        $action_script .= $action['type'].' '
+                            . self::escape_string($action['target']);
+                        break;
 
-                    $script .= "\t".$action['type']." "
-                        . self::escape_string($action['target']) . ";\n";
-                    break;
+                    case 'keep':
+                    case 'discard':
+                    case 'stop':
+                        $action_script .= $action['type'];
+                        break;
 
-                case 'keep':
-                case 'discard':
-                case 'stop':
-                    $script .= "\t" . $action['type'] .";\n";
-                    break;
+                    case 'include':
+                        array_push($exts, 'include');
+                        $action_script .= 'include ';
+                        foreach (array_diff(array_keys($action), array('target', 'type')) as $opt) {
+                            $action_script .= ":$opt ";
+                        }
+                        $action_script .= self::escape_string($action['target']);
+                        break;
 
-                case 'vacation':
-                    array_push($exts, 'vacation');
-                    $script .= "\tvacation";
-                    if (!empty($action['days']))
-                        $script .= " :days " . $action['days'];
-                    if (!empty($action['addresses']))
-                        $script .= " :addresses " . self::escape_string($action['addresses']);
-                    if (!empty($action['subject']))
-                        $script .= " :subject " . self::escape_string($action['subject']);
-                    if (!empty($action['handle']))
-                        $script .= " :handle " . self::escape_string($action['handle']);
-                    if (!empty($action['from']))
-                        $script .= " :from " . self::escape_string($action['from']);
-                    if (!empty($action['mime']))
-                        $script .= " :mime";
-                    $script .= " " . self::escape_string($action['reason']) . ";\n";
-                    break;
+                    case 'set':
+                        array_push($exts, 'variables');
+                        $action_script .= 'set ';
+                        foreach (array_diff(array_keys($action), array('name', 'value', 'type')) as $opt) {
+                            $action_script .= ":$opt ";
+                        }
+                        $action_script .= self::escape_string($action['name']) . ' ' . self::escape_string($action['value']);
+                        break;
+
+                    case 'vacation':
+                        array_push($exts, 'vacation');
+                        $action_script .= 'vacation';
+                        if (!empty($action['days']))
+                            $action_script .= " :days " . $action['days'];
+                        if (!empty($action['addresses']))
+                            $action_script .= " :addresses " . self::escape_string($action['addresses']);
+                        if (!empty($action['subject']))
+                            $action_script .= " :subject " . self::escape_string($action['subject']);
+                        if (!empty($action['handle']))
+                            $action_script .= " :handle " . self::escape_string($action['handle']);
+                        if (!empty($action['from']))
+                            $action_script .= " :from " . self::escape_string($action['from']);
+                        if (!empty($action['mime']))
+                            $action_script .= " :mime";
+                        $action_script .= " " . self::escape_string($action['reason']);
+                        break;
+                    }
+
+                    if ($action_script) {
+                        $script .= !empty($tests) ? "\t" : '';
+                        $script .= $action_script . ";\n";
+                    }
                 }
             }
 
-            $script .= "}\n";
-            $idx++;
+            if ($script) {
+                $output .= $script . (!empty($tests) ? "}\n" : '');
+                $idx++;
+            }
         }
 
         // requires
         if (!empty($exts))
-            $script = 'require ["' . implode('","', array_unique($exts)) . "\"];\n" . $script;
+            $output = 'require ["' . implode('","', array_unique($exts)) . "\"];\n" . $output;
 
-        return $script;
+        if (!empty($this->prefix)) {
+            $output = $this->prefix . "\n\n" . $output;
+        }
+
+        return $output;
     }
 
     /**
@@ -296,35 +474,89 @@ class rcube_sieve_script
      */
     private function _parse_text($script)
     {
-        $i = 0;
-        $content = array();
+        $prefix     = '';
+        $options = array();
+
+        while ($script) {
+            $script = trim($script);
+            $rule   = array();
+
+            // Comments
+            while (!empty($script) && $script[0] == '#') {
+                $endl = strpos($script, "\n");
+                $line = $endl ? substr($script, 0, $endl) : $script;
+
+                // Roundcube format
+                if (preg_match('/^# rule:\[(.*)\]/', $line, $matches)) {
+                    $rulename = $matches[1];
+                }
+                // KEP:14 variables
+                else if (preg_match('/^# (EDITOR|EDITOR_VERSION) (.+)$/', $line, $matches)) {
+                    $this->set_var($matches[1], $matches[2]);
+                }
+                // Horde-Ingo format
+                else if (!empty($options['format']) && $options['format'] == 'INGO'
+                    && preg_match('/^# (.*)/', $line, $matches)
+                ) {
+                    $rulename = $matches[1];
+                }
+                else if (empty($options['prefix'])) {
+                    $prefix .= $line . "\n";
+                }
+
+                $script = ltrim(substr($script, strlen($line) + 1));
+            }
+
+            // handle script header
+            if (empty($options['prefix'])) {
+                $options['prefix'] = true;
+                if ($prefix && strpos($prefix, 'horde.org/ingo')) {
+                    $options['format'] = 'INGO';
+                }
+            }
 
-        // tokenize rules
-        if ($tokens = preg_split('/(# rule:\[.*\])\r?\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE)) {
-            foreach($tokens as $token) {
-                if (preg_match('/^# rule:\[(.*)\]/', $token, $matches)) {
-                    $content[$i]['name'] = $matches[1];
+            // Control structures/blocks
+            if (preg_match('/^(if|else|elsif)/i', $script)) {
+                $rule = $this->_tokenize_rule($script);
+                if (strlen($rulename) && !empty($rule)) {
+                    $rule['name'] = $rulename;
                 }
-                else if (isset($content[$i]['name']) && sizeof($content[$i]) == 1) {
-                    if ($rule = $this->_tokenize_rule($token)) {
-                        $content[$i] = array_merge($content[$i], $rule);
-                        $i++;
+            }
+            // Simple commands
+            else {
+                $rule = $this->_parse_actions($script, ';');
+                if (!empty($rule[0]) && is_array($rule)) {
+                    // set "global" variables
+                    if ($rule[0]['type'] == 'set') {
+                        unset($rule[0]['type']);
+                        $this->vars[] = $rule[0];
+                    }
+                    else {
+                        $rule = array('actions' => $rule);
                     }
-                    else // unknown rule format
-                        unset($content[$i]);
                 }
             }
+
+            $rulename = '';
+
+            if (!empty($rule)) {
+                $this->content[] = $rule;
+            }
         }
 
-        return $content;
+        if (!empty($prefix)) {
+            $this->prefix = trim($prefix);
+        }
     }
 
     /**
      * Convert text script fragment to rule object
      *
      * @param string Text rule
+     *
+     * @return array Rule data
      */
-    private function _tokenize_rule($content)
+    private function _tokenize_rule(&$content)
     {
         $cond = strtolower(self::tokenize($content, 1));
 
@@ -389,7 +621,7 @@ class rcube_sieve_script
                 $header = array('test' => 'header', 'not' => $not, 'arg1' => '', 'arg2' => '');
                 for ($i=0, $len=count($tokens); $i<$len; $i++) {
                     if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) {
-                        $i++;
+                        $header['comparator'] = $tokens[++$i];
                     }
                     else if (!is_array($tokens[$i]) && preg_match('/^:(count|value)$/i', $tokens[$i])) {
                         $header['type'] = strtolower(substr($tokens[$i], 1)) . '-' . $tokens[++$i];
@@ -406,6 +638,52 @@ class rcube_sieve_script
                 $tests[] = $header;
                 break;
 
+            case 'address':
+            case 'envelope':
+                $header = array('test' => $token, 'not' => $not, 'arg1' => '', 'arg2' => '');
+                for ($i=0, $len=count($tokens); $i<$len; $i++) {
+                    if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) {
+                        $header['comparator'] = $tokens[++$i];
+                    }
+                    else if (!is_array($tokens[$i]) && preg_match('/^:(is|contains|matches|regex)$/i', $tokens[$i])) {
+                        $header['type'] = strtolower(substr($tokens[$i], 1));
+                    }
+                    else if (!is_array($tokens[$i]) && preg_match('/^:(localpart|domain|all|user|detail)$/i', $tokens[$i])) {
+                        $header['part'] = strtolower(substr($tokens[$i], 1));
+                    }
+                    else {
+                        $header['arg1'] = $header['arg2'];
+                        $header['arg2'] = $tokens[$i];
+                    }
+                }
+
+                $tests[] = $header;
+                break;
+
+            case 'body':
+                $header = array('test' => 'body', 'not' => $not, 'arg' => '');
+                for ($i=0, $len=count($tokens); $i<$len; $i++) {
+                    if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) {
+                        $header['comparator'] = $tokens[++$i];
+                    }
+                    else if (!is_array($tokens[$i]) && preg_match('/^:(is|contains|matches|regex)$/i', $tokens[$i])) {
+                        $header['type'] = strtolower(substr($tokens[$i], 1));
+                    }
+                    else if (!is_array($tokens[$i]) && preg_match('/^:(raw|content|text)$/i', $tokens[$i])) {
+                        $header['part'] = strtolower(substr($tokens[$i], 1));
+
+                        if ($header['part'] == 'content') {
+                            $header['content'] = $tokens[++$i];
+                        }
+                    }
+                    else {
+                        $header['arg'] = $tokens[$i];
+                    }
+                }
+
+                $tests[] = $header;
+                break;
+
             case 'exists':
                 $tests[] = array('test' => 'exists', 'not'  => $not,
                     'arg'  => array_pop($tokens));
@@ -427,9 +705,7 @@ class rcube_sieve_script
         }
 
         // ...and actions block
-        if ($tests) {
-            $actions = $this->_parse_actions($content);
-        }
+        $actions = $this->_parse_actions($content);
 
         if ($tests && $actions) {
             $result = array(
@@ -447,10 +723,12 @@ class rcube_sieve_script
     /**
      * Parse body of actions section
      *
-     * @param string Text body
+     * @param string $content  Text body
+     * @param string $end      End of text separator
+     *
      * @return array Array of parsed action type/target pairs
      */
-    private function _parse_actions($content)
+    private function _parse_actions(&$content, $end = '}')
     {
         $result = null;
 
@@ -531,12 +809,72 @@ class rcube_sieve_script
                     'target' => $tokens[count($tokens)-1]
                 );
                 break;
+
+            case 'include':
+                $include = array('type' => 'include', 'target' => array_pop($tokens));
+
+                // Parameters: :once, :optional, :global, :personal
+                for ($i=0, $len=count($tokens); $i<$len; $i++) {
+                    $tok = strtolower($tokens[$i]);
+                    if ($tok[0] == ':') {
+                        $include[substr($tok, 1)] = true;
+                    }
+                }
+
+                $result[] = $include;
+                break;
+
+            case 'set':
+                $set = array('type' => 'set', 'value' => array_pop($tokens), 'name' => array_pop($tokens));
+
+                // Parameters: :lower :upper :lowerfirst :upperfirst :quotewildcard :length
+                for ($i=0, $len=count($tokens); $i<$len; $i++) {
+                    $tok = strtolower($tokens[$i]);
+                    if ($tok[0] == ':') {
+                        $set[substr($tok, 1)] = true;
+                    }
+                }
+
+                $result[] = $set;
+                break;
+
+            case 'require':
+                // skip, will be build according to used commands
+                // $result[] = array('type' => 'require', 'target' => $tokens);
+                break;
+
             }
+
+            if ($separator == $end)
+                break;
         }
 
         return $result;
     }
 
+    /**
+     *
+     */
+    private function add_comparator($test, &$out, &$exts)
+    {
+        if (empty($test['comparator'])) {
+            return;
+        }
+
+        if ($test['comparator'] == 'i;ascii-numeric') {
+            array_push($exts, 'relational');
+            array_push($exts, 'comparator-i;ascii-numeric');
+        }
+        else if (!in_array($test['comparator'], array('i;octet', 'i;ascii-casemap'))) {
+            array_push($exts, 'comparator-' . $test['comparator']);
+        }
+
+        // skip default comparator
+        if ($test['comparator'] != 'i;ascii-casemap') {
+            $out .= ' :comparator ' . self::escape_string($test['comparator']);
+        }
+    }
+
     /**
      * Escape special chars into quoted string value or multi-line string
      * or list of strings
@@ -595,7 +933,7 @@ class rcube_sieve_script
      * @param mixed  $num     Number of tokens to return, 0 for all
      *                        or True for all tokens until separator is found.
      *                        Separator will be returned as last token.
-     * @param int    $in_list Enable to called recursively inside a list
+     * @param int    $in_list Enable to call recursively inside a list
      *
      * @return mixed Tokens array or string if $num=1
      */
@@ -654,7 +992,7 @@ class rcube_sieve_script
                 $str = substr($str, 1);
                 if ($num === true) {
                     $result[] = $sep;
-                    break 2; 
+                    break 2;
                 }
                 break;
 
@@ -684,7 +1022,7 @@ class rcube_sieve_script
             // String atom
             default:
                 // empty or one character
-                if ($str === '') {
+                if ($str === '' || $str === null) {
                     break 2;
                 }
                 if (strlen($str) < 2) {
index c0fe389f529732305b3af02243266c3600828a1d..1fcef1e362cb6b12fd6e2aa1e1dca3dc6e1fe950 100644 (file)
@@ -1,5 +1,20 @@
 <?php
 
+/*
+ +-----------------------------------------------------------------------+
+ | localization/de_CH/labels.inc                                         |
+ |                                                                       |
+ | Language file of the Roundcube Webmail client                         |
+ | Copyright (C) 2011, The Roundcube Dev Team                            |
+ | Licensed under the GNU General Public License                         |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas <Unknown>                                              |
+ +-----------------------------------------------------------------------+
+ @version : importgettext.sh 5558 2011-12-07 08:51:01Z thomasb $
+*/
+
+$labels = array();
 $labels['filters'] = 'Filter';
 $labels['managefilters'] = 'Verwalte eingehende Nachrichtenfilter';
 $labels['filtername'] = 'Filtername';
@@ -17,12 +32,18 @@ $labels['filteris'] = 'ist gleich';
 $labels['filterisnot'] = 'ist ungleich';
 $labels['filterexists'] = 'ist vorhanden';
 $labels['filternotexists'] = 'nicht vorhanden';
+$labels['filtermatches'] = 'entspricht Ausdruck';
+$labels['filternotmatches'] = 'entspricht nicht Ausdruck';
+$labels['filterregex'] = 'trifft regulären Ausdruck';
+$labels['filternotregex'] = 'entspricht regulärem Ausdruck';
 $labels['filterunder'] = 'unter';
 $labels['filterover'] = 'über';
 $labels['addrule'] = 'Regel hinzufügen';
 $labels['delrule'] = 'Regel löschen';
 $labels['messagemoveto'] = 'Verschiebe Nachricht nach';
 $labels['messageredirect'] = 'Leite Nachricht um nach';
+$labels['messagecopyto'] = 'Kopiere Nachricht nach';
+$labels['messagesendcopy'] = 'Sende Kopie an';
 $labels['messagereply'] = 'Antworte mit Nachricht';
 $labels['messagedelete'] = 'Nachricht löschen';
 $labels['messagediscard'] = 'Discard with message';
@@ -35,18 +56,64 @@ $labels['recipient'] = 'Empfänger';
 $labels['vacationaddresses'] = 'Zusätzliche Liste von Empfängern (Komma getrennt):';
 $labels['vacationdays'] = 'Antwort wird erneut gesendet nach (in Tagen):';
 $labels['vacationreason'] = 'Inhalt der Nachricht (Abwesenheitsgrund):';
+$labels['vacationsubject'] = 'Betreff';
 $labels['rulestop'] = 'Regelauswertung anhalten';
+$labels['enable'] = 'Aktivieren/Deaktivieren';
+$labels['filterset'] = 'Filtersätze';
+$labels['filtersets'] = 'Filtersätze';
+$labels['filtersetadd'] = 'Filtersatz anlegen';
+$labels['filtersetdel'] = 'Aktuellen Filtersatz löschen';
+$labels['filtersetact'] = 'Aktuellen Filtersatz aktivieren';
+$labels['filtersetdeact'] = 'Aktuellen Filtersatz deaktivieren';
+$labels['filterdef'] = 'Filterdefinition';
+$labels['filtersetname'] = 'Filtersatzname';
+$labels['newfilterset'] = 'Neuer Filtersatz';
+$labels['active'] = 'aktiv';
+$labels['none'] = 'keine';
+$labels['fromset'] = 'aus Filtersatz';
+$labels['fromfile'] = 'aus Datei';
+$labels['filterdisabled'] = 'Filter deaktiviert';
+$labels['countisgreaterthan'] = 'Anzahl ist grösser als';
+$labels['countisgreaterthanequal'] = 'Anzahl ist gleich oder grösser als';
+$labels['countislessthan'] = 'Anzahl ist kleiner als';
+$labels['countislessthanequal'] = 'Anzahl ist gleich oder kleiner als';
+$labels['countequals'] = 'Anzahl ist gleich';
+$labels['countnotequals'] = 'Anzahl ist ungleich';
+$labels['valueisgreaterthan'] = 'Wert ist grösser als';
+$labels['valueisgreaterthanequal'] = 'Wert ist gleich oder grösser als';
+$labels['valueislessthan'] = 'Wert ist kleiner';
+$labels['valueislessthanequal'] = 'Wert ist gleich oder kleiner als';
+$labels['valueequals'] = 'Wert ist gleich';
+$labels['valuenotequals'] = 'Wert ist ungleich';
+$labels['setflags'] = 'Setze Markierungen';
+$labels['addflags'] = 'Füge Markierung hinzu';
+$labels['removeflags'] = 'Entferne Markierung';
+$labels['flagread'] = 'gelesen';
+$labels['flagdeleted'] = 'Gelöscht';
+$labels['flaganswered'] = 'Beantwortet';
+$labels['flagflagged'] = 'Markiert';
+$labels['flagdraft'] = 'Entwurf';
+$labels['filtercreate'] = 'Filter erstellen';
+$labels['usedata'] = 'Die folgenden Daten im Filter benutzen:';
+$labels['nextstep'] = 'Nächster Schritt';
+$labels['...'] = '';
+$labels['advancedopts'] = 'Erweiterte Optionen';
+$labels['body'] = 'Inhalt';
+$labels['address'] = 'Adresse';
+$labels['envelope'] = 'Umschlag';
+$labels['modifier'] = 'Wandler';
+$labels['text'] = 'Text';
+$labels['undecoded'] = 'kodiert (roh)';
+$labels['contenttype'] = 'Inhaltstyp';
+$labels['modtype'] = 'Typ:';
+$labels['allparts'] = 'alle';
+$labels['domain'] = 'Domain';
+$labels['localpart'] = 'lokaler Teil';
+$labels['user'] = 'Benutzer';
+$labels['detail'] = 'Detail';
+$labels['comparator'] = 'Komparator';
+$labels['default'] = 'Vorgabewert';
+$labels['octet'] = 'strikt (Oktet)';
+$labels['asciicasemap'] = 'Gross-/Kleinschreibung ignorieren';
+$labels['asciinumeric'] = 'numerisch (ascii-numeric)';
 
-$messages['filterunknownerror'] = 'Unbekannter Serverfehler';
-$messages['filterconnerror'] = 'Kann nicht zum Sieve-Server verbinden';
-$messages['filterdeleteerror'] = 'Fehler beim des löschen  Filters. Serverfehler';
-$messages['filterdeleted'] = 'Filter erfolgreich gelöscht';
-$messages['filterdeleteconfirm'] = 'Möchten Sie den Filter löschen ?';
-$messages['filtersaved'] = 'Filter gespeichert';
-$messages['filtersaveerror'] = 'Serverfehler, konnte den Filter nicht speichern.';
-$messages['ruledeleteconfirm'] = 'Sicher, dass Sie die Regel löschen wollen?';
-$messages['actiondeleteconfirm'] = 'Sicher, dass Sie die ausgewaehlte Aktion löschen wollen?';
-$messages['forbiddenchars'] = 'Unerlaubte Zeichen im Feld';
-$messages['cannotbeempty'] = 'Feld darf nicht leer sein';
-
-?>
index e71d7e00d6b9aa904595a5dcbdec65ec8b0f6a4b..d78220c619dcaf6b8cf63bb90cc08088f4c590fd 100644 (file)
@@ -1,9 +1,24 @@
 <?php
 
+/*
+ +-----------------------------------------------------------------------+
+ | localization/de_DE/labels.inc                                         |
+ |                                                                       |
+ | Language file of the Roundcube Webmail client                         |
+ | Copyright (C) 2011, The Roundcube Dev Team                            |
+ | Licensed under the GNU General Public License                         |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Christoph Wickert <Unknown>                                   |
+ +-----------------------------------------------------------------------+
+ @version : importgettext.sh 5558 2011-12-07 08:51:01Z thomasb $
+*/
+
+$labels = array();
 $labels['filters'] = 'Filter';
-$labels['managefilters'] = 'Posteingangs-Filter verwalten';
+$labels['managefilters'] = 'Filter für eingehende Nachrichten verwalten';
 $labels['filtername'] = 'Filtername';
-$labels['newfilter'] = 'Filter anlegen';
+$labels['newfilter'] = 'Neuer Filter';
 $labels['filteradd'] = 'Filter hinzufügen';
 $labels['filterdel'] = 'Filter löschen';
 $labels['moveup'] = 'Nach oben';
@@ -17,17 +32,21 @@ $labels['filteris'] = 'ist gleich';
 $labels['filterisnot'] = 'ist ungleich';
 $labels['filterexists'] = 'existiert';
 $labels['filternotexists'] = 'existiert nicht';
+$labels['filtermatches'] = 'trifft auf Ausdruck zu';
+$labels['filternotmatches'] = 'trifft nicht auf Ausdruck zu';
+$labels['filterregex'] = 'trifft auf regulären Ausdruck zu';
+$labels['filternotregex'] = 'trifft nicht auf regulären Ausdruck zu';
 $labels['filterunder'] = 'unter';
 $labels['filterover'] = 'über';
 $labels['addrule'] = 'Regel hinzufügen';
 $labels['delrule'] = 'Regel löschen';
-$labels['messagemoveto'] = 'Verschiebe Nachricht nach';
-$labels['messageredirect'] = 'Leite Nachricht um an';
-$labels['messagecopyto'] = 'Kopiere Nachricht nach';
-$labels['messagesendcopy'] = 'Sende Kopie an';
-$labels['messagereply'] = 'Antworte mit Nachricht';
-$labels['messagedelete'] = 'Lösche Nachricht';
-$labels['messagediscard'] = 'Weise ab mit Nachricht';
+$labels['messagemoveto'] = 'Nachricht verschieben nach';
+$labels['messageredirect'] = 'Nachricht umleiten an';
+$labels['messagecopyto'] = 'Nachricht kopieren nach';
+$labels['messagesendcopy'] = 'Kopie senden an';
+$labels['messagereply'] = 'Mit Nachricht antworten';
+$labels['messagedelete'] = 'Nachricht löschen';
+$labels['messagediscard'] = 'Abweisen mit Nachricht';
 $labels['messagesrules'] = 'Für eingehende Nachrichten:';
 $labels['messagesactions'] = '...führende folgende Aktionen aus:';
 $labels['add'] = 'Hinzufügen';
@@ -37,13 +56,15 @@ $labels['recipient'] = 'Empfänger';
 $labels['vacationaddresses'] = 'Zusätzliche Liste von E-Mail Empfängern (Komma getrennt):';
 $labels['vacationdays'] = 'Wie oft sollen Nachrichten gesendet werden (in Tagen):';
 $labels['vacationreason'] = 'Nachrichteninhalt (Abwesenheitsgrund):';
+$labels['vacationsubject'] = 'Nachrichtenbetreff';
 $labels['rulestop'] = 'Regelauswertung anhalten';
+$labels['enable'] = 'Aktivieren/Deaktivieren';
 $labels['filterset'] = 'Filtersätze';
+$labels['filtersets'] = 'Filtersätze';
 $labels['filtersetadd'] = 'Filtersatz anlegen';
 $labels['filtersetdel'] = 'Aktuellen Filtersatz löschen';
 $labels['filtersetact'] = 'Aktuellen Filtersatz aktivieren';
 $labels['filtersetdeact'] = 'Aktuellen Filtersatz deaktivieren';
-$labels['filtersetget'] = 'Filtersatz im Textformat herunterladen';
 $labels['filterdef'] = 'Filterdefinition';
 $labels['filtersetname'] = 'Filtersatzname';
 $labels['newfilterset'] = 'Neuer Filtersatz';
@@ -64,29 +85,35 @@ $labels['valueislessthan'] = 'Wert ist kleiner';
 $labels['valueislessthanequal'] = 'Wert ist gleich oder kleiner als';
 $labels['valueequals'] = 'Wert ist gleich';
 $labels['valuenotequals'] = 'Wert ist ungleich';
+$labels['setflags'] = 'Markierung an der Nachricht setzen';
+$labels['addflags'] = 'Markierung zur Nachricht hinzufügen';
+$labels['removeflags'] = 'Markierungen von der Nachricht entfernen';
+$labels['flagread'] = 'Gelesen';
+$labels['flagdeleted'] = 'Gelöscht';
+$labels['flaganswered'] = 'Beantwortet';
+$labels['flagflagged'] = 'Markiert';
+$labels['flagdraft'] = 'Entwurf';
+$labels['filtercreate'] = 'Filter erstellen';
+$labels['usedata'] = 'Die folgenden Daten im Filter benutzen:';
+$labels['nextstep'] = 'Nächster Schritt';
+$labels['...'] = '...';
+$labels['advancedopts'] = 'Erweiterte Optionen';
+$labels['body'] = 'Textkörper';
+$labels['address'] = 'Adresse';
+$labels['envelope'] = 'Umschlag';
+$labels['modifier'] = 'Modifikator:';
+$labels['text'] = 'Text';
+$labels['undecoded'] = 'Nicht dekodiert';
+$labels['contenttype'] = 'Inhaltstyp';
+$labels['modtype'] = 'Typ:';
+$labels['allparts'] = 'Alle';
+$labels['domain'] = 'Domäne';
+$labels['localpart'] = 'lokaler Teil';
+$labels['user'] = 'Benutzer';
+$labels['detail'] = 'Detail';
+$labels['comparator'] = 'Komperator:';
+$labels['default'] = 'Vorgabewert';
+$labels['octet'] = 'strikt (Oktett)';
+$labels['asciicasemap'] = 'Groß-/Kleinschreibung ignorieren';
+$labels['asciinumeric'] = 'numerisch (ascii-numeric)';
 
-$messages = array();
-$messages['filterunknownerror'] = 'Unbekannter Serverfehler';
-$messages['filterconnerror'] = 'Kann keine Verbindung mit Managesieve-Server herstellen';
-$messages['filterdeleteerror'] = 'Fehler beim Löschen des Filters. Serverfehler';
-$messages['filterdeleted'] = 'Filter erfolgreich gelöscht';
-$messages['filtersaved'] = 'Filter erfolgreich gespeichert';
-$messages['filtersaveerror'] = 'Fehler beim Speichern des Filters. Serverfehler';
-$messages['filterdeleteconfirm'] = 'Möchten Sie den ausgewählten Filter wirklich löschen?';
-$messages['ruledeleteconfirm'] = 'Sind Sie sicher, dass Sie die ausgewählte Regel löschen möchten?';
-$messages['actiondeleteconfirm'] = 'Sind Sie sicher, dass Sie die ausgewählte Aktion löschen möchten?';
-$messages['forbiddenchars'] = 'Unzulässige Zeichen im Eingabefeld';
-$messages['cannotbeempty'] = 'Eingabefeld darf nicht leer sein';
-$messages['setactivateerror'] = 'Kann ausgewählten Filtersatz nicht aktivieren. Serverfehler';
-$messages['setdeactivateerror'] = 'Kann ausgewählten Filtersatz nicht deaktivieren. Serverfehler';
-$messages['setdeleteerror'] = 'Kann ausgewählten Filtersatz nicht löschen. Serverfehler';
-$messages['setactivated'] = 'Filtersatz wurde erfolgreich aktiviert';
-$messages['setdeactivated'] = 'Filtersatz wurde erfolgreich deaktiviert';
-$messages['setdeleted'] = 'Filtersatz wurde erfolgreich gelöscht';
-$messages['setdeleteconfirm'] = 'Sind Sie sicher, dass Sie den ausgewählten Filtersatz löschen möchten?';
-$messages['setcreateerror'] = 'Kann Filtersatz nicht erstellen. Serverfehler';
-$messages['setcreated'] = 'Filtersatz wurde erfolgreich erstellt';
-$messages['emptyname'] = 'Kann Filtersatz nicht erstellen. Kein Name vergeben';
-$messages['nametoolong'] = 'Kann Filtersatz nicht erstellen. Name zu lang'
-
-?>
index f08357ed9987922f606795ea379112504b77c6a0..94e0ba60c92381bc4521490ac77a4005de56d6a4 100644 (file)
@@ -43,12 +43,13 @@ $labels['vacationdays'] = 'How often send messages (in days):';
 $labels['vacationreason'] = 'Message body (vacation reason):';
 $labels['vacationsubject'] = 'Message subject:';
 $labels['rulestop'] = 'Stop evaluating rules';
+$labels['enable'] = 'Enable/Disable';
 $labels['filterset'] = 'Filters set';
+$labels['filtersets'] = 'Filter sets';
 $labels['filtersetadd'] = 'Add filters set';
 $labels['filtersetdel'] = 'Delete current filters set';
 $labels['filtersetact'] = 'Activate current filters set';
 $labels['filtersetdeact'] = 'Deactivate current filters set';
-$labels['filtersetget'] = 'Download filters set in text format';
 $labels['filterdef'] = 'Filter definition';
 $labels['filtersetname'] = 'Filters set name';
 $labels['newfilterset'] = 'New filters set';
@@ -77,29 +78,61 @@ $labels['flagdeleted'] = 'Deleted';
 $labels['flaganswered'] = 'Answered';
 $labels['flagflagged'] = 'Flagged';
 $labels['flagdraft'] = 'Draft';
+$labels['filtercreate'] = 'Create filter';
+$labels['usedata'] = 'Use following data in the filter:';
+$labels['nextstep'] = 'Next Step';
+$labels['...'] = '...';
+$labels['advancedopts'] = 'Advanced options';
+$labels['body'] = 'Body';
+$labels['address'] = 'address';
+$labels['envelope'] = 'envelope';
+$labels['modifier'] = 'modifier:';
+$labels['text'] = 'text';
+$labels['undecoded'] = 'undecoded (raw)';
+$labels['contenttype'] = 'content type';
+$labels['modtype'] = 'type:';
+$labels['allparts'] = 'all';
+$labels['domain'] = 'domain';
+$labels['localpart'] = 'local part';
+$labels['user'] = 'user';
+$labels['detail'] = 'detail';
+$labels['comparator'] = 'comparator:';
+$labels['default'] = 'default';
+$labels['octet'] = 'strict (octet)';
+$labels['asciicasemap'] = 'case insensitive (ascii-casemap)';
+$labels['asciinumeric'] = 'numeric (ascii-numeric)';
 
 $messages = array();
-$messages['filterunknownerror'] = 'Unknown server error';
-$messages['filterconnerror'] = 'Unable to connect to managesieve server';
-$messages['filterdeleteerror'] = 'Unable to delete filter. Server error occured';
-$messages['filterdeleted'] = 'Filter deleted successfully';
-$messages['filtersaved'] = 'Filter saved successfully';
-$messages['filtersaveerror'] = 'Unable to save filter. Server error occured';
+$messages['filterunknownerror'] = 'Unknown server error.';
+$messages['filterconnerror'] = 'Unable to connect to server.';
+$messages['filterdeleteerror'] = 'Unable to delete filter. Server error occured.';
+$messages['filterdeleted'] = 'Filter deleted successfully.';
+$messages['filtersaved'] = 'Filter saved successfully.';
+$messages['filtersaveerror'] = 'Unable to save filter. Server error occured.';
 $messages['filterdeleteconfirm'] = 'Do you really want to delete selected filter?';
 $messages['ruledeleteconfirm'] = 'Are you sure, you want to delete selected rule?';
 $messages['actiondeleteconfirm'] = 'Are you sure, you want to delete selected action?';
-$messages['forbiddenchars'] = 'Forbidden characters in field';
-$messages['cannotbeempty'] = 'Field cannot be empty';
-$messages['setactivateerror'] = 'Unable to activate selected filters set. Server error occured';
-$messages['setdeactivateerror'] = 'Unable to deactivate selected filters set. Server error occured';
-$messages['setdeleteerror'] = 'Unable to delete selected filters set. Server error occured';
-$messages['setactivated'] = 'Filters set activated successfully';
-$messages['setdeactivated'] = 'Filters set deactivated successfully';
-$messages['setdeleted'] = 'Filters set deleted successfully';
+$messages['forbiddenchars'] = 'Forbidden characters in field.';
+$messages['cannotbeempty'] = 'Field cannot be empty.';
+$messages['ruleexist'] = 'Filter with specified name already exists.';
+$messages['setactivateerror'] = 'Unable to activate selected filters set. Server error occured.';
+$messages['setdeactivateerror'] = 'Unable to deactivate selected filters set. Server error occured.';
+$messages['setdeleteerror'] = 'Unable to delete selected filters set. Server error occured.';
+$messages['setactivated'] = 'Filters set activated successfully.';
+$messages['setdeactivated'] = 'Filters set deactivated successfully.';
+$messages['setdeleted'] = 'Filters set deleted successfully.';
 $messages['setdeleteconfirm'] = 'Are you sure, you want to delete selected filters set?';
-$messages['setcreateerror'] = 'Unable to create filters set. Server error occured';
-$messages['setcreated'] = 'Filters set created successfully';
-$messages['emptyname'] = 'Unable to create filters set. Empty set name';
-$messages['nametoolong'] = 'Unable to create filters set. Name too long'
+$messages['setcreateerror'] = 'Unable to create filters set. Server error occured.';
+$messages['setcreated'] = 'Filters set created successfully.';
+$messages['activateerror'] = 'Unable to enable selected filter(s). Server error occured.';
+$messages['deactivateerror'] = 'Unable to disable selected filter(s). Server error occured.';
+$messages['activated'] = 'Filter(s) disabled successfully.';
+$messages['deactivated'] = 'Filter(s) enabled successfully.';
+$messages['moved'] = 'Filter moved successfully.';
+$messages['moveerror'] = 'Unable to move selected filter. Server error occured.';
+$messages['nametoolong'] = 'Name too long.';
+$messages['namereserved'] = 'Reserved name.';
+$messages['setexist'] = 'Set already exists.';
+$messages['nodata'] = 'At least one position must be selected!';
 
 ?>
index 1dad18d991f7e58eb5072e575b60713bbd9c48d7..b6dbf166b0f871da9334b4be5af381c750919257 100644 (file)
@@ -53,6 +53,31 @@ $labels['none'] = 'ninguno';
 $labels['fromset'] = 'de conjunto ';
 $labels['fromfile'] = 'de archivo';
 $labels['filterdisabled'] = 'Filtro desactivado';
+$labels['filtermatches'] = 'coincide con la expresión';
+$labels['filternotmatches'] = 'no coincide con la expresión';
+$labels['filterregex'] = 'coincide con la expresión regular';
+$labels['filternotregex'] = 'no coincide con la expresión regular';
+$labels['vacationsubject'] = 'Asunto del Mensaje:';
+$labels['countisgreaterthan'] = 'contiene más que';
+$labels['countisgreaterthanequal'] = 'contiene más o igual que';
+$labels['countislessthan'] = 'contiene menos que';
+$labels['countislessthanequal'] = 'contiene menos o igual que';
+$labels['countequals'] = 'contiene igual que';
+$labels['countnotequals'] = 'contiene distinto que';
+$labels['valueisgreaterthan'] = 'el valor es mayor que';
+$labels['valueisgreaterthanequal'] = 'el valor es mayor o igual que';
+$labels['valueislessthan'] = 'el valor es menor que';
+$labels['valueislessthanequal'] = 'el valor es menor o igual que';
+$labels['valueequals'] = 'el valor es igual que';
+$labels['valuenotequals'] = 'el valor es distinto que';
+$labels['setflags'] = 'Etiquetar el mensaje';
+$labels['addflags'] = 'Agregar etiqueta al mensaje';
+$labels['removeflags'] = 'Eliminar etiquetas al mensaje';
+$labels['flagread'] = 'Leido';
+$labels['flagdeleted'] = 'Eliminado';
+$labels['flaganswered'] = 'Respondido';
+$labels['flagflagged'] = 'Marcado';
+$labels['flagdraft'] = 'Borrador';
 
 $messages = array();
 $messages['filterunknownerror'] = 'Error desconocido de servidor';
@@ -76,6 +101,7 @@ $messages['setdeleteconfirm'] = '¿Está seguro de que desea borrar el conjunto
 $messages['setcreateerror'] = 'Imposible crear el conjunto de filtros. Ha ocurrido un error en el servidor';
 $messages['setcreated'] = 'Conjunto de filtros creado satisfactoriamente';
 $messages['emptyname'] = 'Imposible crear el conjunto de filtros. Sin nombre';
-$messages['nametoolong'] = 'Imposible crear el conjunto de filtros. Nombre demasiado largo'
+$messages['nametoolong'] = 'Imposible crear el conjunto de filtros. Nombre demasiado largo';
+$messages['setdeactivateerror'] = 'Imposible desactivar el conjunto de filtros seleccionado. Ha ocurrido un error en el servidor';
 
 ?>
diff --git a/plugins/managesieve/localization/lv_LV.inc b/plugins/managesieve/localization/lv_LV.inc
new file mode 100644 (file)
index 0000000..b394588
--- /dev/null
@@ -0,0 +1,92 @@
+<?php
+
+$labels['filters'] = 'Vēstuļu filtri';
+$labels['managefilters'] = 'Rediģēt ienākošo vēstuļu filtrus';
+$labels['filtername'] = 'Filtra nosaukums';
+$labels['newfilter'] = 'Jauns filtrs';
+$labels['filteradd'] = 'Pievienot filtru';
+$labels['filterdel'] = 'Izdzēst filtru';
+$labels['moveup'] = 'Pārvietot augšup';
+$labels['movedown'] = 'Pārvietot lejup';
+$labels['filterallof'] = 'jāatbilst visiem no sekojošiem nosacījumiem';
+$labels['filteranyof'] = 'jāatbilst jebkuram no sekojošiem nosacījumiem';
+$labels['filterany'] = 'visām vēstulēm';
+$labels['filtercontains'] = 'satur';
+$labels['filternotcontains'] = 'nesatur';
+$labels['filteris'] = 'ir vienāds ar';
+$labels['filterisnot'] = 'nav vienāds ar';
+$labels['filterexists'] = 'eksistē';
+$labels['filternotexists'] = 'neeksistē';
+$labels['filterunder'] = 'zem';
+$labels['filterover'] = 'virs';
+$labels['addrule'] = 'Pievienot nosacījumu';
+$labels['delrule'] = 'Izdzēst nosacījumu';
+$labels['messagemoveto'] = 'Pārvietot vēstuli uz';
+$labels['messageredirect'] = 'Pāradresēt vēstuli uz';
+$labels['messagecopyto'] = 'Pārkopēt vēstuli uz';
+$labels['messagesendcopy'] = 'Pārsūtīt vēstules kopiju uz';
+$labels['messagereply'] = 'Automātiski atbildēt';
+$labels['messagedelete'] = 'Izdzēst vēstuli';
+$labels['messagediscard'] = 'Izdzēst vēstuli un atbildēt';
+$labels['messagesrules'] = 'Ienākošajām vēstulēm:';
+$labels['messagesactions'] = 'Izpildīt sekojošās darbības:';
+$labels['add'] = 'Pievienot';
+$labels['del'] = 'Dzēst';
+$labels['sender'] = 'Sūtītājs';
+$labels['recipient'] = 'Saņēmējs';
+$labels['vacationaddresses'] = 'Ievadiet savu(s) e-pastu(s) caur komatu:';
+$labels['vacationdays'] = 'Cik dienu laikā vienam un tam pašam sūtītājam neatbildēt atkārtoti (piem., 7):';
+$labels['vacationreason'] = 'Atvaļinājuma vēstules teksts:';
+$labels['rulestop'] = 'Apturēt nosacījumu pārbaudi';
+$labels['filterset'] = 'Filtru kopa';
+$labels['filtersetadd'] = 'Pievienot filtru kopu';
+$labels['filtersetdel'] = 'Izdzēst pašreizējo filtru kopu';
+$labels['filtersetact'] = 'Aktivizēt pašreizējo filtru kopu';
+$labels['filtersetdeact'] = 'Deaktivizēt pašreizējo filtru kopu';
+$labels['filtersetget'] = 'Lejupielādēt filtru kopu teksta formātā';
+$labels['filterdef'] = 'Filtra apraksts';
+$labels['filtersetname'] = 'Filtru kopas nosaukums';
+$labels['newfilterset'] = 'Jauna filtru kopa';
+$labels['active'] = 'aktīvs';
+$labels['none'] = 'nav';
+$labels['fromset'] = 'no kopas';
+$labels['fromfile'] = 'no faila';
+$labels['filterdisabled'] = 'Filtrs atslēgts';
+$labels['countisgreaterthan'] = 'skaits ir lielāks nekā';
+$labels['countisgreaterthanequal'] = 'skaits ir vienāds vai lielāks nekā';
+$labels['countislessthan'] = 'skaits ir mazāks nekā';
+$labels['countislessthanequal'] = 'skaits ir vienāds vai mazāks nekā';
+$labels['countequals'] = 'skaits ir vienāds ar';
+$labels['countnotequals'] = 'skaits nav vienāds ar';
+$labels['valueisgreaterthan'] = 'vērtība ir lielāka nekā';
+$labels['valueisgreaterthanequal'] = 'vērtība ir vienāda vai lielāka nekā';
+$labels['valueislessthan'] = 'vērtība ir mazāka nekā';
+$labels['valueislessthanequal'] = 'vērtība ir vienāda vai mazāka nekā';
+$labels['valueequals'] = 'vērtība ir vienāda ar';
+$labels['valuenotequals'] = 'vērtība nav vienāda ar';
+
+$messages = array();
+$messages['filterunknownerror'] = 'Nezināma servera kļūda';
+$messages['filterconnerror'] = 'Neizdevās pieslēgties ManageSieve serverim';
+$messages['filterdeleteerror'] = 'Neizdevās dzēst filtru. Servera iekšējā kļūda';
+$messages['filterdeleted'] = 'Filtrs veiksmīgi izdzēsts';
+$messages['filtersaved'] = 'Filtrs veiksmīgi saglabāts';
+$messages['filtersaveerror'] = 'Neizdevās saglabāt filtru. Servera iekšējā kļūda';
+$messages['filterdeleteconfirm'] = 'Vai tiešām vēlaties dzēst atzīmēto filtru?';
+$messages['ruledeleteconfirm'] = 'Vai tiešām vēlaties dzēst atzīmēto nosacījumu?';
+$messages['actiondeleteconfirm'] = 'Vai tiešām vēlaties dzēst atzīmēto darbību?';
+$messages['forbiddenchars'] = 'Lauks satur aizliegtus simbolus';
+$messages['cannotbeempty'] = 'Lauks nedrīkst būt tukšs';
+$messages['setactivateerror'] = 'Neizdevās aktivizēt atzīmēto filtru kopu. Servera iekšējā kļūda';
+$messages['setdeactivateerror'] = 'Neizdevās deaktivizēt atzīmēto filtru kopu. Servera iekšējā kļūda';
+$messages['setdeleteerror'] = 'Neizdevās izdzēst atzīmēto filtru kopu. Servera iekšējā kļūda';
+$messages['setactivated'] = 'Filtru kopa veiksmīgi aktivizēta';
+$messages['setdeactivated'] = 'Filtru kopa veiksmīgi deaktivizēta';
+$messages['setdeleted'] = 'Filtru kopa veiksmīgi izdzēsta';
+$messages['setdeleteconfirm'] = 'Vai tiešām vēlaties dzēst atzīmēto filtru kopu?';
+$messages['setcreateerror'] = 'Neizdevās izveidot filtru kopu. Servera iekšējā kļūda';
+$messages['setcreated'] = 'Filtru kopa veiksmīgi izveidota';
+$messages['emptyname'] = 'Neizdevās izveidot filtru kopu. Nav ievadīts kopas nosaukums';
+$messages['nametoolong'] = 'Neizdevās izveidot filtru kopu. Pārāk garš kopas nosaukums'
+
+?>
index 290dd1a46042660bcbf40a7495df2301c86f4849..c0ec2ede2bb2aae70f4cb6d8d8fc25de8ed1ecc0 100644 (file)
@@ -7,8 +7,8 @@ $labels['filtername'] = 'Nazwa filtru';
 $labels['newfilter'] = 'Nowy filtr';
 $labels['filteradd'] = 'Dodaj filtr';
 $labels['filterdel'] = 'Usuń filtr';
-$labels['moveup'] = 'Przenieś wyżej';
-$labels['movedown'] = 'Przenieś niżej';
+$labels['enable'] = 'Włącz/Wyłącz';
+$labels['filtersets'] = 'Zbiory fitrów';
 $labels['filterallof'] = 'spełniających wszystkie poniższe kryteria';
 $labels['filteranyof'] = 'spełniających dowolne z poniższych kryteriów';
 $labels['filterany'] = 'wszystkich';
@@ -49,7 +49,6 @@ $labels['filtersetadd'] = 'Dodaj zbiór filtrów';
 $labels['filtersetdel'] = 'Usuń bieżący zbiór filtrów';
 $labels['filtersetact'] = 'Aktywuj bieżący zbiór filtrów';
 $labels['filtersetdeact'] = 'Deaktywuj bieżący zbiór filtrów';
-$labels['filtersetget'] = 'Pobierz bieżący zbiór filtrów w formacie tekstowym';
 $labels['filterdef'] = 'Definicja filtra';
 $labels['filtersetname'] = 'Nazwa zbioru';
 $labels['newfilterset'] = 'Nowy zbiór filtrów';
@@ -78,29 +77,61 @@ $labels['flagdeleted'] = 'Usunięta';
 $labels['flaganswered'] = 'Z odpowiedzią';
 $labels['flagflagged'] = 'Oflagowana';
 $labels['flagdraft'] = 'Szkic';
+$labels['filtercreate'] = 'Utwóż filtr';
+$labels['usedata'] = 'Użyj następujących danych do utworzenia filtra:';
+$labels['nextstep'] = 'Następny krok';
+$labels['...'] = '...';
+$labels['advancedopts'] = 'Zaawansowane opcje';
+$labels['body'] = 'Treść';
+$labels['address'] = 'adres';
+$labels['envelope'] = 'koperta (envelope)';
+$labels['modifier'] = 'modyfikator:';
+$labels['text'] = 'tekst';
+$labels['undecoded'] = 'nie (raw)';
+$labels['contenttype'] = 'typ części (content type)';
+$labels['modtype'] = 'typ:';
+$labels['allparts'] = 'wszystkie';
+$labels['domain'] = 'domena';
+$labels['localpart'] = 'część lokalna';
+$labels['user'] = 'użytkownik';
+$labels['detail'] = 'detal';
+$labels['comparator'] = 'komparator:';
+$labels['default'] = 'domyślny';
+$labels['octet'] = 'dokładny (octet)';
+$labels['asciicasemap'] = 'nierozróżniający wielkości liter (ascii-casemap)';
+$labels['asciinumeric'] = 'numeryczny (ascii-numeric)';
 
 $messages = array();
-$messages['filterunknownerror'] = 'Nieznany błąd serwera';
-$messages['filterconnerror'] = 'Nie można nawiązać połączenia z serwerem managesieve';
-$messages['filterdeleteerror'] = 'Nie można usunąć filtra. Wystąpił błąd serwera';
-$messages['filterdeleted'] = 'Filtr został usunięty pomyślnie';
+$messages['filterunknownerror'] = 'Nieznany błąd serwera.';
+$messages['filterconnerror'] = 'Nie można nawiązać połączenia z serwerem.';
+$messages['filterdeleteerror'] = 'Nie można usunąć filtra. Błąd serwera.';
+$messages['filterdeleted'] = 'Filtr został usunięty pomyślnie.';
 $messages['filterdeleteconfirm'] = 'Czy na pewno chcesz usunąć wybrany filtr?';
-$messages['filtersaved'] = 'Filtr został zapisany pomyślnie';
+$messages['filtersaved'] = 'Filtr został zapisany pomyślnie.';
 $messages['filtersaveerror'] = 'Nie można zapisać filtra. Wystąpił błąd serwera.';
 $messages['ruledeleteconfirm'] = 'Czy na pewno chcesz usunąć wybraną regułę?';
 $messages['actiondeleteconfirm'] = 'Czy na pewno usunąć wybraną akcję?';
-$messages['forbiddenchars'] = 'Pole zawiera niedozwolone znaki';
-$messages['cannotbeempty'] = 'Pole nie może być puste';
-$messages['setactivateerror'] = 'Nie można aktywować wybranego zbioru filtrów. Błąd serwera';
-$messages['setdeactivateerror'] = 'Nie można deaktywować wybranego zbioru filtrów. Błąd serwera';
-$messages['setdeleteerror'] = 'Nie można usunąć wybranego zbioru filtrów. Błąd serwera';
-$messages['setactivated'] = 'Zbiór filtrów został aktywowany pomyślnie';
-$messages['setdeactivated'] = 'Zbiór filtrów został deaktywowany pomyślnie';
-$messages['setdeleted'] = 'Zbiór filtrów został usunięty pomyślnie';
+$messages['forbiddenchars'] = 'Pole zawiera niedozwolone znaki.';
+$messages['cannotbeempty'] = 'Pole nie może być puste.';
+$messages['setactivateerror'] = 'Nie można aktywować wybranego zbioru filtrów. Błąd serwera.';
+$messages['setdeactivateerror'] = 'Nie można deaktywować wybranego zbioru filtrów. Błąd serwera.';
+$messages['setdeleteerror'] = 'Nie można usunąć wybranego zbioru filtrów. Błąd serwera.';
+$messages['setactivated'] = 'Zbiór filtrów został aktywowany pomyślnie.';
+$messages['setdeactivated'] = 'Zbiór filtrów został deaktywowany pomyślnie.';
+$messages['setdeleted'] = 'Zbiór filtrów został usunięty pomyślnie.';
 $messages['setdeleteconfirm'] = 'Czy na pewno chcesz usunąć wybrany zbiór filtrów?';
-$messages['setcreateerror'] = 'Nie można utworzyć zbioru filtrów. Błąd serwera';
-$messages['setcreated'] = 'Zbiór filtrów został utworzony pomyślnie';
-$messages['emptyname'] = 'Nie można utworzyć zbioru filtrów. Pusta nazwa zbioru';
-$messages['nametoolong'] = 'Nie można utworzyć zbioru filtrów. Nazwa zbyt długa'
+$messages['setcreateerror'] = 'Nie można utworzyć zbioru filtrów. Błąd serwera.';
+$messages['setcreated'] = 'Zbiór filtrów został utworzony pomyślnie.';
+$messages['nodata'] = 'Należy wybrać co najmniej jedną pozycję!';
+$messages['ruleexist'] = 'Filtr o podanej nazwie już istnieje.';
+$messages['activateerror'] = 'Nie można włączyć wybranych filtrów. Błąd serwera.';
+$messages['deactivateerror'] = 'Nie można wyłączyć wybranych filtrów. Błąd serwera.';
+$messages['activated'] = 'Filtr(y) wyłączono pomyślnie.';
+$messages['deactivated'] = 'Filtr(y) włączono pomyślnie.';
+$messages['moved'] = 'Filter został przeniesiony pomyślnie.';
+$messages['moveerror'] = 'Nie można przenieść wybranego filtra. Błąd serwera.';
+$messages['nametoolong'] = 'Zbyt długa nazwa.';
+$messages['namereserved'] = 'Nazwa zarezerwowana.';
+$messages['setexist'] = 'Zbiór już istnieje.';
 
 ?>
index b48774e97ee62f82f05e5afa8d31eb40e07e4dc4..af3f05add501ebbcdd107a0091d4c0671ebba225 100644 (file)
@@ -32,7 +32,7 @@ $labels['add'] = 'Adicionar';
 $labels['del'] = 'Excluir';
 $labels['sender'] = 'Remetente';
 $labels['recipient'] = 'Destinatário';
-$labels['vacationaddresses'] = 'Lista adicional de e-mails de remetente (separado por vírgula):';
+$labels['vacationaddresses'] = 'Lista adicional de e-mails destinatários (separado por vírgula):';
 $labels['vacationdays'] = 'Enviar mensagens com que frequência (em dias):';
 $labels['vacationreason'] = 'Corpo da mensagem (motivo de férias):';
 $labels['rulestop'] = 'Parar de avaliar regras';
index ec6247aff8c203a12d99bf146a7316390fe3d630..a8bfaf203c5047a832b8febf4c206abbad23ea9d 100644 (file)
@@ -1,56 +1,97 @@
-/* Sieve Filters (tab) */
+/* (Manage)Sieve Filters */
 
 if (window.rcmail) {
   rcmail.addEventListener('init', function(evt) {
+    // add managesieve-create command to message_commands array,
+    // so it's state will be updated on message selection/unselection
+    if (rcmail.env.task == 'mail') {
+      if (rcmail.env.action != 'show')
+        rcmail.env.message_commands.push('managesieve-create');
+      else
+        rcmail.enable_command('managesieve-create', true);
+    }
+    else {
+      var tab = $('<span>').attr('id', 'settingstabpluginmanagesieve').addClass('tablink'),
+        button = $('<a>').attr('href', rcmail.env.comm_path+'&_action=plugin.managesieve')
+          .attr('title', rcmail.gettext('managesieve.managefilters'))
+          .html(rcmail.gettext('managesieve.filters'))
+          .appendTo(tab);
+
+      // add tab
+      rcmail.add_element(tab, 'tabs');
+    }
+
+    if (rcmail.env.task == 'mail' || rcmail.env.action.indexOf('plugin.managesieve') != -1) {
+      // Create layer for form tips
+      if (!rcmail.env.framed) {
+        rcmail.env.ms_tip_layer = $('<div id="managesieve-tip" class="popupmenu"></div>');
+        rcmail.env.ms_tip_layer.appendTo(document.body);
+      }
+    }
 
-    var tab = $('<span>').attr('id', 'settingstabpluginmanagesieve').addClass('tablink');
-    var button = $('<a>').attr('href', rcmail.env.comm_path+'&_action=plugin.managesieve')
-      .attr('title', rcmail.gettext('managesieve.managefilters'))
-      .html(rcmail.gettext('managesieve.filters'))
-      .appendTo(tab);
-
-    // add button and register commands
-    rcmail.add_element(tab, 'tabs');
-    rcmail.register_command('plugin.managesieve-save', function() { rcmail.managesieve_save() }, true);
-    rcmail.register_command('plugin.managesieve-add', function() { rcmail.managesieve_add() }, true);
-    rcmail.register_command('plugin.managesieve-del', function() { rcmail.managesieve_del() }, true);
-    rcmail.register_command('plugin.managesieve-up', function() { rcmail.managesieve_up() }, true);
-    rcmail.register_command('plugin.managesieve-down', function() { rcmail.managesieve_down() }, true);
-    rcmail.register_command('plugin.managesieve-set', function() { rcmail.managesieve_set() }, true);
-    rcmail.register_command('plugin.managesieve-setadd', function() { rcmail.managesieve_setadd() }, true);
-    rcmail.register_command('plugin.managesieve-setdel', function() { rcmail.managesieve_setdel() }, true);
-    rcmail.register_command('plugin.managesieve-setact', function() { rcmail.managesieve_setact() }, true);
-    rcmail.register_command('plugin.managesieve-setget', function() { rcmail.managesieve_setget() }, true);
-
-    if (rcmail.env.action == 'plugin.managesieve') {
+    // register commands
+    rcmail.register_command('plugin.managesieve-save', function() { rcmail.managesieve_save() });
+    rcmail.register_command('plugin.managesieve-act', function() { rcmail.managesieve_act() });
+    rcmail.register_command('plugin.managesieve-add', function() { rcmail.managesieve_add() });
+    rcmail.register_command('plugin.managesieve-del', function() { rcmail.managesieve_del() });
+    rcmail.register_command('plugin.managesieve-move', function() { rcmail.managesieve_move() });
+    rcmail.register_command('plugin.managesieve-setadd', function() { rcmail.managesieve_setadd() });
+    rcmail.register_command('plugin.managesieve-setdel', function() { rcmail.managesieve_setdel() });
+    rcmail.register_command('plugin.managesieve-setact', function() { rcmail.managesieve_setact() });
+    rcmail.register_command('plugin.managesieve-setget', function() { rcmail.managesieve_setget() });
+
+    if (rcmail.env.action == 'plugin.managesieve' || rcmail.env.action == 'plugin.managesieve-save') {
       if (rcmail.gui_objects.sieveform) {
         rcmail.enable_command('plugin.managesieve-save', true);
+
+        // small resize for header element
+        $('select[name="_header[]"]', rcmail.gui_objects.sieveform).each(function() {
+          if (this.value == '...') this.style.width = '40px';
+        });
+
+        // resize dialog window
+        if (rcmail.env.action == 'plugin.managesieve' && rcmail.env.task == 'mail') {
+          parent.rcmail.managesieve_dialog_resize(rcmail.gui_objects.sieveform);
+        }
+
+        $('input[type="text"]:first', rcmail.gui_objects.sieveform).focus();
       }
       else {
-        rcmail.enable_command('plugin.managesieve-del', 'plugin.managesieve-up',
-          'plugin.managesieve-down', false);
         rcmail.enable_command('plugin.managesieve-add', 'plugin.managesieve-setadd', !rcmail.env.sieveconnerror);
       }
 
-      // Create layer for form tips
-      if (!rcmail.env.framed) {
-        rcmail.env.ms_tip_layer = $('<div id="managesieve-tip" class="popupmenu"></div>');
-        rcmail.env.ms_tip_layer.appendTo(document.body);
-      }
+      var i, p = rcmail, setcnt, set = rcmail.env.currentset;
 
       if (rcmail.gui_objects.filterslist) {
-        var p = rcmail;
-        rcmail.filters_list = new rcube_list_widget(rcmail.gui_objects.filterslist, {multiselect:false, draggable:false, keyboard:false});
+        rcmail.filters_list = new rcube_list_widget(rcmail.gui_objects.filterslist,
+          {multiselect:false, draggable:true, keyboard:false});
         rcmail.filters_list.addEventListener('select', function(o){ p.managesieve_select(o); });
+        rcmail.filters_list.addEventListener('dragstart', function(o){ p.managesieve_dragstart(o); });
+        rcmail.filters_list.addEventListener('dragend', function(e){ p.managesieve_dragend(e); });
+        rcmail.filters_list.row_init = function (row) {
+          row.obj.onmouseover = function() { p.managesieve_focus_filter(row); };
+          row.obj.onmouseout = function() { p.managesieve_unfocus_filter(row); };
+        };
         rcmail.filters_list.init();
         rcmail.filters_list.focus();
+      }
 
-        rcmail.enable_command('plugin.managesieve-set', true);
-        rcmail.enable_command('plugin.managesieve-setact', 'plugin.managesieve-setget', rcmail.gui_objects.filtersetslist.length);
-        rcmail.enable_command('plugin.managesieve-setdel', rcmail.gui_objects.filtersetslist.length > 1);
+      if (rcmail.gui_objects.filtersetslist) {
+        rcmail.filtersets_list = new rcube_list_widget(rcmail.gui_objects.filtersetslist, {multiselect:false, draggable:false, keyboard:false});
+        rcmail.filtersets_list.addEventListener('select', function(o){ p.managesieve_setselect(o); });
+        rcmail.filtersets_list.init();
+        rcmail.filtersets_list.focus();
 
-        $('#'+rcmail.buttons['plugin.managesieve-setact'][0].id).attr('title', rcmail.gettext('managesieve.filterset'
-          + (rcmail.gui_objects.filtersetslist.value == rcmail.env.active_set ? 'deact' : 'act')));
+        if (set != null) {
+          set = rcmail.managesieve_setid(set);
+          rcmail.filtersets_list.shift_start = set;
+          rcmail.filtersets_list.highlight_row(set, false);
+        }
+
+        setcnt = rcmail.filtersets_list.rowcount;
+        rcmail.enable_command('plugin.managesieve-set', true);
+        rcmail.enable_command('plugin.managesieve-setact', 'plugin.managesieve-setget', setcnt);
+        rcmail.enable_command('plugin.managesieve-setdel', setcnt > 1);
       }
     }
     if (rcmail.gui_objects.sieveform && rcmail.env.rule_disabled)
@@ -59,7 +100,7 @@ if (window.rcmail) {
 };
 
 /*********************************************************/
-/*********     Managesieve filters methods       *********/
+/*********       Managesieve UI methods          *********/
 /*********************************************************/
 
 rcube_webmail.prototype.managesieve_add = function()
@@ -71,23 +112,41 @@ rcube_webmail.prototype.managesieve_add = function()
 rcube_webmail.prototype.managesieve_del = function()
 {
   var id = this.filters_list.get_single_selection();
-  if (confirm(this.get_label('managesieve.filterdeleteconfirm')))
-    this.http_request('plugin.managesieve',
-      '_act=delete&_fid='+this.filters_list.rows[id].uid, true);
+  if (confirm(this.get_label('managesieve.filterdeleteconfirm'))) {
+    var lock = this.set_busy(true, 'loading');
+    this.http_post('plugin.managesieve',
+      '_act=delete&_fid='+this.filters_list.rows[id].uid, lock);
+  }
 };
 
-rcube_webmail.prototype.managesieve_up = function()
+rcube_webmail.prototype.managesieve_act = function()
 {
-  var id = this.filters_list.get_single_selection();
-  this.http_request('plugin.managesieve',
-    '_act=up&_fid='+this.filters_list.rows[id].uid, true);
+  var id = this.filters_list.get_single_selection(),
+    lock = this.set_busy(true, 'loading');
+
+  this.http_post('plugin.managesieve',
+    '_act=act&_fid='+this.filters_list.rows[id].uid, lock);
 };
 
-rcube_webmail.prototype.managesieve_down = function()
+// Filter selection
+rcube_webmail.prototype.managesieve_select = function(list)
 {
-  var id = this.filters_list.get_single_selection();
-  this.http_request('plugin.managesieve',
-    '_act=down&_fid='+this.filters_list.rows[id].uid, true);
+  var id = list.get_single_selection();
+  if (id != null)
+    this.load_managesieveframe(list.rows[id].uid);
+};
+
+// Set selection
+rcube_webmail.prototype.managesieve_setselect = function(list)
+{
+  this.show_contentframe(false);
+  this.filters_list.clear(true);
+  this.enable_command('plugin.managesieve-setdel', list.rowcount > 1);
+  this.enable_command( 'plugin.managesieve-setact', 'plugin.managesieve-setget', true);
+
+  var id = list.get_single_selection();
+  if (id != null)
+    this.managesieve_list(this.env.filtersets[id]);
 };
 
 rcube_webmail.prototype.managesieve_rowid = function(id)
@@ -99,194 +158,275 @@ rcube_webmail.prototype.managesieve_rowid = function(id)
       return i;
 };
 
-rcube_webmail.prototype.managesieve_updatelist = function(action, name, id, disabled)
+// Returns set's identifier
+rcube_webmail.prototype.managesieve_setid = function(name)
+{
+  for (var i in this.env.filtersets)
+    if (this.env.filtersets[i] == name)
+      return i;
+};
+
+// Filters listing request
+rcube_webmail.prototype.managesieve_list = function(script)
+{
+  var lock = this.set_busy(true, 'loading');
+
+  this.http_post('plugin.managesieve', '_act=list&_set='+urlencode(script), lock);
+};
+
+// Script download request
+rcube_webmail.prototype.managesieve_setget = function()
+{
+  var id = this.filtersets_list.get_single_selection(),
+    script = this.env.filtersets[id];
+
+  location.href = this.env.comm_path+'&_action=plugin.managesieve&_act=setget&_set='+urlencode(script);
+};
+
+// Set activate/deactivate request
+rcube_webmail.prototype.managesieve_setact = function()
+{
+  var id = this.filtersets_list.get_single_selection(),
+   lock = this.set_busy(true, 'loading'),
+    script = this.env.filtersets[id],
+    action = $('#rcmrow'+id).hasClass('disabled') ? 'setact' : 'deact';
+
+  this.http_post('plugin.managesieve', '_act='+action+'&_set='+urlencode(script), lock);
+};
+
+// Set delete request
+rcube_webmail.prototype.managesieve_setdel = function()
+{
+  if (!confirm(this.get_label('managesieve.setdeleteconfirm')))
+    return false;
+
+  var id = this.filtersets_list.get_single_selection(),
+    lock = this.set_busy(true, 'loading'),
+    script = this.env.filtersets[id];
+
+  this.http_post('plugin.managesieve', '_act=setdel&_set='+urlencode(script), lock);
+};
+
+// Set add request
+rcube_webmail.prototype.managesieve_setadd = function()
+{
+  this.filters_list.clear_selection();
+  this.enable_command('plugin.managesieve-act', 'plugin.managesieve-del', false);
+
+  if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
+    var lock = this.set_busy(true, 'loading');
+    target = window.frames[this.env.contentframe];
+    target.location.href = this.env.comm_path+'&_action=plugin.managesieve&_framed=1&_newset=1&_unlock='+lock;
+  }
+};
+
+rcube_webmail.prototype.managesieve_updatelist = function(action, o)
 {
   this.set_busy(true);
 
   switch (action) {
-    case 'delete':
-      this.filters_list.remove_row(this.managesieve_rowid(id));
-      this.filters_list.clear_selection();
-      this.enable_command('plugin.managesieve-del', 'plugin.managesieve-up', 'plugin.managesieve-down', false);
+
+    // Delete filter row
+    case 'del':
+      var i, list = this.filters_list, rows = list.rows;
+
+      list.remove_row(this.managesieve_rowid(o.id));
+      list.clear_selection();
       this.show_contentframe(false);
+      this.enable_command('plugin.managesieve-del', 'plugin.managesieve-act', false);
 
       // re-numbering filters
-      var i, rows = this.filters_list.rows;
       for (i=0; i<rows.length; i++) {
-        if (rows[i] != null && rows[i].uid > id)
+        if (rows[i] != null && rows[i].uid > o.id)
           rows[i].uid = rows[i].uid-1;
       }
+
       break;
 
-    case 'down':
-      var from, fromstatus, status, rows = this.filters_list.rows;
+    // Update filter row
+    case 'update':
+      var i, row = $('#rcmrow'+o.id);
+
+      if (o.name)
+        $('td', row).html(o.name);
+      if (o.disabled)
+        row.addClass('disabled');
+      else
+        row.removeClass('disabled');
+
+      $('#disabled', $('iframe').contents()).prop('checked', o.disabled);
 
-      // we need only to replace filter names...
-      for (var i=0; i<rows.length; i++) {
-        if (rows[i]==null) { // removed row
-          continue;
-        }
-        else if (rows[i].uid == id) {
-          from = rows[i].obj;
-          fromstatus = $(from).hasClass('disabled');
-        }
-        else if (rows[i].uid == id+1) {
-          name = rows[i].obj.cells[0].innerHTML;
-          status = $(rows[i].obj).hasClass('disabled');
-          rows[i].obj.cells[0].innerHTML = from.cells[0].innerHTML;
-          from.cells[0].innerHTML = name;
-          $(from)[status?'addClass':'removeClass']('disabled');
-          $(rows[i].obj)[fromstatus?'addClass':'removeClass']('disabled');
-          this.filters_list.highlight_row(i);
-          break;
-        }
-      }
-      // ... and disable/enable Down button
-      this.filters_listbuttons();
       break;
 
-    case 'up':
-      var from, status, fromstatus, rows = this.filters_list.rows;
+    // Add filter row to the list
+    case 'add':
+      var list = this.filters_list,
+        row = $('<tr><td class="name"></td></tr>');
+
+      $('td', row).html(o.name);
+      row.attr('id', 'rcmrow'+o.id);
+      if (o.disabled)
+        row.addClass('disabled');
+
+      list.insert_row(row.get(0));
+      list.highlight_row(o.id);
+
+      this.enable_command('plugin.managesieve-del', 'plugin.managesieve-act', true);
 
-      // we need only to replace filter names...
-      for (var i=0; i<rows.length; i++) {
-        if (rows[i] == null) { // removed row
-          continue;
-        }
-        else if (rows[i].uid == id-1) {
-          from = rows[i].obj;
-          fromstatus = $(from).hasClass('disabled');
-          this.filters_list.highlight_row(i);
-        }
-        else if (rows[i].uid == id) {
-          name = rows[i].obj.cells[0].innerHTML;
-          status = $(rows[i].obj).hasClass('disabled');
-          rows[i].obj.cells[0].innerHTML = from.cells[0].innerHTML;
-          from.cells[0].innerHTML = name;
-          $(from)[status?'addClass':'removeClass']('disabled');
-          $(rows[i].obj)[fromstatus?'addClass':'removeClass']('disabled');
-          break;
-        }
-      }
-      // ... and disable/enable Up button
-      this.filters_listbuttons();
       break;
 
-    case 'update':
-      var rows = parent.rcmail.filters_list.rows;
-      for (var i=0; i<rows.length; i++)
-        if (rows[i] && rows[i].uid == id) {
-          rows[i].obj.cells[0].innerHTML = name;
-          if (disabled)
-            $(rows[i].obj).addClass('disabled');
-          else
-            $(rows[i].obj).removeClass('disabled');
-          break;
-        }
+    // Filling rules list
+    case 'list':
+      var i, tr, td, el, list = this.filters_list;
+
+      if (o.clear)
+        list.clear();
+
+      for (i in o.list) {
+        el = o.list[i];
+        tr = document.createElement('TR');
+        td = document.createElement('TD');
+
+        td.innerHTML = el.name;
+        td.className = 'name';
+        tr.id = 'rcmrow' + el.id;
+        if (el['class'])
+            tr.className = el['class'];
+        tr.appendChild(td);
+
+        list.insert_row(tr);
+      }
+
+      if (o.set)
+        list.highlight_row(o.set);
+      else
+        this.enable_command('plugin.managesieve-del', 'plugin.managesieve-act', false);
+
       break;
 
-    case 'add':
-      var row, new_row, td, list = parent.rcmail.filters_list;
-
-      if (!list)
-        break;
-
-      for (var i=0; i<list.rows.length; i++)
-        if (list.rows[i] != null && String(list.rows[i].obj.id).match(/^rcmrow/))
-          row = list.rows[i].obj;
-
-      if (row) {
-        new_row = parent.document.createElement('tr');
-        new_row.id = 'rcmrow'+id;
-        td = parent.document.createElement('td');
-        new_row.appendChild(td);
-        list.insert_row(new_row, false);
-        if (disabled)
-          $(new_row).addClass('disabled');
-          if (row.cells[0].className)
-            td.className = row.cells[0].className;
-
-           td.innerHTML = name;
-        list.highlight_row(id);
-
-        parent.rcmail.enable_command('plugin.managesieve-del', 'plugin.managesieve-up', true);
+    // Sactivate/deactivate set
+    case 'setact':
+      var id = this.managesieve_setid(o.name), row = $('#rcmrow' + id);
+      if (o.active) {
+        if (o.all)
+          $('tr', this.gui_objects.filtersetslist).addClass('disabled');
+        row.removeClass('disabled');
       }
-      else // refresh whole page
-        parent.rcmail.goto_url('plugin.managesieve');
+      else
+        row.addClass('disabled');
+
       break;
-  }
 
-  this.set_busy(false);
-};
+    // Delete set row
+    case 'setdel':
+      var id = this.managesieve_setid(o.name);
 
-rcube_webmail.prototype.managesieve_select = function(list)
-{
-  var id = list.get_single_selection();
-  if (id != null)
-    this.load_managesieveframe(list.rows[id].uid);
-};
+      this.filtersets_list.remove_row(id);
+      this.filters_list.clear();
+      this.show_contentframe(false);
+      this.enable_command('plugin.managesieve-setdel', 'plugin.managesieve-setact', 'plugin.managesieve-setget', false);
 
-rcube_webmail.prototype.managesieve_save = function()
-{
-  if (parent.rcmail && parent.rcmail.filters_list && this.gui_objects.sieveform.name != 'filtersetform') {
-    var id = parent.rcmail.filters_list.get_single_selection();
-    if (id != null)
-      this.gui_objects.sieveform.elements['_fid'].value = parent.rcmail.filters_list.rows[id].uid;
+      delete this.env.filtersets[id];
+
+      break;
+
+    // Create set row
+    case 'setadd':
+      var id = 'S' + new Date().getTime(),
+        list = this.filtersets_list,
+        row = $('<tr class="disabled"><td class="name"></td></tr>');
+
+      $('td', row).html(o.name);
+      row.attr('id', 'rcmrow'+id);
+
+      this.env.filtersets[id] = o.name;
+      list.insert_row(row.get(0));
+
+      // move row into its position on the list
+      if (o.index != list.rowcount-1) {
+        row.detach();
+        var elem = $('tr:visible', list.list).get(o.index);
+        row.insertBefore(elem);
+      }
+
+      list.select(id);
+
+      break;
   }
-  this.gui_objects.sieveform.submit();
+
+  this.set_busy(false);
 };
 
 // load filter frame
 rcube_webmail.prototype.load_managesieveframe = function(id)
 {
-  if (typeof(id) != 'undefined' && id != null) {
-    this.enable_command('plugin.managesieve-del', true);
-    this.filters_listbuttons();
-  }
-  else
-    this.enable_command('plugin.managesieve-up', 'plugin.managesieve-down', 'plugin.managesieve-del', false);
+  var has_id = typeof(id) != 'undefined' && id != null;
+  this.enable_command('plugin.managesieve-act', 'plugin.managesieve-del', has_id);
 
   if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
     target = window.frames[this.env.contentframe];
     var msgid = this.set_busy(true, 'loading');
-    target.location.href = this.env.comm_path+'&_action=plugin.managesieve&_framed=1&_fid='+id+'&_unlock='+msgid;
+    target.location.href = this.env.comm_path+'&_action=plugin.managesieve&_framed=1'
+      +(id ? '&_fid='+id : '')+'&_unlock='+msgid;
   }
 };
 
-// enable/disable Up/Down buttons
-rcube_webmail.prototype.filters_listbuttons = function()
+// load filter frame
+rcube_webmail.prototype.managesieve_dragstart = function(list)
 {
-  var id = this.filters_list.get_single_selection(),
-    rows = this.filters_list.rows;
+  var id = this.filters_list.get_single_selection();
 
-  for (var i=0; i<rows.length; i++) {
-    if (rows[i] == null) { // removed row
-    }
-    else if (i == id) {
-      this.enable_command('plugin.managesieve-up', false);
-      break;
-    }
-    else {
-      this.enable_command('plugin.managesieve-up', true);
-      break;
+  this.drag_active = true;
+  this.drag_filter = id;
+};
+
+rcube_webmail.prototype.managesieve_dragend = function(e)
+{
+  if (this.drag_active) {
+    if (this.drag_filter_target) {
+      var lock = this.set_busy(true, 'loading');
+
+      this.show_contentframe(false);
+      this.http_post('plugin.managesieve', '_act=move&_fid='+this.drag_filter
+        +'&_to='+this.drag_filter_target, lock);
     }
+    this.drag_active = false;
   }
+};
 
-  for (var i=rows.length-1; i>0; i--) {
-    if (rows[i] == null) { // removed row
-    }
-    else if (i == id) {
-      this.enable_command('plugin.managesieve-down', false);
-      break;
-    }
-    else {
-      this.enable_command('plugin.managesieve-down', true);
-      break;
-    }
-  } 
+rcube_webmail.prototype.managesieve_focus_filter = function(row)
+{
+  var id = row.id.replace(/^rcmrow/, '');
+  if (this.drag_active && id != this.drag_filter) {
+    this.drag_filter_target = id;
+    $(row.obj).addClass(id < this.drag_filter ? 'filtermoveup' : 'filtermovedown');
+  }
+};
+
+rcube_webmail.prototype.managesieve_unfocus_filter = function(row)
+{
+  if (this.drag_active) {
+    $(row.obj).removeClass('filtermoveup filtermovedown');
+    this.drag_filter_target = null;
+  }
+};
+
+/*********************************************************/
+/*********          Filter Form methods          *********/
+/*********************************************************/
+
+// Form submition
+rcube_webmail.prototype.managesieve_save = function()
+{
+  if (parent.rcmail && parent.rcmail.filters_list && this.gui_objects.sieveform.name != 'filtersetform') {
+    var id = parent.rcmail.filters_list.get_single_selection();
+    if (id != null)
+      this.gui_objects.sieveform.elements['_fid'].value = parent.rcmail.filters_list.rows[id].uid;
+  }
+  this.gui_objects.sieveform.submit();
 };
 
-// operations on filters form
+// Operations on filters form
 rcube_webmail.prototype.managesieve_ruleadd = function(id)
 {
   this.http_post('plugin.managesieve', '_act=ruleadd&_rid='+id);
@@ -311,6 +451,9 @@ rcube_webmail.prototype.managesieve_rulefill = function(content, id, after)
 
 rcube_webmail.prototype.managesieve_ruledel = function(id)
 {
+  if ($('#ruledel'+id).hasClass('disabled'))
+    return;
+
   if (confirm(this.get_label('managesieve.ruledeleteconfirm'))) {
     var row = document.getElementById('rulerow'+id);
     row.parentNode.removeChild(row);
@@ -341,6 +484,9 @@ rcube_webmail.prototype.managesieve_actionfill = function(content, id, after)
 
 rcube_webmail.prototype.managesieve_actiondel = function(id)
 {
+  if ($('#actiondel'+id).hasClass('disabled'))
+    return;
+
   if (confirm(this.get_label('managesieve.actiondeleteconfirm'))) {
     var row = document.getElementById('actionrow'+id);
     row.parentNode.removeChild(row);
@@ -383,155 +529,44 @@ rcube_webmail.prototype.managesieve_formbuttons = function(div)
     button = document.getElementById(buttons[i]);
     if (i>0 || buttons.length>1) {
       $(button).removeClass('disabled');
-      button.removeAttribute('disabled');
     }
     else {
       $(button).addClass('disabled');
-      button.setAttribute('disabled', true);
     }
   }
 };
 
-// Set change
-rcube_webmail.prototype.managesieve_set = function()
-{
-  var script = $(this.gui_objects.filtersetslist).val();
-  location.href = this.env.comm_path+'&_action=plugin.managesieve&_set='+script;
-};
-
-// Script download
-rcube_webmail.prototype.managesieve_setget = function()
-{
-  var script = $(this.gui_objects.filtersetslist).val();
-  location.href = this.env.comm_path+'&_action=plugin.managesieve&_act=setget&_set='+script;
-};
-
-// Set activate
-rcube_webmail.prototype.managesieve_setact = function()
-{
-  if (!this.gui_objects.filtersetslist)
-    return false;
-
-  var script = this.gui_objects.filtersetslist.value,
-    action = (script == rcmail.env.active_set ? 'deact' : 'setact');
-
-  this.http_post('plugin.managesieve', '_act='+action+'&_set='+script);
-};
-
-// Set activate flag in sets list after set activation
-rcube_webmail.prototype.managesieve_reset = function()
-{
-  if (!this.gui_objects.filtersetslist)
-    return false;
-
-  var list = this.gui_objects.filtersetslist,
-    opts = list.getElementsByTagName('option'),
-    label = ' (' + this.get_label('managesieve.active') + ')',
-    regx = new RegExp(RegExp.escape(label)+'$');
-
-  for (var x=0; x<opts.length; x++) {
-    if (opts[x].value != rcmail.env.active_set && opts[x].innerHTML.match(regx))
-      opts[x].innerHTML = opts[x].innerHTML.replace(regx, '');
-    else if (opts[x].value == rcmail.env.active_set)
-      opts[x].innerHTML = opts[x].innerHTML + label;
-  }
-
-  // change title of setact button
-  $('#'+rcmail.buttons['plugin.managesieve-setact'][0].id).attr('title', rcmail.gettext('managesieve.filterset'
-    + (list.value == rcmail.env.active_set ? 'deact' : 'act')));
-};
-
-// Set delete
-rcube_webmail.prototype.managesieve_setdel = function()
-{
-  if (!this.gui_objects.filtersetslist)
-    return false;
-
-  if (!confirm(this.get_label('managesieve.setdeleteconfirm')))
-    return false;
-
-  var script = this.gui_objects.filtersetslist.value;
-  this.http_post('plugin.managesieve', '_act=setdel&_set='+script);
-};
-
-// Set add
-rcube_webmail.prototype.managesieve_setadd = function()
-{
-  this.filters_list.clear_selection();
-  this.enable_command('plugin.managesieve-up', 'plugin.managesieve-down', 'plugin.managesieve-del', false);
-
-  if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
-    target = window.frames[this.env.contentframe];
-    var msgid = this.set_busy(true, 'loading');
-    target.location.href = this.env.comm_path+'&_action=plugin.managesieve&_framed=1&_newset=1&_unlock='+msgid;
-  }
-};
-
-rcube_webmail.prototype.managesieve_reload = function(set)
-{
-  this.env.reload_set = set;
-  window.setTimeout(function() {
-    location.href = rcmail.env.comm_path + '&_action=plugin.managesieve'
-      + (rcmail.env.reload_set ? '&_set=' + rcmail.env.reload_set : '')
-  }, 500);
-};
-
-// Register onmouse(leave/enter) events for tips on specified form element
-rcube_webmail.prototype.managesieve_tip_register = function(tips)
-{
-  for (var n in tips) {
-    $('#'+tips[n][0])
-      .bind('mouseenter', {str: tips[n][1]},
-        function(e) {
-          var offset = $(this).offset(),
-            tip = rcmail.env.framed ? parent.rcmail.env.ms_tip_layer : rcmail.env.ms_tip_layer,
-            left = offset.left,
-            top = offset.top - 12;
-
-          if (rcmail.env.framed) {
-            offset = $(parent.document.getElementById('filter-box')).offset();
-            top  += offset.top;
-            left += offset.left;
-          }
-
-          tip.html(e.data.str)
-          top -= tip.height();
-          
-          tip.css({left: left, top: top}).show();
-        })
-      .bind('mouseleave',
-        function(e) {
-          var tip = parent.rcmail && parent.rcmail.env.ms_tip_layer ? 
-            parent.rcmail.env.ms_tip_layer : rcmail.env.ms_tip_layer;
-          tip.hide();
-      });
-  }
-};
-
-/*********************************************************/
-/*********     Other Managesieve UI methods      *********/
-/*********************************************************/
-
 function rule_header_select(id)
 {
   var obj = document.getElementById('header' + id),
     size = document.getElementById('rule_size' + id),
     op = document.getElementById('rule_op' + id),
     target = document.getElementById('rule_target' + id),
-    header = document.getElementById('custom_header' + id);
+    header = document.getElementById('custom_header' + id),
+    mod = document.getElementById('rule_mod' + id),
+    trans = document.getElementById('rule_trans' + id),
+    comp = document.getElementById('rule_comp' + id);
 
   if (obj.value == 'size') {
     size.style.display = 'inline';
     op.style.display = 'none';
     target.style.display = 'none';
     header.style.display = 'none';
+    mod.style.display = 'none';
+    trans.style.display = 'none';
+    comp.style.display = 'none';
   }
   else {
     header.style.display = obj.value != '...' ? 'none' : 'inline';
     size.style.display = 'none';
     op.style.display = 'inline';
+    comp.style.display = '';
     rule_op_select(id);
+    mod.style.display = obj.value == 'body' ? 'none' : 'block';
+    trans.style.display = obj.value == 'body' ? 'block' : 'none';
   }
+
+  obj.style.width = obj.value == '...' ? '40px' : '';
 };
 
 function rule_op_select(id)
@@ -542,15 +577,45 @@ function rule_op_select(id)
   target.style.display = obj.value == 'exists' || obj.value == 'notexists' ? 'none' : 'inline';
 };
 
+function rule_trans_select(id)
+{
+  var obj = document.getElementById('rule_trans_op' + id),
+    target = document.getElementById('rule_trans_type' + id);
+
+  target.style.display = obj.value != 'content' ? 'none' : 'inline';
+};
+
+function rule_mod_select(id)
+{
+  var obj = document.getElementById('rule_mod_op' + id),
+    target = document.getElementById('rule_mod_type' + id);
+
+  target.style.display = obj.value != 'address' && obj.value != 'envelope' ? 'none' : 'inline';
+};
+
 function rule_join_radio(value)
 {
   $('#rules').css('display', value == 'any' ? 'none' : 'block');
 };
 
+function rule_adv_switch(id, elem)
+{
+  var elem = $(elem), enabled = elem.hasClass('hide'), adv = $('#rule_advanced'+id);
+
+  if (enabled) {
+    adv.hide();
+    elem.removeClass('hide').addClass('show');
+  }
+  else {
+    adv.show();
+    elem.removeClass('show').addClass('hide');
+  }
+}
+
 function action_type_select(id)
 {
   var obj = document.getElementById('action_type' + id),
-       enabled = {},
+    enabled = {},
     elems = {
       mailbox: document.getElementById('action_mailbox' + id),
       target: document.getElementById('action_target' + id),
@@ -579,3 +644,126 @@ function action_type_select(id)
     elems[x].style.display = !enabled[x] ? 'none' : 'inline';
   }
 };
+
+// Register onmouse(leave/enter) events for tips on specified form element
+rcube_webmail.prototype.managesieve_tip_register = function(tips)
+{
+  var n, framed = parent.rcmail,
+    tip = framed ? parent.rcmail.env.ms_tip_layer : rcmail.env.ms_tip_layer;
+
+  for (var n in tips) {
+    $('#'+tips[n][0])
+      .bind('mouseenter', {str: tips[n][1]},
+        function(e) {
+          var offset = $(this).offset(),
+            left = offset.left,
+            top = offset.top - 12;
+
+          if (framed) {
+            offset = $((rcmail.env.task == 'mail'  ? '#sievefilterform > iframe' : '#filter-box'), parent.document).offset();
+            top  += offset.top;
+            left += offset.left;
+          }
+
+          tip.html(e.data.str)
+          top -= tip.height();
+
+          tip.css({left: left, top: top}).show();
+        })
+      .bind('mouseleave', function(e) { tip.hide(); });
+  }
+};
+
+/*********************************************************/
+/*********           Mail UI methods             *********/
+/*********************************************************/
+
+rcube_webmail.prototype.managesieve_create = function()
+{
+  if (!rcmail.env.sieve_headers || !rcmail.env.sieve_headers.length)
+    return;
+
+  var i, html, buttons = {}, dialog = $("#sievefilterform");
+
+  // create dialog window
+  if (!dialog.length) {
+    dialog = $('<div id="sievefilterform"></div>');
+    $('body').append(dialog);
+  }
+
+  // build dialog window content
+  html = '<fieldset><legend>'+this.gettext('managesieve.usedata')+'</legend><ul>';
+  for (i in rcmail.env.sieve_headers)
+    html += '<li><input type="checkbox" name="headers[]" id="sievehdr'+i+'" value="'+i+'" checked="checked" />'
+      +'<label for="sievehdr'+i+'">'+rcmail.env.sieve_headers[i][0]+':</label> '+rcmail.env.sieve_headers[i][1]+'</li>';
+  html += '</ul></fieldset>';
+
+  dialog.html(html);
+
+  // [Next Step] button action
+  buttons[this.gettext('managesieve.nextstep')] = function () {
+    // check if there's at least one checkbox checked
+    var hdrs = $('input[name="headers[]"]:checked', dialog);
+    if (!hdrs.length) {
+      alert(rcmail.gettext('managesieve.nodata'));
+      return;
+    }
+
+    // build frame URL
+    var url = rcmail.get_task_url('mail');
+    url = rcmail.add_url(url, '_action', 'plugin.managesieve');
+    url = rcmail.add_url(url, '_framed', 1);
+
+    hdrs.map(function() {
+      var val = rcmail.env.sieve_headers[this.value];
+      url = rcmail.add_url(url, 'r['+this.value+']', val[0]+':'+val[1]);
+    });
+
+    // load form in the iframe
+    var frame = $('<iframe>').attr({src: url, frameborder: 0})
+    dialog.empty().append(frame).dialog('dialog').resize();
+
+    // Change [Next Step] button with [Save] button
+    buttons = {};
+    buttons[rcmail.gettext('save')] = function() {
+      var win = $('iframe', dialog).get(0).contentWindow;
+      win.rcmail.managesieve_save();
+    };
+    dialog.dialog('option', 'buttons', buttons);
+  };
+
+  // show dialog window
+  dialog.dialog({
+    modal: false,
+    resizable: !bw.ie6,
+    closeOnEscape: (!bw.ie6 && !bw.ie7),  // disable for performance reasons
+    title: this.gettext('managesieve.newfilter'),
+    close: function() { rcmail.managesieve_dialog_close(); },
+    buttons: buttons,
+    minWidth: 600,
+    minHeight: 300,
+    height: 250
+  }).show();
+
+  this.env.managesieve_dialog = dialog;
+}
+
+rcube_webmail.prototype.managesieve_dialog_close = function()
+{
+  var dialog = this.env.managesieve_dialog;
+
+  // BUG(?): if we don't remove the iframe first, it will be reloaded
+  dialog.html('');
+  dialog.dialog('destroy').hide();
+}
+
+rcube_webmail.prototype.managesieve_dialog_resize = function(o)
+{
+  var dialog = this.env.managesieve_dialog,
+    win = $(window), form = $(o);
+    width = form.width(), height = form.height(),
+    w = win.width(), h = win.height();
+
+  dialog.dialog('option', { height: Math.min(h-20, height+120), width: Math.min(w-20, width+65) })
+    .dialog('option', 'position', ['center', 'center']);  // only works in a separate call (!?)
+}
index 2cb7122752613268769be390b548043646cb8192..d557907968d40e178c960275f3494cda8c543add 100644 (file)
@@ -7,12 +7,13 @@
  * It's clickable interface which operates on text scripts and communicates
  * with server using managesieve protocol. Adds Filters tab in Settings.
  *
- * @version 4.3
+ * @version 5.0
  * @author Aleksander Machniak <alec@alec.pl>
  *
  * Configuration (see config.inc.php.dist)
  *
  * Copyright (C) 2008-2011, The Roundcube Dev Team
+ * Copyright (C) 2011, Kolab Systems AG
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  *
- * $Id: managesieve.php 4983 2011-07-28 07:31:16Z alec $
+ * $Id: managesieve.php 5452 2011-11-18 14:44:48Z alec $
  */
 
 class managesieve extends rcube_plugin
 {
-    public $task = 'settings';
+    public $task = 'mail|settings';
 
     private $rc;
     private $sieve;
@@ -41,29 +42,141 @@ class managesieve extends rcube_plugin
     private $tips = array();
     private $script = array();
     private $exts = array();
+    private $list;
+    private $active = array();
     private $headers = array(
-        'subject'   => 'Subject',
-        'sender'    => 'From',
-        'recipient' => 'To',
+        'subject' => 'Subject',
+        'from'    => 'From',
+        'to'      => 'To',
     );
+    private $addr_headers = array(
+        // Required
+        "from", "to", "cc", "bcc", "sender", "resent-from", "resent-to",
+        // Additional (RFC 822 / RFC 2822)
+        "reply-to", "resent-reply-to", "resent-sender", "resent-cc", "resent-bcc",
+        // Non-standard (RFC 2076, draft-palme-mailext-headers-08.txt)
+        "for-approval", "for-handling", "for-comment", "apparently-to", "errors-to",
+        "delivered-to", "return-receipt-to", "x-admin", "read-receipt-to",
+        "x-confirm-reading-to", "return-receipt-requested",
+        "registered-mail-reply-requested-by", "mail-followup-to", "mail-reply-to",
+        "abuse-reports-to", "x-complaints-to", "x-report-abuse-to",
+        // Undocumented
+        "x-beenthere",
+    );
+
+    const VERSION = '5.0';
+    const PROGNAME = 'Roundcube (Managesieve)';
 
 
     function init()
     {
-        // add Tab label/title
-        $this->add_texts('localization/', array('filters','managefilters'));
+        $this->rc = rcmail::get_instance();
 
         // register actions
         $this->register_action('plugin.managesieve', array($this, 'managesieve_actions'));
         $this->register_action('plugin.managesieve-save', array($this, 'managesieve_save'));
 
-        // include main js script
+        if ($this->rc->task == 'settings') {
+            $this->init_ui();
+        }
+        else if ($this->rc->task == 'mail') {
+            // register message hook
+            $this->add_hook('message_headers_output', array($this, 'mail_headers'));
+
+            // inject Create Filter popup stuff
+            if (empty($this->rc->action) || $this->rc->action == 'show') {
+                $this->mail_task_handler();
+            }
+        }
+    }
+
+    /**
+     * Initializes plugin's UI (localization, js script)
+     */
+    private function init_ui()
+    {
+        if ($this->ui_initialized)
+            return;
+
+        // load localization
+        $this->add_texts('localization/', array('filters','managefilters'));
         $this->include_script('managesieve.js');
+
+        $this->ui_initialized = true;
+    }
+
+    /**
+     * Add UI elements to the 'mailbox view' and 'show message' UI.
+     */
+    function mail_task_handler()
+    {
+        // use jQuery for popup window
+        $this->require_plugin('jqueryui'); 
+
+        // include js script and localization
+        $this->init_ui();
+
+        // include styles
+        $skin = $this->rc->config->get('skin');
+        if (!file_exists($this->home."/skins/$skin/managesieve_mail.css"))
+            $skin = 'default';
+        $this->include_stylesheet("skins/$skin/managesieve_mail.css");
+
+        // add 'Create filter' item to message menu
+        $this->api->add_content(html::tag('li', null, 
+            $this->api->output->button(array(
+                'command'  => 'managesieve-create',
+                'label'    => 'managesieve.filtercreate',
+                'type'     => 'link',
+                'classact' => 'filterlink active',
+                'class'    => 'filterlink',
+            ))), 'messagemenu');
+
+        // register some labels/messages
+        $this->rc->output->add_label('managesieve.newfilter', 'managesieve.usedata',
+            'managesieve.nodata', 'managesieve.nextstep', 'save');
+
+        $this->rc->session->remove('managesieve_current');
     }
 
+    /**
+     * Get message headers for popup window
+     */
+    function mail_headers($args)
+    {
+        $headers = $args['headers'];
+        $ret     = array();
+
+        if ($headers->subject)
+            $ret[] = array('Subject', $this->rc->imap->decode_header($headers->subject));
+
+        // @TODO: List-Id, others?
+        foreach (array('From', 'To') as $h) {
+            $hl = strtolower($h);
+            if ($headers->$hl) {
+                $list = $this->rc->imap->decode_address_list($headers->$hl);
+                foreach ($list as $item) {
+                    if ($item['mailto']) {
+                        $ret[] = array($h, $item['mailto']);
+                    }
+                }
+            }
+        }
+
+        if ($this->rc->action == 'preview')
+            $this->rc->output->command('parent.set_env', array('sieve_headers' => $ret));
+        else
+            $this->rc->output->set_env('sieve_headers', $ret);
+
+
+        return $args;
+    }
+
+    /**
+     * Loads configuration, initializes plugin (including sieve connection)
+     */
     function managesieve_start()
     {
-        $this->rc = rcmail::get_instance();
         $this->load_config();
 
         // register UI objects
@@ -85,33 +198,47 @@ class managesieve extends rcube_plugin
 
         $host = rcube_idn_to_ascii($host);
 
+        $plugin = $this->rc->plugins->exec_hook('managesieve_connect', array(
+            'user'      => $_SESSION['username'],
+            'password'  => $this->rc->decrypt($_SESSION['password']),
+            'host'      => $host,
+            'port'      => $port,
+            'auth_type' => $this->rc->config->get('managesieve_auth_type'),
+            'usetls'    => $this->rc->config->get('managesieve_usetls', false),
+            'disabled'  => $this->rc->config->get('managesieve_disabled_extensions'),
+            'debug'     => $this->rc->config->get('managesieve_debug', false),
+            'auth_cid'  => $this->rc->config->get('managesieve_auth_cid'),
+            'auth_pw'   => $this->rc->config->get('managesieve_auth_pw'),
+        ));
+
         // try to connect to managesieve server and to fetch the script
-        $this->sieve = new rcube_sieve($_SESSION['username'],
-            $this->rc->decrypt($_SESSION['password']), $host, $port,
-            $this->rc->config->get('managesieve_auth_type'),
-            $this->rc->config->get('managesieve_usetls', false),
-            $this->rc->config->get('managesieve_disabled_extensions'),
-            $this->rc->config->get('managesieve_debug', false),
-            $this->rc->config->get('managesieve_auth_cid'), 
-            $this->rc->config->get('managesieve_auth_pw')
+        $this->sieve = new rcube_sieve(
+            $plugin['user'],
+            $plugin['password'],
+            $plugin['host'],
+            $plugin['port'],
+            $plugin['auth_type'],
+            $plugin['usetls'],
+            $plugin['disabled'],
+            $plugin['debug'],
+            $plugin['auth_cid'],
+            $plugin['auth_pw']
         );
 
         if (!($error = $this->sieve->error())) {
+            // Get list of scripts
+            $list = $this->list_scripts();
 
-            $list = $this->sieve->get_scripts();
-            $active = $this->sieve->get_active();
-            $_SESSION['managesieve_active'] = $active;
-
-            if (!empty($_GET['_set'])) {
-                $script_name = get_input_value('_set', RCUBE_INPUT_GET);
+            if (!empty($_GET['_set']) || !empty($_POST['_set'])) {
+                $script_name = get_input_value('_set', RCUBE_INPUT_GPC, true);
             }
             else if (!empty($_SESSION['managesieve_current'])) {
                 $script_name = $_SESSION['managesieve_current'];
             }
             else {
-                // get active script
-                if ($active) {
-                    $script_name = $active;
+                // get (first) active script
+                if (!empty($this->active[0])) {
+                    $script_name = $this->active[0];
                 }
                 else if ($list) {
                     $script_name = $list[0];
@@ -120,19 +247,25 @@ class managesieve extends rcube_plugin
                 else {
                     // if script not exists build default script contents
                     $script_file = $this->rc->config->get('managesieve_default');
-                    $script_name = 'roundcube';
+                    $script_name = $this->rc->config->get('managesieve_script_name');
+
+                    if (empty($script_name))
+                        $script_name = 'roundcube';
+
                     if ($script_file && is_readable($script_file))
                         $content = file_get_contents($script_file);
 
-                // add script and set it active
-                if ($this->sieve->save_script($script_name, $content))
-                    if ($this->sieve->activate($script_name))
-                        $_SESSION['managesieve_active'] = $script_name;
+                    // add script and set it active
+                    if ($this->sieve->save_script($script_name, $content)) {
+                        $this->activate_script($script_name);
+                        $this->list[] = $script_name;
+                    }
                 }
             }
 
-            if ($script_name)
+            if ($script_name) {
                 $this->sieve->load($script_name);
+            }
 
             $error = $this->sieve->error();
         }
@@ -158,9 +291,9 @@ class managesieve extends rcube_plugin
             $this->script = array();
         }
         else {
-            $this->script = $this->sieve->script->as_array();
             $this->exts = $this->sieve->get_extensions();
-            $this->rc->output->set_env('active_set', $_SESSION['managesieve_active']);
+            $this->script = $this->sieve->script->as_array();
+            $this->rc->output->set_env('currentset', $this->sieve->current);
             $_SESSION['managesieve_current'] = $this->sieve->current;
         }
 
@@ -169,94 +302,131 @@ class managesieve extends rcube_plugin
 
     function managesieve_actions()
     {
-        // Init plugin and handle managesieve connection
+        $this->init_ui();
+
         $error = $this->managesieve_start();
 
         // Handle user requests
         if ($action = get_input_value('_act', RCUBE_INPUT_GPC)) {
-            $fid = (int) get_input_value('_fid', RCUBE_INPUT_GET);
+            $fid = (int) get_input_value('_fid', RCUBE_INPUT_POST);
 
-            if ($action == 'up' && !$error) {
-                if ($fid && isset($this->script[$fid]) && isset($this->script[$fid-1])) {
-                    if ($this->sieve->script->update_rule($fid, $this->script[$fid-1]) !== false
-                        && $this->sieve->script->update_rule($fid-1, $this->script[$fid]) !== false) {
-                        $result = $this->sieve->save();
-                    }
+            if ($action == 'delete' && !$error) {
+                if (isset($this->script[$fid])) {
+                    if ($this->sieve->script->delete_rule($fid))
+                        $result = $this->save_script();
 
-                    if ($result) {
-//                      $this->rc->output->show_message('managesieve.filtersaved', 'confirmation');
-                        $this->rc->output->command('managesieve_updatelist', 'up', '', $fid);
-                    } else
-                        $this->rc->output->show_message('managesieve.filtersaveerror', 'error');
+                    if ($result === true) {
+                        $this->rc->output->show_message('managesieve.filterdeleted', 'confirmation');
+                        $this->rc->output->command('managesieve_updatelist', 'del', array('id' => $fid));
+                    } else {
+                        $this->rc->output->show_message('managesieve.filterdeleteerror', 'error');
+                    }
                 }
             }
-            else if ($action == 'down' && !$error) {
-                if (isset($this->script[$fid]) && isset($this->script[$fid+1])) {
-                    if ($this->sieve->script->update_rule($fid, $this->script[$fid+1]) !== false
-                        && $this->sieve->script->update_rule($fid+1, $this->script[$fid]) !== false) {
-                        $result = $this->sieve->save();
+            else if ($action == 'move' && !$error) {
+                if (isset($this->script[$fid])) {
+                    $to   = (int) get_input_value('_to', RCUBE_INPUT_POST);
+                    $rule = $this->script[$fid];
+
+                    // remove rule
+                    unset($this->script[$fid]);
+                    $this->script = array_values($this->script);
+
+                    // add at target position
+                    if ($to >= count($this->script)) {
+                        $this->script[] = $rule;
+                    }
+                    else {
+                        $script = array();
+                        foreach ($this->script as $idx => $r) {
+                            if ($idx == $to)
+                                $script[] = $rule;
+                            $script[] = $r;
+                        }
+                        $this->script = $script;
                     }
 
+                    $this->sieve->script->content = $this->script;
+                    $result = $this->save_script();
+
                     if ($result === true) {
-//                      $this->rc->output->show_message('managesieve.filtersaved', 'confirmation');
-                        $this->rc->output->command('managesieve_updatelist', 'down', '', $fid);
+                        $result = $this->list_rules();
+
+                        $this->rc->output->show_message('managesieve.moved', 'confirmation');
+                        $this->rc->output->command('managesieve_updatelist', 'list',
+                            array('list' => $result, 'clear' => true, 'set' => $to));
                     } else {
-                        $this->rc->output->show_message('managesieve.filtersaveerror', 'error');
+                        $this->rc->output->show_message('managesieve.moveerror', 'error');
                     }
                 }
             }
-            else if ($action == 'delete' && !$error) {
+            else if ($action == 'act' && !$error) {
                 if (isset($this->script[$fid])) {
-                    if ($this->sieve->script->delete_rule($fid))
-                        $result = $this->sieve->save();
+                    $rule     = $this->script[$fid];
+                    $disabled = $rule['disabled'] ? true : false;
+                    $rule['disabled'] = !$disabled;
+                    $result = $this->sieve->script->update_rule($fid, $rule);
+
+                    if ($result !== false)
+                        $result = $this->save_script();
 
                     if ($result === true) {
-                        $this->rc->output->show_message('managesieve.filterdeleted', 'confirmation');
-                        $this->rc->output->command('managesieve_updatelist', 'delete', '', $fid);
+                        if ($rule['disabled'])
+                            $this->rc->output->show_message('managesieve.deactivated', 'confirmation');
+                        else
+                            $this->rc->output->show_message('managesieve.activated', 'confirmation');
+                        $this->rc->output->command('managesieve_updatelist', 'update',
+                            array('id' => $fid, 'disabled' => $rule['disabled']));
                     } else {
-                        $this->rc->output->show_message('managesieve.filterdeleteerror', 'error');
+                        if ($rule['disabled'])
+                            $this->rc->output->show_message('managesieve.deactivateerror', 'error');
+                        else
+                            $this->rc->output->show_message('managesieve.activateerror', 'error');
                     }
                 }
             }
             else if ($action == 'setact' && !$error) {
-                $script_name = get_input_value('_set', RCUBE_INPUT_GPC);
-                $result = $this->sieve->activate($script_name);
+                $script_name = get_input_value('_set', RCUBE_INPUT_GPC, true);
+                $result = $this->activate_script($script_name);
+                $kep14  = $this->rc->config->get('managesieve_kolab_master');
 
                 if ($result === true) {
-                    $this->rc->output->set_env('active_set', $script_name);
+                    $this->rc->output->set_env('active_sets', $this->active);
                     $this->rc->output->show_message('managesieve.setactivated', 'confirmation');
-                    $this->rc->output->command('managesieve_reset');
-                    $_SESSION['managesieve_active'] = $script_name;
+                    $this->rc->output->command('managesieve_updatelist', 'setact',
+                        array('name' => $script_name, 'active' => true, 'all' => !$kep14));
                 } else {
                     $this->rc->output->show_message('managesieve.setactivateerror', 'error');
                 }
             }
             else if ($action == 'deact' && !$error) {
-                $result = $this->sieve->deactivate();
+                $script_name = get_input_value('_set', RCUBE_INPUT_GPC, true);
+                $result = $this->deactivate_script($script_name);
 
                 if ($result === true) {
-                    $this->rc->output->set_env('active_set', '');
+                    $this->rc->output->set_env('active_sets', $this->active);
                     $this->rc->output->show_message('managesieve.setdeactivated', 'confirmation');
-                    $this->rc->output->command('managesieve_reset');
-                    $_SESSION['managesieve_active'] = '';
+                    $this->rc->output->command('managesieve_updatelist', 'setact',
+                        array('name' => $script_name, 'active' => false));
                 } else {
                     $this->rc->output->show_message('managesieve.setdeactivateerror', 'error');
                 }
             }
             else if ($action == 'setdel' && !$error) {
-                $script_name = get_input_value('_set', RCUBE_INPUT_GPC);
-                $result = $this->sieve->remove($script_name);
+                $script_name = get_input_value('_set', RCUBE_INPUT_GPC, true);
+                $result = $this->remove_script($script_name);
 
                 if ($result === true) {
                     $this->rc->output->show_message('managesieve.setdeleted', 'confirmation');
-                    $this->rc->output->command('managesieve_reload');
+                    $this->rc->output->command('managesieve_updatelist', 'setdel',
+                        array('name' => $script_name));
                     $this->rc->session->remove('managesieve_current');
                 } else {
                     $this->rc->output->show_message('managesieve.setdeleteerror', 'error');
                 }
             }
             else if ($action == 'setget') {
-                $script_name = get_input_value('_set', RCUBE_INPUT_GPC);
+                $script_name = get_input_value('_set', RCUBE_INPUT_GPC, true);
                 $script = $this->sieve->get_script($script_name);
 
                 if (PEAR::isError($script))
@@ -281,14 +451,19 @@ class managesieve extends rcube_plugin
                 echo $script;
                 exit;
             }
-            elseif ($action == 'ruleadd') {
+            else if ($action == 'list') {
+                $result = $this->list_rules();
+
+                $this->rc->output->command('managesieve_updatelist', 'list', array('list' => $result));
+            }
+            else if ($action == 'ruleadd') {
                 $rid = get_input_value('_rid', RCUBE_INPUT_GPC);
                 $id = $this->genid();
                 $content = $this->rule_div($fid, $id, false);
 
                 $this->rc->output->command('managesieve_rulefill', $content, $id, $rid);
             }
-            elseif ($action == 'actionadd') {
+            else if ($action == 'actionadd') {
                 $aid = get_input_value('_aid', RCUBE_INPUT_GPC);
                 $id = $this->genid();
                 $content = $this->action_div($fid, $id, false);
@@ -298,25 +473,76 @@ class managesieve extends rcube_plugin
 
             $this->rc->output->send();
         }
+        else if ($this->rc->task == 'mail') {
+            // Initialize the form
+            $rules = get_input_value('r', RCUBE_INPUT_GET);
+            if (!empty($rules)) {
+                $i = 0;
+                foreach ($rules as $rule) {
+                    list($header, $value) = explode(':', $rule, 2);
+                    $tests[$i] = array(
+                        'type' => 'contains',
+                        'test' => 'header',
+                        'arg1' => $header,
+                        'arg2' => $value,
+                    );
+                    $i++;
+                }
+
+                $this->form = array(
+                    'join'  => count($tests) > 1 ? 'allof' : 'anyof',
+                    'name'  => '',
+                    'tests' => $tests,
+                    'actions' => array(
+                        0 => array('type' => 'fileinto'),
+                        1 => array('type' => 'stop'),
+                    ),
+                );
+            }
+        }
 
         $this->managesieve_send();
     }
 
     function managesieve_save()
     {
+        // load localization
+        $this->add_texts('localization/', array('filters','managefilters'));
+
+        // include main js script
+        if ($this->api->output->type == 'html') {
+            $this->include_script('managesieve.js');
+        }
+
         // Init plugin and handle managesieve connection
         $error = $this->managesieve_start();
 
         // filters set add action
         if (!empty($_POST['_newset'])) {
-            $name = get_input_value('_name', RCUBE_INPUT_POST);
-            $copy = get_input_value('_copy', RCUBE_INPUT_POST);
-            $from = get_input_value('_from', RCUBE_INPUT_POST);
-
-            if (!$name)
-                $error = 'managesieve.emptyname';
-            else if (mb_strlen($name)>128)
-                $error = 'managesieve.nametoolong';
+
+            $name       = get_input_value('_name', RCUBE_INPUT_POST, true);
+            $copy       = get_input_value('_copy', RCUBE_INPUT_POST, true);
+            $from       = get_input_value('_from', RCUBE_INPUT_POST);
+            $exceptions = $this->rc->config->get('managesieve_filename_exceptions');
+            $kolab      = $this->rc->config->get('managesieve_kolab_master');
+            $name_uc    = mb_strtolower($name);
+            $list       = $this->list_scripts();
+
+            if (!$name) {
+                $this->errors['name'] = $this->gettext('cannotbeempty');
+            }
+            else if (mb_strlen($name) > 128) {
+                $this->errors['name'] = $this->gettext('nametoolong');
+            }
+            else if (!empty($exceptions) && in_array($name, (array)$exceptions)) {
+                $this->errors['name'] = $this->gettext('namereserved');
+            }
+            else if (!empty($kolab) && in_array($name_uc, array('MASTER', 'USER', 'MANAGEMENT'))) {
+                $this->errors['name'] = $this->gettext('namereserved');
+            }
+            else if (in_array($name, $list)) {
+                $this->errors['name'] = $this->gettext('setexist');
+            }
             else if ($from == 'file') {
                 // from file
                 if (is_uploaded_file($_FILES['_file']['tmp_name'])) {
@@ -325,13 +551,12 @@ class managesieve extends rcube_plugin
                     // for security don't save script directly
                     // check syntax before, like this...
                     $this->sieve->load_script($file);
-                    if (!$this->sieve->save($name)) {
-                        $error = 'managesieve.setcreateerror';
+                    if (!$this->save_script($name)) {
+                        $this->errors['file'] = $this->gettext('setcreateerror');
                     }
                 }
                 else {  // upload failed
                     $err = $_FILES['_file']['error'];
-                    $error = true;
 
                     if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
                         $msg = rcube_label(array('name' => 'filesizeerror',
@@ -339,7 +564,7 @@ class managesieve extends rcube_plugin
                                 show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
                     }
                     else {
-                        $error = 'fileuploaderror';
+                        $this->errors['file'] = $this->gettext('fileuploaderror');
                     }
                 }
             }
@@ -347,12 +572,19 @@ class managesieve extends rcube_plugin
                 $error = 'managesieve.setcreateerror';
             }
 
-            if (!$error) {
+            if (!$error && empty($this->errors)) {
+                // Find position of the new script on the list
+                $list[] = $name;
+                asort($list, SORT_LOCALE_STRING);
+                $list  = array_values($list);
+                $index = array_search($name, $list);
+
                 $this->rc->output->show_message('managesieve.setcreated', 'confirmation');
-                $this->rc->output->command('parent.managesieve_reload', $name);
+                $this->rc->output->command('parent.managesieve_updatelist', 'setadd',
+                    array('name' => $name, 'index' => $index));
             } else if ($msg) {
                 $this->rc->output->command('display_message', $msg, 'error');
-            } else {
+            } else if ($error) {
                 $this->rc->output->show_message($error, 'error');
             }
         }
@@ -363,22 +595,27 @@ class managesieve extends rcube_plugin
             $join = trim(get_input_value('_join', RCUBE_INPUT_POST));
 
             // and arrays
-            $headers = $_POST['_header'];
-            $cust_headers = $_POST['_custom_header'];
-            $ops = $_POST['_rule_op'];
-            $sizeops = $_POST['_rule_size_op'];
-            $sizeitems = $_POST['_rule_size_item'];
-            $sizetargets = $_POST['_rule_size_target'];
-            $targets = $_POST['_rule_target'];
-            $act_types = $_POST['_action_type'];
-            $mailboxes = $_POST['_action_mailbox'];
-            $act_targets = $_POST['_action_target'];
-            $area_targets = $_POST['_action_target_area'];
-            $reasons = $_POST['_action_reason'];
-            $addresses = $_POST['_action_addresses'];
-            $days = $_POST['_action_days'];
-            $subject = $_POST['_action_subject'];
-            $flags = $_POST['_action_flags'];
+            $headers        = get_input_value('_header', RCUBE_INPUT_POST);
+            $cust_headers   = get_input_value('_custom_header', RCUBE_INPUT_POST);
+            $ops            = get_input_value('_rule_op', RCUBE_INPUT_POST);
+            $sizeops        = get_input_value('_rule_size_op', RCUBE_INPUT_POST);
+            $sizeitems      = get_input_value('_rule_size_item', RCUBE_INPUT_POST);
+            $sizetargets    = get_input_value('_rule_size_target', RCUBE_INPUT_POST);
+            $targets        = get_input_value('_rule_target', RCUBE_INPUT_POST, true);
+            $mods           = get_input_value('_rule_mod', RCUBE_INPUT_POST);
+            $mod_types      = get_input_value('_rule_mod_type', RCUBE_INPUT_POST);
+            $body_trans     = get_input_value('_rule_trans', RCUBE_INPUT_POST);
+            $body_types     = get_input_value('_rule_trans_type', RCUBE_INPUT_POST, true);
+            $comparators    = get_input_value('_rule_comp', RCUBE_INPUT_POST);
+            $act_types      = get_input_value('_action_type', RCUBE_INPUT_POST, true);
+            $mailboxes      = get_input_value('_action_mailbox', RCUBE_INPUT_POST, true);
+            $act_targets    = get_input_value('_action_target', RCUBE_INPUT_POST, true);
+            $area_targets   = get_input_value('_action_target_area', RCUBE_INPUT_POST, true);
+            $reasons        = get_input_value('_action_reason', RCUBE_INPUT_POST, true);
+            $addresses      = get_input_value('_action_addresses', RCUBE_INPUT_POST, true);
+            $days           = get_input_value('_action_days', RCUBE_INPUT_POST);
+            $subject        = get_input_value('_action_subject', RCUBE_INPUT_POST, true);
+            $flags          = get_input_value('_action_flags', RCUBE_INPUT_POST);
 
             // we need a "hack" for radiobuttons
             foreach ($sizeitems as $item)
@@ -407,23 +644,101 @@ class managesieve extends rcube_plugin
             }
             else {
                 foreach ($headers as $idx => $header) {
-                    $header = $this->strip_value($header);
-                    $target = $this->strip_value($targets[$idx], true);
-                    $op     = $this->strip_value($ops[$idx]);
+                    $header     = $this->strip_value($header);
+                    $target     = $this->strip_value($targets[$idx], true);
+                    $operator   = $this->strip_value($ops[$idx]);
+                    $comparator = $this->strip_value($comparators[$idx]);
+
+                    if ($header == 'size') {
+                        $sizeop     = $this->strip_value($sizeops[$idx]);
+                        $sizeitem   = $this->strip_value($items[$idx]);
+                        $sizetarget = $this->strip_value($sizetargets[$idx]);
+
+                        $this->form['tests'][$i]['test'] = 'size';
+                        $this->form['tests'][$i]['type'] = $sizeop;
+                        $this->form['tests'][$i]['arg']  = $sizetarget;
+
+                        if ($sizetarget == '')
+                            $this->errors['tests'][$i]['sizetarget'] = $this->gettext('cannotbeempty');
+                        else if (!preg_match('/^[0-9]+(K|M|G)?$/i', $sizetarget.$sizeitem, $m)) {
+                            $this->errors['tests'][$i]['sizetarget'] = $this->gettext('forbiddenchars');
+                            $this->form['tests'][$i]['item'] = $sizeitem;
+                        }
+                        else
+                            $this->form['tests'][$i]['arg'] .= $m[1];
+                    }
+                    else if ($header == 'body') {
+                        $trans      = $this->strip_value($body_trans[$idx]);
+                        $trans_type = $this->strip_value($body_types[$idx], true);
 
-                    // normal header
-                    if (in_array($header, $this->headers)) {
-                        if (preg_match('/^not/', $op))
+                        if (preg_match('/^not/', $operator))
                             $this->form['tests'][$i]['not'] = true;
-                        $type = preg_replace('/^not/', '', $op);
+                        $type = preg_replace('/^not/', '', $operator);
+
+                        if ($type == 'exists') {
+                            $this->errors['tests'][$i]['op'] = true;
+                        }
+
+                        $this->form['tests'][$i]['test'] = 'body';
+                        $this->form['tests'][$i]['type'] = $type;
+                        $this->form['tests'][$i]['arg']  = $target;
+
+                        if ($target == '' && $type != 'exists')
+                            $this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty');
+                        else if (preg_match('/^(value|count)-/', $type) && !preg_match('/[0-9]+/', $target))
+                            $this->errors['tests'][$i]['target'] = $this->gettext('forbiddenchars');
+
+                        $this->form['tests'][$i]['part'] = $trans;
+                        if ($trans == 'content') {
+                            $this->form['tests'][$i]['content'] = $trans_type;
+                        }
+                    }
+                    else {
+                        $cust_header = $headers = $this->strip_value($cust_headers[$idx]);
+                        $mod      = $this->strip_value($mods[$idx]);
+                        $mod_type = $this->strip_value($mod_types[$idx]);
+
+                        if (preg_match('/^not/', $operator))
+                            $this->form['tests'][$i]['not'] = true;
+                        $type = preg_replace('/^not/', '', $operator);
+
+                        if ($header == '...') {
+                            $headers = preg_split('/[\s,]+/', $cust_header, -1, PREG_SPLIT_NO_EMPTY);
+
+                            if (!count($headers))
+                                $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty');
+                            else {
+                                foreach ($headers as $hr)
+                                    if (!preg_match('/^[a-z0-9-]+$/i', $hr))
+                                        $this->errors['tests'][$i]['header'] = $this->gettext('forbiddenchars');
+                            }
+
+                            if (empty($this->errors['tests'][$i]['header']))
+                                $cust_header = (is_array($headers) && count($headers) == 1) ? $headers[0] : $headers;
+                        }
 
                         if ($type == 'exists') {
                             $this->form['tests'][$i]['test'] = 'exists';
-                            $this->form['tests'][$i]['arg'] = $header;
+                            $this->form['tests'][$i]['arg'] = $header == '...' ? $cust_header : $header;
                         }
                         else {
+                            $test   = 'header';
+                            $header = $header == '...' ? $cust_header : $header;
+
+                            if ($mod == 'address' || $mod == 'envelope') {
+                                $found = false;
+                                if (empty($this->errors['tests'][$i]['header'])) {
+                                    foreach ((array)$header as $hdr) {
+                                        if (!in_array(strtolower(trim($hdr)), $this->addr_headers))
+                                            $found = true;
+                                    }
+                                }
+                                if (!$found)
+                                    $test = $mod;
+                            }
+
                             $this->form['tests'][$i]['type'] = $type;
-                            $this->form['tests'][$i]['test'] = 'header';
+                            $this->form['tests'][$i]['test'] = $test;
                             $this->form['tests'][$i]['arg1'] = $header;
                             $this->form['tests'][$i]['arg2'] = $target;
 
@@ -431,65 +746,20 @@ class managesieve extends rcube_plugin
                                 $this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty');
                             else if (preg_match('/^(value|count)-/', $type) && !preg_match('/[0-9]+/', $target))
                                 $this->errors['tests'][$i]['target'] = $this->gettext('forbiddenchars');
+
+                            if ($mod) {
+                                $this->form['tests'][$i]['part'] = $mod_type;
+                            }
                         }
                     }
-                    else
-                        switch ($header) {
-                        case 'size':
-                            $sizeop     = $this->strip_value($sizeops[$idx]);
-                            $sizeitem   = $this->strip_value($items[$idx]);
-                            $sizetarget = $this->strip_value($sizetargets[$idx]);
-
-                            $this->form['tests'][$i]['test'] = 'size';
-                            $this->form['tests'][$i]['type'] = $sizeop;
-                            $this->form['tests'][$i]['arg']  = $sizetarget.$sizeitem;
-
-                            if ($sizetarget == '')
-                                $this->errors['tests'][$i]['sizetarget'] = $this->gettext('cannotbeempty');
-                            else if (!preg_match('/^[0-9]+(K|M|G)*$/i', $sizetarget))
-                                $this->errors['tests'][$i]['sizetarget'] = $this->gettext('forbiddenchars');
-                            break;
-                        case '...':
-                            $cust_header = $headers = $this->strip_value($cust_headers[$idx]);
-
-                            if (preg_match('/^not/', $op))
-                                $this->form['tests'][$i]['not'] = true;
-                            $type = preg_replace('/^not/', '', $op);
-
-                            if ($cust_header == '')
-                                $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty');
-                            else {
-                                $headers = preg_split('/[\s,]+/', $cust_header, -1, PREG_SPLIT_NO_EMPTY);
-
-                                if (!count($headers))
-                                    $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty');
-                                else {
-                                    foreach ($headers as $hr)
-                                        if (!preg_match('/^[a-z0-9-]+$/i', $hr))
-                                            $this->errors['tests'][$i]['header'] = $this->gettext('forbiddenchars');
-                                }
-                            }
 
-                            if (empty($this->errors['tests'][$i]['header']))
-                                $cust_header = (is_array($headers) && count($headers) == 1) ? $headers[0] : $headers;
+                    if ($header != 'size' && $comparator) {
+                        if (preg_match('/^(value|count)/', $this->form['tests'][$i]['type']))
+                            $comparator = 'i;ascii-numeric';
+
+                        $this->form['tests'][$i]['comparator'] = $comparator;
+                    }
 
-                            if ($type == 'exists') {
-                                $this->form['tests'][$i]['test'] = 'exists';
-                                $this->form['tests'][$i]['arg']  = $cust_header;
-                            }
-                            else {
-                                $this->form['tests'][$i]['test'] = 'header';
-                                $this->form['tests'][$i]['type'] = $type;
-                                $this->form['tests'][$i]['arg1'] = $cust_header;
-                                $this->form['tests'][$i]['arg2'] = $target;
-
-                                if ($target == '')
-                                    $this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty');
-                                else if (preg_match('/^(value|count)-/', $type) && !preg_match('/[0-9]+/', $target))
-                                    $this->errors['tests'][$i]['target'] = $this->gettext('forbiddenchars');
-                            }
-                            break;
-                        }
                     $i++;
                 }
             }
@@ -583,7 +853,7 @@ class managesieve extends rcube_plugin
                 $i++;
             }
 
-            if (!$this->errors) {
+            if (!$this->errors && !$error) {
                 // zapis skryptu
                 if (!isset($this->script[$fid])) {
                     $fid = $this->sieve->script->add_rule($this->form);
@@ -592,15 +862,23 @@ class managesieve extends rcube_plugin
                     $fid = $this->sieve->script->update_rule($fid, $this->form);
 
                 if ($fid !== false)
-                    $save = $this->sieve->save();
+                    $save = $this->save_script();
 
                 if ($save && $fid !== false) {
                     $this->rc->output->show_message('managesieve.filtersaved', 'confirmation');
-                    $this->rc->output->add_script(
-                        sprintf("rcmail.managesieve_updatelist('%s', '%s', %d, %d);",
-                            isset($new) ? 'add' : 'update', Q($this->form['name']),
-                            $fid, $this->form['disabled']),
-                        'foot');
+                    if ($this->rc->task != 'mail') {
+                        $this->rc->output->command('parent.managesieve_updatelist',
+                            isset($new) ? 'add' : 'update',
+                            array(
+                                'name' => Q($this->form['name']),
+                                'id' => $fid,
+                                'disabled' => $this->form['disabled']
+                        ));
+                    }
+                    else {
+                        $this->rc->output->command('managesieve_dialog_close');
+                        $this->rc->output->send('iframe');
+                    }
                 }
                 else {
                     $this->rc->output->show_message('managesieve.filtersaveerror', 'error');
@@ -636,14 +914,9 @@ class managesieve extends rcube_plugin
             $attrib['id'] = 'rcmfilterslist';
 
         // define list of cols to be displayed
-        $a_show_cols = array('managesieve.filtername');
+        $a_show_cols = array('name');
 
-        foreach($this->script as $idx => $filter)
-            $result[] = array(
-                'managesieve.filtername' => $filter['name'],
-                'id' => $idx,
-                'class' => $filter['disabled'] ? 'disabled' : '',
-            );
+        $result = $this->list_rules();
 
         // create XHTML table
         $out = rcube_table_output($attrib, $result, $a_show_cols, 'id');
@@ -659,35 +932,56 @@ class managesieve extends rcube_plugin
     }
 
     // return the filters list as <SELECT>
-    function filtersets_list($attrib)
+    function filtersets_list($attrib, $no_env = false)
     {
         // add id to message list table if not specified
         if (!strlen($attrib['id']))
             $attrib['id'] = 'rcmfiltersetslist';
 
-        $list = $this->sieve->get_scripts();
-        $active = $this->sieve->get_active();
-
-        $select = new html_select(array('name' => '_set', 'id' => $attrib['id'],
-            'onchange' => 'rcmail.managesieve_set()'));
+        $list = $this->list_scripts();
 
         if ($list) {
             asort($list, SORT_LOCALE_STRING);
+        }
+
+        if (!empty($attrib['type']) && $attrib['type'] == 'list') {
+            // define list of cols to be displayed
+            $a_show_cols = array('name');
+
+            if ($list) {
+                foreach ($list as $idx => $set) {
+                    $scripts['S'.$idx] = $set;
+                    $result[] = array(
+                        'name' => Q($set),
+                        'id' => 'S'.$idx,
+                        'class' => !in_array($set, $this->active) ? 'disabled' : '',
+                    );
+                }
+            }
+
+            // create XHTML table
+            $out = rcube_table_output($attrib, $result, $a_show_cols, 'id');
 
-            foreach ($list as $set)
-                $select->add($set . ($set == $active ? ' ('.$this->gettext('active').')' : ''), $set);
+            $this->rc->output->set_env('filtersets', $scripts);
+            $this->rc->output->include_script('list.js');
         }
+        else {
+            $select = new html_select(array('name' => '_set', 'id' => $attrib['id'],
+                'onchange' => $this->rc->task != 'mail' ? 'rcmail.managesieve_set()' : ''));
+
+            if ($list) {
+                foreach ($list as $set)
+                    $select->add($set, $set);
+            }
 
-        $out = $select->show($this->sieve->current);
+            $out = $select->show($this->sieve->current);
+        }
 
         // set client env
-        $this->rc->output->add_gui_object('filtersetslist', $attrib['id']);
-        $this->rc->output->add_label(
-            'managesieve.setdeleteconfirm',
-            'managesieve.active',
-            'managesieve.filtersetact',
-            'managesieve.filtersetdeact'
-        );
+        if (!$no_env) {
+            $this->rc->output->add_gui_object('filtersetslist', $attrib['id']);
+            $this->rc->output->add_label('managesieve.setdeleteconfirm');
+        }
 
         return $out;
     }
@@ -737,16 +1031,18 @@ class managesieve extends rcube_plugin
         $out .= sprintf('<label for="%s">%s</label> ', 'from_none', Q($this->gettext('none')));
 
         // filters set list
-        $list   = $this->sieve->get_scripts();
-        $active = $this->sieve->get_active();
-
+        $list   = $this->list_scripts();
         $select = new html_select(array('name' => '_copy', 'id' => '_copy'));
 
         if (is_array($list)) {
             asort($list, SORT_LOCALE_STRING);
 
-            foreach ($list as $set)
-                $select->add($set . ($set == $active ? ' ('.$this->gettext('active').')' : ''), $set);
+            if (!$copy)
+                $copy = $_SESSION['managesieve_current'];
+
+            foreach ($list as $set) {
+                $select->add($set, $set);
+            }
 
             $out .= '<br /><input type="radio" id="from_set" name="_from" value="set"'
                 .($selected=='set' ? ' checked="checked"' : '').'></input>';
@@ -756,7 +1052,7 @@ class managesieve extends rcube_plugin
 
         // script upload box
         $upload = new html_inputfield(array('name' => '_file', 'id' => '_file', 'size' => 30,
-            'type' => 'file', 'class' => ($this->errors['name'] ? 'error' : '')));
+            'type' => 'file', 'class' => ($this->errors['file'] ? 'error' : '')));
 
         $out .= '<br /><input type="radio" id="from_file" name="_from" value="file"'
             .($selected=='file' ? ' checked="checked"' : '').'></input>';
@@ -766,6 +1062,13 @@ class managesieve extends rcube_plugin
 
         $this->rc->output->add_gui_object('sieveform', 'filtersetform');
 
+        if ($this->errors['name'])
+            $this->add_tip('_name', $this->errors['name'], true);
+        if ($this->errors['file'])
+            $this->add_tip('_file', $this->errors['file'], true);
+
+        $this->print_tips();
+
         return $out;
     }
 
@@ -803,10 +1106,17 @@ class managesieve extends rcube_plugin
         else
             $input_name = $input_name->show();
 
-        $out .= sprintf("\n<label for=\"%s\"><b>%s:</b></label> %s<br /><br />\n",
+        $out .= sprintf("\n<label for=\"%s\"><b>%s:</b></label> %s\n",
             $field_id, Q($this->gettext('filtername')), $input_name);
 
-        $out .= '<fieldset><legend>' . Q($this->gettext('messagesrules')) . "</legend>\n";
+        // filter set selector
+        if ($this->rc->task == 'mail') {
+            $out .= sprintf("\n&nbsp;<label for=\"%s\"><b>%s:</b></label> %s\n",
+                $field_id, Q($this->gettext('filterset')),
+                $this->filtersets_list(array('id' => 'sievescriptname'), true));
+        }
+
+        $out .= '<br /><br /><fieldset><legend>' . Q($this->gettext('messagesrules')) . "</legend>\n";
 
         // any, allof, anyof radio buttons
         $field_id = '_allof';
@@ -882,50 +1192,60 @@ class managesieve extends rcube_plugin
         $rule     = isset($this->form) ? $this->form['tests'][$id] : $this->script[$fid]['tests'][$id];
         $rows_num = isset($this->form) ? sizeof($this->form['tests']) : sizeof($this->script[$fid]['tests']);
 
-        $out = $div ? '<div class="rulerow" id="rulerow' .$id .'">'."\n" : '';
-
-        $out .= '<table><tr><td class="rowactions">';
-
         // headers select
         $select_header = new html_select(array('name' => "_header[]", 'id' => 'header'.$id,
             'onchange' => 'rule_header_select(' .$id .')'));
         foreach($this->headers as $name => $val)
             $select_header->add(Q($this->gettext($name)), Q($val));
+        if (in_array('body', $this->exts))
+            $select_header->add(Q($this->gettext('body')), 'body');
         $select_header->add(Q($this->gettext('size')), 'size');
         $select_header->add(Q($this->gettext('...')), '...');
 
         // TODO: list arguments
+        $aout = '';
 
-        if ((isset($rule['test']) && $rule['test'] == 'header')
-            && !is_array($rule['arg1']) && in_array($rule['arg1'], $this->headers))
-            $out .= $select_header->show($rule['arg1']);
+        if ((isset($rule['test']) && in_array($rule['test'], array('header', 'address', 'envelope')))
+            && !is_array($rule['arg1']) && in_array($rule['arg1'], $this->headers)
+        ) {
+            $aout .= $select_header->show($rule['arg1']);
+        }
         else if ((isset($rule['test']) && $rule['test'] == 'exists')
-            && !is_array($rule['arg']) && in_array($rule['arg'], $this->headers))
-            $out .= $select_header->show($rule['arg']);
+            && !is_array($rule['arg']) && in_array($rule['arg'], $this->headers)
+        ) {
+            $aout .= $select_header->show($rule['arg']);
+        }
         else if (isset($rule['test']) && $rule['test'] == 'size')
-            $out .= $select_header->show('size');
+            $aout .= $select_header->show('size');
+        else if (isset($rule['test']) && $rule['test'] == 'body')
+            $aout .= $select_header->show('body');
         else if (isset($rule['test']) && $rule['test'] != 'true')
-            $out .= $select_header->show('...');
+            $aout .= $select_header->show('...');
         else
-            $out .= $select_header->show();
-
-        $out .= '</td><td class="rowtargets">';
+            $aout .= $select_header->show();
 
-        if ((isset($rule['test']) && $rule['test'] == 'header')
-            && (is_array($rule['arg1']) || !in_array($rule['arg1'], $this->headers)))
-            $custom = is_array($rule['arg1']) ? implode(', ', $rule['arg1']) : $rule['arg1'];
-        else if ((isset($rule['test']) && $rule['test'] == 'exists')
-            && (is_array($rule['arg']) || !in_array($rule['arg'], $this->headers)))
-            $custom = is_array($rule['arg']) ? implode(', ', $rule['arg']) : $rule['arg'];
+        if (isset($rule['test']) && in_array($rule['test'], array('header', 'address', 'envelope'))) {
+            if (is_array($rule['arg1']))
+                $custom = implode(', ', $rule['arg1']);
+            else if (!in_array($rule['arg1'], $this->headers))
+                $custom = $rule['arg1'];
+        }
+        else if (isset($rule['test']) && $rule['test'] == 'exists') {
+            if (is_array($rule['arg']))
+                $custom = implode(', ', $rule['arg']);
+            else if (!in_array($rule['arg'], $this->headers))
+                $custom = $rule['arg'];
+        }
 
-        $out .= '<div id="custom_header' .$id. '" style="display:' .(isset($custom) ? 'inline' : 'none'). '">
+        $tout = '<div id="custom_header' .$id. '" style="display:' .(isset($custom) ? 'inline' : 'none'). '">
             <input type="text" name="_custom_header[]" id="custom_header_i'.$id.'" '
             . $this->error_class($id, 'test', 'header', 'custom_header_i')
-            .' value="' .Q($custom). '" size="20" />&nbsp;</div>' . "\n";
+            .' value="' .Q($custom). '" size="15" />&nbsp;</div>' . "\n";
 
         // matching type select (operator)
         $select_op = new html_select(array('name' => "_rule_op[]", 'id' => 'rule_op'.$id,
             'style' => 'display:' .($rule['test']!='size' ? 'inline' : 'none'),
+            'class' => 'operator_selector',
             'onchange' => 'rule_op_select('.$id.')'));
         $select_op->add(Q($this->gettext('filtercontains')), 'contains');
         $select_op->add(Q($this->gettext('filternotcontains')), 'notcontains');
@@ -935,54 +1255,64 @@ class managesieve extends rcube_plugin
         $select_op->add(Q($this->gettext('filternotexists')), 'notexists');
         $select_op->add(Q($this->gettext('filtermatches')), 'matches');
         $select_op->add(Q($this->gettext('filternotmatches')), 'notmatches');
-               if (in_array('regex', $this->exts)) {
+        if (in_array('regex', $this->exts)) {
             $select_op->add(Q($this->gettext('filterregex')), 'regex');
             $select_op->add(Q($this->gettext('filternotregex')), 'notregex');
         }
-               if (in_array('relational', $this->exts)) {
-                       $select_op->add(Q($this->gettext('countisgreaterthan')), 'count-gt');
-                       $select_op->add(Q($this->gettext('countisgreaterthanequal')), 'count-ge');
-                       $select_op->add(Q($this->gettext('countislessthan')), 'count-lt');
-                       $select_op->add(Q($this->gettext('countislessthanequal')), 'count-le');
-                       $select_op->add(Q($this->gettext('countequals')), 'count-eq');
-                       $select_op->add(Q($this->gettext('countnotequals')), 'count-ne');
-                       $select_op->add(Q($this->gettext('valueisgreaterthan')), 'value-gt');
-                       $select_op->add(Q($this->gettext('valueisgreaterthanequal')), 'value-ge');
-                       $select_op->add(Q($this->gettext('valueislessthan')), 'value-lt');
-                       $select_op->add(Q($this->gettext('valueislessthanequal')), 'value-le');
-                       $select_op->add(Q($this->gettext('valueequals')), 'value-eq');
-                       $select_op->add(Q($this->gettext('valuenotequals')), 'value-ne');
-               }
+        if (in_array('relational', $this->exts)) {
+            $select_op->add(Q($this->gettext('countisgreaterthan')), 'count-gt');
+            $select_op->add(Q($this->gettext('countisgreaterthanequal')), 'count-ge');
+            $select_op->add(Q($this->gettext('countislessthan')), 'count-lt');
+            $select_op->add(Q($this->gettext('countislessthanequal')), 'count-le');
+            $select_op->add(Q($this->gettext('countequals')), 'count-eq');
+            $select_op->add(Q($this->gettext('countnotequals')), 'count-ne');
+            $select_op->add(Q($this->gettext('valueisgreaterthan')), 'value-gt');
+            $select_op->add(Q($this->gettext('valueisgreaterthanequal')), 'value-ge');
+            $select_op->add(Q($this->gettext('valueislessthan')), 'value-lt');
+            $select_op->add(Q($this->gettext('valueislessthanequal')), 'value-le');
+            $select_op->add(Q($this->gettext('valueequals')), 'value-eq');
+            $select_op->add(Q($this->gettext('valuenotequals')), 'value-ne');
+        }
 
         // target input (TODO: lists)
 
-        if ($rule['test'] == 'header') {
-            $out .= $select_op->show(($rule['not'] ? 'not' : '').$rule['type']);
+        if (in_array($rule['test'], array('header', 'address', 'envelope'))) {
+            $test   = ($rule['not'] ? 'not' : '').($rule['type'] ? $rule['type'] : 'is');
             $target = $rule['arg2'];
         }
+        else if ($rule['test'] == 'body') {
+            $test   = ($rule['not'] ? 'not' : '').($rule['type'] ? $rule['type'] : 'is');
+            $target = $rule['arg'];
+        }
         else if ($rule['test'] == 'size') {
-            $out .= $select_op->show();
-            if (preg_match('/^([0-9]+)(K|M|G)*$/', $rule['arg'], $matches)) {
+            $test   = '';
+            $target = '';
+            if (preg_match('/^([0-9]+)(K|M|G)?$/', $rule['arg'], $matches)) {
                 $sizetarget = $matches[1];
                 $sizeitem = $matches[2];
             }
+            else {
+                $sizetarget = $rule['arg'];
+                $sizeitem = $rule['item'];
+            }
         }
         else {
-            $out .= $select_op->show(($rule['not'] ? 'not' : '').$rule['test']);
-            $target = '';
+            $test   = ($rule['not'] ? 'not' : '').$rule['test'];
+            $target =  '';
         }
 
-        $out .= '<input type="text" name="_rule_target[]" id="rule_target' .$id. '"
+        $tout .= $select_op->show($test);
+        $tout .= '<input type="text" name="_rule_target[]" id="rule_target' .$id. '"
             value="' .Q($target). '" size="20" ' . $this->error_class($id, 'test', 'target', 'rule_target')
             . ' style="display:' . ($rule['test']!='size' && $rule['test'] != 'exists' ? 'inline' : 'none') . '" />'."\n";
 
         $select_size_op = new html_select(array('name' => "_rule_size_op[]", 'id' => 'rule_size_op'.$id));
-        $select_size_op->add(Q($this->gettext('filterunder')), 'under');
         $select_size_op->add(Q($this->gettext('filterover')), 'over');
+        $select_size_op->add(Q($this->gettext('filterunder')), 'under');
 
-        $out .= '<div id="rule_size' .$id. '" style="display:' . ($rule['test']=='size' ? 'inline' : 'none') .'">';
-        $out .= $select_size_op->show($rule['test']=='size' ? $rule['type'] : '');
-        $out .= '<input type="text" name="_rule_size_target[]" id="rule_size_i'.$id.'" value="'.$sizetarget.'" size="10" ' 
+        $tout .= '<div id="rule_size' .$id. '" style="display:' . ($rule['test']=='size' ? 'inline' : 'none') .'">';
+        $tout .= $select_size_op->show($rule['test']=='size' ? $rule['type'] : '');
+        $tout .= '<input type="text" name="_rule_size_target[]" id="rule_size_i'.$id.'" value="'.$sizetarget.'" size="10" ' 
             . $this->error_class($id, 'test', 'sizetarget', 'rule_size_i') .' />
             <input type="radio" name="_rule_size_item['.$id.']" value=""'
                 . (!$sizeitem ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('B').'
@@ -992,17 +1322,92 @@ class managesieve extends rcube_plugin
                 . ($sizeitem=='M' ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('MB').'
             <input type="radio" name="_rule_size_item['.$id.']" value="G"'
                 . ($sizeitem=='G' ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('GB');
-        $out .= '</div>';
+        $tout .= '</div>';
+
+        // Advanced modifiers (address, envelope)
+        $select_mod = new html_select(array('name' => "_rule_mod[]", 'id' => 'rule_mod_op'.$id,
+            'onchange' => 'rule_mod_select(' .$id .')'));
+        $select_mod->add(Q($this->gettext('none')), '');
+        $select_mod->add(Q($this->gettext('address')), 'address');
+        if (in_array('envelope', $this->exts))
+            $select_mod->add(Q($this->gettext('envelope')), 'envelope');
+
+        $select_type = new html_select(array('name' => "_rule_mod_type[]", 'id' => 'rule_mod_type'.$id));
+        $select_type->add(Q($this->gettext('allparts')), 'all');
+        $select_type->add(Q($this->gettext('domain')), 'domain');
+        $select_type->add(Q($this->gettext('localpart')), 'localpart');
+        if (in_array('subaddress', $this->exts)) {
+            $select_type->add(Q($this->gettext('user')), 'user');
+            $select_type->add(Q($this->gettext('detail')), 'detail');
+        }
+
+        $need_mod = $rule['test'] != 'size' && $rule['test'] != 'body';
+        $mout = '<div id="rule_mod' .$id. '" class="adv" style="display:' . ($need_mod ? 'block' : 'none') .'">';
+        $mout .= ' <span>';
+        $mout .= Q($this->gettext('modifier')) . ' ';
+        $mout .= $select_mod->show($rule['test']);
+        $mout .= '</span>';
+        $mout .= ' <span id="rule_mod_type' . $id . '"';
+        $mout .= ' style="display:' . (in_array($rule['test'], array('address', 'envelope')) ? 'inline' : 'none') .'">';
+        $mout .= Q($this->gettext('modtype')) . ' ';
+        $mout .= $select_type->show($rule['part']);
+        $mout .= '</span>';
+        $mout .= '</div>';
+
+        // Advanced modifiers (body transformations)
+        $select_mod = new html_select(array('name' => "_rule_trans[]", 'id' => 'rule_trans_op'.$id,
+            'onchange' => 'rule_trans_select(' .$id .')'));
+        $select_mod->add(Q($this->gettext('text')), 'text');
+        $select_mod->add(Q($this->gettext('undecoded')), 'raw');
+        $select_mod->add(Q($this->gettext('contenttype')), 'content');
+
+        $mout .= '<div id="rule_trans' .$id. '" class="adv" style="display:' . ($rule['test'] == 'body' ? 'block' : 'none') .'">';
+        $mout .= ' <span>';
+        $mout .= Q($this->gettext('modifier')) . ' ';
+        $mout .= $select_mod->show($rule['part']);
+        $mout .= '<input type="text" name="_rule_trans_type[]" id="rule_trans_type'.$id
+            . '" value="'.(is_array($rule['content']) ? implode(',', $rule['content']) : $rule['content'])
+            .'" size="20" style="display:' . ($rule['part'] == 'content' ? 'inline' : 'none') .'"'
+            . $this->error_class($id, 'test', 'part', 'rule_trans_type') .' />';
+        $mout .= '</span>';
+        $mout .= '</div>';
+
+        // Advanced modifiers (body transformations)
+        $select_comp = new html_select(array('name' => "_rule_comp[]", 'id' => 'rule_comp_op'.$id));
+        $select_comp->add(Q($this->gettext('default')), '');
+        $select_comp->add(Q($this->gettext('octet')), 'i;octet');
+        $select_comp->add(Q($this->gettext('asciicasemap')), 'i;ascii-casemap');
+        if (in_array('comparator-i;ascii-numeric', $this->exts)) {
+            $select_comp->add(Q($this->gettext('asciinumeric')), 'i;ascii-numeric');
+        }
+
+        $mout .= '<div id="rule_comp' .$id. '" class="adv" style="display:' . ($rule['test'] != 'size' ? 'block' : 'none') .'">';
+        $mout .= ' <span>';
+        $mout .= Q($this->gettext('comparator')) . ' ';
+        $mout .= $select_comp->show($rule['comparator']);
+        $mout .= '</span>';
+        $mout .= '</div>';
+
+        // Build output table
+        $out = $div ? '<div class="rulerow" id="rulerow' .$id .'">'."\n" : '';
+        $out .= '<table><tr>';
+        $out .= '<td class="advbutton">';
+        $out .= '<a href="#" id="ruleadv' . $id .'" title="'. Q($this->gettext('advancedopts')). '"
+            onclick="rule_adv_switch(' . $id .', this)" class="show">&nbsp;&nbsp;</a>';
+        $out .= '</td>';
+        $out .= '<td class="rowactions">' . $aout . '</td>';
+        $out .= '<td class="rowtargets">' . $tout . "\n";
+        $out .= '<div id="rule_advanced' .$id. '" style="display:none">' . $mout . '</div>';
         $out .= '</td>';
 
         // add/del buttons
         $out .= '<td class="rowbuttons">';
-        $out .= '<input type="button" id="ruleadd' . $id .'" value="'. Q($this->gettext('add')). '"
-            onclick="rcmail.managesieve_ruleadd(' . $id .')" class="button" /> ';
-        $out .= '<input type="button" id="ruledel' . $id .'" value="'. Q($this->gettext('del')). '"
-            onclick="rcmail.managesieve_ruledel(' . $id .')" class="button' . ($rows_num<2 ? ' disabled' : '') .'"'
-            . ($rows_num<2 ? ' disabled="disabled"' : '') .' />';
-        $out .= '</td></tr></table>';
+        $out .= '<a href="#" id="ruleadd' . $id .'" title="'. Q($this->gettext('add')). '"
+            onclick="rcmail.managesieve_ruleadd(' . $id .')" class="button add"></a>';
+        $out .= '<a href="#" id="ruledel' . $id .'" title="'. Q($this->gettext('del')). '"
+            onclick="rcmail.managesieve_ruledel(' . $id .')" class="button del' . ($rows_num<2 ? ' disabled' : '') .'"></a>';
+        $out .= '</td>';
+        $out .= '</tr></table>';
 
         $out .= $div ? "</div>\n" : '';
 
@@ -1019,7 +1424,7 @@ class managesieve extends rcube_plugin
         $out .= '<table><tr><td class="rowactions">';
 
         // action select
-        $select_action = new html_select(array('name' => "_action_type[]", 'id' => 'action_type'.$id,
+        $select_action = new html_select(array('name' => "_action_type[$id]", 'id' => 'action_type'.$id,
             'onchange' => 'action_type_select(' .$id .')'));
         if (in_array('fileinto', $this->exts))
             $select_action->add(Q($this->gettext('messagemoveto')), 'fileinto');
@@ -1053,12 +1458,12 @@ class managesieve extends rcube_plugin
         // actions target inputs
         $out .= '<td class="rowtargets">';
         // shared targets
-        $out .= '<input type="text" name="_action_target[]" id="action_target' .$id. '" '
-            .'value="' .($action['type']=='redirect' ? Q($action['target'], 'strict', false) : ''). '" size="40" '
+        $out .= '<input type="text" name="_action_target['.$id.']" id="action_target' .$id. '" '
+            .'value="' .($action['type']=='redirect' ? Q($action['target'], 'strict', false) : ''). '" size="35" '
             .'style="display:' .($action['type']=='redirect' ? 'inline' : 'none') .'" '
             . $this->error_class($id, 'action', 'target', 'action_target') .' />';
-        $out .= '<textarea name="_action_target_area[]" id="action_target_area' .$id. '" '
-            .'rows="3" cols="40" '. $this->error_class($id, 'action', 'targetarea', 'action_target_area')
+        $out .= '<textarea name="_action_target_area['.$id.']" id="action_target_area' .$id. '" '
+            .'rows="3" cols="35" '. $this->error_class($id, 'action', 'targetarea', 'action_target_area')
             .'style="display:' .(in_array($action['type'], array('reject', 'ereject')) ? 'inline' : 'none') .'">'
             . (in_array($action['type'], array('reject', 'ereject')) ? Q($action['target'], 'strict', false) : '')
             . "</textarea>\n";
@@ -1066,19 +1471,19 @@ class managesieve extends rcube_plugin
         // vacation
         $out .= '<div id="action_vacation' .$id.'" style="display:' .($action['type']=='vacation' ? 'inline' : 'none') .'">';
         $out .= '<span class="label">'. Q($this->gettext('vacationreason')) .'</span><br />'
-            .'<textarea name="_action_reason[]" id="action_reason' .$id. '" '
-            .'rows="3" cols="45" '. $this->error_class($id, 'action', 'reason', 'action_reason') . '>'
+            .'<textarea name="_action_reason['.$id.']" id="action_reason' .$id. '" '
+            .'rows="3" cols="35" '. $this->error_class($id, 'action', 'reason', 'action_reason') . '>'
             . Q($action['reason'], 'strict', false) . "</textarea>\n";
         $out .= '<br /><span class="label">' .Q($this->gettext('vacationsubject')) . '</span><br />'
-            .'<input type="text" name="_action_subject[]" id="action_subject'.$id.'" '
-            .'value="' . (is_array($action['subject']) ? Q(implode(', ', $action['subject']), 'strict', false) : $action['subject']) . '" size="50" '
+            .'<input type="text" name="_action_subject['.$id.']" id="action_subject'.$id.'" '
+            .'value="' . (is_array($action['subject']) ? Q(implode(', ', $action['subject']), 'strict', false) : $action['subject']) . '" size="35" '
             . $this->error_class($id, 'action', 'subject', 'action_subject') .' />';
         $out .= '<br /><span class="label">' .Q($this->gettext('vacationaddresses')) . '</span><br />'
-            .'<input type="text" name="_action_addresses[]" id="action_addr'.$id.'" '
-            .'value="' . (is_array($action['addresses']) ? Q(implode(', ', $action['addresses']), 'strict', false) : $action['addresses']) . '" size="50" '
+            .'<input type="text" name="_action_addresses['.$id.']" id="action_addr'.$id.'" '
+            .'value="' . (is_array($action['addresses']) ? Q(implode(', ', $action['addresses']), 'strict', false) : $action['addresses']) . '" size="35" '
             . $this->error_class($id, 'action', 'addresses', 'action_addr') .' />';
         $out .= '<br /><span class="label">' . Q($this->gettext('vacationdays')) . '</span><br />'
-            .'<input type="text" name="_action_days[]" id="action_days'.$id.'" '
+            .'<input type="text" name="_action_days['.$id.']" id="action_days'.$id.'" '
             .'value="' .Q($action['days'], 'strict', false) . '" size="2" '
             . $this->error_class($id, 'action', 'days', 'action_days') .' />';
         $out .= '</div>';
@@ -1111,22 +1516,21 @@ class managesieve extends rcube_plugin
 
         $this->rc->imap_connect();
         $select = rcmail_mailbox_select(array(
-               'realnames' => false,
-               'maxlength' => 100,
-               'id' => 'action_mailbox' . $id,
-               'name' => '_action_mailbox[]',
-               'style' => 'display:'.(!isset($action) || $action['type']=='fileinto' ? 'inline' : 'none')
-           ));
+            'realnames' => false,
+            'maxlength' => 100,
+            'id' => 'action_mailbox' . $id,
+            'name' => "_action_mailbox[$id]",
+            'style' => 'display:'.(!isset($action) || $action['type']=='fileinto' ? 'inline' : 'none')
+        ));
         $out .= $select->show($mailbox);
         $out .= '</td>';
 
         // add/del buttons
         $out .= '<td class="rowbuttons">';
-        $out .= '<input type="button" id="actionadd' . $id .'" value="'. Q($this->gettext('add')). '"
-            onclick="rcmail.managesieve_actionadd(' . $id .')" class="button" /> ';
-        $out .= '<input type="button" id="actiondel' . $id .'" value="'. Q($this->gettext('del')). '"
-            onclick="rcmail.managesieve_actiondel(' . $id .')" class="button' . ($rows_num<2 ? ' disabled' : '') .'"'
-            . ($rows_num<2 ? ' disabled="disabled"' : '') .' />';
+        $out .= '<a href="#" id="actionadd' . $id .'" title="'. Q($this->gettext('add')). '"
+            onclick="rcmail.managesieve_actionadd(' . $id .')" class="button add"></a>';
+        $out .= '<a href="#" id="actiondel' . $id .'" title="'. Q($this->gettext('del')). '"
+            onclick="rcmail.managesieve_actiondel(' . $id .')" class="button del' . ($rows_num<2 ? ' disabled' : '') .'"></a>';
         $out .= '</td>';
 
         $out .= '</tr></table>';
@@ -1208,4 +1612,267 @@ class managesieve extends rcube_plugin
 
         return $mailbox;
     }
+
+    /**
+     * List sieve scripts
+     *
+     * @return array Scripts list
+     */
+    public function list_scripts()
+    {
+        if ($this->list !== null) {
+            return $this->list;
+        }
+
+        $this->list = $this->sieve->get_scripts();
+
+        // Handle active script(s) and list of scripts according to Kolab's KEP:14
+        if ($this->rc->config->get('managesieve_kolab_master')) {
+
+            // Skip protected names
+            foreach ((array)$this->list as $idx => $name) {
+                $_name = strtoupper($name);
+                if ($_name == 'MASTER')
+                    $master_script = $name;
+                else if ($_name == 'MANAGEMENT')
+                    $management_script = $name;
+                else if($_name == 'USER')
+                    $user_script = $name;
+                else
+                    continue;
+
+                unset($this->list[$idx]);
+            }
+
+            // get active script(s), read USER script
+            if ($user_script) {
+                $extension = $this->rc->config->get('managesieve_filename_extension', '.sieve');
+                $filename_regex = '/'.preg_quote($extension, '/').'$/';
+                $_SESSION['managesieve_user_script'] = $user_script;
+
+                $this->sieve->load($user_script);
+
+                foreach ($this->sieve->script->as_array() as $rules) {
+                    foreach ($rules['actions'] as $action) {
+                        if ($action['type'] == 'include' && empty($action['global'])) {
+                            $name = preg_replace($filename_regex, '', $action['target']);
+                            $this->active[] = $name;
+                        }
+                    }
+                }
+            }
+            // create USER script if it doesn't exist
+            else {
+                $content = "# USER Management Script\n"
+                    ."#\n"
+                    ."# This script includes the various active sieve scripts\n"
+                    ."# it is AUTOMATICALLY GENERATED. DO NOT EDIT MANUALLY!\n"
+                    ."#\n"
+                    ."# For more information, see http://wiki.kolab.org/KEP:14#USER\n"
+                    ."#\n";
+                if ($this->sieve->save_script('USER', $content)) {
+                    $_SESSION['managesieve_user_script'] = 'USER';
+                    if (empty($this->master_file))
+                        $this->sieve->activate('USER');
+                }
+            }
+        }
+        else if (!empty($this->list)) {
+            // Get active script name
+            if ($active = $this->sieve->get_active()) {
+                $this->active = array($active);
+            }
+        }
+
+        return $this->list;
+    }
+
+    /**
+     * Removes sieve script
+     *
+     * @param string $name Script name
+     *
+     * @return bool True on success, False on failure
+     */
+    public function remove_script($name)
+    {
+        $result = $this->sieve->remove($name);
+
+        // Kolab's KEP:14
+        if ($result && $this->rc->config->get('managesieve_kolab_master')) {
+            $this->deactivate_script($name);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Activates sieve script
+     *
+     * @param string $name Script name
+     *
+     * @return bool True on success, False on failure
+     */
+    public function activate_script($name)
+    {
+        // Kolab's KEP:14
+        if ($this->rc->config->get('managesieve_kolab_master')) {
+            $extension   = $this->rc->config->get('managesieve_filename_extension', '.sieve');
+            $user_script = $_SESSION['managesieve_user_script'];
+
+            // if the script is not active...
+            if ($user_script && ($key = array_search($name, $this->active)) === false) {
+                // ...rewrite USER file adding appropriate include command
+                if ($this->sieve->load($user_script)) {
+                    $script = $this->sieve->script->as_array();
+                    $list   = array();
+                    $regexp = '/' . preg_quote($extension, '/') . '$/';
+
+                    // Create new include entry
+                    $rule = array(
+                        'actions' => array(
+                            0 => array(
+                                'target'   => $name.$extension,
+                                'type'     => 'include',
+                                'personal' => true,
+                    )));
+
+                    // get all active scripts for sorting
+                    foreach ($script as $rid => $rules) {
+                        foreach ($rules['actions'] as $aid => $action) {
+                            if ($action['type'] == 'include' && empty($action['global'])) {
+                                $target = $extension ? preg_replace($regexp, '', $action['target']) : $action['target'];
+                                $list[] = $target;
+                            }
+                        }
+                    }
+                    $list[] = $name;
+
+                    // Sort and find current script position
+                    asort($list, SORT_LOCALE_STRING);
+                    $list = array_values($list);
+                    $index = array_search($name, $list);
+
+                    // add rule at the end of the script
+                    if ($index === false || $index == count($list)-1) {
+                        $this->sieve->script->add_rule($rule);
+                    }
+                    // add rule at index position
+                    else {
+                        $script2 = array();
+                        foreach ($script as $rid => $rules) {
+                            if ($rid == $index) {
+                                $script2[] = $rule;
+                            }
+                            $script2[] = $rules;
+                        }
+                        $this->sieve->script->content = $script2;
+                    }
+
+                    $result = $this->sieve->save();
+                    if ($result) {
+                        $this->active[] = $name;
+                    }
+                }
+            }
+        }
+        else {
+            $result = $this->sieve->activate($name);
+            if ($result)
+                $this->active = array($name);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Deactivates sieve script
+     *
+     * @param string $name Script name
+     *
+     * @return bool True on success, False on failure
+     */
+    public function deactivate_script($name)
+    {
+        // Kolab's KEP:14
+        if ($this->rc->config->get('managesieve_kolab_master')) {
+            $extension   = $this->rc->config->get('managesieve_filename_extension', '.sieve');
+            $user_script = $_SESSION['managesieve_user_script'];
+
+            // if the script is active...
+            if ($user_script && ($key = array_search($name, $this->active)) !== false) {
+                // ...rewrite USER file removing appropriate include command
+                if ($this->sieve->load($user_script)) {
+                    $script = $this->sieve->script->as_array();
+                    $name   = $name.$extension;
+
+                    foreach ($script as $rid => $rules) {
+                        foreach ($rules['actions'] as $aid => $action) {
+                            if ($action['type'] == 'include' && empty($action['global'])
+                                && $action['target'] == $name
+                            ) {
+                                break 2;
+                            }
+                        }
+                    }
+
+                    // Entry found
+                    if ($rid < count($script)) {
+                        $this->sieve->script->delete_rule($rid);
+                        $result = $this->sieve->save();
+                        if ($result) {
+                            unset($this->active[$key]);
+                        }
+                    }
+                }
+            }
+        }
+        else {
+            $result = $this->sieve->deactivate();
+            if ($result)
+                $this->active = array();
+        }
+
+        return $result;
+    }
+
+    /**
+     * Saves current script (adding some variables)
+     */
+    public function save_script($name = null)
+    {
+        // Kolab's KEP:14
+        if ($this->rc->config->get('managesieve_kolab_master')) {
+            $this->sieve->script->set_var('EDITOR', self::PROGNAME);
+            $this->sieve->script->set_var('EDITOR_VERSION', self::VERSION);
+        }
+
+        return $this->sieve->save($name);
+    }
+
+    /**
+     * Returns list of rules from the current script
+     *
+     * @return array List of rules
+     */
+    public function list_rules()
+    {
+        $result = array();
+        $i      = 1;
+
+        foreach ($this->script as $idx => $filter) {
+            if ($filter['type'] != 'if') {
+                continue;
+            }
+            $fname = $filter['name'] ? $filter['name'] : "#$i";
+            $result[] = array(
+                'id'    => $idx,
+                'name'  => Q($fname),
+                'class' => $filter['disabled'] ? 'disabled' : '',
+            );
+            $i++;
+        }
+
+        return $result;
+    }
 }
diff --git a/plugins/managesieve/package.xml b/plugins/managesieve/package.xml
new file mode 100644 (file)
index 0000000..56655d2
--- /dev/null
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+    http://pear.php.net/dtd/tasks-1.0.xsd
+    http://pear.php.net/dtd/package-2.0
+    http://pear.php.net/dtd/package-2.0.xsd">
+       <name>managesieve</name>
+       <channel>pear.roundcube.net</channel>
+       <summary>Sieve filters manager for Roundcube</summary>
+       <description>
+           Adds a possibility to manage Sieve scripts (incoming mail filters).
+        It's clickable interface which operates on text scripts and communicates
+        with server using managesieve protocol. Adds Filters tab in Settings.
+       </description>
+       <lead>
+               <name>Aleksander Machniak</name>
+               <user>alec</user>
+               <email>alec@alec.pl</email>
+               <active>yes</active>
+       </lead>
+       <date>2011-11-17</date>
+       <version>
+               <release>5.0</release>
+               <api>5.0</api>
+       </version>
+       <stability>
+               <release>stable</release>
+               <api>stable</api>
+       </stability>
+       <license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license>
+    <notes>-</notes>
+       <contents>
+               <dir baseinstalldir="/" name="/">
+                       <file name="managesieve.php" role="php">
+                               <tasks:replace from="@name@" to="name" type="package-info"/>
+                               <tasks:replace from="@package_version@" to="version" type="package-info"/>
+                       </file>
+                       <file name="managesieve.js" role="data">
+                               <tasks:replace from="@name@" to="name" type="package-info"/>
+                               <tasks:replace from="@package_version@" to="version" type="package-info"/>
+                       </file>
+                       <file name="localization/bg_BG.inc" role="data"></file>
+                       <file name="localization/cs_CZ.inc" role="data"></file>
+                       <file name="localization/de_CH.inc" role="data"></file>
+                       <file name="localization/de_DE.inc" role="data"></file>
+                       <file name="localization/el_GR.inc" role="data"></file>
+                       <file name="localization/en_GB.inc" role="data"></file>
+                       <file name="localization/en_US.inc" role="data"></file>
+                       <file name="localization/es_AR.inc" role="data"></file>
+                       <file name="localization/es_ES.inc" role="data"></file>
+                       <file name="localization/et_EE.inc" role="data"></file>
+                       <file name="localization/fi_FI.inc" role="data"></file>
+                       <file name="localization/fr_FR.inc" role="data"></file>
+                       <file name="localization/gl_ES.inc" role="data"></file>
+                       <file name="localization/hr_HR.inc" role="data"></file>
+                       <file name="localization/hu_HU.inc" role="data"></file>
+                       <file name="localization/it_IT.inc" role="data"></file>
+                       <file name="localization/ja_JP.inc" role="data"></file>
+                       <file name="localization/lv_LV.inc" role="data"></file>
+                       <file name="localization/nb_NO.inc" role="data"></file>
+                       <file name="localization/nl_NL.inc" role="data"></file>
+                       <file name="localization/pl_PL.inc" role="data"></file>
+                       <file name="localization/pt_BR.inc" role="data"></file>
+                       <file name="localization/pt_PT.inc" role="data"></file>
+                       <file name="localization/ru_RU.inc" role="data"></file>
+                       <file name="localization/sk_SK.inc" role="data"></file>
+                       <file name="localization/sl_SI.inc" role="data"></file>
+                       <file name="localization/sv_SE.inc" role="data"></file>
+                       <file name="localization/uk_UA.inc" role="data"></file>
+                       <file name="localization/zh_CN.inc" role="data"></file>
+                       <file name="localization/zh_TW.inc" role="data"></file>
+                       <file name="skins/default/managesieve.css" role="data"></file>
+                       <file name="skins/default/managesieve_mail.css" role="data"></file>
+                       <file name="skins/default/templates/filteredit.html" role="data"></file>
+                       <file name="skins/default/templates/managesieve.html" role="data"></file>
+                       <file name="skins/default/templates/setedit.html" role="data"></file>
+                       <file name="skins/default/images/add.png" role="data"></file>
+                       <file name="skins/default/images/del.png" role="data"></file>
+                       <file name="skins/default/images/down_small.gif" role="data"></file>
+                       <file name="skins/default/images/filter.png" role="data"></file>
+                       <file name="skins/default/images/up_small.gif" role="data"></file>
+                       <file name="managesieve.php" role="php"></file>
+                       <file name="lib/rcube_sieve.php" role="php"></file>
+                       <file name="lib/rcube_sieve_script.php" role="php"></file>
+                       <file name="lib/Net/Sieve.php" role="php"></file>
+                       <file name="config.inc.php.dist" role="data"></file>
+               </dir>
+               <!-- / -->
+       </contents>
+       <dependencies>
+               <required>
+                       <php>
+                               <min>5.2.1</min>
+                       </php>
+                       <pearinstaller>
+                               <min>1.7.0</min>
+                       </pearinstaller>
+               </required>
+       </dependencies>
+       <phprelease/>
+</package>
diff --git a/plugins/managesieve/skins/default/images/add.png b/plugins/managesieve/skins/default/images/add.png
new file mode 100644 (file)
index 0000000..97a6422
Binary files /dev/null and b/plugins/managesieve/skins/default/images/add.png differ
diff --git a/plugins/managesieve/skins/default/images/del.png b/plugins/managesieve/skins/default/images/del.png
new file mode 100644 (file)
index 0000000..518905b
Binary files /dev/null and b/plugins/managesieve/skins/default/images/del.png differ
diff --git a/plugins/managesieve/skins/default/images/down_small.gif b/plugins/managesieve/skins/default/images/down_small.gif
new file mode 100644 (file)
index 0000000..f865893
Binary files /dev/null and b/plugins/managesieve/skins/default/images/down_small.gif differ
diff --git a/plugins/managesieve/skins/default/images/filter.png b/plugins/managesieve/skins/default/images/filter.png
new file mode 100644 (file)
index 0000000..a79ba10
Binary files /dev/null and b/plugins/managesieve/skins/default/images/filter.png differ
diff --git a/plugins/managesieve/skins/default/images/up_small.gif b/plugins/managesieve/skins/default/images/up_small.gif
new file mode 100644 (file)
index 0000000..40deb89
Binary files /dev/null and b/plugins/managesieve/skins/default/images/up_small.gif differ
index 675c5d0dc9d8c589a12f51db6767466303aca9a4..60f632504a0af627d97bcabffc17f6394c635622 100644 (file)
-/***** Roundcube|Filters styles *****/
-
-
-#filterslist
+#filtersetslistbox
 {
   position: absolute;
-  left: 20px;
-  top: 120px;
-  bottom: 20px;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  width: 195px;
   border: 1px solid #999999;
-  overflow: auto;
+  background-color: #F9F9F9;
+  overflow: hidden;
   /* css hack for IE */
-  height: expression((parseInt(document.documentElement.clientHeight)-140)+'px');
+  height: expression(parseInt(this.parentNode.offsetHeight)+'px');
 }
 
-#filters-table
+#filtersscreen
 {
-  width: 100%;
-  table-layout: fixed;
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 205px;
   /* css hack for IE */
-  width: expression(document.getElementById('filterslist').clientWidth);
+  height: expression(parseInt(this.parentNode.offsetHeight)+'px');
 }
 
-#filters-table tbody td
+#filterslistbox
 {
-  cursor: pointer;
+  position: absolute;
+  left: 0;
+  top: 0;
+  bottom: 0;
+  border: 1px solid #999999;
+  overflow: auto;
+  /* css hack for IE */
+  height: expression(parseInt(this.parentNode.offsetHeight)+'px');
 }
 
-#filters-table tbody tr.disabled td
+#filterslist,
+#filtersetslist
 {
-  color: #999999;
+  width: 100%;
+  table-layout: fixed;
 }
 
-#filtersbuttons
+#filterslist tbody td,
+#filtersetslist tbody td
 {
-  position: absolute;
-  left: 20px;
-  top: 85px;
+  cursor: default;
+  text-overflow: ellipsis;
+  -o-text-overflow: ellipsis;
 }
 
-#filtersetsbuttons
+#filterslist tbody tr.disabled td,
+#filtersetslist tbody tr.disabled td
 {
-  position: absolute;
-  left: 230px;
-  top: 85px;
+  color: #999999;
 }
 
-#filtersbuttons a,
-#filtersetsbuttons a
+#filtersetslist tbody td
 {
-  display: block;
-  float: left;
+  font-weight: bold;
 }
-
-#filtersbuttons a.button,
-#filtersbuttons a.buttonPas,
-#filtersetsbuttons a.button,
-#filtersetsbuttons a.buttonPas
+/*
+#filtersetslist tr.selected
 {
-  display: block;
-  float: left;
-  width: 32px;
-  height: 32px;
-  padding: 0;
-  margin-right: 3px;
-  overflow: hidden;
-  background: url(managesieve_toolbar.png) 0 0 no-repeat transparent;
-  opacity: 0.99; /* this is needed to make buttons appear correctly in Chrome */
+  background-color: #929292;
+  border-bottom: 1px solid #898989;
+  color: #FFF;
+  font-weight: bold;
 }
+*/
 
-#filtersbuttons a.buttonPas,
-#filtersetsbuttons a.buttonPas
+#filterslist tbody tr.filtermoveup td
 {
-  filter: alpha(opacity=35);
-  opacity: 0.35;
-}
-
-#filtersbuttons a.add {
-  background-position: 0px 0px;
-}
-
-#filtersbuttons a.addsel {
-  background-position: 0 -32px;
-}
-
-#filtersbuttons a.del {
-  background-position: -32px 0px;
-}
-
-#filtersbuttons a.delsel {
-  background-position: -32px -32px;
-}
-
-#filtersbuttons a.up {
-  background-position: -64px 0px;
-}
-
-#filtersbuttons a.upsel {
-  background-position: -64px -32px;
-}
-
-#filtersbuttons a.down {
-  background-position: -96px 0px;
-}
-
-#filtersbuttons a.downsel {
-  background-position: -96px -32px;
-}
-
-#filtersetsbuttons a.setadd {
-  background-position: -128px 0px;
-}
-
-#filtersetsbuttons a.setaddsel {
-  background-position: -128px -32px;
-}
-
-#filtersetsbuttons a.setdel {
-  background-position: -160px 0px;
-}
-
-#filtersetsbuttons a.setdelsel {
-  background-position: -160px -32px;
-}
-
-#filtersetsbuttons a.setset {
-  background-position: -192px 0px;
-}
-
-#filtersetsbuttons a.setsetsel {
-  background-position: -192px -32px;
+  border-top: 2px dotted #555;
+  padding-top: 0px;
 }
 
-#filtersetsbuttons a.setget {
-  background-position: -224px 0px;
-}
-
-#filtersetsbuttons a.setgetsel {
-  background-position: -224px -32px;
-}
-
-#filtersetselect
+#filterslist tbody tr.filtermovedown td
 {
-  position: absolute;
-  left: 375px;
-  top: 90px;
+  border-bottom: 2px dotted #555;
+  padding-bottom: 1px;
 }
 
 #filter-box
 {
   position: absolute;
-  top: 120px;
-  right: 20px;
-  bottom: 20px;
+  top: 0;
+  right: 0;
+  bottom: 0;
   border: 1px solid #999999;
   overflow: hidden;
   /* css hack for IE */
-  width: expression((parseInt(document.documentElement.clientWidth)-40-parseInt(document.getElementById('filterslist').offsetWidth))+'px');
-  height: expression((parseInt(document.documentElement.clientHeight)-140)+'px');
+  width: expression((parseInt(this.parentNode.offsetWidth)-20-parseInt(document.getElementById('filterslistbox').offsetWidth))+'px');
+  height: expression(parseInt(this.parentNode.offsetHeight)+'px');
 }
 
 #filter-frame
 
 body.iframe
 {
-  min-width: 740px;
-  width: expression(Math.max(740, document.documentElement.clientWidth)+'px');
+  min-width: 620px;
+  width: expression(Math.max(620, document.documentElement.clientWidth)+'px');
+  background-color: #F2F2F2;
 }
 
 #filter-form
 {
-  min-width: 650px;
+  min-width: 550px;
+  width: expression(Math.max(550, document.documentElement.clientWidth)+'px');
   white-space: nowrap;
   padding: 20px 10px 10px 10px;
 }
@@ -208,7 +146,37 @@ div.rulerow:hover, div.actionrow:hover
 div.rulerow table, div.actionrow table
 {
   padding: 0px;
-  width: 100%;
+  min-width: 600px;
+  width: expression(Math.max(600, document.documentElement.clientWidth)+'px');
+}
+
+td
+{
+  vertical-align: top;
+}
+
+td.advbutton
+{
+  width: 1%;
+}
+
+td.advbutton a
+{
+  display: block;
+  padding-top: 14px;
+  height: 6px;
+  width: 12px;
+  text-decoration: none;
+}
+
+td.advbutton a.show
+{
+  background: url(images/down_small.gif) center no-repeat;
+}
+
+td.advbutton a.hide
+{
+  background: url(images/up_small.gif) center no-repeat;
 }
 
 td.rowbuttons
@@ -222,13 +190,20 @@ td.rowactions
 {
   white-space: nowrap;
   width: 1%;
+  padding-top: 2px;
 }
 
 td.rowtargets
 {
   white-space: nowrap;
   width: 98%;
-  padding-left: 10px;
+  padding-left: 3px;
+  padding-top: 2px;
+}
+
+td.rowtargets div.adv
+{
+  padding-top: 3px;
 }
 
 input.disabled, input.disabled:hover
@@ -245,8 +220,15 @@ input.box,
 input.radio
 {
   border: 0;
+  margin-top: 0;
 }
 
+select.operator_selector
+{
+  width: 200px;
+}
+
+td.rowtargets span,
 span.label
 {
   color: #666666;
@@ -259,7 +241,7 @@ span.label
   padding-top: 5px;
   width: 100%;
 }
-  
+
 #footer .footerleft
 {
   padding-left: 2px;
@@ -294,3 +276,47 @@ span.sieve.error
 {
   width: 200px;
 }
+
+a.button.add
+{
+  background: url(images/add.png) no-repeat;
+  width: 30px;
+  height: 20px;
+  margin-right: 4px;
+  display: inline-block;
+}
+
+a.button.del
+{
+  background: url(images/del.png) no-repeat;
+  width: 30px;
+  height: 20px;
+  display: inline-block;
+}
+
+a.button.disabled
+{
+  opacity: 0.35;
+  filter: alpha(opacity=35);
+  cursor: default;
+}
+
+#filter-form select,
+#filter-form input,
+#filter-form textarea
+{
+  font-size: 11px;
+}
+
+/* fixes for popup window */
+
+body.iframe.mail
+{
+  margin: 0;
+  padding: 0;
+}
+
+body.iframe.mail #filter-form
+{
+  padding: 10px 5px 5px 5px;
+}
diff --git a/plugins/managesieve/skins/default/managesieve_mail.css b/plugins/managesieve/skins/default/managesieve_mail.css
new file mode 100644 (file)
index 0000000..7fefaed
--- /dev/null
@@ -0,0 +1,63 @@
+#messagemenu li a.filterlink {
+  background-image: url(images/filter.png);
+  background-position: 7px 0;
+}
+
+#sievefilterform {
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background-color: #F2F2F2;
+  border: 1px solid #999999;
+  padding: 0;
+  margin: 5px;
+}
+
+#sievefilterform iframe {
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  width: 100%;
+  min-height: 100%; /* Chrome 14 bug */
+  background-color: #F2F2F2;
+  border: 0;
+  padding: 0;
+  margin: 0;
+}
+
+#sievefilterform ul {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+  margin-top: 5px;
+}
+
+#sievefilterform fieldset {
+  margin: 5px;
+}
+
+#sievefilterform ul li {
+  margin-bottom: 5px;
+  white-space: nowrap;
+}
+
+#sievefilterform ul li input {
+  margin-right: 5px;
+}
+
+#sievefilterform label {
+  font-weight: bold;
+}
+
+#managesieve-tip
+{
+  width: 200px;
+  z-index: 100000;
+}
+
+span.sieve.error
+{
+  color: red;
+}
diff --git a/plugins/managesieve/skins/default/managesieve_toolbar.png b/plugins/managesieve/skins/default/managesieve_toolbar.png
deleted file mode 100644 (file)
index 473dbc8..0000000
Binary files a/plugins/managesieve/skins/default/managesieve_toolbar.png and /dev/null differ
index 8b1993528985fca4fdda5363ad066b1f3964e44e..6ecb03caef0260e6925abbc7480f709021b693e9 100644 (file)
@@ -5,13 +5,16 @@
 <roundcube:include file="/includes/links.html" />
 <link rel="stylesheet" type="text/css" href="/this/managesieve.css" />
 </head>
-<body class="iframe">
+<body class="iframe<roundcube:exp expression="env:task != 'mail' ? '' : ' mail'" />">
 
+<roundcube:if condition="env:task != 'mail'" />
 <div id="filter-title" class="boxtitle"><roundcube:label name="managesieve.filterdef" /></div>
+<roundcube:endif />
 
 <div id="filter-form" class="boxcontent">
 <roundcube:object name="filterform" />
 
+<roundcube:if condition="env:task != 'mail'" />
 <div id="footer">
 <div class="footerleft">
 <roundcube:button command="plugin.managesieve-save" type="input" class="button mainaction" label="save" />
 <input type="checkbox" id="disabled" name="_disabled" value="1" />
 </div>
 </div>
+<roundcube:endif />
 
 </form>
 </div>
 
-
 </body>
 </html>
index 94cd1f1cc11d9da57452e85a4f7eb9dc657b39f2..71eebe105559c98c56ab9dd3c79766c8bb0c9a6c 100644 (file)
@@ -8,46 +8,79 @@
 <script type="text/javascript" src="/splitter.js"></script>
 
 <style type="text/css">
-#filterslist { width: <roundcube:exp expression="!empty(cookie:sieveviewsplitter) ? cookie:sieveviewsplitter-5 : 210" />px; }
+#filterslistbox { width: <roundcube:exp expression="!empty(cookie:sieveviewsplitter) ? cookie:sieveviewsplitter-5 : 210" />px; }
 #filter-box { left: <roundcube:exp expression="!empty(cookie:sieveviewsplitter) ? cookie:sieveviewsplitter+5 : 220" />px;
 <roundcube:exp expression="browser:ie ? ('width:expression((parseInt(this.parentNode.offsetWidth)-'.(!empty(cookie:sieveviewsplitter) ? cookie:sieveviewsplitter+5 : 220).')+\\'px\\');') : ''" />
 }
+#filtersetslistbox { width: <roundcube:exp expression="!empty(cookie:sieveviewsplitter2) ? cookie:sieveviewsplitter2-5 : 175" />px; }
+#filtersscreen { left: <roundcube:exp expression="!empty(cookie:sieveviewsplitter2) ? cookie:sieveviewsplitter2+5 : 185" />px;
+<roundcube:exp expression="browser:ie ? ('width:expression((parseInt(this.parentNode.offsetWidth)-'.(!empty(cookie:sieveviewsplitter2) ? cookie:sieveviewsplitter2+5 : 185).')+\\'px\\');') : ''" />
+}
 </style>
 
 </head>
-<body>
+<body onload="rcube_init_mail_ui()">
 
 <roundcube:include file="/includes/taskbar.html" />
 <roundcube:include file="/includes/header.html" />
 <roundcube:include file="/includes/settingstabs.html" />
 
-<div id="filtersbuttons">
-<roundcube:button command="plugin.managesieve-add" type="link" class="buttonPas add" classSel="button addsel" classAct="button add" title="managesieve.filteradd" content=" " />
-<roundcube:button command="plugin.managesieve-del" type="link" class="buttonPas del" classSel="button delsel" classAct="button del" title="managesieve.filterdel" content=" " />
-<roundcube:button command="plugin.managesieve-up" type="link" class="buttonPas up" classSel="button upsel" classAct="button up" title="managesieve.moveup" content=" " />
-<roundcube:button command="plugin.managesieve-down" type="link" class="buttonPas down" classSel="button downsel" classAct="button down" title="managesieve.movedown" content=" " />
-</div>
+<div id="mainscreen">
 
-<div id="filtersetsbuttons">
-<roundcube:button command="plugin.managesieve-setadd" type="link" class="buttonPas setadd" classSel="button setaddsel" classAct="button setadd" title="managesieve.filtersetadd" content=" " />
-<roundcube:button command="plugin.managesieve-setdel" type="link" class="buttonPas setdel" classSel="button setdelsel" classAct="button setdel" title="managesieve.filtersetdel" content=" " />
-<roundcube:button command="plugin.managesieve-setact" type="link" class="buttonPas setset" classSel="button setsetsel" classAct="button setset" content=" " />
-<roundcube:button command="plugin.managesieve-setget" type="link" class="buttonPas setget" classSel="button setgetsel" classAct="button setget" title="managesieve.filtersetget" content=" " />
+<div id="filtersetslistbox">
+<div id="filtersetslist-title" class="boxtitle"><roundcube:label name="managesieve.filtersets" /></div>
+<div class="boxlistcontent">
+  <roundcube:object name="filtersetslist" id="filtersetslist" class="records-table" cellspacing="0" summary="Filters list" type="list" noheader="true" />
+</div>
+<div class="boxfooter">
+  <roundcube:button command="plugin.managesieve-setadd" type="link" title="managesieve.filtersetadd" class="buttonPas addfilterset" classAct="button addfilterset" content=" " />
+  <roundcube:button name="filtersetmenulink" id="filtersetmenulink" type="link" title="moreactions" class="button groupactions" onclick="rcmail_ui.show_popup('filtersetmenu', undefined, {above:1});return false" content=" " />
 </div>
-<div id="filtersetselect">
-<roundcube:label name="managesieve.filterset" />:
-<roundcube:object name="filtersetslist" id="filtersets-select" />
 </div>
 
-<div id="filterslist">
-<roundcube:object name="filterslist" id="filters-table" class="records-table" cellspacing="0" summary="Filters list" />
+<div id="filtersscreen">
+<div id="filterslistbox">
+<div class="boxtitle"><roundcube:label name="managesieve.filters" /></div>
+<div class="boxlistcontent">
+  <roundcube:object name="filterslist" id="filterslist" class="records-table" cellspacing="0" summary="Filters list" noheader="true" />
+</div>
+<div class="boxfooter">
+  <roundcube:button command="plugin.managesieve-add" type="link" title="managesieve.filteradd" class="buttonPas addfilter" classAct="button addfilter" content=" " />
+  <roundcube:button name="filtermenulink" id="filtermenulink" type="link" title="moreactions" class="button groupactions" onclick="rcmail_ui.show_popup('filtermenu', undefined, {above:1});return false" content=" " />
 </div>
+</div>
+
 <script type="text/javascript">
-  var sieveviewsplit = new rcube_splitter({id:'sieveviewsplitter', p1: 'filterslist', p2: 'filter-box', orientation: 'v', relative: true, start: 215});
+  var sieveviewsplit2 = new rcube_splitter({id:'sieveviewsplitter2', p1: 'filtersetslistbox', p2: 'filtersscreen', orientation: 'v', relative: true, start: 200});
+  rcmail.add_onload('sieveviewsplit2.init()');
+
+  var sieveviewsplit = new rcube_splitter({id:'sieveviewsplitter', p1: 'filterslistbox', p2: 'filter-box', orientation: 'v', relative: true, start: 215});
   rcmail.add_onload('sieveviewsplit.init()');
 </script>
+
 <div id="filter-box">
-<roundcube:object name="filterframe" id="filter-frame" width="100%" height="100%" frameborder="0" src="/watermark.html" />
+  <roundcube:object name="filterframe" id="filter-frame" width="100%" height="100%" frameborder="0" src="/watermark.html" />
+</div>
+
+</div>
+</div>
+</div>
+
+<div id="filtersetmenu" class="popupmenu">
+  <ul>
+    <li><roundcube:button command="plugin.managesieve-setact" label="managesieve.enable" classAct="active" /></li>
+    <li><roundcube:button command="plugin.managesieve-setdel" label="delete" classAct="active" /></li>
+    <li class="separator_above"><roundcube:button command="plugin.managesieve-setget" label="download" classAct="active" /></li>
+    <roundcube:container name="filtersetoptions" id="filtersetmenu" />
+  </ul>
+</div>
+
+<div id="filtermenu" class="popupmenu">
+  <ul>
+    <li><roundcube:button command="plugin.managesieve-act" label="managesieve.enable" classAct="active" /></li>
+    <li><roundcube:button command="plugin.managesieve-del" label="delete" classAct="active" /></li>
+    <roundcube:container name="filteroptions" id="filtermenu" />
+  </ul>
 </div>
 
 </body>
index d703534599fef9e1fa32eabdaa7da09c7b9f9e02..aec042187436e9c0ecfd6a2e7fab3a6d03d5d2fc 100644 (file)
@@ -6,7 +6,7 @@ Main test of script parser
 include '../lib/rcube_sieve_script.php';
 
 $txt = '
-require ["fileinto","vacation","reject","relational","comparator-i;ascii-numeric","imapflags"];
+require ["fileinto","reject","envelope"];
 # rule:[spam]
 if anyof (header :contains "X-DSPAM-Result" "Spam")
 {
@@ -14,28 +14,17 @@ if anyof (header :contains "X-DSPAM-Result" "Spam")
        stop;
 }
 # rule:[test1]
-if anyof (header :contains ["From","To"] "test@domain.tld")
+if anyof (header :comparator "i;ascii-casemap" :contains ["From","To"] "test@domain.tld")
 {
        discard;
        stop;
 }
 # rule:[test2]
-if anyof (not header :contains ["Subject"] "[test]", header :contains "Subject" "[test2]")
+if anyof (not header :comparator "i;octet" :contains ["Subject"] "[test]", header :contains "Subject" "[test2]")
 {
        fileinto "test";
        stop;
 }
-# rule:[test-vacation]
-if anyof (header :contains "Subject" "vacation")
-{
-       vacation :days 1 text:
-# test
-test test /* test */
-test
-.
-;
-       stop;
-}
 # rule:[comments]
 if anyof (true) /* comment
  * "comment" #comment */ {
@@ -44,24 +33,40 @@ if anyof (true) /* comment
 }
 # rule:[reject]
 if size :over 5000K {
-    reject "Message over 5MB size limit. Please contact me before sending this.";
+       reject "Message over 5MB size limit. Please contact me before sending this.";
+}
+# rule:[false]
+if false # size :over 5000K
+{
+       stop; /* rule disabled */
+}
+# rule:[true]
+if true
+{
+       stop;
 }
-# rule:[redirect]
-if header :value "ge" :comparator "i;ascii-numeric"
-    ["X-Spam-score"] ["14"] {redirect "test@test.tld";}
-# rule:[imapflags]
-if header :matches "Subject" "^Test$" {
-    setflag "\\\\Seen";
-    addflag ["\\\\Answered","\\\\Deleted"];
+fileinto "Test";
+# rule:[address test]
+if address :all :is "From" "nagios@domain.tld"
+{
+       fileinto "domain.tld";
+       stop;
+}
+# rule:[envelope test]
+if envelope :domain :is "From" "domain.tld"
+{
+       fileinto "domain.tld";
+       stop;
 }
 ';
 
 $s = new rcube_sieve_script($txt);
 echo $s->as_text();
 
+// -------------------------------------------------------------------------------
 ?>
 --EXPECT--
-require ["fileinto","vacation","reject","relational","comparator-i;ascii-numeric","imapflags"];
+require ["fileinto","reject","envelope"];
 # rule:[spam]
 if header :contains "X-DSPAM-Result" "Spam"
 {
@@ -75,22 +80,11 @@ if header :contains ["From","To"] "test@domain.tld"
        stop;
 }
 # rule:[test2]
-if anyof (not header :contains "Subject" "[test]", header :contains "Subject" "[test2]")
+if anyof (not header :comparator "i;octet" :contains "Subject" "[test]", header :contains "Subject" "[test2]")
 {
        fileinto "test";
        stop;
 }
-# rule:[test-vacation]
-if header :contains "Subject" "vacation"
-{
-       vacation :days 1 text:
-# test
-test test /* test */
-test
-.
-;
-       stop;
-}
 # rule:[comments]
 if true
 {
@@ -101,14 +95,26 @@ if size :over 5000K
 {
        reject "Message over 5MB size limit. Please contact me before sending this.";
 }
-# rule:[redirect]
-if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-score" "14"
+# rule:[false]
+if false # size :over 5000K
 {
-       redirect "test@test.tld";
+       stop;
 }
-# rule:[imapflags]
-if header :matches "Subject" "^Test$"
+# rule:[true]
+if true
 {
-       setflag "\\Seen";
-       addflag ["\\Answered","\\Deleted"];
+       stop;
+}
+fileinto "Test";
+# rule:[address test]
+if address :all :is "From" "nagios@domain.tld"
+{
+       fileinto "domain.tld";
+       stop;
+}
+# rule:[envelope test]
+if envelope :domain :is "From" "domain.tld"
+{
+       fileinto "domain.tld";
+       stop;
 }
diff --git a/plugins/managesieve/tests/parser_body.phpt b/plugins/managesieve/tests/parser_body.phpt
new file mode 100644 (file)
index 0000000..08ad549
--- /dev/null
@@ -0,0 +1,49 @@
+--TEST--
+Test of Sieve body extension (RFC5173)
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["body","fileinto"];
+if body :raw :contains "MAKE MONEY FAST"
+{
+       stop;
+}
+if body :content "text" :contains ["missile","coordinates"]
+{
+       fileinto "secrets";
+}
+if body :content "audio/mp3" :contains ""
+{
+       fileinto "jukebox";
+}
+if body :text :contains "project schedule"
+{
+       fileinto "project/schedule";
+}
+';
+
+$s = new rcube_sieve_script($txt);
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["body","fileinto"];
+if body :raw :contains "MAKE MONEY FAST"
+{
+       stop;
+}
+if body :content "text" :contains ["missile","coordinates"]
+{
+       fileinto "secrets";
+}
+if body :content "audio/mp3" :contains ""
+{
+       fileinto "jukebox";
+}
+if body :text :contains "project schedule"
+{
+       fileinto "project/schedule";
+}
diff --git a/plugins/managesieve/tests/parser_imapflags.phpt b/plugins/managesieve/tests/parser_imapflags.phpt
new file mode 100644 (file)
index 0000000..a4bc465
--- /dev/null
@@ -0,0 +1,28 @@
+--TEST--
+Test of Sieve vacation extension (RFC5232)
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["imapflags"];
+# rule:[imapflags]
+if header :matches "Subject" "^Test$" {
+    setflag "\\\\Seen";
+    addflag ["\\\\Answered","\\\\Deleted"];
+}
+';
+
+$s = new rcube_sieve_script($txt, array('imapflags'));
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["imapflags"];
+# rule:[imapflags]
+if header :matches "Subject" "^Test$"
+{
+       setflag "\\Seen";
+       addflag ["\\Answered","\\Deleted"];
+}
diff --git a/plugins/managesieve/tests/parser_include.phpt b/plugins/managesieve/tests/parser_include.phpt
new file mode 100644 (file)
index 0000000..addc0d4
--- /dev/null
@@ -0,0 +1,30 @@
+--TEST--
+Test of Sieve include extension
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["include"];
+
+include "script.sieve";
+# rule:[two]
+if true
+{
+    include :optional "second.sieve";
+}
+';
+
+$s = new rcube_sieve_script($txt, array(), array('variables'));
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["include"];
+include "script.sieve";
+# rule:[two]
+if true
+{
+       include :optional "second.sieve";
+}
diff --git a/plugins/managesieve/tests/parser_kep14.phpt b/plugins/managesieve/tests/parser_kep14.phpt
new file mode 100644 (file)
index 0000000..dcdbd48
--- /dev/null
@@ -0,0 +1,19 @@
+--TEST--
+Test of Kolab's KEP:14 implementation
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+# EDITOR Roundcube
+# EDITOR_VERSION 123
+';
+
+$s = new rcube_sieve_script($txt, array('body'));
+echo $s->as_text();
+
+?>
+--EXPECT--
+# EDITOR Roundcube
+# EDITOR_VERSION 123
diff --git a/plugins/managesieve/tests/parser_prefix.phpt b/plugins/managesieve/tests/parser_prefix.phpt
new file mode 100644 (file)
index 0000000..c87e965
--- /dev/null
@@ -0,0 +1,25 @@
+--TEST--
+Test of prefix comments handling
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+# this is a comment
+# and the second line
+
+require ["variables"];
+set "b" "c";
+';
+
+$s = new rcube_sieve_script($txt, array(), array('variables'));
+echo $s->as_text();
+
+?>
+--EXPECT--
+# this is a comment
+# and the second line
+
+require ["variables"];
+set "b" "c";
diff --git a/plugins/managesieve/tests/parser_relational.phpt b/plugins/managesieve/tests/parser_relational.phpt
new file mode 100644 (file)
index 0000000..6b6f29f
--- /dev/null
@@ -0,0 +1,25 @@
+--TEST--
+Test of Sieve relational extension (RFC5231)
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["relational","comparator-i;ascii-numeric"];
+# rule:[redirect]
+if header :value "ge" :comparator "i;ascii-numeric"
+    ["X-Spam-score"] ["14"] {redirect "test@test.tld";}
+';
+
+$s = new rcube_sieve_script($txt);
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["relational","comparator-i;ascii-numeric"];
+# rule:[redirect]
+if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-score" "14"
+{
+       redirect "test@test.tld";
+}
diff --git a/plugins/managesieve/tests/parser_vacation.phpt b/plugins/managesieve/tests/parser_vacation.phpt
new file mode 100644 (file)
index 0000000..a603ff6
--- /dev/null
@@ -0,0 +1,39 @@
+--TEST--
+Test of Sieve vacation extension (RFC5230)
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["vacation"];
+# rule:[test-vacation]
+if anyof (header :contains "Subject" "vacation")
+{
+       vacation :days 1 text:
+# test
+test test /* test */
+test
+.
+;
+       stop;
+}
+';
+
+$s = new rcube_sieve_script($txt);
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["vacation"];
+# rule:[test-vacation]
+if header :contains "Subject" "vacation"
+{
+       vacation :days 1 text:
+# test
+test test /* test */
+test
+.
+;
+       stop;
+}
diff --git a/plugins/managesieve/tests/parser_variables.phpt b/plugins/managesieve/tests/parser_variables.phpt
new file mode 100644 (file)
index 0000000..cf1f8fc
--- /dev/null
@@ -0,0 +1,39 @@
+--TEST--
+Test of Sieve variables extension
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["variables"];
+set "honorific" "Mr";
+set "vacation" text:
+Dear ${HONORIFIC} ${last_name},
+I am out, please leave a message after the meep.
+.
+;
+set :length "b" "${a}";
+set :lower "b" "${a}";
+set :upperfirst "b" "${a}";
+set :upperfirst :lower "b" "${a}";
+set :quotewildcard "b" "Rock*";
+';
+
+$s = new rcube_sieve_script($txt, array(), array('variables'));
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["variables"];
+set "honorific" "Mr";
+set "vacation" text:
+Dear ${HONORIFIC} ${last_name},
+I am out, please leave a message after the meep.
+.
+;
+set :length "b" "${a}";
+set :lower "b" "${a}";
+set :upperfirst "b" "${a}";
+set :upperfirst :lower "b" "${a}";
+set :quotewildcard "b" "Rock*";
diff --git a/plugins/managesieve/tests/parset_subaddress.phpt b/plugins/managesieve/tests/parset_subaddress.phpt
new file mode 100644 (file)
index 0000000..6d4d03c
--- /dev/null
@@ -0,0 +1,38 @@
+--TEST--
+Test of Sieve subaddress extension (RFC5233)
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["envelope","subaddress","fileinto"];
+if envelope :user "To" "postmaster"
+{
+       fileinto "postmaster";
+       stop;
+}
+if envelope :detail :is "To" "mta-filters"
+{
+       fileinto "mta-filters";
+       stop;
+}
+';
+
+$s = new rcube_sieve_script($txt);
+echo $s->as_text();
+
+// -------------------------------------------------------------------------------
+?>
+--EXPECT--
+require ["envelope","subaddress","fileinto"];
+if envelope :user "To" "postmaster"
+{
+       fileinto "postmaster";
+       stop;
+}
+if envelope :detail :is "To" "mta-filters"
+{
+       fileinto "mta-filters";
+       stop;
+}
diff --git a/plugins/new_user_dialog/localization/bg_BG.inc b/plugins/new_user_dialog/localization/bg_BG.inc
new file mode 100644 (file)
index 0000000..9dc0452
--- /dev/null
@@ -0,0 +1,7 @@
+<?php
+
+$labels = array();
+$labels['identitydialogtitle'] = 'Моля попълнете Вашите данни.';
+$labels['identitydialoghint'] = 'Това съобщение се появява само при първото влизане.';
+
+?>
\ No newline at end of file
index 21b3d96c2e8748e2ef4bc9664eef29bb022e2464..067fe19f168d93181077bb2b9fb2f9a5fa0da862 100644 (file)
@@ -6,4 +6,7 @@ $rcmail_config['newmail_notifier_basic'] = false;
 // Enables sound notification
 $rcmail_config['newmail_notifier_sound'] = false;
 
+// Enables desktop notification
+$rcmail_config['newmail_notifier_desktop'] = false;
+
 ?>
diff --git a/plugins/newmail_notifier/localization/de_CH.inc b/plugins/newmail_notifier/localization/de_CH.inc
new file mode 100644 (file)
index 0000000..55cfd71
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+$labels['basic'] = 'Anzeige im Browser bei neuer Nachricht';
+$labels['sound'] = 'Akustische Meldung bei neuer Nachricht';
+$labels['desktop'] = 'Desktop-Benachrichtigung bei neuer Nachricht';
+$labels['test'] = 'Test';
+$labels['title'] = 'Neue E-Mail!';
+$labels['body'] = 'Sie haben eine neue Nachricht';
+$labels['testbody'] = 'Dies ist eine Testbenachrichtigung';
+$labels['desktopdisabled'] = 'Desktop-Benachrichtigungen sind deaktiviert.';
+$labels['desktopunsupported'] = 'Ihr Browser unterstützt keine Desktop-Benachrichtigungen.';
+
+?>
diff --git a/plugins/newmail_notifier/localization/de_DE.inc b/plugins/newmail_notifier/localization/de_DE.inc
new file mode 100644 (file)
index 0000000..65381ca
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+$labels['basic'] = 'Benachrichtigung im Browser bei neuer Nachricht';
+$labels['sound'] = 'Akustische Meldung bei neuer Nachricht';
+$labels['desktop'] = 'Desktop-Benachrichtigung bei neuer Nachricht';
+$labels['test'] = 'Test';
+$labels['title'] = 'Neue E-Mail!';
+$labels['body'] = 'Sie haben eine neue Nachricht';
+$labels['testbody'] = 'Dies ist eine Testbenachrichtigung';
+$labels['desktopdisabled'] = 'Desktop-Benachrichtigungen sind deaktiviert.';
+$labels['desktopunsupported'] = 'Ihr Browser unterstützt keine Desktop-Benachrichtigungen.';
+
+?>
index fbb5d9aaece0f3d44a4209cc957d060ee1d4a507..3017c43dc545dfae0be155fb8fd79db26a07f7d2 100644 (file)
@@ -1,6 +1,13 @@
 <?php
 
-$labels['basic'] = 'Display notifications on new message';
+$labels['basic'] = 'Display browser notifications on new message';
+$labels['desktop'] = 'Display desktop notifications on new message';
 $labels['sound'] = 'Play the sound on new message';
+$labels['test'] = 'Test';
+$labels['title'] = 'New Email!';
+$labels['body'] = 'You\'ve received a new message.';
+$labels['testbody'] = 'This is a test notification.';
+$labels['desktopdisabled'] = 'Desktop notifications are disabled in your browser.';
+$labels['desktopunsupported'] = 'Your browser does not support desktop notifications.';
 
 ?>
diff --git a/plugins/newmail_notifier/localization/lv_LV.inc b/plugins/newmail_notifier/localization/lv_LV.inc
new file mode 100644 (file)
index 0000000..bab30f5
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+$labels['basic'] = 'Attēlot paziņojumu pie jaunas vēstules saņemšanas';
+$labels['desktop'] = 'Attēlot darbvirsmas paziņojumu pie jaunas vēstules saņemšanas';
+$labels['sound'] = 'Atskaņot skaņas signālu pie jaunas vēstules saņemšanas';
+$labels['test'] = 'Test';
+$labels['title'] = 'Jauns E-pasts!';
+$labels['body'] = 'Jūs esat saņēmis jaunu e-pastu.';
+$labels['testbody'] = 'Šis ir testa paziņojums.';
+$labels['desktopdisabled'] = 'Darbvirsmas paziņojumi ir atslēgti Jūsu pārlūkprogrammā.';
+$labels['desktopunsupported'] = 'Jūsu pārlūkprogramma neatbalsta darbvirsmas paziņojumus.';
+
+?>
index 5fe37903b67db4e8007b7e7ddf44200358f1ffac..d95e658af1d074cb365f9aa1f68218565a5a1db5 100644 (file)
@@ -1,6 +1,13 @@
 <?php
 
-$labels['basic'] = 'Wyświetlaj powiadomienia o nadejściu nowej wiadomości';
+$labels['basic'] = 'Wyświetlaj powiadomienia o nadejściu nowej wiadomości w przeglądarce';
 $labels['sound'] = 'Odtwarzaj dźwięk o nadejściu nowej wiadomości';
+$labels['desktop'] = 'Wyświetlaj powiadomienia o nadejściu nowej wiadomości na pulpicie';
+$labels['test'] = 'Testuj powiadomienie';
+$labels['title'] = 'Nowa wiadomość!';
+$labels['body'] = 'Nadeszła nowa wiadomość.';
+$labels['testbody'] = 'To jest testowe powiadomienie.';
+$labels['desktopdisabled'] = 'Powiadomienia na pulpicie zostały zablokowane w twojej przeglądarce.';
+$labels['desktopunsupported'] = 'Twoja przeglądarka nie obsługuje powiadomień na pulpicie.';
 
 ?>
diff --git a/plugins/newmail_notifier/localization/pt_BR.inc b/plugins/newmail_notifier/localization/pt_BR.inc
new file mode 100644 (file)
index 0000000..66f8e93
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+$labels['basic'] = 'Exibir notificação quando uma nova mensagem chegar';
+$labels['desktop'] = 'Exibir notificação no desktop quando uma nova mensagem chegar';
+$labels['sound'] = 'Alerta sonoro quando uma nova mensagem chegar';
+$labels['test'] = 'Testar';
+$labels['title'] = 'Novo Email!';
+$labels['body'] = 'Você recebeu uma nova mensagem.';
+$labels['testbody'] = 'Essa é uma notificação de teste.';
+$labels['desktopdisabled'] = 'As notificações no desktop estão desabilitadas no seu navegador.';
+$labels['desktopunsupported'] = 'Seu navegador não suporta notificações no desktop';
+
+?>
diff --git a/plugins/newmail_notifier/localization/ru_RU.inc b/plugins/newmail_notifier/localization/ru_RU.inc
new file mode 100644 (file)
index 0000000..65abb74
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+/*
++-----------------------------------------------------------------------+
+| language/ru_RU/labels.inc                                             |
+|                                                                       |
+| Language file of the RoundCube Webmail client                         |
+| Copyright (C) 2008-2011, RoundQube Dev. - Switzerland                 |
+| Licensed under the GNU GPL                                            |
+|                                                                       |
++-----------------------------------------------------------------------+
+| Author:        Doubrovine S.                                          |
++-----------------------------------------------------------------------+
+
+*/
+
+$labels = array();
+$labels['basic'] = 'Визуальное уведомление по приходу нового сообщения';
+$labels['sound'] = 'Звуковое уведомление по приходу нового сообщения';
+
+?>
\ No newline at end of file
diff --git a/plugins/newmail_notifier/mail.png b/plugins/newmail_notifier/mail.png
new file mode 100644 (file)
index 0000000..79f3a53
Binary files /dev/null and b/plugins/newmail_notifier/mail.png differ
index 6afd66aee83fc689c5d51e5dd791a224c8707072..16e3edd42cba60680497082af2d65c5d0acfee28 100644 (file)
@@ -1,7 +1,7 @@
 /**
  * New Mail Notifier plugin script
  *
- * @version 0.2
+ * @version 0.3
  * @author Aleksander Machniak <alec@alec.pl>
  */
 
@@ -22,6 +22,8 @@ function newmail_notifier_run(prop)
         newmail_notifier_basic();
     if (prop.sound)
         newmail_notifier_sound();
+    if (prop.desktop)
+        newmail_notifier_desktop(rcmail.gettext('body', 'newmail_notifier'));
 }
 
 // Stops notification
@@ -37,11 +39,13 @@ function newmail_notifier_stop(prop)
 // Basic notification: window.focus and favicon change
 function newmail_notifier_basic()
 {
-    window.focus();
+    var w = rcmail.is_framed() ? window.parent : window;
+
+    w.focus();
 
     // we cannot simply change a href attribute, we must to replace the link element (at least in FF)
     var link = $('<link rel="shortcut icon" href="plugins/newmail_notifier/favicon.ico"/>'),
-        oldlink = $('link[rel="shortcut icon"]');
+        oldlink = $('link[rel="shortcut icon"]', w.document);
 
     rcmail.env.favicon_href = oldlink.attr('href');
     link.replaceAll(oldlink);
@@ -64,3 +68,53 @@ function newmail_notifier_sound()
         window.setTimeout("$('#sound').remove()", 5000);
     }
 }
+
+// Desktop notification (need Chrome or Firefox with a plugin)
+function newmail_notifier_desktop(body)
+{
+    var dn = window.webkitNotifications;
+
+    if (dn && !dn.checkPermission()) {
+        if (rcmail.newmail_popup)
+            rcmail.newmail_popup.cancel();
+        var popup = window.webkitNotifications.createNotification('plugins/newmail_notifier/mail.png',
+            rcmail.gettext('title', 'newmail_notifier'), body);
+        popup.onclick = function() {
+            this.cancel();
+        }
+        popup.show();
+        setTimeout(function() { popup.cancel(); }, 10000); // close after 10 seconds
+        rcmail.newmail_popup = popup;
+        return true;
+    }
+
+    return false;
+}
+
+function newmail_notifier_test_desktop()
+{
+    var dn = window.webkitNotifications,
+        txt = rcmail.gettext('testbody', 'newmail_notifier');
+
+    if (dn) {
+        if (!dn.checkPermission())
+            newmail_notifier_desktop(txt);
+        else
+            dn.requestPermission(function() {
+                if (!newmail_notifier_desktop(txt))
+                    rcmail.display_message(rcmail.gettext('desktopdisabled', 'newmail_notifier'), 'error');
+            });
+    }
+    else
+        rcmail.display_message(rcmail.gettext('desktopunsupported', 'newmail_notifier'), 'error');
+}
+
+function newmail_notifier_test_basic()
+{
+    newmail_notifier_basic();
+}
+
+function newmail_notifier_test_sound()
+{
+    newmail_notifier_sound();
+}
index a72d728dcb2e7da67b27ac36c717ebac432100d9..01e25984d5dee7620eb779449770fbd889d5dbf1 100644 (file)
@@ -6,8 +6,10 @@
  * Supports two methods of notification:
  * 1. Basic - focus browser window and change favicon
  * 2. Sound - play wav file
+ * 3. Desktop - display desktop notification (using webkitNotifications feature,
+ *              supported by Chrome and Firefox with 'HTML5 Notifications' plugin)
  *
- * @version 0.2
+ * @version 0.3
  * @author Aleksander Machniak <alec@alec.pl>
  *
  *
@@ -32,6 +34,7 @@ class newmail_notifier extends rcube_plugin
     public $task = 'mail|settings';
 
     private $rc;
+    private $notified;
 
     /**
      * Plugin initialization
@@ -49,6 +52,8 @@ class newmail_notifier extends rcube_plugin
             $this->add_hook('new_messages', array($this, 'notify'));
             // add script when not in ajax and not in frame
             if (is_a($this->rc->output, 'rcube_template') && empty($_REQUEST['_framed'])) {
+                $this->add_texts('localization/');
+                $this->rc->output->add_label('newmail_notifier.title', 'newmail_notifier.body');
                 $this->include_script('newmail_notifier.js');
             }
         }
@@ -69,27 +74,29 @@ class newmail_notifier extends rcube_plugin
         // Load localization and configuration
         $this->add_texts('localization/');
 
-        // Check that configuration is not disabled
-        $dont_override  = (array) $this->rc->config->get('dont_override', array());
-        $basic_override = in_array('newmail_notifier_basic', $dont_override);
-        $sound_override = in_array('newmail_notifier_sound', $dont_override);
-
-        if (!$basic_override) {
-            $field_id = '_newmail_notifier_basic';
-            $input    = new html_checkbox(array('name' => $field_id, 'id' => $field_id, 'value' => 1));
-            $args['blocks']['new_message']['options']['newmail_notifier_basic'] = array(
-                'title' => html::label($field_id, Q($this->gettext('basic'))),
-                'content' => $input->show($this->rc->config->get('newmail_notifier_basic')),
-            );
+        if (!empty($_REQUEST['_framed'])) {
+            $this->rc->output->add_label('newmail_notifier.title', 'newmail_notifier.testbody',
+                'newmail_notifier.desktopunsupported', 'newmail_notifier.desktopenabled', 'newmail_notifier.desktopdisabled');
+            $this->include_script('newmail_notifier.js');
         }
 
-        if (!$sound_override) {
-            $field_id = '_newmail_notifier_sound';
-            $input    = new html_checkbox(array('name' => $field_id, 'id' => $field_id, 'value' => 1));
-            $args['blocks']['new_message']['options']['newmail_notifier_sound'] = array(
-                'title' => html::label($field_id, Q($this->gettext('sound'))),
-                'content' => $input->show($this->rc->config->get('newmail_notifier_sound')),
-            );
+        // Check that configuration is not disabled
+        $dont_override = (array) $this->rc->config->get('dont_override', array());
+
+        foreach (array('basic', 'desktop', 'sound') as $type) {
+            $key = 'newmail_notifier_' . $type;
+            if (!in_array($key, $dont_override)) {
+                $field_id = '_' . $key;
+                $input    = new html_checkbox(array('name' => $field_id, 'id' => $field_id, 'value' => 1));
+                $content  = $input->show($this->rc->config->get($key))
+                    . ' ' . html::a(array('href' => '#', 'onclick' => 'newmail_notifier_test_'.$type.'()'),
+                        $this->gettext('test'));
+
+                $args['blocks']['new_message']['options'][$key] = array(
+                    'title' => html::label($field_id, Q($this->gettext($type))),
+                    'content' => $content
+                );
+            }
         }
 
         return $args;
@@ -108,17 +115,13 @@ class newmail_notifier extends rcube_plugin
         $this->load_config();
 
         // Check that configuration is not disabled
-        $dont_override  = (array) $this->rc->config->get('dont_override', array());
-        $basic_override = in_array('newmail_notifier_basic', $dont_override);
-        $sound_override = in_array('newmail_notifier_sound', $dont_override);
+        $dont_override = (array) $this->rc->config->get('dont_override', array());
 
-        if (!$basic_override) {
-            $key = 'newmail_notifier_basic';
-            $args['prefs'][$key] = get_input_value('_'.$key, RCUBE_INPUT_POST) ? true : false;
-        }
-        if (!$sound_override) {
-            $key = 'newmail_notifier_sound';
-            $args['prefs'][$key] = get_input_value('_'.$key, RCUBE_INPUT_POST) ? true : false;
+        foreach (array('basic', 'desktop', 'sound') as $type) {
+            $key = 'newmail_notifier_' . $type;
+            if (!in_array($key, $dont_override)) {
+                $args['prefs'][$key] = get_input_value('_'.$key, RCUBE_INPUT_POST) ? true : false;
+            }
         }
 
         return $args;
@@ -129,15 +132,22 @@ class newmail_notifier extends rcube_plugin
      */
     function notify($args)
     {
+        if ($this->notified || !empty($_GET['_refresh'])) {
+            return $args;
+        }
+
+        $this->notified = true;
+
         // Load configuration
         $this->load_config();
 
-        $basic = $this->rc->config->get('newmail_notifier_basic');
-        $sound = $this->rc->config->get('newmail_notifier_sound');
+        $basic   = $this->rc->config->get('newmail_notifier_basic');
+        $sound   = $this->rc->config->get('newmail_notifier_sound');
+        $desktop = $this->rc->config->get('newmail_notifier_desktop');
 
-        if ($basic || $sound) {
+        if ($basic || $sound || $desktop) {
             $this->rc->output->command('plugin.newmail_notifier',
-                array('basic' => $basic, 'sound' => $sound));
+                array('basic' => $basic, 'sound' => $sound, 'desktop' => $desktop));
         }
 
         return $args;
index 94af6d77690e55609dbd97b780035e2372f846da..313e47fdacbe8e76ca0e02a96cc0a3edddb4e9a1 100644 (file)
@@ -47,6 +47,10 @@ $rcmail_config['password_db_dsn'] = '';
 // Default: "SELECT update_passwd(%c, %u)"
 $rcmail_config['password_query'] = 'SELECT update_passwd(%c, %u)';
 
+// By default domains in variables are using unicode.
+// Enable this option to use punycoded names
+$rcmail_config['password_idn_ascii'] = false;
+
 // Path for dovecotpw (if not in $PATH)
 // $rcmail_config['password_dovecotpw'] = '/usr/local/sbin/dovecotpw';
 
index 3ea30a69c6b1eea5e24e67c10330eb4dff979caf..e6450e5e12fc6378eb3a461a83ae14404d819f26 100644 (file)
@@ -272,6 +272,7 @@ function hashPassword( $passwordClear, $encodageType )
         case 'samba':
             if (function_exists('hash')) {
                 $cryptedPassword = hash('md4', rcube_charset_convert($passwordClear, RCMAIL_CHARSET, 'UTF-16LE'));
+                $cryptedPassword = strtoupper($cryptedPassword);
             } else {
                                /* Your PHP install does not have the hash() function */
                                return false;
index 482b7e56fc4ea27a5e5582b153c578344d0d1ba6..2f51b754791da7f8224e7796fb9d454d149eeb5b 100644 (file)
@@ -238,6 +238,7 @@ function ldap_simple_hash_password($password_clear, $encodage_type)
         case 'samba':
             if (function_exists('hash')) {
                 $crypted_password = hash('md4', rcube_charset_convert($password_clear, RCMAIL_CHARSET, 'UTF-16LE'));
+                $crypted_password = strtoupper($crypted_password);
             } else {
                                /* Your PHP install does not have the hash() function */
                                return false;
index 9ea33df2f4bbb49852a411fb1c8294b7ba67072a..06a6b75ffac4f0ad10ade9cb9da36c7d7f3a5485 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Driver for passwords stored in SQL database
  *
- * @version 1.3
+ * @version 1.4
  * @author Aleksander 'A.L.E.C' Machniak <alec@alec.pl>
  *
  */
@@ -37,16 +37,21 @@ function password_save($curpass, $passwd)
     // crypted password
     if (strpos($sql, '%c') !== FALSE) {
         $salt = '';
-        if (CRYPT_MD5) { 
-           $len = rand(3, CRYPT_SALT_LENGTH);
+        if (CRYPT_MD5) {
+            // Always use eight salt characters for MD5 (#1488136)
+           $len = 8;
         } else if (CRYPT_STD_DES) {
            $len = 2;
         } else {
            return PASSWORD_CRYPT_ERROR;
         }
+
+        //Restrict the character set used as salt (#1488136)
+        $seedchars = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
         for ($i = 0; $i < $len ; $i++) {
-           $salt .= chr(rand(ord('.'), ord('z')));
+           $salt .= $seedchars[rand(0, 63)];
         }
+
         $sql = str_replace('%c',  $db->quote(crypt($passwd, CRYPT_MD5 ? '$1$'.$salt.'$' : $salt)), $sql);
     }
 
@@ -61,7 +66,7 @@ function password_save($curpass, $passwd)
         $tmp_dir = $rcmail->config->get('temp_dir');
         $tmpfile = tempnam($tmp_dir, 'roundcube-');
 
-        $pipe = popen("'$dovecotpw' -s '$method' > '$tmpfile'", "w");
+        $pipe = popen("$dovecotpw -s '$method' > '$tmpfile'", "w");
         if (!$pipe) {
             unlink($tmpfile);
             return PASSWORD_CRYPT_ERROR;
@@ -97,15 +102,15 @@ function password_save($curpass, $passwd)
 
            if (!($hash_algo = strtolower($rcmail->config->get('password_hash_algorithm'))))
             $hash_algo = 'sha1';
-        
+
            $hash_passwd = hash($hash_algo, $passwd);
         $hash_curpass = hash($hash_algo, $curpass);
-        
+
            if ($rcmail->config->get('password_hash_base64')) {
             $hash_passwd = base64_encode(pack('H*', $hash_passwd));
             $hash_curpass = base64_encode(pack('H*', $hash_curpass));
         }
-       
+
            $sql = str_replace('%n', $db->quote($hash_passwd, 'text'), $sql);
            $sql = str_replace('%q', $db->quote($hash_curpass, 'text'), $sql);
     }
@@ -125,11 +130,28 @@ function password_save($curpass, $passwd)
         }
     }
 
+    $local_part  = $rcmail->user->get_username('local');
+    $domain_part = $rcmail->user->get_username('domain');
+    $username    = $_SESSION['username'];
+    $host        = $_SESSION['imap_host'];
+
+    // convert domains to/from punnycode
+    if ($rcmail->config->get('password_idn_ascii')) {
+        $domain_part = rcube_idn_to_ascii($domain_part);
+        $username    = rcube_idn_to_ascii($username);
+        $host        = rcube_idn_to_ascii($host);
+    }
+    else {
+        $domain_part = rcube_idn_to_utf8($domain_part);
+        $username    = rcube_idn_to_utf8($username);
+        $host        = rcube_idn_to_utf8($host);
+    }
+
     // at least we should always have the local part
-    $sql = str_replace('%l', $db->quote($rcmail->user->get_username('local'), 'text'), $sql);
-    $sql = str_replace('%d', $db->quote($rcmail->user->get_username('domain'), 'text'), $sql);
-    $sql = str_replace('%u', $db->quote($_SESSION['username'],'text'), $sql);
-    $sql = str_replace('%h', $db->quote($_SESSION['imap_host'],'text'), $sql);
+    $sql = str_replace('%l', $db->quote($local_part, 'text'), $sql);
+    $sql = str_replace('%d', $db->quote($domain_part, 'text'), $sql);
+    $sql = str_replace('%u', $db->quote($username, 'text'), $sql);
+    $sql = str_replace('%h', $db->quote($host, 'text'), $sql);
 
     $res = $db->query($sql, $sql_vars);
 
index a4f74c19221ca44cc0a4ccaf9a6ba2db3a0dd057..45688e1188e97aa2b33ab5f8c1ac132d55c776ca 100644 (file)
                <email>alec@alec.pl</email>
                <active>yes</active>
        </lead>
-       <date></date>
-       <time></time>
+       <date>2011-11-23</date>
        <version>
-               <release></release>
+               <release>2.4</release>
                <api>1.6</api>
        </version>
        <stability>
@@ -27,9 +26,8 @@
        </stability>
        <license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license>
        <notes>
-- When old and new passwords are the same, do nothing, return success (#1487823)
-- Fixed Samba password hashing in 'ldap' driver
-- Added 'password_change' hook for plugin actions after successful password change
+- Added option to use punycode or unicode for domain names (#1488103)
+- Save Samba password hashes in capital letters (#1488197)
     </notes>
        <contents>
                <dir baseinstalldir="/" name="/">
                        <file name="localization/de_CH.inc" role="data"></file>
                        <file name="localization/de_DE.inc" role="data"></file>
                        <file name="localization/en_US.inc" role="data"></file>
+                       <file name="localization/es_AR.inc" role="data"></file>
                        <file name="localization/es_ES.inc" role="data"></file>
                        <file name="localization/et_EE.inc" role="data"></file>
                        <file name="localization/fi_FI.inc" role="data"></file>
                        <file name="localization/fr_FR.inc" role="data"></file>
                        <file name="localization/gl_ES.inc" role="data"></file>
+                       <file name="localization/hr_HR.inc" role="data"></file>
                        <file name="localization/hu_HU.inc" role="data"></file>
                        <file name="localization/it_IT.inc" role="data"></file>
+                       <file name="localization/ja_JA.inc" role="data"></file>
                        <file name="localization/lt_LT.inc" role="data"></file>
                        <file name="localization/lv_LV.inc" role="data"></file>
                        <file name="localization/nl_NL.inc" role="data"></file>
                        <file name="localization/pl_PL.inc" role="data"></file>
                        <file name="localization/pt_BR.inc" role="data"></file>
                        <file name="localization/pt_PT.inc" role="data"></file>
+                       <file name="localization/ru_RU.inc" role="data"></file>
+                       <file name="localization/sk_SK.inc" role="data"></file>
                        <file name="localization/sl_SI.inc" role="data"></file>
                        <file name="localization/sv_SE.inc" role="data"></file>
+                       <file name="localization/tr_TR.inc" role="data"></file>
                        <file name="localization/zh_TW.inc" role="data"></file>
 
                        <file name="drivers/chgsaslpasswd.c" role="data"></file>
 - Virtualmin driver: Add option for setting username format (#1487781)
             </notes>
         </release>
+        <release>
+               <date>2011-10-26</date>
+               <time>12:00</time>
+               <version>
+                       <release>2.3</release>
+                       <api>1.6</api>
+               </version>
+               <stability>
+                       <release>stable</release>
+                       <api>stable</api>
+               </stability>
+               <license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license>
+               <notes>
+- When old and new passwords are the same, do nothing, return success (#1487823)
+- Fixed Samba password hashing in 'ldap' driver
+- Added 'password_change' hook for plugin actions after successful password change
+- Fixed bug where 'doveadm pw' command was used as dovecotpw utility
+- Improve generated crypt() passwords (#1488136)
+            </notes>
+        </release>
     </changelog>
 </package>
index b1c7863fcea7ce7391d97fbd256ce4ade9c6b4dd..06e3448f0dfe529d8d1e29ad708f19a4d6f74c93 100644 (file)
@@ -223,7 +223,7 @@ class password extends rcube_plugin
     {
         $config = rcmail::get_instance()->config;
         $driver = $this->home.'/drivers/'.$config->get('password_driver', 'sql').'.php';
-    
+
         if (!is_readable($driver)) {
             raise_error(array(
                 'code' => 600,
@@ -233,7 +233,7 @@ class password extends rcube_plugin
             ), true, false);
             return $this->gettext('internalerror');
         }
-    
+
         include($driver);
 
         if (!function_exists('password_save')) {
@@ -270,5 +270,5 @@ class password extends rcube_plugin
         }
 
         return $reason;
-    }                                     
+    }
 }
diff --git a/plugins/userinfo/localization/fr_FR.inc b/plugins/userinfo/localization/fr_FR.inc
new file mode 100644 (file)
index 0000000..ef7b8aa
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+$labels = array();
+$labels['userinfo'] = 'Info utilisateur';
+$labels['created'] = 'Date de création';
+$labels['lastlogin'] = 'Dernière connexion';
+$labels['defaultidentity'] = 'Identité principale';
+
+?>
diff --git a/plugins/userinfo/localization/ro_RO.inc b/plugins/userinfo/localization/ro_RO.inc
new file mode 100644 (file)
index 0000000..bf7a476
--- /dev/null
@@ -0,0 +1,9 @@
+<?php
+
+$labels = array();
+$labels['userinfo'] = 'Informatii utilisator';
+$labels['created'] = 'Data creatiei';
+$labels['lastlogin'] = 'Ultima conectare';
+$labels['defaultidentity'] = 'Identitate principala';
+
+?>
index 8d01bb1ad3ddeccf89fca639e6aebff173b04410..ae90562f08301fb63ff3cf588464bab30bc28520 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: clisetup.php 4678 2011-04-20 13:58:21Z alec $
+ $Id: clisetup.php 5299 2011-10-03 09:25:33Z alec $
 
 */
 
@@ -25,6 +25,8 @@ if (php_sapi_name() != 'cli') {
 
 require_once INSTALL_PATH . 'program/include/iniset.php';
 
+// Unset max. execution time limit, set to 120 seconds in iniset.php
+@set_time_limit(0);
 
 /**
  * Parse commandline arguments into a hash array
index 10ae11e51b5eac9372132394c7404e566afb9c04..d8ead568cbbdf4e8ee96a0747f285d6ba42cec60 100755 (executable)
@@ -16,7 +16,7 @@
  |         Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: iniset.php 5292 2011-09-28 19:16:41Z thomasb $
+ $Id: iniset.php 5582 2011-12-09 08:55:40Z thomasb $
 
 */
 
@@ -37,7 +37,7 @@ foreach ($crit_opts as $optname => $optval) {
 }
 
 // application constants
-define('RCMAIL_VERSION', '0.6');
+define('RCMAIL_VERSION', '0.7');
 define('RCMAIL_CHARSET', 'UTF-8');
 define('JS_OBJECT_NAME', 'rcmail');
 define('RCMAIL_START', microtime(true));
index 30d90993e7b36af08313cd170699da061dee33b7..f92976245328fabd52946f2b01195d47a0b4ae6a 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: main.inc 5151 2011-08-31 12:49:44Z alec $
+ $Id: main.inc 5591 2011-12-10 14:16:31Z thomasb $
 
 */
 
@@ -97,9 +97,9 @@ function rcube_label($p, $domain=null)
  *
  * @see rcmail::text_exists()
  */
-function rcube_label_exists($name, $domain=null)
+function rcube_label_exists($name, $domain=null, &$ref_domain = null)
 {
-  return rcmail::get_instance()->text_exists($name, $domain);
+  return rcmail::get_instance()->text_exists($name, $domain, $ref_domain);
 }
 
 
@@ -169,11 +169,17 @@ function rcmail_cache_gc()
   // get target timestamp
   $ts = get_offset_time($rcmail->config->get('message_cache_lifetime', '30d'), -1);
 
-  $db->query("DELETE FROM ".get_table_name('messages')."
-             WHERE  created < " . $db->fromunixtime($ts));
+  $db->query("DELETE FROM ".get_table_name('cache_messages')
+        ." WHERE changed < " . $db->fromunixtime($ts));
 
-  $db->query("DELETE FROM ".get_table_name('cache')."
-              WHERE  created < " . $db->fromunixtime($ts));
+  $db->query("DELETE FROM ".get_table_name('cache_index')
+        ." WHERE changed < " . $db->fromunixtime($ts));
+
+  $db->query("DELETE FROM ".get_table_name('cache_thread')
+        ." WHERE changed < " . $db->fromunixtime($ts));
+
+  $db->query("DELETE FROM ".get_table_name('cache')
+        ." WHERE created < " . $db->fromunixtime($ts));
 }
 
 
@@ -634,20 +640,23 @@ function JQ($str)
 function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
 {
   $value = NULL;
-  
-  if ($source==RCUBE_INPUT_GET && isset($_GET[$fname]))
-    $value = $_GET[$fname];
-  else if ($source==RCUBE_INPUT_POST && isset($_POST[$fname]))
-    $value = $_POST[$fname];
-  else if ($source==RCUBE_INPUT_GPC)
-    {
+
+  if ($source == RCUBE_INPUT_GET) {
+    if (isset($_GET[$fname]))
+      $value = $_GET[$fname];
+  }
+  else if ($source == RCUBE_INPUT_POST) {
+    if (isset($_POST[$fname]))
+      $value = $_POST[$fname];
+  }
+  else if ($source == RCUBE_INPUT_GPC) {
     if (isset($_POST[$fname]))
       $value = $_POST[$fname];
     else if (isset($_GET[$fname]))
       $value = $_GET[$fname];
     else if (isset($_COOKIE[$fname]))
       $value = $_COOKIE[$fname];
-    }
+  }
 
   return parse_input_value($value, $allow_html, $charset);
 }
@@ -655,7 +664,7 @@ function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
 /**
  * Parse/validate input value. See get_input_value()
  * Performs stripslashes() and charset conversion if necessary
- * 
+ *
  * @param  string   Input value
  * @param  boolean  Allow HTML tags in field value
  * @param  string   Charset to convert into
@@ -681,15 +690,21 @@ function parse_input_value($value, $allow_html=FALSE, $charset=NULL)
   else if (get_magic_quotes_gpc() || get_magic_quotes_runtime())
     $value = stripslashes($value);
 
-  // remove HTML tags if not allowed    
+  // remove HTML tags if not allowed
   if (!$allow_html)
     $value = strip_tags($value);
-  
+
+  $output_charset = is_object($OUTPUT) ? $OUTPUT->get_charset() : null;
+
+  // remove invalid characters (#1488124)
+  if ($output_charset == 'UTF-8')
+    $value = rc_utf8_clean($value);
+
   // convert to internal charset
-  if (is_object($OUTPUT) && $charset)
-    return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset);
-  else
-    return $value;
+  if ($charset && $output_charset)
+    $value = rcube_charset_convert($value, $output_charset, $charset);
+
+  return $value;
 }
 
 /**
@@ -699,15 +714,16 @@ function parse_input_value($value, $allow_html=FALSE, $charset=NULL)
  * @param  int   Source to get value from (GPC)
  * @return array Hash array with all request parameters
  */
-function request2param($mode = RCUBE_INPUT_GPC)
+function request2param($mode = RCUBE_INPUT_GPC, $ignore = 'task|action')
 {
   $out = array();
   $src = $mode == RCUBE_INPUT_GET ? $_GET : ($mode == RCUBE_INPUT_POST ? $_POST : $_REQUEST);
   foreach ($src as $key => $value) {
     $fname = $key[0] == '_' ? substr($key, 1) : $key;
-    $out[$fname] = get_input_value($key, $mode);
+    if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname))
+      $out[$fname] = get_input_value($key, $mode);
   }
-  
+
   return $out;
 }
 
@@ -723,12 +739,14 @@ function asciiwords($str, $css_id = false, $replace_with = '')
 
 /**
  * Convert the given string into a valid HTML identifier
- * Same functionality as done in app.js with this.identifier_expr
- *
+ * Same functionality as done in app.js with rcube_webmail.html_identifier()
  */
-function html_identifier($str)
+function html_identifier($str, $encode=false)
 {
-  return asciiwords($str, true, '_');
+  if ($encode)
+    return rtrim(strtr(base64_encode($str), '+/', '-_'), '=');
+  else
+    return asciiwords($str, true, '_');
 }
 
 /**
@@ -765,52 +783,48 @@ function strip_newlines($str)
  * @return string HTML table code
  */
 function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col)
-  {
+{
   global $RCMAIL;
-  
+
   $table = new html_table(/*array('cols' => count($a_show_cols))*/);
-    
+
   // add table header
   if (!$attrib['noheader'])
     foreach ($a_show_cols as $col)
       $table->add_header($col, Q(rcube_label($col)));
-  
+
   $c = 0;
-  if (!is_array($table_data)) 
+  if (!is_array($table_data))
   {
     $db = $RCMAIL->get_dbh();
     while ($table_data && ($sql_arr = $db->fetch_assoc($table_data)))
     {
-      $zebra_class = $c % 2 ? 'even' : 'odd';
-      $table->add_row(array('id' => 'rcmrow' . html_identifier($sql_arr[$id_col]), 'class' => $zebra_class));
+      $table->add_row(array('id' => 'rcmrow' . html_identifier($sql_arr[$id_col])));
 
       // format each col
       foreach ($a_show_cols as $col)
         $table->add($col, Q($sql_arr[$col]));
-      
+
       $c++;
     }
   }
-  else 
-  {
+  else {
     foreach ($table_data as $row_data)
     {
-      $zebra_class = $c % 2 ? 'even' : 'odd';
-      if (!empty($row_data['class']))
-        $zebra_class .= ' '.$row_data['class'];
+      $class = !empty($row_data['class']) ? $row_data['class'] : '';
 
-      $table->add_row(array('id' => 'rcmrow' . html_identifier($row_data[$id_col]), 'class' => $zebra_class));
+      $table->add_row(array('id' => 'rcmrow' . html_identifier($row_data[$id_col]), 'class' => $class));
 
       // format each col
       foreach ($a_show_cols as $col)
         $table->add($col, Q(is_array($row_data[$col]) ? $row_data[$col][0] : $row_data[$col]));
-        
+
       $c++;
     }
   }
 
   return $table->show($attrib);
-  }
+}
 
 
 /**
@@ -869,23 +883,37 @@ function rcmail_get_edit_field($col, $value, $attrib, $type='text')
  * @param string Container ID to use as prefix
  * @return string Modified CSS source
  */
-function rcmail_mod_css_styles($source, $container_id)
+function rcmail_mod_css_styles($source, $container_id, $allow_remote=false)
   {
   $last_pos = 0;
   $replacements = new rcube_string_replacer;
 
   // ignore the whole block if evil styles are detected
-  $stripped = preg_replace('/[^a-z\(:;]/', '', rcmail_xss_entity_decode($source));
-  if (preg_match('/expression|behavior|url\(|import[^a]/', $stripped))
+  $source = rcmail_xss_entity_decode($source);
+  $stripped = preg_replace('/[^a-z\(:;]/i', '', $source);
+  $evilexpr = 'expression|behavior|javascript:|import[^a]' . (!$allow_remote ? '|url\(' : '');
+  if (preg_match("/$evilexpr/i", $stripped))
     return '/* evil! */';
 
-  // remove css comments (sometimes used for some ugly hacks)
-  $source = preg_replace('!/\*(.+)\*/!Ums', '', $source);
-
   // cut out all contents between { and }
-  while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
-  {
-    $key = $replacements->add(substr($source, $pos+1, $pos2-($pos+1)));
+  while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) {
+    $styles = substr($source, $pos+1, $pos2-($pos+1));
+
+    // check every line of a style block...
+    if ($allow_remote) {
+      $a_styles = preg_split('/;[\r\n]*/', $styles, -1, PREG_SPLIT_NO_EMPTY);
+      foreach ($a_styles as $line) {
+        $stripped = preg_replace('/[^a-z\(:;]/i', '', $line);
+        // ... and only allow strict url() values
+        if (stripos($stripped, 'url(') && !preg_match('!url\s*\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\)!Uims', $line)) {
+          $a_styles = array('/* evil! */');
+          break;
+        }
+      }
+      $styles = join(";\n", $a_styles);
+    }
+
+    $key = $replacements->add($styles);
     $source = substr($source, 0, $pos+1) . $replacements->get_replacement($key) . substr($source, $pos2, strlen($source)-$pos2);
     $last_pos = $pos+2;
   }
@@ -923,7 +951,7 @@ function rcmail_xss_entity_decode($content)
 {
   $out = html_entity_decode(html_entity_decode($content));
   $out = preg_replace_callback('/\\\([0-9a-f]{4})/i', 'rcmail_xss_entity_decode_callback', $out);
-  $out = preg_replace('#/\*.*\*/#Um', '', $out);
+  $out = preg_replace('#/\*.*\*/#Ums', '', $out);
   return $out;
 }
 
@@ -1015,15 +1043,15 @@ function rcube_strtotime($date)
  * Convert the given date to a human readable form
  * This uses the date formatting properties from config
  *
- * @param mixed Date representation (string or timestamp)
+ * @param mixed  Date representation (string or timestamp)
  * @param string Date format to use
+ * @param bool   Enables date convertion according to user timezone
+ *
  * @return string Formatted date string
  */
-function format_date($date, $format=NULL)
+function format_date($date, $format=NULL, $convert=true)
 {
   global $RCMAIL, $CONFIG;
-  
-  $ts = NULL;
 
   if (!empty($date))
     $ts = rcube_strtotime($date);
@@ -1031,31 +1059,37 @@ function format_date($date, $format=NULL)
   if (empty($ts))
     return '';
 
-  // get user's timezone
-  $tz = $RCMAIL->config->get_timezone();
-
-  // convert time to user's timezone
-  $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
+  if ($convert) {
+    // get user's timezone offset
+    $tz = $RCMAIL->config->get_timezone();
 
-  // get current timestamp in user's timezone
-  $now = time();  // local time
-  $now -= (int)date('Z'); // make GMT time
-  $now += ($tz * 3600); // user's time
-  $now_date = getdate($now);
+    // convert time to user's timezone
+    $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
 
-  $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']);
+    // get current timestamp in user's timezone
+    $now = time();  // local time
+    $now -= (int)date('Z'); // make GMT time
+    $now += ($tz * 3600); // user's time
+  }
+  else {
+    $now       = time();
+    $timestamp = $ts;
+  }
 
   // define date format depending on current time
   if (!$format) {
+    $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']);
+
     if ($CONFIG['prettydate'] && $timestamp > $today_limit && $timestamp < $now) {
-      $format = $CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i';
+      $format = $RCMAIL->config->get('date_today', $RCMAIL->config->get('time_format', 'H:i'));
       $today  = true;
     }
     else if ($CONFIG['prettydate'] && $timestamp > $week_limit && $timestamp < $now)
-      $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i';
+      $format = $RCMAIL->config->get('date_short', 'D H:i');
     else
-      $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i';
+      $format = $RCMAIL->config->get('date_long', 'Y-m-d H:i');
   }
 
   // strftime() format
@@ -1207,9 +1241,9 @@ function rcmail_mailbox_select($p = array())
     $p['folder_name'] = '*';
 
   if ($p['unsubscribed'])
-    $list = $RCMAIL->imap->list_unsubscribed('', $p['folder_name'], $p['folder_filter']);
+    $list = $RCMAIL->imap->list_unsubscribed('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']);
   else
-    $list = $RCMAIL->imap->list_mailboxes('', $p['folder_name'], $p['folder_filter']);
+    $list = $RCMAIL->imap->list_mailboxes('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']);
 
   $delimiter = $RCMAIL->imap->get_hierarchy_delimiter();
 
@@ -1223,7 +1257,7 @@ function rcmail_mailbox_select($p = array())
   if ($p['noselection'])
     $select->add($p['noselection'], '');
 
-  rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p['exceptions']);
+  rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p);
 
   return $select;
 }
@@ -1272,11 +1306,6 @@ function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
   $path .= $prefix.$currentFolder;
 
   if (!isset($arrFolders[$currentFolder])) {
-    // Check \Noselect option (if options are in cache)
-    if (!$virtual && ($opts = $RCMAIL->imap->mailbox_options($path))) {
-      $virtual = in_array('\\Noselect', $opts);
-    }
-
     $arrFolders[$currentFolder] = array(
       'id' => $path,
       'name' => rcube_charset_convert($currentFolder, 'UTF7-IMAP'),
@@ -1304,13 +1333,14 @@ function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $at
   $realnames = (bool)$attrib['realnames'];
   $msgcounts = $RCMAIL->imap->get_cache('messagecount');
 
-  $idx = 0;
   $out = '';
   foreach ($arrFolders as $key => $folder) {
-    $zebra_class = (($nestLevel+1)*$idx) % 2 == 0 ? 'even' : 'odd';
-    $title = null;
+    $title        = null;
+    $folder_class = rcmail_folder_classname($folder['id']);
+    $collapsed    = strpos($CONFIG['collapsed_folders'], '&'.rawurlencode($folder['id']).'&') !== false;
+    $unread       = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0;
 
-    if (($folder_class = rcmail_folder_classname($folder['id'])) && !$realnames) {
+    if ($folder_class && !$realnames) {
       $foldername = rcube_label($folder_class);
     }
     else {
@@ -1326,31 +1356,16 @@ function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $at
     }
 
     // make folder name safe for ids and class names
-    $folder_id = html_identifier($folder['id']);
+    $folder_id = html_identifier($folder['id'], true);
     $classes = array('mailbox');
 
     // set special class for Sent, Drafts, Trash and Junk
-    if ($folder['id'] == $CONFIG['sent_mbox'])
-      $classes[] = 'sent';
-    else if ($folder['id'] == $CONFIG['drafts_mbox'])
-      $classes[] = 'drafts';
-    else if ($folder['id'] == $CONFIG['trash_mbox'])
-      $classes[] = 'trash';
-    else if ($folder['id'] == $CONFIG['junk_mbox'])
-      $classes[] = 'junk';
-    else if ($folder['id'] == 'INBOX')
-      $classes[] = 'inbox';
-    else
-      $classes[] = '_'.asciiwords($folder_class ? $folder_class : strtolower($folder['id']), true);
-
-    $classes[] = $zebra_class;
+    if ($folder_class)
+      $classes[] = $folder_class;
 
     if ($folder['id'] == $mbox_name)
       $classes[] = 'selected';
 
-    $collapsed = strpos($CONFIG['collapsed_folders'], '&'.rawurlencode($folder['id']).'&') !== false;
-    $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0;
-
     if ($folder['virtual'])
       $classes[] = 'virtual';
     else if ($unread)
@@ -1384,7 +1399,6 @@ function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $at
     }
 
     $out .= "</li>\n";
-    $idx++;
   }
 
   return $out;
@@ -1396,30 +1410,40 @@ function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $at
  * @access private
  * @return string
  */
-function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0, $exceptions=array())
+function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0, $opts=array())
 {
+  global $RCMAIL;
+
   $out = '';
 
   foreach ($arrFolders as $key => $folder) {
-    if (empty($exceptions) || !in_array($folder['id'], $exceptions)) {
-      if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id'])))
-        $foldername = rcube_label($folder_class);
-      else {
-        $foldername = $folder['name'];
-
-        // shorten the folder name to a given length
-        if ($maxlength && $maxlength>1)
-          $foldername = abbreviate_string($foldername, $maxlength);
-      }
-
-      $select->add(str_repeat('&nbsp;', $nestLevel*4) . $foldername, $folder['id']);
+    // skip exceptions (and its subfolders)
+    if (!empty($opts['exceptions']) && in_array($folder['id'], $opts['exceptions'])) {
+      continue;
     }
-    else if ($nestLevel)
+
+    // skip folders in which it isn't possible to create subfolders
+    if (!empty($opts['skip_noinferiors']) && ($attrs = $RCMAIL->imap->mailbox_attributes($folder['id']))
+        && in_array('\\Noinferiors', $attrs)
+    ) {
       continue;
+    }
+
+    if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id'])))
+      $foldername = rcube_label($folder_class);
+    else {
+      $foldername = $folder['name'];
+
+      // shorten the folder name to a given length
+      if ($maxlength && $maxlength>1)
+        $foldername = abbreviate_string($foldername, $maxlength);
+    }
+
+    $select->add(str_repeat('&nbsp;', $nestLevel*4) . $foldername, $folder['id']);
 
     if (!empty($folder['folders']))
       $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength,
-        $select, $realnames, $nestLevel+1, $exceptions);
+        $select, $realnames, $nestLevel+1, $opts);
   }
 
   return $out;
@@ -1568,7 +1592,11 @@ function rcmail_display_server_error($fallback=null, $fallback_args=null)
         $RCMAIL->output->show_message('errorreadonly', 'error');
     }
     else if ($err_code && ($err_str = $RCMAIL->imap->get_error_str())) {
-        $RCMAIL->output->show_message('servererrormsg', 'error', array('msg' => $err_str));
+        // try to detect access rights problem and display appropriate message
+        if (stripos($err_str, 'Permission denied') !== false)
+            $RCMAIL->output->show_message('errornoperm', 'error');
+        else
+            $RCMAIL->output->show_message('servererrormsg', 'error', array('msg' => $err_str));
     }
     else if ($fallback) {
         $RCMAIL->output->show_message($fallback, 'error', $fallback_args);
@@ -1591,7 +1619,7 @@ function rcube_html_editor($mode='')
   $hook = $RCMAIL->plugins->exec_hook('html_editor', array('mode' => $mode));
 
   if ($hook['abort'])
-    return;  
+    return;
 
   $lang = strtolower($_SESSION['language']);
 
@@ -1603,9 +1631,14 @@ function rcube_html_editor($mode='')
 
   $RCMAIL->output->include_script('tiny_mce/tiny_mce.js');
   $RCMAIL->output->include_script('editor.js');
-  $RCMAIL->output->add_script(sprintf("rcmail_editor_init('\$__skin_path', '%s', %d, '%s');",
-    JQ($lang), intval($CONFIG['enable_spellcheck']), $mode),
-    'foot');
+  $RCMAIL->output->add_script(sprintf("rcmail_editor_init(%s)",
+    json_encode(array(
+        'mode'       => $mode,
+        'skin_path'  => '$__skin_path',
+        'lang'       => $lang,
+        'spellcheck' => intval($CONFIG['enable_spellcheck']),
+        'spelldict'  => intval($CONFIG['spellcheck_dictionary']),
+    ))), 'foot');
 }
 
 
@@ -2369,5 +2402,5 @@ function rcube_autocomplete_init()
 
     $RCMAIL->output->set_env('autocomplete_max', (int)$RCMAIL->config->get('autocomplete_max', 15));
     $RCMAIL->output->set_env('autocomplete_min_length', $RCMAIL->config->get('autocomplete_min_length'));
-    $RCMAIL->output->add_label('autocompletechars');
+    $RCMAIL->output->add_label('autocompletechars', 'autocompletemore');
 }
index 5323b649f54ff371759e2869afdfde888573d6d7..aa3bade71e3e75d209c1a4ca3c00b4c5df4a1423 100644 (file)
@@ -6,6 +6,7 @@
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
  | Copyright (C) 2008-2011, The Roundcube Dev Team                       |
+ | Copyright (C) 2011, Kolab Systems AG                                  |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -15,7 +16,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcmail.php 5235 2011-09-19 06:43:57Z alec $
+ $Id: rcmail.php 5527 2011-12-02 09:58:03Z alec $
 
 */
 
@@ -452,8 +453,7 @@ class rcmail
     }
 
     // add to the 'books' array for shutdown function
-    if (!isset($this->address_books[$id]))
-      $this->address_books[$id] = $contacts;
+    $this->address_books[$id] = $contacts;
 
     return $contacts;
   }
@@ -482,7 +482,8 @@ class rcmail
         'name'     => rcube_label('personaladrbook'),
         'groups'   => $this->address_books['0']->groups,
         'readonly' => $this->address_books['0']->readonly,
-        'autocomplete' => in_array('sql', $autocomplete)
+        'autocomplete' => in_array('sql', $autocomplete),
+        'undelete' => $this->address_books['0']->undelete && $this->config->get('undo_timeout'),
       );
     }
 
@@ -592,7 +593,6 @@ class rcmail
       return;
 
     $this->imap = new rcube_imap();
-    $this->imap->debug_level = $this->config->get('debug_level');
     $this->imap->skip_deleted = $this->config->get('skip_deleted');
 
     // enable caching of imap data
@@ -614,7 +614,7 @@ class rcmail
     // Setting root and delimiter before establishing the connection
     // can save time detecting them using NAMESPACE and LIST
     $options = array(
-      'auth_method' => $this->config->get('imap_auth_type', 'check'),
+      'auth_type'   => $this->config->get('imap_auth_type', 'check'),
       'auth_cid'    => $this->config->get('imap_auth_cid'),
       'auth_pw'     => $this->config->get('imap_auth_pw'),
       'debug'       => (bool) $this->config->get('imap_debug', 0),
@@ -677,18 +677,21 @@ class rcmail
     if (session_id())
       return;
 
+    $sess_name   = $this->config->get('session_name');
+    $sess_domain = $this->config->get('session_domain');
+    $lifetime    = $this->config->get('session_lifetime', 0) * 60;
+
     // set session domain
-    if ($domain = $this->config->get('session_domain')) {
-      ini_set('session.cookie_domain', $domain);
+    if ($sess_domain) {
+      ini_set('session.cookie_domain', $sess_domain);
     }
     // set session garbage collecting time according to session_lifetime
-    $lifetime = $this->config->get('session_lifetime', 0) * 60;
     if ($lifetime) {
       ini_set('session.gc_maxlifetime', $lifetime * 2);
     }
 
     ini_set('session.cookie_secure', rcube_https_check());
-    ini_set('session.name', 'roundcube_sessid');
+    ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid');
     ini_set('session.use_cookies', 1);
     ini_set('session.use_only_cookies', 1);
     ini_set('session.serialize_handler', 'php');
@@ -728,7 +731,7 @@ class rcmail
       $keep_alive = max(60, $keep_alive);
       $this->session->set_keep_alive($keep_alive);
     }
-    
+
     $this->session->set_secret($this->config->get('des_key') . $_SERVER['HTTP_USER_AGENT']);
     $this->session->set_ip_check($this->config->get('ip_check'));
   }
@@ -838,16 +841,8 @@ class rcmail
     if (!$imap_login)
       return false;
 
-    $this->set_imap_prop();
-
     // user already registered -> update user's record
     if (is_object($user)) {
-      // fix some old settings according to namespace prefix
-      $this->fix_namespace_settings($user);
-
-      // create default folders on first login
-      if (!$user->data['last_login'] && $config['create_default_folders'])
-        $this->imap->create_default_folders();
       // update last login timestamp
       $user->touch();
     }
@@ -855,13 +850,6 @@ class rcmail
     else if ($config['auto_create_user']) {
       if ($created = rcube_user::create($username, $host)) {
         $user = $created;
-
-        // fix default settings according to namespace prefix
-        $this->fix_namespace_settings($user);
-
-        // create default folders on first login
-        if ($config['create_default_folders'])
-          $this->imap->create_default_folders();
       }
       else {
         raise_error(array(
@@ -881,9 +869,19 @@ class rcmail
 
     // login succeeded
     if (is_object($user) && $user->ID) {
+      // Configure environment
       $this->set_user($user);
+      $this->set_imap_prop();
       $this->session_configure();
 
+      // fix some old settings according to namespace prefix
+      $this->fix_namespace_settings($user);
+
+      // create default folders on first login
+      if ($config['create_default_folders'] && (!empty($created) || empty($user->data['last_login']))) {
+        $this->imap->create_default_folders();
+      }
+
       // set session vars
       $_SESSION['user_id']   = $user->ID;
       $_SESSION['username']  = $user->data['username'];
@@ -892,9 +890,11 @@ class rcmail
       $_SESSION['imap_ssl']  = $imap_ssl;
       $_SESSION['password']  = $this->encrypt($pass);
       $_SESSION['login_time'] = mktime();
-      
+
       if (isset($_REQUEST['_timezone']) && $_REQUEST['_timezone'] != '_default_')
         $_SESSION['timezone'] = floatval($_REQUEST['_timezone']);
+      if (isset($_REQUEST['_dstactive']) && $_REQUEST['_dstactive'] != '_default_')
+        $_SESSION['dst_active'] = intval($_REQUEST['_dstactive']);
 
       // force reloading complete list of subscribed mailboxes
       $this->imap->clear_cache('mailboxes', true);
@@ -973,7 +973,9 @@ class rcmail
   /**
    * Get localized text in the desired language
    *
-   * @param mixed Named parameters array or label name
+   * @param mixed   $attrib  Named parameters array or label name
+   * @param string  $domain  Label domain (plugin) name
+   *
    * @return string Localized text
    */
   public function gettext($attrib, $domain=null)
@@ -988,7 +990,7 @@ class rcmail
 
     $nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
     $name = $attrib['name'] ? $attrib['name'] : '';
-    
+
     // attrib contain text values: use them from now
     if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us']))
         $this->texts[$name] = $setval;
@@ -1044,19 +1046,40 @@ class rcmail
 
 
   /**
-   * Check if the given text lable exists
+   * Check if the given text label exists
+   *
+   * @param string  $name       Label name
+   * @param string  $domain     Label domain (plugin) name or '*' for all domains
+   * @param string  $ref_domain Sets domain name if label is found
    *
-   * @param string Label name
    * @return boolean True if text exists (either in the current language or in en_US)
    */
-  public function text_exists($name, $domain=null)
+  public function text_exists($name, $domain = null, &$ref_domain = null)
   {
     // load localization files if not done yet
     if (empty($this->texts))
       $this->load_language();
 
-    // check for text with domain first
-    return ($domain && isset($this->texts[$domain.'.'.$name])) || isset($this->texts[$name]);
+    if (isset($this->texts[$name])) {
+        $ref_domain = '';
+        return true;
+    }
+
+    // any of loaded domains (plugins)
+    if ($domain == '*') {
+      foreach ($this->plugins->loaded_plugins() as $domain)
+        if (isset($this->texts[$domain.'.'.$name])) {
+          $ref_domain = $domain;
+          return true;
+        }
+    }
+    // specified domain
+    else if ($domain) {
+      $ref_domain = $domain;
+      return isset($this->texts[$domain.'.'.$name]);
+    }
+
+    return false;
   }
 
   /**
@@ -1204,7 +1227,6 @@ class rcmail
 
     // before closing the database connection, write session data
     if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) {
-      $this->session->cleanup();
       session_write_close();
     }
 
@@ -1246,7 +1268,7 @@ class rcmail
   {
     $sess_id = $_COOKIE[ini_get('session.name')];
     if (!$sess_id) $sess_id = session_id();
-    $plugin = $this->plugins->exec_hook('request_token', array('value' => md5('RT' . $this->task . $this->config->get('des_key') . $sess_id)));
+    $plugin = $this->plugins->exec_hook('request_token', array('value' => md5('RT' . $this->user->ID . $this->config->get('des_key') . $sess_id)));
     return $plugin['value'];
   }
 
@@ -1540,7 +1562,7 @@ class rcmail
 
     // use strtr behaviour of going through source string once
     $cmd = strtr($cmd, $replacements);
-    
+
     return (string)shell_exec($cmd);
   }
 
@@ -1576,7 +1598,7 @@ class rcmail
       }
     }
   }
-  
+
   /**
    * Returns current action filename
    *
@@ -1606,8 +1628,8 @@ class rcmail
     if (!$prefix_len)
       return;
 
-    $prefs = $user->get_prefs();
-    if (empty($prefs) || $prefs['namespace_fixed'])
+    $prefs = $this->config->all();
+    if (!empty($prefs['namespace_fixed']))
       return;
 
     // Build namespace prefix regexp
diff --git a/program/include/rcmail.php.orig b/program/include/rcmail.php.orig
new file mode 100644 (file)
index 0000000..7d0da0a
--- /dev/null
@@ -0,0 +1,1716 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcmail.php                                            |
+ |                                                                       |
+ | This file is part of the Roundcube Webmail client                     |
+ | Copyright (C) 2008-2011, The Roundcube Dev Team                       |
+ | Copyright (C) 2011, Kolab Systems AG                                  |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Application class providing core functions and holding              |
+ |   instances of all 'global' objects like db- and imap-connections     |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com>                        |
+ +-----------------------------------------------------------------------+
+
+ $Id: rcmail.php 5527 2011-12-02 09:58:03Z alec $
+
+*/
+
+
+/**
+ * Application class of Roundcube Webmail
+ * implemented as singleton
+ *
+ * @package Core
+ */
+class rcmail
+{
+  /**
+   * Main tasks.
+   *
+   * @var array
+   */
+  static public $main_tasks = array('mail','settings','addressbook','login','logout','utils','dummy');
+
+  /**
+   * Singleton instace of rcmail
+   *
+   * @var rcmail
+   */
+  static private $instance;
+
+  /**
+   * Stores instance of rcube_config.
+   *
+   * @var rcube_config
+   */
+  public $config;
+
+  /**
+   * Stores rcube_user instance.
+   *
+   * @var rcube_user
+   */
+  public $user;
+
+  /**
+   * Instace of database class.
+   *
+   * @var rcube_mdb2
+   */
+  public $db;
+
+  /**
+   * Instace of Memcache class.
+   *
+   * @var rcube_mdb2
+   */
+  public $memcache;
+
+  /**
+   * Instace of rcube_session class.
+   *
+   * @var rcube_session
+   */
+  public $session;
+
+  /**
+   * Instance of rcube_smtp class.
+   *
+   * @var rcube_smtp
+   */
+  public $smtp;
+
+  /**
+   * Instance of rcube_imap class.
+   *
+   * @var rcube_imap
+   */
+  public $imap;
+
+  /**
+   * Instance of rcube_template class.
+   *
+   * @var rcube_template
+   */
+  public $output;
+
+  /**
+   * Instance of rcube_plugin_api.
+   *
+   * @var rcube_plugin_api
+   */
+  public $plugins;
+
+  /**
+   * Current task.
+   *
+   * @var string
+   */
+  public $task;
+
+  /**
+   * Current action.
+   *
+   * @var string
+   */
+  public $action = '';
+  public $comm_path = './';
+
+  private $texts;
+  private $address_books = array();
+  private $caches = array();
+  private $action_map = array();
+  private $shutdown_functions = array();
+
+
+  /**
+   * This implements the 'singleton' design pattern
+   *
+   * @return rcmail The one and only instance
+   */
+  static function get_instance()
+  {
+    if (!self::$instance) {
+      self::$instance = new rcmail();
+      self::$instance->startup();  // init AFTER object was linked with self::$instance
+    }
+
+    return self::$instance;
+  }
+
+
+  /**
+   * Private constructor
+   */
+  private function __construct()
+  {
+    // load configuration
+    $this->config = new rcube_config();
+
+    register_shutdown_function(array($this, 'shutdown'));
+  }
+
+
+  /**
+   * Initial startup function
+   * to register session, create database and imap connections
+   *
+   * @todo Remove global vars $DB, $USER
+   */
+  private function startup()
+  {
+    // initialize syslog
+    if ($this->config->get('log_driver') == 'syslog') {
+      $syslog_id = $this->config->get('syslog_id', 'roundcube');
+      $syslog_facility = $this->config->get('syslog_facility', LOG_USER);
+      openlog($syslog_id, LOG_ODELAY, $syslog_facility);
+    }
+
+    // connect to database
+    $GLOBALS['DB'] = $this->get_dbh();
+
+    // start session
+    $this->session_init();
+
+    // create user object
+    $this->set_user(new rcube_user($_SESSION['user_id']));
+
+    // configure session (after user config merge!)
+    $this->session_configure();
+
+    // set task and action properties
+    $this->set_task(get_input_value('_task', RCUBE_INPUT_GPC));
+    $this->action = asciiwords(get_input_value('_action', RCUBE_INPUT_GPC));
+
+    // reset some session parameters when changing task
+    if ($this->task != 'utils') {
+      if ($this->session && $_SESSION['task'] != $this->task)
+        $this->session->remove('page');
+      // set current task to session
+      $_SESSION['task'] = $this->task;
+    }
+
+    // init output class
+    if (!empty($_REQUEST['_remote']))
+      $GLOBALS['OUTPUT'] = $this->json_init();
+    else
+      $GLOBALS['OUTPUT'] = $this->load_gui(!empty($_REQUEST['_framed']));
+
+    // create plugin API and load plugins
+    $this->plugins = rcube_plugin_api::get_instance();
+
+    // init plugins
+    $this->plugins->init();
+  }
+
+
+  /**
+   * Setter for application task
+   *
+   * @param string Task to set
+   */
+  public function set_task($task)
+  {
+    $task = asciiwords($task);
+
+    if ($this->user && $this->user->ID)
+      $task = !$task ? 'mail' : $task;
+    else
+      $task = 'login';
+
+    $this->task = $task;
+    $this->comm_path = $this->url(array('task' => $this->task));
+
+    if ($this->output)
+      $this->output->set_env('task', $this->task);
+  }
+
+
+  /**
+   * Setter for system user object
+   *
+   * @param rcube_user Current user instance
+   */
+  public function set_user($user)
+  {
+    if (is_object($user)) {
+      $this->user = $user;
+      $GLOBALS['USER'] = $this->user;
+
+      // overwrite config with user preferences
+      $this->config->set_user_prefs((array)$this->user->get_prefs());
+    }
+
+    $_SESSION['language'] = $this->user->language = $this->language_prop($this->config->get('language', $_SESSION['language']));
+
+    // set localization
+    setlocale(LC_ALL, $_SESSION['language'] . '.utf8', 'en_US.utf8');
+
+    // workaround for http://bugs.php.net/bug.php?id=18556
+    if (in_array($_SESSION['language'], array('tr_TR', 'ku', 'az_AZ')))
+      setlocale(LC_CTYPE, 'en_US' . '.utf8');
+  }
+
+
+  /**
+   * Check the given string and return a valid language code
+   *
+   * @param string Language code
+   * @return string Valid language code
+   */
+  private function language_prop($lang)
+  {
+    static $rcube_languages, $rcube_language_aliases;
+
+    // user HTTP_ACCEPT_LANGUAGE if no language is specified
+    if (empty($lang) || $lang == 'auto') {
+       $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
+       $lang = str_replace('-', '_', $accept_langs[0]);
+     }
+
+    if (empty($rcube_languages)) {
+      @include(INSTALL_PATH . 'program/localization/index.inc');
+    }
+
+    // check if we have an alias for that language
+    if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) {
+      $lang = $rcube_language_aliases[$lang];
+    }
+    // try the first two chars
+    else if (!isset($rcube_languages[$lang])) {
+      $short = substr($lang, 0, 2);
+
+      // check if we have an alias for the short language code
+      if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) {
+        $lang = $rcube_language_aliases[$short];
+      }
+      // expand 'nn' to 'nn_NN'
+      else if (!isset($rcube_languages[$short])) {
+        $lang = $short.'_'.strtoupper($short);
+      }
+    }
+
+    if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
+      $lang = 'en_US';
+    }
+
+    return $lang;
+  }
+
+
+  /**
+   * Get the current database connection
+   *
+   * @return rcube_mdb2  Database connection object
+   */
+  public function get_dbh()
+  {
+    if (!$this->db) {
+      $config_all = $this->config->all();
+
+      $this->db = new rcube_mdb2($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']);
+      $this->db->sqlite_initials = INSTALL_PATH . 'SQL/sqlite.initial.sql';
+      $this->db->set_debug((bool)$config_all['sql_debug']);
+    }
+
+    return $this->db;
+  }
+  
+  
+  /**
+   * Get global handle for memcache access
+   *
+   * @return object Memcache
+   */
+  public function get_memcache()
+  {
+    if (!isset($this->memcache)) {
+      // no memcache support in PHP
+      if (!class_exists('Memcache')) {
+        $this->memcache = false;
+        return false;
+      }
+
+      $this->memcache = new Memcache;
+      $this->mc_available = 0;
+      
+      // add alll configured hosts to pool
+      $pconnect = $this->config->get('memcache_pconnect', true);
+      foreach ($this->config->get('memcache_hosts', array()) as $host) {
+        list($host, $port) = explode(':', $host);
+        if (!$port) $port = 11211;
+        $this->mc_available += intval($this->memcache->addServer($host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure')));
+      }
+      
+      // test connection and failover (will result in $this->mc_available == 0 on complete failure)
+      $this->memcache->increment('__CONNECTIONTEST__', 1);  // NOP if key doesn't exist
+
+      if (!$this->mc_available)
+        $this->memcache = false;
+    }
+
+    return $this->memcache;
+  }
+  
+  /**
+   * Callback for memcache failure
+   */
+  public function memcache_failure($host, $port)
+  {
+    static $seen = array();
+    
+    // only report once
+    if (!$seen["$host:$port"]++) {
+      $this->mc_available--;
+      raise_error(array('code' => 604, 'type' => 'db',
+        'line' => __LINE__, 'file' => __FILE__,
+        'message' => "Memcache failure on host $host:$port"),
+        true, false);
+    }
+  }
+
+
+  /**
+   * Initialize and get cache object
+   *
+   * @param string $name   Cache identifier
+   * @param string $type   Cache type ('db', 'apc' or 'memcache')
+   * @param int    $ttl    Expiration time for cache items in seconds
+   * @param bool   $packed Enables/disables data serialization
+   *
+   * @return rcube_cache Cache object
+   */
+  public function get_cache($name, $type='db', $ttl=0, $packed=true)
+  {
+    if (!isset($this->caches[$name])) {
+      $this->caches[$name] = new rcube_cache($type, $_SESSION['user_id'], $name, $ttl, $packed);
+    }
+
+    return $this->caches[$name];
+  }
+
+
+  /**
+   * Return instance of the internal address book class
+   *
+   * @param string  Address book identifier
+   * @param boolean True if the address book needs to be writeable
+   *
+   * @return rcube_contacts Address book object
+   */
+  public function get_address_book($id, $writeable = false)
+  {
+    $contacts    = null;
+    $ldap_config = (array)$this->config->get('ldap_public');
+    $abook_type  = strtolower($this->config->get('address_book_type'));
+
+    // 'sql' is the alias for '0' used by autocomplete
+    if ($id == 'sql')
+        $id = '0';
+
+    // use existing instance
+    if (isset($this->address_books[$id]) && is_object($this->address_books[$id])
+      && is_a($this->address_books[$id], 'rcube_addressbook')
+      && (!$writeable || !$this->address_books[$id]->readonly)
+    ) {
+      $contacts = $this->address_books[$id];
+    }
+    else if ($id && $ldap_config[$id]) {
+      $contacts = new rcube_ldap($ldap_config[$id], $this->config->get('ldap_debug'), $this->config->mail_domain($_SESSION['imap_host']));
+    }
+    else if ($id === '0') {
+      $contacts = new rcube_contacts($this->db, $this->user->ID);
+    }
+    else {
+      $plugin = $this->plugins->exec_hook('addressbook_get', array('id' => $id, 'writeable' => $writeable));
+
+      // plugin returned instance of a rcube_addressbook
+      if ($plugin['instance'] instanceof rcube_addressbook) {
+        $contacts = $plugin['instance'];
+      }
+      // get first source from the list
+      else if (!$id) {
+        $source = reset($this->get_address_sources($writeable));
+        if (!empty($source)) {
+          $contacts = $this->get_address_book($source['id']);
+          if ($contacts)
+            $id = $source['id'];
+        }
+      }
+    }
+
+    if (!$contacts) {
+      raise_error(array(
+        'code' => 700, 'type' => 'php',
+        'file' => __FILE__, 'line' => __LINE__,
+        'message' => "Addressbook source ($id) not found!"),
+        true, true);
+    }
+
+    // add to the 'books' array for shutdown function
+    $this->address_books[$id] = $contacts;
+
+    return $contacts;
+  }
+
+
+  /**
+   * Return address books list
+   *
+   * @param boolean True if the address book needs to be writeable
+   *
+   * @return array  Address books array
+   */
+  public function get_address_sources($writeable = false)
+  {
+    $abook_type = strtolower($this->config->get('address_book_type'));
+    $ldap_config = $this->config->get('ldap_public');
+    $autocomplete = (array) $this->config->get('autocomplete_addressbooks');
+    $list = array();
+
+    // We are using the DB address book
+    if ($abook_type != 'ldap') {
+      if (!isset($this->address_books['0']))
+        $this->address_books['0'] = new rcube_contacts($this->db, $this->user->ID);
+      $list['0'] = array(
+        'id'       => '0',
+        'name'     => rcube_label('personaladrbook'),
+        'groups'   => $this->address_books['0']->groups,
+        'readonly' => $this->address_books['0']->readonly,
+        'autocomplete' => in_array('sql', $autocomplete),
+        'undelete' => $this->address_books['0']->undelete && $this->config->get('undo_timeout'),
+      );
+    }
+
+    if ($ldap_config) {
+      $ldap_config = (array) $ldap_config;
+      foreach ($ldap_config as $id => $prop)
+        $list[$id] = array(
+          'id'       => $id,
+          'name'     => $prop['name'],
+          'groups'   => is_array($prop['groups']),
+          'readonly' => !$prop['writable'],
+          'hidden'   => $prop['hidden'],
+          'autocomplete' => in_array($id, $autocomplete)
+        );
+    }
+
+    $plugin = $this->plugins->exec_hook('addressbooks_list', array('sources' => $list));
+    $list = $plugin['sources'];
+
+    foreach ($list as $idx => $item) {
+      // register source for shutdown function
+      if (!is_object($this->address_books[$item['id']]))
+        $this->address_books[$item['id']] = $item;
+      // remove from list if not writeable as requested
+      if ($writeable && $item['readonly'])
+          unset($list[$idx]);
+    }
+
+    return $list;
+  }
+
+
+  /**
+   * Init output object for GUI and add common scripts.
+   * This will instantiate a rcmail_template object and set
+   * environment vars according to the current session and configuration
+   *
+   * @param boolean True if this request is loaded in a (i)frame
+   * @return rcube_template Reference to HTML output object
+   */
+  public function load_gui($framed = false)
+  {
+    // init output page
+    if (!($this->output instanceof rcube_template))
+      $this->output = new rcube_template($this->task, $framed);
+
+    // set keep-alive/check-recent interval
+    if ($this->session && ($keep_alive = $this->session->get_keep_alive())) {
+      $this->output->set_env('keep_alive', $keep_alive);
+    }
+
+    if ($framed) {
+      $this->comm_path .= '&_framed=1';
+      $this->output->set_env('framed', true);
+    }
+
+    $this->output->set_env('task', $this->task);
+    $this->output->set_env('action', $this->action);
+    $this->output->set_env('comm_path', $this->comm_path);
+    $this->output->set_charset(RCMAIL_CHARSET);
+
+    // add some basic labels to client
+    $this->output->add_label('loading', 'servererror');
+
+    return $this->output;
+  }
+
+
+  /**
+   * Create an output object for JSON responses
+   *
+   * @return rcube_json_output Reference to JSON output object
+   */
+  public function json_init()
+  {
+    if (!($this->output instanceof rcube_json_output))
+      $this->output = new rcube_json_output($this->task);
+
+    return $this->output;
+  }
+
+
+  /**
+   * Create SMTP object and connect to server
+   *
+   * @param boolean True if connection should be established
+   */
+  public function smtp_init($connect = false)
+  {
+    $this->smtp = new rcube_smtp();
+
+    if ($connect)
+      $this->smtp->connect();
+  }
+
+
+  /**
+   * Create global IMAP object and connect to server
+   *
+   * @param boolean True if connection should be established
+   * @todo Remove global $IMAP
+   */
+  public function imap_init($connect = false)
+  {
+    // already initialized
+    if (is_object($this->imap))
+      return;
+
+    $this->imap = new rcube_imap();
+    $this->imap->skip_deleted = $this->config->get('skip_deleted');
+
+    // enable caching of imap data
+    $imap_cache = $this->config->get('imap_cache');
+    $messages_cache = $this->config->get('messages_cache');
+    // for backward compatybility
+    if ($imap_cache === null && $messages_cache === null && $this->config->get('enable_caching')) {
+        $imap_cache     = 'db';
+        $messages_cache = true;
+    }
+    if ($imap_cache)
+        $this->imap->set_caching($imap_cache);
+    if ($messages_cache)
+        $this->imap->set_messages_caching(true);
+
+    // set pagesize from config
+    $this->imap->set_pagesize($this->config->get('pagesize', 50));
+
+    // Setting root and delimiter before establishing the connection
+    // can save time detecting them using NAMESPACE and LIST
+    $options = array(
+      'auth_type'   => $this->config->get('imap_auth_type', 'check'),
+      'auth_cid'    => $this->config->get('imap_auth_cid'),
+      'auth_pw'     => $this->config->get('imap_auth_pw'),
+      'debug'       => (bool) $this->config->get('imap_debug', 0),
+      'force_caps'  => (bool) $this->config->get('imap_force_caps'),
+      'timeout'     => (int) $this->config->get('imap_timeout', 0),
+    );
+
+    $this->imap->set_options($options);
+
+    // set global object for backward compatibility
+    $GLOBALS['IMAP'] = $this->imap;
+
+    $hook = $this->plugins->exec_hook('imap_init', array('fetch_headers' => $this->imap->fetch_add_headers));
+    if ($hook['fetch_headers'])
+      $this->imap->fetch_add_headers = $hook['fetch_headers'];
+
+    // support this parameter for backward compatibility but log warning
+    if ($connect) {
+      $this->imap_connect();
+      raise_error(array(
+        'code' => 800, 'type' => 'imap',
+        'file' => __FILE__, 'line' => __LINE__,
+        'message' => "rcube::imap_init(true) is deprecated, use rcube::imap_connect() instead"),
+        true, false);
+    }
+  }
+
+
+  /**
+   * Connect to IMAP server with stored session data
+   *
+   * @return bool True on success, false on error
+   */
+  public function imap_connect()
+  {
+    if (!$this->imap)
+      $this->imap_init();
+
+    if ($_SESSION['imap_host'] && !$this->imap->conn->connected()) {
+      if (!$this->imap->connect($_SESSION['imap_host'], $_SESSION['username'], $this->decrypt($_SESSION['password']), $_SESSION['imap_port'], $_SESSION['imap_ssl'])) {
+        if ($this->output)
+          $this->output->show_message($this->imap->get_error_code() == -1 ? 'imaperror' : 'sessionerror', 'error');
+      }
+      else {
+        $this->set_imap_prop();
+        return $this->imap->conn;
+      }
+    }
+
+    return false;
+  }
+
+
+  /**
+   * Create session object and start the session.
+   */
+  public function session_init()
+  {
+    // session started (Installer?)
+    if (session_id())
+      return;
+
+    $sess_name   = $this->config->get('session_name');
+    $sess_domain = $this->config->get('session_domain');
+    $lifetime    = $this->config->get('session_lifetime', 0) * 60;
+
+    // set session domain
+    if ($sess_domain) {
+      ini_set('session.cookie_domain', $sess_domain);
+    }
+    // set session garbage collecting time according to session_lifetime
+    if ($lifetime) {
+      ini_set('session.gc_maxlifetime', $lifetime * 2);
+    }
+
+    ini_set('session.cookie_secure', rcube_https_check());
+    ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid');
+    ini_set('session.use_cookies', 1);
+    ini_set('session.use_only_cookies', 1);
+    ini_set('session.serialize_handler', 'php');
+
+    // use database for storing session data
+    $this->session = new rcube_session($this->get_dbh(), $this->config);
+
+    $this->session->register_gc_handler('rcmail_temp_gc');
+    if ($this->config->get('enable_caching'))
+      $this->session->register_gc_handler('rcmail_cache_gc');
+
+    // start PHP session (if not in CLI mode)
+    if ($_SERVER['REMOTE_ADDR'])
+      session_start();
+
+    // set initial session vars
+    if (!$_SESSION['user_id'])
+      $_SESSION['temp'] = true;
+  }
+
+
+  /**
+   * Configure session object internals
+   */
+  public function session_configure()
+  {
+    if (!$this->session)
+      return;
+
+    $lifetime = $this->config->get('session_lifetime', 0) * 60;
+
+    // set keep-alive/check-recent interval
+    if ($keep_alive = $this->config->get('keep_alive')) {
+      // be sure that it's less than session lifetime
+      if ($lifetime)
+        $keep_alive = min($keep_alive, $lifetime - 30);
+      $keep_alive = max(60, $keep_alive);
+      $this->session->set_keep_alive($keep_alive);
+    }
+
+    $this->session->set_secret($this->config->get('des_key') . $_SERVER['HTTP_USER_AGENT']);
+    $this->session->set_ip_check($this->config->get('ip_check'));
+  }
+
+
+  /**
+   * Perfom login to the IMAP server and to the webmail service.
+   * This will also create a new user entry if auto_create_user is configured.
+   *
+   * @param string IMAP user name
+   * @param string IMAP password
+   * @param string IMAP host
+   * @return boolean True on success, False on failure
+   */
+  function login($username, $pass, $host=NULL)
+  {
+    $user = NULL;
+    $config = $this->config->all();
+
+    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 != rcube_parse_host($config['default_host']))
+      return false;
+
+    // parse $host URL
+    $a_host = parse_url($host);
+    if ($a_host['host']) {
+      $host = $a_host['host'];
+      $imap_ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null;
+      if (!empty($a_host['port']))
+        $imap_port = $a_host['port'];
+      else if ($imap_ssl && $imap_ssl != 'tls' && (!$config['default_port'] || $config['default_port'] == 143))
+        $imap_port = 993;
+    }
+
+    $imap_port = $imap_port ? $imap_port : $config['default_port'];
+
+    /* Modify username with domain if required
+       Inspired by Marco <P0L0_notspam_binware.org>
+    */
+    // Check if we need to add domain
+    if (!empty($config['username_domain']) && strpos($username, '@') === false) {
+      if (is_array($config['username_domain']) && isset($config['username_domain'][$host]))
+        $username .= '@'.rcube_parse_host($config['username_domain'][$host], $host);
+      else if (is_string($config['username_domain']))
+        $username .= '@'.rcube_parse_host($config['username_domain'], $host);
+    }
+
+    // Convert username to lowercase. If IMAP backend
+    // is case-insensitive we need to store always the same username (#1487113)
+    if ($config['login_lc']) {
+      $username = mb_strtolower($username);
+    }
+
+    // try to resolve email address from virtuser table
+    if (strpos($username, '@') && ($virtuser = rcube_user::email2user($username))) {
+      $username = $virtuser;
+    }
+
+    // Here we need IDNA ASCII
+    // Only rcube_contacts class is using domain names in Unicode
+    $host = rcube_idn_to_ascii($host);
+    if (strpos($username, '@')) {
+      // lowercase domain name
+      list($local, $domain) = explode('@', $username);
+      $username = $local . '@' . mb_strtolower($domain);
+      $username = rcube_idn_to_ascii($username);
+    }
+
+    // user already registered -> overwrite username
+    if ($user = rcube_user::query($username, $host))
+      $username = $user->data['username'];
+
+    if (!$this->imap)
+      $this->imap_init();
+
+    // try IMAP login
+    if (!($imap_login = $this->imap->connect($host, $username, $pass, $imap_port, $imap_ssl))) {
+      // try with lowercase
+      $username_lc = mb_strtolower($username);
+      if ($username_lc != $username) {
+        // try to find user record again -> overwrite username
+        if (!$user && ($user = rcube_user::query($username_lc, $host)))
+          $username_lc = $user->data['username'];
+
+        if ($imap_login = $this->imap->connect($host, $username_lc, $pass, $imap_port, $imap_ssl))
+          $username = $username_lc;
+      }
+    }
+
+    // exit if IMAP login failed
+    if (!$imap_login)
+      return false;
+
+    // user already registered -> update user's record
+    if (is_object($user)) {
+      // update last login timestamp
+      $user->touch();
+    }
+    // create new system user
+    else if ($config['auto_create_user']) {
+      if ($created = rcube_user::create($username, $host)) {
+        $user = $created;
+      }
+      else {
+        raise_error(array(
+          'code' => 620, 'type' => 'php',
+          'file' => __FILE__, 'line' => __LINE__,
+          'message' => "Failed to create a user record. Maybe aborted by a plugin?"
+          ), true, false);
+      }
+    }
+    else {
+      raise_error(array(
+        'code' => 621, 'type' => 'php',
+        'file' => __FILE__, 'line' => __LINE__,
+        'message' => "Access denied for new user $username. 'auto_create_user' is disabled"
+        ), true, false);
+    }
+
+    // login succeeded
+    if (is_object($user) && $user->ID) {
+      // Configure environment
+      $this->set_user($user);
+      $this->set_imap_prop();
+      $this->session_configure();
+
+      // fix some old settings according to namespace prefix
+      $this->fix_namespace_settings($user);
+
+      // create default folders on first login
+      if ($config['create_default_folders'] && (!empty($created) || empty($user->data['last_login']))) {
+        $this->imap->create_default_folders();
+      }
+
+      // set session vars
+      $_SESSION['user_id']   = $user->ID;
+      $_SESSION['username']  = $user->data['username'];
+      $_SESSION['imap_host'] = $host;
+      $_SESSION['imap_port'] = $imap_port;
+      $_SESSION['imap_ssl']  = $imap_ssl;
+      $_SESSION['password']  = $this->encrypt($pass);
+      $_SESSION['login_time'] = mktime();
+
+      if (isset($_REQUEST['_timezone']) && $_REQUEST['_timezone'] != '_default_')
+        $_SESSION['timezone'] = floatval($_REQUEST['_timezone']);
+      if (isset($_REQUEST['_dstactive']) && $_REQUEST['_dstactive'] != '_default_')
+        $_SESSION['dst_active'] = intval($_REQUEST['_dstactive']);
+
+      // force reloading complete list of subscribed mailboxes
+      $this->imap->clear_cache('mailboxes', true);
+
+      return true;
+    }
+
+    return false;
+  }
+
+
+  /**
+   * Set root dir and last stored mailbox
+   * This must be done AFTER connecting to the server!
+   */
+  public function set_imap_prop()
+  {
+    $this->imap->set_charset($this->config->get('default_charset', RCMAIL_CHARSET));
+
+    if ($default_folders = $this->config->get('default_imap_folders')) {
+      $this->imap->set_default_mailboxes($default_folders);
+    }
+    if (isset($_SESSION['mbox'])) {
+      $this->imap->set_mailbox($_SESSION['mbox']);
+    }
+    if (isset($_SESSION['page'])) {
+      $this->imap->set_page($_SESSION['page']);
+    }
+  }
+
+
+  /**
+   * Auto-select IMAP host based on the posted login information
+   *
+   * @return string Selected IMAP host
+   */
+  public function autoselect_host()
+  {
+    $default_host = $this->config->get('default_host');
+    $host = null;
+
+    if (is_array($default_host)) {
+      $post_host = get_input_value('_host', RCUBE_INPUT_POST);
+
+      // direct match in default_host array
+      if ($default_host[$post_host] || in_array($post_host, array_values($default_host))) {
+        $host = $post_host;
+      }
+
+      // try to select host by mail domain
+      list($user, $domain) = explode('@', get_input_value('_user', RCUBE_INPUT_POST));
+      if (!empty($domain)) {
+        foreach ($default_host as $imap_host => $mail_domains) {
+          if (is_array($mail_domains) && in_array($domain, $mail_domains)) {
+            $host = $imap_host;
+            break;
+          }
+        }
+      }
+
+      // take the first entry if $host is still an array
+      if (empty($host)) {
+        $host = array_shift($default_host);
+      }
+    }
+    else if (empty($default_host)) {
+      $host = get_input_value('_host', RCUBE_INPUT_POST);
+    }
+    else
+      $host = rcube_parse_host($default_host);
+
+    return $host;
+  }
+
+
+  /**
+   * Get localized text in the desired language
+   *
+   * @param mixed   $attrib  Named parameters array or label name
+   * @param string  $domain  Label domain (plugin) name
+   *
+   * @return string Localized text
+   */
+  public function gettext($attrib, $domain=null)
+  {
+    // load localization files if not done yet
+    if (empty($this->texts))
+      $this->load_language();
+
+    // extract attributes
+    if (is_string($attrib))
+      $attrib = array('name' => $attrib);
+
+    $nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
+    $name = $attrib['name'] ? $attrib['name'] : '';
+
+    // attrib contain text values: use them from now
+    if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us']))
+        $this->texts[$name] = $setval;
+
+    // check for text with domain
+    if ($domain && ($text_item = $this->texts[$domain.'.'.$name]))
+      ;
+    // text does not exist
+    else if (!($text_item = $this->texts[$name])) {
+      return "[$name]";
+    }
+
+    // make text item array
+    $a_text_item = is_array($text_item) ? $text_item : array('single' => $text_item);
+
+    // decide which text to use
+    if ($nr == 1) {
+      $text = $a_text_item['single'];
+    }
+    else if ($nr > 0) {
+      $text = $a_text_item['multiple'];
+    }
+    else if ($nr == 0) {
+      if ($a_text_item['none'])
+        $text = $a_text_item['none'];
+      else if ($a_text_item['single'])
+        $text = $a_text_item['single'];
+      else if ($a_text_item['multiple'])
+        $text = $a_text_item['multiple'];
+    }
+
+    // default text is single
+    if ($text == '') {
+      $text = $a_text_item['single'];
+    }
+
+    // replace vars in text
+    if (is_array($attrib['vars'])) {
+      foreach ($attrib['vars'] as $var_key => $var_value)
+        $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text);
+    }
+
+    // format output
+    if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])
+      return ucfirst($text);
+    else if ($attrib['uppercase'])
+      return mb_strtoupper($text);
+    else if ($attrib['lowercase'])
+      return mb_strtolower($text);
+
+    return $text;
+  }
+
+
+  /**
+   * Check if the given text label exists
+   *
+   * @param string  $name       Label name
+   * @param string  $domain     Label domain (plugin) name or '*' for all domains
+   * @param string  $ref_domain Sets domain name if label is found
+   *
+   * @return boolean True if text exists (either in the current language or in en_US)
+   */
+  public function text_exists($name, $domain = null, &$ref_domain = null)
+  {
+    // load localization files if not done yet
+    if (empty($this->texts))
+      $this->load_language();
+
+    if (isset($this->texts[$name])) {
+        $ref_domain = '';
+        return true;
+    }
+
+    // any of loaded domains (plugins)
+    if ($domain == '*') {
+      foreach ($this->plugins->loaded_plugins() as $domain)
+        if (isset($this->texts[$domain.'.'.$name])) {
+          $ref_domain = $domain;
+          return true;
+        }
+    }
+    // specified domain
+    else if ($domain) {
+      $ref_domain = $domain;
+      return isset($this->texts[$domain.'.'.$name]);
+    }
+
+    return false;
+  }
+
+  /**
+   * Load a localization package
+   *
+   * @param string Language ID
+   */
+  public function load_language($lang = null, $add = array())
+  {
+    $lang = $this->language_prop(($lang ? $lang : $_SESSION['language']));
+
+    // load localized texts
+    if (empty($this->texts) || $lang != $_SESSION['language']) {
+      $this->texts = array();
+
+      // handle empty lines after closing PHP tag in localization files
+      ob_start();
+
+      // get english labels (these should be complete)
+      @include(INSTALL_PATH . 'program/localization/en_US/labels.inc');
+      @include(INSTALL_PATH . 'program/localization/en_US/messages.inc');
+
+      if (is_array($labels))
+        $this->texts = $labels;
+      if (is_array($messages))
+        $this->texts = array_merge($this->texts, $messages);
+
+      // include user language files
+      if ($lang != 'en' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) {
+        include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc');
+        include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc');
+
+        if (is_array($labels))
+          $this->texts = array_merge($this->texts, $labels);
+        if (is_array($messages))
+          $this->texts = array_merge($this->texts, $messages);
+      }
+
+      ob_end_clean();
+
+      $_SESSION['language'] = $lang;
+    }
+
+    // append additional texts (from plugin)
+    if (is_array($add) && !empty($add))
+      $this->texts += $add;
+  }
+
+
+  /**
+   * Read directory program/localization and return a list of available languages
+   *
+   * @return array List of available localizations
+   */
+  public function list_languages()
+  {
+    static $sa_languages = array();
+
+    if (!sizeof($sa_languages)) {
+      @include(INSTALL_PATH . 'program/localization/index.inc');
+
+      if ($dh = @opendir(INSTALL_PATH . 'program/localization')) {
+        while (($name = readdir($dh)) !== false) {
+          if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name))
+            continue;
+
+          if ($label = $rcube_languages[$name])
+            $sa_languages[$name] = $label;
+        }
+        closedir($dh);
+      }
+    }
+
+    return $sa_languages;
+  }
+
+
+  /**
+   * Destroy session data and remove cookie
+   */
+  public function kill_session()
+  {
+    $this->plugins->exec_hook('session_destroy');
+
+    $this->session->kill();
+    $_SESSION = array('language' => $this->user->language, 'temp' => true);
+    $this->user->reset();
+  }
+
+
+  /**
+   * Do server side actions on logout
+   */
+  public function logout_actions()
+  {
+    $config = $this->config->all();
+
+    // on logout action we're not connected to imap server
+    if (($config['logout_purge'] && !empty($config['trash_mbox'])) || $config['logout_expunge']) {
+      if (!$this->session->check_auth())
+        return;
+
+      $this->imap_connect();
+    }
+
+    if ($config['logout_purge'] && !empty($config['trash_mbox'])) {
+      $this->imap->clear_mailbox($config['trash_mbox']);
+    }
+
+    if ($config['logout_expunge']) {
+      $this->imap->expunge('INBOX');
+    }
+
+    // Try to save unsaved user preferences
+    if (!empty($_SESSION['preferences'])) {
+      $this->user->save_prefs(unserialize($_SESSION['preferences']));
+    }
+  }
+
+
+  /**
+   * Function to be executed in script shutdown
+   * Registered with register_shutdown_function()
+   */
+  public function shutdown()
+  {
+    foreach ($this->shutdown_functions as $function)
+      call_user_func($function);
+
+    if (is_object($this->smtp))
+      $this->smtp->disconnect();
+
+    foreach ($this->address_books as $book) {
+      if (is_object($book) && is_a($book, 'rcube_addressbook'))
+        $book->close();
+    }
+
+    foreach ($this->caches as $cache) {
+        if (is_object($cache))
+            $cache->close();
+    }
+
+    if (is_object($this->imap))
+      $this->imap->close();
+
+    // before closing the database connection, write session data
+    if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) {
+      session_write_close();
+    }
+
+    // write performance stats to logs/console
+    if ($this->config->get('devel_mode')) {
+      if (function_exists('memory_get_usage'))
+        $mem = show_bytes(memory_get_usage());
+      if (function_exists('memory_get_peak_usage'))
+        $mem .= '/'.show_bytes(memory_get_peak_usage());
+
+      $log = $this->task . ($this->action ? '/'.$this->action : '') . ($mem ? " [$mem]" : '');
+      if (defined('RCMAIL_START'))
+        rcube_print_time(RCMAIL_START, $log);
+      else
+        console($log);
+    }
+  }
+
+
+  /**
+   * Registers shutdown function to be executed on shutdown.
+   * The functions will be executed before destroying any
+   * objects like smtp, imap, session, etc.
+   *
+   * @param callback Function callback
+   */
+  public function add_shutdown_function($function)
+  {
+    $this->shutdown_functions[] = $function;
+  }
+
+
+  /**
+   * Generate a unique token to be used in a form request
+   *
+   * @return string The request token
+   */
+  public function get_request_token()
+  {
+    $sess_id = $_COOKIE[ini_get('session.name')];
+    if (!$sess_id) $sess_id = session_id();
+    $plugin = $this->plugins->exec_hook('request_token', array('value' => md5('RT' . $this->user->ID . $this->config->get('des_key') . $sess_id)));
+    return $plugin['value'];
+  }
+
+
+  /**
+   * Check if the current request contains a valid token
+   *
+   * @param int Request method
+   * @return boolean True if request token is valid false if not
+   */
+  public function check_request($mode = RCUBE_INPUT_POST)
+  {
+    $token = get_input_value('_token', $mode);
+    $sess_id = $_COOKIE[ini_get('session.name')];
+    return !empty($sess_id) && $token == $this->get_request_token();
+  }
+
+
+  /**
+   * Create unique authorization hash
+   *
+   * @param string Session ID
+   * @param int Timestamp
+   * @return string The generated auth hash
+   */
+  private function get_auth_hash($sess_id, $ts)
+  {
+    $auth_string = sprintf('rcmail*sess%sR%s*Chk:%s;%s',
+      $sess_id,
+      $ts,
+      $this->config->get('ip_check') ? $_SERVER['REMOTE_ADDR'] : '***.***.***.***',
+      $_SERVER['HTTP_USER_AGENT']);
+
+    if (function_exists('sha1'))
+      return sha1($auth_string);
+    else
+      return md5($auth_string);
+  }
+
+
+  /**
+   * Encrypt using 3DES
+   *
+   * @param string $clear clear text input
+   * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'
+   * @param boolean $base64 whether or not to base64_encode() the result before returning
+   *
+   * @return string encrypted text
+   */
+  public function encrypt($clear, $key = 'des_key', $base64 = true)
+  {
+    if (!$clear)
+      return '';
+    /*-
+     * Add a single canary byte to the end of the clear text, which
+     * will help find out how much of padding will need to be removed
+     * upon decryption; see http://php.net/mcrypt_generic#68082
+     */
+    $clear = pack("a*H2", $clear, "80");
+
+    if (function_exists('mcrypt_module_open') &&
+        ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")))
+    {
+      $iv = $this->create_iv(mcrypt_enc_get_iv_size($td));
+      mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
+      $cipher = $iv . mcrypt_generic($td, $clear);
+      mcrypt_generic_deinit($td);
+      mcrypt_module_close($td);
+    }
+    else {
+      @include_once 'des.inc';
+
+      if (function_exists('des')) {
+        $des_iv_size = 8;
+        $iv = $this->create_iv($des_iv_size);
+        $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv);
+      }
+      else {
+        raise_error(array(
+          'code' => 500, 'type' => 'php',
+          'file' => __FILE__, 'line' => __LINE__,
+          'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available"
+        ), true, true);
+      }
+    }
+
+    return $base64 ? base64_encode($cipher) : $cipher;
+  }
+
+  /**
+   * Decrypt 3DES-encrypted string
+   *
+   * @param string $cipher encrypted text
+   * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key'
+   * @param boolean $base64 whether or not input is base64-encoded
+   *
+   * @return string decrypted text
+   */
+  public function decrypt($cipher, $key = 'des_key', $base64 = true)
+  {
+    if (!$cipher)
+      return '';
+
+    $cipher = $base64 ? base64_decode($cipher) : $cipher;
+
+    if (function_exists('mcrypt_module_open') &&
+        ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")))
+    {
+      $iv_size = mcrypt_enc_get_iv_size($td);
+      $iv = substr($cipher, 0, $iv_size);
+
+      // session corruption? (#1485970)
+      if (strlen($iv) < $iv_size)
+        return '';
+
+      $cipher = substr($cipher, $iv_size);
+      mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
+      $clear = mdecrypt_generic($td, $cipher);
+      mcrypt_generic_deinit($td);
+      mcrypt_module_close($td);
+    }
+    else {
+      @include_once 'des.inc';
+
+      if (function_exists('des')) {
+        $des_iv_size = 8;
+        $iv = substr($cipher, 0, $des_iv_size);
+        $cipher = substr($cipher, $des_iv_size);
+        $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv);
+      }
+      else {
+        raise_error(array(
+          'code' => 500, 'type' => 'php',
+          'file' => __FILE__, 'line' => __LINE__,
+          'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available"
+        ), true, true);
+      }
+    }
+
+    /*-
+     * Trim PHP's padding and the canary byte; see note in
+     * rcmail::encrypt() and http://php.net/mcrypt_generic#68082
+     */
+    $clear = substr(rtrim($clear, "\0"), 0, -1);
+
+    return $clear;
+  }
+
+  /**
+   * Generates encryption initialization vector (IV)
+   *
+   * @param int Vector size
+   * @return string Vector string
+   */
+  private function create_iv($size)
+  {
+    // mcrypt_create_iv() can be slow when system lacks entrophy
+    // we'll generate IV vector manually
+    $iv = '';
+    for ($i = 0; $i < $size; $i++)
+        $iv .= chr(mt_rand(0, 255));
+    return $iv;
+  }
+
+  /**
+   * Build a valid URL to this instance of Roundcube
+   *
+   * @param mixed Either a string with the action or url parameters as key-value pairs
+   * @return string Valid application URL
+   */
+  public function url($p)
+  {
+    if (!is_array($p))
+      $p = array('_action' => @func_get_arg(0));
+
+    $task = $p['_task'] ? $p['_task'] : ($p['task'] ? $p['task'] : $this->task);
+    $p['_task'] = $task;
+    unset($p['task']);
+
+    $url = './';
+    $delm = '?';
+    foreach (array_reverse($p) as $key => $val) {
+      if ($val !== '') {
+        $par = $key[0] == '_' ? $key : '_'.$key;
+        $url .= $delm.urlencode($par).'='.urlencode($val);
+        $delm = '&';
+      }
+    }
+    return $url;
+  }
+
+
+  /**
+   * Use imagemagick or GD lib to read image properties
+   *
+   * @param string Absolute file path
+   * @return mixed Hash array with image props like type, width, height or False on error
+   */
+  public static function imageprops($filepath)
+  {
+    $rcmail = rcmail::get_instance();
+    if ($cmd = $rcmail->config->get('im_identify_path', false)) {
+      list(, $type, $size) = explode(' ', strtolower(rcmail::exec($cmd. ' 2>/dev/null {in}', array('in' => $filepath))));
+      if ($size)
+        list($width, $height) = explode('x', $size);
+    }
+    else if (function_exists('getimagesize')) {
+      $imsize = @getimagesize($filepath);
+      $width = $imsize[0];
+      $height = $imsize[1];
+      $type = preg_replace('!image/!', '', $imsize['mime']);
+    }
+
+    return $type ? array('type' => $type, 'width' => $width, 'height' => $height) : false;
+  }
+
+
+  /**
+   * Convert an image to a given size and type using imagemagick (ensures input is an image)
+   *
+   * @param $p['in']  Input filename (mandatory)
+   * @param $p['out'] Output filename (mandatory)
+   * @param $p['size']  Width x height of resulting image, e.g. "160x60"
+   * @param $p['type']  Output file type, e.g. "jpg"
+   * @param $p['-opts'] Custom command line options to ImageMagick convert
+   * @return Success of convert as true/false
+   */
+  public static function imageconvert($p)
+  {
+    $result = false;
+    $rcmail = rcmail::get_instance();
+    $convert  = $rcmail->config->get('im_convert_path', false);
+    $identify = $rcmail->config->get('im_identify_path', false);
+
+    // imagemagick is required for this
+    if (!$convert)
+        return false;
+
+    if (!(($imagetype = @exif_imagetype($p['in'])) && ($type = image_type_to_extension($imagetype, false))))
+      list(, $type) = explode(' ', strtolower(rcmail::exec($identify . ' 2>/dev/null {in}', $p))); # for things like eps
+
+    $type = strtr($type, array("jpeg" => "jpg", "tiff" => "tif", "ps" => "eps", "ept" => "eps"));
+    $p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif", 'quality' => 75);
+    $p['-opts'] = array('-resize' => $p['size'].'>') + (array)$p['-opts'];
+
+    if (in_array($type, explode(',', $p['types']))) # Valid type?
+      $result = rcmail::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {in} {type}:{out}', $p) === "";
+
+    return $result;
+  }
+
+
+  /**
+   * Construct shell command, execute it and return output as string.
+   * Keywords {keyword} are replaced with arguments
+   *
+   * @param $cmd Format string with {keywords} to be replaced
+   * @param $values (zero, one or more arrays can be passed)
+   * @return output of command. shell errors not detectable
+   */
+  public static function exec(/* $cmd, $values1 = array(), ... */)
+  {
+    $args = func_get_args();
+    $cmd = array_shift($args);
+    $values = $replacements = array();
+
+    // merge values into one array
+    foreach ($args as $arg)
+      $values += (array)$arg;
+
+    preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER);
+    foreach ($matches as $tags) {
+      list(, $tag, $option, $key) = $tags;
+      $parts = array();
+
+      if ($option) {
+        foreach ((array)$values["-$key"] as $key => $value) {
+          if ($value === true || $value === false || $value === null)
+            $parts[] = $value ? $key : "";
+          else foreach ((array)$value as $val)
+            $parts[] = "$key " . escapeshellarg($val);
+        }
+      }
+      else {
+        foreach ((array)$values[$key] as $value)
+          $parts[] = escapeshellarg($value);
+      }
+
+      $replacements[$tag] = join(" ", $parts);
+    }
+
+    // use strtr behaviour of going through source string once
+    $cmd = strtr($cmd, $replacements);
+
+    return (string)shell_exec($cmd);
+  }
+
+
+  /**
+   * Helper method to set a cookie with the current path and host settings
+   *
+   * @param string Cookie name
+   * @param string Cookie value
+   * @param string Expiration time
+   */
+  public static function setcookie($name, $value, $exp = 0)
+  {
+    if (headers_sent())
+      return;
+
+    $cookie = session_get_cookie_params();
+
+    setcookie($name, $value, $exp, $cookie['path'], $cookie['domain'],
+      rcube_https_check(), true);
+  }
+
+  /**
+   * Registers action aliases for current task
+   *
+   * @param array $map Alias-to-filename hash array
+   */
+  public function register_action_map($map)
+  {
+    if (is_array($map)) {
+      foreach ($map as $idx => $val) {
+        $this->action_map[$idx] = $val;
+      }
+    }
+  }
+
+  /**
+   * Returns current action filename
+   *
+   * @param array $map Alias-to-filename hash array
+   */
+  public function get_action_file()
+  {
+    if (!empty($this->action_map[$this->action])) {
+      return $this->action_map[$this->action];
+    }
+
+    return strtr($this->action, '-', '_') . '.inc';
+  }
+
+  /**
+   * Fixes some user preferences according to namespace handling change.
+   * Old Roundcube versions were using folder names with removed namespace prefix.
+   * Now we need to add the prefix on servers where personal namespace has prefix.
+   *
+   * @param rcube_user $user User object
+   */
+  private function fix_namespace_settings($user)
+  {
+    $prefix     = $this->imap->get_namespace('prefix');
+    $prefix_len = strlen($prefix);
+
+    if (!$prefix_len)
+      return;
+
+    $prefs = $this->config->all();
+    if (!empty($prefs['namespace_fixed']))
+      return;
+
+    // Build namespace prefix regexp
+    $ns     = $this->imap->get_namespace();
+    $regexp = array();
+
+    foreach ($ns as $entry) {
+      if (!empty($entry)) {
+        foreach ($entry as $item) {
+          if (strlen($item[0])) {
+            $regexp[] = preg_quote($item[0], '/');
+          }
+        }
+      }
+    }
+    $regexp = '/^('. implode('|', $regexp).')/';
+
+    // Fix preferences
+    $opts = array('drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox', 'archive_mbox');
+    foreach ($opts as $opt) {
+      if ($value = $prefs[$opt]) {
+        if ($value != 'INBOX' && !preg_match($regexp, $value)) {
+          $prefs[$opt] = $prefix.$value;
+        }
+      }
+    }
+
+    if (!empty($prefs['default_imap_folders'])) {
+      foreach ($prefs['default_imap_folders'] as $idx => $name) {
+        if ($name != 'INBOX' && !preg_match($regexp, $name)) {
+          $prefs['default_imap_folders'][$idx] = $prefix.$name;
+        }
+      }
+    }
+
+    if (!empty($prefs['search_mods'])) {
+      $folders = array();
+      foreach ($prefs['search_mods'] as $idx => $value) {
+        if ($idx != 'INBOX' && $idx != '*' && !preg_match($regexp, $idx)) {
+          $idx = $prefix.$idx;
+        }
+        $folders[$idx] = $value;
+      }
+      $prefs['search_mods'] = $folders;
+    }
+
+    if (!empty($prefs['message_threading'])) {
+      $folders = array();
+      foreach ($prefs['message_threading'] as $idx => $value) {
+        if ($idx != 'INBOX' && !preg_match($regexp, $idx)) {
+          $idx = $prefix.$idx;
+        }
+        $folders[$prefix.$idx] = $value;
+      }
+      $prefs['message_threading'] = $folders;
+    }
+
+    if (!empty($prefs['collapsed_folders'])) {
+      $folders     = explode('&&', $prefs['collapsed_folders']);
+      $count       = count($folders);
+      $folders_str = '';
+
+      if ($count) {
+          $folders[0]        = substr($folders[0], 1);
+          $folders[$count-1] = substr($folders[$count-1], 0, -1);
+      }
+
+      foreach ($folders as $value) {
+        if ($value != 'INBOX' && !preg_match($regexp, $value)) {
+          $value = $prefix.$value;
+        }
+        $folders_str .= '&'.$value.'&';
+      }
+      $prefs['collapsed_folders'] = $folders_str;
+    }
+
+    $prefs['namespace_fixed'] = true;
+
+    // save updated preferences and reset imap settings (default folders)
+    $user->save_prefs($prefs);
+    $this->set_imap_prop();
+  }
+
+}
index fc8c44156457280fcb1a06096718a995beaeead3..b9a3acbfa192fc3fad1b7b9d22a9cb42d6a72615 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_addressbook.php 4965 2011-07-25 11:48:50Z alec $
+ $Id: rcube_addressbook.php 5415 2011-11-11 15:04:45Z alec $
 
 */
 
@@ -30,7 +30,7 @@ abstract class rcube_addressbook
     /** constants for error reporting **/
     const ERROR_READ_ONLY = 1;
     const ERROR_NO_CONNECTION = 2;
-    const ERROR_INCOMPLETE = 3;
+    const ERROR_VALIDATE = 3;
     const ERROR_SAVING = 4;
     const ERROR_SEARCH = 5;
 
@@ -38,6 +38,7 @@ abstract class rcube_addressbook
     public $primary_key;
     public $groups = false;
     public $readonly = true;
+    public $searchonly = false;
     public $undelete = false;
     public $ready = false;
     public $group_id = null;
@@ -95,12 +96,16 @@ abstract class rcube_addressbook
      *
      * @param array   List of fields to search in
      * @param string  Search value
+     * @param int     Matching mode:
+     *                0 - partial (*abc*),
+     *                1 - strict (=),
+     *                2 - prefix (abc*)
      * @param boolean True if results are requested, False if count only
      * @param boolean True to skip the count query (select only)
      * @param array   List of fields that cannot be empty
      * @return object rcube_result_set List of contact records and 'count' value
      */
-    abstract function search($fields, $value, $strict=false, $select=true, $nocount=false, $required=array());
+    abstract function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array());
 
     /**
      * Count number of available contacts in database
@@ -181,15 +186,16 @@ abstract class rcube_addressbook
      * If input isn't valid, the message to display can be fetched using get_error()
      *
      * @param array Assoziative array with data to save
+     * @param boolean Attempt to fix/complete record automatically
      * @return boolean True if input is valid, False if not.
      */
-    public function validate($save_data)
+    public function validate(&$save_data, $autofix = false)
     {
         // check validity of email addresses
         foreach ($this->get_col_values('email', $save_data, true) as $email) {
             if (strlen($email)) {
                 if (!check_email(rcube_idn_to_ascii($email))) {
-                    $this->set_error('warning', rcube_label(array('name' => 'emailformaterror', 'vars' => array('email' => $email))));
+                    $this->set_error(self::ERROR_VALIDATE, rcube_label(array('name' => 'emailformaterror', 'vars' => array('email' => $email))));
                     return false;
                 }
             }
@@ -293,6 +299,18 @@ abstract class rcube_addressbook
         return array();
     }
 
+    /**
+     * Get group properties such as name and email address(es)
+     *
+     * @param string Group identifier
+     * @return array Group properties as hash array
+     */
+    function get_group($group_id)
+    {
+        /* empty for address books don't supporting groups */
+        return null;
+    }
+
     /**
      * Create a contact group with the given name
      *
@@ -385,7 +403,7 @@ abstract class rcube_addressbook
     {
         $out = array();
         foreach ($data as $c => $values) {
-            if (strpos($c, $col) === 0) {
+            if ($c === $col || strpos($c, $col.':') === 0) {
                 if ($flat) {
                     $out = array_merge($out, (array)$values);
                 }
index 10b214cf93df9bb746f15263df82c58f46f68a9e..1727586e41d743c2a8d7c200bac7cb9af4d786fa 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_browser.php 4971 2011-07-27 06:37:17Z alec $
+ $Id: rcube_browser.php 5499 2011-11-28 09:03:27Z alec $
 
 */
 
@@ -33,19 +33,19 @@ class rcube_browser
         $HTTP_USER_AGENT = strtolower($_SERVER['HTTP_USER_AGENT']);
 
         $this->ver = 0;
-        $this->win = strstr($HTTP_USER_AGENT, 'win');
-        $this->mac = strstr($HTTP_USER_AGENT, 'mac');
-        $this->linux = strstr($HTTP_USER_AGENT, 'linux');
-        $this->unix  = strstr($HTTP_USER_AGENT, 'unix');
+        $this->win = strpos($HTTP_USER_AGENT, 'win') != false;
+        $this->mac = strpos($HTTP_USER_AGENT, 'mac') != false;
+        $this->linux = strpos($HTTP_USER_AGENT, 'linux') != false;
+        $this->unix  = strpos($HTTP_USER_AGENT, 'unix') != false;
 
-        $this->opera = strstr($HTTP_USER_AGENT, 'opera');
-        $this->ns4 = strstr($HTTP_USER_AGENT, 'mozilla/4') && !stristr($HTTP_USER_AGENT, 'msie');
-        $this->ns  = ($this->ns4 || strstr($HTTP_USER_AGENT, 'netscape'));
-        $this->ie  = !$this->opera && stristr($HTTP_USER_AGENT, 'compatible; msie');
-        $this->mz  = !$this->ie && strstr($HTTP_USER_AGENT, 'mozilla/5');
-        $this->chrome = strstr($HTTP_USER_AGENT, 'chrome');
-        $this->khtml = strstr($HTTP_USER_AGENT, 'khtml');
-        $this->safari = !$this->chrome && ($this->khtml || strstr($HTTP_USER_AGENT, 'safari'));
+        $this->opera = strpos($HTTP_USER_AGENT, 'opera') !== false;
+        $this->ns4 = strpos($HTTP_USER_AGENT, 'mozilla/4') !== false && strpos($HTTP_USER_AGENT, 'msie') === false;
+        $this->ns  = ($this->ns4 || strpos($HTTP_USER_AGENT, 'netscape') !== false);
+        $this->ie  = !$this->opera && strpos($HTTP_USER_AGENT, 'compatible; msie') !== false;
+        $this->mz  = !$this->ie && strpos($HTTP_USER_AGENT, 'mozilla/5') !== false;
+        $this->chrome = strpos($HTTP_USER_AGENT, 'chrome') !== false;
+        $this->khtml = strpos($HTTP_USER_AGENT, 'khtml') !== false;
+        $this->safari = !$this->chrome && ($this->khtml || strpos($HTTP_USER_AGENT, 'safari') !== false);
 
         if ($this->ns || $this->chrome) {
             $test = preg_match('/(mozilla|chrome)\/([0-9.]+)/', $HTTP_USER_AGENT, $regs);
index 7224ee6bd505fe945933d523f17b2dc01bf69bde..cc472ae1cc864745dfbb44f7e073f9aadc5d96b1 100644 (file)
@@ -17,7 +17,7 @@
  | Author: Aleksander Machniak <alec@alec.pl>                            |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_cache.php 4909 2011-07-05 17:09:25Z alec $
+ $Id: rcube_cache.php 5305 2011-10-03 18:04:14Z alec $
 
 */
 
@@ -28,7 +28,7 @@
  * @package    Cache
  * @author     Thomas Bruederli <roundcube@gmail.com>
  * @author     Aleksander Machniak <alec@alec.pl>
- * @version    1.0
+ * @version    1.1
  */
 class rcube_cache
 {
@@ -187,6 +187,24 @@ class rcube_cache
     }
 
 
+    /**
+     * Remove cache records older than ttl
+     */
+    function expunge()
+    {
+        if ($this->type == 'db' && $this->db) {
+            $this->db->query(
+                "DELETE FROM ".get_table_name('cache').
+                " WHERE user_id = ?".
+                " AND cache_key LIKE ?".
+                " AND " . $this->db->unixtimestamp('created')." < ?",
+                $this->userid,
+                $this->prefix.'.%',
+                time() - $this->ttl);
+        }
+    }
+
+
     /**
      * Writes the cache back to the DB.
      */
@@ -227,26 +245,30 @@ class rcube_cache
             return null;
         }
 
-        if ($this->type == 'memcache') {
-            $data = $this->db->get($this->ckey($key));
-        }
-        else if ($this->type == 'apc') {
-            $data = apc_fetch($this->ckey($key));
-           }
+        if ($this->type != 'db') {
+            if ($this->type == 'memcache') {
+                $data = $this->db->get($this->ckey($key));
+            }
+            else if ($this->type == 'apc') {
+                $data = apc_fetch($this->ckey($key));
+               }
 
-        if ($data) {
-            $md5sum = md5($data);
-            $data   = $this->packed ? unserialize($data) : $data;
+            if ($data) {
+                $md5sum = md5($data);
+                $data   = $this->packed ? unserialize($data) : $data;
 
-            if ($nostore) {
-                return $data;
-            }
+                if ($nostore) {
+                    return $data;
+                }
 
-            $this->cache_sums[$key] = $md5sum;
-            $this->cache[$key]      = $data;
+                $this->cache_sums[$key] = $md5sum;
+                $this->cache[$key]      = $data;
+            }
+            else {
+                $this->cache[$key] = null;
+            }
         }
-
-        if ($this->type == 'db') {
+        else {
             $sql_result = $this->db->limitquery(
                 "SELECT cache_id, data, cache_key".
                 " FROM ".get_table_name('cache').
@@ -272,6 +294,9 @@ class rcube_cache
                    $this->cache_sums[$key] = $md5sum;
                 $this->cache_keys[$key] = $sql_arr['cache_id'];
             }
+            else {
+                $this->cache[$key] = null;
+            }
         }
 
         return $this->cache[$key];
index 7d46df05168c08e681a81ec5c19b522d33c1c5e2..ceada11fa84d96eda82d3df47d9c2225f58e8092 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_config.php 5151 2011-08-31 12:49:44Z alec $
+ $Id: rcube_config.php 5499 2011-11-28 09:03:27Z alec $
 
 */
 
@@ -91,6 +91,15 @@ class rcube_config
         // enable display_errors in 'show' level, but not for ajax requests
         ini_set('display_errors', intval(empty($_REQUEST['_remote']) && ($this->prop['debug_level'] & 4)));
 
+        // set timezone auto settings values
+        if ($this->prop['timezone'] == 'auto') {
+          $this->prop['dst_active'] = intval(date('I'));
+          $this->prop['_timezone_value'] = date('Z') / 3600 - $this->prop['dst_active'];
+        }
+        else if ($this->prop['dst_active'] === null) {
+          $this->prop['dst_active'] = intval(date('I'));
+        }
+
         // export config data
         $GLOBALS['CONFIG'] = &$this->prop;
     }
@@ -152,6 +161,9 @@ class rcube_config
     {
         $result = isset($this->prop[$name]) ? $this->prop[$name] : $def;
         $rcmail = rcmail::get_instance();
+        
+        if ($name == 'timezone' && isset($this->prop['_timezone_value']))
+            $result = $this->prop['_timezone_value'];
 
         if (is_object($rcmail->plugins)) {
             $plugin = $rcmail->plugins->exec_hook('config_get', array(
@@ -207,6 +219,14 @@ class rcube_config
 
         $this->userprefs = $prefs;
         $this->prop      = array_merge($this->prop, $prefs);
+
+        // override timezone settings with client values
+        if ($this->prop['timezone'] == 'auto') {
+            $this->prop['_timezone_value'] = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : $this->prop['_timezone_value'];
+            $this->prop['dst_active'] = $this->userprefs['dst_active'] = isset($_SESSION['dst_active']) ? $_SESSION['dst_active'] : $this->prop['dst_active'];
+        }
+        else if (isset($this->prop['_timezone_value']))
+           unset($this->prop['_timezone_value']);
     }
 
 
@@ -221,17 +241,13 @@ class rcube_config
     }
 
     /**
-     * Special getter for user's timezone
+     * Special getter for user's timezone offset including DST
+     *
+     * @return float  Timezone offset (in hours)
      */
     public function get_timezone()
     {
-      $tz = $this->get('timezone');
-      if ($tz == 'auto')
-        $tz = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : date('Z') / 3600;
-      else
-        $tz = intval($tz) + intval($this->get('dst_active'));
-
-      return $tz;
+      return floatval($this->get('timezone')) + intval($this->get('dst_active'));
     }
 
     /**
index af07b32bedd9d91bf07c2ed3f1b3dc20afa22320..a2883156ae8448a60b2885e19686049ffadecb3b 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_contacts.php 5011 2011-08-03 09:32:45Z alec $
+ $Id: rcube_contacts.php 5415 2011-11-11 15:04:45Z alec $
 
 */
 
@@ -41,7 +41,6 @@ class rcube_contacts extends rcube_addressbook
     private $user_id = 0;
     private $filter = null;
     private $result = null;
-    private $name;
     private $cache;
     private $table_cols = array('name', 'email', 'firstname', 'surname');
     private $fulltext_cols = array('name', 'firstname', 'surname', 'middlename', 'nickname',
@@ -50,6 +49,7 @@ class rcube_contacts extends rcube_addressbook
 
     // public properties
     public $primary_key = 'contact_id';
+    public $name;
     public $readonly = false;
     public $groups = true;
     public $undelete = true;
@@ -163,6 +163,29 @@ class rcube_contacts extends rcube_addressbook
     }
 
 
+    /**
+     * Get group properties such as name and email address(es)
+     *
+     * @param string Group identifier
+     * @return array Group properties as hash array
+     */
+    function get_group($group_id)
+    {
+        $sql_result = $this->db->query(
+            "SELECT * FROM ".get_table_name($this->db_groups).
+            " WHERE del<>1".
+            " AND contactgroup_id=?".
+            " AND user_id=?",
+            $group_id, $this->user_id);
+
+        if ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) {
+            $sql_arr['ID'] = $sql_arr['contactgroup_id'];
+            return $sql_arr;
+        }
+
+        return null;
+    }
+
     /**
      * List the current set of contact records
      *
@@ -245,14 +268,17 @@ class rcube_contacts extends rcube_addressbook
      *
      * @param mixed   $fields   The field name of array of field names to search in
      * @param mixed   $value    Search value (or array of values when $fields is array)
-     * @param boolean $strict   True for strict (=), False for partial (LIKE) matching
+     * @param int     $mode     Matching mode:
+     *                          0 - partial (*abc*),
+     *                          1 - strict (=),
+     *                          2 - prefix (abc*)
      * @param boolean $select   True if results are requested, False if count only
      * @param boolean $nocount  True to skip the count query (select only)
      * @param array   $required List of fields that cannot be empty
      *
      * @return object rcube_result_set Contact records and 'count' value
      */
-    function search($fields, $value, $strict=false, $select=true, $nocount=false, $required=array())
+    function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array())
     {
         if (!is_array($fields))
             $fields = array($fields);
@@ -260,6 +286,7 @@ class rcube_contacts extends rcube_addressbook
             $required = array($required);
 
         $where = $and_where = array();
+        $mode = intval($mode);
 
         foreach ($fields as $idx => $col) {
             // direct ID search
@@ -272,26 +299,56 @@ class rcube_contacts extends rcube_addressbook
             // fulltext search in all fields
             else if ($col == '*') {
                 $words = array();
-                foreach (explode(" ", self::normalize_string($value)) as $word)
-                    $words[] = $this->db->ilike('words', '%'.$word.'%');
+                foreach (explode(" ", self::normalize_string($value)) as $word) {
+                    switch ($mode) {
+                    case 1: // strict
+                        $words[] = '(' . $this->db->ilike('words', $word.' %')
+                            . ' OR ' . $this->db->ilike('words', '% '.$word.' %')
+                            . ' OR ' . $this->db->ilike('words', '% '.$word) . ')';
+                        break;
+                    case 2: // prefix
+                        $words[] = '(' . $this->db->ilike('words', $word.'%')
+                            . ' OR ' . $this->db->ilike('words', '% '.$word.'%') . ')';
+                        break;
+                    default: // partial
+                        $words[] = $this->db->ilike('words', '%'.$word.'%');
+                    }
+                }
                 $where[] = '(' . join(' AND ', $words) . ')';
             }
             else {
                 $val = is_array($value) ? $value[$idx] : $value;
                 // table column
                 if (in_array($col, $this->table_cols)) {
-                    if ($strict) {
+                    switch ($mode) {
+                    case 1: // strict
                         $where[] = $this->db->quoteIdentifier($col).' = '.$this->db->quote($val);
-                    }
-                    else {
+                        break;
+                    case 2: // prefix
+                        $where[] = $this->db->ilike($col, $val.'%');
+                        break;
+                    default: // partial
                         $where[] = $this->db->ilike($col, '%'.$val.'%');
                     }
                 }
                 // vCard field
                 else {
                     if (in_array($col, $this->fulltext_cols)) {
-                        foreach (explode(" ", self::normalize_string($val)) as $word)
-                            $words[] = $this->db->ilike('words', '%'.$word.'%');
+                        foreach (explode(" ", self::normalize_string($val)) as $word) {
+                            switch ($mode) {
+                            case 1: // strict
+                                $words[] = '(' . $this->db->ilike('words', $word.' %')
+                                    . ' OR ' . $this->db->ilike('words', '% '.$word.' %')
+                                    . ' OR ' . $this->db->ilike('words', '% '.$word) . ')';
+                                break;
+                            case 2: // prefix
+                                $words[] = '(' . $this->db->ilike('words', $word.'%')
+                                    . ' OR ' . $this->db->ilike('words', ' '.$word.'%') . ')';
+                                break;
+                            default: // partial
+                                $words[] = $this->db->ilike('words', '%'.$word.'%');
+                            }
+                        }
                         $where[] = '(' . join(' AND ', $words) . ')';
                     }
                     if (is_array($value))
@@ -339,13 +396,24 @@ class rcube_contacts extends rcube_addressbook
                         $search  = $post_search[$colname];
                         foreach ((array)$row[$col] as $value) {
                             // composite field, e.g. address
-                            if (is_array($value)) {
-                                $value = implode($value);
-                            }
-                            $value = mb_strtolower($value);
-                            if (($strict && $value == $search) || (!$strict && strpos($value, $search) !== false)) {
-                                $found[$colname] = true;
-                                break;
+                            foreach ((array)$value as $val) {
+                                $val = mb_strtolower($val);
+                                switch ($mode) {
+                                case 1:
+                                    $got = ($val == $search);
+                                    break;
+                                case 2:
+                                    $got = ($search == substr($val, 0, strlen($search)));
+                                    break;
+                                default:
+                                    $got = (strpos($val, $search) !== false);
+                                    break;
+                                }
+
+                                if ($got) {
+                                    $found[$colname] = true;
+                                    break 2;
+                                }
                             }
                         }
                     }
@@ -500,16 +568,17 @@ class rcube_contacts extends rcube_addressbook
      * If input not valid, the message to display can be fetched using get_error()
      *
      * @param array Assoziative array with data to save
+     * @param boolean Try to fix/complete record automatically
      * @return boolean True if input is valid, False if not.
      */
-    public function validate($save_data)
+    public function validate(&$save_data, $autofix = false)
     {
         // validate e-mail addresses
-        $valid = parent::validate($save_data);
+        $valid = parent::validate($save_data, $autofix);
 
         // require at least one e-mail address (syntax check is already done)
         if ($valid && !array_filter($this->get_col_values('email', $save_data, true))) {
-            $this->set_error('warning', 'noemailwarning');
+            $this->set_error(self::ERROR_VALIDATE, 'noemailwarning');
             $valid = false;
         }
 
@@ -773,8 +842,9 @@ class rcube_contacts extends rcube_addressbook
         $sql_result = $this->db->query(
             "UPDATE ".get_table_name($this->db_groups).
             " SET del=1, changed=".$this->db->now().
-            " WHERE contactgroup_id=?",
-            $gid
+            " WHERE contactgroup_id=?".
+            " AND user_id=?",
+            $gid, $this->user_id
         );
 
         $this->cache = null;
@@ -798,8 +868,9 @@ class rcube_contacts extends rcube_addressbook
         $sql_result = $this->db->query(
             "UPDATE ".get_table_name($this->db_groups).
             " SET name=?, changed=".$this->db->now().
-            " WHERE contactgroup_id=?",
-            $name, $gid
+            " WHERE contactgroup_id=?".
+            " AND user_id=?",
+            $name, $gid, $this->user_id
         );
 
         return $this->db->affected_rows() ? $name : false;
index f57f6a6c0a073d78fde1fac4976c58791118769a..77b7ff2d86db642ceab0a37b901419ce6e311c54 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_html_page.php 5151 2011-08-31 12:49:44Z alec $
+ $Id: rcube_html_page.php 5135 2011-08-26 09:22:53Z alec $
 
 */
 
index 48627d890d2fe51a79c86a1f05bce29d09b9c4b3..b48adcfc8551efc4e61709ac18e99e14362b95c7 100644 (file)
@@ -5,7 +5,8 @@
  | program/include/rcube_imap.php                                        |
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2005-2010, The Roundcube Dev Team                       |
+ | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
+ | Copyright (C) 2011, Kolab Systems AG                                  |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -16,7 +17,7 @@
  | Author: Aleksander Machniak <alec@alec.pl>                            |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_imap.php 5281 2011-09-27 07:29:49Z alec $
+ $Id: rcube_imap.php 5601 2011-12-14 09:08:54Z alec $
 
 */
 
@@ -31,7 +32,6 @@
  */
 class rcube_imap
 {
-    public $debug_level = 1;
     public $skip_deleted = false;
     public $page_size = 10;
     public $list_page = 1;
@@ -47,11 +47,11 @@ class rcube_imap
     public $conn;
 
     /**
-     * Instance of rcube_mdb2
+     * Instance of rcube_imap_cache
      *
-     * @var rcube_mdb2
+     * @var rcube_imap_cache
      */
-    private $db;
+    private $mcache;
 
     /**
      * Instance of rcube_cache
@@ -59,6 +59,14 @@ class rcube_imap
      * @var rcube_cache
      */
     private $cache;
+
+    /**
+     * Internal (in-memory) cache
+     *
+     * @var array
+     */
+    private $icache = array();
+
     private $mailbox = 'INBOX';
     private $delimiter = NULL;
     private $namespace = NULL;
@@ -67,8 +75,6 @@ class rcube_imap
     private $default_charset = 'ISO-8859-1';
     private $struct_charset = NULL;
     private $default_folders = array('INBOX');
-    private $messages_caching = false;
-    private $icache = array();
     private $uid_id_map = array();
     private $msg_headers = array();
     public  $search_set = NULL;
@@ -77,10 +83,10 @@ class rcube_imap
     private $search_sort_field = '';
     private $search_threads = false;
     private $search_sorted = false;
-    private $db_header_fields = array('idx', 'uid', 'subject', 'from', 'to', 'cc', 'date', 'size');
     private $options = array('auth_method' => 'check');
     private $host, $user, $pass, $port, $ssl;
     private $caching = false;
+    private $messages_caching = false;
 
     /**
      * All (additional) headers used (in any way) by Roundcube
@@ -96,7 +102,6 @@ class rcube_imap
         'MESSAGE-ID',
         'CONTENT-TRANSFER-ENCODING',
         'REFERENCES',
-        'X-PRIORITY',
         'X-DRAFT-INFO',
         'MAIL-FOLLOWUP-TO',
         'MAIL-REPLY-TO',
@@ -170,12 +175,13 @@ class rcube_imap
         $attempt = 0;
         do {
             $data = rcmail::get_instance()->plugins->exec_hook('imap_connect',
-                array('host' => $host, 'user' => $user, 'attempt' => ++$attempt));
+                array_merge($this->options, array('host' => $host, 'user' => $user,
+                    'attempt' => ++$attempt)));
 
             if (!empty($data['pass']))
                 $pass = $data['pass'];
 
-            $this->conn->connect($data['host'], $data['user'], $pass, $this->options);
+            $this->conn->connect($data['host'], $data['user'], $pass, $data);
         } while(!$this->conn->connected() && $data['retry']);
 
         $this->host = $data['host'];
@@ -214,6 +220,8 @@ class rcube_imap
     function close()
     {
         $this->conn->closeConnection();
+        if ($this->mcache)
+            $this->mcache->close();
     }
 
 
@@ -308,6 +316,19 @@ class rcube_imap
     }
 
 
+    /**
+     * Activate/deactivate debug mode
+     *
+     * @param boolean $dbg True if IMAP conversation should be logged
+     * @access public
+     */
+    function set_debug($dbg = true)
+    {
+        $this->options['debug'] = $dbg;
+        $this->conn->setDebug($dbg, array($this, 'debug_handler'));
+    }
+
+
     /**
      * Set default message charset
      *
@@ -457,7 +478,7 @@ class rcube_imap
      */
     function get_mailbox_name()
     {
-        return $this->conn->connected() ? $this->mailbox : '';
+        return $this->mailbox;
     }
 
 
@@ -689,7 +710,7 @@ class rcube_imap
 
             if ($status) {
                 $this->set_folder_stats($mailbox, 'cnt', $res['msgcount']);
-                $this->set_folder_stats($mailbox, 'maxuid', $res['maxuid'] ? $this->_id2uid($res['maxuid'], $mailbox) : 0);
+                $this->set_folder_stats($mailbox, 'maxuid', $res['maxuid'] ? $this->id2uid($res['maxuid'], $mailbox) : 0);
             }
         }
         // RECENT count is fetched a bit different
@@ -722,9 +743,9 @@ class rcube_imap
             $count = is_array($index) ? $index['COUNT'] : 0;
 
             if ($mode == 'ALL') {
-                if ($need_uid && $this->messages_caching) {
-                    // Save messages index for check_cache_status()
-                    $this->icache['all_undeleted_idx'] = $index['ALL'];
+                if ($this->messages_caching) {
+                    // Save additional info required by cache status check
+                    $this->icache['undeleted_idx'] = array($mailbox, $index['ALL'], $index['COUNT']);
                 }
                 if ($status) {
                     $this->set_folder_stats($mailbox, 'cnt', $count);
@@ -739,7 +760,7 @@ class rcube_imap
                 $count = $this->conn->countMessages($mailbox);
                 if ($status) {
                     $this->set_folder_stats($mailbox,'cnt', $count);
-                    $this->set_folder_stats($mailbox, 'maxuid', $count ? $this->_id2uid($count, $mailbox) : 0);
+                    $this->set_folder_stats($mailbox, 'maxuid', $count ? $this->id2uid($count, $mailbox) : 0);
                 }
             }
         }
@@ -774,7 +795,7 @@ class rcube_imap
                 'maxuid'   => $dcount ? max(array_keys($this->icache['threads']['depth'])) : 0,
             );
         }
-        else if (is_array($result = $this->_fetch_threads($mailbox))) {
+        else if (is_array($result = $this->fetch_threads($mailbox))) {
             $dcount = count($result[1]);
             $result = array(
                 'count'    => count($result[0]),
@@ -805,7 +826,7 @@ class rcube_imap
             $mailbox = $this->mailbox;
         }
 
-        return $this->_list_headers($mailbox, $page, $sort_field, $sort_order, false, $slice);
+        return $this->_list_headers($mailbox, $page, $sort_field, $sort_order, $slice);
     }
 
 
@@ -817,11 +838,11 @@ class rcube_imap
      * @param   string   $sort_field Header field to sort by
      * @param   string   $sort_order Sort order [ASC|DESC]
      * @param   int      $slice      Number of slice items to extract from result array
+     *
      * @return  array    Indexed array with message header objects
-     * @access  private
      * @see     rcube_imap::list_headers
      */
-    private function _list_headers($mailbox='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $recursive=false, $slice=0)
+    private function _list_headers($mailbox='', $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0)
     {
         if (!strlen($mailbox))
             return array();
@@ -831,41 +852,39 @@ class rcube_imap
             return $this->_list_header_set($mailbox, $page, $sort_field, $sort_order, $slice);
 
         if ($this->threading)
-            return $this->_list_thread_headers($mailbox, $page, $sort_field, $sort_order, $recursive, $slice);
+            return $this->_list_thread_headers($mailbox, $page, $sort_field, $sort_order, $slice);
 
         $this->_set_sort_order($sort_field, $sort_order);
 
-        $page         = $page ? $page : $this->list_page;
-        $cache_key    = $mailbox.'.msg';
-
-        if ($this->messages_caching) {
-            // cache is OK, we can get messages from local cache
-            // (assume cache is in sync when in recursive mode)
-            if ($recursive || $this->check_cache_status($mailbox, $cache_key)>0) {
-                $start_msg = ($page-1) * $this->page_size;
-                $a_msg_headers = $this->get_message_cache($cache_key, $start_msg,
-                    $start_msg+$this->page_size, $this->sort_field, $this->sort_order);
-                $result = array_values($a_msg_headers);
-                if ($slice)
-                    $result = array_slice($result, -$slice, $slice);
-                return $result;
-            }
-            // cache is incomplete, sync it (all messages in the folder)
-            else if (!$recursive) {
-                $this->sync_header_index($mailbox);
-                return $this->_list_headers($mailbox, $page, $this->sort_field, $this->sort_order, true, $slice);
-            }
-        }
+        $page = $page ? $page : $this->list_page;
 
-        // retrieve headers from IMAP
-        $a_msg_headers = array();
+        // Use messages cache
+        if ($mcache = $this->get_mcache_engine()) {
+            $msg_index = $mcache->get_index($mailbox, $this->sort_field, $this->sort_order);
+
+            if (empty($msg_index))
+                return array();
+
+            $from      = ($page-1) * $this->page_size;
+            $to        = $from + $this->page_size;
+            $msg_index = array_values($msg_index); // UIDs
+            $is_uid    = true;
+            $sorted    = true;
+
+            if ($from || $to)
+                $msg_index = array_slice($msg_index, $from, $to - $from);
 
+            if ($slice)
+                $msg_index = array_slice($msg_index, -$slice, $slice);
+
+            $a_msg_headers = $mcache->get_messages($mailbox, $msg_index);
+        }
+        // retrieve headers from IMAP
         // use message index sort as default sorting (for better performance)
-        if (!$this->sort_field) {
+        else if (!$this->sort_field) {
             if ($this->skip_deleted) {
                 // @TODO: this could be cached
                 if ($msg_index = $this->_search_index($mailbox, 'ALL UNDELETED')) {
-                    $max = max($msg_index);
                     list($begin, $end) = $this->_get_message_range(count($msg_index), $page);
                     $msg_index = array_slice($msg_index, $begin, $end-$begin);
                 }
@@ -882,30 +901,32 @@ class rcube_imap
 
             // fetch reqested headers from server
             if ($msg_index)
-                $this->_fetch_headers($mailbox, join(",", $msg_index), $a_msg_headers, $cache_key);
+                $a_msg_headers = $this->fetch_headers($mailbox, $msg_index);
         }
         // use SORT command
         else if ($this->get_capability('SORT') &&
             // Courier-IMAP provides SORT capability but allows to disable it by admin (#1486959)
-            ($msg_index = $this->conn->sort($mailbox, $this->sort_field, $this->skip_deleted ? 'UNDELETED' : '')) !== false
+            ($msg_index = $this->conn->sort($mailbox, $this->sort_field,
+                $this->skip_deleted ? 'UNDELETED' : '', true)) !== false
         ) {
             if (!empty($msg_index)) {
                 list($begin, $end) = $this->_get_message_range(count($msg_index), $page);
-                $max = max($msg_index);
                 $msg_index = array_slice($msg_index, $begin, $end-$begin);
+                $is_uid    = true;
 
                 if ($slice)
                     $msg_index = array_slice($msg_index, ($this->sort_order == 'DESC' ? 0 : -$slice), $slice);
 
                 // fetch reqested headers from server
-                $this->_fetch_headers($mailbox, join(',', $msg_index), $a_msg_headers, $cache_key);
+                $a_msg_headers = $this->fetch_headers($mailbox, $msg_index, true);
             }
         }
         // fetch specified header for all messages and sort
-        else if ($a_index = $this->conn->fetchHeaderIndex($mailbox, "1:*", $this->sort_field, $this->skip_deleted)) {
-            asort($a_index); // ASC
-            $msg_index = array_keys($a_index);
-            $max = max($msg_index);
+        else if ($msg_index = $this->conn->fetchHeaderIndex($mailbox, "1:*",
+            $this->sort_field, $this->skip_deleted)
+        ) {
+            asort($msg_index); // ASC
+            $msg_index = array_keys($msg_index);
             list($begin, $end) = $this->_get_message_range(count($msg_index), $page);
             $msg_index = array_slice($msg_index, $begin, $end-$begin);
 
@@ -913,26 +934,19 @@ class rcube_imap
                 $msg_index = array_slice($msg_index, ($this->sort_order == 'DESC' ? 0 : -$slice), $slice);
 
             // fetch reqested headers from server
-            $this->_fetch_headers($mailbox, join(",", $msg_index), $a_msg_headers, $cache_key);
+            $a_msg_headers = $this->fetch_headers($mailbox, $msg_index);
         }
 
-        // 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
-        // ...
-
         // return empty array if no messages found
         if (!is_array($a_msg_headers) || empty($a_msg_headers))
             return array();
 
         // use this class for message sorting
         $sorter = new rcube_header_sorter();
-        $sorter->set_sequence_numbers($msg_index);
+        $sorter->set_index($msg_index, $is_uid);
         $sorter->sort_headers($a_msg_headers);
 
-        if ($this->sort_order == 'DESC')
+        if ($this->sort_order == 'DESC' && !$sorted)
             $a_msg_headers = array_reverse($a_msg_headers);
 
         return array_values($a_msg_headers);
@@ -946,27 +960,28 @@ class rcube_imap
      * @param   int      $page       Current page to list
      * @param   string   $sort_field Header field to sort by
      * @param   string   $sort_order Sort order [ASC|DESC]
-     * @param   boolean  $recursive  True if called recursively
      * @param   int      $slice      Number of slice items to extract from result array
+     *
      * @return  array    Indexed array with message header objects
-     * @access  private
      * @see     rcube_imap::list_headers
      */
-    private function _list_thread_headers($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $recursive=false, $slice=0)
+    private function _list_thread_headers($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0)
     {
         $this->_set_sort_order($sort_field, $sort_order);
 
-        $page = $page ? $page : $this->list_page;
-//    $cache_key = $mailbox.'.msg';
-//    $cache_status = $this->check_cache_status($mailbox, $cache_key);
+        $page   = $page ? $page : $this->list_page;
+        $mcache = $this->get_mcache_engine();
 
-        // get all threads (default sort order)
-        list ($thread_tree, $msg_depth, $has_children) = $this->_fetch_threads($mailbox);
+        // get all threads (not sorted)
+        if ($mcache)
+            list ($thread_tree, $msg_depth, $has_children) = $mcache->get_thread($mailbox);
+        else
+            list ($thread_tree, $msg_depth, $has_children) = $this->fetch_threads($mailbox);
 
         if (empty($thread_tree))
             return array();
 
-        $msg_index = $this->_sort_threads($mailbox, $thread_tree);
+        $msg_index = $this->sort_threads($mailbox, $thread_tree);
 
         return $this->_fetch_thread_headers($mailbox,
             $thread_tree, $msg_depth, $has_children, $msg_index, $page, $slice);
@@ -974,14 +989,20 @@ class rcube_imap
 
 
     /**
-     * Private method for fetching threads data
+     * Method for fetching threads data
+     *
+     * @param  string $mailbox  Folder name
+     * @param  bool   $force    Use IMAP server, no cache
      *
-     * @param   string   $mailbox Mailbox/folder name
      * @return  array    Array with thread data
-     * @access  private
      */
-    private function _fetch_threads($mailbox)
+    function fetch_threads($mailbox, $force = false)
     {
+        if (!$force && ($mcache = $this->get_mcache_engine())) {
+            // don't store in self's internal cache, cache has it's own internal cache
+            return $mcache->get_thread($mailbox);
+        }
+
         if (empty($this->icache['threads'])) {
             // get all threads
             $result = $this->conn->thread($mailbox, $this->threading,
@@ -1012,12 +1033,12 @@ class rcube_imap
      * @param array   $msg_index    Messages index
      * @param int     $page         List page number
      * @param int     $slice        Number of threads to slice
+     *
      * @return array  Messages headers
      * @access  private
      */
     private function _fetch_thread_headers($mailbox, $thread_tree, $msg_depth, $has_children, $msg_index, $page, $slice=0)
     {
-        $cache_key = $mailbox.'.msg';
         // now get IDs for current page
         list($begin, $end) = $this->_get_message_range(count($msg_index), $page);
         $msg_index = array_slice($msg_index, $begin, $end-$begin);
@@ -1038,7 +1059,7 @@ class rcube_imap
         }
 
         // fetch reqested headers from server
-        $this->_fetch_headers($mailbox, $all_ids, $a_msg_headers, $cache_key);
+        $a_msg_headers = $this->fetch_headers($mailbox, $all_ids);
 
         // return empty array if no messages found
         if (!is_array($a_msg_headers) || empty($a_msg_headers))
@@ -1046,7 +1067,7 @@ class rcube_imap
 
         // use this class for message sorting
         $sorter = new rcube_header_sorter();
-        $sorter->set_sequence_numbers($all_ids);
+        $sorter->set_index($all_ids);
         $sorter->sort_headers($a_msg_headers);
 
         // Set depth, has_children and unread_children fields in headers
@@ -1077,7 +1098,7 @@ class rcube_imap
 
             if (!empty($parents)) {
                 $headers[$idx]->parent_uid = end($parents);
-                if (!$header->seen)
+                if (empty($header->flags['SEEN']))
                     $headers[$parents[0]]->unread_children++;
             }
             array_push($parents, $header->uid);
@@ -1135,11 +1156,11 @@ class rcube_imap
                 $msgs = array_slice($msgs, -$slice, $slice);
 
             // fetch headers
-            $this->_fetch_headers($mailbox, join(',',$msgs), $a_msg_headers, NULL);
+            $a_msg_headers = $this->fetch_headers($mailbox, $msgs);
 
             // I didn't found in RFC that FETCH always returns messages sorted by index
             $sorter = new rcube_header_sorter();
-            $sorter->set_sequence_numbers($msgs);
+            $sorter->set_index($msgs);
             $sorter->sort_headers($a_msg_headers);
 
             return array_values($a_msg_headers);
@@ -1165,10 +1186,10 @@ class rcube_imap
                 $msgs = array_slice($msgs, -$slice, $slice);
 
             // fetch headers
-            $this->_fetch_headers($mailbox, join(',',$msgs), $a_msg_headers, NULL);
+            $a_msg_headers = $this->fetch_headers($mailbox, $msgs);
 
             $sorter = new rcube_header_sorter();
-            $sorter->set_sequence_numbers($msgs);
+            $sorter->set_index($msgs);
             $sorter->sort_headers($a_msg_headers);
 
             return array_values($a_msg_headers);
@@ -1184,21 +1205,22 @@ class rcube_imap
                 if ($slice)
                     $msgs = array_slice($msgs, -$slice, $slice);
                 // ...and fetch headers
-                $this->_fetch_headers($mailbox, join(',', $msgs), $a_msg_headers, NULL);
+                $a_msg_headers = $this->fetch_headers($mailbox, $msgs);
+
 
                 // return empty array if no messages found
                 if (!is_array($a_msg_headers) || empty($a_msg_headers))
                     return array();
 
                 $sorter = new rcube_header_sorter();
-                $sorter->set_sequence_numbers($msgs);
+                $sorter->set_index($msgs);
                 $sorter->sort_headers($a_msg_headers);
 
                 return array_values($a_msg_headers);
             }
             else {
                 // for small result set we can fetch all messages headers
-                $this->_fetch_headers($mailbox, join(',', $msgs), $a_msg_headers, NULL);
+                $a_msg_headers = $this->fetch_headers($mailbox, $msgs);
 
                 // return empty array if no messages found
                 if (!is_array($a_msg_headers) || empty($a_msg_headers))
@@ -1256,7 +1278,7 @@ class rcube_imap
 
         $this->_set_sort_order($sort_field, $sort_order);
 
-        $msg_index = $this->_sort_threads($mailbox, $thread_tree, array_keys($msg_depth));
+        $msg_index = $this->sort_threads($mailbox, $thread_tree, array_keys($msg_depth));
 
         return $this->_fetch_thread_headers($mailbox,
             $thread_tree, $msg_depth, $has_children, $msg_index, $page, $slice=0);
@@ -1297,64 +1319,37 @@ class rcube_imap
 
 
     /**
-     * Fetches message headers (used for loop)
+     * Fetches messages headers
+     *
+     * @param  string  $mailbox  Mailbox name
+     * @param  array   $msgs     Messages sequence numbers
+     * @param  bool    $is_uid   Enable if $msgs numbers are UIDs
+     * @param  bool    $force    Disables cache use
      *
-     * @param  string  $mailbox       Mailbox name
-     * @param  string  $msgs          Message index to fetch
-     * @param  array   $a_msg_headers Reference to message headers array
-     * @param  string  $cache_key     Cache index key
-     * @return int     Messages count
+     * @return array Messages headers indexed by UID
      * @access private
      */
-    private function _fetch_headers($mailbox, $msgs, &$a_msg_headers, $cache_key)
+    function fetch_headers($mailbox, $msgs, $is_uid = false, $force = false)
     {
-        // fetch reqested headers from server
-        $a_header_index = $this->conn->fetchHeaders(
-            $mailbox, $msgs, false, false, $this->get_fetch_headers());
-
-        if (empty($a_header_index))
-            return 0;
+        if (empty($msgs))
+            return array();
 
-        foreach ($a_header_index as $i => $headers) {
-            $a_msg_headers[$headers->uid] = $headers;
+        if (!$force && ($mcache = $this->get_mcache_engine())) {
+            return $mcache->get_messages($mailbox, $msgs, $is_uid);
         }
 
-        // Update cache
-        if ($this->messages_caching && $cache_key) {
-            // cache is incomplete?
-            $cache_index = $this->get_message_cache_index($cache_key);
-
-            foreach ($a_header_index as $headers) {
-                // message in cache
-                if ($cache_index[$headers->id] == $headers->uid) {
-                    unset($cache_index[$headers->id]);
-                    continue;
-                }
-                // wrong UID at this position
-                if ($cache_index[$headers->id]) {
-                    $for_remove[] = $cache_index[$headers->id];
-                    unset($cache_index[$headers->id]);
-                }
-                // message UID in cache but at wrong position
-                if (is_int($key = array_search($headers->uid, $cache_index))) {
-                    $for_remove[] = $cache_index[$key];
-                    unset($cache_index[$key]);
-                }
-
-                $for_create[] = $headers->uid;
-            }
+        // fetch reqested headers from server
+        $index = $this->conn->fetchHeaders(
+            $mailbox, $msgs, $is_uid, false, $this->get_fetch_headers());
 
-            if ($for_remove)
-                $this->remove_message_cache($cache_key, $for_remove);
+        if (empty($index))
+            return array();
 
-            // add messages to cache
-            foreach ((array)$for_create as $uid) {
-                $headers = $a_msg_headers[$uid];
-                $this->add_message_cache($cache_key, $headers->id, $headers, NULL, true);
-            }
+        foreach ($index as $headers) {
+            $a_msg_headers[$headers->uid] = $headers;
         }
 
-        return count($a_msg_headers);
+        return $a_msg_headers;
     }
 
 
@@ -1378,6 +1373,11 @@ class rcube_imap
         $this->_messagecount($mailbox, 'ALL', true);
 
         $result = 0;
+
+        if (empty($old)) {
+            return $result;
+        }
+
         $new = $this->get_folder_stats($mailbox);
 
         // got new messages
@@ -1471,7 +1471,7 @@ class rcube_imap
             }
             else {
                 $a_index = $this->conn->fetchHeaderIndex($mailbox,
-                       join(',', $this->search_set), $this->sort_field, $this->skip_deleted);
+                    join(',', $this->search_set), $this->sort_field, $this->skip_deleted);
 
                 if (is_array($a_index)) {
                     if ($this->sort_order=="ASC")
@@ -1492,53 +1492,65 @@ class rcube_imap
             return $this->icache[$key];
 
         // check local cache
-        $cache_key = $mailbox.'.msg';
-        $cache_status = $this->check_cache_status($mailbox, $cache_key);
-
-        // cache is OK
-        if ($cache_status>0) {
-            $a_index = $this->get_message_cache_index($cache_key,
-                $this->sort_field, $this->sort_order);
-            return array_keys($a_index);
+        if ($mcache = $this->get_mcache_engine()) {
+            $a_index = $mcache->get_index($mailbox, $this->sort_field, $this->sort_order);
+            $this->icache[$key] = array_keys($a_index);
+        }
+        // fetch from IMAP server
+        else {
+            $this->icache[$key] = $this->message_index_direct(
+                $mailbox, $this->sort_field, $this->sort_order);
         }
 
+        return $this->icache[$key];
+    }
+
+
+    /**
+     * Return sorted array of message IDs (not UIDs) directly from IMAP server.
+     * Doesn't use cache and ignores current search settings.
+     *
+     * @param string $mailbox    Mailbox to get index from
+     * @param string $sort_field Sort column
+     * @param string $sort_order Sort order [ASC, DESC]
+     *
+     * @return array Indexed array with message IDs
+     */
+    function message_index_direct($mailbox, $sort_field = null, $sort_order = null)
+    {
         // use message index sort as default sorting
-        if (!$this->sort_field) {
+        if (!$sort_field) {
             if ($this->skip_deleted) {
                 $a_index = $this->conn->search($mailbox, 'ALL UNDELETED');
                 // I didn't found that SEARCH should return sorted IDs
                 if (is_array($a_index))
                     sort($a_index);
-            } else if ($max = $this->_messagecount($mailbox)) {
+            } else if ($max = $this->_messagecount($mailbox, 'ALL', true, false)) {
                 $a_index = range(1, $max);
             }
 
-            if ($a_index !== false && $this->sort_order == 'DESC')
+            if ($a_index !== false && $sort_order == 'DESC')
                 $a_index = array_reverse($a_index);
-
-            $this->icache[$key] = $a_index;
         }
         // fetch complete message index
         else if ($this->get_capability('SORT') &&
             ($a_index = $this->conn->sort($mailbox,
-                $this->sort_field, $this->skip_deleted ? 'UNDELETED' : '')) !== false
+                $sort_field, $this->skip_deleted ? 'UNDELETED' : '')) !== false
         ) {
-            if ($this->sort_order == 'DESC')
+            if ($sort_order == 'DESC')
                 $a_index = array_reverse($a_index);
-
-            $this->icache[$key] = $a_index;
         }
         else if ($a_index = $this->conn->fetchHeaderIndex(
-            $mailbox, "1:*", $this->sort_field, $this->skip_deleted)) {
-            if ($this->sort_order=="ASC")
+            $mailbox, "1:*", $sort_field, $skip_deleted)) {
+            if ($sort_order=="ASC")
                 asort($a_index);
-            else if ($this->sort_order=="DESC")
+            else if ($sort_order=="DESC")
                 arsort($a_index);
 
-            $this->icache[$key] = array_keys($a_index);
+            $a_index = array_keys($a_index);
         }
 
-        return $this->icache[$key] !== false ? $this->icache[$key] : array();
+        return $a_index !== false ? $a_index : array();
     }
 
 
@@ -1570,19 +1582,9 @@ class rcube_imap
         // have stored it in RAM
         if (isset($this->icache[$key]))
             return $this->icache[$key];
-/*
-        // check local cache
-        $cache_key = $mailbox.'.msg';
-        $cache_status = $this->check_cache_status($mailbox, $cache_key);
 
-        // cache is OK
-        if ($cache_status>0) {
-            $a_index = $this->get_message_cache_index($cache_key, $this->sort_field, $this->sort_order);
-            return array_keys($a_index);
-        }
-*/
         // get all threads (default sort order)
-        list ($thread_tree) = $this->_fetch_threads($mailbox);
+        list ($thread_tree) = $this->fetch_threads($mailbox);
 
         $this->icache[$key] = $this->_flatten_threads($mailbox, $thread_tree);
 
@@ -1594,7 +1596,7 @@ class rcube_imap
      * Return array of threaded messages (all, not only roots)
      *
      * @param string $mailbox     Mailbox to get index from
-     * @param array  $thread_tree Threaded messages array (see _fetch_threads())
+     * @param array  $thread_tree Threaded messages array (see fetch_threads())
      * @param array  $ids         Message IDs if we know what we need (e.g. search result)
      *                            for better performance
      * @return array Indexed array with message IDs
@@ -1606,7 +1608,7 @@ class rcube_imap
         if (empty($thread_tree))
             return array();
 
-        $msg_index = $this->_sort_threads($mailbox, $thread_tree, $ids);
+        $msg_index = $this->sort_threads($mailbox, $thread_tree, $ids);
 
         if ($this->sort_order == 'DESC')
             $msg_index = array_reverse($msg_index);
@@ -1625,99 +1627,6 @@ class rcube_imap
     }
 
 
-    /**
-     * @param string $mailbox Mailbox name
-     * @access private
-     */
-    private function sync_header_index($mailbox)
-    {
-        $cache_key = $mailbox.'.msg';
-        $cache_index = $this->get_message_cache_index($cache_key);
-        $chunk_size = 1000;
-
-        // cache is empty, get all messages
-        if (is_array($cache_index) && empty($cache_index)) {
-            $max = $this->_messagecount($mailbox);
-            // syncing a big folder maybe slow
-            @set_time_limit(0);
-            $start = 1;
-            $end   = min($chunk_size, $max);
-            while (true) {
-                // do this in loop to save memory (1000 msgs ~= 10 MB)
-                if ($headers = $this->conn->fetchHeaders($mailbox,
-                    "$start:$end", false, false, $this->get_fetch_headers())
-                ) {
-                    foreach ($headers as $header) {
-                        $this->add_message_cache($cache_key, $header->id, $header, NULL, true);
-                    }
-                }
-                if ($end - $start < $chunk_size - 1)
-                    break;
-
-                $end   = min($end+$chunk_size, $max);
-                $start += $chunk_size;
-            }
-            return;
-        }
-
-        // fetch complete message index
-        if (isset($this->icache['folder_index']))
-            $a_message_index = &$this->icache['folder_index'];
-        else
-            $a_message_index = $this->conn->fetchHeaderIndex($mailbox, "1:*", 'UID', $this->skip_deleted);
-
-        if ($a_message_index === false || $cache_index === null)
-            return;
-
-        // compare cache index with real index
-        foreach ($a_message_index as $id => $uid) {
-            // message in cache at correct position
-            if ($cache_index[$id] == $uid) {
-                unset($cache_index[$id]);
-                continue;
-            }
-
-            // other message at this position
-            if (isset($cache_index[$id])) {
-                $for_remove[] = $cache_index[$id];
-                unset($cache_index[$id]);
-            }
-
-            // message in cache but at wrong position
-            if (is_int($key = array_search($uid, $cache_index))) {
-                $for_remove[] = $uid;
-                unset($cache_index[$key]);
-            }
-
-            $for_update[] = $id;
-        }
-
-        // remove messages at wrong positions and those deleted that are still in cache_index
-        if (!empty($for_remove))
-            $cache_index = array_merge($cache_index, $for_remove);
-
-        if (!empty($cache_index))
-            $this->remove_message_cache($cache_key, $cache_index);
-
-        // fetch complete headers and add to cache
-        if (!empty($for_update)) {
-            // syncing a big folder maybe slow
-            @set_time_limit(0);
-            // To save memory do this in chunks
-            $for_update = array_chunk($for_update, $chunk_size);
-            foreach ($for_update as $uids) {
-                if ($headers = $this->conn->fetchHeaders($mailbox,
-                    $uids, false, false, $this->get_fetch_headers())
-                ) {
-                    foreach ($headers as $header) {
-                        $this->add_message_cache($cache_key, $header->id, $header, NULL, true);
-                    }
-                }
-            }
-        }
-    }
-
-
     /**
      * Invoke search request to IMAP server
      *
@@ -1727,6 +1636,7 @@ class rcube_imap
      * @param  string  $sort_field Header field to sort by
      * @return array   search results as list of message IDs
      * @access public
+     * @todo: Search criteria should be provided in non-IMAP format, eg. array
      */
     function search($mailbox='', $str=NULL, $charset=NULL, $sort_field=NULL)
     {
@@ -1753,8 +1663,8 @@ class rcube_imap
      * @param string $criteria   Search criteria
      * @param string $charset    Charset
      * @param string $sort_field Sorting field
+     *
      * @return array   search results as list of message ids
-     * @access private
      * @see rcube_imap::search()
      */
     private function _search_index($mailbox, $criteria='ALL', $charset=NULL, $sort_field=NULL)
@@ -1776,9 +1686,9 @@ class rcube_imap
             if ($a_messages !== false) {
                 list ($thread_tree, $msg_depth, $has_children) = $a_messages;
                 $a_messages = array(
-                    'tree'     => $thread_tree,
-                       'depth' => $msg_depth,
-                       'children' => $has_children
+                    'tree' => $thread_tree,
+                    'depth'=> $msg_depth,
+                    'children' => $has_children
                 );
             }
 
@@ -1790,7 +1700,7 @@ class rcube_imap
             $a_messages = $this->conn->sort($mailbox, $sort_field, $criteria, false, $charset);
 
             // Error, try with US-ASCII (RFC5256: SORT/THREAD must support US-ASCII and UTF-8,
-            // but I've seen that Courier doesn't support UTF-8)
+            // but I've seen Courier with disabled UTF-8 support)
             if ($a_messages === false && $charset && $charset != 'US-ASCII')
                 $a_messages = $this->conn->sort($mailbox, $sort_field,
                     $this->convert_criteria($criteria, $charset), false, 'US-ASCII');
@@ -1802,7 +1712,7 @@ class rcube_imap
         }
 
         if ($orig_criteria == 'ALL') {
-            $max = $this->_messagecount($mailbox);
+            $max = $this->_messagecount($mailbox, 'ALL', true, false);
             $a_messages = $max ? range(1, $max) : array();
         }
         else {
@@ -1832,8 +1742,8 @@ class rcube_imap
      * @param  string  $mailbox Mailbox name to search in
      * @param  string  $str     Search string
      * @param  boolean $ret_uid True if UIDs should be returned
+     *
      * @return array   Search results as list of message IDs or UIDs
-     * @access public
      */
     function search_once($mailbox='', $str=NULL, $ret_uid=false)
     {
@@ -1866,9 +1776,9 @@ class rcube_imap
                 $string_offset = $m[1] + strlen($m[0]) + 4; // {}\r\n
                 $string = substr($str, $string_offset - 1, $m[0]);
                 $string = rcube_charset_convert($string, $charset, $dest_charset);
-                if (!$string)
+                if ($string === false)
                     continue;
-                $res .= sprintf("%s{%d}\r\n%s", substr($str, $last, $m[1] - $last - 1), strlen($string), $string);
+                $res .= substr($str, $last, $m[1] - $last - 1) . rcube_imap_generic::escape($string);
                 $last = $m[0] + $string_offset - 1;
             }
             if ($last < strlen($str))
@@ -1887,43 +1797,53 @@ class rcube_imap
      * @param string $mailbox     Mailbox name
      * @param  array $thread_tree Unsorted thread tree (rcube_imap_generic::thread() result)
      * @param  array $ids         Message IDs if we know what we need (e.g. search result)
+     *
      * @return array Sorted roots IDs
-     * @access private
      */
-    private function _sort_threads($mailbox, $thread_tree, $ids=NULL)
+    function sort_threads($mailbox, $thread_tree, $ids = null)
     {
-        // THREAD=ORDEREDSUBJECT:      sorting by sent date of root message
-        // THREAD=REFERENCES:  sorting by sent date of root message
-        // THREAD=REFS:                sorting by the most recent date in each thread
+        // THREAD=ORDEREDSUBJECT: sorting by sent date of root message
+        // THREAD=REFERENCES:     sorting by sent date of root message
+        // THREAD=REFS:           sorting by the most recent date in each thread
+
         // default sorting
         if (!$this->sort_field || ($this->sort_field == 'date' && $this->threading == 'REFS')) {
             return array_keys((array)$thread_tree);
-          }
-        // here we'll implement REFS sorting, for performance reason
-        else { // ($sort_field == 'date' && $this->threading != 'REFS')
+        }
+        // here we'll implement REFS sorting
+        else {
+            if ($mcache = $this->get_mcache_engine()) {
+                $a_index = $mcache->get_index($mailbox, $this->sort_field, 'ASC');
+                if (is_array($a_index)) {
+                    $a_index = array_keys($a_index);
+                    // now we must remove IDs that doesn't exist in $ids
+                    if (!empty($ids))
+                        $a_index = array_intersect($a_index, $ids);
+                }
+            }
             // use SORT command
-            if ($this->get_capability('SORT') && 
+            else if ($this->get_capability('SORT') &&
                 ($a_index = $this->conn->sort($mailbox, $this->sort_field,
-                       !empty($ids) ? $ids : ($this->skip_deleted ? 'UNDELETED' : ''))) !== false
+                    !empty($ids) ? $ids : ($this->skip_deleted ? 'UNDELETED' : ''))) !== false
             ) {
-                   // return unsorted tree if we've got no index data
-                   if (!$a_index)
-                       return array_keys((array)$thread_tree);
+                // do nothing
             }
             else {
                 // fetch specified headers for all messages and sort them
                 $a_index = $this->conn->fetchHeaderIndex($mailbox, !empty($ids) ? $ids : "1:*",
-                       $this->sort_field, $this->skip_deleted);
+                    $this->sort_field, $this->skip_deleted);
 
-                   // return unsorted tree if we've got no index data
-                   if (!$a_index)
-                       return array_keys((array)$thread_tree);
-
-                asort($a_index); // ASC
-                   $a_index = array_values($a_index);
+                // return unsorted tree if we've got no index data
+                if (!empty($a_index)) {
+                    asort($a_index); // ASC
+                    $a_index = array_values($a_index);
+                }
             }
 
-               return $this->_sort_thread_refs($thread_tree, $a_index);
+            if (empty($a_index))
+                return array_keys((array)$thread_tree);
+
+            return $this->_sort_thread_refs($thread_tree, $a_index);
         }
     }
 
@@ -1931,10 +1851,10 @@ class rcube_imap
     /**
      * THREAD=REFS sorting implementation
      *
-     * @param  array $tree  Thread tree array (message identifiers as keys)
-     * @param  array $index Array of sorted message identifiers
+     * @param  array $tree   Thread tree array (message identifiers as keys)
+     * @param  array $index  Array of sorted message identifiers
+     *
      * @return array   Array of sorted roots messages
-     * @access private
      */
     private function _sort_thread_refs($tree, $index)
     {
@@ -1983,7 +1903,7 @@ class rcube_imap
     {
         if (!empty($this->search_string))
             $this->search_set = $this->search('', $this->search_string, $this->search_charset,
-               $this->search_sort_field, $this->search_threads, $this->search_sorted);
+                $this->search_sort_field, $this->search_threads, $this->search_sorted);
 
         return $this->get_search_set();
     }
@@ -2011,32 +1931,25 @@ class rcube_imap
     /**
      * Return message headers object of a specific message
      *
-     * @param int     $id       Message ID
+     * @param int     $id       Message sequence ID or UID
      * @param string  $mailbox  Mailbox to read from
-     * @param boolean $is_uid   True if $id is the message UID
-     * @param boolean $bodystr  True if we need also BODYSTRUCTURE in headers
-     * @return object Message headers representation
+     * @param bool    $force    True to skip cache
+     *
+     * @return rcube_mail_header Message headers
      */
-    function get_headers($id, $mailbox=null, $is_uid=true, $bodystr=false)
+    function get_headers($uid, $mailbox = null, $force = false)
     {
         if (!strlen($mailbox)) {
             $mailbox = $this->mailbox;
         }
-        $uid = $is_uid ? $id : $this->_id2uid($id, $mailbox);
 
         // get cached headers
-        if ($uid && ($headers = &$this->get_cached_message($mailbox.'.msg', $uid)))
-            return $headers;
-
-        $headers = $this->conn->fetchHeader(
-            $mailbox, $id, $is_uid, $bodystr, $this->get_fetch_headers());
-
-        // write headers cache
-        if ($headers) {
-            if ($headers->uid && $headers->id)
-                $this->uid_id_map[$mailbox][$headers->uid] = $headers->id;
-
-            $this->add_message_cache($mailbox.'.msg', $headers->id, $headers, NULL, false, true);
+        if (!$force && $uid && ($mcache = $this->get_mcache_engine())) {
+            $headers = $mcache->get_message($mailbox, $uid);
+        }
+        else {
+            $headers = $this->conn->fetchHeader(
+                $mailbox, $uid, true, true, $this->get_fetch_headers());
         }
 
         return $headers;
@@ -2044,33 +1957,47 @@ class rcube_imap
 
 
     /**
-     * Fetch body structure from the IMAP server and build
+     * Fetch message headers and body structure from the IMAP server and build
      * an object structure similar to the one generated by PEAR::Mail_mimeDecode
      *
-     * @param int    $uid           Message UID to fetch
-     * @param string $structure_str Message BODYSTRUCTURE string (optional)
-     * @return object rcube_message_part Message part tree or False on failure
+     * @param int     $uid      Message UID to fetch
+     * @param string  $mailbox  Mailbox to read from
+     *
+     * @return object rcube_mail_header Message data
      */
-    function &get_structure($uid, $structure_str='')
+    function get_message($uid, $mailbox = null)
     {
-        $cache_key = $this->mailbox.'.msg';
-        $headers = &$this->get_cached_message($cache_key, $uid);
+        if (!strlen($mailbox)) {
+            $mailbox = $this->mailbox;
+        }
 
-        // return cached message structure
-        if (is_object($headers) && is_object($headers->structure)) {
-            return $headers->structure;
+        // Check internal cache
+        if (!empty($this->icache['message'])) {
+            if (($headers = $this->icache['message']) && $headers->uid == $uid) {
+                return $headers;
+            }
         }
 
-        if (!$structure_str) {
-            $structure_str = $this->conn->fetchStructureString($this->mailbox, $uid, true);
+        $headers = $this->get_headers($uid, $mailbox);
+
+        // message doesn't exist?
+        if (empty($headers))
+            return null; 
+
+        // structure might be cached
+        if (!empty($headers->structure))
+            return $headers;
+
+        $this->_msg_uid = $uid;
+
+        if (empty($headers->bodystructure)) {
+            $headers->bodystructure = $this->conn->getStructure($mailbox, $uid, true);
         }
-        $structure = rcube_mime_struct::parseStructure($structure_str);
-        $struct = false;
 
-        // parse structure and add headers
-        if (!empty($structure)) {
-            $headers = $this->get_headers($uid);
-            $this->_msg_id = $headers->id;
+        $structure = $headers->bodystructure;
+
+        if (empty($structure))
+            return $headers;
 
         // set message charset from message headers
         if ($headers->charset)
@@ -2093,26 +2020,21 @@ class rcube_imap
                 $structure[1] = $m[2];
             }
             else
-                return false;
+                return $headers;
         }
 
         $struct = &$this->_structure_part($structure, 0, '', $headers);
-        $struct->headers = get_object_vars($headers);
 
         // don't trust given content-type
-        if (empty($struct->parts) && !empty($struct->headers['ctype'])) {
+        if (empty($struct->parts) && !empty($headers->ctype)) {
             $struct->mime_id = '1';
-            $struct->mimetype = strtolower($struct->headers['ctype']);
+            $struct->mimetype = strtolower($headers->ctype);
             list($struct->ctype_primary, $struct->ctype_secondary) = explode('/', $struct->mimetype);
         }
 
-        // write structure to cache
-        if ($this->messages_caching)
-            $this->add_message_cache($cache_key, $this->_msg_id, $headers, $struct,
-                $this->icache['message.id'][$uid], true);
-        }
+        $headers->structure = $struct;
 
-        return $struct;
+        return $this->icache['message'] = $headers;
     }
 
 
@@ -2177,7 +2099,7 @@ class rcube_imap
             // headers for parts on all levels
             if ($mime_part_headers) {
                 $mime_part_headers = $this->conn->fetchMIMEHeaders($this->mailbox,
-                    $this->_msg_id, $mime_part_headers);
+                    $this->_msg_uid, $mime_part_headers);
             }
 
             $struct->parts = array();
@@ -2279,7 +2201,7 @@ class rcube_imap
         if ($struct->ctype_primary == 'message' || ($struct->ctype_parameters['name'] && !$struct->content_id)) {
             if (empty($mime_headers)) {
                 $mime_headers = $this->conn->fetchPartHeader(
-                    $this->mailbox, $this->_msg_id, false, $struct->mime_id);
+                    $this->mailbox, $this->_msg_uid, true, $struct->mime_id);
             }
 
             if (is_string($mime_headers))
@@ -2342,7 +2264,7 @@ class rcube_imap
             if ($i<2) {
                 if (!$headers) {
                     $headers = $this->conn->fetchPartHeader(
-                        $this->mailbox, $this->_msg_id, false, $part->mime_id);
+                        $this->mailbox, $this->_msg_uid, true, $part->mime_id);
                 }
                 $filename_mime = '';
                 $i = 0;
@@ -2361,7 +2283,7 @@ class rcube_imap
             if ($i<2) {
                 if (!$headers) {
                     $headers = $this->conn->fetchPartHeader(
-                            $this->mailbox, $this->_msg_id, false, $part->mime_id);
+                            $this->mailbox, $this->_msg_uid, true, $part->mime_id);
                 }
                 $filename_encoded = '';
                 $i = 0; $matches = array();
@@ -2380,7 +2302,7 @@ class rcube_imap
             if ($i<2) {
                 if (!$headers) {
                     $headers = $this->conn->fetchPartHeader(
-                        $this->mailbox, $this->_msg_id, false, $part->mime_id);
+                        $this->mailbox, $this->_msg_uid, true, $part->mime_id);
                 }
                 $filename_mime = '';
                 $i = 0; $matches = array();
@@ -2399,7 +2321,7 @@ class rcube_imap
             if ($i<2) {
                 if (!$headers) {
                     $headers = $this->conn->fetchPartHeader(
-                        $this->mailbox, $this->_msg_id, false, $part->mime_id);
+                        $this->mailbox, $this->_msg_uid, true, $part->mime_id);
                 }
                 $filename_encoded = '';
                 $i = 0; $matches = array();
@@ -2420,9 +2342,14 @@ class rcube_imap
 
         // decode filename
         if (!empty($filename_mime)) {
-            $part->filename = rcube_imap::decode_mime_string($filename_mime,
-                $part->charset ? $part->charset : ($this->struct_charset ? $this->struct_charset :
-                rc_detect_encoding($filename_mime, $this->default_charset)));
+            if (!empty($part->charset))
+                $charset = $part->charset;
+            else if (!empty($this->struct_charset))
+                $charset = $this->struct_charset;
+            else
+                $charset = rc_detect_encoding($filename_mime, $this->default_charset);
+
+            $part->filename = rcube_imap::decode_mime_string($filename_mime, $charset);
         }
         else if (!empty($filename_encoded)) {
             // decode filename according to RFC 2231, Section 4
@@ -2430,6 +2357,7 @@ class rcube_imap
                 $filename_charset = $fmatches[1];
                 $filename_encoded = $fmatches[2];
             }
+
             $part->filename = rcube_charset_convert(urldecode($filename_encoded), $filename_charset);
         }
     }
@@ -2466,30 +2394,23 @@ class rcube_imap
      */
     function &get_message_part($uid, $part=1, $o_part=NULL, $print=NULL, $fp=NULL, $skip_charset_conv=false)
     {
-        // get part encoding if not provided
+        // get part data if not provided
         if (!is_object($o_part)) {
-            $structure_str = $this->conn->fetchStructureString($this->mailbox, $uid, true);
-            $structure = new rcube_mime_struct();
-            // error or message not found
-            if (!$structure->loadStructure($structure_str)) {
-                return false;
-            }
+            $structure = $this->conn->getStructure($this->mailbox, $uid, true);
+            $part_data = rcube_imap_generic::getStructurePartData($structure, $part);
 
             $o_part = new rcube_message_part;
-            $o_part->ctype_primary = strtolower($structure->getPartType($part));
-            $o_part->encoding      = strtolower($structure->getPartEncoding($part));
-            $o_part->charset       = $structure->getPartCharset($part);
+            $o_part->ctype_primary = $part_data['type'];
+            $o_part->encoding      = $part_data['encoding'];
+            $o_part->charset       = $part_data['charset'];
+            $o_part->size          = $part_data['size'];
         }
 
-        // TODO: Add caching for message parts
-
-        if (!$part) {
-            $part = 'TEXT';
+        if ($o_part && $o_part->size) {
+            $body = $this->conn->handlePartBody($this->mailbox, $uid, true,
+                $part ? $part : 'TEXT', $o_part->encoding, $print, $fp);
         }
 
-        $body = $this->conn->handlePartBody($this->mailbox, $uid, true, $part,
-            $o_part->encoding, $print, $fp);
-
         if ($fp || $print) {
             return true;
         }
@@ -2501,7 +2422,11 @@ class rcube_imap
 
            if (!$skip_charset_conv) {
                 if (!$o_part->charset || strtoupper($o_part->charset) == 'US-ASCII') {
-                    $o_part->charset = $this->default_charset;
+                    // try to extract charset information from HTML meta tag (#1488125)
+                    if ($o_part->ctype_secondary == 'html' && preg_match('/<meta[^>]+charset=([a-z0-9-_]+)/i', $body, $m))
+                        $o_part->charset = strtoupper($m[1]);
+                    else
+                        $o_part->charset = $this->default_charset;
                 }
                 $body = rcube_charset_convert($body, $o_part->charset);
             }
@@ -2590,12 +2515,12 @@ class rcube_imap
 
         if ($result) {
             // reload message headers if cached
-            if ($this->messages_caching && !$skip_cache) {
-                $cache_key = $mailbox.'.msg';
-                if ($all_mode)
-                    $this->clear_message_cache($cache_key);
-                else
-                    $this->remove_message_cache($cache_key, explode(',', $uids));
+            // @TODO: update flags instead removing from cache
+            if (!$skip_cache && ($mcache = $this->get_mcache_engine())) {
+                $status = strpos($flag, 'UN') !== 0;
+                $mflag  = preg_replace('/^UN/', '', $flag);
+                $mcache->change_flag($mailbox, $all_mode ? null : explode(',', $uids),
+                    $mflag, $status);
             }
 
             // clear cached counters
@@ -2636,7 +2561,7 @@ class rcube_imap
      * @param string  $headers Headers string if $message contains only the body
      * @param boolean $is_file True if $message is a filename
      *
-     * @return boolean True on success, False on error
+     * @return int|bool Appended message UID or True on success, False on error
      */
     function save_message($mailbox, &$message, $headers='', $is_file=false)
     {
@@ -2687,10 +2612,14 @@ class rcube_imap
 
         // make sure mailbox exists
         if ($to_mbox != 'INBOX' && !$this->mailbox_exists($to_mbox)) {
-            if (in_array($to_mbox, $this->default_folders))
-                $this->create_mailbox($to_mbox, true);
-            else
+            if (in_array($to_mbox, $this->default_folders)) {
+                if (!$this->create_mailbox($to_mbox, true)) {
+                    return false;
+                }
+            }
+            else {
                 return false;
+            }
         }
 
         $config = rcmail::get_instance()->config;
@@ -2727,19 +2656,18 @@ class rcube_imap
                 if ($this->search_threads || $all_mode)
                     $this->refresh_search();
                 else {
-                    $uids = explode(',', $uids);
-                    foreach ($uids as $uid)
-                        $a_mids[] = $this->_uid2id($uid, $from_mbox);
+                    $a_uids = explode(',', $uids);
+                    foreach ($a_uids as $uid)
+                        $a_mids[] = $this->uid2id($uid, $from_mbox);
                     $this->search_set = array_diff($this->search_set, $a_mids);
                 }
+                unset($a_mids);
+                unset($a_uids);
             }
 
-            // update cached message headers
-            $cache_key = $from_mbox.'.msg';
-            if ($all_mode || ($start_index = $this->get_message_cache_index_min($cache_key, $uids))) {
-                // clear cache from the lowest index on
-                $this->clear_message_cache($cache_key, $all_mode ? 1 : $start_index);
-            }
+            // remove cached messages
+            // @TODO: do cache update instead of clearing it
+            $this->clear_message_cache($from_mbox, $all_mode ? null : explode(',', $uids));
         }
 
         return $moved;
@@ -2769,10 +2697,14 @@ class rcube_imap
 
         // make sure mailbox exists
         if ($to_mbox != 'INBOX' && !$this->mailbox_exists($to_mbox)) {
-            if (in_array($to_mbox, $this->default_folders))
-                $this->create_mailbox($to_mbox, true);
-            else
+            if (in_array($to_mbox, $this->default_folders)) {
+                if (!$this->create_mailbox($to_mbox, true)) {
+                    return false;
+                }
+            }
+            else {
                 return false;
+            }
         }
 
         // copy messages
@@ -2824,19 +2756,17 @@ class rcube_imap
                 if ($this->search_threads || $all_mode)
                     $this->refresh_search();
                 else {
-                    $uids = explode(',', $uids);
-                    foreach ($uids as $uid)
-                        $a_mids[] = $this->_uid2id($uid, $mailbox);
+                    $a_uids = explode(',', $uids);
+                    foreach ($a_uids as $uid)
+                        $a_mids[] = $this->uid2id($uid, $mailbox);
                     $this->search_set = array_diff($this->search_set, $a_mids);
+                    unset($a_uids);
+                    unset($a_mids);
                 }
             }
 
-            // remove deleted messages from cache
-            $cache_key = $mailbox.'.msg';
-            if ($all_mode || ($start_index = $this->get_message_cache_index_min($cache_key, $uids))) {
-                // clear cache from the lowest index on
-                $this->clear_message_cache($cache_key, $all_mode ? 1 : $start_index);
-            }
+            // remove cached messages
+            $this->clear_message_cache($mailbox, $all_mode ? null : explode(',', $uids));
         }
 
         return $deleted;
@@ -2861,9 +2791,9 @@ class rcube_imap
             $cleared = $this->conn->clearFolder($mailbox);
         }
 
-        // make sure the message count cache is cleared as well
+        // make sure the cache is cleared as well
         if ($cleared) {
-            $this->clear_message_cache($mailbox.'.msg');
+            $this->clear_message_cache($mailbox);
             $a_mailbox_cache = $this->get_cache('messagecount');
             unset($a_mailbox_cache[$mailbox]);
             $this->update_cache('messagecount', $a_mailbox_cache);
@@ -2904,9 +2834,9 @@ class rcube_imap
     private function _expunge($mailbox, $clear_cache=true, $uids=NULL)
     {
         if ($uids && $this->get_capability('UIDPLUS'))
-            $a_uids = is_array($uids) ? join(',', $uids) : $uids;
+            list($uids, $all_mode) = $this->_parse_uids($uids, $mailbox);
         else
-            $a_uids = NULL;
+            $uids = null;
 
         // force mailbox selection and check if mailbox is writeable
         // to prevent a situation when CLOSE is executed on closed
@@ -2921,13 +2851,13 @@ class rcube_imap
         }
 
         // CLOSE(+SELECT) should be faster than EXPUNGE
-        if (empty($a_uids) || $a_uids == '1:*')
+        if (empty($uids) || $all_mode)
             $result = $this->conn->close();
         else
-            $result = $this->conn->expunge($mailbox, $a_uids);
+            $result = $this->conn->expunge($mailbox, $uids);
 
         if ($result && $clear_cache) {
-            $this->clear_message_cache($mailbox.'.msg');
+            $this->clear_message_cache($mailbox, $all_mode ? null : explode(',', $uids));
             $this->_clear_messagecount($mailbox);
         }
 
@@ -2992,7 +2922,7 @@ class rcube_imap
             $mailbox = $this->mailbox;
         }
 
-        return $this->_uid2id($uid, $mailbox);
+        return $this->uid2id($uid, $mailbox);
     }
 
 
@@ -3010,7 +2940,7 @@ class rcube_imap
             $mailbox = $this->mailbox;
         }
 
-        return $this->_id2uid($id, $mailbox);
+        return $this->id2uid($id, $mailbox);
     }
 
 
@@ -3022,55 +2952,72 @@ class rcube_imap
     /**
      * Public method for listing subscribed folders
      *
-     * @param   string  $root   Optional root folder
-     * @param   string  $name   Optional name pattern
-     * @param   string  $filter Optional filter
+     * @param   string  $root      Optional root folder
+     * @param   string  $name      Optional name pattern
+     * @param   string  $filter    Optional filter
+     * @param   string  $rights    Optional ACL requirements
+     * @param   bool    $skip_sort Enable to return unsorted list (for better performance)
      *
-     * @return  array   List of mailboxes/folders
+     * @return  array   List of folders
      * @access  public
      */
-    function list_mailboxes($root='', $name='*', $filter=null)
+    function list_mailboxes($root='', $name='*', $filter=null, $rights=null, $skip_sort=false)
     {
-        $a_mboxes = $this->_list_mailboxes($root, $name, $filter);
+        $cache_key = $root.':'.$name;
+        if (!empty($filter)) {
+            $cache_key .= ':'.(is_string($filter) ? $filter : serialize($filter));
+        }
+        $cache_key .= ':'.$rights;
+        $cache_key = 'mailboxes.'.md5($cache_key);
+
+        // get cached folder list
+        $a_mboxes = $this->get_cache($cache_key);
+        if (is_array($a_mboxes)) {
+            return $a_mboxes;
+        }
+
+        $a_mboxes = $this->_list_mailboxes($root, $name, $filter, $rights);
+
+        if (!is_array($a_mboxes)) {
+            return array();
+        }
+
+        // filter folders list according to rights requirements
+        if ($rights && $this->get_capability('ACL')) {
+            $a_mboxes = $this->filter_rights($a_mboxes, $rights);
+        }
 
         // INBOX should always be available
         if ((!$filter || $filter == 'mail') && !in_array('INBOX', $a_mboxes)) {
             array_unshift($a_mboxes, 'INBOX');
         }
 
-        // sort mailboxes
-        $a_mboxes = $this->_sort_mailbox_list($a_mboxes);
+        // sort mailboxes (always sort for cache)
+        if (!$skip_sort || $this->cache) {
+            $a_mboxes = $this->_sort_mailbox_list($a_mboxes);
+        }
+
+        // write mailboxlist to cache
+        $this->update_cache($cache_key, $a_mboxes);
 
         return $a_mboxes;
     }
 
 
     /**
-     * Private method for mailbox listing
+     * Private method for mailbox listing (LSUB)
      *
      * @param   string  $root   Optional root folder
      * @param   string  $name   Optional name pattern
      * @param   mixed   $filter Optional filter
+     * @param   string  $rights Optional ACL requirements
      *
-     * @return  array   List of mailboxes/folders
+     * @return  array   List of subscribed folders
      * @see     rcube_imap::list_mailboxes()
      * @access  private
      */
-    private function _list_mailboxes($root='', $name='*', $filter=null)
+    private function _list_mailboxes($root='', $name='*', $filter=null, $rights=null)
     {
-        $cache_key = $root.':'.$name;
-        if (!empty($filter)) {
-            $cache_key .= ':'.(is_string($filter) ? $filter : serialize($filter));
-        }
-
-        $cache_key = 'mailboxes.'.md5($cache_key);
-
-        // get cached folder list
-        $a_mboxes = $this->get_cache($cache_key);
-        if (is_array($a_mboxes)) {
-            return $a_mboxes;
-        }
-
         $a_defaults = $a_out = array();
 
         // Give plugins a chance to provide a list of mailboxes
@@ -3081,7 +3028,7 @@ class rcube_imap
             $a_folders = $data['folders'];
         }
         else if (!$this->conn->connected()) {
-           return array();
+           return null;
         }
         else {
             // Server supports LIST-EXTENDED, we can use selection options
@@ -3129,9 +3076,6 @@ class rcube_imap
             $a_folders = array();
         }
 
-        // write mailboxlist to cache
-        $this->update_cache($cache_key, $a_folders);
-
         return $a_folders;
     }
 
@@ -3139,15 +3083,29 @@ class rcube_imap
     /**
      * Get a list of all folders available on the IMAP server
      *
-     * @param string $root   IMAP root dir
-     * @param string  $name   Optional name pattern
-     * @param mixed   $filter Optional filter
+     * @param string  $root      IMAP root dir
+     * @param string  $name      Optional name pattern
+     * @param mixed   $filter    Optional filter
+     * @param string  $rights    Optional ACL requirements
+     * @param bool    $skip_sort Enable to return unsorted list (for better performance)
      *
      * @return array Indexed array with folder names
      */
-    function list_unsubscribed($root='', $name='*', $filter=null)
+    function list_unsubscribed($root='', $name='*', $filter=null, $rights=null, $skip_sort=false)
     {
-        // @TODO: caching
+        $cache_key = $root.':'.$name;
+        if (!empty($filter)) {
+            $cache_key .= ':'.(is_string($filter) ? $filter : serialize($filter));
+        }
+        $cache_key .= ':'.$rights;
+        $cache_key = 'mailboxes.list.'.md5($cache_key);
+
+        // get cached folder list
+        $a_mboxes = $this->get_cache($cache_key);
+        if (is_array($a_mboxes)) {
+            return $a_mboxes;
+        }
+
         // Give plugins a chance to provide a list of mailboxes
         $data = rcmail::get_instance()->plugins->exec_hook('mailboxes_list',
             array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LIST'));
@@ -3157,7 +3115,7 @@ class rcube_imap
         }
         else {
             // retrieve list of folders from IMAP server
-            $a_mboxes = $this->conn->listMailboxes($root, $name);
+            $a_mboxes = $this->_list_unsubscribed($root, $name);
         }
 
         if (!is_array($a_mboxes)) {
@@ -3169,13 +3127,108 @@ class rcube_imap
             array_unshift($a_mboxes, 'INBOX');
         }
 
+        // cache folder attributes
+        if ($root == '' && $name == '*' && empty($filter)) {
+            $this->update_cache('mailboxes.attributes', $this->conn->data['LIST']);
+        }
+
+        // filter folders list according to rights requirements
+        if ($rights && $this->get_capability('ACL')) {
+            $a_folders = $this->filter_rights($a_folders, $rights);
+        }
+
         // filter folders and sort them
-        $a_mboxes = $this->_sort_mailbox_list($a_mboxes);
+        if (!$skip_sort) {
+            $a_mboxes = $this->_sort_mailbox_list($a_mboxes);
+        }
+
+        // write mailboxlist to cache
+        $this->update_cache($cache_key, $a_mboxes);
 
         return $a_mboxes;
     }
 
 
+    /**
+     * Private method for mailbox listing (LIST)
+     *
+     * @param   string  $root   Optional root folder
+     * @param   string  $name   Optional name pattern
+     *
+     * @return  array   List of folders
+     * @see     rcube_imap::list_unsubscribed()
+     */
+    private function _list_unsubscribed($root='', $name='*')
+    {
+        $result = $this->conn->listMailboxes($root, $name);
+
+        if (!is_array($result)) {
+            return array();
+        }
+
+        // #1486796: some server configurations doesn't
+        // return folders in all namespaces, we'll try to detect that situation
+        // and ask for these namespaces separately
+        if ($root == '' && $name == '*') {
+            $delim     = $this->get_hierarchy_delimiter();
+            $namespace = $this->get_namespace();
+            $search    = array();
+
+            // build list of namespace prefixes
+            foreach ((array)$namespace as $ns) {
+                if (is_array($ns)) {
+                    foreach ($ns as $ns_data) {
+                        if (strlen($ns_data[0])) {
+                            $search[] = $ns_data[0];
+                        }
+                    }
+                }
+            }
+
+            if (!empty($search)) {
+                // go through all folders detecting namespace usage
+                foreach ($result as $folder) {
+                    foreach ($search as $idx => $prefix) {
+                        if (strpos($folder, $prefix) === 0) {
+                            unset($search[$idx]);
+                        }
+                    }
+                    if (empty($search)) {
+                        break;
+                    }
+                }
+
+                // get folders in hidden namespaces and add to the result
+                foreach ($search as $prefix) {
+                    $list = $this->conn->listMailboxes($prefix, $name);
+
+                    if (!empty($list)) {
+                        $result = array_merge($result, $list);
+                    }
+                }
+            }
+        }
+
+        return $result;
+    }
+
+
+    /**
+     * Filter the given list of folders according to access rights
+     */
+    private function filter_rights($a_folders, $rights)
+    {
+        $regex = '/('.$rights.')/';
+        foreach ($a_folders as $idx => $folder) {
+            $myrights = join('', (array)$this->my_rights($folder));
+            if ($myrights !== null && !preg_match($regex, $myrights))
+                unset($a_folders[$idx]);
+        }
+
+        return $a_folders;
+    }
+
+
     /**
      * Get mailbox quota information
      * added by Nuny
@@ -3308,11 +3361,14 @@ class rcube_imap
                     $this->conn->unsubscribe($c_subscribed);
                     $this->conn->subscribe(preg_replace('/^'.preg_quote($mailbox, '/').'/',
                         $new_name, $c_subscribed));
+
+                    // clear cache
+                    $this->clear_message_cache($c_subscribed);
                 }
             }
 
             // clear cache
-            $this->clear_message_cache($mailbox.'.msg');
+            $this->clear_message_cache($mailbox);
             $this->clear_cache('mailboxes', true);
         }
 
@@ -3348,13 +3404,13 @@ class rcube_imap
                 if (preg_match('/^'.preg_quote($mailbox.$delm, '/').'/', $c_mbox)) {
                     $this->conn->unsubscribe($c_mbox);
                     if ($this->conn->deleteFolder($c_mbox)) {
-                           $this->clear_message_cache($c_mbox.'.msg');
+                           $this->clear_message_cache($c_mbox);
                     }
                 }
             }
 
             // clear mailbox-related cache
-            $this->clear_message_cache($mailbox.'.msg');
+            $this->clear_message_cache($mailbox);
             $this->clear_cache('mailboxes', true);
         }
 
@@ -3429,8 +3485,8 @@ class rcube_imap
         foreach ($this->namespace as $type => $namespace) {
             if (is_array($namespace)) {
                 foreach ($namespace as $ns) {
-                    if (strlen($ns[0])) {
-                        if ((strlen($ns[0])>1 && $mailbox == substr($ns[0], 0, -1))
+                    if ($len = strlen($ns[0])) {
+                        if (($len > 1 && $mailbox == substr($ns[0], 0, -1))
                             || strpos($mailbox, $ns[0]) === 0
                         ) {
                             return $type;
@@ -3484,35 +3540,66 @@ class rcube_imap
 
 
     /**
-     * Gets folder options from LIST response, e.g. \Noselect, \Noinferiors
+     * Gets folder attributes from LIST response, e.g. \Noselect, \Noinferiors
      *
      * @param string $mailbox Folder name
-     * @param bool   $force   Set to True if options should be refreshed
-     *                        Options are available after LIST command only
+     * @param bool   $force   Set to True if attributes should be refreshed
      *
      * @return array Options list
      */
-    function mailbox_options($mailbox, $force=false)
+    function mailbox_attributes($mailbox, $force=false)
     {
-        if ($mailbox == 'INBOX') {
-            return array();
+        // get attributes directly from LIST command
+        if (!empty($this->conn->data['LIST']) && is_array($this->conn->data['LIST'][$mailbox])) {
+            $opts = $this->conn->data['LIST'][$mailbox];
         }
-
-        if (!is_array($this->conn->data['LIST']) || !is_array($this->conn->data['LIST'][$mailbox])) {
-            if ($force) {
-                $this->conn->listMailboxes('', $mailbox);
-            }
-            else {
-                return array();
-            }
+        // get cached folder attributes
+        else if (!$force) {
+            $opts = $this->get_cache('mailboxes.attributes');
+            $opts = $opts[$mailbox];
         }
 
-        $opts = $this->conn->data['LIST'][$mailbox];
+        if (!is_array($opts)) {
+            $this->conn->listMailboxes('', $mailbox);
+            $opts = $this->conn->data['LIST'][$mailbox];
+        }
 
         return is_array($opts) ? $opts : array();
     }
 
 
+    /**
+     * Gets connection (and current mailbox) data: UIDVALIDITY, EXISTS, RECENT,
+     * PERMANENTFLAGS, UIDNEXT, UNSEEN
+     *
+     * @param string $mailbox Folder name
+     *
+     * @return array Data
+     */
+    function mailbox_data($mailbox)
+    {
+        if (!strlen($mailbox))
+            $mailbox = $this->mailbox !== null ? $this->mailbox : 'INBOX';
+
+        if ($this->conn->selected != $mailbox) {
+            if ($this->conn->select($mailbox))
+                $this->mailbox = $mailbox;
+            else
+                return null;
+        }
+
+        $data = $this->conn->data;
+
+        // add (E)SEARCH result for ALL UNDELETED query
+        if (!empty($this->icache['undeleted_idx']) && $this->icache['undeleted_idx'][0] == $mailbox) {
+            $data['ALL_UNDELETED']   = $this->icache['undeleted_idx'][1];
+            $data['COUNT_UNDELETED'] = $this->icache['undeleted_idx'][2];
+        }
+
+        return $data;
+    }
+
+
     /**
      * Returns extended information about the folder
      *
@@ -3558,17 +3645,17 @@ class rcube_imap
             }
         }
 
-        $options['name']      = $mailbox;
-        $options['options']   = $this->mailbox_options($mailbox, true);
-        $options['namespace'] = $this->mailbox_namespace($mailbox);
-        $options['rights']    = $acl && !$options['is_root'] ? (array)$this->my_rights($mailbox) : array();
-        $options['special']   = in_array($mailbox, $this->default_folders);
+        $options['name']       = $mailbox;
+        $options['attributes'] = $this->mailbox_attributes($mailbox, true);
+        $options['namespace']  = $this->mailbox_namespace($mailbox);
+        $options['rights']     = $acl && !$options['is_root'] ? (array)$this->my_rights($mailbox) : array();
+        $options['special']    = in_array($mailbox, $this->default_folders);
 
         // Set 'noselect' and 'norename' flags
-        if (is_array($options['options'])) {
-            foreach ($options['options'] as $opt) {
-                $opt = strtolower($opt);
-                if ($opt == '\noselect' || $opt == '\nonexistent') {
+        if (is_array($options['attributes'])) {
+            foreach ($options['attributes'] as $attrib) {
+                $attrib = strtolower($attrib);
+                if ($attrib == '\noselect' || $attrib == '\nonexistent') {
                     $options['noselect'] = true;
                 }
             }
@@ -3594,6 +3681,19 @@ class rcube_imap
     }
 
 
+    /**
+     * Synchronizes messages cache.
+     *
+     * @param string $mailbox Folder name
+     */
+    public function mailbox_sync($mailbox)
+    {
+        if ($mcache = $this->get_mcache_engine()) {
+            $mcache->synchronize($mailbox);
+        }
+    }
+
+
     /**
      * Get message header names for rcube_imap_generic::fetchHeader(s)
      *
@@ -3855,7 +3955,7 @@ class rcube_imap
         else {
             if ($this->cache)
                 $this->cache->close();
-            $this->cache = null;
+            $this->cache   = null;
             $this->caching = false;
         }
     }
@@ -3925,419 +4025,50 @@ class rcube_imap
      * Enable or disable messages caching
      *
      * @param boolean $set Flag
-     * @access public
      */
     function set_messages_caching($set)
     {
-        $rcmail = rcmail::get_instance();
-
-        if ($set && ($dbh = $rcmail->get_dbh())) {
-            $this->db = $dbh;
+        if ($set) {
             $this->messages_caching = true;
         }
         else {
+            if ($this->mcache)
+                $this->mcache->close();
+            $this->mcache = null;
             $this->messages_caching = false;
         }
     }
 
     /**
-     * Checks if the cache is up-to-date
-     *
-     * @param string $mailbox   Mailbox name
-     * @param string $cache_key Internal cache key
-     * @return int   Cache status: -3 = off, -2 = incomplete, -1 = dirty, 1 = OK
-     */
-    private function check_cache_status($mailbox, $cache_key)
-    {
-        if (!$this->messages_caching)
-            return -3;
-
-        $cache_index = $this->get_message_cache_index($cache_key);
-        $msg_count = $this->_messagecount($mailbox);
-        $cache_count = count($cache_index);
-
-        // empty mailbox
-        if (!$msg_count) {
-            return $cache_count ? -2 : 1;
-        }
-
-        if ($cache_count == $msg_count) {
-            if ($this->skip_deleted) {
-                if (!empty($this->icache['all_undeleted_idx'])) {
-                    $uids = rcube_imap_generic::uncompressMessageSet($this->icache['all_undeleted_idx']);
-                    $uids = array_flip($uids);
-                    foreach ($cache_index as $uid) {
-                        unset($uids[$uid]);
-                    }
-                }
-                else {
-                    // get all undeleted messages excluding cached UIDs
-                    $uids = $this->search_once($mailbox, 'ALL UNDELETED NOT UID '.
-                        rcube_imap_generic::compressMessageSet($cache_index));
-                }
-                if (empty($uids)) {
-                    return 1;
-                }
-            } else {
-                // get UID of the message with highest index
-                $uid = $this->_id2uid($msg_count, $mailbox);
-                $cache_uid = array_pop($cache_index);
-
-                // uids of highest message matches -> cache seems OK
-                if ($cache_uid == $uid) {
-                    return 1;
-                }
-            }
-            // cache is dirty
-            return -1;
-        }
-
-        // if cache count differs less than 10% report as dirty
-        return (abs($msg_count - $cache_count) < $msg_count/10) ? -1 : -2;
-    }
-
-
-    /**
-     * @param string $key Cache key
-     * @param string $from
-     * @param string $to
-     * @param string $sort_field
-     * @param string $sort_order
-     * @access private
+     * Getter for messages cache object
      */
-    private function get_message_cache($key, $from, $to, $sort_field, $sort_order)
+    private function get_mcache_engine()
     {
-        if (!$this->messages_caching)
-            return NULL;
-
-        // use idx sort as default sorting
-        if (!$sort_field || !in_array($sort_field, $this->db_header_fields)) {
-            $sort_field = 'idx';
-        }
-
-        $result = array();
-
-        $sql_result = $this->db->limitquery(
-                "SELECT idx, uid, headers".
-                " FROM ".get_table_name('messages').
-                " WHERE user_id=?".
-                " AND cache_key=?".
-                " ORDER BY ".$this->db->quoteIdentifier($sort_field)." ".strtoupper($sort_order),
-                $from,
-                $to - $from,
-                $_SESSION['user_id'],
-                $key);
-
-        while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
-            $uid = intval($sql_arr['uid']);
-            $result[$uid] = $this->db->decode(unserialize($sql_arr['headers']));
-
-            // featch headers if unserialize failed
-            if (empty($result[$uid]))
-                $result[$uid] = $this->conn->fetchHeader(
-                    preg_replace('/.msg$/', '', $key), $uid, true, false, $this->get_fetch_headers());
-        }
-
-        return $result;
-    }
-
-
-    /**
-     * @param string $key Cache key
-     * @param int    $uid Message UID
-     * @return mixed
-     * @access private
-     */
-    private function &get_cached_message($key, $uid)
-    {
-        $internal_key = 'message';
-
-        if ($this->messages_caching && !isset($this->icache[$internal_key][$uid])) {
-            $sql_result = $this->db->query(
-                "SELECT idx, headers, structure, message_id".
-                " FROM ".get_table_name('messages').
-                " WHERE user_id=?".
-                " AND cache_key=?".
-                " AND uid=?",
-                $_SESSION['user_id'],
-                $key,
-                $uid);
-
-            if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
-                $this->icache['message.id'][$uid] = intval($sql_arr['message_id']);
-                   $this->uid_id_map[preg_replace('/\.msg$/', '', $key)][$uid] = intval($sql_arr['idx']);
-                $this->icache[$internal_key][$uid] = $this->db->decode(unserialize($sql_arr['headers']));
-
-                if (is_object($this->icache[$internal_key][$uid]) && !empty($sql_arr['structure']))
-                    $this->icache[$internal_key][$uid]->structure = $this->db->decode(unserialize($sql_arr['structure']));
+        if ($this->messages_caching && !$this->mcache) {
+            $rcmail = rcmail::get_instance();
+            if ($dbh = $rcmail->get_dbh()) {
+                $this->mcache = new rcube_imap_cache(
+                    $dbh, $this, $rcmail->user->ID, $this->skip_deleted);
             }
         }
 
-        return $this->icache[$internal_key][$uid];
-    }
-
-
-    /**
-     * @param string  $key        Cache key
-     * @param string  $sort_field Sorting column
-     * @param string  $sort_order Sorting order
-     * @return array Messages index
-     * @access private
-     */
-    private function get_message_cache_index($key, $sort_field='idx', $sort_order='ASC')
-    {
-        if (!$this->messages_caching || empty($key))
-            return NULL;
-
-        // use idx sort as default
-        if (!$sort_field || !in_array($sort_field, $this->db_header_fields))
-            $sort_field = 'idx';
-
-        if (array_key_exists('index', $this->icache)
-            && $this->icache['index']['key'] == $key
-            && $this->icache['index']['sort_field'] == $sort_field
-        ) {
-            if ($this->icache['index']['sort_order'] == $sort_order)
-                return $this->icache['index']['result'];
-            else
-                return array_reverse($this->icache['index']['result'], true);
-        }
-
-        $this->icache['index'] = array(
-            'result'     => array(),
-            'key'        => $key,
-            'sort_field' => $sort_field,
-            'sort_order' => $sort_order,
-        );
-
-        $sql_result = $this->db->query(
-            "SELECT idx, uid".
-            " FROM ".get_table_name('messages').
-            " WHERE user_id=?".
-            " AND cache_key=?".
-            " ORDER BY ".$this->db->quote_identifier($sort_field)." ".$sort_order,
-            $_SESSION['user_id'],
-            $key);
-
-        while ($sql_arr = $this->db->fetch_assoc($sql_result))
-            $this->icache['index']['result'][$sql_arr['idx']] = intval($sql_arr['uid']);
-
-        return $this->icache['index']['result'];
+        return $this->mcache;
     }
 
-
     /**
-     * @access private
-     */
-    private function add_message_cache($key, $index, $headers, $struct=null, $force=false, $internal_cache=false)
-    {
-        if (empty($key) || !is_object($headers) || empty($headers->uid))
-            return;
-
-        // add to internal (fast) cache
-        if ($internal_cache) {
-            $this->icache['message'][$headers->uid] = clone $headers;
-            $this->icache['message'][$headers->uid]->structure = $struct;
-        }
-
-        // no further caching
-        if (!$this->messages_caching)
-            return;
-
-        // known message id
-        if (is_int($force) && $force > 0) {
-            $message_id = $force;
-        }
-        // check for an existing record (probably headers are cached but structure not)
-        else if (!$force) {
-            $sql_result = $this->db->query(
-                "SELECT message_id".
-                " FROM ".get_table_name('messages').
-                " WHERE user_id=?".
-                " AND cache_key=?".
-                " AND uid=?",
-                $_SESSION['user_id'],
-                $key,
-                $headers->uid);
-
-            if ($sql_arr = $this->db->fetch_assoc($sql_result))
-                $message_id = $sql_arr['message_id'];
-        }
-
-        // update cache record
-        if ($message_id) {
-            $this->db->query(
-                "UPDATE ".get_table_name('messages').
-                " SET idx=?, headers=?, structure=?".
-                " WHERE message_id=?",
-                $index,
-                serialize($this->db->encode(clone $headers)),
-                is_object($struct) ? serialize($this->db->encode(clone $struct)) : NULL,
-                $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)mb_substr($this->db->encode($this->decode_header($headers->subject, true)), 0, 128),
-                (string)mb_substr($this->db->encode($this->decode_header($headers->from, true)), 0, 128),
-                (string)mb_substr($this->db->encode($this->decode_header($headers->to, true)), 0, 128),
-                (string)mb_substr($this->db->encode($this->decode_header($headers->cc, true)), 0, 128),
-                (int)$headers->size,
-                serialize($this->db->encode(clone $headers)),
-                is_object($struct) ? serialize($this->db->encode(clone $struct)) : NULL
-            );
-        }
-
-        unset($this->icache['index']);
-    }
-
-
-    /**
-     * @access private
-     */
-    private function remove_message_cache($key, $ids, $idx=false)
-    {
-        if (!$this->messages_caching)
-            return;
-
-        $this->db->query(
-            "DELETE FROM ".get_table_name('messages').
-            " WHERE user_id=?".
-            " AND cache_key=?".
-            " AND ".($idx ? "idx" : "uid")." IN (".$this->db->array2list($ids, 'integer').")",
-            $_SESSION['user_id'],
-            $key);
-
-        unset($this->icache['index']);
-    }
-
-
-    /**
-     * @param string $key         Cache key
-     * @param int    $start_index Start index
-     * @access private
-     */
-    private function clear_message_cache($key, $start_index=1)
-    {
-        if (!$this->messages_caching)
-            return;
-
-        $this->db->query(
-            "DELETE FROM ".get_table_name('messages').
-            " WHERE user_id=?".
-            " AND cache_key=?".
-            " AND idx>=?",
-            $_SESSION['user_id'], $key, $start_index);
-
-        unset($this->icache['index']);
-    }
-
-
-    /**
-     * @access private
-     */
-    private function get_message_cache_index_min($key, $uids=NULL)
-    {
-        if (!$this->messages_caching)
-            return;
-
-        if (!empty($uids) && !is_array($uids)) {
-            if ($uids == '*' || $uids == '1:*')
-                $uids = NULL;
-            else
-                $uids = explode(',', $uids);
-        }
-
-        $sql_result = $this->db->query(
-            "SELECT MIN(idx) AS minidx".
-            " FROM ".get_table_name('messages').
-            " WHERE  user_id=?".
-            " AND    cache_key=?"
-            .(!empty($uids) ? " AND uid IN (".$this->db->array2list($uids, 'integer').")" : ''),
-            $_SESSION['user_id'],
-            $key);
-
-        if ($sql_arr = $this->db->fetch_assoc($sql_result))
-            return $sql_arr['minidx'];
-        else
-            return 0;
-    }
-
-
-    /**
-     * @param string $key Cache key
-     * @param int    $id  Message (sequence) ID
-     * @return int Message UID
-     * @access private
+     * Clears the messages cache.
+     *
+     * @param string $mailbox Folder name
+     * @param array  $uids    Optional message UIDs to remove from cache
      */
-    private function get_cache_id2uid($key, $id)
+    function clear_message_cache($mailbox = null, $uids = null)
     {
-        if (!$this->messages_caching)
-            return null;
-
-        if (array_key_exists('index', $this->icache)
-            && $this->icache['index']['key'] == $key
-        ) {
-            return $this->icache['index']['result'][$id];
+        if ($mcache = $this->get_mcache_engine()) {
+            $mcache->clear($mailbox, $uids);
         }
-
-        $sql_result = $this->db->query(
-            "SELECT uid".
-            " FROM ".get_table_name('messages').
-            " WHERE user_id=?".
-            " AND cache_key=?".
-            " AND idx=?",
-            $_SESSION['user_id'], $key, $id);
-
-        if ($sql_arr = $this->db->fetch_assoc($sql_result))
-            return intval($sql_arr['uid']);
-
-        return null;
     }
 
 
-    /**
-     * @param string $key Cache key
-     * @param int    $uid Message UID
-     * @return int Message (sequence) ID
-     * @access private
-     */
-    private function get_cache_uid2id($key, $uid)
-    {
-        if (!$this->messages_caching)
-            return null;
-
-        if (array_key_exists('index', $this->icache)
-            && $this->icache['index']['key'] == $key
-        ) {
-            return array_search($uid, $this->icache['index']['result']);
-        }
-
-        $sql_result = $this->db->query(
-            "SELECT idx".
-            " FROM ".get_table_name('messages').
-            " WHERE user_id=?".
-            " AND cache_key=?".
-            " AND uid=?",
-            $_SESSION['user_id'], $key, $uid);
-
-        if ($sql_arr = $this->db->fetch_assoc($sql_result))
-            return intval($sql_arr['idx']);
-
-        return null;
-    }
-
 
     /* --------------------------------
      *   encoding/decoding methods
@@ -4432,7 +4163,7 @@ class rcube_imap
         $input = preg_replace("/\?=\s+=\?/", '?==?', $input);
 
         // encoded-word regexp
-        $re = '/=\?([^?]+)\?([BbQq])\?([^?\n]*)\?=/';
+        $re = '/=\?([^?]+)\?([BbQq])\?([^\n]*?)\?=/';
 
         // Find all RFC2047's encoded words
         if (preg_match_all($re, $input, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
@@ -4634,36 +4365,46 @@ class rcube_imap
 
 
     /**
-     * @param int    $uid     Message UID
-     * @param string $mailbox Mailbox name
+     * Finds message sequence ID for specified UID
+     *
+     * @param int    $uid      Message UID
+     * @param string $mailbox  Mailbox name
+     * @param bool   $force    True to skip cache
+     *
      * @return int Message (sequence) ID
-     * @access private
      */
-    private function _uid2id($uid, $mailbox=NULL)
+    function uid2id($uid, $mailbox = null, $force = false)
     {
         if (!strlen($mailbox)) {
             $mailbox = $this->mailbox;
         }
 
-        if (!isset($this->uid_id_map[$mailbox][$uid])) {
-            if (!($id = $this->get_cache_uid2id($mailbox.'.msg', $uid)))
-                $id = $this->conn->UID2ID($mailbox, $uid);
-
-            $this->uid_id_map[$mailbox][$uid] = $id;
+        if (!empty($this->uid_id_map[$mailbox][$uid])) {
+            return $this->uid_id_map[$mailbox][$uid];
         }
 
-        return $this->uid_id_map[$mailbox][$uid];
+        if (!$force && ($mcache = $this->get_mcache_engine()))
+            $id = $mcache->uid2id($mailbox, $uid);
+
+        if (empty($id))
+            $id = $this->conn->UID2ID($mailbox, $uid);
+
+        $this->uid_id_map[$mailbox][$uid] = $id;
+
+        return $id;
     }
 
 
     /**
-     * @param int    $id      Message (sequence) ID
-     * @param string $mailbox Mailbox name
+     * Find UID of the specified message sequence ID
+     *
+     * @param int    $id       Message (sequence) ID
+     * @param string $mailbox  Mailbox name
+     * @param bool   $force    True to skip cache
      *
      * @return int Message UID
-     * @access private
      */
-    private function _id2uid($id, $mailbox=null)
+    function id2uid($id, $mailbox = null, $force = false)
     {
         if (!strlen($mailbox)) {
             $mailbox = $this->mailbox;
@@ -4673,9 +4414,11 @@ class rcube_imap
             return $uid;
         }
 
-        if (!($uid = $this->get_cache_id2uid($mailbox.'.msg', $id))) {
+        if (!$force && ($mcache = $this->get_mcache_engine()))
+            $uid = $mcache->id2uid($mailbox, $id);
+
+        if (empty($uid))
             $uid = $this->conn->ID2UID($mailbox, $id);
-        }
 
         $this->uid_id_map[$mailbox][$uid] = $id;
 
@@ -4961,16 +4704,24 @@ class rcube_message_part
  */
 class rcube_header_sorter
 {
-    var $sequence_numbers = array();
+    private $seqs = array();
+    private $uids = array();
+
 
     /**
      * Set the predetermined sort order.
      *
-     * @param array $seqnums Numerically indexed array of IMAP message sequence numbers
+     * @param array $index  Numerically indexed array of IMAP ID or UIDs
+     * @param bool  $is_uid Set to true if $index contains UIDs
      */
-    function set_sequence_numbers($seqnums)
+    function set_index($index, $is_uid = false)
     {
-        $this->sequence_numbers = array_flip($seqnums);
+        $index = array_flip($index);
+
+        if ($is_uid)
+            $this->uids = $index;
+        else
+            $this->seqs = $index;
     }
 
     /**
@@ -4980,14 +4731,10 @@ class rcube_header_sorter
      */
     function sort_headers(&$headers)
     {
-        /*
-        * uksort would work if the keys were the sequence number, but unfortunately
-        * the keys are the UIDs.  We'll use uasort instead and dereference the value
-        * to get the sequence number (in the "id" field).
-        *
-        * uksort($headers, array($this, "compare_seqnums"));
-        */
-        uasort($headers, array($this, "compare_seqnums"));
+        if (!empty($this->uids))
+            uksort($headers, array($this, "compare_uids"));
+        else
+            uasort($headers, array($this, "compare_seqnums"));
     }
 
     /**
@@ -5003,8 +4750,24 @@ class rcube_header_sorter
         $seqb = $b->id;
 
         // then find each sequence number in my ordered list
-        $posa = isset($this->sequence_numbers[$seqa]) ? intval($this->sequence_numbers[$seqa]) : -1;
-        $posb = isset($this->sequence_numbers[$seqb]) ? intval($this->sequence_numbers[$seqb]) : -1;
+        $posa = isset($this->seqs[$seqa]) ? intval($this->seqs[$seqa]) : -1;
+        $posb = isset($this->seqs[$seqb]) ? intval($this->seqs[$seqb]) : -1;
+
+        // return the relative position as the comparison value
+        return $posa - $posb;
+    }
+
+    /**
+     * Sort method called by uksort()
+     *
+     * @param int $a Array key (UID)
+     * @param int $b Array key (UID)
+     */
+    function compare_uids($a, $b)
+    {
+        // then find each sequence number in my ordered list
+        $posa = isset($this->uids[$a]) ? intval($this->uids[$a]) : -1;
+        $posb = isset($this->uids[$b]) ? intval($this->uids[$b]) : -1;
 
         // return the relative position as the comparison value
         return $posa - $posb;
diff --git a/program/include/rcube_imap_cache.php b/program/include/rcube_imap_cache.php
new file mode 100644 (file)
index 0000000..75b9443
--- /dev/null
@@ -0,0 +1,1212 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/include/rcube_imap_cache.php                                  |
+ |                                                                       |
+ | This file is part of the Roundcube Webmail client                     |
+ | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Caching of IMAP folder contents (messages and index)                |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com>                        |
+ | Author: Aleksander Machniak <alec@alec.pl>                            |
+ +-----------------------------------------------------------------------+
+
+ $Id: rcube_imap_cache.php 5366 2011-10-26 11:48:27Z thomasb $
+
+*/
+
+
+/**
+ * Interface class for accessing Roundcube messages cache
+ *
+ * @package    Cache
+ * @author     Thomas Bruederli <roundcube@gmail.com>
+ * @author     Aleksander Machniak <alec@alec.pl>
+ * @version    1.0
+ */
+class rcube_imap_cache
+{
+    /**
+     * Instance of rcube_imap
+     *
+     * @var rcube_imap
+     */
+    private $imap;
+
+    /**
+     * Instance of rcube_mdb2
+     *
+     * @var rcube_mdb2
+     */
+    private $db;
+
+    /**
+     * User ID
+     *
+     * @var int
+     */
+    private $userid;
+
+    /**
+     * Internal (in-memory) cache
+     *
+     * @var array
+     */
+    private $icache = array();
+
+    private $skip_deleted = false;
+
+    /**
+     * List of known flags. Thanks to this we can handle flag changes
+     * with good performance. Bad thing is we need to know used flags.
+     */
+    public $flags = array(
+        1       => 'SEEN',          // RFC3501
+        2       => 'DELETED',       // RFC3501
+        4       => 'ANSWERED',      // RFC3501
+        8       => 'FLAGGED',       // RFC3501
+        16      => 'DRAFT',         // RFC3501
+        32      => 'MDNSENT',       // RFC3503
+        64      => 'FORWARDED',     // RFC5550
+        128     => 'SUBMITPENDING', // RFC5550
+        256     => 'SUBMITTED',     // RFC5550
+        512     => 'JUNK',
+        1024    => 'NONJUNK',
+        2048    => 'LABEL1',
+        4096    => 'LABEL2',
+        8192    => 'LABEL3',
+        16384   => 'LABEL4',
+        32768   => 'LABEL5',
+    );
+
+
+    /**
+     * Object constructor.
+     */
+    function __construct($db, $imap, $userid, $skip_deleted)
+    {
+        $this->db           = $db;
+        $this->imap         = $imap;
+        $this->userid       = (int)$userid;
+        $this->skip_deleted = $skip_deleted;
+    }
+
+
+    /**
+     * Cleanup actions (on shutdown).
+     */
+    public function close()
+    {
+        $this->save_icache();
+        $this->icache = null;
+    }
+
+
+    /**
+     * Return (sorted) messages index.
+     * If index doesn't exist or is invalid, will be updated.
+     *
+     * @param string  $mailbox     Folder name
+     * @param string  $sort_field  Sorting column
+     * @param string  $sort_order  Sorting order (ASC|DESC)
+     * @param bool    $exiting     Skip index initialization if it doesn't exist in DB
+     *
+     * @return array Messages index
+     */
+    function get_index($mailbox, $sort_field = null, $sort_order = null, $existing = false)
+    {
+        if (empty($this->icache[$mailbox]))
+            $this->icache[$mailbox] = array();
+
+        $sort_order = strtoupper($sort_order) == 'ASC' ? 'ASC' : 'DESC';
+
+        // Seek in internal cache
+        if (array_key_exists('index', $this->icache[$mailbox])) {
+            // The index was fetched from database already, but not validated yet
+            if (!array_key_exists('result', $this->icache[$mailbox]['index'])) {
+                $index = $this->icache[$mailbox]['index'];
+            }
+            // We've got a valid index
+            else if ($sort_field == 'ANY' || $this->icache[$mailbox]['index']['sort_field'] == $sort_field
+            ) {
+                if ($this->icache[$mailbox]['index']['sort_order'] == $sort_order)
+                    return $this->icache[$mailbox]['index']['result'];
+                else
+                    return array_reverse($this->icache[$mailbox]['index']['result'], true);
+            }
+        }
+
+        // Get index from DB (if DB wasn't already queried)
+        if (empty($index) && empty($this->icache[$mailbox]['index_queried'])) {
+            $index = $this->get_index_row($mailbox);
+
+            // set the flag that DB was already queried for index
+            // this way we'll be able to skip one SELECT, when
+            // get_index() is called more than once
+            $this->icache[$mailbox]['index_queried'] = true;
+        }
+
+        $data = null;
+
+        // @TODO: Think about skipping validation checks.
+        // If we could check only every 10 minutes, we would be able to skip
+        // expensive checks, mailbox selection or even IMAP connection, this would require
+        // additional logic to force cache invalidation in some cases
+        // and many rcube_imap changes to connect when needed
+
+        // Entry exists, check cache status
+        if (!empty($index)) {
+            $exists = true;
+
+            if ($sort_field == 'ANY') {
+                $sort_field = $index['sort_field'];
+            }
+
+            if ($sort_field != $index['sort_field']) {
+                $is_valid = false;
+            }
+            else {
+                $is_valid = $this->validate($mailbox, $index, $exists);
+            }
+
+            if ($is_valid) {
+                // build index, assign sequence IDs to unique IDs
+                $data = array_combine($index['seq'], $index['uid']);
+                // revert the order if needed
+                if ($index['sort_order'] != $sort_order)
+                    $data = array_reverse($data, true);
+            }
+        }
+        else {
+            if ($existing) {
+                return null;
+            }
+            else if ($sort_field == 'ANY') {
+                $sort_field = '';
+            }
+
+            // Got it in internal cache, so the row already exist
+            $exists = array_key_exists('index', $this->icache[$mailbox]);
+        }
+
+        // Index not found, not valid or sort field changed, get index from IMAP server
+        if ($data === null) {
+            // Get mailbox data (UIDVALIDITY, counters, etc.) for status check
+            $mbox_data = $this->imap->mailbox_data($mailbox);
+            $data      = $this->get_index_data($mailbox, $sort_field, $sort_order, $mbox_data);
+
+            // insert/update
+            $this->add_index_row($mailbox, $sort_field, $sort_order, $data, $mbox_data,
+                $exists, $index['modseq']);
+        }
+
+        $this->icache[$mailbox]['index'] = array(
+            'result'     => $data,
+            'sort_field' => $sort_field,
+            'sort_order' => $sort_order,
+            'modseq'     => !empty($index['modseq']) ? $index['modseq'] : $mbox_data['HIGHESTMODSEQ']
+        );
+
+        return $data;
+    }
+
+
+    /**
+     * Return messages thread.
+     * If threaded index doesn't exist or is invalid, will be updated.
+     *
+     * @param string  $mailbox     Folder name
+     * @param string  $sort_field  Sorting column
+     * @param string  $sort_order  Sorting order (ASC|DESC)
+     *
+     * @return array Messages threaded index
+     */
+    function get_thread($mailbox)
+    {
+        if (empty($this->icache[$mailbox]))
+            $this->icache[$mailbox] = array();
+
+        // Seek in internal cache
+        if (array_key_exists('thread', $this->icache[$mailbox])) {
+            return array(
+                $this->icache[$mailbox]['thread']['tree'],
+                $this->icache[$mailbox]['thread']['depth'],
+                $this->icache[$mailbox]['thread']['children'],
+            );
+        }
+
+        // Get thread from DB (if DB wasn't already queried)
+        if (empty($this->icache[$mailbox]['thread_queried'])) {
+            $index = $this->get_thread_row($mailbox);
+
+            // set the flag that DB was already queried for thread
+            // this way we'll be able to skip one SELECT, when
+            // get_thread() is called more than once or after clear()
+            $this->icache[$mailbox]['thread_queried'] = true;
+        }
+
+        $data = null;
+
+        // Entry exist, check cache status
+        if (!empty($index)) {
+            $exists   = true;
+            $is_valid = $this->validate($mailbox, $index, $exists);
+
+            if (!$is_valid) {
+                $index = null;
+            }
+        }
+
+        // Index not found or not valid, get index from IMAP server
+        if ($index === null) {
+            // Get mailbox data (UIDVALIDITY, counters, etc.) for status check
+            $mbox_data = $this->imap->mailbox_data($mailbox);
+
+            if ($mbox_data['EXISTS']) {
+                // get all threads (default sort order)
+                list ($thread_tree, $msg_depth, $has_children) = $this->imap->fetch_threads($mailbox, true);
+            }
+
+            $index = array(
+                'tree'     => !empty($thread_tree) ? $thread_tree : array(),
+                'depth'    => !empty($msg_depth) ? $msg_depth : array(),
+                'children' => !empty($has_children) ? $has_children : array(),
+            );
+
+            // insert/update
+            $this->add_thread_row($mailbox, $index, $mbox_data, $exists);
+        }
+
+        $this->icache[$mailbox]['thread'] = $index;
+
+        return array($index['tree'], $index['depth'], $index['children']);
+    }
+
+
+    /**
+     * Returns list of messages (headers). See rcube_imap::fetch_headers().
+     *
+     * @param string $mailbox  Folder name
+     * @param array  $msgs     Message sequence numbers
+     * @param bool   $is_uid   True if $msgs contains message UIDs
+     *
+     * @return array The list of messages (rcube_mail_header) indexed by UID
+     */
+    function get_messages($mailbox, $msgs = array(), $is_uid = true)
+    {
+        if (empty($msgs)) {
+            return array();
+        }
+
+        // @TODO: it would be nice if we could work with UIDs only
+        // then index would be not needed. For now we need it to
+        // map id to uid here and to update message id for cached message
+
+        // Convert IDs to UIDs
+        $index = $this->get_index($mailbox, 'ANY');
+        if (!$is_uid) {
+            foreach ($msgs as $idx => $msgid)
+                if ($uid = $index[$msgid])
+                    $msgs[$idx] = $uid;
+        }
+
+        // Fetch messages from cache
+        $sql_result = $this->db->query(
+            "SELECT uid, data, flags"
+            ." FROM ".get_table_name('cache_messages')
+            ." WHERE user_id = ?"
+                ." AND mailbox = ?"
+                ." AND uid IN (".$this->db->array2list($msgs, 'integer').")",
+            $this->userid, $mailbox);
+
+        $msgs   = array_flip($msgs);
+        $result = array();
+
+        while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
+            $uid          = intval($sql_arr['uid']);
+            $result[$uid] = $this->build_message($sql_arr);
+
+            // save memory, we don't need message body here (?)
+            $result[$uid]->body = null;
+
+            // update message ID according to index data
+            if (!empty($index) && ($id = array_search($uid, $index)))
+                $result[$uid]->id = $id;
+
+            if (!empty($result[$uid])) {
+                unset($msgs[$uid]);
+            }
+        }
+
+        // Fetch not found messages from IMAP server
+        if (!empty($msgs)) {
+            $messages = $this->imap->fetch_headers($mailbox, array_keys($msgs), true, true);
+
+            // Insert to DB and add to result list
+            if (!empty($messages)) {
+                foreach ($messages as $msg) {
+                    $this->add_message($mailbox, $msg, !array_key_exists($msg->uid, $result));
+                    $result[$msg->uid] = $msg;
+                }
+            }
+        }
+
+        return $result;
+    }
+
+
+    /**
+     * Returns message data.
+     *
+     * @param string $mailbox  Folder name
+     * @param int    $uid      Message UID
+     * @param bool   $update   If message doesn't exists in cache it will be fetched
+     *                         from IMAP server
+     * @param bool   $no_cache Enables internal cache usage
+     *
+     * @return rcube_mail_header Message data
+     */
+    function get_message($mailbox, $uid, $update = true, $cache = true)
+    {
+        // Check internal cache
+        if (($message = $this->icache['message'])
+            && $message['mailbox'] == $mailbox && $message['object']->uid == $uid
+        ) {
+            return $this->icache['message']['object'];
+        }
+
+        $sql_result = $this->db->query(
+            "SELECT flags, data"
+            ." FROM ".get_table_name('cache_messages')
+            ." WHERE user_id = ?"
+                ." AND mailbox = ?"
+                ." AND uid = ?",
+                $this->userid, $mailbox, (int)$uid);
+
+        if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
+            $message = $this->build_message($sql_arr);
+            $found   = true;
+
+            // update message ID according to index data
+            $index = $this->get_index($mailbox, 'ANY');
+            if (!empty($index) && ($id = array_search($uid, $index)))
+                $message->id = $id;
+        }
+
+        // Get the message from IMAP server
+        if (empty($message) && $update) {
+            $message = $this->imap->get_headers($uid, $mailbox, true);
+            // cache will be updated in close(), see below
+        }
+
+        // Save the message in internal cache, will be written to DB in close()
+        // Common scenario: user opens unseen message
+        // - get message (SELECT)
+        // - set message headers/structure (INSERT or UPDATE)
+        // - set \Seen flag (UPDATE)
+        // This way we can skip one UPDATE
+        if (!empty($message) && $cache) {
+            // Save current message from internal cache
+            $this->save_icache();
+
+            $this->icache['message'] = array(
+                'object'  => $message,
+                'mailbox' => $mailbox,
+                'exists'  => $found,
+                'md5sum'  => md5(serialize($message)),
+            );
+        }
+
+        return $message;
+    }
+
+
+    /**
+     * Saves the message in cache.
+     *
+     * @param string            $mailbox  Folder name
+     * @param rcube_mail_header $message  Message data
+     * @param bool              $force    Skips message in-cache existance check
+     */
+    function add_message($mailbox, $message, $force = false)
+    {
+        if (!is_object($message) || empty($message->uid))
+            return;
+
+        $msg   = serialize($this->db->encode(clone $message));
+        $flags = 0;
+
+        if (!empty($message->flags)) {
+            foreach ($this->flags as $idx => $flag)
+                if (!empty($message->flags[$flag]))
+                    $flags += $idx;
+        }
+        unset($msg->flags);
+
+        // update cache record (even if it exists, the update
+        // here will work as select, assume row exist if affected_rows=0)
+        if (!$force) {
+            $res = $this->db->query(
+                "UPDATE ".get_table_name('cache_messages')
+                ." SET flags = ?, data = ?, changed = ".$this->db->now()
+                ." WHERE user_id = ?"
+                    ." AND mailbox = ?"
+                    ." AND uid = ?",
+                $flags, $msg, $this->userid, $mailbox, (int) $message->uid);
+
+            if ($this->db->affected_rows())
+                return;
+        }
+
+        // insert new record
+        $this->db->query(
+            "INSERT INTO ".get_table_name('cache_messages')
+            ." (user_id, mailbox, uid, flags, changed, data)"
+            ." VALUES (?, ?, ?, ?, ".$this->db->now().", ?)",
+            $this->userid, $mailbox, (int) $message->uid, $flags, $msg);
+    }
+
+
+    /**
+     * Sets the flag for specified message.
+     *
+     * @param string  $mailbox  Folder name
+     * @param array   $uids     Message UIDs or null to change flag
+     *                          of all messages in a folder
+     * @param string  $flag     The name of the flag
+     * @param bool    $enabled  Flag state
+     */
+    function change_flag($mailbox, $uids, $flag, $enabled = false)
+    {
+        $flag = strtoupper($flag);
+        $idx  = (int) array_search($flag, $this->flags);
+
+        if (!$idx) {
+            return;
+        }
+
+        // Internal cache update
+        if ($uids && count($uids) == 1 && ($uid = current($uids))
+            && ($message = $this->icache['message'])
+            && $message['mailbox'] == $mailbox && $message['object']->uid == $uid
+        ) {
+            $message['object']->flags[$flag] = $enabled;
+            return;
+        }
+
+        $this->db->query(
+            "UPDATE ".get_table_name('cache_messages')
+            ." SET changed = ".$this->db->now()
+            .", flags = flags ".($enabled ? "+ $idx" : "- $idx")
+            ." WHERE user_id = ?"
+                ." AND mailbox = ?"
+                .($uids !== null ? " AND uid IN (".$this->db->array2list((array)$uids, 'integer').")" : "")
+                ." AND (flags & $idx) ".($enabled ? "= 0" : "= $idx"),
+            $this->userid, $mailbox);
+    }
+
+
+    /**
+     * Removes message(s) from cache.
+     *
+     * @param string $mailbox  Folder name
+     * @param array  $uids     Message UIDs, NULL removes all messages
+     */
+    function remove_message($mailbox = null, $uids = null)
+    {
+        if (!strlen($mailbox)) {
+            $this->db->query(
+                "DELETE FROM ".get_table_name('cache_messages')
+                ." WHERE user_id = ?",
+                $this->userid);
+        }
+        else {
+            // Remove the message from internal cache
+            if (!empty($uids) && !is_array($uids) && ($message = $this->icache['message'])
+                && $message['mailbox'] == $mailbox && $message['object']->uid == $uids
+            ) {
+                $this->icache['message'] = null;
+            }
+
+            $this->db->query(
+                "DELETE FROM ".get_table_name('cache_messages')
+                ." WHERE user_id = ?"
+                    ." AND mailbox = ".$this->db->quote($mailbox)
+                    .($uids !== null ? " AND uid IN (".$this->db->array2list((array)$uids, 'integer').")" : ""),
+                $this->userid);
+        }
+
+    }
+
+
+    /**
+     * Clears index cache.
+     *
+     * @param string  $mailbox     Folder name
+     * @param bool    $remove      Enable to remove the DB row
+     */
+    function remove_index($mailbox = null, $remove = false)
+    {
+        // The index should be only removed from database when
+        // UIDVALIDITY was detected or the mailbox is empty
+        // otherwise use 'valid' flag to not loose HIGHESTMODSEQ value
+        if ($remove)
+            $this->db->query(
+                "DELETE FROM ".get_table_name('cache_index')
+                ." WHERE user_id = ".intval($this->userid)
+                    .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : "")
+            );
+        else
+            $this->db->query(
+                "UPDATE ".get_table_name('cache_index')
+                ." SET valid = 0"
+                ." WHERE user_id = ".intval($this->userid)
+                    .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : "")
+            );
+
+        if (strlen($mailbox)) {
+            unset($this->icache[$mailbox]['index']);
+            // Index removed, set flag to skip SELECT query in get_index()
+            $this->icache[$mailbox]['index_queried'] = true;
+        }
+        else
+            $this->icache = array();
+    }
+
+
+    /**
+     * Clears thread cache.
+     *
+     * @param string  $mailbox     Folder name
+     */
+    function remove_thread($mailbox = null)
+    {
+        $this->db->query(
+            "DELETE FROM ".get_table_name('cache_thread')
+            ." WHERE user_id = ".intval($this->userid)
+                .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : "")
+        );
+
+        if (strlen($mailbox)) {
+            unset($this->icache[$mailbox]['thread']);
+            // Thread data removed, set flag to skip SELECT query in get_thread()
+            $this->icache[$mailbox]['thread_queried'] = true;
+        }
+        else
+            $this->icache = array();
+    }
+
+
+    /**
+     * Clears the cache.
+     *
+     * @param string $mailbox  Folder name
+     * @param array  $uids     Message UIDs, NULL removes all messages in a folder
+     */
+    function clear($mailbox = null, $uids = null)
+    {
+        $this->remove_index($mailbox, true);
+        $this->remove_thread($mailbox);
+        $this->remove_message($mailbox, $uids);
+    }
+
+
+    /**
+     * @param string $mailbox Folder name
+     * @param int    $id      Message (sequence) ID
+     *
+     * @return int Message UID
+     */
+    function id2uid($mailbox, $id)
+    {
+        if (!empty($this->icache['pending_index_update']))
+            return null;
+
+        // get index if it exists
+        $index = $this->get_index($mailbox, 'ANY', null, true);
+
+        return $index[$id];
+    }
+
+
+    /**
+     * @param string $mailbox Folder name
+     * @param int    $uid     Message UID
+     *
+     * @return int Message (sequence) ID
+     */
+    function uid2id($mailbox, $uid)
+    {
+        if (!empty($this->icache['pending_index_update']))
+            return null;
+
+        // get index if it exists
+        $index = $this->get_index($mailbox, 'ANY', null, true);
+
+        return array_search($uid, (array)$index);
+    }
+
+    /**
+     * Fetches index data from database
+     */
+    private function get_index_row($mailbox)
+    {
+        // Get index from DB
+        $sql_result = $this->db->query(
+            "SELECT data, valid"
+            ." FROM ".get_table_name('cache_index')
+            ." WHERE user_id = ?"
+                ." AND mailbox = ?",
+            $this->userid, $mailbox);
+
+        if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
+            $data = explode('@', $sql_arr['data']);
+
+            return array(
+                'valid'      => $sql_arr['valid'],
+                'seq'        => explode(',', $data[0]),
+                'uid'        => explode(',', $data[1]),
+                'sort_field' => $data[2],
+                'sort_order' => $data[3],
+                'deleted'    => $data[4],
+                'validity'   => $data[5],
+                'uidnext'    => $data[6],
+                'modseq'     => $data[7],
+            );
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Fetches thread data from database
+     */
+    private function get_thread_row($mailbox)
+    {
+        // Get thread from DB
+        $sql_result = $this->db->query(
+            "SELECT data"
+            ." FROM ".get_table_name('cache_thread')
+            ." WHERE user_id = ?"
+                ." AND mailbox = ?",
+            $this->userid, $mailbox);
+
+        if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
+            $data = explode('@', $sql_arr['data']);
+
+            // Uncompress data, see add_thread_row()
+  //          $data[0] = str_replace(array('*', '^', '#'), array(';a:0:{}', 'i:', ';a:1:'), $data[0]);
+            $data[0] = unserialize($data[0]);
+
+            // build 'depth' and 'children' arrays
+            $depth = $children = array();
+            $this->build_thread_data($data[0], $depth, $children);
+
+            return array(
+                'tree'     => $data[0],
+                'depth'    => $depth,
+                'children' => $children,
+                'deleted'  => $data[1],
+                'validity' => $data[2],
+                'uidnext'  => $data[3],
+            );
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Saves index data into database
+     */
+    private function add_index_row($mailbox, $sort_field, $sort_order,
+        $data = array(), $mbox_data = array(), $exists = false, $modseq = null)
+    {
+        $data = array(
+            implode(',', array_keys($data)),
+            implode(',', array_values($data)),
+            $sort_field,
+            $sort_order,
+            (int) $this->skip_deleted,
+            (int) $mbox_data['UIDVALIDITY'],
+            (int) $mbox_data['UIDNEXT'],
+            $modseq ? $modseq : $mbox_data['HIGHESTMODSEQ'],
+        );
+        $data = implode('@', $data);
+
+        if ($exists)
+            $sql_result = $this->db->query(
+                "UPDATE ".get_table_name('cache_index')
+                ." SET data = ?, valid = 1, changed = ".$this->db->now()
+                ." WHERE user_id = ?"
+                    ." AND mailbox = ?",
+                $data, $this->userid, $mailbox);
+        else
+            $sql_result = $this->db->query(
+                "INSERT INTO ".get_table_name('cache_index')
+                ." (user_id, mailbox, data, valid, changed)"
+                ." VALUES (?, ?, ?, 1, ".$this->db->now().")",
+                $this->userid, $mailbox, $data);
+    }
+
+
+    /**
+     * Saves thread data into database
+     */
+    private function add_thread_row($mailbox, $data = array(), $mbox_data = array(), $exists = false)
+    {
+        $tree = serialize($data['tree']);
+        // This significantly reduces data length
+//        $tree = str_replace(array(';a:0:{}', 'i:', ';a:1:'), array('*', '^', '#'), $tree);
+
+        $data = array(
+            $tree,
+            (int) $this->skip_deleted,
+            (int) $mbox_data['UIDVALIDITY'],
+            (int) $mbox_data['UIDNEXT'],
+        );
+        $data = implode('@', $data);
+
+        if ($exists)
+            $sql_result = $this->db->query(
+                "UPDATE ".get_table_name('cache_thread')
+                ." SET data = ?, changed = ".$this->db->now()
+                ." WHERE user_id = ?"
+                    ." AND mailbox = ?",
+                $data, $this->userid, $mailbox);
+        else
+            $sql_result = $this->db->query(
+                "INSERT INTO ".get_table_name('cache_thread')
+                ." (user_id, mailbox, data, changed)"
+                ." VALUES (?, ?, ?, ".$this->db->now().")",
+                $this->userid, $mailbox, $data);
+    }
+
+
+    /**
+     * Checks index/thread validity
+     */
+    private function validate($mailbox, $index, &$exists = true)
+    {
+        $is_thread = isset($index['tree']);
+
+        // Get mailbox data (UIDVALIDITY, counters, etc.) for status check
+        $mbox_data = $this->imap->mailbox_data($mailbox);
+
+        // @TODO: Think about skipping validation checks.
+        // If we could check only every 10 minutes, we would be able to skip
+        // expensive checks, mailbox selection or even IMAP connection, this would require
+        // additional logic to force cache invalidation in some cases
+        // and many rcube_imap changes to connect when needed
+
+        // Check UIDVALIDITY
+        if ($index['validity'] != $mbox_data['UIDVALIDITY']) {
+            $this->clear($mailbox);
+            $exists = false;
+            return false;
+        }
+
+        // Folder is empty but cache isn't
+        if (empty($mbox_data['EXISTS'])) {
+            if (!empty($index['seq']) || !empty($index['tree'])) {
+                $this->clear($mailbox);
+                $exists = false;
+                return false;
+            }
+        }
+        // Folder is not empty but cache is
+        else if (empty($index['seq']) && empty($index['tree'])) {
+            unset($this->icache[$mailbox][$is_thread ? 'thread' : 'index']);
+            return false;
+        }
+
+        // Validation flag
+        if (!$is_thread && empty($index['valid'])) {
+            unset($this->icache[$mailbox][$is_thread ? 'thread' : 'index']);
+            return false;
+        }
+
+        // Index was created with different skip_deleted setting
+        if ($this->skip_deleted != $index['deleted']) {
+            return false;
+        }
+
+        // Check HIGHESTMODSEQ
+        if (!empty($index['modseq']) && !empty($mbox_data['HIGHESTMODSEQ'])
+            && $index['modseq'] == $mbox_data['HIGHESTMODSEQ']
+        ) {
+            return true;
+        }
+
+        // Check UIDNEXT
+        if ($index['uidnext'] != $mbox_data['UIDNEXT']) {
+            unset($this->icache[$mailbox][$is_thread ? 'thread' : 'index']);
+            return false;
+        }
+
+        // @TODO: find better validity check for threaded index
+        if ($is_thread) {
+            // check messages number...
+            if (!$this->skip_deleted && $mbox_data['EXISTS'] != @max(array_keys($index['depth']))) {
+                return false;
+            }
+            return true;
+        }
+
+        // The rest of checks, more expensive
+        if (!empty($this->skip_deleted)) {
+            // compare counts if available
+            if ($mbox_data['COUNT_UNDELETED'] != null
+                && $mbox_data['COUNT_UNDELETED'] != count($index['uid'])) {
+                return false;
+            }
+            // compare UID sets
+            if ($mbox_data['ALL_UNDELETED'] != null) {
+                $uids_new = rcube_imap_generic::uncompressMessageSet($mbox_data['ALL_UNDELETED']);
+                $uids_old = $index['uid'];
+
+                if (count($uids_new) != count($uids_old)) {
+                    return false;
+                }
+
+                sort($uids_new, SORT_NUMERIC);
+                sort($uids_old, SORT_NUMERIC);
+
+                if ($uids_old != $uids_new)
+                    return false;
+            }
+            else {
+                // get all undeleted messages excluding cached UIDs
+                $ids = $this->imap->search_once($mailbox, 'ALL UNDELETED NOT UID '.
+                    rcube_imap_generic::compressMessageSet($index['uid']));
+
+                if (!empty($ids)) {
+                    return false;
+                }
+            }
+        }
+        else {
+            // check messages number...
+            if ($mbox_data['EXISTS'] != max($index['seq'])) {
+                return false;
+            }
+            // ... and max UID
+            if (max($index['uid']) != $this->imap->id2uid($mbox_data['EXISTS'], $mailbox, true)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Synchronizes the mailbox.
+     *
+     * @param string $mailbox Folder name
+     */
+    function synchronize($mailbox)
+    {
+        // RFC4549: Synchronization Operations for Disconnected IMAP4 Clients
+        // RFC4551: IMAP Extension for Conditional STORE Operation
+        //          or Quick Flag Changes Resynchronization
+        // RFC5162: IMAP Extensions for Quick Mailbox Resynchronization
+
+        // @TODO: synchronize with other methods?
+        $qresync   = $this->imap->get_capability('QRESYNC');
+        $condstore = $qresync ? true : $this->imap->get_capability('CONDSTORE');
+
+        if (!$qresync && !$condstore) {
+            return;
+        }
+
+        // Get stored index
+        $index = $this->get_index_row($mailbox);
+
+        // database is empty
+        if (empty($index)) {
+            // set the flag that DB was already queried for index
+            // this way we'll be able to skip one SELECT in get_index()
+            $this->icache[$mailbox]['index_queried'] = true;
+            return;
+        }
+
+        $this->icache[$mailbox]['index'] = $index;
+
+        // no last HIGHESTMODSEQ value
+        if (empty($index['modseq'])) {
+            return;
+        }
+
+        // NOTE: make sure the mailbox isn't selected, before
+        // enabling QRESYNC and invoking SELECT
+        if ($this->imap->conn->selected !== null) {
+            $this->imap->conn->close();
+        }
+
+        // Enable QRESYNC
+        $res = $this->imap->conn->enable($qresync ? 'QRESYNC' : 'CONDSTORE');
+        if (!is_array($res)) {
+            return;
+        }
+
+        // Get mailbox data (UIDVALIDITY, HIGHESTMODSEQ, counters, etc.)
+        $mbox_data = $this->imap->mailbox_data($mailbox);
+
+        if (empty($mbox_data)) {
+             return;
+        }
+
+        // Check UIDVALIDITY
+        if ($index['validity'] != $mbox_data['UIDVALIDITY']) {
+            $this->clear($mailbox);
+            return;
+        }
+
+        // QRESYNC not supported on specified mailbox
+        if (!empty($mbox_data['NOMODSEQ']) || empty($mbox_data['HIGHESTMODSEQ'])) {
+            return;
+        }
+
+        // Nothing new
+        if ($mbox_data['HIGHESTMODSEQ'] == $index['modseq']) {
+            return;
+        }
+
+        // Get known uids
+        $uids = array();
+        $sql_result = $this->db->query(
+            "SELECT uid"
+            ." FROM ".get_table_name('cache_messages')
+            ." WHERE user_id = ?"
+                ." AND mailbox = ?",
+            $this->userid, $mailbox);
+
+        while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
+          $uids[] = $sql_arr['uid'];
+        }
+
+        // No messages in database, nothing to sync
+        if (empty($uids)) {
+            return;
+        }
+
+        // Get modified flags and vanished messages
+        // UID FETCH 1:* (FLAGS) (CHANGEDSINCE 0123456789 VANISHED)
+        $result = $this->imap->conn->fetch($mailbox,
+            !empty($uids) ? $uids : '1:*', true, array('FLAGS'),
+            $index['modseq'], $qresync);
+
+        $invalidated = false;
+
+        if (!empty($result)) {
+            foreach ($result as $id => $msg) {
+                $uid = $msg->uid;
+                // Remove deleted message
+                if ($this->skip_deleted && !empty($msg->flags['DELETED'])) {
+                    $this->remove_message($mailbox, $uid);
+
+                    if (!$invalidated) {
+                        $invalidated = true;
+                        // Invalidate thread indexes (?)
+                        $this->remove_thread($mailbox);
+                        // Invalidate index
+                        $index['valid'] = false;
+                    }
+                    continue;
+                }
+
+                $flags = 0;
+                if (!empty($msg->flags)) {
+                    foreach ($this->flags as $idx => $flag)
+                        if (!empty($msg->flags[$flag]))
+                            $flags += $idx;
+                }
+
+                $this->db->query(
+                    "UPDATE ".get_table_name('cache_messages')
+                    ." SET flags = ?, changed = ".$this->db->now()
+                    ." WHERE user_id = ?"
+                        ." AND mailbox = ?"
+                        ." AND uid = ?"
+                        ." AND flags <> ?",
+                    $flags, $this->userid, $mailbox, $uid, $flags);
+            }
+        }
+
+        // Get VANISHED
+        if ($qresync) {
+            $mbox_data = $this->imap->mailbox_data($mailbox);
+
+            // Removed messages
+            if (!empty($mbox_data['VANISHED'])) {
+                $uids = rcube_imap_generic::uncompressMessageSet($mbox_data['VANISHED']);
+                if (!empty($uids)) {
+                    // remove messages from database
+                    $this->remove_message($mailbox, $uids);
+
+                    // Invalidate thread indexes (?)
+                    $this->remove_thread($mailbox);
+                    // Invalidate index
+                    $index['valid'] = false;
+                }
+            }
+        }
+
+        $sort_field = $index['sort_field'];
+        $sort_order = $index['sort_order'];
+        $exists     = true;
+
+        // Validate index
+        if (!$this->validate($mailbox, $index, $exists)) {
+            // Update index
+            $data = $this->get_index_data($mailbox, $sort_field, $sort_order, $mbox_data);
+        }
+        else {
+            $data = array_combine($index['seq'], $index['uid']);
+        }
+
+        // update index and/or HIGHESTMODSEQ value
+        $this->add_index_row($mailbox, $sort_field, $sort_order, $data, $mbox_data, $exists);
+
+        // update internal cache for get_index()
+        $this->icache[$mailbox]['index']['result'] = $data;
+    }
+
+
+    /**
+     * Converts cache row into message object.
+     *
+     * @param array $sql_arr Message row data
+     *
+     * @return rcube_mail_header Message object
+     */
+    private function build_message($sql_arr)
+    {
+        $message = $this->db->decode(unserialize($sql_arr['data']));
+
+        if ($message) {
+            $message->flags = array();
+            foreach ($this->flags as $idx => $flag)
+                if (($sql_arr['flags'] & $idx) == $idx)
+                    $message->flags[$flag] = true;
+        }
+
+        return $message;
+    }
+
+
+    /**
+     * Creates 'depth' and 'children' arrays from stored thread 'tree' data.
+     */
+    private function build_thread_data($data, &$depth, &$children, $level = 0)
+    {
+        foreach ((array)$data as $key => $val) {
+            $children[$key] = !empty($val);
+            $depth[$key] = $level;
+            if (!empty($val))
+                $this->build_thread_data($val, $depth, $children, $level + 1);
+        }
+    }
+
+
+    /**
+     * Saves message stored in internal cache
+     */
+    private function save_icache()
+    {
+        // Save current message from internal cache
+        if ($message = $this->icache['message']) {
+            // clean up some object's data
+            $object = $this->message_object_prepare($message['object']);
+
+            // calculate current md5 sum
+            $md5sum = md5(serialize($object));
+
+            if ($message['md5sum'] != $md5sum) {
+                $this->add_message($message['mailbox'], $object, !$message['exists']);
+            }
+
+            $this->icache['message']['md5sum'] = $md5sum;
+        }
+    }
+
+
+    /**
+     * Prepares message object to be stored in database.
+     */
+    private function message_object_prepare($msg)
+    {
+        // Remove body too big (>25kB)
+        if ($msg->body && strlen($msg->body) > 25 * 1024) {
+            unset($msg->body);
+        }
+
+        // Fix mimetype which might be broken by some code when message is displayed
+        // Another solution would be to use object's copy in rcube_message class
+        // to prevent related issues, however I'm not sure which is better
+        if ($msg->mimetype) {
+            list($msg->ctype_primary, $msg->ctype_secondary) = explode('/', $msg->mimetype);
+        }
+
+        if (is_array($msg->structure->parts)) {
+            foreach ($msg->structure->parts as $idx => $part) {
+                $msg->structure->parts[$idx] = $this->message_object_prepare($part);
+            }
+        }
+
+        return $msg;
+    }
+
+
+    /**
+     * Fetches index data from IMAP server
+     */
+    private function get_index_data($mailbox, $sort_field, $sort_order, $mbox_data = array())
+    {
+        $data = array();
+
+        if (empty($mbox_data)) {
+            $mbox_data = $this->imap->mailbox_data($mailbox);
+        }
+
+        // Prevent infinite loop.
+        // It happens when rcube_imap::message_index_direct() is called.
+        // There id2uid() is called which will again call get_index() and so on.
+        if (!$sort_field && !$this->skip_deleted)
+            $this->icache['pending_index_update'] = true;
+
+        if ($mbox_data['EXISTS']) {
+            // fetch sorted sequence numbers
+            $data_seq = $this->imap->message_index_direct($mailbox, $sort_field, $sort_order);
+            // fetch UIDs
+            if (!empty($data_seq)) {
+                // Seek in internal cache
+                if (array_key_exists('index', (array)$this->icache[$mailbox])
+                    && array_key_exists('result', (array)$this->icache[$mailbox]['index'])
+                )
+                    $data_uid = $this->icache[$mailbox]['index']['result'];
+                else
+                    $data_uid = $this->imap->conn->fetchUIDs($mailbox, $data_seq);
+
+                // build index
+                if (!empty($data_uid)) {
+                    foreach ($data_seq as $seq)
+                        if ($uid = $data_uid[$seq])
+                            $data[$seq] = $uid;
+                }
+            }
+        }
+
+        // Reset internal flags
+        $this->icache['pending_index_update'] = false;
+
+        return $data;
+    }
+}
index 7ae59bbd097a9aa7122a1ceeaa09de13dfd92b55..4c002fc14d5ef0a78ccbd2331cf2aaa62a493189 100644 (file)
@@ -6,6 +6,7 @@
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
  | Copyright (C) 2005-2010, The Roundcube Dev Team                       |
+ | Copyright (C) 2011, Kolab Systems AG                                  |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -21,7 +22,7 @@
  | Author: Ryo Chijiiwa <Ryo@IlohaMail.org>                              |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_imap_generic.php 5213 2011-09-13 08:09:50Z alec $
+ $Id: rcube_imap_generic.php 5402 2011-11-09 10:03:54Z alec $
 
 */
 
@@ -48,23 +49,14 @@ class rcube_mail_header
     public $encoding;
     public $charset;
     public $ctype;
-    public $flags;
     public $timestamp;
-    public $body_structure;
+    public $bodystructure;
     public $internaldate;
     public $references;
     public $priority;
     public $mdn_to;
-    public $mdn_sent = false;
-    public $seen = false;
-    public $deleted = false;
-    public $answered = false;
-    public $forwarded = false;
-    public $flagged = false;
-    public $has_children = false;
-    public $depth = 0;
-    public $unread_children = 0;
     public $others = array();
+    public $flags = array();
 }
 
 // For backward compatibility with cached messages (#1486602)
@@ -84,6 +76,7 @@ class rcube_imap_generic
     public $errornum;
     public $result;
     public $resultcode;
+    public $selected;
     public $data = array();
     public $flags = array(
         'SEEN'     => '\\Seen',
@@ -96,7 +89,6 @@ class rcube_imap_generic
         '*'        => '\\*',
     );
 
-    private $selected;
     private $fp;
     private $host;
     private $logged = false;
@@ -235,14 +227,14 @@ class rcube_imap_generic
         return $line;
     }
 
-    function multLine($line, $escape=false)
+    function multLine($line, $escape = false)
     {
         $line = rtrim($line);
-        if (preg_match('/\{[0-9]+\}$/', $line)) {
-            $out = '';
+        if (preg_match('/\{([0-9]+)\}$/', $line, $m)) {
+            $out   = '';
+            $str   = substr($line, 0, -strlen($m[0]));
+            $bytes = $m[1];
 
-            preg_match_all('/(.*)\{([0-9]+)\}$/', $line, $a);
-            $bytes = $a[2][0];
             while (strlen($out) < $bytes) {
                 $line = $this->readBytes($bytes);
                 if ($line === NULL)
@@ -250,7 +242,7 @@ class rcube_imap_generic
                 $out .= $line;
             }
 
-            $line = $a[1][0] . ($escape ? $this->escape($out) : $out);
+            $line = $str . ($escape ? $this->escape($out) : $out);
         }
 
         return $line;
@@ -318,6 +310,10 @@ class rcube_imap_generic
                 }
                 else {
                     $this->resultcode = null;
+                    // parse response for [APPENDUID 1204196876 3456]
+                    if (preg_match("/^\[APPENDUID [0-9]+ ([0-9,:*]+)\]/i", $str, $m)) {
+                        $this->data['APPENDUID'] = $m[1];
+                    }
                 }
                 $this->result = $str;
 
@@ -680,8 +676,8 @@ class rcube_imap_generic
             $this->prefs = $options;
         }
         // set auth method
-        if (!empty($this->prefs['auth_method'])) {
-            $auth_method = strtoupper($this->prefs['auth_method']);
+        if (!empty($this->prefs['auth_type'])) {
+            $auth_method = strtoupper($this->prefs['auth_type']);
         } else {
             $auth_method = 'CHECK';
         }
@@ -691,7 +687,7 @@ class rcube_imap_generic
         // initialize connection
         $this->error    = '';
         $this->errornum = self::ERROR_OK;
-        $this->selected = '';
+        $this->selected = null;
         $this->user     = $user;
         $this->host     = $host;
         $this->logged   = false;
@@ -877,18 +873,18 @@ class rcube_imap_generic
     /**
      * Executes SELECT command (if mailbox is already not in selected state)
      *
-     * @param string $mailbox Mailbox name
+     * @param string $mailbox      Mailbox name
+     * @param array  $qresync_data QRESYNC data (RFC5162)
      *
      * @return boolean True on success, false on error
-     * @access public
      */
-    function select($mailbox)
+    function select($mailbox, $qresync_data = null)
     {
         if (!strlen($mailbox)) {
             return false;
         }
 
-        if ($this->selected == $mailbox) {
+        if ($this->selected === $mailbox) {
             return true;
         }
 /*
@@ -901,7 +897,21 @@ class rcube_imap_generic
             }
         }
 */
-        list($code, $response) = $this->execute('SELECT', array($this->escape($mailbox)));
+        $params = array($this->escape($mailbox));
+
+        // QRESYNC data items
+        //    0. the last known UIDVALIDITY,
+        //    1. the last known modification sequence,
+        //    2. the optional set of known UIDs, and
+        //    3. an optional parenthesized list of known sequence ranges and their
+        //       corresponding UIDs.
+        if (!empty($qresync_data)) {
+            if (!empty($qresync_data[2]))
+                $qresync_data[2] = self::compressMessageSet($qresync_data[2]);
+            $params[] = array('QRESYNC', $qresync_data);
+        }
+
+        list($code, $response) = $this->execute('SELECT', $params);
 
         if ($code == self::ERROR_OK) {
             $response = explode("\r\n", $response);
@@ -909,11 +919,39 @@ class rcube_imap_generic
                 if (preg_match('/^\* ([0-9]+) (EXISTS|RECENT)$/i', $line, $m)) {
                     $this->data[strtoupper($m[2])] = (int) $m[1];
                 }
-                else if (preg_match('/^\* OK \[(UIDNEXT|UIDVALIDITY|UNSEEN) ([0-9]+)\]/i', $line, $match)) {
-                    $this->data[strtoupper($match[1])] = (int) $match[2];
+                else if (preg_match('/^\* OK \[/i', $line, $match)) {
+                    $line = substr($line, 6);
+                    if (preg_match('/^(UIDNEXT|UIDVALIDITY|UNSEEN) ([0-9]+)/i', $line, $match)) {
+                        $this->data[strtoupper($match[1])] = (int) $match[2];
+                    }
+                    else if (preg_match('/^(HIGHESTMODSEQ) ([0-9]+)/i', $line, $match)) {
+                        $this->data[strtoupper($match[1])] = (string) $match[2];
+                    }
+                    else if (preg_match('/^(NOMODSEQ)/i', $line, $match)) {
+                        $this->data[strtoupper($match[1])] = true;
+                    }
+                    else if (preg_match('/^PERMANENTFLAGS \(([^\)]+)\)/iU', $line, $match)) {
+                        $this->data['PERMANENTFLAGS'] = explode(' ', $match[1]);
+                    }
+                }
+                // QRESYNC FETCH response (RFC5162)
+                else if (preg_match('/^\* ([0-9+]) FETCH/i', $line, $match)) {
+                    $line       = substr($line, strlen($match[0]));
+                    $fetch_data = $this->tokenizeResponse($line, 1);
+                    $data       = array('id' => $match[1]);
+
+                    for ($i=0, $size=count($fetch_data); $i<$size; $i+=2) {
+                        $data[strtolower($fetch_data[$i])] = $fetch_data[$i+1];
+                    }
+
+                    $this->data['QRESYNC'][$data['uid']] = $data;
                 }
-                else if (preg_match('/^\* OK \[PERMANENTFLAGS \(([^\)]+)\)\]/iU', $line, $match)) {
-                    $this->data['PERMANENTFLAGS'] = explode(' ', $match[1]);
+                // QRESYNC VANISHED response (RFC5162)
+                else if (preg_match('/^\* VANISHED [()EARLIER]*/i', $line, $match)) {
+                    $line   = substr($line, strlen($match[0]));
+                    $v_data = $this->tokenizeResponse($line, 1);
+
+                    $this->data['VANISHED'] = $v_data;
                 }
             }
 
@@ -935,7 +973,6 @@ class rcube_imap_generic
      *                        in RFC3501: UIDNEXT, UIDVALIDITY, RECENT
      *
      * @return array Status item-value hash
-     * @access public
      * @since 0.5-beta
      */
     function status($mailbox, $items=array())
@@ -971,7 +1008,7 @@ class rcube_imap_generic
             }
 
             for ($i=0, $len=count($items); $i<$len; $i += 2) {
-                $result[$items[$i]] = (int) $items[$i+1];
+                $result[$items[$i]] = $items[$i+1];
             }
 
             $this->data['STATUS:'.$mailbox] = $result;
@@ -989,7 +1026,6 @@ class rcube_imap_generic
      * @param string $messages Message UIDs to expunge
      *
      * @return boolean True on success, False on error
-     * @access public
      */
     function expunge($mailbox, $messages=NULL)
     {
@@ -1011,7 +1047,7 @@ class rcube_imap_generic
             $result = $this->execute('EXPUNGE', null, self::COMMAND_NORESPONSE);
 
         if ($result == self::ERROR_OK) {
-            $this->selected = ''; // state has changed, need to reselect
+            $this->selected = null; // state has changed, need to reselect
             return true;
         }
 
@@ -1022,7 +1058,6 @@ class rcube_imap_generic
      * Executes CLOSE command
      *
      * @return boolean True on success, False on error
-     * @access public
      * @since 0.5
      */
     function close()
@@ -1030,7 +1065,7 @@ class rcube_imap_generic
         $result = $this->execute('CLOSE', NULL, self::COMMAND_NORESPONSE);
 
         if ($result == self::ERROR_OK) {
-            $this->selected = '';
+            $this->selected = null;
             return true;
         }
 
@@ -1043,7 +1078,6 @@ class rcube_imap_generic
      * @param string $mailbox Mailbox name
      *
      * @return boolean True on success, False on error
-     * @access public
      */
     function subscribe($mailbox)
     {
@@ -1059,7 +1093,6 @@ class rcube_imap_generic
      * @param string $mailbox Mailbox name
      *
      * @return boolean True on success, False on error
-     * @access public
      */
     function unsubscribe($mailbox)
     {
@@ -1075,7 +1108,6 @@ class rcube_imap_generic
      * @param string $mailbox Mailbox name
      *
      * @return boolean True on success, False on error
-     * @access public
      */
     function deleteFolder($mailbox)
     {
@@ -1091,7 +1123,6 @@ class rcube_imap_generic
      * @param string $mailbox Mailbox name
      *
      * @return boolean True on success, False on error
-     * @access public
      */
     function clearFolder($mailbox)
     {
@@ -1101,7 +1132,7 @@ class rcube_imap_generic
         }
 
         if ($res) {
-            if ($this->selected == $mailbox)
+            if ($this->selected === $mailbox)
                 $res = $this->close();
             else
                 $res = $this->expunge($mailbox);
@@ -1116,15 +1147,14 @@ class rcube_imap_generic
      * @param string $mailbox Mailbox name
      *
      * @return int Number of messages, False on error
-     * @access public
      */
     function countMessages($mailbox, $refresh = false)
     {
         if ($refresh) {
-            $this->selected = '';
+            $this->selected = null;
         }
 
-        if ($this->selected == $mailbox) {
+        if ($this->selected === $mailbox) {
             return $this->data['EXISTS'];
         }
 
@@ -1149,7 +1179,6 @@ class rcube_imap_generic
      * @param string $mailbox Mailbox name
      *
      * @return int Number of messages, False on error
-     * @access public
      */
     function countRecent($mailbox)
     {
@@ -1159,7 +1188,7 @@ class rcube_imap_generic
 
         $this->select($mailbox);
 
-        if ($this->selected == $mailbox) {
+        if ($this->selected === $mailbox) {
             return $this->data['RECENT'];
         }
 
@@ -1172,7 +1201,6 @@ class rcube_imap_generic
      * @param string $mailbox Mailbox name
      *
      * @return int Number of messages, False on error
-     * @access public
      */
     function countUnseen($mailbox)
     {
@@ -1203,7 +1231,6 @@ class rcube_imap_generic
      * @param array $items Client identification information key/value hash
      *
      * @return array Server identification information key/value hash
-     * @access public
      * @since 0.6
      */
     function id($items=array())
@@ -1235,6 +1262,37 @@ class rcube_imap_generic
         return false;
     }
 
+    /**
+     * Executes ENABLE command (RFC5161)
+     *
+     * @param mixed $extension Extension name to enable (or array of names)
+     *
+     * @return array|bool List of enabled extensions, False on error
+     * @since 0.6
+     */
+    function enable($extension)
+    {
+        if (empty($extension))
+            return false;
+
+        if (!$this->hasCapability('ENABLE'))
+            return false;
+
+        if (!is_array($extension))
+            $extension = array($extension);
+
+        list($code, $response) = $this->execute('ENABLE', $extension);
+
+        if ($code == self::ERROR_OK && preg_match('/\* ENABLED /i', $response)) {
+            $response = substr($response, 10); // remove prefix "* ENABLED "
+            $result   = (array) $this->tokenizeResponse($response);
+
+            return $result;
+        }
+
+        return false;
+    }
+
     function sort($mailbox, $field, $add='', $is_uid=FALSE, $encoding = 'US-ASCII')
     {
         $field = strtoupper($field);
@@ -1472,7 +1530,6 @@ class rcube_imap_generic
      * @param int    $uid     Message unique identifier (UID)
      *
      * @return int Message sequence identifier
-     * @access public
      */
     function UID2ID($mailbox, $uid)
     {
@@ -1492,12 +1549,11 @@ class rcube_imap_generic
      * @param int    $uid     Message sequence identifier
      *
      * @return int Message unique identifier
-     * @access public
      */
     function ID2UID($mailbox, $id)
     {
         if (empty($id) || $id < 0) {
-            return     null;
+            return null;
         }
 
         if (!$this->select($mailbox)) {
@@ -1515,47 +1571,59 @@ class rcube_imap_generic
 
     function fetchUIDs($mailbox, $message_set=null)
     {
-        if (is_array($message_set))
-            $message_set = join(',', $message_set);
-        else if (empty($message_set))
+        if (empty($message_set))
             $message_set = '1:*';
 
         return $this->fetchHeaderIndex($mailbox, $message_set, 'UID', false);
     }
 
-    function fetchHeaders($mailbox, $message_set, $uidfetch=false, $bodystr=false, $add='')
+    /**
+     * FETCH command (RFC3501)
+     *
+     * @param string $mailbox     Mailbox name
+     * @param mixed  $message_set Message(s) sequence identifier(s) or UID(s)
+     * @param bool   $is_uid      True if $message_set contains UIDs
+     * @param array  $query_items FETCH command data items
+     * @param string $mod_seq     Modification sequence for CHANGEDSINCE (RFC4551) query
+     * @param bool   $vanished    Enables VANISHED parameter (RFC5162) for CHANGEDSINCE query
+     *
+     * @return array List of rcube_mail_header elements, False on error
+     * @since 0.6
+     */
+    function fetch($mailbox, $message_set, $is_uid = false, $query_items = array(),
+        $mod_seq = null, $vanished = false)
     {
-        $result = array();
-
         if (!$this->select($mailbox)) {
             return false;
         }
 
         $message_set = $this->compressMessageSet($message_set);
+        $result      = array();
 
-        if ($add)
-            $add = ' '.trim($add);
-
-        /* FETCH uid, size, flags and headers */
         $key      = $this->nextTag();
-        $request  = $key . ($uidfetch ? ' UID' : '') . " FETCH $message_set ";
-        $request .= "(UID RFC822.SIZE FLAGS INTERNALDATE ";
-        if ($bodystr)
-            $request .= "BODYSTRUCTURE ";
-        $request .= "BODY.PEEK[HEADER.FIELDS (DATE FROM TO SUBJECT CONTENT-TYPE ";
-        $request .= "CC REPLY-TO LIST-POST DISPOSITION-NOTIFICATION-TO".$add.")])";
+        $request  = $key . ($is_uid ? ' UID' : '') . " FETCH $message_set ";
+        $request .= "(" . implode(' ', $query_items) . ")";
+
+        if ($mod_seq !== null && $this->hasCapability('CONDSTORE')) {
+            $request .= " (CHANGEDSINCE $mod_seq" . ($vanished ? " VANISHED" : '') .")";
+        }
 
         if (!$this->putLine($request)) {
             $this->setError(self::ERROR_COMMAND, "Unable to send command: $request");
             return false;
         }
+
         do {
             $line = $this->readLine(4096);
-            $line = $this->multLine($line);
 
             if (!$line)
                 break;
 
+            // Sample reply line:
+            // * 321 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen)
+            // INTERNALDATE "16-Nov-2008 21:08:46 +0100" BODYSTRUCTURE (...)
+            // BODY[HEADER.FIELDS ...
+
             if (preg_match('/^\* ([0-9]+) FETCH/', $line, $m)) {
                 $id = intval($m[1]);
 
@@ -1565,101 +1633,91 @@ class rcube_imap_generic
                 $result[$id]->messageID = 'mid:' . $id;
 
                 $lines = array();
-                $ln = 0;
-
-                // Sample reply line:
-                // * 321 FETCH (UID 2417 RFC822.SIZE 2730 FLAGS (\Seen)
-                // INTERNALDATE "16-Nov-2008 21:08:46 +0100" BODYSTRUCTURE (...)
-                // BODY[HEADER.FIELDS ...
+                $line  = substr($line, strlen($m[0]) + 2);
+                $ln    = 0;
 
-                if (preg_match('/^\* [0-9]+ FETCH \((.*) BODY/sU', $line, $matches)) {
-                    $str = $matches[1];
+                // get complete entry
+                while (preg_match('/\{([0-9]+)\}\r\n$/', $line, $m)) {
+                    $bytes = $m[1];
+                    $out   = '';
 
-                    while (list($name, $value) = $this->tokenizeResponse($str, 2)) {
-                        if ($name == 'UID') {
-                            $result[$id]->uid = intval($value);
-                        }
-                        else if ($name == 'RFC822.SIZE') {
-                            $result[$id]->size = intval($value);
-                        }
-                        else if ($name == 'INTERNALDATE') {
-                            $result[$id]->internaldate = $value;
-                            $result[$id]->date         = $value;
-                            $result[$id]->timestamp    = $this->StrToTime($value);
-                        }
-                        else if ($name == 'FLAGS') {
-                            $flags_a = $value;
-                        }
+                    while (strlen($out) < $bytes) {
+                        $out = $this->readBytes($bytes);
+                        if ($out === NULL)
+                            break;
+                        $line .= $out;
                     }
 
-                    // BODYSTRUCTURE
-                    if ($bodystr) {
-                        while (!preg_match('/ BODYSTRUCTURE (.*) BODY\[HEADER.FIELDS/sU', $line, $m)) {
-                            $line2 = $this->readLine(1024);
-                            $line .= $this->multLine($line2, true);
-                        }
-                        $result[$id]->body_structure = $m[1];
+                    $str = $this->readLine(4096);
+                    if ($str === false)
+                        break;
+
+                    $line .= $str;
+                }
+
+                // Tokenize response and assign to object properties
+                while (list($name, $value) = $this->tokenizeResponse($line, 2)) {
+                    if ($name == 'UID') {
+                        $result[$id]->uid = intval($value);
+                    }
+                    else if ($name == 'RFC822.SIZE') {
+                        $result[$id]->size = intval($value);
                     }
+                    else if ($name == 'RFC822.TEXT') {
+                        $result[$id]->body = $value;
+                    }
+                    else if ($name == 'INTERNALDATE') {
+                        $result[$id]->internaldate = $value;
+                        $result[$id]->date         = $value;
+                        $result[$id]->timestamp    = $this->StrToTime($value);
+                    }
+                    else if ($name == 'FLAGS') {
+                        if (!empty($value)) {
+                            foreach ((array)$value as $flag) {
+                                $flag = str_replace(array('$', '\\'), '', $flag);
+                                $flag = strtoupper($flag);
 
-                    // the rest of the result
-                    if (preg_match('/ BODY\[HEADER.FIELDS \(.*?\)\]\s*(.*)$/s', $line, $m)) {
-                        $reslines = explode("\n", trim($m[1], '"'));
-                        // re-parse (see below)
-                        foreach ($reslines as $resln) {
-                            if (ord($resln[0])<=32) {
-                                $lines[$ln] .= (empty($lines[$ln])?'':"\n").trim($resln);
-                            } else {
-                                $lines[++$ln] = trim($resln);
+                                $result[$id]->flags[$flag] = true;
                             }
                         }
                     }
-                }
-
-                // Start parsing headers.  The problem is, some header "lines" take up multiple lines.
-                // So, we'll read ahead, and if the one we're reading now is a valid header, we'll
-                // process the previous line.  Otherwise, we'll keep adding the strings until we come
-                // to the next valid header line.
-
-                do {
-                    $line = rtrim($this->readLine(300), "\r\n");
-
-                    // The preg_match below works around communigate imap, which outputs " UID <number>)".
-                    // Without this, the while statement continues on and gets the "FH0 OK completed" message.
-                    // If this loop gets the ending message, then the outer loop does not receive it from radline on line 1249.
-                    // This in causes the if statement on line 1278 to never be true, which causes the headers to end up missing
-                    // If the if statement was changed to pick up the fh0 from this loop, then it causes the outer loop to spin
-                    // An alternative might be:
-                    // if (!preg_match("/:/",$line) && preg_match("/\)$/",$line)) break;
-                    // however, unsure how well this would work with all imap clients.
-                    if (preg_match("/^\s*UID [0-9]+\)$/", $line)) {
-                        break;
+                    else if ($name == 'MODSEQ') {
+                        $result[$id]->modseq = $value[0];
                     }
-
-                    // handle FLAGS reply after headers (AOL, Zimbra?)
-                    if (preg_match('/\s+FLAGS \((.*)\)\)$/', $line, $matches)) {
-                        $flags_a = $this->tokenizeResponse($matches[1]);
-                        break;
+                    else if ($name == 'ENVELOPE') {
+                        $result[$id]->envelope = $value;
                     }
-
-                    if (ord($line[0])<=32) {
-                        $lines[$ln] .= (empty($lines[$ln])?'':"\n").trim($line);
-                    } else {
-                        $lines[++$ln] = trim($line);
+                    else if ($name == 'BODYSTRUCTURE' || ($name == 'BODY' && count($value) > 2)) {
+                        if (!is_array($value[0]) && (strtolower($value[0]) == 'message' && strtolower($value[1]) == 'rfc822')) {
+                            $value = array($value);
+                        }
+                        $result[$id]->bodystructure = $value;
                     }
-                // patch from "Maksim Rubis" <siburny@hotmail.com>
-                } while ($line[0] != ')' && !$this->startsWith($line, $key, true));
-
-                if (strncmp($line, $key, strlen($key))) {
-                    // process header, fill rcube_mail_header obj.
-                    // initialize
-                    if (is_array($headers)) {
-                        reset($headers);
-                        while (list($k, $bar) = each($headers)) {
-                            $headers[$k] = '';
+                    else if ($name == 'RFC822') {
+                        $result[$id]->body = $value;
+                    }
+                    else if ($name == 'BODY') {
+                        $body = $this->tokenizeResponse($line, 1);
+                        if ($value[0] == 'HEADER.FIELDS')
+                            $headers = $body;
+                        else if (!empty($value))
+                            $result[$id]->bodypart[$value[0]] = $body;
+                        else
+                            $result[$id]->body = $body;
+                    }
+                }
+
+                // create array with header field:data
+                if (!empty($headers)) {
+                    $headers = explode("\n", trim($headers));
+                    foreach ($headers as $hid => $resln) {
+                        if (ord($resln[0]) <= 32) {
+                            $lines[$ln] .= (empty($lines[$ln]) ? '' : "\n") . trim($resln);
+                        } else {
+                            $lines[++$ln] = trim($resln);
                         }
                     }
 
-                    // create array with header field:data
                     while (list($lines_key, $str) = each($lines)) {
                         list($field, $string) = explode(':', $str, 2);
 
@@ -1723,47 +1781,44 @@ class rcube_imap_generic
                                 $result[$id]->others[$field] = $string;
                             }
                             break;
-                        } // end switch ()
-                    } // end while ()
-                }
-
-                // process flags
-                if (!empty($flags_a)) {
-                    foreach ($flags_a as $flag) {
-                        $flag = str_replace('\\', '', $flag);
-                        $result[$id]->flags[] = $flag;
-
-                        switch (strtoupper($flag)) {
-                        case 'SEEN':
-                            $result[$id]->seen = true;
-                            break;
-                        case 'DELETED':
-                            $result[$id]->deleted = true;
-                            break;
-                        case 'ANSWERED':
-                            $result[$id]->answered = true;
-                            break;
-                        case '$FORWARDED':
-                            $result[$id]->forwarded = true;
-                            break;
-                        case '$MDNSENT':
-                            $result[$id]->mdn_sent = true;
-                            break;
-                        case 'FLAGGED':
-                            $result[$id]->flagged = true;
-                            break;
                         }
                     }
                 }
             }
+
+            // VANISHED response (QRESYNC RFC5162)
+            // Sample: * VANISHED (EARLIER) 300:310,405,411
+
+            else if (preg_match('/^\* VANISHED [()EARLIER]*/i', $line, $match)) {
+                $line   = substr($line, strlen($match[0]));
+                $v_data = $this->tokenizeResponse($line, 1);
+
+                $this->data['VANISHED'] = $v_data;
+            }
+
         } while (!$this->startsWith($line, $key, true));
 
         return $result;
     }
 
+    function fetchHeaders($mailbox, $message_set, $is_uid = false, $bodystr = false, $add = '')
+    {
+        $query_items = array('UID', 'RFC822.SIZE', 'FLAGS', 'INTERNALDATE');
+        if ($bodystr)
+            $query_items[] = 'BODYSTRUCTURE';
+        $query_items[] = 'BODY.PEEK[HEADER.FIELDS ('
+            . 'DATE FROM TO SUBJECT CONTENT-TYPE CC REPLY-TO LIST-POST DISPOSITION-NOTIFICATION-TO X-PRIORITY'
+            . ($add ? ' ' . trim($add) : '')
+            . ')]';
+
+        $result = $this->fetch($mailbox, $message_set, $is_uid, $query_items);
+
+        return $result;
+    }
+
     function fetchHeader($mailbox, $id, $uidfetch=false, $bodystr=false, $add='')
     {
-        $a  = $this->fetchHeaders($mailbox, $id, $uidfetch, $bodystr, $add);
+        $a = $this->fetchHeaders($mailbox, $id, $uidfetch, $bodystr, $add);
         if (is_array($a)) {
             return array_shift($a);
         }
@@ -2043,6 +2098,7 @@ class rcube_imap_generic
             $params .= 'RETURN (' . implode(' ', $items) . ')';
         }
         if (!empty($criteria)) {
+            $modseq = stripos($criteria, 'MODSEQ') !== false;
             $params .= ($params ? ' ' : '') . $criteria;
         }
         else {
@@ -2054,20 +2110,27 @@ class rcube_imap_generic
 
         if ($code == self::ERROR_OK) {
             // remove prefix...
-            $response = substr($response, stripos($response, 
+            $response = substr($response, stripos($response,
                 $esearch ? '* ESEARCH' : '* SEARCH') + ($esearch ? 10 : 9));
             // ...and unilateral untagged server responses
             if ($pos = strpos($response, '*')) {
                 $response = rtrim(substr($response, 0, $pos));
             }
 
+            // remove MODSEQ response
+            if ($modseq) {
+                if (preg_match('/\(MODSEQ ([0-9]+)\)$/', $response, $m)) {
+                    $response = substr($response, 0, -strlen($m[0]));
+                }
+            }
+
             if ($esearch) {
                 // Skip prefix: ... (TAG "A285") UID ...
                 $this->tokenizeResponse($response, $return_uid ? 2 : 1);
 
                 $result = array();
                 for ($i=0; $i<count($items); $i++) {
-                    // If the SEARCH results in no matches, the server MUST NOT
+                    // If the SEARCH returns no matches, the server MUST NOT
                     // include the item result option in the ESEARCH response
                     if ($ret = $this->tokenizeResponse($response, 2)) {
                         list ($name, $value) = $ret;
@@ -2116,7 +2179,6 @@ class rcube_imap_generic
      *
      * @return array List of mailboxes or hash of options if $status_opts argument
      *               is non-empty.
-     * @access public
      */
     function listMailboxes($ref, $mailbox, $status_opts=array(), $select_opts=array())
     {
@@ -2132,7 +2194,6 @@ class rcube_imap_generic
      *
      * @return array List of mailboxes or hash of options if $status_opts argument
      *               is non-empty.
-     * @access public
      */
     function listSubscribed($ref, $mailbox, $status_opts=array())
     {
@@ -2152,7 +2213,6 @@ class rcube_imap_generic
      *
      * @return array List of mailboxes or hash of options if $status_ops argument
      *               is non-empty.
-     * @access private
      */
     private function _listMailboxes($ref, $mailbox, $subscribed=false,
         $status_opts=array(), $select_opts=array())
@@ -2182,12 +2242,29 @@ class rcube_imap_generic
         list($code, $response) = $this->execute($subscribed ? 'LSUB' : 'LIST', $args);
 
         if ($code == self::ERROR_OK) {
-            $folders = array();
-            while ($this->tokenizeResponse($response, 1) == '*') {
-                $cmd = strtoupper($this->tokenizeResponse($response, 1));
+            $folders  = array();
+            $last     = 0;
+            $pos      = 0;
+            $response .= "\r\n";
+
+            while ($pos = strpos($response, "\r\n", $pos+1)) {
+                // literal string, not real end-of-command-line
+                if ($response[$pos-1] == '}') {
+                    continue;
+                }
+
+                $line = substr($response, $last, $pos - $last);
+                $last = $pos + 2;
+
+                if (!preg_match('/^\* (LIST|LSUB|STATUS) /i', $line, $m)) {
+                    continue;
+                }
+                $cmd  = strtoupper($m[1]);
+                $line = substr($line, strlen($m[0]));
+
                 // * LIST (<options>) <delimiter> <mailbox>
                 if ($cmd == 'LIST' || $cmd == 'LSUB') {
-                    list($opts, $delim, $mailbox) = $this->tokenizeResponse($response, 3);
+                    list($opts, $delim, $mailbox) = $this->tokenizeResponse($line, 3);
 
                     // Add to result array
                     if (!$lstatus) {
@@ -2198,31 +2275,21 @@ class rcube_imap_generic
                     }
 
                     // Add to options array
-                    if (!empty($opts)) {
-                        if (empty($this->data['LIST'][$mailbox]))
-                            $this->data['LIST'][$mailbox] = $opts;
-                        else
-                            $this->data['LIST'][$mailbox] = array_unique(array_merge(
-                                $this->data['LIST'][$mailbox], $opts));
-                    }
+                    if (empty($this->data['LIST'][$mailbox]))
+                        $this->data['LIST'][$mailbox] = $opts;
+                    else if (!empty($opts))
+                        $this->data['LIST'][$mailbox] = array_unique(array_merge(
+                            $this->data['LIST'][$mailbox], $opts));
                 }
                 // * STATUS <mailbox> (<result>)
                 else if ($cmd == 'STATUS') {
-                    list($mailbox, $status) = $this->tokenizeResponse($response, 2);
+                    list($mailbox, $status) = $this->tokenizeResponse($line, 2);
 
                     for ($i=0, $len=count($status); $i<$len; $i += 2) {
                         list($name, $value) = $this->tokenizeResponse($status, 2);
                         $folders[$mailbox][$name] = $value;
                     }
                 }
-                // other untagged response line, skip it
-                else {
-                    $response = ltrim($response);
-                    if (($position = strpos($response, "\n")) !== false)
-                        $response = substr($response, $position+1);
-                    else
-                        $response = '';
-                }
             }
 
             return $folders;
@@ -2231,7 +2298,7 @@ class rcube_imap_generic
         return false;
     }
 
-    function fetchMIMEHeaders($mailbox, $id, $parts, $mime=true)
+    function fetchMIMEHeaders($mailbox, $uid, $parts, $mime=true)
     {
         if (!$this->select($mailbox)) {
             return false;
@@ -2240,16 +2307,15 @@ class rcube_imap_generic
         $result = false;
         $parts  = (array) $parts;
         $key    = $this->nextTag();
-        $peeks  = '';
-        $idx    = 0;
+        $peeks  = array();
         $type   = $mime ? 'MIME' : 'HEADER';
 
         // format request
-        foreach($parts as $part) {
+        foreach ($parts as $part) {
             $peeks[] = "BODY.PEEK[$part.$type]";
         }
 
-        $request = "$key FETCH $id (" . implode(' ', $peeks) . ')';
+        $request = "$key UID FETCH $uid (" . implode(' ', $peeks) . ')';
 
         // send request
         if (!$this->putLine($request)) {
@@ -2259,13 +2325,25 @@ class rcube_imap_generic
 
         do {
             $line = $this->readLine(1024);
-            $line = $this->multLine($line);
 
-            if (preg_match('/BODY\[([0-9\.]+)\.'.$type.'\]/', $line, $matches)) {
-                $idx = $matches[1];
-                $result[$idx] = preg_replace('/^(\* '.$id.' FETCH \()?\s*BODY\['.$idx.'\.'.$type.'\]\s+/', '', $line);
-                $result[$idx] = trim($result[$idx], '"');
-                $result[$idx] = rtrim($result[$idx], "\t\r\n\0\x0B");
+            if (preg_match('/^\* [0-9]+ FETCH [0-9UID( ]+BODY\[([0-9\.]+)\.'.$type.'\]/', $line, $matches)) {
+                $idx     = $matches[1];
+                $headers = '';
+
+                // get complete entry
+                if (preg_match('/\{([0-9]+)\}\r\n$/', $line, $m)) {
+                    $bytes = $m[1];
+                    $out   = '';
+
+                    while (strlen($out) < $bytes) {
+                        $out = $this->readBytes($bytes);
+                        if ($out === null)
+                            break;
+                        $headers .= $out;
+                    }
+                }
+
+                $result[$idx] = trim($headers);
             }
         } while (!$this->startsWith($line, $key, true));
 
@@ -2322,8 +2400,10 @@ class rcube_imap_generic
         $len    = strlen($line);
         $result = false;
 
+        if ($a[2] != 'FETCH') {
+        }
         // handle empty "* X FETCH ()" response
-        if ($line[$len-1] == ')' && $line[$len-2] != '(') {
+        else if ($line[$len-1] == ')' && $line[$len-2] != '(') {
             // one line response, get everything between first and last quotes
             if (substr($line, -4, 3) == 'NIL') {
                 // NIL response
@@ -2442,8 +2522,18 @@ class rcube_imap_generic
         return ($result == self::ERROR_OK);
     }
 
+    /**
+     * Handler for IMAP APPEND command
+     *
+     * @param string $mailbox Mailbox name
+     * @param string $message Message content
+     *
+     * @return string|bool On success APPENDUID response (if available) or True, False on failure
+     */
     function append($mailbox, &$message)
     {
+        unset($this->data['APPENDUID']);
+
         if (!$mailbox) {
             return false;
         }
@@ -2482,7 +2572,12 @@ class rcube_imap_generic
             // Clear internal status cache
             unset($this->data['STATUS:'.$mailbox]);
 
-            return ($this->parseResult($line, 'APPEND: ') == self::ERROR_OK);
+            if ($this->parseResult($line, 'APPEND: ') != self::ERROR_OK)
+                return false;
+            else if (!empty($this->data['APPENDUID']))
+                return $this->data['APPENDUID'];
+            else
+                return true;
         }
         else {
             $this->setError(self::ERROR_COMMAND, "Unable to send command: $request");
@@ -2491,8 +2586,19 @@ class rcube_imap_generic
         return false;
     }
 
+    /**
+     * Handler for IMAP APPEND command.
+     *
+     * @param string $mailbox Mailbox name
+     * @param string $path    Path to the file with message body
+     * @param string $headers Message headers
+     *
+     * @return string|bool On success APPENDUID response (if available) or True, False on failure
+     */
     function appendFromFile($mailbox, $path, $headers=null)
     {
+        unset($this->data['APPENDUID']);
+
         if (!$mailbox) {
             return false;
         }
@@ -2559,7 +2665,12 @@ class rcube_imap_generic
             // Clear internal status cache
             unset($this->data['STATUS:'.$mailbox]);
 
-            return ($this->parseResult($line, 'APPEND: ') == self::ERROR_OK);
+            if ($this->parseResult($line, 'APPEND: ') != self::ERROR_OK)
+                return false;
+            else if (!empty($this->data['APPENDUID']))
+                return $this->data['APPENDUID'];
+            else
+                return true;
         }
         else {
             $this->setError(self::ERROR_COMMAND, "Unable to send command: $request");
@@ -2568,33 +2679,6 @@ class rcube_imap_generic
         return false;
     }
 
-    function fetchStructureString($mailbox, $id, $is_uid=false)
-    {
-        if (!$this->select($mailbox)) {
-            return false;
-        }
-
-        $key = $this->nextTag();
-        $result = false;
-        $command = $key . ($is_uid ? ' UID' : '') ." FETCH $id (BODYSTRUCTURE)";
-
-        if ($this->putLine($command)) {
-            do {
-                $line = $this->readLine(5000);
-                $line = $this->multLine($line, true);
-                if (!preg_match("/^$key /", $line))
-                    $result .= $line;
-            } while (!$this->startsWith($line, $key, true, true));
-
-            $result = trim(substr($result, strpos($result, 'BODYSTRUCTURE')+13, -1));
-        }
-        else {
-            $this->setError(self::ERROR_COMMAND, "Unable to send command: $command");
-        }
-
-        return $result;
-    }
-
     function getQuota()
     {
         /*
@@ -2658,7 +2742,6 @@ class rcube_imap_generic
      *
      * @return boolean True on success, False on failure
      *
-     * @access public
      * @since 0.5-beta
      */
     function setACL($mailbox, $user, $acl)
@@ -2682,7 +2765,6 @@ class rcube_imap_generic
      *
      * @return boolean True on success, False on failure
      *
-     * @access public
      * @since 0.5-beta
      */
     function deleteACL($mailbox, $user)
@@ -2700,7 +2782,6 @@ class rcube_imap_generic
      * @param string $mailbox Mailbox name
      *
      * @return array User-rights array on success, NULL on error
-     * @access public
      * @since 0.5-beta
      */
     function getACL($mailbox)
@@ -2741,7 +2822,6 @@ class rcube_imap_generic
      * @param string $user    User name
      *
      * @return array List of user rights
-     * @access public
      * @since 0.5-beta
      */
     function listRights($mailbox, $user)
@@ -2773,7 +2853,6 @@ class rcube_imap_generic
      * @param string $mailbox Mailbox name
      *
      * @return array MYRIGHTS response on success, NULL on error
-     * @access public
      * @since 0.5-beta
      */
     function myRights($mailbox)
@@ -2800,7 +2879,6 @@ class rcube_imap_generic
      * @param array  $entries Entry-value array (use NULL value as NIL)
      *
      * @return boolean True on success, False on failure
-     * @access public
      * @since 0.5-beta
      */
     function setMetadata($mailbox, $entries)
@@ -2830,7 +2908,6 @@ class rcube_imap_generic
      *
      * @return boolean True on success, False on failure
      *
-     * @access public
      * @since 0.5-beta
      */
     function deleteMetadata($mailbox, $entries)
@@ -2860,7 +2937,6 @@ class rcube_imap_generic
      *
      * @return array GETMETADATA result on success, NULL on error
      *
-     * @access public
      * @since 0.5-beta
      */
     function getMetadata($mailbox, $entries, $options=array())
@@ -2952,7 +3028,6 @@ class rcube_imap_generic
      *                        three elements: entry name, attribute name, value
      *
      * @return boolean True on success, False on failure
-     * @access public
      * @since 0.5-beta
      */
     function setAnnotation($mailbox, $data)
@@ -2984,7 +3059,6 @@ class rcube_imap_generic
      *
      * @return boolean True on success, False on failure
      *
-     * @access public
      * @since 0.5-beta
      */
     function deleteAnnotation($mailbox, $data)
@@ -3006,7 +3080,6 @@ class rcube_imap_generic
      *
      * @return array Annotations result on success, NULL on error
      *
-     * @access public
      * @since 0.5-beta
      */
     function getAnnotation($mailbox, $entries, $attribs)
@@ -3090,11 +3163,104 @@ class rcube_imap_generic
         return NULL;
     }
 
+    /**
+     * Returns BODYSTRUCTURE for the specified message.
+     *
+     * @param string $mailbox Folder name
+     * @param int    $id      Message sequence number or UID
+     * @param bool   $is_uid  True if $id is an UID
+     *
+     * @return array/bool Body structure array or False on error.
+     * @since 0.6
+     */
+    function getStructure($mailbox, $id, $is_uid = false)
+    {
+        $result = $this->fetch($mailbox, $id, $is_uid, array('BODYSTRUCTURE'));
+        if (is_array($result)) {
+            $result = array_shift($result);
+            return $result->bodystructure;
+        }
+        return false;
+    }
+
+    /**
+     * Returns data of a message part according to specified structure.
+     *
+     * @param array  $structure Message structure (getStructure() result)
+     * @param string $part      Message part identifier
+     *
+     * @return array Part data as hash array (type, encoding, charset, size)
+     */
+    static function getStructurePartData($structure, $part)
+    {
+           $part_a = self::getStructurePartArray($structure, $part);
+           $data   = array();
+
+           if (empty($part_a)) {
+            return $data;
+        }
+
+        // content-type
+        if (is_array($part_a[0])) {
+            $data['type'] = 'multipart';
+        }
+        else {
+            $data['type'] = strtolower($part_a[0]);
+
+            // encoding
+            $data['encoding'] = strtolower($part_a[5]);
+
+            // charset
+            if (is_array($part_a[2])) {
+               while (list($key, $val) = each($part_a[2])) {
+                    if (strcasecmp($val, 'charset') == 0) {
+                        $data['charset'] = $part_a[2][$key+1];
+                        break;
+                    }
+                }
+            }
+        }
+
+        // size
+        $data['size'] = intval($part_a[6]);
+
+        return $data;
+    }
+
+    static function getStructurePartArray($a, $part)
+    {
+           if (!is_array($a)) {
+            return false;
+        }
+           if (strpos($part, '.') > 0) {
+                   $original_part = $part;
+                   $pos = strpos($part, '.');
+                   $rest = substr($original_part, $pos+1);
+                   $part = substr($original_part, 0, $pos);
+                   if ((strcasecmp($a[0], 'message') == 0) && (strcasecmp($a[1], 'rfc822') == 0)) {
+                           $a = $a[8];
+                   }
+                   return self::getStructurePartArray($a[$part-1], $rest);
+           }
+        else if ($part>0) {
+                   if (!is_array($a[0]) && (strcasecmp($a[0], 'message') == 0)
+                && (strcasecmp($a[1], 'rfc822') == 0)) {
+                           $a = $a[8];
+                   }
+                   if (is_array($a[$part-1]))
+                return $a[$part-1];
+                   else
+                return $a;
+           }
+        else if (($part == 0) || (empty($part))) {
+                   return $a;
+           }
+    }
+
     /**
      * Creates next command identifier (tag)
      *
      * @return string Command identifier
-     * @access public
      * @since 0.5-beta
      */
     function nextTag()
@@ -3113,7 +3279,6 @@ class rcube_imap_generic
      * @param int    $options   Execution options
      *
      * @return mixed Response code or list of response code and data
-     * @access public
      * @since 0.5-beta
      */
     function execute($command, $arguments=array(), $options=0)
@@ -3124,7 +3289,9 @@ class rcube_imap_generic
         $response = $noresp ? null : '';
 
         if (!empty($arguments)) {
-            $query .= ' ' . implode(' ', $arguments);
+            foreach ($arguments as $arg) {
+                $query .= ' ' . self::r_implode($arg);
+            }
         }
 
         // Send command
@@ -3171,7 +3338,6 @@ class rcube_imap_generic
      * @param int    $num  Number of tokens to return
      *
      * @return mixed Tokens array or string if $num=1
-     * @access public
      * @since 0.5-beta
      */
     static function tokenizeResponse(&$str, $num=0)
@@ -3192,7 +3358,7 @@ class rcube_imap_generic
                 if (!is_numeric(($bytes = substr($str, 1, $epos - 1)))) {
                     // error
                 }
-                $result[] = substr($str, $epos + 3, $bytes);
+                $result[] = $bytes ? substr($str, $epos + 3, $bytes) : '';
                 // Advance the string
                 $str = substr($str, $epos + 3 + $bytes);
                 break;
@@ -3221,28 +3387,25 @@ class rcube_imap_generic
 
             // Parenthesized list
             case '(':
+            case '[':
                 $str = substr($str, 1);
                 $result[] = self::tokenizeResponse($str);
                 break;
             case ')':
+            case ']':
                 $str = substr($str, 1);
                 return $result;
                 break;
 
             // String atom, number, NIL, *, %
             default:
-                // empty or one character
-                if ($str === '') {
+                // empty string
+                if ($str === '' || $str === null) {
                     break 2;
                 }
-                if (strlen($str) < 2) {
-                    $result[] = $str;
-                    $str = '';
-                    break;
-                }
 
-                // excluded chars: SP, CTL, )
-                if (preg_match('/^([^\x00-\x20\x29\x7F]+)/', $str, $m)) {
+                // excluded chars: SP, CTL, ), [, ]
+                if (preg_match('/^([^\x00-\x20\x29\x5B\x5D\x7F]+)/', $str, $m)) {
                     $result[] = $m[1] == 'NIL' ? NULL : $m[1];
                     $str = substr($str, strlen($m[1]));
                 }
@@ -3253,6 +3416,23 @@ class rcube_imap_generic
         return $num == 1 ? $result[0] : $result;
     }
 
+    static function r_implode($element)
+    {
+        $string = '';
+
+        if (is_array($element)) {
+            reset($element);
+            while (list($key, $value) = each($element)) {
+                $string .= ' ' . self::r_implode($value);
+            }
+        }
+        else {
+            return $element;
+        }
+
+        return '(' . trim($string) . ')';
+    }
+
     private function _xor($string, $string2)
     {
         $result = '';
@@ -3346,7 +3526,6 @@ class rcube_imap_generic
      *
      * @param   boolean $debug      New value for the debugging flag.
      *
-     * @access  public
      * @since   0.5-stable
      */
     function setDebug($debug, $handler = null)
@@ -3360,7 +3539,6 @@ class rcube_imap_generic
      *
      * @param   string  $message    Debug mesage text.
      *
-     * @access  private
      * @since   0.5-stable
      */
     private function debug($message)
index 60b90ec7d5af99687834e5fbd2971b4a8467e5d5..6801a40962ce4ec01df35beb63b481dd08ee33b4 100644 (file)
@@ -16,7 +16,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_json_output.php 5037 2011-08-09 18:40:42Z alec $
+ $Id: rcube_json_output.php 5227 2011-09-16 17:54:07Z thomasb $
 
 */
 
index 5db60c5dc12e560caa797f5465b6350548c67499..0cd179b823029a87dd4b04de629eeb65f128b844 100644 (file)
@@ -17,7 +17,7 @@
  |         Aleksander Machniak <machniak@kolabsys.com>                   |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_ldap.php 5261 2011-09-21 12:22:40Z alec $
+ $Id: rcube_ldap.php 5541 2011-12-04 17:05:42Z thomasb $
 
 */
 
@@ -53,8 +53,8 @@ class rcube_ldap extends rcube_addressbook
 
     private $base_dn = '';
     private $groups_base_dn = '';
-    private $group_cache = array();
-    private $group_members = array();
+    private $group_url = null;
+    private $cache;
 
     private $vlv_active = false;
     private $vlv_count = 0;
@@ -72,6 +72,9 @@ class rcube_ldap extends rcube_addressbook
     {
         $this->prop = $p;
 
+        if (isset($p['searchonly']))
+            $this->searchonly = $p['searchonly'];
+
         // check if groups are configured
         if (is_array($p['groups']) && count($p['groups'])) {
             $this->groups = true;
@@ -80,6 +83,11 @@ class rcube_ldap extends rcube_addressbook
                 $this->prop['member_attr'] = strtolower($p['groups']['member_attr']);
             else if (empty($p['member_attr']))
                 $this->prop['member_attr'] = 'member';
+            // set default name attribute to cn
+            if (empty($this->prop['groups']['name_attr']))
+                $this->prop['groups']['name_attr'] = 'cn';
+            if (empty($this->prop['groups']['scope']))
+                $this->prop['groups']['scope'] = 'sub';
         }
 
         // fieldmap property is given
@@ -125,6 +133,10 @@ class rcube_ldap extends rcube_addressbook
         $this->debug       = $debug;
         $this->mail_domain = $mail_domain;
 
+        // initialize cache
+        $rcmail = rcmail::get_instance();
+        $this->cache = $rcmail->get_cache('LDAP.' . asciiwords($this->prop['name']), 'db', 600);
+
         $this->_connect();
     }
 
@@ -153,12 +165,14 @@ class rcube_ldap extends rcube_addressbook
 
         foreach ($this->prop['hosts'] as $host)
         {
-            $host = idn_to_ascii(rcube_parse_host($host));
-            $this->_debug("C: Connect [$host".($this->prop['port'] ? ':'.$this->prop['port'] : '')."]");
+            $host     = idn_to_ascii(rcube_parse_host($host));
+            $hostname = $host.($this->prop['port'] ? ':'.$this->prop['port'] : '');
+
+            $this->_debug("C: Connect [$hostname]");
 
             if ($lc = @ldap_connect($host, $this->prop['port']))
             {
-                if ($this->prop['use_tls']===true)
+                if ($this->prop['use_tls'] === true)
                     if (!ldap_start_tls($lc))
                         continue;
 
@@ -167,90 +181,117 @@ class rcube_ldap extends rcube_addressbook
                 ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, $this->prop['ldap_version']);
                 $this->prop['host'] = $host;
                 $this->conn = $lc;
+
+                if (isset($this->prop['referrals']))
+                    ldap_set_option($lc, LDAP_OPT_REFERRALS, $this->prop['referrals']);
                 break;
             }
             $this->_debug("S: NOT OK");
         }
 
-        if (is_resource($this->conn))
-        {
-            $this->ready = true;
+        // See if the directory is writeable.
+        if ($this->prop['writable']) {
+            $this->readonly = false;
+        }
 
-            $bind_pass = $this->prop['bind_pass'];
-            $bind_user = $this->prop['bind_user'];
-            $bind_dn   = $this->prop['bind_dn'];
+        if (!is_resource($this->conn)) {
+            raise_error(array('code' => 100, 'type' => 'ldap',
+                'file' => __FILE__, 'line' => __LINE__,
+                'message' => "Could not connect to any LDAP server, last tried $hostname"), true);
 
-            $this->base_dn        = $this->prop['base_dn'];
-            $this->groups_base_dn = ($this->prop['groups']['base_dn']) ?
-                $this->prop['groups']['base_dn'] : $this->base_dn;
+            return false;
+        }
 
-            // User specific access, generate the proper values to use.
-            if ($this->prop['user_specific']) {
-                // No password set, use the session password
-                if (empty($bind_pass)) {
-                    $bind_pass = $RCMAIL->decrypt($_SESSION['password']);
-                }
+        $bind_pass = $this->prop['bind_pass'];
+        $bind_user = $this->prop['bind_user'];
+        $bind_dn   = $this->prop['bind_dn'];
 
-                // Get the pieces needed for variable replacement.
-                if ($fu = $RCMAIL->user->get_username())
-                  list($u, $d) = explode('@', $fu);
-                else
-                  $d = $this->mail_domain;
+        $this->base_dn        = $this->prop['base_dn'];
+        $this->groups_base_dn = ($this->prop['groups']['base_dn']) ?
+        $this->prop['groups']['base_dn'] : $this->base_dn;
 
-                $dc = 'dc='.strtr($d, array('.' => ',dc=')); // hierarchal domain string
+        // User specific access, generate the proper values to use.
+        if ($this->prop['user_specific']) {
+            // No password set, use the session password
+            if (empty($bind_pass)) {
+                $bind_pass = $RCMAIL->decrypt($_SESSION['password']);
+            }
 
-                $replaces = array('%dc' => $dc, '%d' => $d, '%fu' => $fu, '%u' => $u);
+            // Get the pieces needed for variable replacement.
+            if ($fu = $RCMAIL->user->get_username())
+                list($u, $d) = explode('@', $fu);
+            else
+                $d = $this->mail_domain;
 
-                if ($this->prop['search_base_dn'] && $this->prop['search_filter']) {
-                    // Search for the dn to use to authenticate
-                    $this->prop['search_base_dn'] = strtr($this->prop['search_base_dn'], $replaces);
-                    $this->prop['search_filter'] = strtr($this->prop['search_filter'], $replaces);
+            $dc = 'dc='.strtr($d, array('.' => ',dc=')); // hierarchal domain string
 
-                    $this->_debug("S: searching with base {$this->prop['search_base_dn']} for {$this->prop['search_filter']}");
+            $replaces = array('%dn' => '', '%dc' => $dc, '%d' => $d, '%fu' => $fu, '%u' => $u);
 
-                    $res = @ldap_search($this->conn, $this->prop['search_base_dn'], $this->prop['search_filter'], array('uid'));
-                    if ($res && ($entry = ldap_first_entry($this->conn, $res))) {
-                        $bind_dn = ldap_get_dn($this->conn, $entry);
+            if ($this->prop['search_base_dn'] && $this->prop['search_filter']) {
+                if (!empty($this->prop['search_bind_dn']) && !empty($this->prop['search_bind_pw'])) {
+                    $this->bind($this->prop['search_bind_dn'], $this->prop['search_bind_pw']);
+                }
 
-                        $this->_debug("S: search returned dn: $bind_dn");
+                // Search for the dn to use to authenticate
+                $this->prop['search_base_dn'] = strtr($this->prop['search_base_dn'], $replaces);
+                $this->prop['search_filter'] = strtr($this->prop['search_filter'], $replaces);
 
-                        if ($bind_dn) {
-                            $dn = ldap_explode_dn($bind_dn, 1);
-                            $replaces['%dn'] = $dn[0];
-                        }
+                $this->_debug("S: searching with base {$this->prop['search_base_dn']} for {$this->prop['search_filter']}");
+
+                $res = @ldap_search($this->conn, $this->prop['search_base_dn'], $this->prop['search_filter'], array('uid'));
+                if ($res) {
+                    if (($entry = ldap_first_entry($this->conn, $res))
+                        && ($bind_dn = ldap_get_dn($this->conn, $entry))
+                    ) {
+                        $this->_debug("S: search returned dn: $bind_dn");
+                        $dn = ldap_explode_dn($bind_dn, 1);
+                        $replaces['%dn'] = $dn[0];
                     }
                 }
-                // Replace the bind_dn and base_dn variables.
-                $bind_dn              = strtr($bind_dn, $replaces);
-                $this->base_dn        = strtr($this->base_dn, $replaces);
-                $this->groups_base_dn = strtr($this->groups_base_dn, $replaces);
+                else {
+                    $this->_debug("S: ".ldap_error($this->conn));
+                }
 
-                if (empty($bind_user)) {
-                    $bind_user = $u;
+                // DN not found
+                if (empty($replaces['%dn'])) {
+                    if (!empty($this->prop['search_dn_default']))
+                        $replaces['%dn'] = $this->prop['search_dn_default'];
+                    else {
+                        raise_error(array(
+                            'code' => 100, 'type' => 'ldap',
+                            'file' => __FILE__, 'line' => __LINE__,
+                            'message' => "DN not found using LDAP search."), true);
+                        return false;
+                    }
                 }
             }
 
-            if (!empty($bind_pass)) {
-                if (!empty($bind_dn)) {
-                    $this->ready = $this->_bind($bind_dn, $bind_pass);
-                }
-                else if (!empty($this->prop['auth_cid'])) {
-                    $this->ready = $this->_sasl_bind($this->prop['auth_cid'], $bind_pass, $bind_user);
-                }
-                else {
-                    $this->ready = $this->_sasl_bind($bind_user, $bind_pass);
-                }
+            // Replace the bind_dn and base_dn variables.
+            $bind_dn              = strtr($bind_dn, $replaces);
+            $this->base_dn        = strtr($this->base_dn, $replaces);
+            $this->groups_base_dn = strtr($this->groups_base_dn, $replaces);
+
+            if (empty($bind_user)) {
+                $bind_user = $u;
             }
         }
-        else
-            raise_error(array('code' => 100, 'type' => 'ldap',
-                'file' => __FILE__, 'line' => __LINE__,
-                'message' => "Could not connect to any LDAP server, last tried $host:{$this->prop[port]}"), true);
 
-        // See if the directory is writeable.
-        if ($this->prop['writable']) {
-            $this->readonly = false;
-        } // end if
+        if (empty($bind_pass)) {
+            $this->ready = true;
+        }
+        else {
+            if (!empty($bind_dn)) {
+                $this->ready = $this->bind($bind_dn, $bind_pass);
+            }
+            else if (!empty($this->prop['auth_cid'])) {
+                $this->ready = $this->sasl_bind($this->prop['auth_cid'], $bind_pass, $bind_user);
+            }
+            else {
+                $this->ready = $this->sasl_bind($bind_user, $bind_pass);
+            }
+        }
+
+        return $this->ready;
     }
 
 
@@ -263,7 +304,7 @@ class rcube_ldap extends rcube_addressbook
      *
      * @return boolean True on success, False on error
      */
-    private function _sasl_bind($authc, $pass, $authz=null)
+    public function sasl_bind($authc, $pass, $authz=null)
     {
         if (!$this->conn) {
             return false;
@@ -314,7 +355,7 @@ class rcube_ldap extends rcube_addressbook
      *
      * @return boolean True on success, False on error
      */
-    private function _bind($dn, $pass)
+    public function bind($dn, $pass)
     {
         if (!$this->conn) {
             return false;
@@ -429,59 +470,219 @@ class rcube_ldap extends rcube_addressbook
      */
     function list_records($cols=null, $subset=0)
     {
-        // add general filter to query
-        if (!empty($this->prop['filter']) && empty($this->filter))
+        if ($this->prop['searchonly'] && empty($this->filter) && !$this->group_id)
         {
-            $filter = $this->prop['filter'];
-            $this->set_search_set($filter);
+            $this->result = new rcube_result_set(0);
+            $this->result->searchonly = true;
+            return $this->result;
         }
 
-        // exec LDAP search if no result resource is stored
-        if ($this->conn && !$this->ldap_result)
-            $this->_exec_search();
+        // fetch group members recursively
+        if ($this->group_id && $this->group_data['dn'])
+        {
+            $entries = $this->list_group_members($this->group_data['dn']);
 
-        // count contacts for this user
-        $this->result = $this->count();
+            // make list of entries unique and sort it
+            $seen = array();
+            foreach ($entries as $i => $rec) {
+                if ($seen[$rec['dn']]++)
+                    unset($entries[$i]);
+            }
+            usort($entries, array($this, '_entry_sort_cmp'));
 
-        // we have a search result resource
-        if ($this->ldap_result && $this->result->count > 0)
+            $entries['count'] = count($entries);
+            $this->result = new rcube_result_set($entries['count'], ($this->list_page-1) * $this->page_size);
+        }
+        else
         {
-            // sorting still on the ldap server
-            if ($this->sort_col && $this->prop['scope'] !== 'base' && !$this->vlv_active)
-                ldap_sort($this->conn, $this->ldap_result, $this->sort_col);
+            // add general filter to query
+            if (!empty($this->prop['filter']) && empty($this->filter))
+                $this->set_search_set($this->prop['filter']);
 
-            // start and end of the page
-            $start_row = $this->vlv_active ? 0 : $this->result->first;
-            $start_row = $subset < 0 ? $start_row + $this->page_size + $subset : $start_row;
-            $last_row = $this->result->first + $this->page_size;
-            $last_row = $subset != 0 ? $start_row + abs($subset) : $last_row;
+            // exec LDAP search if no result resource is stored
+            if ($this->conn && !$this->ldap_result)
+                $this->_exec_search();
 
-            // get all entries from the ldap server
-            $entries = ldap_get_entries($this->conn, $this->ldap_result);
+            // count contacts for this user
+            $this->result = $this->count();
 
-            // filtering for group members
-            if ($this->groups and $this->group_id)
+            // we have a search result resource
+            if ($this->ldap_result && $this->result->count > 0)
             {
-                $count = 0;
-                $members = array();
-                foreach ($entries as $entry)
-                {
-                    if ($this->group_members[self::dn_encode($entry['dn'])])
-                    {
-                        $members[] = $entry;
-                        $count++;
-                    }
+                // sorting still on the ldap server
+                if ($this->sort_col && $this->prop['scope'] !== 'base' && !$this->vlv_active)
+                    ldap_sort($this->conn, $this->ldap_result, $this->sort_col);
+
+                // get all entries from the ldap server
+                $entries = ldap_get_entries($this->conn, $this->ldap_result);
+            }
+
+        }  // end else
+
+        // start and end of the page
+        $start_row = $this->vlv_active ? 0 : $this->result->first;
+        $start_row = $subset < 0 ? $start_row + $this->page_size + $subset : $start_row;
+        $last_row = $this->result->first + $this->page_size;
+        $last_row = $subset != 0 ? $start_row + abs($subset) : $last_row;
+
+        // filter entries for this page
+        for ($i = $start_row; $i < min($entries['count'], $last_row); $i++)
+            $this->result->add($this->_ldap2result($entries[$i]));
+
+        return $this->result;
+    }
+
+    /**
+     * Get all members of the given group
+     *
+     * @param string Group DN
+     * @param array  Group entries (if called recursively)
+     * @return array Accumulated group members
+     */
+    function list_group_members($dn, $count = false, $entries = null)
+    {
+        $group_members = array();
+
+        // fetch group object
+        if (empty($entries)) {
+            $result = @ldap_read($this->conn, $dn, '(objectClass=*)', array('dn','objectClass','member','uniqueMember','memberURL'));
+            if ($result === false)
+            {
+                $this->_debug("S: ".ldap_error($this->conn));
+                return $group_members;
+            }
+
+            $entries = @ldap_get_entries($this->conn, $result);
+        }
+
+        for ($i=0; $i < $entries["count"]; $i++)
+        {
+            $entry = $entries[$i];
+
+            if (empty($entry['objectclass']))
+                continue;
+
+            foreach ((array)$entry['objectclass'] as $objectclass)
+            {
+                switch (strtolower($objectclass)) {
+                    case "groupofnames":
+                    case "kolabgroupofnames":
+                        $group_members = array_merge($group_members, $this->_list_group_members($dn, $entry, 'member', $count));
+                        break;
+                    case "groupofuniquenames":
+                    case "kolabgroupofuniquenames":
+                        $group_members = array_merge($group_members, $this->_list_group_members($dn, $entry, 'uniquemember', $count));
+                        break;
+                    case "groupofurls":
+                        $group_members = array_merge($group_members, $this->_list_group_memberurl($dn, $entry, $count));
+                        break;
                 }
-                $entries = $members;
-                $entries['count'] = $count;
-                $this->result->count = $count;
             }
+            
+            if ($this->prop['sizelimit'] && count($group_members) > $this->prop['sizelimit'])
+              break;
+        }
+
+        return array_filter($group_members);
+    }
 
-            // filter entries for this page
-            for ($i = $start_row; $i < min($entries['count'], $last_row); $i++)
-                $this->result->add($this->_ldap2result($entries[$i]));
+    /**
+     * Fetch members of the given group entry from server
+     *
+     * @param string Group DN
+     * @param array  Group entry
+     * @param string Member attribute to use
+     * @return array Accumulated group members
+     */
+    private function _list_group_members($dn, $entry, $attr, $count)
+    {
+        // Use the member attributes to return an array of member ldap objects
+        // NOTE that the member attribute is supposed to contain a DN
+        $group_members = array();
+        if (empty($entry[$attr]))
+            return $group_members;
+
+        // read these attributes for all members
+        $attrib = $count ? array('dn') : array_values($this->fieldmap);
+        $attrib[] = 'objectClass';
+        $attrib[] = 'member';
+        $attrib[] = 'uniqueMember';
+        $attrib[] = 'memberURL';
+
+        for ($i=0; $i < $entry[$attr]['count']; $i++)
+        {
+            $result = @ldap_read($this->conn, $entry[$attr][$i], '(objectclass=*)',
+                $attrib, 0, (int)$this->prop['sizelimit'], (int)$this->prop['timelimit']);
+
+            $members = @ldap_get_entries($this->conn, $result);
+            if ($members == false)
+            {
+                $this->_debug("S: ".ldap_error($this->conn));
+                $members = array();
+            }
+
+            // for nested groups, call recursively
+            $nested_group_members = $this->list_group_members($entry[$attr][$i], $count, $members);
+
+            unset($members['count']);
+            $group_members = array_merge($group_members, array_filter($members), $nested_group_members);
         }
-        return $this->result;
+
+        return $group_members;
+    }
+
+    /**
+     * List members of group class groupOfUrls
+     *
+     * @param string Group DN
+     * @param array  Group entry
+     * @param boolean True if only used for counting
+     * @return array Accumulated group members
+     */
+    private function _list_group_memberurl($dn, $entry, $count)
+    {
+        $group_members = array();
+
+        for ($i=0; $i < $entry['memberurl']['count']; $i++)
+        {
+            // extract components from url
+            if (!preg_match('!ldap:///([^\?]+)\?\?(\w+)\?(.*)$!', $entry['memberurl'][$i], $m))
+                continue;
+
+            // add search filter if any
+            $filter = $this->filter ? '(&(' . $m[3] . ')(' . $this->filter . '))' : $m[3];
+            $func = $m[2] == 'sub' ? 'ldap_search' : ($m[2] == 'base' ? 'ldap_read' : 'ldap_list');
+
+            $attrib = $count ? array('dn') : array_values($this->fieldmap);
+            if ($result = @$func($this->conn, $m[1], $filter,
+                $attrib, 0, (int)$this->prop['sizelimit'], (int)$this->prop['timelimit'])
+            ) {
+                $this->_debug("S: ".ldap_count_entries($this->conn, $result)." record(s) for ".$m[1]);
+            }
+            else {
+                $this->_debug("S: ".ldap_error($this->conn));
+                return $group_members;
+            }
+
+            $entries = @ldap_get_entries($this->conn, $result);
+            for ($j = 0; $j < $entries['count']; $j++)
+            {
+                if ($nested_group_members = $this->list_group_members($entries[$j]['dn'], $count))
+                    $group_members = array_merge($group_members, $nested_group_members);
+                else
+                    $group_members[] = $entries[$j];
+            }
+        }
+
+        return $group_members;
+    }
+
+    /**
+     * Callback for sorting entries
+     */
+    function _entry_sort_cmp($a, $b)
+    {
+        return strcmp($a[$this->sort_col][0], $b[$this->sort_col][0]);
     }
 
 
@@ -490,15 +691,20 @@ class rcube_ldap extends rcube_addressbook
      *
      * @param mixed   $fields   The field name of array of field names to search in
      * @param mixed   $value    Search value (or array of values when $fields is array)
-     * @param boolean $strict   True for strict, False for partial (fuzzy) matching
+     * @param int     $mode     Matching mode:
+     *                          0 - partial (*abc*),
+     *                          1 - strict (=),
+     *                          2 - prefix (abc*)
      * @param boolean $select   True if results are requested, False if count only
      * @param boolean $nocount  (Not used)
      * @param array   $required List of fields that cannot be empty
      *
      * @return array  Indexed list of contact records and 'count' value
      */
-    function search($fields, $value, $strict=false, $select=true, $nocount=false, $required=array())
+    function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array())
     {
+        $mode = intval($mode);
+
         // special treatment for ID-based search
         if ($fields == 'ID' || $fields == $this->primary_key)
         {
@@ -515,9 +721,70 @@ class rcube_ldap extends rcube_addressbook
             return $result;
         }
 
+        // use VLV pseudo-search for autocompletion
+        if ($this->prop['vlv_search'] && $this->conn && join(',', (array)$fields) == 'email,name')
+        {
+            // add general filter to query
+            if (!empty($this->prop['filter']) && empty($this->filter))
+                $this->set_search_set($this->prop['filter']);
+
+            // set VLV controls with encoded search string
+            $this->_vlv_set_controls($this->prop, $this->list_page, $this->page_size, $value);
+
+            $function = $this->_scope2func($this->prop['scope']);
+            $this->ldap_result = @$function($this->conn, $this->base_dn, $this->filter ? $this->filter : '(objectclass=*)',
+                array_values($this->fieldmap), 0, $this->page_size, (int)$this->prop['timelimit']);
+
+            $this->result = new rcube_result_set(0);
+
+            if (!$this->ldap_result) {
+                $this->_debug("S: ".ldap_error($this->conn));
+                return $this->result;
+            }
+
+            $this->_debug("S: ".ldap_count_entries($this->conn, $this->ldap_result)." record(s)");
+
+            // get all entries of this page and post-filter those that really match the query
+            $search = mb_strtolower($value);
+            $entries = ldap_get_entries($this->conn, $this->ldap_result);
+
+            for ($i = 0; $i < $entries['count']; $i++) {
+                $rec = $this->_ldap2result($entries[$i]);
+                foreach (array('email', 'name') as $f) {
+                    $val = mb_strtolower($rec[$f]);
+                    switch ($mode) {
+                    case 1:
+                        $got = ($val == $search);
+                        break;
+                    case 2:
+                        $got = ($search == substr($val, 0, strlen($search)));
+                        break;
+                    default:
+                        $got = (strpos($val, $search) !== false);
+                        break;
+                    }
+
+                    if ($got) {
+                        $this->result->add($rec);
+                        $this->result->count++;
+                        break;
+                    }
+                }
+            }
+
+            return $this->result;
+        }
+
         // use AND operator for advanced searches
         $filter = is_array($value) ? '(&' : '(|';
-        $wc     = !$strict && $this->prop['fuzzy_search'] ? '*' : '';
+        // set wildcards
+        $wp = $ws = '';
+        if (!empty($this->prop['fuzzy_search']) && $mode != 1) {
+            $ws = '*';
+            if (!$mode) {
+                $wp = '*';
+            }
+        }
 
         if ($fields == '*')
         {
@@ -531,7 +798,7 @@ class rcube_ldap extends rcube_addressbook
             if (is_array($this->prop['search_fields']))
             {
                 foreach ($this->prop['search_fields'] as $field) {
-                    $filter .= "($field=$wc" . $this->_quote_string($value) . "$wc)";
+                    $filter .= "($field=$wp" . $this->_quote_string($value) . "$ws)";
                 }
             }
         }
@@ -540,7 +807,7 @@ class rcube_ldap extends rcube_addressbook
             foreach ((array)$fields as $idx => $field) {
                 $val = is_array($value) ? $value[$idx] : $value;
                 if ($f = $this->_map_field($field)) {
-                    $filter .= "($f=$wc" . $this->_quote_string($val) . "$wc)";
+                    $filter .= "($f=$wp" . $this->_quote_string($val) . "$ws)";
                 }
             }
         }
@@ -585,18 +852,21 @@ class rcube_ldap extends rcube_addressbook
         $count = 0;
         if ($this->conn && $this->ldap_result) {
             $count = $this->vlv_active ? $this->vlv_count : ldap_count_entries($this->conn, $this->ldap_result);
-        } // end if
-        elseif ($this->conn) {
+        }
+        else if ($this->group_id && $this->group_data['dn']) {
+            $count = count($this->list_group_members($this->group_data['dn'], true));
+        }
+        else if ($this->conn) {
             // We have a connection but no result set, attempt to get one.
             if (empty($this->filter)) {
                 // The filter is not set, set it.
                 $this->filter = $this->prop['filter'];
-            } // end if
+            }
             $this->_exec_search(true);
             if ($this->ldap_result) {
                 $count = ldap_count_entries($this->conn, $this->ldap_result);
-            } // end if
-        } // end else
+            }
+        }
 
         return new rcube_result_set($count, ($this->list_page-1) * $this->page_size);
     }
@@ -658,19 +928,49 @@ class rcube_ldap extends rcube_addressbook
      * If input not valid, the message to display can be fetched using get_error()
      *
      * @param array Assoziative array with data to save
-     *
+     * @param boolean Try to fix/complete record automatically
      * @return boolean True if input is valid, False if not.
      */
-    public function validate($save_data)
+    public function validate(&$save_data, $autofix = false)
     {
         // check for name input
         if (empty($save_data['name'])) {
-            $this->set_error('warning', 'nonamewarning');
+            $this->set_error(self::ERROR_VALIDATE, 'nonamewarning');
+            return false;
+        }
+
+        // Verify that the required fields are set.
+        $missing = null;
+        $ldap_data = $this->_map_data($save_data);
+        foreach ($this->prop['required_fields'] as $fld) {
+            if (!isset($ldap_data[$fld])) {
+                $missing[$fld] = 1;
+            }
+        }
+
+        if ($missing) {
+            // try to complete record automatically
+            if ($autofix) {
+                $reverse_map = array_flip($this->fieldmap);
+                $name_parts = preg_split('/[\s,.]+/', $save_data['name']);
+                if ($missing['sn']) {
+                    $sn_field = $reverse_map['sn'];
+                    $save_data[$sn_field] = array_pop ($name_parts);
+                }
+                if ($missing[($fn_field = $this->fieldmap['firstname'])]) {
+                    $save_data['firstname'] = array_shift($name_parts);
+                }
+
+                return $this->validate($save_data, false);
+            }
+
+            // TODO: generate message saying which fields are missing
+            $this->set_error(self::ERROR_VALIDATE, 'formincomplete');
             return false;
         }
 
         // validate e-mail addresses
-        return parent::validate($save_data);
+        return parent::validate($save_data, $autofix);
     }
 
 
@@ -684,17 +984,8 @@ class rcube_ldap extends rcube_addressbook
     function insert($save_cols)
     {
         // Map out the column names to their LDAP ones to build the new entry.
-        $newentry = array();
+        $newentry = $this->_map_data($save_cols);
         $newentry['objectClass'] = $this->prop['LDAP_Object_Classes'];
-        foreach ($this->fieldmap as $col => $fld) {
-            $val = $save_cols[$col];
-            if (is_array($val))
-                $val = array_filter($val);  // remove empty entries
-            if ($fld && $val) {
-                // The field does exist, add it to the entry.
-                $newentry[$fld] = $val;
-            } // end if
-        } // end foreach
 
         // Verify that the required fields are set.
         $missing = null;
@@ -707,7 +998,7 @@ class rcube_ldap extends rcube_addressbook
         // abort process if requiered fields are missing
         // TODO: generate message saying which fields are missing
         if ($missing) {
-            $this->set_error(self::ERROR_INCOMPLETE, 'formincomplete');
+            $this->set_error(self::ERROR_VALIDATE, 'formincomplete');
             return false;
         }
 
@@ -728,7 +1019,7 @@ class rcube_ldap extends rcube_addressbook
         $dn = self::dn_encode($dn);
 
         // add new contact to the selected group
-        if ($this->groups)
+        if ($this->group_id)
             $this->add_to_group($this->group_id, $dn);
 
         return $dn;
@@ -753,39 +1044,33 @@ class rcube_ldap extends rcube_addressbook
         $replacedata = array();
         $deletedata = array();
 
-        // flatten composite fields in $record
-        if (is_array($record['address'])) {
-          foreach ($record['address'] as $i => $struct) {
-            foreach ($struct as $col => $val) {
-              $record[$col][$i] = $val;
-            }
-          }
-        }
+        $ldap_data = $this->_map_data($save_cols);
+        $old_data = $record['_raw_attrib'];
 
         foreach ($this->fieldmap as $col => $fld) {
-            $val = $save_cols[$col];
+            $val = $ldap_data[$fld];
             if ($fld) {
                 // remove empty array values
                 if (is_array($val))
                     $val = array_filter($val);
                 // The field does exist compare it to the ldap record.
-                if ($record[$col] != $val) {
+                if ($old_data[$fld] != $val) {
                     // Changed, but find out how.
-                    if (!isset($record[$col])) {
+                    if (!isset($old_data[$fld])) {
                         // Field was not set prior, need to add it.
                         $newdata[$fld] = $val;
-                    } // end if
-                    elseif ($val == '') {
+                    }
+                    else if ($val == '') {
                         // Field supplied is empty, verify that it is not required.
                         if (!in_array($fld, $this->prop['required_fields'])) {
                             // It is not, safe to clear.
-                            $deletedata[$fld] = $record[$col];
-                        } // end if
+                            $deletedata[$fld] = $old_data[$fld];
+                        }
                     } // end elseif
                     else {
                         // The data was modified, save it out.
                         $replacedata[$fld] = $val;
-                    } // end else
+                    }
                 } // end if
             } // end if
         } // end foreach
@@ -917,14 +1202,14 @@ class rcube_ldap extends rcube_addressbook
         if ($this->ready)
         {
             $filter = $this->filter ? $this->filter : '(objectclass=*)';
-            $function = $this->prop['scope'] == 'sub' ? 'ldap_search' : ($this->prop['scope'] == 'base' ? 'ldap_read' : 'ldap_list');
+            $function = $this->_scope2func($this->prop['scope'], $ns_function);
 
-            $this->_debug("C: Search [$filter]");
+            $this->_debug("C: Search [$filter][dn: $this->base_dn]");
 
             // when using VLV, we get the total count by...
-            if (!$count && $function != 'ldap_read' && $this->prop['vlv']) {
+            if (!$count && $function != 'ldap_read' && $this->prop['vlv'] && !$this->group_id) {
                 // ...either reading numSubOrdinates attribute
-                if ($this->prop['numsub_filter'] && ($result_count = @$function($this->conn, $this->base_dn, $this->prop['numsub_filter'], array('numSubOrdinates'), 0, 0, 0))) {
+                if ($this->prop['numsub_filter'] && ($result_count = @$ns_function($this->conn, $this->base_dn, $this->prop['numsub_filter'], array('numSubOrdinates'), 0, 0, 0))) {
                     $counts = ldap_get_entries($this->conn, $result_count);
                     for ($this->vlv_count = $j = 0; $j < $counts['count']; $j++)
                         $this->vlv_count += $counts[$j]['numsubordinates'][0];
@@ -933,21 +1218,20 @@ class rcube_ldap extends rcube_addressbook
                 else  // ...or by fetching all records dn and count them
                     $this->vlv_count = $this->_exec_search(true);
 
-                $this->vlv_active = $this->_vlv_set_controls();
+                $this->vlv_active = $this->_vlv_set_controls($this->prop, $this->list_page, $this->page_size);
             }
 
             // only fetch dn for count (should keep the payload low)
             $attrs = $count ? array('dn') : array_values($this->fieldmap);
             if ($this->ldap_result = @$function($this->conn, $this->base_dn, $filter,
-                $attrs, 0, (int)$this->prop['sizelimit'], (int)$this->prop['timelimit']))
-            {
-                $this->_debug("S: ".ldap_count_entries($this->conn, $this->ldap_result)." record(s)");
-                if ($err = ldap_errno($this->conn))
-                    $this->_debug("S: Error: " .ldap_err2str($err));
-                return true;
+                $attrs, 0, (int)$this->prop['sizelimit'], (int)$this->prop['timelimit'])
+            {
+                $entries_count = ldap_count_entries($this->conn, $this->ldap_result);
+                $this->_debug("S: $count_entries record(s)");
+
+                return $count ? $count_entries : true;
             }
-            else
-            {
+            else {
                 $this->_debug("S: ".ldap_error($this->conn));
             }
         }
@@ -955,16 +1239,38 @@ class rcube_ldap extends rcube_addressbook
         return false;
     }
 
+    /**
+     * Choose the right PHP function according to scope property
+     */
+    private function _scope2func($scope, &$ns_function = null)
+    {
+        switch ($scope) {
+          case 'sub':
+            $function = $ns_function  = 'ldap_search';
+            break;
+          case 'base':
+            $function = $ns_function = 'ldap_read';
+            break;
+          default:
+            $function = 'ldap_list';
+            $ns_function = 'ldap_read';
+            break;
+        }
+        
+        return $function;
+    }
+
     /**
      * Set server controls for Virtual List View (paginated listing)
      */
-    private function _vlv_set_controls()
+    private function _vlv_set_controls($prop, $list_page, $page_size, $search = null)
     {
-        $sort_ctrl = array('oid' => "1.2.840.113556.1.4.473",  'value' => $this->_sort_ber_encode((array)$this->prop['sort']));
-        $vlv_ctrl  = array('oid' => "2.16.840.1.113730.3.4.9", 'value' => $this->_vlv_ber_encode(($offset = ($this->list_page-1) * $this->page_size + 1), $this->page_size), 'iscritical' => true);
+        $sort_ctrl = array('oid' => "1.2.840.113556.1.4.473",  'value' => $this->_sort_ber_encode((array)$prop['sort']));
+        $vlv_ctrl  = array('oid' => "2.16.840.1.113730.3.4.9", 'value' => $this->_vlv_ber_encode(($offset = ($list_page-1) * $page_size + 1), $page_size, $search), 'iscritical' => true);
 
-        $this->_debug("C: set controls sort=" . join(' ', unpack('H'.(strlen($sort_ctrl['value'])*2), $sort_ctrl['value'])) . " ({$this->sort_col});"
-            . " vlv=" . join(' ', (unpack('H'.(strlen($vlv_ctrl['value'])*2), $vlv_ctrl['value']))) . " ($offset)");
+        $sort = (array)$prop['sort'];
+        $this->_debug("C: set controls sort=" . join(' ', unpack('H'.(strlen($sort_ctrl['value'])*2), $sort_ctrl['value'])) . " ($sort[0]);"
+            . " vlv=" . join(' ', (unpack('H'.(strlen($vlv_ctrl['value'])*2), $vlv_ctrl['value']))) . " ($offset/$page_size)");
 
         if (!ldap_set_option($this->conn, LDAP_OPT_SERVER_CONTROLS, array($sort_ctrl, $vlv_ctrl))) {
             $this->_debug("S: ".ldap_error($this->conn));
@@ -991,6 +1297,9 @@ class rcube_ldap extends rcube_addressbook
             for ($i=0; $i < $rec[$lf]['count']; $i++) {
                 if (!($value = $rec[$lf][$i]))
                     continue;
+
+                $out['_raw_attrib'][$lf][$i] = $value;
+
                 if ($rf == 'email' && $this->mail_domain && !strpos($value, '@'))
                     $out[$rf][] = sprintf('%s@%s', $value, $this->mail_domain);
                 else if (in_array($rf, array('street','zipcode','locality','country','region')))
@@ -1000,6 +1309,11 @@ class rcube_ldap extends rcube_addressbook
                 else
                     $out[$rf] = $value;
             }
+
+            // Make sure name fields aren't arrays (#1488108)
+            if (is_array($out[$rf]) && in_array($rf, array('name', 'surname', 'firstname', 'middlename', 'nickname'))) {
+                $out[$rf] = $out['_raw_attrib'][$lf] = $out[$rf][0];
+            }
         }
 
         return $out;
@@ -1015,6 +1329,40 @@ class rcube_ldap extends rcube_addressbook
     }
 
 
+    /**
+     * Convert a record data set into LDAP field attributes
+     */
+    private function _map_data($save_cols)
+    {
+        // flatten composite fields first
+        foreach ($this->coltypes as $col => $colprop) {
+            if (is_array($colprop['childs']) && ($values = $this->get_col_values($col, $save_cols, false))) {
+                foreach ($values as $subtype => $childs) {
+                    $subtype = $subtype ? ':'.$subtype : '';
+                    foreach ($childs as $i => $child_values) {
+                        foreach ((array)$child_values as $childcol => $value) {
+                            $save_cols[$childcol.$subtype][$i] = $value;
+                        }
+                    }
+                }
+            }
+        }
+
+        $ldap_data = array();
+        foreach ($this->fieldmap as $col => $fld) {
+            $val = $save_cols[$col];
+            if (is_array($val))
+                $val = array_filter($val);  // remove empty entries
+            if ($fld && $val) {
+                // The field does exist, add it to the entry.
+                $ldap_data[$fld] = $val;
+            }
+        }
+        
+        return $ldap_data;
+    }
+
+
     /**
      * Returns unified attribute name (resolving aliases)
      */
@@ -1042,6 +1390,18 @@ class rcube_ldap extends rcube_addressbook
     }
 
 
+    /**
+     * Activate/deactivate debug mode
+     *
+     * @param boolean $dbg True if LDAP commands should be logged
+     * @access public
+     */
+    function set_debug($dbg = true)
+    {
+        $this->debug = $dbg;
+    }
+
+
     /**
      * Quotes attribute value string
      *
@@ -1075,22 +1435,17 @@ class rcube_ldap extends rcube_addressbook
     {
         if ($group_id)
         {
-            if (!$this->group_cache)
-                $this->list_groups();
-
-            $cache_members = $this->group_cache[$group_id]['members'];
+            if (($group_cache = $this->cache->get('groups')) === null)
+                $group_cache = $this->_fetch_groups();
 
-            $members = array();
-            for ($i=0; $i<$cache_members["count"]; $i++)
-            {
-                if (!empty($cache_members[$i]))
-                    $members[self::dn_encode($cache_members[$i])] = 1;
-            }
-            $this->group_members = $members;
             $this->group_id = $group_id;
+            $this->group_data = $group_cache[$group_id];
         }
         else
+        {
             $this->group_id = 0;
+            $this->group_data = null;
+        }
     }
 
     /**
@@ -1104,12 +1459,49 @@ class rcube_ldap extends rcube_addressbook
         if (!$this->groups)
             return array();
 
+        // use cached list for searching
+        $this->cache->expunge();
+        if (!$search || ($group_cache = $this->cache->get('groups')) === null)
+            $group_cache = $this->_fetch_groups();
+
+        $groups = array();
+        if ($search) {
+            $search = mb_strtolower($search);
+            foreach ($group_cache as $group) {
+                if (strpos(mb_strtolower($group['name']), $search) !== false)
+                    $groups[] = $group;
+            }
+        }
+        else
+            $groups = $group_cache;
+
+        return array_values($groups);
+    }
+
+    /**
+     * Fetch groups from server
+     */
+    private function _fetch_groups($vlv_page = 0)
+    {
         $base_dn = $this->groups_base_dn;
         $filter = $this->prop['groups']['filter'];
+        $name_attr = $this->prop['groups']['name_attr'];
+        $email_attr = $this->prop['groups']['email_attr'] ? $this->prop['groups']['email_attr'] : 'mail';
+        $sort_attrs = $this->prop['groups']['sort'] ? (array)$this->prop['groups']['sort'] : array($name_attr);
+        $sort_attr = $sort_attrs[0];
 
         $this->_debug("C: Search [$filter][dn: $base_dn]");
 
-        $res = @ldap_search($this->conn, $base_dn, $filter, array('cn', $this->prop['member_attr']));
+        // use vlv to list groups
+        if ($this->prop['groups']['vlv']) {
+            $page_size = 200;
+            if (!$this->prop['groups']['sort'])
+                $this->prop['groups']['sort'] = $sort_attrs;
+            $vlv_active = $this->_vlv_set_controls($this->prop['groups'], $vlv_page+1, $page_size);
+        }
+
+        $function = $this->_scope2func($this->prop['groups']['scope'], $ns_function);
+        $res = @$function($this->conn, $base_dn, $filter, array_unique(array('dn', 'objectClass', $name_attr, $email_attr, $sort_attr)));
         if ($res === false)
         {
             $this->_debug("S: ".ldap_error($this->conn));
@@ -1121,24 +1513,79 @@ class rcube_ldap extends rcube_addressbook
 
         $groups = array();
         $group_sortnames = array();
-        for ($i=0; $i<$ldap_data["count"]; $i++)
+        $group_count = $ldap_data["count"];
+        for ($i=0; $i < $group_count; $i++)
         {
-            $group_name = $ldap_data[$i]['cn'][0];
-            if (!$search || strstr(strtolower($group_name), strtolower($search)))
-            {
-                $group_id = self::dn_encode($group_name);
-                $groups[$group_id]['ID'] = $group_id;
-                $groups[$group_id]['name'] = $group_name;
-                $groups[$group_id]['members'] = $ldap_data[$i][$this->prop['member_attr']];
-                $group_sortnames[] = strtolower($group_name);
+            $group_name = is_array($ldap_data[$i][$name_attr]) ? $ldap_data[$i][$name_attr][0] : $ldap_data[$i][$name_attr];
+            $group_id = self::dn_encode($group_name);
+            $groups[$group_id]['ID'] = $group_id;
+            $groups[$group_id]['dn'] = $ldap_data[$i]['dn'];
+            $groups[$group_id]['name'] = $group_name;
+            $groups[$group_id]['member_attr'] = $this->prop['member_attr'];
+
+            // check objectClass attributes of group and act accordingly
+            for ($j=0; $j < $ldap_data[$i]['objectclass']['count']; $j++) {
+                switch (strtolower($ldap_data[$i]['objectclass'][$j])) {
+                    case 'groupofnames':
+                    case 'kolabgroupofnames':
+                        $groups[$group_id]['member_attr'] = 'member';
+                        break;
+
+                    case 'groupofuniquenames':
+                    case 'kolabgroupofuniquenames':
+                        $groups[$group_id]['member_attr'] = 'uniqueMember';
+                        break;
+                }
             }
+
+            // list email attributes of a group
+            for ($j=0; $ldap_data[$i][$email_attr] && $j < $ldap_data[$i][$email_attr]['count']; $j++) {
+                if (strpos($ldap_data[$i][$email_attr][$j], '@') > 0)
+                    $groups[$group_id]['email'][] = $ldap_data[$i][$email_attr][$j];
+            }
+
+            $group_sortnames[] = mb_strtolower($ldap_data[$i][$sort_attr][0]);
         }
-        array_multisort($group_sortnames, SORT_ASC, SORT_STRING, $groups);
-        $this->group_cache = $groups;
+
+        // recursive call can exit here
+        if ($vlv_page > 0)
+            return $groups;
+
+        // call recursively until we have fetched all groups
+        while ($vlv_active && $group_count == $page_size)
+        {
+            $next_page = $this->_fetch_groups(++$vlv_page);
+            $groups = array_merge($groups, $next_page);
+            $group_count = count($next_page);
+        }
+
+        // when using VLV the list of groups is already sorted
+        if (!$this->prop['groups']['vlv'])
+            array_multisort($group_sortnames, SORT_ASC, SORT_STRING, $groups);
+
+        // cache this
+        $this->cache->set('groups', $groups);
 
         return $groups;
     }
 
+    /**
+     * Get group properties such as name and email address(es)
+     *
+     * @param string Group identifier
+     * @return array Group properties as hash array
+     */
+    function get_group($group_id)
+    {
+        if (($group_cache = $this->cache->get('groups')) === null)
+            $group_cache = $this->_fetch_groups();
+
+        $group_data = $group_cache[$group_id];
+        unset($group_data['dn'], $group_data['member_attr']);
+
+        return $group_data;
+    }
+
     /**
      * Create a contact group with the given name
      *
@@ -1147,17 +1594,16 @@ class rcube_ldap extends rcube_addressbook
      */
     function create_group($group_name)
     {
-        if (!$this->group_cache)
-            $this->list_groups();
-
         $base_dn = $this->groups_base_dn;
         $new_dn = "cn=$group_name,$base_dn";
         $new_gid = self::dn_encode($group_name);
+        $member_attr = $this->prop['groups']['member_attr'];
+        $name_attr = $this->prop['groups']['name_attr'];
 
         $new_entry = array(
             'objectClass' => $this->prop['groups']['object_classes'],
-            'cn' => $group_name,
-            $this->prop['member_attr'] => '',
+            $name_attr => $group_name,
+            $member_attr => '',
         );
 
         $this->_debug("C: Add [dn: $new_dn]: ".print_r($new_entry, true));
@@ -1171,6 +1617,7 @@ class rcube_ldap extends rcube_addressbook
         }
 
         $this->_debug("S: OK");
+        $this->cache->remove('groups');
 
         return array('id' => $new_gid, 'name' => $group_name);
     }
@@ -1183,11 +1630,11 @@ class rcube_ldap extends rcube_addressbook
      */
     function delete_group($group_id)
     {
-        if (!$this->group_cache)
-            $this->list_groups();
+        if (($group_cache = $this->cache->get('groups')) === null)
+            $group_cache = $this->_fetch_groups();
 
         $base_dn = $this->groups_base_dn;
-        $group_name = $this->group_cache[$group_id]['name'];
+        $group_name = $group_cache[$group_id]['name'];
         $del_dn = "cn=$group_name,$base_dn";
 
         $this->_debug("C: Delete [dn: $del_dn]");
@@ -1201,6 +1648,7 @@ class rcube_ldap extends rcube_addressbook
         }
 
         $this->_debug("S: OK");
+        $this->cache->remove('groups');
 
         return true;
     }
@@ -1215,11 +1663,11 @@ class rcube_ldap extends rcube_addressbook
      */
     function rename_group($group_id, $new_name, &$new_gid)
     {
-        if (!$this->group_cache)
-            $this->list_groups();
+        if (($group_cache = $this->cache->get('groups')) === null)
+            $group_cache = $this->_fetch_groups();
 
         $base_dn = $this->groups_base_dn;
-        $group_name = $this->group_cache[$group_id]['name'];
+        $group_name = $group_cache[$group_id]['name'];
         $old_dn = "cn=$group_name,$base_dn";
         $new_rdn = "cn=$new_name";
         $new_gid = self::dn_encode($new_name);
@@ -1235,6 +1683,7 @@ class rcube_ldap extends rcube_addressbook
         }
 
         $this->_debug("S: OK");
+        $this->cache->remove('groups');
 
         return $new_name;
     }
@@ -1248,16 +1697,19 @@ class rcube_ldap extends rcube_addressbook
      */
     function add_to_group($group_id, $contact_ids)
     {
-        if (!$this->group_cache)
-            $this->list_groups();
+        if (($group_cache = $this->cache->get('groups')) === null)
+            $group_cache = $this->_fetch_groups();
+
+        if (!is_array($contact_ids))
+            $contact_ids = explode(',', $contact_ids);
 
         $base_dn     = $this->groups_base_dn;
-        $group_name  = $this->group_cache[$group_id]['name'];
-        $member_attr = $this->prop['member_attr'];
+        $group_name  = $group_cache[$group_id]['name'];
+        $member_attr = $group_cache[$group_id]['member_attr'];
         $group_dn    = "cn=$group_name,$base_dn";
 
         $new_attrs = array();
-        foreach (explode(",", $contact_ids) as $id)
+        foreach ($contact_ids as $id)
             $new_attrs[$member_attr][] = self::dn_decode($id);
 
         $this->_debug("C: Add [dn: $group_dn]: ".print_r($new_attrs, true));
@@ -1271,6 +1723,7 @@ class rcube_ldap extends rcube_addressbook
         }
 
         $this->_debug("S: OK");
+        $this->cache->remove('groups');
 
         return count($new_attrs['member']);
     }
@@ -1284,12 +1737,12 @@ class rcube_ldap extends rcube_addressbook
      */
     function remove_from_group($group_id, $contact_ids)
     {
-        if (!$this->group_cache)
-            $this->list_groups();
+        if (($group_cache = $this->cache->get('groups')) === null)
+            $group_cache = $this->_fetch_groups();
 
         $base_dn     = $this->groups_base_dn;
-        $group_name  = $this->group_cache[$group_id]['name'];
-        $member_attr = $this->prop['member_attr'];
+        $group_name  = $group_cache[$group_id]['name'];
+        $member_attr = $group_cache[$group_id]['member_attr'];
         $group_dn    = "cn=$group_name,$base_dn";
 
         $del_attrs = array();
@@ -1307,6 +1760,7 @@ class rcube_ldap extends rcube_addressbook
         }
 
         $this->_debug("S: OK");
+        $this->cache->remove('groups');
 
         return count($del_attrs['member']);
     }
@@ -1326,12 +1780,16 @@ class rcube_ldap extends rcube_addressbook
 
         $base_dn     = $this->groups_base_dn;
         $contact_dn  = self::dn_decode($contact_id);
+        $name_attr   = $this->prop['groups']['name_attr'];
         $member_attr = $this->prop['member_attr'];
-        $filter      = strtr("($member_attr=$contact_dn)", array('\\' => '\\\\'));
+        $add_filter  = '';
+        if ($member_attr != 'member' && $member_attr != 'uniqueMember')
+            $add_filter = "($member_attr=$contact_dn)";
+        $filter = strtr("(|(member=$contact_dn)(uniqueMember=$contact_dn)$add_filter)", array('\\' => '\\\\'));
 
         $this->_debug("C: Search [$filter][dn: $base_dn]");
 
-        $res = @ldap_search($this->conn, $base_dn, $filter, array('cn'));
+        $res = @ldap_search($this->conn, $base_dn, $filter, array($name_attr));
         if ($res === false)
         {
             $this->_debug("S: ".ldap_error($this->conn));
@@ -1343,7 +1801,7 @@ class rcube_ldap extends rcube_addressbook
         $groups = array();
         for ($i=0; $i<$ldap_data["count"]; $i++)
         {
-            $group_name = $ldap_data[$i]['cn'][0];
+            $group_name = $ldap_data[$i][$name_attr][0];
             $group_id = self::dn_encode($group_name);
             $groups[$group_id] = $group_id;
         }
@@ -1358,17 +1816,23 @@ class rcube_ldap extends rcube_addressbook
      * @param integer Records per page
      * @return string BER encoded option value
      */
-    private function _vlv_ber_encode($offset, $rpp)
+    private function _vlv_ber_encode($offset, $rpp, $search = '')
     {
         # this string is ber-encoded, php will prefix this value with:
         # 04 (octet string) and 10 (length of 16 bytes)
         # the code behind this string is broken down as follows:
         # 30 = ber sequence with a length of 0e (14) bytes following
-        # 20 = type integer (in two's complement form) with 2 bytes following (beforeCount): 01 00 (ie 0)
-        # 20 = type integer (in two's complement form) with 2 bytes following (afterCount):  01 18 (ie 25-1=24)
+        # 02 = type integer (in two's complement form) with 2 bytes following (beforeCount): 01 00 (ie 0)
+        # 02 = type integer (in two's complement form) with 2 bytes following (afterCount):  01 18 (ie 25-1=24)
         # a0 = type context-specific/constructed with a length of 06 (6) bytes following
-        # 20 = type integer with 2 bytes following (offset): 01 01 (ie 1)
-        # 20 = type integer with 2 bytes following (contentCount):  01 00
+        # 02 = type integer with 2 bytes following (offset): 01 01 (ie 1)
+        # 02 = type integer with 2 bytes following (contentCount):  01 00
+        
+        # whith a search string present:
+        # 81 = type context-specific/constructed with a length of 04 (4) bytes following (the length will change here)
+        # 81 indicates a user string is present where as a a0 indicates just a offset search
+        # 81 = type context-specific/constructed with a length of 06 (6) bytes following
+        
         # the following info was taken from the ISO/IEC 8825-1:2003 x.690 standard re: the
         # encoding of integer values (note: these values are in
         # two-complement form so since offset will never be negative bit 8 of the
@@ -1378,20 +1842,29 @@ class rcube_ldap extends rcube_addressbook
         # of the second (to the left of first octet) octet:
         # a) shall not all be ones; and
         # b) shall not all be zero
+        
+        if ($search)
+        {
+            $search = preg_replace('/[^-[:alpha:] ,.()0-9]+/', '', $search);
+            $ber_val = self::_string2hex($search);
+            $str = self::_ber_addseq($ber_val, '81');
+        }
+        else
+        {
+            # construct the string from right to left
+            $str = "020100"; # contentCount
 
-        # construct the string from right to left
-        $str = "020100"; # contentCount
-
-        $ber_val = self::_ber_encode_int($offset);  // returns encoded integer value in hex format
-
-        // calculate octet length of $ber_val
-        $str = self::_ber_addseq($ber_val, '02') . $str;
+            $ber_val = self::_ber_encode_int($offset);  // returns encoded integer value in hex format
 
-        // now compute length over $str
-        $str = self::_ber_addseq($str, 'a0');
+            // calculate octet length of $ber_val
+            $str = self::_ber_addseq($ber_val, '02') . $str;
 
+            // now compute length over $str
+            $str = self::_ber_addseq($str, 'a0');
+        }
+        
         // now tack on records per page
-        $str = sprintf("0201000201%02x", min(255, $rpp)-1) . $str;
+        $str = "020100" . self::_ber_addseq(self::_ber_encode_int($rpp-1), '02') . $str;
 
         // now tack on sequence identifier and length
         $str = self::_ber_addseq($str, '30');
index e44826e956980134b54eabce2e9f8d24ee38f2f2..f25acef4df04f6f14e2fa1c6ea7e22239e74e538 100644 (file)
@@ -16,7 +16,7 @@
  | Author: Lukas Kahwe Smith <smith@pooteeweet.org>                      |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_mdb2.php 4810 2011-05-27 11:02:51Z alec $
+ $Id: rcube_mdb2.php 5543 2011-12-05 07:24:36Z alec $
 
 */
 
  */
 class rcube_mdb2
 {
-    var $db_dsnw;               // DSN for write operations
-    var $db_dsnr;               // DSN for read operations
-    var $db_connected = false;  // Already connected ?
-    var $db_mode = '';          // Connection mode
-    var $db_handle = 0;         // Connection handle
-    var $db_error = false;
-    var $db_error_msg = '';
+    public $db_dsnw;               // DSN for write operations
+    public $db_dsnr;               // DSN for read operations
+    public $db_connected = false;  // Already connected ?
+    public $db_mode = '';          // Connection mode
+    public $db_handle = 0;         // Connection handle
+    public $db_error = false;
+    public $db_error_msg = '';
 
     private $debug_mode = false;
-    private $write_failure = false;
+    private $conn_failure = false;
     private $a_query_results = array('dummy');
     private $last_res_id = 0;
     private $tables;
@@ -58,7 +58,7 @@ class rcube_mdb2
      */
     function __construct($db_dsnw, $db_dsnr='', $pconn=false)
     {
-        if ($db_dsnr == '')
+        if (empty($db_dsnr))
             $db_dsnr = $db_dsnw;
 
         $this->db_dsnw = $db_dsnw;
@@ -91,6 +91,8 @@ class rcube_mdb2
             $db_options['disable_smart_seqname'] = true;
             $db_options['seqname_format'] = '%s';
         }
+        $this->db_error     = false;
+        $this->db_error_msg = null;
 
         $dbh = MDB2::connect($dsn, $db_options);
 
@@ -122,30 +124,40 @@ class rcube_mdb2
      */
     function db_connect($mode)
     {
+        // previous connection failed, don't attempt to connect again
+        if ($this->conn_failure) {
+            return;
+        }
+
+        // no replication
+        if ($this->db_dsnw == $this->db_dsnr) {
+            $mode = 'w';
+        }
+
         // Already connected
         if ($this->db_connected) {
-            // connected to read-write db, current connection is ok
-            if ($this->db_mode == 'w' && !$this->write_failure)
-                return;
-
-            // no replication, current connection is ok for read and write
-            if (empty($this->db_dsnr) || $this->db_dsnw == $this->db_dsnr) {
-                $this->db_mode = 'w';
+            // connected to db with the same or "higher" mode
+            if ($this->db_mode == 'w' || $this->db_mode == $mode) {
                 return;
             }
-
-            // Same mode, current connection is ok
-            if ($this->db_mode == $mode)
-                return;
         }
 
         $dsn = ($mode == 'r') ? $this->db_dsnr : $this->db_dsnw;
 
-        $this->db_handle = $this->dsn_connect($dsn);
+        $this->db_handle    = $this->dsn_connect($dsn);
         $this->db_connected = !PEAR::isError($this->db_handle);
 
+        // use write-master when read-only fails
+        if (!$this->db_connected && $mode == 'r') {
+            $mode = 'w';
+            $this->db_handle    = $this->dsn_connect($this->db_dsnw);
+            $this->db_connected = !PEAR::isError($this->db_handle);
+        }
+
         if ($this->db_connected)
-          $this->db_mode = $mode;
+            $this->db_mode = $mode;
+        else
+            $this->conn_failure = true;
     }
 
 
@@ -256,10 +268,6 @@ class rcube_mdb2
         // Read or write ?
         $mode = (strtolower(substr(trim($query),0,6)) == 'select') ? 'r' : 'w';
 
-        // don't event attempt to connect if previous write-operation failed
-        if ($this->write_failure && $mode == 'w')
-            return false;
-
         $this->db_connect($mode);
 
         // check connection before proceeding
@@ -284,7 +292,7 @@ class rcube_mdb2
                 raise_error(array('code' => 500, 'type' => 'db',
                     'line' => __LINE__, 'file' => __FILE__,
                     'message' => $this->db_error_msg), true, false);
-                
+
                 $result = false;
             }
             else {
@@ -293,10 +301,6 @@ class rcube_mdb2
             }
         }
 
-        // remember that write-operation failed
-        if ($mode == 'w' && ($result === false || PEAR::isError($result)))
-            $this->write_failure = true;
-
         // add result, even if it's an error
         return $this->_add_result($result);
     }
@@ -447,7 +451,7 @@ class rcube_mdb2
         if (!PEAR::isError($result = $this->db_handle->listTableFields($table))) {
             return $result;
         }
-        
+
         return null;
     }
 
@@ -530,7 +534,7 @@ class rcube_mdb2
      */
     function now()
     {
-        switch($this->db_provider) {
+        switch ($this->db_provider) {
             case 'mssql':
             case 'sqlsrv':
                 return "getdate()";
index 667657b5e9d925b15bb7778837f3854f6c26276e..05ae2b1396a0cb2d01132a26a2e3309b2e2ea606 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_message.php 5261 2011-09-21 12:22:40Z alec $
+ $Id: rcube_message.php 5514 2011-11-30 11:35:43Z alec $
 
 */
 
@@ -48,7 +48,6 @@ class rcube_message
 
     public $uid = null;
     public $headers;
-    public $structure;
     public $parts = array();
     public $mime_parts = array();
     public $attachments = array();
@@ -77,7 +76,7 @@ class rcube_message
         $this->imap->get_all_headers = true;
 
         $this->uid = $uid;
-        $this->headers = $this->imap->get_headers($uid, NULL, true, true);
+        $this->headers = $this->imap->get_message($uid);
 
         if (!$this->headers)
             return;
@@ -94,9 +93,9 @@ class rcube_message
                 '_mbox' => $this->imap->get_mailbox_name(), '_uid' => $uid))
         );
 
-        if ($this->structure = $this->imap->get_structure($uid, $this->headers->body_structure)) {
-            $this->get_mime_numbers($this->structure);
-            $this->parse_structure($this->structure);
+        if (!empty($this->headers->structure)) {
+            $this->get_mime_numbers($this->headers->structure);
+            $this->parse_structure($this->headers->structure);
         }
         else {
             $this->body = $this->imap->get_body($uid);
@@ -143,10 +142,10 @@ class rcube_message
      * @param string $mime_id Part MIME-ID
      * @return string URL or false if part does not exist
      */
-    public function get_part_url($mime_id)
+    public function get_part_url($mime_id, $embed = false)
     {
         if ($this->mime_parts[$mime_id])
-            return $this->opt['get_url'] . '&_part=' . $mime_id;
+            return $this->opt['get_url'] . '&_part=' . $mime_id . ($embed ? '&_embed=1' : '');
         else
             return false;
     }
@@ -299,8 +298,10 @@ class rcube_message
             $structure->type = 'content';
             $this->parts[] = &$structure;
         }
-        // message contains alternative parts
-        else if ($mimetype == 'multipart/alternative' && is_array($structure->parts)) {
+        // message contains (more than one!) alternative parts
+        else if ($mimetype == 'multipart/alternative'
+            && is_array($structure->parts) && count($structure->parts) > 1
+        ) {
             // get html/plaintext parts
             $plain_part = $html_part = $print_part = $related_part = null;
 
@@ -510,7 +511,7 @@ class rcube_message
                 $img_regexp = '/^image\/(gif|jpe?g|png|tiff|bmp|svg)/';
 
                 foreach ($this->inline_parts as $inline_object) {
-                    $part_url = $this->get_part_url($inline_object->mime_id);
+                    $part_url = $this->get_part_url($inline_object->mime_id, true);
                     if ($inline_object->content_id)
                         $a_replaces['cid:'.$inline_object->content_id] = $part_url;
                     if ($inline_object->content_location) {
index de283d24619a68bb87423b2f4d49eceebdff6813..ed28af31f0cb6f54e62bfd0adedae47ab4b4be37 100644 (file)
@@ -1,77 +1,7 @@
-<?php
 
-
-/*
- +-----------------------------------------------------------------------+
- | program/include/rcube_mime_struct.php                                 |
- |                                                                       |
- | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
- | Licensed under the GNU GPL                                            |
- |                                                                       |
- | PURPOSE:                                                              |
- |   Provide functions for handling mime messages structure              |
- |                                                                       |
- |   Based on Iloha MIME Library. See http://ilohamail.org/ for details  |
- |                                                                       |
- +-----------------------------------------------------------------------+
- | Author: Aleksander Machniak <alec@alec.pl>                            |
- | Author: Ryo Chijiiwa <Ryo@IlohaMail.org>                              |
- +-----------------------------------------------------------------------+
-
- $Id: rcube_mime_struct.php 4980 2011-07-27 18:21:49Z alec $
-
-*/
-
-/**
- * Helper class to process IMAP's BODYSTRUCTURE string
- *
- * @package    Mail
- * @author     Aleksander Machniak <alec@alec.pl>
- */
-class rcube_mime_struct
-{
-    private $structure;
-
-
-    function __construct($str=null)
-    {
-        if ($str)
-            $this->structure = $this->parseStructure($str);
-    }
-
-    /*
-     * Parses IMAP's BODYSTRUCTURE string into array
-    */
-    function parseStructure($str)
-    {
-        $line = substr($str, 1, strlen($str) - 2);
-        $line = str_replace(')(', ') (', $line);
-
-           $struct = rcube_imap_generic::tokenizeResponse($line);
-       if (!is_array($struct[0]) && (strcasecmp($struct[0], 'message') == 0)
-                   && (strcasecmp($struct[1], 'rfc822') == 0)) {
-                   $struct = array($struct);
-           }
-
-        return $struct;
-    }
-
-    /*
-     * Parses IMAP's BODYSTRUCTURE string into array and loads it into class internal variable
-    */
-    function loadStructure($str)
+    function getStructurePartType($structure, $part)
     {
-        if (empty($str))
-            return true;
-
-        $this->structure = $this->parseStructure($str);
-        return (!empty($this->structure));
-    }
-
-    function getPartType($part)
-    {
-           $part_a = $this->getPartArray($this->structure, $part);
+           $part_a = self::getPartArray($structure, $part);
            if (!empty($part_a)) {
                    if (is_array($part_a[0]))
                 return 'multipart';
@@ -82,9 +12,9 @@ class rcube_mime_struct
         return 'other';
     }
 
-    function getPartEncoding($part)
+    function getStructurePartEncoding($structure, $part)
     {
-           $part_a = $this->getPartArray($this->structure, $part);
+           $part_a = self::getPartArray($structure, $part);
            if ($part_a) {
                    if (!is_array($part_a[0]))
                 return $part_a[5];
@@ -93,9 +23,9 @@ class rcube_mime_struct
         return '';
     }
 
-    function getPartCharset($part)
+    function getStructurePartCharset($structure, $part)
     {
-           $part_a = $this->getPartArray($this->structure, $part);
+           $part_a = self::getPartArray($structure, $part);
            if ($part_a) {
                    if (is_array($part_a[0]))
                 return '';
@@ -112,7 +42,7 @@ class rcube_mime_struct
         return '';
     }
 
-    function getPartArray($a, $part)
+    function getStructurePartArray($a, $part)
     {
            if (!is_array($a)) {
             return false;
@@ -137,9 +67,7 @@ class rcube_mime_struct
                    else
                 return $a;
            }
-        else if (($part==0) || (empty($part))) {
+        else if (($part == 0) || (empty($part))) {
                    return $a;
            }
     }
-
-}
index c75a17cb4aab17790408eff339cc2772dce082ed..748d958e8ed1cb98eac1b73d778ef82f3178f2be 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_plugin.php 4870 2011-06-21 09:10:14Z alec $
+ $Id: rcube_plugin.php 5168 2011-09-05 11:08:48Z alec $
 
 */
 
@@ -147,8 +147,11 @@ abstract class rcube_plugin
     ob_start();
 
     foreach (array('en_US', $lang) as $lng) {
-      @include($locdir . $lng . '.inc');
-      $texts = (array)$labels + (array)$messages + (array)$texts;
+      $fpath = $locdir . $lng . '.inc';
+      if (is_file($fpath) && is_readable($fpath)) {
+        include($fpath);
+        $texts = (array)$labels + (array)$messages + (array)$texts;
+      }
     }
 
     ob_end_clean();
index 6fa566b724a6a3a498c0405a6689c1b56047fa4e..e762fff0816ee16dcd9b3680f06205d62a4876c0 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_plugin_api.php 5151 2011-08-31 12:49:44Z alec $
+ $Id: rcube_plugin_api.php 5207 2011-09-12 12:52:01Z alec $
 
 */
 
@@ -421,6 +421,17 @@ class rcube_plugin_api
   }
 
 
+  /**
+   * Returns list of loaded plugins names
+   *
+   * @return array List of plugin names
+   */
+  public function loaded_plugins()
+  {
+    return array_keys($this->plugins);
+  }
+
+
   /**
    * Callback for template_container hooks
    *
index cf023d6363946b46849e34174a9528d95667ab59..e4a4ad4f3fd077950809de94d8bb4e34eee82a5c 100644 (file)
@@ -5,7 +5,7 @@
  | program/include/rcube_result_set.php                                  |
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2006-2010, The Roundcube Dev Team                       |
+ | Copyright (C) 2006-2011, The Roundcube Dev Team                       |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_result_set.php 4834 2011-06-03 11:03:13Z alec $
+ $Id: rcube_result_set.php 5258 2011-09-21 11:17:46Z thomasb $
 
 */
 
@@ -31,6 +31,7 @@ class rcube_result_set
     var $count = 0;
     var $first = 0;
     var $current = 0;
+    var $searchonly = false;
     var $records = array();
 
 
index 01b93670cfe93a9943a574ef18191f94d015e276..bd0ce60e46f2eb2d948a0a0d3af1ce06b157f04b 100644 (file)
@@ -6,6 +6,7 @@
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
  | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
+ | Copyright (C) 2011, Kolab Systems AG                                  |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -330,20 +331,7 @@ class rcube_session
   public function gc()
   {
     foreach ($this->gc_handlers as $fct)
-      $fct();
-  }
-
-
-  /**
-   * Cleanup session data before saving
-   */
-  public function cleanup()
-  {
-    // current compose information is stored in $_SESSION['compose'], move it to $_SESSION['compose_data_<ID>']
-    if ($compose_id = $_SESSION['compose']['id']) {
-      $_SESSION['compose_data_'.$compose_id] = $_SESSION['compose'];
-      $this->remove('compose');
-    }
+      call_user_func($fct);
   }
 
 
@@ -403,6 +391,21 @@ class rcube_session
   }
 
 
+  /**
+   * Re-read session data from storage backend
+   */
+  public function reload()
+  {
+    if ($this->key && $this->memcache)
+      $data = $this->mc_read($this->key);
+    else if ($this->key)
+      $data = $this->db_read($this->key);
+
+    if ($data)
+     session_decode($data);
+  }
+
+
   /**
    * Serialize session data
    */
index 3cd96c915108bc2e3e0dfb18469cbd6d6cfa966b..6e58ad86982c5293ae05d48ac77e1dd351a16403 100644 (file)
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_shared.inc 4710 2011-04-29 08:17:42Z alec $
+ $Id: rcube_shared.inc 5274 2011-09-23 10:11:27Z alec $
 
 */
 
 
 /**
  * Roundcube shared functions
- * 
+ *
  * @package Core
  */
 
@@ -69,50 +69,6 @@ function send_future_expire_header($offset=2600000)
 }
 
 
-/**
- * Check request for If-Modified-Since and send an according response.
- * This will terminate the current script if headers match the given values
- *
- * @param int Modified date as unix timestamp
- * @param string Etag value for caching
- */
-function send_modified_header($mdate, $etag=null, $skip_check=false)
-{
-  if (headers_sent())
-    return;
-    
-  $iscached = false;
-  $etag = $etag ? "\"$etag\"" : null;
-
-  if (!$skip_check)
-  {
-    if ($_SERVER['HTTP_IF_MODIFIED_SINCE'] && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $mdate)
-      $iscached = true;
-  
-    if ($etag)
-      $iscached = ($_SERVER['HTTP_IF_NONE_MATCH'] == $etag);
-  }
-  
-  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: private, must-revalidate, max-age=0");
-  header("Expires: ");
-  header("Pragma: ");
-  
-  if ($etag)
-    header("Etag: $etag");
-  
-  if ($iscached)
-    {
-    ob_end_clean();
-    exit;
-    }
-}
-
-
 /**
  * Similar function as in_array() but case-insensitive
  *
@@ -126,7 +82,7 @@ function in_array_nocase($needle, $haystack)
   foreach ($haystack as $value)
     if ($needle===mb_strtolower($value))
       return true;
-  
+
   return false;
 }
 
@@ -218,7 +174,7 @@ function make_absolute_url($path, $base_url)
 {
   $host_url = $base_url;
   $abs_path = $path;
-  
+
   // check if path is an absolute URL
   if (preg_match('/^[fhtps]+:\/\//', $path))
     return $path;
@@ -325,7 +281,7 @@ function rc_request_header($name)
   }
 
   return $hdrs[$key];
-  }
+}
 
 
 /**
@@ -344,7 +300,7 @@ function unslashify($str)
 {
   return preg_replace('/\/$/', '', $str);
 }
-  
+
 
 /**
  * Delete all files within a folder
@@ -374,7 +330,7 @@ function clear_directory($dir_path)
  * @return int Unix timestamp
  */
 function get_offset_time($offset_str, $factor=1)
-  {
+{
   if (preg_match('/^([0-9]+)\s*([smhdw])/i', $offset_str, $regs))
   {
     $amount = (int)$regs[1];
@@ -385,7 +341,7 @@ function get_offset_time($offset_str, $factor=1)
     $amount = (int)$offset_str;
     $unit = 's';
   }
-    
+
   $ts = mktime();
   switch ($unit)
   {
@@ -418,7 +374,7 @@ function get_offset_time($offset_str, $factor=1)
 function abbreviate_string($str, $maxlength, $place_holder='...', $ending=false)
 {
   $length = mb_strlen($str);
-  
+
   if ($length > $maxlength)
   {
     if ($ending)
@@ -433,6 +389,7 @@ function abbreviate_string($str, $maxlength, $place_holder='...', $ending=false)
   return $str;
 }
 
+
 /**
  * A method to guess the mime_type of an attachment.
  *
@@ -547,7 +504,7 @@ function rc_utf8_clean($input)
       $input[$idx] = rc_utf8_clean($val);
     return $input;
   }
-  
+
   if (!is_string($input) || $input == '')
     return $input;
 
@@ -569,7 +526,7 @@ function rc_utf8_clean($input)
     '|[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]'.// UTF8-4
     '|\xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]'.       // UTF8-4
     ')$/';
-  
+
   $seq = '';
   $out = '';
 
@@ -640,7 +597,7 @@ function rcube_explode_quoted_string($delimiter, $string)
       $p = $i + 1;
     }
   }
-  
+
   $result[] = substr($string, $p);
   return $result;
 }
@@ -655,7 +612,7 @@ function rcube_explode_quoted_string($delimiter, $string)
 function array_keys_recursive($array)
 {
   $keys = array();
-  
+
   if (!empty($array))
     foreach ($array as $key => $child) {
       $keys[] = $key;
index 28df53ba5bad5fdab43cf5d0eae517328fd5e399..8c668c0a4b03fb46f1a08d57051ba7783f262f96 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_smtp.php 5033 2011-08-09 09:46:54Z alec $
+ $Id: rcube_smtp.php 5499 2011-11-28 09:03:27Z alec $
 
 */
 
@@ -214,14 +214,10 @@ class rcube_smtp
     if ($opts['dsn']) {
       $exts = $this->conn->getServiceExtensions();
 
-      if (!isset($exts['DSN'])) {
-        $this->error = array('label' => 'smtpdsnerror');
-        $this->response[] = "DSN not supported";
-        return false;
+      if (isset($exts['DSN'])) {
+        $from_params      = 'RET=HDRS';
+        $recipient_params = 'NOTIFY=SUCCESS,FAILURE';
       }
-
-      $from_params      = 'RET=HDRS';
-      $recipient_params = 'NOTIFY=SUCCESS,FAILURE';
     }
 
     // RFC2298.3: remove envelope sender address
@@ -385,7 +381,7 @@ class rcube_smtp
           $from = $addresses[0];
 
         // Reject envelope From: addresses with spaces.
-        if (strstr($from, ' '))
+        if (strpos($from, ' ') !== false)
           return false;
 
         $lines[] = $key . ': ' . $value;
index 804622321b053fabb119ce70a5e6625a6abe9fc5..3b565c3632ab6252a44ff19ed3100a3027623a01 100644 (file)
@@ -17,7 +17,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_spellchecker.php 4817 2011-05-30 17:08:47Z alec $
+ $Id: rcube_spellchecker.php 5181 2011-09-06 13:39:45Z alec $
 
 */
 
@@ -34,8 +34,11 @@ class rcube_spellchecker
     private $lang;
     private $rc;
     private $error;
-    private $separator = '/[ !"#$%&()*+\\,\/\n:;<=>?@\[\]^_{|}-]+|\.[^\w]/';
-    
+    private $separator = '/[\s\r\n\t\(\)\/\[\]{}<>\\"]+|[:;?!,\.]([^\w]|$)/';
+    private $options = array();
+    private $dict;
+    private $have_dict;
+
 
     // default settings
     const GOOGLE_HOST = 'ssl://www.google.com';
@@ -50,9 +53,9 @@ class rcube_spellchecker
      */
     function __construct($lang = 'en')
     {
-        $this->rc = rcmail::get_instance();
+        $this->rc     = rcmail::get_instance();
         $this->engine = $this->rc->config->get('spellcheck_engine', 'googie');
-        $this->lang = $lang ? $lang : 'en';
+        $this->lang   = $lang ? $lang : 'en';
 
         if ($this->engine == 'pspell' && !extension_loaded('pspell')) {
             raise_error(array(
@@ -60,6 +63,13 @@ class rcube_spellchecker
                 'file' => __FILE__, 'line' => __LINE__,
                 'message' => "Pspell extension not available"), true, true);
         }
+
+        $this->options = array(
+            'ignore_syms' => $this->rc->config->get('spellcheck_ignore_syms'),
+            'ignore_nums' => $this->rc->config->get('spellcheck_ignore_nums'),
+            'ignore_caps' => $this->rc->config->get('spellcheck_ignore_caps'),
+            'dictionary'  => $this->rc->config->get('spellcheck_dictionary'),
+        );
     }
 
 
@@ -71,7 +81,7 @@ class rcube_spellchecker
      *
      * @return bool True when no mispelling found, otherwise false
      */
-    function check($text, $is_html=false)
+    function check($text, $is_html = false)
     {
         // convert to plain text
         if ($is_html) {
@@ -116,9 +126,9 @@ class rcube_spellchecker
             return $this->_pspell_suggestions($word);
         }
 
-        return $this->_googie_suggestions($word);    
+        return $this->_googie_suggestions($word);
     }
-    
+
 
     /**
      * Returns mispelled words
@@ -179,7 +189,7 @@ class rcube_spellchecker
             $result[$word] = is_array($item[4]) ? implode("\t", $item[4]) : $item[4];
         }
 
-        return $out;
+        return $result;
     }
 
 
@@ -211,15 +221,18 @@ class rcube_spellchecker
         // tokenize
         $text = preg_split($this->separator, $text, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE);
 
-        $diff = 0;
-        $matches = array();
+        $diff       = 0;
+        $matches    = array();
 
         foreach ($text as $w) {
             $word = trim($w[0]);
             $pos  = $w[1] - $diff;
             $len  = mb_strlen($word);
 
-            if ($word && preg_match('/[^0-9\.]/', $word) && !pspell_check($this->plink, $word)) {
+            // skip exceptions
+            if ($this->is_exception($word)) {
+            }
+            else if (!pspell_check($this->plink, $word)) {
                 $suggestions = pspell_suggest($this->plink, $word);
 
                    if (sizeof($suggestions) > self::MAX_SUGGESTIONS)
@@ -240,6 +253,8 @@ class rcube_spellchecker
      */
     private function _pspell_words($text = null, $is_html=false)
     {
+        $result = array();
+
         if ($text) {
             // init spellchecker
             $this->_pspell_init();
@@ -257,7 +272,13 @@ class rcube_spellchecker
 
             foreach ($text as $w) {
                 $word = trim($w[0]);
-                if ($word && preg_match('/[^0-9\.]/', $word) && !pspell_check($this->plink, $word)) {
+
+                // skip exceptions
+                if ($this->is_exception($word)) {
+                    continue;
+                }
+
+                if (!pspell_check($this->plink, $word)) {
                     $result[] = $word;
                 }
             }
@@ -265,8 +286,6 @@ class rcube_spellchecker
             return $result;
         }
 
-        $result = array();
-
         foreach ($this->matches as $m) {
             $result[] = $m[0];
         }
@@ -330,21 +349,21 @@ class rcube_spellchecker
         }
 
         // Google has some problem with spaces, use \n instead
-        $text = str_replace(' ', "\n", $text);
+        $gtext = str_replace(' ', "\n", $text);
 
-        $text = '<?xml version="1.0" encoding="utf-8" ?>'
+        $gtext = '<?xml version="1.0" encoding="utf-8" ?>'
             .'<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">'
-            .'<text>' . $text . '</text>'
+            .'<text>' . $gtext . '</text>'
             .'</spellrequest>';
 
         $store = '';
         if ($fp = fsockopen($host, $port, $errno, $errstr, 30)) {
             $out = "POST $path HTTP/1.0\r\n";
             $out .= "Host: " . str_replace('ssl://', '', $host) . "\r\n";
-            $out .= "Content-Length: " . strlen($text) . "\r\n";
+            $out .= "Content-Length: " . strlen($gtext) . "\r\n";
             $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
             $out .= "Connection: Close\r\n\r\n";
-            $out .= $text;
+            $out .= $gtext;
             fwrite($fp, $out);
 
             while (!feof($fp))
@@ -358,6 +377,19 @@ class rcube_spellchecker
 
         preg_match_all('/<c o="([^"]*)" l="([^"]*)" s="([^"]*)">([^<]*)<\/c>/', $store, $matches, PREG_SET_ORDER);
 
+        // skip exceptions (if appropriate options are enabled)
+        if (!empty($this->options['ignore_syms']) || !empty($this->options['ignore_nums'])
+            || !empty($this->options['ignore_caps']) || !empty($this->options['dictionary'])
+        ) {
+            foreach ($matches as $idx => $m) {
+                $word = mb_substr($text, $m[1], $m[2], RCMAIL_CHARSET);
+                // skip  exceptions
+                if ($this->is_exception($word)) {
+                    unset($matches[$idx]);
+                }
+            }
+        }
+
         return $matches;
     }
 
@@ -413,4 +445,172 @@ class rcube_spellchecker
         $h2t = new html2text($text, false, true, 0);
         return $h2t->get_text();
     }
+
+
+    /**
+     * Check if the specified word is an exception accoring to 
+     * spellcheck options.
+     *
+     * @param string  $word  The word
+     *
+     * @return bool True if the word is an exception, False otherwise
+     */
+    public function is_exception($word)
+    {
+        // Contain only symbols (e.g. "+9,0", "2:2")
+        if (!$word || preg_match('/^[0-9@#$%^&_+~*=:;?!,.-]+$/', $word))
+            return true;
+
+        // Contain symbols (e.g. "g@@gle"), all symbols excluding separators
+        if (!empty($this->options['ignore_syms']) && preg_match('/[@#$%^&_+~*=-]/', $word))
+            return true;
+
+        // Contain numbers (e.g. "g00g13")
+        if (!empty($this->options['ignore_nums']) && preg_match('/[0-9]/', $word))
+            return true;
+
+        // Blocked caps (e.g. "GOOGLE")
+        if (!empty($this->options['ignore_caps']) && $word == mb_strtoupper($word))
+            return true;
+
+        // Use exceptions from dictionary
+        if (!empty($this->options['dictionary'])) {
+            $this->load_dict();
+
+            // @TODO: should dictionary be case-insensitive?
+            if (!empty($this->dict) && in_array($word, $this->dict))
+                return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Add a word to dictionary
+     *
+     * @param string  $word  The word to add
+     */
+    public function add_word($word)
+    {
+        $this->load_dict();
+
+        foreach (explode(' ', $word) as $word) {
+            // sanity check
+            if (strlen($word) < 512) {
+                $this->dict[] = $word;
+                $valid = true;
+            }
+        }
+
+        if ($valid) {
+            $this->dict = array_unique($this->dict);
+            $this->update_dict();
+        }
+    }
+
+
+    /**
+     * Remove a word from dictionary
+     *
+     * @param string  $word  The word to remove
+     */
+    public function remove_word($word)
+    {
+        $this->load_dict();
+
+        if (($key = array_search($word, $this->dict)) !== false) {
+            unset($this->dict[$key]);
+            $this->update_dict();
+        }
+    }
+
+
+    /**
+     * Update dictionary row in DB
+     */
+    private function update_dict()
+    {
+        if (strcasecmp($this->options['dictionary'], 'shared') != 0) {
+            $userid = (int) $this->rc->user->ID;
+        }
+
+        $plugin = $this->rc->plugins->exec_hook('spell_dictionary_save', array(
+            'userid' => $userid, 'language' => $this->lang, 'dictionary' => $this->dict));
+
+        if (!empty($plugin['abort'])) {
+            return;
+        }
+
+        if ($this->have_dict) {
+            if (!empty($this->dict)) {
+                $this->rc->db->query(
+                    "UPDATE ".get_table_name('dictionary')
+                    ." SET data = ?"
+                    ." WHERE user_id " . ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL")
+                        ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?",
+                    implode(' ', $plugin['dictionary']), $plugin['language']);
+            }
+            // don't store empty dict
+            else {
+                $this->rc->db->query(
+                    "DELETE FROM " . get_table_name('dictionary')
+                    ." WHERE user_id " . ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL")
+                        ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?",
+                    $plugin['language']);
+            }
+        }
+        else if (!empty($this->dict)) {
+            $this->rc->db->query(
+                "INSERT INTO " .get_table_name('dictionary')
+                ." (user_id, " . $this->rc->db->quoteIdentifier('language') . ", data) VALUES (?, ?, ?)",
+                $plugin['userid'], $plugin['language'], implode(' ', $plugin['dictionary']));
+        }
+    }
+
+
+    /**
+     * Get dictionary from DB
+     */
+    private function load_dict()
+    {
+        if (is_array($this->dict)) {
+            return $this->dict;
+        }
+
+        if (strcasecmp($this->options['dictionary'], 'shared') != 0) {
+            $userid = (int) $this->rc->user->ID;
+        }
+
+        $plugin = $this->rc->plugins->exec_hook('spell_dictionary_get', array(
+            'userid' => $userid, 'language' => $this->lang, 'dictionary' => array()));
+
+        if (empty($plugin['abort'])) {
+            $dict = array();
+            $this->rc->db->query(
+                "SELECT data FROM ".get_table_name('dictionary')
+                ." WHERE user_id ". ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL")
+                    ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?",
+                $plugin['language']);
+
+            if ($sql_arr = $this->rc->db->fetch_assoc($sql_result)) {
+                $this->have_dict = true;
+                if (!empty($sql_arr['data'])) {
+                    $dict = explode(' ', $sql_arr['data']);
+                }
+            }
+
+            $plugin['dictionary'] = array_merge((array)$plugin['dictionary'], $dict);
+        }
+
+        if (!empty($plugin['dictionary']) && is_array($plugin['dictionary'])) {
+            $this->dict = $plugin['dictionary'];
+        }
+        else {
+            $this->dict = array();
+        }
+
+        return $this->dict;
+    }
+
 }
index f753aa81f9e3358c5720eed0d81fbd24a21b95d6..b69e9cf50b8814afd6ebe3a5ec06777666957eaf 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_string_replacer.php 5203 2011-09-12 06:44:56Z alec $
+ $Id: rcube_string_replacer.php 5481 2011-11-24 07:53:00Z alec $
 
 */
 
@@ -39,7 +39,7 @@ class rcube_string_replacer
     // Support unicode/punycode in top-level domain part
     $utf_domain = '[^?&@"\'\\/()\s\r\t\n]+\\.([^\\x00-\\x2f\\x3b-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,})';
     $url1 = '.:;,';
-    $url2 = 'a-z0-9%=#@+?&\\/_~\\[\\]-';
+    $url2 = 'a-z0-9%=#@+?!&\\/_~\\[\\]{}-';
 
     $this->link_pattern = "/([\w]+:\/\/|\Wwww\.)($utf_domain([$url1]?[$url2]+)*)/i";
     $this->mailto_pattern = "/("
index 9c208b332e56cd2fad13f0addad34f5214297f0c..5a09fca9200c882ab486ced150e55d87043dfc4f 100755 (executable)
@@ -16,7 +16,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_template.php 5165 2011-09-05 08:49:04Z thomasb $
+ $Id: rcube_template.php 5481 2011-11-24 07:53:00Z alec $
 
  */
 
@@ -71,6 +71,7 @@ class rcube_template extends rcube_html_page
 
         //$this->framed = $framed;
         $this->set_env('task', $task);
+        $this->set_env('x_frame_options', $this->app->config->get('x_frame_options', 'sameorigin'));
 
         // load the correct skin (in case user-defined)
         $this->set_skin($this->config['skin']);
@@ -217,7 +218,9 @@ class rcube_template extends rcube_html_page
     public function command()
     {
         $cmd = func_get_args();
-        if (strpos($cmd[0], 'plugin.') === false)
+        if (strpos($cmd[0], 'plugin.') !== false)
+          $this->js_commands[] = array('triggerEvent', $cmd[0], $cmd[1]);
+        else
           $this->js_commands[] = $cmd;
     }
 
@@ -353,10 +356,6 @@ class rcube_template extends rcube_html_page
         $js .= $this->get_js_commands() . ($this->framed ? ' }' : '');
         $this->add_script($js, 'head_top');
 
-        // make sure all <form> tags have a valid request token
-        $template = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $template);
-        $this->footer = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $this->footer);
-        
         // send clickjacking protection headers
         $iframe = $this->framed || !empty($_REQUEST['_framed']);
         if (!headers_sent() && ($xframe = $this->app->config->get('x_frame_options', 'sameorigin')))
@@ -437,6 +436,10 @@ class rcube_template extends rcube_html_page
 
         $output = $this->parse_with_globals($hook['content']);
 
+        // make sure all <form> tags have a valid request token
+        $output = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $output);
+        $this->footer = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $this->footer);
+
         if ($write) {
             // add debug console
             if ($realname != 'error' && ($this->config['debug_level'] & 8)) {
@@ -689,7 +692,7 @@ class rcube_template extends rcube_html_page
                     $vars = $attrib + array('product' => $this->config['product_name']);
                     unset($vars['name'], $vars['command']);
                     $label = rcube_label($attrib + array('vars' => $vars));
-                    return !$attrbi['noshow'] ? Q($label) : '';
+                    return !$attrib['noshow'] ? (get_boolean((string)$attrib['html']) ? $label : Q($label)) : '';
                 }
                 break;
 
@@ -913,6 +916,7 @@ class rcube_template extends rcube_html_page
             // make valid href to specific buttons
             if (in_array($attrib['command'], rcmail::$main_tasks)) {
                 $attrib['href'] = rcmail_url(null, null, $attrib['command']);
+                $attrib['onclick'] = sprintf("%s.switch_task('%s');return false", JS_OBJECT_NAME, $attrib['command']);
             }
             else if ($attrib['task'] && in_array($attrib['task'], rcmail::$main_tasks)) {
                 $attrib['href'] = rcmail_url($attrib['command'], null, $attrib['task']);
@@ -1105,6 +1109,7 @@ class rcube_template extends rcube_html_page
         $input_task   = new html_hiddenfield(array('name' => '_task', 'value' => 'login'));
         $input_action = new html_hiddenfield(array('name' => '_action', 'value' => 'login'));
         $input_tzone  = new html_hiddenfield(array('name' => '_timezone', 'id' => 'rcmlogintz', 'value' => '_default_'));
+        $input_dst    = new html_hiddenfield(array('name' => '_dstactive', 'id' => 'rcmlogindst', 'value' => '_default_'));
         $input_url    = new html_hiddenfield(array('name' => '_url', 'id' => 'rcmloginurl', 'value' => $url));
         $input_user   = new html_inputfield(array('name' => '_user', 'id' => 'rcmloginuser')
             + $attrib + $user_attrib);
@@ -1142,20 +1147,21 @@ class rcube_template extends rcube_html_page
         $table = new html_table(array('cols' => 2));
 
         $table->add('title', html::label('rcmloginuser', Q(rcube_label('username'))));
-        $table->add(null, $input_user->show(get_input_value('_user', RCUBE_INPUT_GPC)));
+        $table->add('input', $input_user->show(get_input_value('_user', RCUBE_INPUT_GPC)));
 
         $table->add('title', html::label('rcmloginpwd', Q(rcube_label('password'))));
-        $table->add(null, $input_pass->show());
+        $table->add('input', $input_pass->show());
 
         // add host selection row
         if (is_object($input_host) && !$hide_host) {
             $table->add('title', html::label('rcmloginhost', Q(rcube_label('server'))));
-            $table->add(null, $input_host->show(get_input_value('_host', RCUBE_INPUT_GPC)));
+            $table->add('input', $input_host->show(get_input_value('_host', RCUBE_INPUT_GPC)));
         }
 
         $out  = $input_task->show();
         $out .= $input_action->show();
         $out .= $input_tzone->show();
+        $out .= $input_dst->show();
         $out .= $input_url->show();
         $out .= $table->show();
 
index 19d08117d7e5ffa2f02de41cd77ce04936987af2..cc006c0c13d1bea47e4053d488cd45835272328e 100644 (file)
@@ -16,7 +16,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_user.php 5165 2011-09-05 08:49:04Z thomasb $
+ $Id: rcube_user.php 5183 2011-09-06 17:18:12Z alec $
 
 */
 
@@ -47,6 +47,8 @@ class rcube_user
      */
     private $rc;
 
+    const SEARCH_ADDRESSBOOK = 1;
+    const SEARCH_MAIL = 2;
 
     /**
      * Object constructor
@@ -397,11 +399,8 @@ class rcube_user
     {
         $dbh = rcmail::get_instance()->get_dbh();
 
-        // use BINARY (case-sensitive) comparison on MySQL, other engines are case-sensitive
-        $mod = preg_match('/^mysql/', $dbh->db_provider) ? 'BINARY' : '';
-
         // query for matching user name
-        $query = "SELECT * FROM ".get_table_name('users')." WHERE mail_host = ? AND %s = $mod ?";
+        $query = "SELECT * FROM ".get_table_name('users')." WHERE mail_host = ? AND %s = ?";
         $sql_result = $dbh->query(sprintf($query, 'username'), $host, $user);
 
         // query for matching alias
@@ -551,4 +550,129 @@ class rcube_user
         return empty($plugin['email']) ? NULL : $plugin['email'];
     }
 
+
+    /**
+     * Return a list of saved searches linked with this user
+     *
+     * @param int  $type  Search type
+     *
+     * @return array List of saved searches indexed by search ID
+     */
+    function list_searches($type)
+    {
+        $plugin = $this->rc->plugins->exec_hook('saved_search_list', array('type' => $type));
+
+        if ($plugin['abort']) {
+            return (array) $plugin['result'];
+        }
+
+        $result = array();
+
+        $sql_result = $this->db->query(
+            "SELECT search_id AS id, ".$this->db->quoteIdentifier('name')
+            ." FROM ".get_table_name('searches')
+            ." WHERE user_id = ?"
+                ." AND ".$this->db->quoteIdentifier('type')." = ?"
+            ." ORDER BY ".$this->db->quoteIdentifier('name'),
+            (int) $this->ID, (int) $type);
+
+        while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
+            $sql_arr['data'] = unserialize($sql_arr['data']);
+            $result[$sql_arr['id']] = $sql_arr;
+        }
+
+        return $result;
+    }
+
+
+    /**
+     * Return saved search data.
+     *
+     * @param int  $id  Row identifier
+     *
+     * @return array Data
+     */
+    function get_search($id)
+    {
+        $plugin = $this->rc->plugins->exec_hook('saved_search_get', array('id' => $id));
+
+        if ($plugin['abort']) {
+            return $plugin['result'];
+        }
+
+        $sql_result = $this->db->query(
+            "SELECT ".$this->db->quoteIdentifier('name')
+                .", ".$this->db->quoteIdentifier('data')
+                .", ".$this->db->quoteIdentifier('type')
+            ." FROM ".get_table_name('searches')
+            ." WHERE user_id = ?"
+                ." AND search_id = ?",
+            (int) $this->ID, (int) $id);
+
+        while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
+            return array(
+                'id'   => $id,
+                'name' => $sql_arr['name'],
+                'type' => $sql_arr['type'],
+                'data' => unserialize($sql_arr['data']),
+            );
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Deletes given saved search record
+     *
+     * @param  int  $sid  Search ID
+     *
+     * @return boolean True if deleted successfully, false if nothing changed
+     */
+    function delete_search($sid)
+    {
+        if (!$this->ID)
+            return false;
+
+        $this->db->query(
+            "DELETE FROM ".get_table_name('searches')
+            ." WHERE user_id = ?"
+                ." AND search_id = ?",
+            (int) $this->ID, $sid);
+
+        return $this->db->affected_rows();
+    }
+
+
+    /**
+     * Create a new saved search record linked with this user
+     *
+     * @param array $data Hash array with col->value pairs to save
+     *
+     * @return int  The inserted search ID or false on error
+     */
+    function insert_search($data)
+    {
+        if (!$this->ID)
+            return false;
+
+        $insert_cols[]   = 'user_id';
+        $insert_values[] = (int) $this->ID;
+        $insert_cols[]   = $this->db->quoteIdentifier('type');
+        $insert_values[] = (int) $data['type'];
+        $insert_cols[]   = $this->db->quoteIdentifier('name');
+        $insert_values[] = $data['name'];
+        $insert_cols[]   = $this->db->quoteIdentifier('data');
+        $insert_values[] = serialize($data['data']);
+
+        $sql = "INSERT INTO ".get_table_name('searches')
+            ." (".join(', ', $insert_cols).")"
+            ." VALUES (".join(', ', array_pad(array(), sizeof($insert_values), '?')).")";
+
+        call_user_func_array(array($this->db, 'query'),
+            array_merge(array($sql), $insert_values));
+
+        return $this->db->insert_id('searches');
+    }
+
 }
index c02210becd4e31f60d3e9ca1b6f1e741907cccbc..dba020af7d0727b1c35886d43cf0d34d953ab732 100644 (file)
@@ -14,7 +14,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: rcube_vcard.php 5165 2011-09-05 08:49:04Z thomasb $
+ $Id: rcube_vcard.php 5160 2011-09-05 07:40:18Z thomasb $
 
 */
 
index 5957acf2b396e10ea7d4b372f690e8b4bbe3270b..c871cc66356370d9bb6541afcc1e6d2ede3dae96 100644 (file)
-function rcube_webmail(){this.env={};this.labels={};this.buttons={};this.buttons_sel={};this.gui_objects={};this.gui_containers={};this.commands={};this.command_handlers={};this.onloads=[];this.messages={};this.ref="rcmail";var l=this;this.dblclick_time=500;this.message_time=2E3;this.identifier_expr=RegExp("[^0-9a-z-_]","gi");this.env.keep_alive=60;this.env.request_timeout=180;this.env.draft_autosave=0;this.env.comm_path="./";this.env.blankpage="program/blank.gif";$.ajaxSetup({cache:!1,error:function(a,
-b,d){l.http_error(a,b,d)},beforeSend:function(a){a.setRequestHeader("X-Roundcube-Request",l.env.request_token)}});this.set_env=function(a,b){if(a!=null&&typeof a==="object"&&!b)for(var d in a)this.env[d]=a[d];else this.env[a]=b};this.add_label=function(a,b){typeof a=="string"?this.labels[a]=b:typeof a=="object"&&$.extend(this.labels,a)};this.register_button=function(a,b,d,e,f,g){this.buttons[a]||(this.buttons[a]=[]);b={id:b,type:d};if(e)b.act=e;if(f)b.sel=f;if(g)b.over=g;this.buttons[a].push(b);this.loaded&&
-t(a,b)};this.gui_object=function(a,b){this.gui_objects[a]=this.loaded?rcube_find_object(b):b};this.gui_container=function(a,b){this.gui_containers[a]=b};this.add_element=function(a,b){this.gui_containers[b]&&this.gui_containers[b].jquery&&this.gui_containers[b].append(a)};this.register_command=function(a,b,d){this.command_handlers[a]=b;d&&this.enable_command(a,!0)};this.add_onload=function(a){this.onloads.push(a)};this.init=function(){var a=this;this.task=this.env.task;if(!bw.dom||!bw.xmlhttp_test())this.goto_url("error",
-"_code=0x199");else{for(var b in this.gui_containers)this.gui_containers[b]=$("#"+this.gui_containers[b]);for(b in this.gui_objects)this.gui_objects[b]=rcube_find_object(this.gui_objects[b]);this.init_buttons();if(this.is_framed())parent.rcmail.set_busy(!1,null,parent.rcmail.env.frame_lock),parent.rcmail.env.frame_lock=null;this.enable_command("logout","mail","addressbook","settings","save-pref","undo",!0);this.env.permaurl&&this.enable_command("permaurl",!0);switch(this.task){case "mail":this.enable_command("list",
-"checkmail","compose","add-contact","search","reset-search","collapse-folder",!0);if(this.gui_objects.messagelist)this.message_list=new rcube_list_widget(this.gui_objects.messagelist,{multiselect:!0,multiexpand:!0,draggable:!0,keyboard:!0,column_movable:this.env.col_movable,dblclick_time:this.dblclick_time}),this.message_list.row_init=function(b){a.init_message_row(b)},this.message_list.addEventListener("dblclick",function(b){a.msglist_dbl_click(b)}),this.message_list.addEventListener("click",function(b){a.msglist_click(b)}),
-this.message_list.addEventListener("keypress",function(b){a.msglist_keypress(b)}),this.message_list.addEventListener("select",function(b){a.msglist_select(b)}),this.message_list.addEventListener("dragstart",function(b){a.drag_start(b)}),this.message_list.addEventListener("dragmove",function(b){a.drag_move(b)}),this.message_list.addEventListener("dragend",function(b){a.drag_end(b)}),this.message_list.addEventListener("expandcollapse",function(b){a.msglist_expand(b)}),this.message_list.addEventListener("column_replace",
-function(b){a.msglist_set_coltypes(b)}),document.onmouseup=function(b){return a.doc_mouse_up(b)},this.gui_objects.messagelist.parentNode.onmousedown=function(b){return a.click_on_list(b)},this.message_list.init(),this.enable_command("toggle_status","toggle_flag","menu-open","menu-save",!0),this.command("list");if(this.gui_objects.qsearchbox){if(this.env.search_text!=null)this.gui_objects.qsearchbox.value=this.env.search_text;$(this.gui_objects.qsearchbox).focusin(function(){rcmail.message_list.blur()})}!this.env.flag_for_deletion&&
-this.env.trash_mailbox&&this.env.mailbox!=this.env.trash_mailbox&&this.set_alttext("delete","movemessagetotrash");this.env.message_commands="show,reply,reply-all,reply-list,forward,moveto,copy,delete,open,mark,edit,viewsource,download,print,load-attachment,load-headers,forward-attachment".split(",");if(this.env.action=="show"||this.env.action=="preview"){this.enable_command(this.env.message_commands,this.env.uid);this.enable_command("reply-list",this.env.list_post);this.env.action=="show"&&this.http_request("pagenav",
-"_uid="+this.env.uid+"&_mbox="+urlencode(this.env.mailbox),this.display_message("","loading"));if(this.env.blockedobjects){if(this.gui_objects.remoteobjectsmsg)this.gui_objects.remoteobjectsmsg.style.display="block";this.enable_command("load-images","always-load",!0)}this.env.action=="preview"&&this.is_framed()&&(this.enable_command("compose","add-contact",!1),parent.rcmail.show_contentframe(!0))}else if(this.env.action=="compose"){this.env.compose_commands=["send-attachment","remove-attachment",
-"send","cancel","toggle-editor"];this.env.drafts_mailbox&&this.env.compose_commands.push("savedraft");this.enable_command(this.env.compose_commands,"identities",!0);if(this.env.spellcheck)this.env.spellcheck.spelling_state_observer=function(a){l.set_spellcheck_state(a)},this.env.compose_commands.push("spellcheck"),this.set_spellcheck_state("ready"),$("input[name='_is_html']").val()=="1"&&this.display_spellcheck_controls(!1);document.onmouseup=function(b){return a.doc_mouse_up(b)};this.init_messageform()}else this.env.action==
-"print"&&this.env.uid&&(bw.safari?window.setTimeout("window.print()",10):window.print());if(this.gui_objects.mailboxlist)this.env.unread_counts={},this.gui_objects.folderlist=this.gui_objects.mailboxlist,this.http_request("getunread","");this.env.mdn_request&&this.env.uid&&(b="_uid="+this.env.uid+"&_mbox="+urlencode(this.env.mailbox),confirm(this.get_label("mdnrequest"))?this.http_post("sendmdn",b):this.http_post("mark",b+"&_flag=mdnsent"));break;case "addressbook":if(this.gui_objects.folderlist)this.env.contactfolders=
-$.extend($.extend({},this.env.address_sources),this.env.contactgroups);if(this.gui_objects.contactslist)this.contact_list=new rcube_list_widget(this.gui_objects.contactslist,{multiselect:!0,draggable:this.gui_objects.folderlist?!0:!1,keyboard:!0}),this.contact_list.row_init=function(b){a.triggerEvent("insertrow",{cid:b.uid,row:b})},this.contact_list.addEventListener("keypress",function(b){a.contactlist_keypress(b)}),this.contact_list.addEventListener("select",function(b){a.contactlist_select(b)}),
-this.contact_list.addEventListener("dragstart",function(b){a.drag_start(b)}),this.contact_list.addEventListener("dragmove",function(b){a.drag_move(b)}),this.contact_list.addEventListener("dragend",function(b){a.drag_end(b)}),this.contact_list.init(),this.env.cid&&this.contact_list.highlight_row(this.env.cid),this.gui_objects.contactslist.parentNode.onmousedown=function(b){return a.click_on_list(b)},document.onmouseup=function(b){return a.doc_mouse_up(b)},this.gui_objects.qsearchbox&&$(this.gui_objects.qsearchbox).focusin(function(){rcmail.contact_list.blur()}),
-this.update_group_commands();this.set_page_buttons();this.env.cid&&(this.enable_command("show","edit",!0),this.gui_objects.editform&&$("input.groupmember").change(function(){l.group_member_change(this.checked?"add":"del",l.env.cid,l.env.source,this.value)}));this.gui_objects.editform&&(this.enable_command("save",!0),(this.env.action=="add"||this.env.action=="edit")&&this.init_contact_form());this.gui_objects.qsearchbox&&this.enable_command("search","reset-search","moveto",!0);this.contact_list&&this.contact_list.rowcount>
-0&&this.enable_command("export",!0);this.enable_command("add","import",this.env.writable_source);this.enable_command("list","listgroup","advanced-search",!0);this.env.action||this.command("list",this.env.source);break;case "settings":this.enable_command("preferences","identities","save","folders",!0);if(this.env.action=="identities")this.enable_command("add",this.env.identities_level<2);else if(this.env.action=="edit-identity"||this.env.action=="add-identity")this.enable_command("add",this.env.identities_level<
-2),this.enable_command("save","delete","edit","toggle-editor",!0);else if(this.env.action=="folders")this.enable_command("subscribe","unsubscribe","create-folder","rename-folder",!0);else if(this.env.action=="edit-folder"&&this.gui_objects.editform)this.enable_command("save","folder-size",!0),parent.rcmail.env.messagecount=this.env.messagecount,parent.rcmail.enable_command("purge",this.env.messagecount),$("input[type='text']").first().select();this.gui_objects.identitieslist?(this.identity_list=new rcube_list_widget(this.gui_objects.identitieslist,
-{multiselect:!1,draggable:!1,keyboard:!1}),this.identity_list.addEventListener("select",function(b){a.identity_select(b)}),this.identity_list.init(),this.identity_list.focus(),this.env.iid&&this.identity_list.highlight_row(this.env.iid)):this.gui_objects.sectionslist?(this.sections_list=new rcube_list_widget(this.gui_objects.sectionslist,{multiselect:!1,draggable:!1,keyboard:!1}),this.sections_list.addEventListener("select",function(b){a.section_select(b)}),this.sections_list.init(),this.sections_list.focus()):
-this.gui_objects.subscriptionlist&&this.init_subscription_list();break;case "login":b=$("#rcmloginuser"),b.bind("keyup",function(a){return rcmail.login_user_keyup(a)}),b.val()==""?b.focus():$("#rcmloginpwd").focus(),$("#rcmlogintz").val((new Date).getTimezoneOffset()/-60),$("form").submit(function(){$("input[type=submit]",this).prop("disabled",!0);rcmail.display_message("","loading")}),this.enable_command("login",!0)}bw.ie&&$("input[type=file]").keydown(function(a){a.keyCode=="13"&&a.preventDefault()});
-this.loaded=!0;this.pending_message&&this.display_message(this.pending_message[0],this.pending_message[1],this.pending_message[2]);if(this.gui_objects.folderlist)this.gui_containers.foldertray=$(this.gui_objects.folderlist);this.triggerEvent("init",{task:this.task,action:this.env.action});for(var d in this.onloads)if(typeof this.onloads[d]==="string")eval(this.onloads[d]);else if(typeof this.onloads[d]==="function")this.onloads[d]();this.start_keepalive()}};this.log=function(a){window.console&&console.log&&
-console.log(a)};this.command=function(a,b,d){d&&d.blur&&d.blur();if(this.busy)return!1;if(!this.commands[a])return this.is_framed()&&parent.rcmail.command(a,b),!1;if(this.task=="mail"&&this.env.action=="compose"&&$.inArray(a,this.env.compose_commands)<0&&this.cmp_hash!=this.compose_field_hash()&&!confirm(this.get_label("notsentwarning")))return!1;if(typeof this.command_handlers[a]==="function"){var e=this.command_handlers[a](b,d);return e!==void 0?e:d?!1:!0}else if(typeof this.command_handlers[a]===
-"string")return e=window[this.command_handlers[a]](b,d),e!==void 0?e:d?!1:!0;this.triggerEvent("actionbefore",{props:b,action:a});e=this.triggerEvent("before"+a,b);if(e!==void 0)if(e===!1)return!1;else b=e;switch(a){case "login":this.gui_objects.loginform&&this.gui_objects.loginform.submit();break;case "mail":case "addressbook":case "settings":case "logout":this.switch_task(a);break;case "permaurl":if(d&&d.href&&d.target)return!0;else if(this.env.permaurl)parent.location.href=this.env.permaurl;break;
-case "menu-open":case "menu-save":return this.triggerEvent(a,{props:b}),!1;case "open":var f;if(f=this.get_single_uid())return d.href="?_task="+this.env.task+"&_action=show&_mbox="+urlencode(this.env.mailbox)+"&_uid="+f,!0;break;case "list":this.task=="mail"?((!this.env.search_request||b&&b!=this.env.mailbox)&&this.reset_qsearch(),this.list_mailbox(b),this.env.trash_mailbox&&!this.env.flag_for_deletion&&this.set_alttext("delete",this.env.mailbox!=this.env.trash_mailbox?"movemessagetotrash":"deletemessage")):
-this.task=="addressbook"&&((!this.env.search_request||b!=this.env.source)&&this.reset_qsearch(),this.list_contacts(b),this.enable_command("add","import",this.env.writable_source));break;case "load-headers":this.load_headers(d);break;case "sort":var g;f=b;g=this.env.sort_col==f?this.env.sort_order=="ASC"?"DESC":"ASC":"ASC";this.set_list_sorting(f,g);this.list_mailbox("","",f+"_"+g);break;case "nextpage":this.list_page("next");break;case "lastpage":this.list_page("last");break;case "previouspage":this.list_page("prev");
-break;case "firstpage":this.list_page("first");break;case "expunge":this.env.messagecount&&this.expunge_mailbox(this.env.mailbox);break;case "purge":case "empty-mailbox":this.env.messagecount&&this.purge_mailbox(this.env.mailbox);break;case "show":if(this.task=="mail"){if((f=this.get_single_uid())&&(!this.env.uid||f!=this.env.uid))this.env.mailbox==this.env.drafts_mailbox?this.goto_url("compose","_draft_uid="+f+"&_mbox="+urlencode(this.env.mailbox),!0):this.show_message(f)}else if(this.task=="addressbook"){var h=
-b?b:this.get_single_cid();h&&!(this.env.action=="show"&&h==this.env.cid)&&this.load_contact(h,"show")}break;case "add":this.task=="addressbook"?this.load_contact(0,"add"):this.task=="settings"&&(this.identity_list.clear_selection(),this.load_identity(0,"add-identity"));break;case "edit":if(this.task=="addressbook"&&(h=this.get_single_cid()))this.load_contact(h,"edit");else if(this.task=="settings"&&b)this.load_identity(b,"edit-identity");else if(this.task=="mail"&&(h=this.get_single_uid()))g=this.env.mailbox==
-this.env.drafts_mailbox?"_draft_uid=":"_uid=",this.goto_url("compose",g+h+"&_mbox="+urlencode(this.env.mailbox),!0);break;case "save":var k;if(g=this.gui_objects.editform){if(this.env.action!="search")if((k=$("input[name='_pagesize']",g))&&k.length&&isNaN(parseInt(k.val()))){alert(this.get_label("nopagesizewarning"));k.focus();break}else{if(b=="reload")g.action+="?_reload=1";else if(this.task=="settings"&&this.env.identities_level%2==0&&(k=$("input[name='_email']",g))&&k.length&&!rcube_check_email(k.val())){alert(this.get_label("noemailwarning"));
-k.focus();break}$("input.placeholder").each(function(){if(this.value==this._placeholder)this.value=""})}if(parent.rcmail&&parent.rcmail.env.source)g.action=this.add_url(g.action,"_orig_source",parent.rcmail.env.source);g.submit()}break;case "delete":this.task=="mail"?this.delete_messages():this.task=="addressbook"?this.delete_contacts():this.task=="settings"&&this.delete_identity();break;case "move":case "moveto":this.task=="mail"?this.move_messages(b):this.task=="addressbook"&&this.drag_active&&
-this.copy_contact(null,b);break;case "copy":this.task=="mail"&&this.copy_messages(b);break;case "mark":b&&this.mark_message(b);break;case "toggle_status":if(b&&!b._row)break;g="read";if(b._row.uid)f=b._row.uid,this.message_list.rows[f].deleted?g="undelete":this.message_list.rows[f].unread||(g="unread");this.mark_message(g,f);break;case "toggle_flag":if(b&&!b._row)break;g="flagged";if(b._row.uid)f=b._row.uid,this.message_list.rows[f].flagged&&(g="unflagged");this.mark_message(g,f);break;case "always-load":if(this.env.uid&&
-this.env.sender){this.add_contact(urlencode(this.env.sender));window.setTimeout(function(){l.command("load-images")},300);break}case "load-images":this.env.uid&&this.show_message(this.env.uid,!0,this.env.action=="preview");break;case "load-attachment":g="_mbox="+urlencode(this.env.mailbox)+"&_uid="+this.env.uid+"&_part="+b.part;if(this.env.uid&&b.mimetype&&this.env.mimetypes&&$.inArray(b.mimetype,this.env.mimetypes)>=0&&(b.mimetype=="text/html"&&(g+="&_safe=1"),this.attachment_win=window.open(this.env.comm_path+
-"&_action=get&"+g+"&_frame=1","rcubemailattachment"))){window.setTimeout(function(){l.attachment_win.focus()},10);break}this.goto_url("get",g+"&_download=1",!1);break;case "select-all":this.select_all_mode=b?!1:!0;this.dummy_select=!0;b=="invert"?this.message_list.invert_selection():this.message_list.select_all(b=="page"?"":b);this.dummy_select=null;break;case "select-none":this.select_all_mode=!1;this.message_list.clear_selection();break;case "expand-all":this.env.autoexpand_threads=1;this.message_list.expand_all();
-break;case "expand-unread":this.env.autoexpand_threads=2;this.message_list.collapse_all();this.expand_unread();break;case "collapse-all":this.env.autoexpand_threads=0;this.message_list.collapse_all();break;case "nextmessage":this.env.next_uid&&this.show_message(this.env.next_uid,!1,this.env.action=="preview");break;case "lastmessage":this.env.last_uid&&this.show_message(this.env.last_uid);break;case "previousmessage":this.env.prev_uid&&this.show_message(this.env.prev_uid,!1,this.env.action=="preview");
-break;case "firstmessage":this.env.first_uid&&this.show_message(this.env.first_uid);break;case "checkmail":this.check_for_recent(!0);break;case "compose":g=this.env.comm_path+"&_action=compose";if(this.task=="mail")if(g+="&_mbox="+urlencode(this.env.mailbox),this.env.mailbox==this.env.drafts_mailbox){if(f=this.get_single_uid())g+="&_draft_uid="+f}else b&&(g+="&_to="+urlencode(b));else if(this.task=="addressbook"){if(b&&b.indexOf("@")>0){g=this.get_task_url("mail",g);this.redirect(g+"&_to="+urlencode(b));
-break}h=[];if(b)h.push(b);else if(this.contact_list){k=this.contact_list.get_selection();for(g=0,f=k.length;g<f;g++)h.push(k[g])}h.length&&this.http_post("mailto",{_cid:h.join(","),_source:this.env.source},!0);break}this.redirect(g);break;case "spellcheck":window.tinyMCE&&tinyMCE.get(this.env.composebody)?tinyMCE.execCommand("mceSpellCheck",!0):this.env.spellcheck&&this.env.spellcheck.spellCheck&&this.spellcheck_ready&&(this.env.spellcheck.spellCheck(),this.set_spellcheck_state("checking"));break;
-case "savedraft":self.clearTimeout(this.save_timer);if(!this.gui_objects.messageform)break;if(!this.env.drafts_mailbox||this.cmp_hash==this.compose_field_hash())break;g=this.gui_objects.messageform;f=this.set_busy(!0,"savingmessage");g.target="savetarget";g._draft.value="1";g.action=this.add_url(g.action,"_unlock",f);g.submit();break;case "send":if(!this.gui_objects.messageform)break;if(!this.check_compose_input())break;self.clearTimeout(this.save_timer);h=this.spellcheck_lang();g=this.gui_objects.messageform;
-f=this.set_busy(!0,"sendingmessage");g.target="savetarget";g._draft.value="";g.action=this.add_url(g.action,"_unlock",f);g.action=this.add_url(g.action,"_lang",h);g.submit();clearTimeout(this.request_timer);break;case "send-attachment":self.clearTimeout(this.save_timer);this.upload_file(b);break;case "insert-sig":this.change_identity($("[name='_from']")[0],!0);break;case "reply-all":case "reply-list":case "reply":if(f=this.get_single_uid())g="_reply_uid="+f+"&_mbox="+urlencode(this.env.mailbox),a==
-"reply-all"?g+="&_all="+(!b&&this.commands["reply-list"]?"list":"all"):a=="reply-list"&&(g+="&_all=list"),this.goto_url("compose",g,!0);break;case "forward-attachment":case "forward":if(f=this.get_single_uid()){g="_forward_uid="+f+"&_mbox="+urlencode(this.env.mailbox);if(a=="forward-attachment"||!b&&this.env.forward_attachment)g+="&_attachment=1";this.goto_url("compose",g,!0)}break;case "print":if(f=this.get_single_uid())l.printwin=window.open(this.env.comm_path+"&_action=print&_uid="+f+"&_mbox="+
-urlencode(this.env.mailbox)+(this.env.safemode?"&_safe=1":"")),this.printwin&&(window.setTimeout(function(){l.printwin.focus()},20),this.env.action!="show"&&this.mark_message("read",f));break;case "viewsource":if(f=this.get_single_uid())l.sourcewin=window.open(this.env.comm_path+"&_action=viewsource&_uid="+f+"&_mbox="+urlencode(this.env.mailbox)),this.sourcewin&&window.setTimeout(function(){l.sourcewin.focus()},20);break;case "download":(f=this.get_single_uid())&&this.goto_url("viewsource","&_uid="+
-f+"&_mbox="+urlencode(this.env.mailbox)+"&_save=1");break;case "search":if(!b&&this.gui_objects.qsearchbox)b=this.gui_objects.qsearchbox.value;if(b){this.qsearch(b);break}case "reset-search":f=this.env.search_request||this.env.qsearch;this.reset_qsearch();this.select_all_mode=!1;if(f&&this.env.mailbox)this.list_mailbox(this.env.mailbox,1);else if(f&&this.task=="addressbook"){if(this.env.source==""){for(g in this.env.address_sources)break;this.env.source=g;this.env.group=""}this.list_contacts(this.env.source,
-this.env.group,1)}break;case "listgroup":this.list_contacts(b.source,b.id);break;case "import":if(this.env.action=="import"&&this.gui_objects.importform){if((g=document.getElementById("rcmimportfile"))&&!g.value){alert(this.get_label("selectimportfile"));break}this.gui_objects.importform.submit();this.set_busy(!0,"importwait");this.lock_form(this.gui_objects.importform,!0)}else this.goto_url("import",this.env.source?"_target="+urlencode(this.env.source)+"&":"");break;case "export":this.contact_list.rowcount>
-0&&this.goto_url("export",{_source:this.env.source,_gid:this.env.group,_search:this.env.search_request});break;case "upload-photo":this.upload_contact_photo(b);break;case "delete-photo":this.replace_contact_photo("-del-");break;case "preferences":case "identities":case "folders":this.goto_url("settings/"+a);break;case "undo":this.http_request("undo","",this.display_message("","loading"));break;default:if(g=a.replace(/-/g,"_"),this[g]&&typeof this[g]==="function")this[g](b)}this.triggerEvent("after"+
-a,b);this.triggerEvent("actionafter",{props:b,action:a});return d?!1:!0};this.enable_command=function(){for(var a=Array.prototype.slice.call(arguments),b=a.pop(),d,e=0;e<a.length;e++)if(d=a[e],typeof d==="string")this.commands[d]=b,this.set_button(d,b?"act":"pas");else for(var f in d)a.push(d[f])};this.set_busy=function(a,b,d){a&&b?(d=this.get_label(b),d==b&&(d="Loading..."),d=this.display_message(d,"loading")):!a&&d&&this.hide_message(d);this.busy=a;this.gui_objects.editform&&this.lock_form(this.gui_objects.editform,
-a);this.request_timer&&clearTimeout(this.request_timer);if(a&&this.env.request_timeout)this.request_timer=window.setTimeout(function(){l.request_timed_out()},this.env.request_timeout*1E3);return d};this.gettext=this.get_label=function(a,b){return b&&this.labels[b+"."+a]?this.labels[b+"."+a]:this.labels[a]?this.labels[a]:a};this.switch_task=function(a){if(!(this.task===a&&a!="mail")){var b=this.get_task_url(a);a=="mail"&&(b+="&_mbox=INBOX");this.redirect(b)}};this.get_task_url=function(a,b){if(!b)b=
-this.env.comm_path;return b.replace(/_task=[a-z]+/,"_task="+a)};this.request_timed_out=function(){this.set_busy(!1);this.display_message("Request timed out!","error")};this.reload=function(a){if(this.is_framed())parent.rcmail.reload(a);else if(a)window.setTimeout(function(){rcmail.reload()},a);else if(window.location)location.href=this.env.comm_path+(this.env.action?"&_action="+this.env.action:"")};this.add_url=function(a,b,d){d=urlencode(d);if(/(\?.*)$/.test(a)){var e=RegExp.$1,f=RegExp("((\\?|&)"+
-RegExp.escape(b)+"=[^&]*)");f.test(e)?e=e.replace(f,RegExp.$2+b+"="+d):e+="&"+b+"="+d;return a.replace(/(\?.*)$/,e)}else return a+"?"+b+"="+d};this.is_framed=function(){return this.env.framed&&parent.rcmail&&parent.rcmail!=this&&parent.rcmail.command};this.save_pref=function(a){var b={_name:a.name,_value:a.value};if(a.session)b._session=a.session;if(a.env)this.env[a.env]=a.value;this.http_post("save-pref",b)};this.drag_menu=function(a,b){var d=rcube_event.get_modifier(a),e=this.gui_objects.message_dragmenu;
-return e&&d==SHIFT_KEY&&this.commands.copy?(d=rcube_event.get_mouse_pos(a),this.env.drag_target=b,$(e).css({top:d.y-10+"px",left:d.x-10+"px"}).show(),!0):!1};this.drag_menu_action=function(a){var b=this.gui_objects.message_dragmenu;b&&$(b).hide();this.command(a,this.env.drag_target);this.env.drag_target=null};this.drag_start=function(a){var b=this.task=="mail"?this.env.mailboxes:this.env.contactfolders;this.drag_active=!0;this.preview_timer&&clearTimeout(this.preview_timer);this.preview_read_timer&&
-clearTimeout(this.preview_read_timer);if(this.gui_objects.folderlist&&b){this.initialBodyScrollTop=bw.ie?0:window.pageYOffset;this.initialListScrollTop=this.gui_objects.folderlist.parentNode.scrollTop;var d,e,a=$(this.gui_objects.folderlist);d=a.offset();this.env.folderlist_coords={x1:d.left,y1:d.top,x2:d.left+a.width(),y2:d.top+a.height()};this.env.folder_coords=[];for(var f in b)if(a=this.get_folder_li(f))if(e=a.firstChild.offsetHeight)d=$(a.firstChild).offset(),this.env.folder_coords[f]={x1:d.left,
-y1:d.top,x2:d.left+a.firstChild.offsetWidth,y2:d.top+e,on:0}}};this.drag_end=function(){this.drag_active=!1;this.env.last_folder_target=null;if(this.folder_auto_timer)window.clearTimeout(this.folder_auto_timer),this.folder_auto_expand=this.folder_auto_timer=null;if(this.gui_objects.folderlist&&this.env.folder_coords)for(var a in this.env.folder_coords)this.env.folder_coords[a].on&&$(this.get_folder_li(a)).removeClass("droptarget")};this.drag_move=function(a){if(this.gui_objects.folderlist&&this.env.folder_coords){var b=
--(this.initialListScrollTop-this.gui_objects.folderlist.parentNode.scrollTop)-(bw.ie?-document.documentElement.scrollTop:this.initialBodyScrollTop),d,e,f;d="draglayernormal";this.contact_list&&this.contact_list.draglayer&&(f=this.contact_list.draglayer.attr("class"));a=rcube_event.get_mouse_pos(a);e=this.env.folderlist_coords;a.y+=b;if(a.x<e.x1||a.x>=e.x2||a.y<e.y1||a.y>=e.y2){if(this.env.last_folder_target)$(this.get_folder_li(this.env.last_folder_target)).removeClass("droptarget"),this.env.folder_coords[this.env.last_folder_target].on=
-0,this.env.last_folder_target=null}else for(var g in this.env.folder_coords)if(e=this.env.folder_coords[g],a.x>=e.x1&&a.x<e.x2&&a.y>=e.y1&&a.y<e.y2)if(b=this.check_droptarget(g)){d=this.get_folder_li(g);e=$(d.getElementsByTagName("div")[0]);if(e.hasClass("collapsed"))this.folder_auto_timer&&window.clearTimeout(this.folder_auto_timer),this.folder_auto_expand=g,this.folder_auto_timer=window.setTimeout(function(){rcmail.command("collapse-folder",rcmail.folder_auto_expand);rcmail.drag_start(null)},1E3);
-else if(this.folder_auto_timer)window.clearTimeout(this.folder_auto_timer),this.folder_auto_expand=this.folder_auto_timer=null;$(d).addClass("droptarget");this.env.folder_coords[g].on=1;this.env.last_folder_target=g;d="draglayer"+(b>1?"copy":"normal")}else this.env.last_folder_target=null;else if(e.on)$(this.get_folder_li(g)).removeClass("droptarget"),this.env.folder_coords[g].on=0;d!=f&&this.contact_list&&this.contact_list.draglayer&&this.contact_list.draglayer.attr("class",d)}};this.collapse_folder=
-function(a){var b=this.get_folder_li(a),d=$(b.getElementsByTagName("div")[0]);if(d&&(d.hasClass("collapsed")||d.hasClass("expanded"))){var e=$(b.getElementsByTagName("ul")[0]);d.hasClass("collapsed")?(e.show(),d.removeClass("collapsed").addClass("expanded"),this.env.collapsed_folders=this.env.collapsed_folders.replace(RegExp("&"+urlencode(a)+"&"),"")):(e.hide(),d.removeClass("expanded").addClass("collapsed"),this.env.collapsed_folders=this.env.collapsed_folders+"&"+urlencode(a)+"&",this.env.mailbox.indexOf(a+
-this.env.delimiter)==0&&this.command("list",a));if(bw.ie6||bw.ie7)if((d=b.nextSibling?b.nextSibling.getElementsByTagName("ul"):null)&&d.length&&(b=d[0])&&b.style&&b.style.display!="none")b.style.display="none",b.style.display="";this.command("save-pref",{name:"collapsed_folders",value:this.env.collapsed_folders});this.set_unread_count_display(a,!1)}};this.doc_mouse_up=function(a){var b,d,e;(d=this.message_list)?(rcube_mouse_is_over(a,d.list.parentNode)?d.focus():d.blur(),b=this.env.mailboxes):(d=
-this.contact_list)?(rcube_mouse_is_over(a,d.list.parentNode)?d.focus():d.blur(),b=this.env.contactfolders):this.ksearch_value&&this.ksearch_blur();if(this.drag_active&&b&&this.env.last_folder_target)b=b[this.env.last_folder_target],$(this.get_folder_li(this.env.last_folder_target)).removeClass("droptarget"),this.env.last_folder_target=null,d.draglayer.hide(),this.drag_menu(a,b)||this.command("moveto",b);if(this.buttons_sel){for(e in this.buttons_sel)typeof e!=="function"&&this.button_out(this.buttons_sel[e],
-e);this.buttons_sel={}}};this.click_on_list=function(){this.gui_objects.qsearchbox&&this.gui_objects.qsearchbox.blur();this.message_list?this.message_list.focus():this.contact_list&&this.contact_list.focus();return!0};this.msglist_select=function(a){this.preview_timer&&clearTimeout(this.preview_timer);this.preview_read_timer&&clearTimeout(this.preview_read_timer);var b=a.get_single_selection()!=null;this.enable_command(this.env.message_commands,b);b&&(this.env.mailbox==this.env.drafts_mailbox?this.enable_command("reply",
-"reply-all","reply-list","forward","forward-attachment",!1):this.env.messages[a.get_single_selection()].ml||this.enable_command("reply-list",!1));this.enable_command("delete","moveto","copy","mark",a.selection.length>0?!0:!1);if(b||a.selection.length&&a.selection.length!=a.rowcount)this.select_all_mode=!1;b&&this.env.contentframe&&!a.multi_selecting&&!this.dummy_select?this.preview_timer=window.setTimeout(function(){l.msglist_get_preview()},200):this.env.contentframe&&this.show_contentframe(!1)};
-this.msglist_click=function(a){if(!a.multi_selecting&&this.env.contentframe&&a.get_single_selection()&&window.frames&&window.frames[this.env.contentframe]&&window.frames[this.env.contentframe].location.href.indexOf(this.env.blankpage)>=0)this.preview_timer&&clearTimeout(this.preview_timer),this.preview_read_timer&&clearTimeout(this.preview_read_timer),this.preview_timer=window.setTimeout(function(){l.msglist_get_preview()},200)};this.msglist_dbl_click=function(a){this.preview_timer&&clearTimeout(this.preview_timer);
-this.preview_read_timer&&clearTimeout(this.preview_read_timer);(a=a.get_single_selection())&&this.env.mailbox==this.env.drafts_mailbox?this.goto_url("compose","_draft_uid="+a+"&_mbox="+urlencode(this.env.mailbox),!0):a&&this.show_message(a,!1,!1)};this.msglist_keypress=function(a){a.key_pressed==a.ENTER_KEY?this.command("show"):a.key_pressed==a.DELETE_KEY?this.command("delete"):a.key_pressed==a.BACKSPACE_KEY?this.command("delete"):a.key_pressed==33?this.command("previouspage"):a.key_pressed==34&&
-this.command("nextpage")};this.msglist_get_preview=function(){var a=this.get_single_uid();a&&this.env.contentframe&&!this.drag_active?this.show_message(a,!1,!0):this.env.contentframe&&this.show_contentframe(!1)};this.msglist_expand=function(a){if(this.env.messages[a.uid])this.env.messages[a.uid].expanded=a.expanded};this.msglist_set_coltypes=function(a){var b,d=a.list.tHead.rows[0].cells;this.env.coltypes=[];for(a=0;a<d.length;a++)d[a].id&&d[a].id.match(/^rcm/)&&(b=d[a].id.replace(/^rcm/,""),this.env.coltypes.push(b==
-"to"?"from":b));if((a=$.inArray("flag",this.env.coltypes))>=0)this.env.flagged_col=a;if((a=$.inArray("subject",this.env.coltypes))>=0)this.env.subject_col=a;this.command("save-pref",{name:"list_cols",value:this.env.coltypes,session:"list_attrib/columns"})};this.check_droptarget=function(a){var b=!1,d=!1;if(this.task=="mail")b=this.env.mailboxes[a]&&this.env.mailboxes[a].id!=this.env.mailbox&&!this.env.mailboxes[a].virtual;else if(this.task=="settings")b=a!=this.env.mailbox;else if(this.task=="addressbook"&&
-a!=this.env.source&&this.env.contactfolders[a])this.env.contactfolders[a].type=="group"?(d=this.env.contactfolders[a].source,b=this.env.contactfolders[a].id!=this.env.group&&!this.env.contactfolders[d].readonly,d=d!=this.env.source):(b=!this.env.contactfolders[a].readonly,d=!0);return b?d?2:1:0};this.init_message_row=function(a){var b,d=this,e=a.uid,f=(this.env.status_col!=null?"status":"msg")+"icn"+a.uid;e&&this.env.messages[e]&&$.extend(a,this.env.messages[e]);if(a.icon=document.getElementById(f))a.icon._row=
-a.obj,a.icon.onmousedown=function(a){d.command("toggle_status",this);rcube_event.cancel(a)};a.msgicon=this.env.status_col!=null?document.getElementById("msgicn"+a.uid):a.icon;if(this.env.flagged_col!=null&&(a.flagicon=document.getElementById("flagicn"+a.uid)))a.flagicon._row=a.obj,a.flagicon.onmousedown=function(a){d.command("toggle_flag",this);rcube_event.cancel(a)};if(!a.depth&&a.has_children&&(b=document.getElementById("rcmexpando"+a.uid)))a.expando=b,b.onmousedown=function(a){return d.expand_message_row(a,
-e)};this.triggerEvent("insertrow",{uid:e,row:a})};this.add_message_row=function(a,b,d,e){if(!this.gui_objects.messagelist||!this.message_list)return!1;this.env.messages[a]||(this.env.messages[a]={});$.extend(this.env.messages[a],{deleted:d.deleted?1:0,replied:d.replied?1:0,unread:d.unread?1:0,forwarded:d.forwarded?1:0,flagged:d.flagged?1:0,has_children:d.has_children?1:0,depth:d.depth?d.depth:0,unread_children:d.unread_children?d.unread_children:0,parent_uid:d.parent_uid?d.parent_uid:0,selected:this.select_all_mode||
-this.message_list.in_selection(a),ml:d.ml?1:0,ctype:d.ctype,flags:d.extra_flags});var f,g=expando="",h=this.message_list,k=h.rows;f=this.env.messages[a];var j="message"+(this.gui_objects.messagelist.tBodies[0].rows.length%2?" even":" odd")+(d.unread?" unread":"")+(d.deleted?" deleted":"")+(d.flagged?" flagged":"")+(d.unread_children&&!d.unread&&!this.env.autoexpand_threads?" unroot":"")+(f.selected?" selected":""),l=document.createElement("tr"),m=document.createElement("td");l.id="rcmrow"+a;l.className=
-j;j="msgicon";this.env.status_col===null&&(j+=" status",d.deleted?j+=" deleted":d.unread?j+=" unread":d.unread_children>0&&(j+=" unreadchildren"));d.replied&&(j+=" replied");d.forwarded&&(j+=" forwarded");f.selected&&!h.in_selection(a)&&h.selection.push(a);if(this.env.threading){m=f.depth*15;if(f.depth)k[f.parent_uid]&&k[f.parent_uid].expanded===!1||(this.env.autoexpand_threads==0||this.env.autoexpand_threads==2)&&(!k[f.parent_uid]||!k[f.parent_uid].expanded)?(l.style.display="none",f.expanded=!1):
-f.expanded=!0;else if(f.has_children&&f.expanded===void 0&&(this.env.autoexpand_threads==1||this.env.autoexpand_threads==2&&f.unread_children))f.expanded=!0;m&&(g+='<span id="rcmtab'+a+'" class="branch" style="width:'+m+'px;">&nbsp;&nbsp;</span>');f.has_children&&!f.depth&&(expando='<div id="rcmexpando'+a+'" class="'+(f.expanded?"expanded":"collapsed")+'">&nbsp;&nbsp;</div>')}g+='<span id="msgicn'+a+'" class="'+j+'">&nbsp;</span>';if(!bw.ie&&b.subject)m=d.mbox==this.env.drafts_mailbox?"_draft_uid":
-"_uid",b.subject='<a href="./?_task=mail&_action='+(d.mbox==this.env.drafts_mailbox?"compose":"show")+"&_mbox="+urlencode(d.mbox)+"&"+m+"="+a+'" onclick="return rcube_event.cancel(event)" onmouseover="rcube_webmail.long_subject_title(this,'+(f.depth+1)+')">'+b.subject+"</a>";for(var n in this.env.coltypes)f=this.env.coltypes[n],m=document.createElement("td"),m.className=String(f).toLowerCase(),f=="flag"?(j=d.flagged?"flagged":"unflagged",f='<span id="flagicn'+a+'" class="'+j+'">&nbsp;</span>'):f==
-"attachment"?f=/application\/|multipart\/m/.test(d.ctype)?'<span class="attachment">&nbsp;</span>':/multipart\/report/.test(d.ctype)?'<span class="report">&nbsp;</span>':"&nbsp;":f=="status"?(j=d.deleted?"deleted":d.unread?"unread":d.unread_children>0?"unreadchildren":"msgicon",f='<span id="statusicn'+a+'" class="'+j+'">&nbsp;</span>'):f=f=="threads"?expando:f=="subject"?g+b[f]:b[f],m.innerHTML=f,l.appendChild(m);h.insert_row(l,e);e&&this.env.pagesize&&h.rowcount>this.env.pagesize&&(a=h.get_last_row(),
-h.remove_row(a),h.clear_selection(a))};this.set_list_sorting=function(a,b){$("#rcm"+this.env.sort_col).removeClass("sorted"+this.env.sort_order.toUpperCase());a&&$("#rcm"+a).addClass("sorted"+b);this.env.sort_col=a;this.env.sort_order=b};this.set_list_options=function(a,b,d,e){var f,g="";if(b===void 0)b=this.env.sort_col;if(!d)d=this.env.sort_order;if(this.env.sort_col!=b||this.env.sort_order!=d)f=1,this.set_list_sorting(b,d);this.env.threading!=e&&(f=1,g+="&_threads="+e);if(a&&a.length){for(var h,
-k,j=[],l=this.env.coltypes,e=0;e<l.length;e++)k=l[e]=="to"?"from":l[e],h=$.inArray(k,a),h!=-1&&(j.push(k),delete a[h]);for(e=0;e<a.length;e++)a[e]&&j.push(a[e]);j.join()!=l.join()&&(f=1,g+="&_cols="+j.join(","))}f&&this.list_mailbox("","",b+"_"+d,g)};this.show_message=function(a,b,d){if(a){var e=window,f=d?"preview":"show",g="&_action="+f+"&_uid="+a+"&_mbox="+urlencode(this.env.mailbox);d&&this.env.contentframe&&window.frames&&window.frames[this.env.contentframe]&&(e=window.frames[this.env.contentframe],
-g+="&_framed=1");b&&(g+="&_safe=1");this.env.search_request&&(g+="&_search="+this.env.search_request);if(f=="preview"&&String(e.location.href).indexOf(g)>=0)this.show_contentframe(!0);else if(this.location_href(this.env.comm_path+g,e,!0),f=="preview"&&this.message_list&&this.message_list.rows[a]&&this.message_list.rows[a].unread&&this.env.preview_pane_mark_read>=0)this.preview_read_timer=window.setTimeout(function(){l.set_message(a,"unread",!1);l.update_thread_root(a,"read");l.env.unread_counts[l.env.mailbox]&&
-(l.env.unread_counts[l.env.mailbox]-=1,l.set_unread_count(l.env.mailbox,l.env.unread_counts[l.env.mailbox],l.env.mailbox=="INBOX"));l.env.preview_pane_mark_read>0&&l.http_post("mark","_uid="+a+"&_flag=read&_quiet=1")},this.env.preview_pane_mark_read*1E3)}};this.show_contentframe=function(a){var b,d;if(this.env.contentframe&&(b=$("#"+this.env.contentframe))&&b.length)if(!a&&(d=window.frames[this.env.contentframe])){if(d.location&&d.location.href.indexOf(this.env.blankpage)<0)d.location.href=this.env.blankpage}else if(!bw.safari&&
-!bw.konq)b[a?"show":"hide"]();!a&&this.busy&&this.set_busy(!1,null,this.env.frame_lock)};this.lock_frame=function(){if(!this.env.frame_lock)(this.is_framed()?parent.rcmail:this).env.frame_lock=this.set_busy(!0,"loading")};this.list_page=function(a){a=="next"?a=this.env.current_page+1:a=="last"?a=this.env.pagecount:a=="prev"&&this.env.current_page>1?a=this.env.current_page-1:a=="first"&&this.env.current_page>1&&(a=1);if(a>0&&a<=this.env.pagecount)this.env.current_page=a,this.task=="mail"?this.list_mailbox(this.env.mailbox,
-a):this.task=="addressbook"&&this.list_contacts(this.env.source,this.env.group,a)};this.filter_mailbox=function(a){var b,d=this.set_busy(!0,"searching");if(this.gui_objects.qsearchbox)b=this.gui_objects.qsearchbox.value;this.clear_message_list();this.env.current_page=1;this.http_request("search","_filter="+a+(b?"&_q="+urlencode(b):"")+(this.env.mailbox?"&_mbox="+urlencode(this.env.mailbox):""),d)};this.list_mailbox=function(a,b,d,e){var f="",g=window;a||(a=this.env.mailbox?this.env.mailbox:"INBOX");
-e&&(f+=e);d&&(f+="&_sort="+d);this.env.search_request&&(f+="&_search="+this.env.search_request);if(this.env.mailbox!=a)b=1,this.env.current_page=b,this.select_all_mode=!1;this.clear_message_list();if(a!=this.env.mailbox||a==this.env.mailbox&&!b&&!d)f+="&_refresh=1";this.select_folder(a,this.env.mailbox);this.env.mailbox=a;this.gui_objects.messagelist?this.list_mailbox_remote(a,b,f):(this.env.contentframe&&window.frames&&window.frames[this.env.contentframe]&&(g=window.frames[this.env.contentframe],
-f+="&_framed=1"),a&&(this.set_busy(!0,"loading"),this.location_href(this.env.comm_path+"&_mbox="+urlencode(a)+(b?"&_page="+b:"")+f,g)))};this.clear_message_list=function(){this.env.messages={};this.last_selected=0;this.show_contentframe(!1);this.message_list&&this.message_list.clear(!0)};this.list_mailbox_remote=function(a,b,d){this.message_list.clear();a="_mbox="+urlencode(a)+(b?"&_page="+b:"");b=this.set_busy(!0,"loading");this.http_request("list",a+d,b)};this.update_selection=function(){var a=
-this.message_list.selection,b=this.message_list.rows,d,e=[];for(d in a)b[a[d]]&&e.push(a[d]);this.message_list.selection=e};this.expand_unread=function(){for(var a,b=this.gui_objects.messagelist.tBodies[0].firstChild;b;){if(b.nodeType==1&&(a=this.message_list.rows[b.uid])&&a.unread_children)this.message_list.expand_all(a),this.set_unread_children(a.uid);b=b.nextSibling}return!1};this.expand_message_row=function(a,b){var d=this.message_list.rows[b];d.expanded=!d.expanded;this.set_unread_children(b);
-d.expanded=!d.expanded;this.message_list.expand_row(a,b)};this.expand_threads=function(){if(this.env.threading&&this.env.autoexpand_threads&&this.message_list)switch(this.env.autoexpand_threads){case 2:this.expand_unread();break;case 1:this.message_list.expand_all()}};this.init_threads=function(a){for(var b=0,d=a.length;b<d;b++)this.add_tree_icons(a[b]);this.expand_threads()};this.add_tree_icons=function(a){var b,d,e,f,g=[],h=[],k,j=this.message_list.rows;for(k=a?j[a]?j[a].obj:null:this.message_list.list.tBodies[0].firstChild;k;){if(k.nodeType==
-1&&(d=j[k.uid]))if(d.depth){for(b=g.length-1;b>=0;b--)if(e=g[b].length,e>d.depth?(f=e-d.depth,g[b][f]&2||(g[b][f]=g[b][f]?g[b][f]+2:2)):e==d.depth&&(g[b][0]&2||(g[b][0]+=2)),d.depth>e)break;g.push(Array(d.depth));g[g.length-1][0]=1;h.push(d.uid)}else{if(g.length){for(b in g)this.set_tree_icons(h[b],g[b]);g=[];h=[]}if(a&&k!=j[a].obj)break}k=k.nextSibling}if(g.length)for(b in g)this.set_tree_icons(h[b],g[b])};this.set_tree_icons=function(a,b){var d,e=[],f="",g=b.length;for(d=0;d<g;d++)b[d]>2?e.push({"class":"l3",
-width:15}):b[d]>1?e.push({"class":"l2",width:15}):b[d]>0?e.push({"class":"l1",width:15}):e.length&&!e[e.length-1]["class"]?e[e.length-1].width+=15:e.push({"class":null,width:15});for(d=e.length-1;d>=0;d--)f+=e[d]["class"]?'<div class="tree '+e[d]["class"]+'" />':'<div style="width:'+e[d].width+'px" />';f&&$("#rcmtab"+a).html(f)};this.update_thread_root=function(a,b){if(this.env.threading){var d=this.message_list.find_root(a);if(a!=d){var e=this.message_list.rows[d];if(b=="read"&&e.unread_children)e.unread_children--;
-else if(b=="unread"&&e.has_children)e.unread_children=e.unread_children?e.unread_children+1:1;else return;this.set_message_icon(d);this.set_unread_children(d)}}};this.update_thread=function(a){if(!this.env.threading)return 0;var b,d=0,e=this.message_list.rows,f=e[a],g=e[a].depth,h=[];f.depth?f.unread&&(a=this.message_list.find_root(a),e[a].unread_children--,this.set_unread_children(a)):d--;a=f.parent_uid;for(f=f.obj.nextSibling;f;){if(f.nodeType==1&&(b=e[f.uid])){if(!b.depth||b.depth<=g)break;b.depth--;
-$("#rcmtab"+b.uid).width(b.depth*15).html("");if(b.depth){if(b.depth==g)b.parent_uid=a;b.unread&&h.length&&h[h.length-1].unread_children++}else{d++;b.parent_uid=0;if(b.has_children)$("#rcmrow"+b.uid+" .leaf:first").attr("id","rcmexpando"+b.uid).attr("class",b.obj.style.display!="none"?"expanded":"collapsed").bind("mousedown",{uid:b.uid,p:this},function(a){return a.data.p.expand_message_row(a,a.data.uid)}),b.unread_children=0,h.push(b);b.obj.style.display=="none"&&$(b.obj).show()}}f=f.nextSibling}for(b=
-0;b<h.length;b++)this.set_unread_children(h[b].uid);return d};this.delete_excessive_thread_rows=function(){for(var a=this.message_list.rows,b=this.message_list.list.tBodies[0].firstChild,d=this.env.pagesize+1;b;){if(b.nodeType==1&&(r=a[b.uid]))!r.depth&&d&&d--,d||this.message_list.remove_row(b.uid);b=b.nextSibling}};this.set_message_icon=function(a){var b=this.message_list.rows[a];if(!b)return!1;if(b.icon)a="msgicon",b.deleted?a+=" deleted":b.unread?a+=" unread":b.unread_children&&(a+=" unreadchildren"),
-b.msgicon==b.icon&&(b.replied&&(a+=" replied"),b.forwarded&&(a+=" forwarded"),a+=" status"),b.icon.className=a;if(b.msgicon&&b.msgicon!=b.icon)a="msgicon",!b.unread&&b.unread_children&&(a+=" unreadchildren"),b.replied&&(a+=" replied"),b.forwarded&&(a+=" forwarded"),b.msgicon.className=a;if(b.flagicon)a=b.flagged?"flagged":"unflagged",b.flagicon.className=a};this.set_message_status=function(a,b,d){a=this.message_list.rows[a];if(!a)return!1;if(b=="unread")a.unread=d;else if(b=="deleted")a.deleted=d;
-else if(b=="replied")a.replied=d;else if(b=="forwarded")a.forwarded=d;else if(b=="flagged")a.flagged=d};this.set_message=function(a,b,d){var e=this.message_list.rows[a];if(!e)return!1;b&&this.set_message_status(a,b,d);b=$(e.obj);e.unread&&!b.hasClass("unread")?b.addClass("unread"):!e.unread&&b.hasClass("unread")&&b.removeClass("unread");e.deleted&&!b.hasClass("deleted")?b.addClass("deleted"):!e.deleted&&b.hasClass("deleted")&&b.removeClass("deleted");e.flagged&&!b.hasClass("flagged")?b.addClass("flagged"):
-!e.flagged&&b.hasClass("flagged")&&b.removeClass("flagged");this.set_unread_children(a);this.set_message_icon(a)};this.set_unread_children=function(a){a=this.message_list.rows[a];a.parent_uid||(!a.unread&&a.unread_children&&!a.expanded?$(a.obj).addClass("unroot"):$(a.obj).removeClass("unroot"))};this.copy_messages=function(a){if(a&&typeof a==="object")a=a.id;if(a&&!(a==this.env.mailbox||!this.env.uid&&(!this.message_list||!this.message_list.get_selection().length))){var b=[],d=this.display_message(this.get_label("copyingmessage"),
-"loading"),a="&_target_mbox="+urlencode(a)+"&_from="+(this.env.action?this.env.action:"");if(this.env.uid)b[0]=this.env.uid;else{var e=this.message_list.get_selection(),f;for(f in e)b.push(e[f])}a+="&_uid="+this.uids_to_list(b);this.http_post("copy","_mbox="+urlencode(this.env.mailbox)+a,d)}};this.move_messages=function(a){if(a&&typeof a==="object")a=a.id;if(a&&!(a==this.env.mailbox||!this.env.uid&&(!this.message_list||!this.message_list.get_selection().length))){var b=!1,a="&_target_mbox="+urlencode(a)+
-"&_from="+(this.env.action?this.env.action:"");this.env.action=="show"?b=this.set_busy(!0,"movingmessage"):this.show_contentframe(!1);this.enable_command(this.env.message_commands,!1);this._with_selected_messages("moveto",b,a)}};this.delete_messages=function(){var a,b,d,e=this.env.trash_mailbox,f=this.message_list,g=f?$.merge([],f.get_selection()):[];if(this.env.uid||g.length){for(b=0,d=g.length;b<d;b++)a=g[b],f.rows[a].has_children&&!f.rows[a].expanded&&f.select_childs(a);if(this.env.flag_for_deletion)return this.mark_message("delete"),
-!1;else!e||this.env.mailbox==e?this.permanently_remove_messages():f&&f.shiftkey?confirm(this.get_label("deletemessagesconfirm"))&&this.permanently_remove_messages():this.move_messages(e);return!0}};this.permanently_remove_messages=function(){if(this.env.uid||this.message_list&&this.message_list.get_selection().length)this.show_contentframe(!1),this._with_selected_messages("delete",!1,"&_from="+(this.env.action?this.env.action:""))};this._with_selected_messages=function(a,b,d){var e=[],f=0;if(this.env.uid)e[0]=
-this.env.uid;else{var g,h,k,j=[],l=this.message_list.get_selection();for(g=0,len=l.length;g<len;g++)h=l[g],e.push(h),this.env.threading&&(f+=this.update_thread(h),k=this.message_list.find_root(h),k!=h&&$.inArray(k,j)<0&&j.push(k)),this.message_list.remove_row(h,this.env.display_next&&g==l.length-1);this.env.display_next||this.message_list.clear_selection();for(g=0,len=j.length;g<len;g++)this.add_tree_icons(j[g])}this.env.search_request&&(d+="&_search="+this.env.search_request);this.env.display_next&&
-this.env.next_uid&&(d+="&_next_uid="+this.env.next_uid);f<0?d+="&_count="+f*-1:f>0&&this.delete_excessive_thread_rows();d+="&_uid="+this.uids_to_list(e);b||(b=this.display_message(this.get_label(a=="moveto"?"movingmessage":"deletingmessage"),"loading"));this.http_post(a,"_mbox="+urlencode(this.env.mailbox)+d,b)};this.mark_message=function(a,b){var d=[],e=[],f,g,h;h=this.message_list?this.message_list.get_selection():[];if(b)d[0]=b;else if(this.env.uid)d[0]=this.env.uid;else if(this.message_list)for(g=
-0,f=h.length;g<f;g++)d.push(h[g]);if(this.message_list)for(g=0,f=d.length;g<f;g++)h=d[g],(a=="read"&&this.message_list.rows[h].unread||a=="unread"&&!this.message_list.rows[h].unread||a=="delete"&&!this.message_list.rows[h].deleted||a=="undelete"&&this.message_list.rows[h].deleted||a=="flagged"&&!this.message_list.rows[h].flagged||a=="unflagged"&&this.message_list.rows[h].flagged)&&e.push(h);else e=d;if(e.length||this.select_all_mode)switch(a){case "read":case "unread":this.toggle_read_status(a,e);
-break;case "delete":case "undelete":this.toggle_delete_status(e);break;case "flagged":case "unflagged":this.toggle_flagged_status(a,d)}};this.toggle_read_status=function(a,b){var d,e=b.length,f="_uid="+this.uids_to_list(b)+"&_flag="+a,g=this.display_message(this.get_label("markingmessage"),"loading");for(d=0;d<e;d++)this.set_message(b[d],"unread",a=="unread"?!0:!1);this.env.search_request&&(f+="&_search="+this.env.search_request);this.http_post("mark",f,g);for(d=0;d<e;d++)this.update_thread_root(b[d],
-a)};this.toggle_flagged_status=function(a,b){var d,e=b.length,f="_uid="+this.uids_to_list(b)+"&_flag="+a,g=this.display_message(this.get_label("markingmessage"),"loading");for(d=0;d<e;d++)this.set_message(b[d],"flagged",a=="flagged"?!0:!1);this.env.search_request&&(f+="&_search="+this.env.search_request);this.http_post("mark",f,g)};this.toggle_delete_status=function(a){var b=a.length,d,e,f=!0,g=this.message_list?this.message_list.rows:[];if(b==1)return!g.length||g[a[0]]&&!g[a[0]].deleted?this.flag_as_deleted(a):
-this.flag_as_undeleted(a),!0;for(d=0;d<b;d++)if(e=a[d],g[e]&&!g[e].deleted){f=!1;break}f?this.flag_as_undeleted(a):this.flag_as_deleted(a);return!0};this.flag_as_undeleted=function(a){var b,d=a.length,e="_uid="+this.uids_to_list(a)+"&_flag=undelete",f=this.display_message(this.get_label("markingmessage"),"loading");for(b=0;b<d;b++)this.set_message(a[b],"deleted",!1);this.env.search_request&&(e+="&_search="+this.env.search_request);this.http_post("mark",e,f);return!0};this.flag_as_deleted=function(a){for(var b=
-"",d=[],b=this.message_list?this.message_list.rows:[],e=0,f=0,g=a.length;f<g;f++)uid=a[f],b[uid]&&(b[uid].unread&&(d[d.length]=uid),this.env.skip_deleted?(e+=this.update_thread(uid),this.message_list.remove_row(uid,this.env.display_next&&f==this.message_list.selection.length-1)):this.set_message(uid,"deleted",!0));this.env.skip_deleted&&this.message_list&&(this.env.display_next||this.message_list.clear_selection(),e<0||e>0&&this.delete_excessive_thread_rows());b="&_from="+(this.env.action?this.env.action:
-"");lock=this.display_message(this.get_label("markingmessage"),"loading");d.length&&(b+="&_ruid="+this.uids_to_list(d));this.env.skip_deleted&&this.env.display_next&&this.env.next_uid&&(b+="&_next_uid="+this.env.next_uid);this.env.search_request&&(b+="&_search="+this.env.search_request);this.http_post("mark","_uid="+this.uids_to_list(a)+"&_flag=delete"+b,lock);return!0};this.flag_deleted_as_read=function(a){var b,d,e,f=this.message_list?this.message_list.rows:[],a=String(a).split(",");for(d=0,e=a.length;d<
-e;d++)b=a[d],f[b]&&this.set_message(b,"unread",!1)};this.uids_to_list=function(a){return this.select_all_mode?"*":a.join(",")};this.expunge_mailbox=function(a){var b,d="_mbox="+urlencode(a);a==this.env.mailbox&&(b=this.set_busy(!0,"loading"),d+="&_reload=1",this.env.search_request&&(d+="&_search="+this.env.search_request));this.http_post("expunge",d,b)};this.purge_mailbox=function(a){var b=!1,d="_mbox="+urlencode(a);if(!confirm(this.get_label("purgefolderconfirm")))return!1;a==this.env.mailbox&&(b=
-this.set_busy(!0,"loading"),d+="&_reload=1");this.http_post("purge",d,b)};this.purge_mailbox_test=function(){return this.env.messagecount&&(this.env.mailbox==this.env.trash_mailbox||this.env.mailbox==this.env.junk_mailbox||this.env.mailbox.match("^"+RegExp.escape(this.env.trash_mailbox)+RegExp.escape(this.env.delimiter))||this.env.mailbox.match("^"+RegExp.escape(this.env.junk_mailbox)+RegExp.escape(this.env.delimiter)))};this.login_user_keyup=function(a){var b=rcube_event.get_keycode(a),d=$("#rcmloginpwd");
-return b==13&&d.length&&!d.val()?(d.focus(),rcube_event.cancel(a)):!0};this.init_messageform=function(){if(!this.gui_objects.messageform)return!1;var a=$("[name='_from']"),b=$("[name='_to']"),d=$("input[name='_subject']"),e=$("[name='_message']").get(0),f=$("input[name='_is_html']").val()=="1",g=["cc","bcc","replyto","followupto"],h;this.env.autocomplete_threads>0&&(h={threads:this.env.autocomplete_threads,sources:this.env.autocomplete_sources});this.init_address_input_events(b,h);for(var k in g)this.init_address_input_events($("[name='_"+
-g[k]+"']"),h);f||(this.set_caret_pos(e,this.env.top_posting?0:$(e).val().length),a.prop("type")=="select-one"&&$("input[name='_draft_saveid']").val()==""&&this.change_identity(a[0]));b.val()==""?b.focus():d.val()==""?d.focus():e&&e.focus();this.env.compose_focus_elem=document.activeElement;this.compose_field_hash(!0);this.auto_save_start()};this.init_address_input_events=function(a,b){a[bw.ie||bw.safari||bw.chrome?"keydown":"keypress"](function(a){return l.ksearch_keydown(a,this,b)}).attr("autocomplete",
-"off")};this.check_compose_input=function(){var a,b=$("[name='_to']"),d=$("[name='_cc']"),e=$("[name='_bcc']"),f=$("[name='_from']"),g=$("[name='_subject']"),h=$("[name='_message']");if(f.prop("type")=="text"&&!rcube_check_email(f.val(),!0))return alert(this.get_label("nosenderwarning")),f.focus(),!1;d=b.val()?b.val():d.val()?d.val():e.val();if(!rcube_check_email(d.replace(/^\s+/,"").replace(/[\s,;]+$/,""),!0))return alert(this.get_label("norecipientwarning")),b.focus(),!1;for(var k in this.env.attachments)if(typeof this.env.attachments[k]===
-"object"&&!this.env.attachments[k].complete)return alert(this.get_label("notuploadedwarning")),!1;if(g.val()=="")if(b=prompt(this.get_label("nosubjectwarning"),this.get_label("nosubject")),!b&&b!=="")return g.focus(),!1;else g.val(b?b:this.get_label("nosubject"));this.stop_spellchecking();window.tinyMCE&&(a=tinyMCE.get(this.env.composebody));if(!a&&h.val()==""&&!confirm(this.get_label("nobodywarning")))return h.focus(),!1;else if(a){if(!a.getContent()&&!confirm(this.get_label("nobodywarning")))return a.focus(),
-!1;tinyMCE.triggerSave()}return!0};this.toggle_editor=function(a){if(a.mode=="html")this.display_spellcheck_controls(!1),this.plain2html($("#"+a.id).val(),a.id),tinyMCE.execCommand("mceAddControl",!1,a.id);else{var b=tinyMCE.get(a.id);b.plugins.spellchecker&&b.plugins.spellchecker.active&&b.execCommand("mceSpellCheck",!1);if(b=b.getContent()){if(!confirm(this.get_label("editorwarning")))return!1;this.html2plain(b,a.id)}tinyMCE.execCommand("mceRemoveControl",!1,a.id);this.display_spellcheck_controls(!0)}return!0};
-this.stop_spellchecking=function(){var a;if(window.tinyMCE&&(a=tinyMCE.get(this.env.composebody)))a.plugins.spellchecker&&a.plugins.spellchecker.active&&a.execCommand("mceSpellCheck");else if((a=this.env.spellcheck)&&!this.spellcheck_ready)$(a.spell_span).trigger("click"),this.set_spellcheck_state("ready")};this.display_spellcheck_controls=function(a){this.env.spellcheck&&(a||this.stop_spellchecking(),$(this.env.spellcheck.spell_container).css("visibility",a?"visible":"hidden"))};this.set_spellcheck_state=
-function(a){this.spellcheck_ready=a=="ready"||a=="no_error_found";this.enable_command("spellcheck",this.spellcheck_ready)};this.spellcheck_lang=function(){var a;if(window.tinyMCE&&(a=tinyMCE.get(this.env.composebody))&&a.plugins.spellchecker)return a.plugins.spellchecker.selectedLang;else if(this.env.spellcheck)return GOOGIE_CUR_LANG};this.spellcheck_resume=function(a,b){if(a){var d=tinyMCE.get(this.env.composebody),e=d.plugins.spellchecker;e.active=1;e._markWords(b);d.nodeChanged()}else{var e=this.env.spellcheck;
-e.prepare(!1,!0);e.processData(b)}};this.set_draft_id=function(a){$("input[name='_draft_saveid']").val(a)};this.auto_save_start=function(){if(this.env.draft_autosave)this.save_timer=self.setTimeout(function(){l.command("savedraft")},this.env.draft_autosave*1E3);this.busy=!1};this.compose_field_hash=function(a){var b,d="",e=$("[name='_to']").val(),f=$("[name='_cc']").val(),g=$("[name='_bcc']").val(),h=$("[name='_subject']").val();e&&(d+=e+":");f&&(d+=f+":");g&&(d+=g+":");h&&(d+=h+":");d+=window.tinyMCE&&
-(b=tinyMCE.get(this.env.composebody))?b.getContent():$("[name='_message']").val();if(this.env.attachments)for(var k in this.env.attachments)d+=k;if(a)this.cmp_hash=d;return d};this.change_identity=function(a,b){if(!a||!a.options)return!1;if(!b)b=this.env.show_sig;var d,e=-1,f=a.options[a.selectedIndex].value,g=$("[name='_message']"),h=g.val(),k=$("input[name='_is_html']").val()=="1",j=this.env.identity;d=this.env.sig_above&&(this.env.compose_mode=="reply"||this.env.compose_mode=="forward")?"---":
-"-- ";this.env.signatures&&this.env.signatures[f]?(this.enable_command("insert-sig",!0),this.env.compose_commands.push("insert-sig")):this.enable_command("insert-sig",!1);if(k){if(b&&this.env.signatures&&(e=tinyMCE.get(this.env.composebody),g=e.dom.get("_rc_sig"),g||(j=e.getBody(),h=e.getDoc(),g=h.createElement("div"),g.setAttribute("id","_rc_sig"),this.env.sig_above?(e.getWin().focus(),e=e.selection.getNode(),e.nodeName=="BODY"?(j.insertBefore(g,j.firstChild),j.insertBefore(h.createElement("br"),
-j.firstChild)):(j.insertBefore(g,e.nextSibling),j.insertBefore(h.createElement("br"),e.nextSibling))):(bw.ie&&j.appendChild(h.createElement("br")),j.appendChild(g))),this.env.signatures[f]))this.env.signatures[f].is_html?(j=this.env.signatures[f].text,this.env.signatures[f].plain_text.match(/^--[ -]\r?\n/)||(j=d+"<br />"+j)):(j=this.env.signatures[f].text,j.match(/^--[ -]\r?\n/)||(j=d+"\n"+j),j="<pre>"+j+"</pre>"),g.innerHTML=j}else b&&j&&this.env.signatures&&this.env.signatures[j]&&(j=this.env.signatures[j].is_html?
-this.env.signatures[j].plain_text:this.env.signatures[j].text,j=j.replace(/\r\n/g,"\n"),j.match(/^--[ -]\n/)||(j=d+"\n"+j),e=this.env.sig_above?h.indexOf(j):h.lastIndexOf(j),e>=0&&(h=h.substring(0,e)+h.substring(e+j.length,h.length))),b&&this.env.signatures&&this.env.signatures[f]?(j=this.env.signatures[f].is_html?this.env.signatures[f].plain_text:this.env.signatures[f].text,j=j.replace(/\r\n/g,"\n"),j.match(/^--[ -]\n/)||(j=d+"\n"+j),this.env.sig_above?e>=0?(h=h.substring(0,e)+j+h.substring(e,h.length),
-d=e-1):(pos=this.get_caret_pos(g.get(0)))?(h=h.substring(0,pos)+"\n"+j+"\n\n"+h.substring(pos,h.length),d=pos):(d=0,h="\n\n"+j+"\n\n"+h.replace(/^[\r\n]+/,"")):(h=h.replace(/[\r\n]+$/,""),d=!this.env.top_posting&&h.length?h.length+1:0,h+="\n\n"+j)):d=this.env.top_posting?0:h.length,g.val(h),this.set_caret_pos(g.get(0),d);this.env.identity=f;return!0};this.upload_file=function(a){if(!a)return!1;var b,d=0,e=$("input[type=file]",a).get(0),f=e.files?e.files.length:e.value?1:0;if(f){if(e.files&&this.env.max_filesize&&
-this.env.filesizeerror){for(b=0;b<f;b++)d+=e.files[b].size;if(d&&d>this.env.max_filesize){this.display_message(this.env.filesizeerror,"error");return}}b=this.async_upload_form(a,"upload",function(a){var b,d="";try{if(this.contentDocument)b=this.contentDocument;else if(this.contentWindow)b=this.contentWindow.document;d=b.childNodes[0].innerHTML}catch(e){}if(!d.match(/add2attachment/)&&(!bw.opera||rcmail.env.uploadframe&&rcmail.env.uploadframe==a.data.ts))d.match(/display_message/)||rcmail.display_message(rcmail.get_label("fileuploaderror"),
-"error"),rcmail.remove_from_attachment_list(a.data.ts);if(bw.opera)rcmail.env.uploadframe=a.data.ts});f="<span>"+this.get_label("uploading"+(f>1?"many":""))+"</span>";d=b.replace(/^rcmupload/,"");this.env.loadingicon&&(f='<img src="'+this.env.loadingicon+'" alt="" />'+f);this.env.cancelicon&&(f='<a title="'+this.get_label("cancel")+'" onclick="return rcmail.cancel_attachment_upload(\''+d+"', '"+b+'\');" href="#cancelupload"><img src="'+this.env.cancelicon+'" alt="" /></a>'+f);this.add2attachment_list(d,
-{name:"",html:f,complete:!1});this.env.upload_progress_time&&this.upload_progress_start("upload",d)}this.gui_objects.attachmentform=a;return!0};this.add2attachment_list=function(a,b,d){if(!this.gui_objects.attachmentlist)return!1;var e,f=$("<li>").attr("id",a).html(b.html);d&&(e=document.getElementById(d))?f.replaceAll(e):f.appendTo(this.gui_objects.attachmentlist);d&&this.env.attachments[d]&&delete this.env.attachments[d];this.env.attachments[a]=b;return!0};this.remove_from_attachment_list=function(a){this.env.attachments[a]&&
-delete this.env.attachments[a];if(!this.gui_objects.attachmentlist)return!1;var b=this.gui_objects.attachmentlist.getElementsByTagName("li");for(i=0;i<b.length;i++)b[i].id==a&&this.gui_objects.attachmentlist.removeChild(b[i])};this.remove_attachment=function(a){a&&this.env.attachments[a]&&this.http_post("remove-attachment",{_id:this.env.compose_id,_file:a});return!0};this.cancel_attachment_upload=function(a,b){if(!a||!b)return!1;this.remove_from_attachment_list(a);$("iframe[name='"+b+"']").remove();
-return!1};this.upload_progress_start=function(a,b){window.setTimeout(function(){rcmail.http_request(a,{_progress:b})},this.env.upload_progress_time*1E3)};this.upload_progress_update=function(a){var b=$("#"+a.name+"> span");b.length&&a.text&&(b.text(a.text),a.done||this.upload_progress_start(a.action,a.name))};this.add_contact=function(a){a&&this.http_post("addcontact","_address="+a);return!0};this.qsearch=function(a){if(a!=""){var b,d="",e=[],f=this.env.search_mods,g=this.env.mailbox,h=this.set_busy(!0,
-"searching");this.message_list?(this.clear_message_list(),f&&(f=f[g]?f[g]:f["*"])):this.contact_list&&this.list_contacts_clear();if(f){for(b in f)e.push(b);d+="&_headers="+e.join(",")}this.gui_objects.search_filter&&(d+="&_filter="+this.gui_objects.search_filter.value);this.env.current_page=1;a=this.http_request("search","_q="+urlencode(a)+(g?"&_mbox="+urlencode(g):"")+(this.env.source?"&_source="+urlencode(this.env.source):"")+(this.env.group?"&_gid="+urlencode(this.env.group):"")+(d?d:""),h);this.env.qsearch=
-{lock:h,request:a}}};this.reset_qsearch=function(){if(this.gui_objects.qsearchbox)this.gui_objects.qsearchbox.value="";this.env.qsearch&&this.abort_request(this.env.qsearch);this.env.qsearch=null;this.env.search_request=null};this.sent_successfully=function(a,b){this.display_message(b,a);window.setTimeout(function(){l.list_mailbox()},500)};this.ksearch_keydown=function(a,b,d){this.ksearch_timer&&clearTimeout(this.ksearch_timer);var e=rcube_event.get_keycode(a),f=rcube_event.get_modifier(a);switch(e){case 38:case 40:if(!this.ksearch_pane)break;
-e=e==38?1:0;b=document.getElementById("rcmksearchSelected");if(!b)b=this.ksearch_pane.__ul.firstChild;b&&this.ksearch_select(e?b.previousSibling:b.nextSibling);return rcube_event.cancel(a);case 9:if(f==SHIFT_KEY||!this.ksearch_visible()){this.ksearch_hide();return}case 13:if(!this.ksearch_visible())return!1;this.insert_recipient(this.ksearch_selected);this.ksearch_hide();return rcube_event.cancel(a);case 27:this.ksearch_hide();return;case 37:case 39:if(f!=SHIFT_KEY)return}this.ksearch_timer=window.setTimeout(function(){l.ksearch_get_results(d)},
-200);this.ksearch_input=b;return!0};this.ksearch_visible=function(){return this.ksearch_selected!==null&&this.ksearch_selected!==void 0&&this.ksearch_value};this.ksearch_select=function(a){var b=$("#rcmksearchSelected");b[0]&&a&&b.removeAttr("id").removeClass("selected");if(a)$(a).attr("id","rcmksearchSelected").addClass("selected"),this.ksearch_selected=a._rcm_id};this.insert_recipient=function(a){if(this.env.contacts[a]&&this.ksearch_input){var b=this.ksearch_input.value,d=this.get_caret_pos(this.ksearch_input),
-d=b.lastIndexOf(this.ksearch_value,d),e=!1,f="",g=b.substring(0,d),b=b.substring(d+this.ksearch_value.length,b.length);this.ksearch_destroy();typeof this.env.contacts[a]==="object"&&this.env.contacts[a].id?(f+=this.env.contacts[a].name+", ",this.group2expand=$.extend({},this.env.contacts[a]),this.group2expand.input=this.ksearch_input,this.http_request("mail/group-expand","_source="+urlencode(this.env.contacts[a].source)+"&_gid="+urlencode(this.env.contacts[a].id),!1)):typeof this.env.contacts[a]===
-"string"&&(f=this.env.contacts[a]+", ",e=!0);this.ksearch_input.value=g+f+b;d+=f.length;this.ksearch_input.setSelectionRange&&this.ksearch_input.setSelectionRange(d,d);e&&this.triggerEvent("autocomplete_insert",{field:this.ksearch_input,insert:f})}};this.replace_group_recipients=function(a,b){if(this.group2expand&&this.group2expand.id==a)this.group2expand.input.value=this.group2expand.input.value.replace(this.group2expand.name,b),this.triggerEvent("autocomplete_insert",{field:this.group2expand.input,
-insert:b}),this.group2expand=null};this.ksearch_get_results=function(a){var b=this.ksearch_input?this.ksearch_input.value:null;if(b!==null){this.ksearch_pane&&this.ksearch_pane.is(":visible")&&this.ksearch_pane.hide();var d=this.get_caret_pos(this.ksearch_input),e=b.lastIndexOf(",",d-1),b=b.substring(e+1,d),e=this.env.autocomplete_min_length,d=this.ksearch_data,b=$.trim(b);if(b!=this.ksearch_value)if(b.length&&b.length<e){if(!this.env.acinfo)this.env.acinfo=this.display_message(this.get_label("autocompletechars").replace("$min",
-e))}else if(this.env.acinfo&&this.hide_message(this.env.acinfo),e=this.ksearch_value,this.ksearch_value=b,this.ksearch_destroy(),b.length&&(!e||!e.length||!(b.indexOf(e)==0&&(!d||!d.num)&&this.env.contacts&&!this.env.contacts.length))){var f,g,h,d=(new Date).getTime(),e=a&&a.threads?a.threads:1;f=a&&a.sources?a.sources:[];a=a&&a.action?a.action:"mail/autocomplete";this.ksearch_data={id:d,sources:f.slice(),action:a,locks:[],requests:[],num:f.length};for(f=0;f<e;f++){h=this.ksearch_data.sources.shift();
-if(e>1&&h===null)break;g=this.display_message(this.get_label("searching"),"loading");h=this.http_post(a,"_search="+urlencode(b)+"&_id="+d+(h?"&_source="+urlencode(h):""),g);this.ksearch_data.locks.push(g);this.ksearch_data.requests.push(h)}}}};this.ksearch_query_results=function(a,b,d){if(this.ksearch_value&&!(this.ksearch_input&&b!=this.ksearch_value)){var e,f,g,b=this.ksearch_value,h=this.env.autocomplete_max?this.env.autocomplete_max:15;if(!this.ksearch_pane)e=$("<ul>"),this.ksearch_pane=$("<div>").attr("id",
-"rcmKSearchpane").css({position:"absolute","z-index":3E4}).append(e).appendTo(document.body),this.ksearch_pane.__ul=e[0];e=this.ksearch_pane.__ul;d&&this.ksearch_pane.data("reqid")==d?h-=e.childNodes.length:(this.ksearch_pane.data("reqid",d),e.innerHTML="",this.env.contacts=[],f=$(this.ksearch_input).offset(),this.ksearch_pane.css({left:f.left+"px",top:f.top+this.ksearch_input.offsetHeight+"px",display:"none"}));if(a&&a.length)for(i=0;i<a.length&&h>0;i++)g=typeof a[i]==="object"?a[i].name:a[i],f=
-document.createElement("LI"),f.innerHTML=g.replace(RegExp("("+RegExp.escape(b)+")","ig"),"##$1%%").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/##([^%]+)%%/g,"<b>$1</b>"),f.onmouseover=function(){l.ksearch_select(this)},f.onmouseup=function(){l.ksearch_click(this)},f._rcm_id=this.env.contacts.length+i,e.appendChild(f),h-=1;if(e.childNodes.length&&(this.ksearch_pane.show(),!this.env.contacts.length))$("li:first",e).attr("id","rcmksearchSelected").addClass("selected"),this.ksearch_selected=0;
-if(a&&a.length)this.env.contacts=this.env.contacts.concat(a);if(h>0&&this.ksearch_data.id==d&&this.ksearch_data.sources.length&&(e=this.ksearch_data,h=e.sources.shift()))data.num--,a=this.display_message(this.get_label("searching"),"loading"),d=this.http_post(e.action,"_search="+urlencode(b)+"&_id="+d+"&_source="+urlencode(h),a),this.ksearch_data.locks.push(a),this.ksearch_data.requests.push(d)}};this.ksearch_click=function(a){this.ksearch_input&&this.ksearch_input.focus();this.insert_recipient(a._rcm_id);
-this.ksearch_hide()};this.ksearch_blur=function(){this.ksearch_timer&&clearTimeout(this.ksearch_timer);this.ksearch_input=null;this.ksearch_hide()};this.ksearch_hide=function(){this.ksearch_selected=null;this.ksearch_value="";this.ksearch_pane&&this.ksearch_pane.hide();this.ksearch_destroy()};this.ksearch_destroy=function(){var a,b,d=this.ksearch_data;if(d){for(a=0,b=d.locks.length;a<b;a++)this.abort_request({request:d.requests[a],lock:d.locks[a]});this.ksearch_data=null}};this.contactlist_keypress=
-function(a){a.key_pressed==a.DELETE_KEY&&this.command("delete")};this.contactlist_select=function(a){this.preview_timer&&clearTimeout(this.preview_timer);var b,d,e,f=this,g=!1;e=this.env.source?this.env.address_sources[this.env.source]:null;(d=a.get_single_selection())?this.preview_timer=window.setTimeout(function(){f.load_contact(d,"show")},200):this.env.contentframe&&this.show_contentframe(!1);if(a.selection.length)if(e)g=!e.readonly;else for(b in a.selection)if((e=String(a.selection[b]).replace(/^[^-]+-/,
-""))&&this.env.address_sources[e]&&!this.env.address_sources[e].readonly){g=!0;break}this.enable_command("compose",a.selection.length>0);this.enable_command("edit",d&&g);this.enable_command("delete",a.selection.length&&g);return!1};this.list_contacts=function(a,b,d){var e="",f=window;if(!a)a=this.env.source;if(d&&this.current_page==d&&a==this.env.source&&b==this.env.group)return!1;if(a!=this.env.source)d=this.env.current_page=1,this.reset_qsearch();else if(b!=this.env.group)d=this.env.current_page=
-1;this.select_folder(b?"G"+a+b:a,this.env.group?"G"+this.env.source+this.env.group:this.env.source);this.env.source=a;this.env.group=b;this.gui_objects.contactslist?this.list_contacts_remote(a,b,d):(this.env.contentframe&&window.frames&&window.frames[this.env.contentframe]&&(f=window.frames[this.env.contentframe],e="&_framed=1"),b&&(e+="&_gid="+b),d&&(e+="&_page="+d),this.env.search_request&&(e+="&_search="+this.env.search_request),this.set_busy(!0,"loading"),this.location_href(this.env.comm_path+
-(a?"&_source="+urlencode(a):"")+e,f))};this.list_contacts_remote=function(a,b,d){this.list_contacts_clear();var d=(a?"_source="+urlencode(a):"")+(d?(a?"&":"")+"_page="+d:""),e=this.set_busy(!0,"loading");this.env.source=a;(this.env.group=b)&&(d+="&_gid="+b);this.env.search_request&&(d+="&_search="+this.env.search_request);this.http_request("list",d,e)};this.list_contacts_clear=function(){this.contact_list.clear(!0);this.show_contentframe(!1);this.enable_command("delete","compose",!1)};this.load_contact=
-function(a,b,d){var e="",f=window;if(this.env.contentframe&&window.frames&&window.frames[this.env.contentframe])e="&_framed=1",f=window.frames[this.env.contentframe],this.show_contentframe(!0),a||(this.contact_list.clear_selection(),this.enable_command("delete","compose",!1));else if(d)return!1;if(b&&(a||b=="add")&&!this.drag_active)this.env.group&&(e+="&_gid="+urlencode(this.env.group)),this.location_href(this.env.comm_path+"&_action="+b+"&_source="+urlencode(this.env.source)+"&_cid="+urlencode(a)+
-e,f,!0);return!0};this.group_member_change=function(a,b,d,e){var a=a=="add"?"add":"del",f=this.display_message(this.get_label(a=="add"?"addingmember":"removingmember"),"loading");this.http_post("group-"+a+"members","_cid="+urlencode(b)+"&_source="+urlencode(d)+"&_gid="+urlencode(e),f)};this.copy_contact=function(a,b){a||(a=this.contact_list.get_selection().join(","));if(b.type=="group"&&b.source==this.env.source)this.group_member_change("add",a,b.source,b.id);else if(b.type=="group"&&!this.env.address_sources[b.source].readonly){var d=
-this.display_message(this.get_label("copyingcontact"),"loading");this.http_post("copy","_cid="+urlencode(a)+"&_source="+urlencode(this.env.source)+"&_to="+urlencode(b.source)+"&_togid="+urlencode(b.id)+(this.env.group?"&_gid="+urlencode(this.env.group):""),d)}else b.id!=this.env.source&&a&&this.env.address_sources[b.id]&&!this.env.address_sources[b.id].readonly&&(d=this.display_message(this.get_label("copyingcontact"),"loading"),this.http_post("copy","_cid="+urlencode(a)+"&_source="+urlencode(this.env.source)+
-"&_to="+urlencode(b.id)+(this.env.group?"&_gid="+urlencode(this.env.group):""),d))};this.delete_contacts=function(){var a=this.contact_list.get_selection();if((a.length||this.env.cid)&&confirm(this.get_label("deletecontactconfirm"))){var b,d,e=[],f="";if(this.env.cid)e.push(this.env.cid);else{for(d=0;d<a.length;d++)b=a[d],e.push(b),this.contact_list.remove_row(b,d==a.length-1);a.length==1&&this.show_contentframe(!1)}this.env.group&&(f+="&_gid="+urlencode(this.env.group));this.env.search_request&&
-(f+="&_search="+this.env.search_request);this.http_post("delete","_cid="+urlencode(e.join(","))+"&_source="+urlencode(this.env.source)+"&_from="+(this.env.action?this.env.action:"")+f);return!0}};this.update_contact_row=function(a,b,d,e){var f,g=this.contact_list,a=String(a).replace(this.identifier_expr,"_");g.rows[a]||(a=a+"-"+e,d&&(d=d+"-"+e));if(g.rows[a]&&(f=g.rows[a].obj)){for(e=0;e<b.length;e++)f.cells[e]&&$(f.cells[e]).html(b[e]);if(d)d=String(d).replace(this.identifier_expr,"_"),f.id="rcmrow"+
-d,g.remove_row(a),g.init_row(f),g.selection[0]=d,f.style.display=""}};this.add_contact_row=function(a,b){if(!this.gui_objects.contactslist||!this.gui_objects.contactslist.tBodies[0])return!1;var d=this.gui_objects.contactslist.tBodies[0].rows.length%2,e=document.createElement("tr");e.id="rcmrow"+String(a).replace(this.identifier_expr,"_");e.className="contact "+(d?"even":"odd");this.contact_list.in_selection(a)&&(e.className+=" selected");for(var f in b)col=document.createElement("td"),col.className=
-String(f).toLowerCase(),col.innerHTML=b[f],e.appendChild(col);this.contact_list.insert_row(e);this.enable_command("export",this.contact_list.rowcount>0)};this.init_contact_form=function(){var a=this,b;this.set_photo_actions($("#ff_photo").val());for(b in this.env.coltypes)this.init_edit_field(b,null);$(".contactfieldgroup .row a.deletebutton").click(function(){a.delete_edit_field(this);return!1});$("select.addfieldmenu").change(function(){a.insert_edit_field($(this).val(),$(this).attr("rel"),this);
-this.selectedIndex=0});$("input[type='text']:visible").first().focus()};this.group_create=function(){if(this.gui_objects.folderlist){if(!this.name_input)this.name_input=$("<input>").attr("type","text"),this.name_input.bind("keydown",function(a){return rcmail.add_input_keydown(a)}),this.name_input_li=$("<li>").addClass("contactgroup").append(this.name_input),this.name_input_li.insertAfter(this.get_folder_li(this.env.source));this.name_input.select().focus()}};this.group_rename=function(){if(this.env.group&&
-this.gui_objects.folderlist){if(!this.name_input){this.enable_command("list","listgroup",!1);this.name_input=$("<input>").attr("type","text").val(this.env.contactgroups["G"+this.env.source+this.env.group].name);this.name_input.bind("keydown",function(a){return rcmail.add_input_keydown(a)});this.env.group_renaming=!0;var a,b=this.get_folder_li(this.env.source+this.env.group,"rcmliG");b&&(a=b.firstChild)&&$(a).hide().before(this.name_input)}this.name_input.select().focus()}};this.group_delete=function(){if(this.env.group&&
-confirm(this.get_label("deletegroupconfirm"))){var a=this.set_busy(!0,"groupdeleting");this.http_post("group-delete","_source="+urlencode(this.env.source)+"&_gid="+urlencode(this.env.group),a)}};this.remove_group_item=function(a){var b,d="G"+a.source+a.id;if(b=this.get_folder_li(d))this.triggerEvent("group_delete",{source:a.source,id:a.id,li:b}),b.parentNode.removeChild(b),delete this.env.contactfolders[d],delete this.env.contactgroups[d];this.list_contacts(a.source,0)};this.add_input_keydown=function(a){a=
-rcube_event.get_keycode(a);if(a==13){if(a=this.name_input.val()){var b=this.set_busy(!0,"loading");this.env.group_renaming?this.http_post("group-rename","_source="+urlencode(this.env.source)+"&_gid="+urlencode(this.env.group)+"&_name="+urlencode(a),b):this.http_post("group-create","_source="+urlencode(this.env.source)+"&_name="+urlencode(a),b)}return!1}else a==27&&this.reset_add_input();return!0};this.reset_add_input=function(){if(this.name_input){if(this.env.group_renaming)this.name_input.parent().children().last().show(),
-this.env.group_renaming=!1;this.name_input.remove();this.name_input_li&&this.name_input_li.remove();this.name_input=this.name_input_li=null}this.enable_command("list","listgroup",!0)};this.insert_contact_group=function(a){this.reset_add_input();a.type="group";var b="G"+a.source+a.id,d=$("<a>").attr("href","#").attr("rel",a.source+":"+a.id).click(function(){return rcmail.command("listgroup",a,this)}).html(a.name),d=$("<li>").attr({id:"rcmli"+b.replace(this.identifier_expr,"_"),"class":"contactgroup"}).append(d);
-this.env.contactfolders[b]=this.env.contactgroups[b]=a;this.add_contact_group_row(a,d);this.triggerEvent("group_insert",{id:a.id,source:a.source,name:a.name,li:d[0]})};this.update_contact_group=function(a){this.reset_add_input();var b="G"+a.source+a.id,d=this.get_folder_li(b),e;if(d&&a.newid){e="G"+a.source+a.newid;var f=$.extend({},a);d.id=String("rcmli"+e).replace(this.identifier_expr,"_");this.env.contactfolders[e]=this.env.contactfolders[b];this.env.contactfolders[e].id=a.newid;this.env.group=
-a.newid;delete this.env.contactfolders[b];delete this.env.contactgroups[b];f.id=a.newid;f.type="group";e=$("<a>").attr("href","#").attr("rel",a.source+":"+a.newid).click(function(){return rcmail.command("listgroup",f,this)}).html(a.name);$(d).children().replaceWith(e)}else if(d&&(e=d.firstChild)&&e.tagName.toLowerCase()=="a")e.innerHTML=a.name;this.env.contactfolders[b].name=this.env.contactgroups[b].name=a.name;this.add_contact_group_row(a,$(d),!0);this.triggerEvent("group_update",{id:a.id,source:a.source,
-name:a.name,li:d[0],newid:a.newid})};this.add_contact_group_row=function(a,b,d){var e=a.name.toUpperCase(),f=this.get_folder_li(a.source),a="rcmliG"+a.source.replace(this.identifier_expr,"_");d?(d=b.clone(!0),b.remove()):d=b;$('li[id^="'+a+'"]',this.gui_objects.folderlist).each(function(a,b){if(e>=$(this).text().toUpperCase())f=b;else return!1});d.insertAfter(f)};this.update_group_commands=function(){var a=this.env.source!=""?this.env.address_sources[this.env.source]:null;this.enable_command("group-create",
-a&&a.groups&&!a.readonly);this.enable_command("group-rename","group-delete",a&&a.groups&&this.env.group&&!a.readonly)};this.init_edit_field=function(a,b){b||(b=$(".ff_"+a));b.focus(function(){l.focus_textfield(this)}).blur(function(){l.blur_textfield(this)}).each(function(){this._placeholder=this.title=l.env.coltypes[a].label;l.blur_textfield(this)})};this.insert_edit_field=function(a,b,d){var e=$("#ff_"+a);if(e.length)e.show().focus(),$(d).children('option[value="'+a+'"]').prop("disabled",!0);else if($(".ff_"+
-a),e=$("#contactsection"+b+" .contactcontroller"+a),e.length||(e=$("<fieldset>").addClass("contactfieldgroup contactcontroller"+a).insertAfter($("#contactsection"+b+" .contactfieldgroup").last())),e.length&&e.get(0).nodeName=="FIELDSET"){var f,b=this.env.coltypes[a],g=$("<div>").addClass("row"),h=$("<div>").addClass("contactfieldcontent data"),k=$("<div>").addClass("contactfieldlabel label");b.subtypes_select?k.html(b.subtypes_select):k.html(b.label);var j=b.limit!=1?"[]":"";if(b.type=="text"||b.type==
-"date")f=$("<input>").addClass("ff_"+a).attr({type:"text",name:"_"+a+j,size:b.size}).appendTo(h),this.init_edit_field(a,f);else if(b.type=="composite"){var o,m,n=[],p=[];if(f=this.env[a+"_template"])for(o=0;o<f.length;o++)n.push(f[o][1]),p.push(f[o][2]);else for(o in b.childs)n.push(o);for(var q=0;q<n.length;q++)o=n[q],f=b.childs[o],f=$("<input>").addClass("ff_"+o).attr({type:"text",name:"_"+o+j,size:f.size}).appendTo(h),h.append(p[q]||" "),this.init_edit_field(o,f),m||(m=f);f=m}else if(b.type=="select"){f=
-$("<select>").addClass("ff_"+a).attr("name","_"+a+j).appendTo(h);var s=f.attr("options");s[s.length]=new Option("---","");b.options&&$.each(b.options,function(a,b){s[s.length]=new Option(b,a)})}if(f){$('<a href="#del"></a>').addClass("contactfieldbutton deletebutton").attr({title:this.get_label("delete"),rel:a}).html(this.env.delbutton).click(function(){l.delete_edit_field(this);return!1}).appendTo(h);g.append(k).append(h).appendTo(e.show());f.first().focus();if(!b.count)b.count=0;++b.count==b.limit&&
-b.limit&&$(d).children('option[value="'+a+'"]').prop("disabled",!0)}}};this.delete_edit_field=function(a){var b=$(a).attr("rel"),d=this.env.coltypes[b],e=$(a).parents("fieldset.contactfieldgroup"),f=e.parent().find("select.addfieldmenu");--d.count<=0&&d.visible?$(a).parent().children("input").val("").blur():($(a).parents("div.row").remove(),e.children("div.row").length||e.hide());f.length&&(a=f.children('option[value="'+b+'"]'),a.length?a.prop("disabled",!1):$("<option>").attr("value",b).html(d.label).appendTo(f),
-f.show())};this.upload_contact_photo=function(a){if(a&&a.elements._photo.value)this.async_upload_form(a,"upload-photo",function(){rcmail.set_busy(!1,null,rcmail.photo_upload_id)}),this.photo_upload_id=this.set_busy(!0,"uploading")};this.replace_contact_photo=function(a){var b=a=="-del-"?this.env.photo_placeholder:this.env.comm_path+"&_action=photo&_source="+this.env.source+"&_cid="+this.env.cid+"&_photo="+a;this.set_photo_actions(a);$(this.gui_objects.contactphoto).children("img").attr("src",b)};
-this.photo_upload_end=function(){this.set_busy(!1,null,this.photo_upload_id);delete this.photo_upload_id};this.set_photo_actions=function(a){var b,d=this.buttons["upload-photo"];for(b=0;d&&b<d.length;b++)$("#"+d[b].id).html(this.get_label(a=="-del-"?"addphoto":"replacephoto"));$("#ff_photo").val(a);this.enable_command("upload-photo",this.env.coltypes.photo?!0:!1);this.enable_command("delete-photo",this.env.coltypes.photo&&a!="-del-")};this.advanced_search=function(){var a="&_form=1",b=window;this.env.contentframe&&
-window.frames&&window.frames[this.env.contentframe]&&(a+="&_framed=1",b=window.frames[this.env.contentframe],this.contact_list.clear_selection());this.location_href(this.env.comm_path+"&_action=search"+a,b,!0);return!0};this.unselect_directory=function(){if(this.env.address_sources.length>1||this.env.group!="")this.select_folder("",this.env.group?"G"+this.env.source+this.env.group:this.env.source),this.env.group="",this.env.source=""};this.section_select=function(a){var a=a.get_single_selection(),
-b="",d=window;a&&(this.env.contentframe&&window.frames&&window.frames[this.env.contentframe]&&(b="&_framed=1",d=window.frames[this.env.contentframe]),this.location_href(this.env.comm_path+"&_action=edit-prefs&_section="+a+b,d,!0));return!0};this.identity_select=function(a){var b;(b=a.get_single_selection())&&this.load_identity(b,"edit-identity")};this.load_identity=function(a,b){if(b=="edit-identity"&&(!a||a==this.env.iid))return!1;var d="",e=window;if(this.env.contentframe&&window.frames&&window.frames[this.env.contentframe])d=
-"&_framed=1",e=window.frames[this.env.contentframe],document.getElementById(this.env.contentframe).style.visibility="inherit";if(b&&(a||b=="add-identity"))this.set_busy(!0),this.location_href(this.env.comm_path+"&_action="+b+"&_iid="+a+d,e);return!0};this.delete_identity=function(a){var b=this.identity_list.get_selection();if(b.length||this.env.iid)return a||(a=this.env.iid?this.env.iid:b[0]),this.goto_url("delete-identity","_iid="+a+"&_token="+this.env.request_token,!0),!0};this.init_subscription_list=
-function(){var a=this;this.subscription_list=new rcube_list_widget(this.gui_objects.subscriptionlist,{multiselect:!1,draggable:!0,keyboard:!1,toggleselect:!0});this.subscription_list.addEventListener("select",function(b){a.subscription_select(b)});this.subscription_list.addEventListener("dragstart",function(){a.drag_active=!0});this.subscription_list.addEventListener("dragend",function(b){a.subscription_move_folder(b)});this.subscription_list.row_init=function(b){b.obj.onmouseover=function(){a.focus_subscription(b.id)};
-b.obj.onmouseout=function(){a.unfocus_subscription(b.id)}};this.subscription_list.init();$("#mailboxroot").mouseover(function(){a.focus_subscription(this.id)}).mouseout(function(){a.unfocus_subscription(this.id)})};this.focus_subscription=function(a){var b,d,e=RegExp.escape(this.env.delimiter),e=RegExp("["+e+"]?[^"+e+"]+$");if(this.drag_active&&this.env.mailbox&&(b=document.getElementById(a)))if(this.env.subscriptionrows[a]&&(d=this.env.subscriptionrows[a][0])!==null&&this.check_droptarget(d)&&!this.env.subscriptionrows[this.get_folder_row_id(this.env.mailbox)][2]&&
-d!=this.env.mailbox.replace(e,"")&&!d.match(RegExp("^"+RegExp.escape(this.env.mailbox+this.env.delimiter))))this.env.dstfolder=d,$(b).addClass("droptarget")};this.unfocus_subscription=function(a){var b=$("#"+a);this.env.dstfolder=null;this.env.subscriptionrows[a]&&b[0]?b.removeClass("droptarget"):$(this.subscription_list.frame).removeClass("droptarget")};this.subscription_select=function(a){var b,d;a&&(b=a.get_single_selection())&&(d=this.env.subscriptionrows["rcmrow"+b])?(this.env.mailbox=d[0],this.show_folder(d[0]),
-this.enable_command("delete-folder",!d[2])):(this.env.mailbox=null,this.show_contentframe(!1),this.enable_command("delete-folder","purge",!1))};this.subscription_move_folder=function(){var a=RegExp.escape(this.env.delimiter);this.env.mailbox&&this.env.dstfolder!==null&&this.env.dstfolder!=this.env.mailbox&&this.env.dstfolder!=this.env.mailbox.replace(RegExp("["+a+"]?[^"+a+"]+$"),"")&&(a=this.env.mailbox.replace(RegExp("[^"+a+"]*["+a+"]","g"),""),a=this.env.dstfolder===""?a:this.env.dstfolder+this.env.delimiter+
-a,a!=this.env.mailbox&&(this.http_post("rename-folder","_folder_oldname="+urlencode(this.env.mailbox)+"&_folder_newname="+urlencode(a),this.set_busy(!0,"foldermoving")),this.subscription_list.draglayer.hide()));this.drag_active=!1;this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder))};this.create_folder=function(){this.show_folder("",this.env.mailbox)};this.delete_folder=function(a){if((a=this.env.subscriptionrows[this.get_folder_row_id(a?a:this.env.mailbox)][0])&&confirm(this.get_label("deletefolderconfirm"))){var b=
-this.set_busy(!0,"folderdeleting");this.http_post("delete-folder","_mbox="+urlencode(a),b)}};this.add_folder_row=function(a,b,d,e,f,g){if(!this.gui_objects.subscriptionlist)return!1;var h,k,j,l,m,n=[],p=[],q=this.gui_objects.subscriptionlist.tBodies[0];h=$("tr",q).get(1);var s="rcmrow"+(new Date).getTime();if(!h)return this.goto_url("folders"),!1;h=$(h).clone(!0);h.attr("id",s);h.attr("class",g);h.find("td:first").html(b);$('input[name="_subscribed[]"]',h).val(a).prop({checked:e?!0:!1,disabled:d?
-!0:!1});this.env.subscriptionrows[s]=[a,b,0];l=[];$.each(this.env.subscriptionrows,function(a,b){l.push(b)});l.sort(function(a,b){return a[0]<b[0]?-1:a[0]>b[0]?1:0});for(k in l)l[k][2]?(p.push(l[k][0]),j=l[k][0]+this.env.delimiter):j&&l[k][0].indexOf(j)==0?p.push(l[k][0]):(n.push(l[k][0]),j=null);for(k=0;k<p.length;k++)a.indexOf(p[k]+this.env.delimiter)==0&&(m=this.get_folder_row_id(p[k]));for(k=0;!m&&k<n.length;k++)k&&n[k]==a&&(m=this.get_folder_row_id(n[k-1]));m?$("#"+m).after(h):h.appendTo(q);
-this.subscription_list.clear_selection();f||this.init_subscription_list();h=h.get(0);h.scrollIntoView&&h.scrollIntoView();return h};this.replace_folder_row=function(a,b,d,e,f){if(!this.gui_objects.subscriptionlist)return!1;var g,h,k,j,l=this.get_folder_row_id(a),m=RegExp("^"+RegExp.escape(a));g=$('input[name="_subscribed[]"]',$("#"+l)).prop("checked");var n=this.get_subfolders(a);this._remove_folder_row(l);e=$(this.add_folder_row(b,d,e,g,!0,f));if(d=n.length)j=a.split(this.env.delimiter).length-b.split(this.env.delimiter).length;
-for(a=0;a<d;a++)if(l=n[a],g=this.env.subscriptionrows[l][0],f=this.env.subscriptionrows[l][1],h=$("#"+l),k=h.clone(!0),h.remove(),e.after(k),e=k,g=g.replace(m,b),$('input[name="_subscribed[]"]',e).val(g),this.env.subscriptionrows[l][0]=g,j!=0){if(j>0)for(g=j;g>0;g--)f=f.replace(/^&nbsp;&nbsp;&nbsp;&nbsp;/,"");else for(g=j;g<0;g++)f="&nbsp;&nbsp;&nbsp;&nbsp;"+f;e.find("td:first").html(f);this.env.subscriptionrows[l][1]=f}this.init_subscription_list()};this.remove_folder_row=function(a,b){var d,e,f=
-[];d=this.get_folder_row_id(a);b&&(f=this.get_subfolders(a));this._remove_folder_row(d);for(d=0,e=f.length;d<e;d++)this._remove_folder_row(f[d])};this._remove_folder_row=function(a){this.subscription_list.remove_row(a.replace(/^rcmrow/,""));$("#"+a).remove();delete this.env.subscriptionrows[a]};this.get_subfolders=function(a){for(var b=[],d=RegExp("^"+RegExp.escape(a)+RegExp.escape(this.env.delimiter)),e=$("#"+this.get_folder_row_id(a)).get(0);e=e.nextSibling;)if(e.id)if(a=this.env.subscriptionrows[e.id][0],
-d.test(a))b.push(e.id);else break;return b};this.subscribe=function(a){if(a){var b=this.display_message(this.get_label("foldersubscribing"),"loading");this.http_post("subscribe","_mbox="+urlencode(a),b)}};this.unsubscribe=function(a){if(a){var b=this.display_message(this.get_label("folderunsubscribing"),"loading");this.http_post("unsubscribe","_mbox="+urlencode(a),b)}};this.get_folder_row_id=function(a){var b,d=this.env.subscriptionrows;for(b in d)if(d[b]&&d[b][0]==a)break;return b};this.show_folder=
-function(a,b,d){var e=window,a="&_action=edit-folder&_mbox="+urlencode(a);b&&(a+="&_path="+urlencode(b));this.env.contentframe&&window.frames&&window.frames[this.env.contentframe]&&(e=window.frames[this.env.contentframe],a+="&_framed=1");String(e.location.href).indexOf(a)>=0&&!d?this.show_contentframe(!0):this.location_href(this.env.comm_path+a,e,!0)};this.disable_subscription=function(a){(a=this.get_folder_row_id(a))&&$('input[name="_subscribed[]"]',$("#"+a)).prop("disabled",!0)};this.folder_size=
-function(a){var b=this.set_busy(!0,"loading");this.http_post("folder-size","_mbox="+urlencode(a),b)};this.folder_size_update=function(a){$("#folder-size").replaceWith(a)};var t=function(a,b){var d=document.getElementById(b.id);if(d){var e=!1;if(b.type=="image")d=d.parentNode,e=!0;d._command=a;d._id=b.id;if(b.sel&&(d.onmousedown=function(){return rcmail.button_sel(this._command,this._id)},d.onmouseup=function(){return rcmail.button_out(this._command,this._id)},e))(new Image).src=b.sel;if(b.over&&(d.onmouseover=
-function(){return rcmail.button_over(this._command,this._id)},d.onmouseout=function(){return rcmail.button_out(this._command,this._id)},e))(new Image).src=b.over}};this.set_page_buttons=function(){this.enable_command("nextpage","lastpage",this.env.pagecount>this.env.current_page);this.enable_command("previouspage","firstpage",this.env.current_page>1)};this.init_buttons=function(){for(var a in this.buttons)if(typeof a==="string")for(var b=0;b<this.buttons[a].length;b++)t(a,this.buttons[a][b])};this.set_button=
-function(a,b){var d,e,f=this.buttons[a];if(!f||!f.length)return!1;for(var g=0;g<f.length;g++){d=f[g];if((e=document.getElementById(d.id))&&d.type=="image"&&!d.status){if(d.pas=e._original_src?e._original_src:e.src,e.runtimeStyle&&e.runtimeStyle.filter&&e.runtimeStyle.filter.match(/src=['"]([^'"]+)['"]/))d.pas=RegExp.$1}else if(e&&!d.status)d.pas=String(e.className);if(e&&d.type=="image"&&d[b])d.status=b,e.src=d[b];else if(e&&d[b]!==void 0)d.status=b,e.className=d[b];if(e&&d.type=="input")d.status=
-b,e.disabled=!b}};this.set_alttext=function(a,b){if(this.buttons[a]&&this.buttons[a].length)for(var d,e,f,g=0;g<this.buttons[a].length;g++)d=this.buttons[a][g],e=document.getElementById(d.id),d.type=="image"&&e?(e.setAttribute("alt",this.get_label(b)),(f=e.parentNode)&&f.tagName.toLowerCase()=="a"&&f.setAttribute("title",this.get_label(b))):e&&e.setAttribute("title",this.get_label(b))};this.button_over=function(a,b){var d,e,f=this.buttons[a];if(!f||!f.length)return!1;for(var g=0;g<f.length;g++)if(d=
-f[g],d.id==b&&d.status=="act"&&(e=document.getElementById(d.id))&&d.over)d.type=="image"?e.src=d.over:e.className=d.over};this.button_sel=function(a,b){var d,e,f=this.buttons[a];if(f&&f.length)for(var g=0;g<f.length;g++)if(d=f[g],d.id==b&&d.status=="act"){if((e=document.getElementById(d.id))&&d.sel)d.type=="image"?e.src=d.sel:e.className=d.sel;this.buttons_sel[b]=a}};this.button_out=function(a,b){var d,e,f=this.buttons[a];if(f&&f.length)for(var g=0;g<f.length;g++)if(d=f[g],d.id==b&&d.status=="act"&&
-(e=document.getElementById(d.id))&&d.act)d.type=="image"?e.src=d.act:e.className=d.act};this.focus_textfield=function(a){a._hasfocus=!0;var b=$(a);(b.hasClass("placeholder")||b.val()==a._placeholder)&&b.val("").removeClass("placeholder").attr("spellcheck",!0)};this.blur_textfield=function(a){a._hasfocus=!1;var b=$(a);a._placeholder&&(!b.val()||b.val()==a._placeholder)&&b.addClass("placeholder").attr("spellcheck",!1).val(a._placeholder)};this.set_pagetitle=function(a){if(a&&document.title)document.title=
-a};this.display_message=function(a,b,d){if(this.is_framed())return parent.rcmail.display_message(a,b,d);if(!this.gui_objects.message){if(b!="loading")this.pending_message=[a,b,d];return!1}var b=b?b:"notice",e=this,f=String(a).replace(this.identifier_expr,"_"),g=b+(new Date).getTime();d||(d=this.message_time*(b=="error"||b=="warning"?2:1));b=="loading"&&(f="loading",d=this.env.request_timeout*1E3,a||(a=this.get_label("loading")));if(this.messages[f])return this.messages[f].obj&&this.messages[f].obj.html(a),
-b=="loading"&&this.messages[f].labels.push({id:g,msg:a}),this.messages[f].elements.push(g),window.setTimeout(function(){e.hide_message(g,b=="loading")},d),g;var h=$("<div>").addClass(b).html(a).data("key",f);$(this.gui_objects.message).append(h).show();this.messages[f]={obj:h,elements:[g]};b=="loading"?this.messages[f].labels=[{id:g,msg:a}]:h.click(function(){return e.hide_message(h)});d>0&&window.setTimeout(function(){e.hide_message(g,b=="loading")},d);return g};this.hide_message=function(a,b){if(this.is_framed())return parent.rcmail.hide_message(a,
-b);var d,e,f,g,h=this.messages;if(typeof a==="object")$(a)[b?"fadeOut":"hide"](),g=$(a).data("key"),this.messages[g]&&delete this.messages[g];else for(d in h)for(e in h[d].elements)if(h[d]&&h[d].elements[e]==a)if(h[d].elements.splice(e,1),h[d].elements.length){if(d=="loading")for(f in h[d].labels)h[d].labels[f].id==a?delete h[d].labels[f]:g=h[d].labels[f].msg,h[d].obj.html(g)}else h[d].obj[b?"fadeOut":"hide"](),delete h[d]};this.select_folder=function(a,b,d){if(this.gui_objects.folderlist){var e,
-f;(e=this.get_folder_li(b,d))&&$(e).removeClass("selected").addClass("unfocused");(f=this.get_folder_li(a,d))&&$(f).removeClass("unfocused").addClass("selected");this.triggerEvent("selectfolder",{folder:a,old:b,prefix:d})}};this.get_folder_li=function(a,b){b||(b="rcmli");return this.gui_objects.folderlist?(a=String(a).replace(this.identifier_expr,"_"),document.getElementById(b+a)):null};this.set_message_coltypes=function(a,b){var d=this.message_list,e=d?d.list.tHead:null,f,g,h,k;this.env.coltypes=
-a;if(e){if(b){g=document.createElement("thead");h=document.createElement("tr");for(c=0,k=b.length;c<k;c++){f=document.createElement("td");f.innerHTML=b[c].html;if(b[c].id)f.id=b[c].id;if(b[c].className)f.className=b[c].className;h.appendChild(f)}g.appendChild(h);e.parentNode.replaceChild(g,e);e=g}for(h=0,k=this.env.coltypes.length;h<k;h++)if(g=this.env.coltypes[h],(f=e.rows[0].cells[h])&&(g=="from"||g=="to")){f.id="rcm"+g;if(f.firstChild&&f.firstChild.tagName.toLowerCase()=="a")f=f.firstChild,f.onclick=
-function(){return rcmail.command("sort",this.__col,this)},f.__col=g;f.innerHTML=this.get_label(g)}}this.env.subject_col=null;this.env.flagged_col=null;this.env.status_col=null;if((h=$.inArray("subject",this.env.coltypes))>=0)if(this.env.subject_col=h,d)d.subject_col=h;if((h=$.inArray("flag",this.env.coltypes))>=0)this.env.flagged_col=h;if((h=$.inArray("status",this.env.coltypes))>=0)this.env.status_col=h;d&&d.init_header()};this.set_rowcount=function(a){$(this.gui_objects.countdisplay).html(a);this.set_page_buttons()};
-this.set_mailboxname=function(a){if(this.gui_objects.mailboxname&&a)this.gui_objects.mailboxname.innerHTML=a};this.set_quota=function(a){a&&this.gui_objects.quotadisplay&&(typeof a==="object"&&a.type=="image"?this.percent_indicator(this.gui_objects.quotadisplay,a):$(this.gui_objects.quotadisplay).html(a))};this.set_unread_count=function(a,b,d){if(!this.gui_objects.mailboxlist)return!1;this.env.unread_counts[a]=b;this.set_unread_count_display(a,d)};this.set_unread_count_display=function(a,b){var d,
-e,f,g,h;if(f=this.get_folder_li(a)){g=this.env.unread_counts[a]?this.env.unread_counts[a]:0;e=$(f).children("a").eq(0);d=e.children("span.unreadcount");!d.length&&g&&(d=$("<span>").addClass("unreadcount").appendTo(e));e=0;if((h=f.getElementsByTagName("div")[0])&&h.className.match(/collapsed/))for(var k in this.env.unread_counts)k.indexOf(a+this.env.delimiter)==0&&(e+=this.env.unread_counts[k]);g&&d.length?d.html(" ("+g+")"):d.length&&d.remove();d=RegExp(RegExp.escape(this.env.delimiter)+"[^"+RegExp.escape(this.env.delimiter)+
-"]+$");a.match(d)&&this.set_unread_count_display(a.replace(d,""),!1);g+e>0?$(f).addClass("unread"):$(f).removeClass("unread")}d=/^\([0-9]+\)\s+/i;b&&document.title&&(f="",f=String(document.title),f=g&&f.match(d)?f.replace(d,"("+g+") "):g?"("+g+") "+f:f.replace(d,""),this.set_pagetitle(f))};this.toggle_prefer_html=function(a){var b;if(b=document.getElementById("rcmfd_addrbook_show_images"))b.disabled=!a.checked};this.toggle_preview_pane=function(a){var b;if(b=document.getElementById("rcmfd_preview_pane_mark_read"))b.disabled=
-!a.checked};this.set_headers=function(a){this.gui_objects.all_headers_row&&this.gui_objects.all_headers_box&&a&&$(this.gui_objects.all_headers_box).html(a).show()};this.load_headers=function(a){if(this.gui_objects.all_headers_row&&this.gui_objects.all_headers_box&&this.env.uid)$(a).removeClass("show-headers").addClass("hide-headers"),$(this.gui_objects.all_headers_row).show(),a.onclick=function(){rcmail.hide_headers(a)},this.gui_objects.all_headers_box.innerHTML||this.http_post("headers","_uid="+
-this.env.uid,this.display_message(this.get_label("loading"),"loading"))};this.hide_headers=function(a){if(this.gui_objects.all_headers_row&&this.gui_objects.all_headers_box)$(a).removeClass("hide-headers").addClass("show-headers"),$(this.gui_objects.all_headers_row).hide(),a.onclick=function(){rcmail.load_headers(a)}};this.percent_indicator=function(a,b){if(!b||!a)return!1;var d=b.width?b.width:this.env.indicator_width?this.env.indicator_width:100,e=b.height?b.height:this.env.indicator_height?this.env.indicator_height:
-14,f=b.percent?Math.abs(parseInt(b.percent)):0,g=parseInt(f/100*d),h=$(a).position();h.top=Math.max(0,h.top);h.left=Math.max(0,h.left);this.env.indicator_width=d;this.env.indicator_height=e;g>d&&(g=d,f=100);if(b.title)b.title=this.get_label("quota")+": "+b.title;var k=$("<div>");k.css({position:"absolute",top:h.top,left:h.left,width:d+"px",height:e+"px",zIndex:100,lineHeight:e+"px"}).attr("title",b.title).addClass("quota_text").html(f+"%");var j=$("<div>");j.css({position:"absolute",top:h.top+1,left:h.left+
-1,width:g+"px",height:e+"px",zIndex:99});g=$("<div>");g.css({position:"absolute",top:h.top+1,left:h.left+1,width:d+"px",height:e+"px",zIndex:98}).addClass("quota_bg");f>=80?(k.addClass(" quota_text_high"),j.addClass("quota_high")):f>=55?(k.addClass(" quota_text_mid"),j.addClass("quota_mid")):(k.addClass(" quota_text_low"),j.addClass("quota_low"));$(a).html("").append(j).append(g).append(k);$("#quotaimg").attr("title",b.title)};this.html2plain=function(a,b){var d=this,e=this.set_busy(!0,"converting");
-this.log("HTTP POST: ?_task=utils&_action=html2text");$.ajax({type:"POST",url:"?_task=utils&_action=html2text",data:a,contentType:"application/octet-stream",error:function(a,b,h){d.http_error(a,b,h,e)},success:function(a){d.set_busy(!1,null,e);$(document.getElementById(b)).val(a);d.log(a)}})};this.plain2html=function(a,b){var d=this.set_busy(!0,"converting");$(document.getElementById(b)).val("<pre>"+a+"</pre>");this.set_busy(!1,null,d)};this.url=function(a,b){var d=typeof b==="string"?"&"+b:"";if(typeof a!==
-"string")b=a;else if(!b||typeof b!=="object")b={};b._action=a?a:this.env.action;var e=this.env.comm_path;if(b._action.match(/([a-z]+)\/([a-z-_.]+)/))b._action=RegExp.$2,e=e.replace(/\_task=[a-z]+/,"_task="+RegExp.$1);var f={},g;for(g in b)b[g]!==void 0&&b[g]!==null&&(f[g]=b[g]);return e+"&"+$.param(f)+d};this.redirect=function(a,b){(b||b===null)&&this.set_busy(!0);this.is_framed()?parent.rcmail.redirect(a,b):this.location_href(a,window)};this.goto_url=function(a,b){this.redirect(this.url(a,b))};this.location_href=
-function(a,b,d){d&&this.lock_frame();bw.ie&&b==window?$("<a>").attr("href",a).appendTo(document.body).get(0).click():b.location.href=a};this.http_request=function(a,b,d){var e=this.url(a,b),a=this.triggerEvent("request"+a,b);if(a!==void 0)if(a===!1)return!1;else b=a;e+="&_remote=1";this.log("HTTP GET: "+e);return $.ajax({type:"GET",url:e,data:{_unlock:d?d:0},dataType:"json",success:function(a){l.http_response(a)},error:function(a,b,e){rcmail.http_error(a,b,e,d)}})};this.http_post=function(a,b,d){var e=
-this.url(a);b&&typeof b==="object"?(b._remote=1,b._unlock=d?d:0):b+=(b?"&":"")+"_remote=1"+(d?"&_unlock="+d:"");a=this.triggerEvent("request"+a,b);if(a!==void 0)if(a===!1)return!1;else b=a;this.log("HTTP POST: "+e);return $.ajax({type:"POST",url:e,data:b,dataType:"json",success:function(a){l.http_response(a)},error:function(a,b,e){rcmail.http_error(a,b,e,d)}})};this.abort_request=function(a){a.request&&a.request.abort();a.lock&&this.set_busy(!1,null,a.lock)};this.http_response=function(a){if(a){a.unlock&&
-this.set_busy(!1);this.triggerEvent("responsebefore",{response:a});this.triggerEvent("responsebefore"+a.action,{response:a});a.env&&this.set_env(a.env);if(typeof a.texts==="object")for(var b in a.texts)typeof a.texts[b]==="string"&&this.add_label(b,a.texts[b]);a.exec&&(this.log(a.exec),eval(a.exec));if(a.callbacks&&a.callbacks.length)for(b=0;b<a.callbacks.length;b++)this.triggerEvent(a.callbacks[b][0],a.callbacks[b][1]);switch(a.action){case "delete":if(this.task=="addressbook"){var d;b=this.contact_list.get_selection();
-d=!1;b&&this.contact_list.rows[b]&&(d=this.env.source==""?(d=String(b).replace(/^[^-]+-/,""))&&this.env.address_sources[d]&&!this.env.address_sources[d].readonly:!this.env.address_sources[this.env.source].readonly);this.enable_command("compose",b&&this.contact_list.rows[b]);this.enable_command("delete","edit",d);this.enable_command("export",this.contact_list&&this.contact_list.rowcount>0)}case "moveto":this.env.action=="show"?(this.enable_command(this.env.message_commands,!0),this.env.list_post||
-this.enable_command("reply-list",!1)):this.task=="addressbook"&&this.triggerEvent("listupdate",{folder:this.env.source,rowcount:this.contact_list.rowcount});case "purge":case "expunge":this.task=="mail"&&(this.env.messagecount||(this.env.contentframe&&this.show_contentframe(!1),this.enable_command(this.env.message_commands,"purge","expunge","select-all","select-none","sort","expand-all","expand-unread","collapse-all",!1)),this.message_list&&this.triggerEvent("listupdate",{folder:this.env.mailbox,
-rowcount:this.message_list.rowcount}));break;case "check-recent":case "getunread":case "search":this.env.qsearch=null;case "list":if(this.task=="mail"){if(this.enable_command("show","expunge","select-all","select-none","sort",this.env.messagecount>0),this.enable_command("purge",this.purge_mailbox_test()),this.enable_command("expand-all","expand-unread","collapse-all",this.env.threading&&this.env.messagecount),a.action=="list"||a.action=="search")this.msglist_select(this.message_list),this.triggerEvent("listupdate",
-{folder:this.env.mailbox,rowcount:this.message_list.rowcount})}else if(this.task=="addressbook"&&(this.enable_command("export",this.contact_list&&this.contact_list.rowcount>0),a.action=="list"||a.action=="search"))this.update_group_commands(),this.triggerEvent("listupdate",{folder:this.env.source,rowcount:this.contact_list.rowcount})}a.unlock&&this.hide_message(a.unlock);this.triggerEvent("responseafter",{response:a});this.triggerEvent("responseafter"+a.action,{response:a})}};this.http_error=function(a,
-b,d,e){b=a.statusText;this.set_busy(!1,null,e);a.abort();a.status&&b&&this.display_message(this.get_label("servererror")+" ("+b+")","error")};this.async_upload_form=function(a,b,d){var e=(new Date).getTime(),f="rcmupload"+e;if(this.env.upload_progress_name){var g=this.env.upload_progress_name,h=$("input[name="+g+"]",a);h.length||(h=$("<input>").attr({type:"hidden",name:g}),h.prependTo(a));h.val(e)}document.all?document.body.insertAdjacentHTML("BeforeEnd",'<iframe name="'+f+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>'):
-(g=document.createElement("iframe"),g.name=f,g.style.border="none",g.style.width=0,g.style.height=0,g.style.visibility="hidden",document.body.appendChild(g));$(f).bind("load",{ts:e},d);$(a).attr({target:f,action:this.url(b,{_id:this.env.compose_id||"",_uploadid:e}),method:"POST"}).attr(a.encoding?"encoding":"enctype","multipart/form-data").submit();return f};this.start_keepalive=function(){this._int&&clearInterval(this._int);if(this.env.keep_alive&&!this.env.framed&&this.task=="mail"&&this.gui_objects.mailboxlist)this._int=
-setInterval(function(){l.check_for_recent(!1)},this.env.keep_alive*1E3);else if(this.env.keep_alive&&!this.env.framed&&this.task!="login"&&this.env.action!="print")this._int=setInterval(function(){l.keep_alive()},this.env.keep_alive*1E3)};this.keep_alive=function(){this.busy||this.http_request("keep-alive")};this.check_for_recent=function(a){if(!this.busy){var b,d="_mbox="+urlencode(this.env.mailbox);a&&(b=this.set_busy(!0,"checkingmail"),d+="&_refresh=1",this.start_keepalive());this.gui_objects.messagelist&&
-(d+="&_list=1");this.gui_objects.quotadisplay&&(d+="&_quota=1");this.env.search_request&&(d+="&_search="+this.env.search_request);this.http_request("check-recent",d,b)}};this.get_single_uid=function(){return this.env.uid?this.env.uid:this.message_list?this.message_list.get_single_selection():null};this.get_single_cid=function(){return this.env.cid?this.env.cid:this.contact_list?this.contact_list.get_single_selection():null};this.get_caret_pos=function(a){if(a.selectionEnd!==void 0)return a.selectionEnd;
-else if(document.selection&&document.selection.createRange){var b=document.selection.createRange();if(b.parentElement()!=a)return 0;var d=b.duplicate();a.tagName=="TEXTAREA"?d.moveToElementText(a):d.expand("textedit");d.setEndPoint("EndToStart",b);b=d.text.length;return b<=a.value.length?b:-1}else return a.value.length};this.set_caret_pos=function(a,b){if(a.setSelectionRange)a.setSelectionRange(b,b);else if(a.createTextRange){var d=a.createTextRange();d.collapse(!0);d.moveEnd("character",b);d.moveStart("character",
-b);d.select()}};this.lock_form=function(a,b){if(a&&a.elements){var d,e,f;if(b)this.disabled_form_elements=[];for(d=0,e=a.elements.length;d<e;d++)if(f=a.elements[d],f.type!="hidden")if(b&&f.disabled)this.disabled_form_elements.push(f);else if(b||this.disabled_form_elements&&$.inArray(f,this.disabled_form_elements)<0)f.disabled=b}}}rcube_webmail.long_subject_title=function(l,t){if(!l.title){var a=$(l);if(a.width()+t*15>a.parent().width())l.title=a.html()}};rcube_webmail.prototype.addEventListener=rcube_event_engine.prototype.addEventListener;
-rcube_webmail.prototype.removeEventListener=rcube_event_engine.prototype.removeEventListener;rcube_webmail.prototype.triggerEvent=rcube_event_engine.prototype.triggerEvent;
+function rcube_webmail(){this.env={recipients_separator:",",recipients_delimiter:", "};this.labels={};this.buttons={};this.buttons_sel={};this.gui_objects={};this.gui_containers={};this.commands={};this.command_handlers={};this.onloads=[];this.messages={};this.ref="rcmail";var i=this;this.dblclick_time=500;this.message_time=4E3;this.identifier_expr=RegExp("[^0-9a-z-_]","gi");this.env.keep_alive=60;this.env.request_timeout=180;this.env.draft_autosave=0;this.env.comm_path="./";this.env.blankpage="program/blank.gif";
+$.ajaxSetup({cache:!1,error:function(a,b,d){i.http_error(a,b,d)},beforeSend:function(a){a.setRequestHeader("X-Roundcube-Request",i.env.request_token)}});this.set_env=function(a,b){if(null!=a&&"object"===typeof a&&!b)for(var d in a)this.env[d]=a[d];else this.env[a]=b};this.add_label=function(a,b){"string"==typeof a?this.labels[a]=b:"object"==typeof a&&$.extend(this.labels,a)};this.register_button=function(a,b,d,e,f,h){this.buttons[a]||(this.buttons[a]=[]);b={id:b,type:d};if(e)b.act=e;if(f)b.sel=f;
+if(h)b.over=h;this.buttons[a].push(b);this.loaded&&s(a,b)};this.gui_object=function(a,b){this.gui_objects[a]=this.loaded?rcube_find_object(b):b};this.gui_container=function(a,b){this.gui_containers[a]=b};this.add_element=function(a,b){this.gui_containers[b]&&this.gui_containers[b].jquery&&this.gui_containers[b].append(a)};this.register_command=function(a,b,d){this.command_handlers[a]=b;d&&this.enable_command(a,!0)};this.add_onload=function(a){this.onloads.push(a)};this.init=function(){var a,b=this;
+this.task=this.env.task;if(!bw.dom||!bw.xmlhttp_test())this.goto_url("error","_code=0x199");else{for(a in this.gui_containers)this.gui_containers[a]=$("#"+this.gui_containers[a]);for(a in this.gui_objects)this.gui_objects[a]=rcube_find_object(this.gui_objects[a]);if(this.env.x_frame_options)try{if("deny"==this.env.x_frame_options&&top.location.href!=self.location.href)top.location.href=self.location.href;else if(top.location.hostname!=self.location.hostname)throw 1;}catch(d){$("form").each(function(){i.lock_form(this,
+!0)});this.display_message("Blocked: possible clickjacking attack!","error");return}this.init_buttons();if(this.is_framed())parent.rcmail.set_busy(!1,null,parent.rcmail.env.frame_lock),parent.rcmail.env.frame_lock=null;this.enable_command("logout","mail","addressbook","settings","save-pref","compose","undo",!0);this.env.permaurl&&this.enable_command("permaurl",!0);switch(this.task){case "mail":this.enable_command("list","checkmail","add-contact","search","reset-search","collapse-folder",!0);if(this.gui_objects.messagelist)this.message_list=
+new rcube_list_widget(this.gui_objects.messagelist,{multiselect:!0,multiexpand:!0,draggable:!0,keyboard:!0,column_movable:this.env.col_movable,dblclick_time:this.dblclick_time}),this.message_list.row_init=function(a){b.init_message_row(a)},this.message_list.addEventListener("dblclick",function(a){b.msglist_dbl_click(a)}),this.message_list.addEventListener("click",function(a){b.msglist_click(a)}),this.message_list.addEventListener("keypress",function(a){b.msglist_keypress(a)}),this.message_list.addEventListener("select",
+function(a){b.msglist_select(a)}),this.message_list.addEventListener("dragstart",function(a){b.drag_start(a)}),this.message_list.addEventListener("dragmove",function(a){b.drag_move(a)}),this.message_list.addEventListener("dragend",function(a){b.drag_end(a)}),this.message_list.addEventListener("expandcollapse",function(a){b.msglist_expand(a)}),this.message_list.addEventListener("column_replace",function(a){b.msglist_set_coltypes(a)}),document.onmouseup=function(a){return b.doc_mouse_up(a)},this.gui_objects.messagelist.parentNode.onmousedown=
+function(a){return b.click_on_list(a)},this.message_list.init(),this.enable_command("toggle_status","toggle_flag","menu-open","menu-save",!0),this.command("list");if(this.gui_objects.qsearchbox){if(null!=this.env.search_text)this.gui_objects.qsearchbox.value=this.env.search_text;$(this.gui_objects.qsearchbox).focusin(function(){rcmail.message_list.blur()})}!this.env.flag_for_deletion&&this.env.trash_mailbox&&this.env.mailbox!=this.env.trash_mailbox&&this.set_alttext("delete","movemessagetotrash");
+this.env.message_commands="show,reply,reply-all,reply-list,forward,moveto,copy,delete,open,mark,edit,viewsource,download,print,load-attachment,load-headers,forward-attachment".split(",");if("show"==this.env.action||"preview"==this.env.action){this.enable_command(this.env.message_commands,this.env.uid);this.enable_command("reply-list",this.env.list_post);"show"==this.env.action&&this.http_request("pagenav","_uid="+this.env.uid+"&_mbox="+urlencode(this.env.mailbox)+(this.env.search_request?"&_search="+
+this.env.search_request:""),this.display_message("","loading"));if(this.env.blockedobjects){if(this.gui_objects.remoteobjectsmsg)this.gui_objects.remoteobjectsmsg.style.display="block";this.enable_command("load-images","always-load",!0)}"preview"==this.env.action&&this.is_framed()&&(this.enable_command("compose","add-contact",!1),parent.rcmail.show_contentframe(!0))}else if("compose"==this.env.action){this.env.compose_commands=["send-attachment","remove-attachment","send","cancel","toggle-editor"];
+this.env.drafts_mailbox&&this.env.compose_commands.push("savedraft");this.enable_command(this.env.compose_commands,"identities",!0);if(this.env.spellcheck)this.env.spellcheck.spelling_state_observer=function(a){i.set_spellcheck_state(a)},this.env.compose_commands.push("spellcheck"),this.set_spellcheck_state("ready"),"1"==$("input[name='_is_html']").val()&&this.display_spellcheck_controls(!1);document.onmouseup=function(a){return b.doc_mouse_up(a)};this.init_messageform()}else"print"==this.env.action&&
+this.env.uid&&(bw.safari?window.setTimeout("window.print()",10):window.print());if(this.gui_objects.mailboxlist)this.env.unread_counts={},this.gui_objects.folderlist=this.gui_objects.mailboxlist,this.http_request("getunread","");this.env.mdn_request&&this.env.uid&&(a="_uid="+this.env.uid+"&_mbox="+urlencode(this.env.mailbox),confirm(this.get_label("mdnrequest"))?this.http_post("sendmdn",a):this.http_post("mark",a+"&_flag=mdnsent"));break;case "addressbook":if(this.gui_objects.folderlist)this.env.contactfolders=
+$.extend($.extend({},this.env.address_sources),this.env.contactgroups);this.enable_command("add","import",this.env.writable_source);this.enable_command("list","listgroup","listsearch","advanced-search",!0);if(this.gui_objects.contactslist)this.contact_list=new rcube_list_widget(this.gui_objects.contactslist,{multiselect:!0,draggable:this.gui_objects.folderlist?!0:!1,keyboard:!0}),this.contact_list.row_init=function(a){b.triggerEvent("insertrow",{cid:a.uid,row:a})},this.contact_list.addEventListener("keypress",
+function(a){b.contactlist_keypress(a)}),this.contact_list.addEventListener("select",function(a){b.contactlist_select(a)}),this.contact_list.addEventListener("dragstart",function(a){b.drag_start(a)}),this.contact_list.addEventListener("dragmove",function(a){b.drag_move(a)}),this.contact_list.addEventListener("dragend",function(a){b.drag_end(a)}),this.contact_list.init(),this.env.cid&&this.contact_list.highlight_row(this.env.cid),this.gui_objects.contactslist.parentNode.onmousedown=function(a){return b.click_on_list(a)},
+document.onmouseup=function(a){return b.doc_mouse_up(a)},this.gui_objects.qsearchbox&&$(this.gui_objects.qsearchbox).focusin(function(){rcmail.contact_list.blur()}),this.update_group_commands(),this.command("list");this.set_page_buttons();this.env.cid&&(this.enable_command("show","edit",!0),this.gui_objects.editform&&$("input.groupmember").change(function(){i.group_member_change(this.checked?"add":"del",i.env.cid,i.env.source,this.value)}));this.gui_objects.editform&&(this.enable_command("save",!0),
+("add"==this.env.action||"edit"==this.env.action)&&this.init_contact_form());this.gui_objects.qsearchbox&&this.enable_command("search","reset-search","moveto",!0);break;case "settings":this.enable_command("preferences","identities","save","folders",!0);if("identities"==this.env.action)this.enable_command("add",2>this.env.identities_level);else if("edit-identity"==this.env.action||"add-identity"==this.env.action)this.enable_command("add",2>this.env.identities_level),this.enable_command("save","delete",
+"edit","toggle-editor",!0);else if("folders"==this.env.action)this.enable_command("subscribe","unsubscribe","create-folder","rename-folder",!0);else if("edit-folder"==this.env.action&&this.gui_objects.editform)this.enable_command("save","folder-size",!0),parent.rcmail.env.messagecount=this.env.messagecount,parent.rcmail.enable_command("purge",this.env.messagecount),$("input[type='text']").first().select();this.gui_objects.identitieslist?(this.identity_list=new rcube_list_widget(this.gui_objects.identitieslist,
+{multiselect:!1,draggable:!1,keyboard:!1}),this.identity_list.addEventListener("select",function(a){b.identity_select(a)}),this.identity_list.init(),this.identity_list.focus(),this.env.iid&&this.identity_list.highlight_row(this.env.iid)):this.gui_objects.sectionslist?(this.sections_list=new rcube_list_widget(this.gui_objects.sectionslist,{multiselect:!1,draggable:!1,keyboard:!1}),this.sections_list.addEventListener("select",function(a){b.section_select(a)}),this.sections_list.init(),this.sections_list.focus()):
+this.gui_objects.subscriptionlist&&this.init_subscription_list();break;case "login":a=$("#rcmloginuser");a.bind("keyup",function(a){return rcmail.login_user_keyup(a)});""==a.val()?a.focus():$("#rcmloginpwd").focus();var e=new Date;a=e.getTimezoneOffset()/-60;e=e.getStdTimezoneOffset()/-60;$("#rcmlogintz").val(e);$("#rcmlogindst").val(a>e?1:0);$("form").submit(function(){$("input[type=submit]",this).prop("disabled",!0);rcmail.display_message("","loading")});this.enable_command("login",!0)}bw.ie&&$("input[type=file]").keydown(function(a){"13"==
+a.keyCode&&a.preventDefault()});this.loaded=!0;this.pending_message&&this.display_message(this.pending_message[0],this.pending_message[1],this.pending_message[2]);if(this.gui_objects.folderlist)this.gui_containers.foldertray=$(this.gui_objects.folderlist);this.triggerEvent("init",{task:this.task,action:this.env.action});for(var f in this.onloads)if("string"===typeof this.onloads[f])eval(this.onloads[f]);else if("function"===typeof this.onloads[f])this.onloads[f]();this.start_keepalive()}};this.log=
+function(a){window.console&&console.log&&console.log(a)};this.command=function(a,b,d){var e,f,h,g;d&&d.blur&&d.blur();if(this.busy)return!1;if(!this.commands[a])return this.is_framed()&&parent.rcmail.command(a,b),!1;if("mail"==this.task&&"compose"==this.env.action&&0>$.inArray(a,this.env.compose_commands)&&this.cmp_hash!=this.compose_field_hash()&&!confirm(this.get_label("notsentwarning")))return!1;if("function"===typeof this.command_handlers[a])return e=this.command_handlers[a](b,d),void 0!==e?e:
+d?!1:!0;if("string"===typeof this.command_handlers[a])return e=window[this.command_handlers[a]](b,d),void 0!==e?e:d?!1:!0;this.triggerEvent("actionbefore",{props:b,action:a});e=this.triggerEvent("before"+a,b);if(void 0!==e){if(!1===e)return!1;b=e}e=void 0;switch(a){case "login":this.gui_objects.loginform&&this.gui_objects.loginform.submit();break;case "mail":case "addressbook":case "settings":case "logout":this.switch_task(a);break;case "permaurl":if(d&&d.href&&d.target)return!0;if(this.env.permaurl)parent.location.href=
+this.env.permaurl;break;case "menu-open":case "menu-save":return this.triggerEvent(a,{props:b}),!1;case "open":if(f=this.get_single_uid())return d.href="?_task="+this.env.task+"&_action=show&_mbox="+urlencode(this.env.mailbox)+"&_uid="+f,!0;break;case "list":this.reset_qsearch();"mail"==this.task?(this.list_mailbox(b),this.env.trash_mailbox&&!this.env.flag_for_deletion&&this.set_alttext("delete",this.env.mailbox!=this.env.trash_mailbox?"movemessagetotrash":"deletemessage")):"addressbook"==this.task&&
+this.list_contacts(b);break;case "load-headers":this.load_headers(d);break;case "sort":f=b;g=this.env.sort_col==f?"ASC"==this.env.sort_order?"DESC":"ASC":"ASC";this.set_list_sorting(f,g);this.list_mailbox("","",f+"_"+g);break;case "nextpage":this.list_page("next");break;case "lastpage":this.list_page("last");break;case "previouspage":this.list_page("prev");break;case "firstpage":this.list_page("first");break;case "expunge":this.env.messagecount&&this.expunge_mailbox(this.env.mailbox);break;case "purge":case "empty-mailbox":this.env.messagecount&&
+this.purge_mailbox(this.env.mailbox);break;case "show":if("mail"==this.task){if((f=this.get_single_uid())&&(!this.env.uid||f!=this.env.uid))this.env.mailbox==this.env.drafts_mailbox?this.goto_url("compose","_draft_uid="+f+"&_mbox="+urlencode(this.env.mailbox),!0):this.show_message(f)}else"addressbook"==this.task&&(h=b?b:this.get_single_cid())&&!("show"==this.env.action&&h==this.env.cid)&&this.load_contact(h,"show");break;case "add":"addressbook"==this.task?this.load_contact(0,"add"):"settings"==this.task&&
+(this.identity_list.clear_selection(),this.load_identity(0,"add-identity"));break;case "edit":if("addressbook"==this.task&&(h=this.get_single_cid()))this.load_contact(h,"edit");else if("settings"==this.task&&b)this.load_identity(b,"edit-identity");else if("mail"==this.task&&(h=this.get_single_uid()))g=this.env.mailbox==this.env.drafts_mailbox?"_draft_uid=":"_uid=",this.goto_url("compose",g+h+"&_mbox="+urlencode(this.env.mailbox),!0);break;case "save":var k;if(g=this.gui_objects.editform){if("search"!=
+this.env.action)if((k=$("input[name='_pagesize']",g))&&k.length&&isNaN(parseInt(k.val()))){alert(this.get_label("nopagesizewarning"));k.focus();break}else{if("reload"==b)g.action+="?_reload=1";else if("settings"==this.task&&0==this.env.identities_level%2&&(k=$("input[name='_email']",g))&&k.length&&!rcube_check_email(k.val())){alert(this.get_label("noemailwarning"));k.focus();break}$("input.placeholder").each(function(){if(this.value==this._placeholder)this.value=""})}if(parent.rcmail&&parent.rcmail.env.source)g.action=
+this.add_url(g.action,"_orig_source",parent.rcmail.env.source);g.submit()}break;case "delete":"mail"==this.task?this.delete_messages():"addressbook"==this.task?this.delete_contacts():"settings"==this.task&&this.delete_identity();break;case "move":case "moveto":"mail"==this.task?this.move_messages(b):"addressbook"==this.task&&this.drag_active&&this.copy_contact(null,b);break;case "copy":"mail"==this.task&&this.copy_messages(b);break;case "mark":b&&this.mark_message(b);break;case "toggle_status":if(b&&
+!b._row)break;g="read";if(b._row.uid)f=b._row.uid,this.message_list.rows[f].deleted?g="undelete":this.message_list.rows[f].unread||(g="unread");this.mark_message(g,f);break;case "toggle_flag":if(b&&!b._row)break;g="flagged";if(b._row.uid)f=b._row.uid,this.message_list.rows[f].flagged&&(g="unflagged");this.mark_message(g,f);break;case "always-load":if(this.env.uid&&this.env.sender){this.add_contact(urlencode(this.env.sender));window.setTimeout(function(){i.command("load-images")},300);break}case "load-images":this.env.uid&&
+this.show_message(this.env.uid,!0,"preview"==this.env.action);break;case "load-attachment":g="_mbox="+urlencode(this.env.mailbox)+"&_uid="+this.env.uid+"&_part="+b.part;if(this.env.uid&&b.mimetype&&this.env.mimetypes&&0<=$.inArray(b.mimetype,this.env.mimetypes)&&("text/html"==b.mimetype&&(g+="&_safe=1"),this.attachment_win=window.open(this.env.comm_path+"&_action=get&"+g+"&_frame=1","rcubemailattachment"))){window.setTimeout(function(){i.attachment_win.focus()},10);break}this.goto_url("get",g+"&_download=1",
+!1);break;case "select-all":this.select_all_mode=b?!1:!0;this.dummy_select=!0;"invert"==b?this.message_list.invert_selection():this.message_list.select_all("page"==b?"":b);this.dummy_select=null;break;case "select-none":this.select_all_mode=!1;this.message_list.clear_selection();break;case "expand-all":this.env.autoexpand_threads=1;this.message_list.expand_all();break;case "expand-unread":this.env.autoexpand_threads=2;this.message_list.collapse_all();this.expand_unread();break;case "collapse-all":this.env.autoexpand_threads=
+0;this.message_list.collapse_all();break;case "nextmessage":this.env.next_uid&&this.show_message(this.env.next_uid,!1,"preview"==this.env.action);break;case "lastmessage":this.env.last_uid&&this.show_message(this.env.last_uid);break;case "previousmessage":this.env.prev_uid&&this.show_message(this.env.prev_uid,!1,"preview"==this.env.action);break;case "firstmessage":this.env.first_uid&&this.show_message(this.env.first_uid);break;case "checkmail":this.check_for_recent(!0);break;case "compose":g=this.url("mail/compose");
+if("mail"==this.task)g+="&_mbox="+urlencode(this.env.mailbox),b&&(g+="&_to="+urlencode(b));else if("addressbook"==this.task){if(b&&0<b.indexOf("@")){g=this.get_task_url("mail",g);this.redirect(g+"&_to="+urlencode(b));break}h=[];if(b)h.push(b);else if(this.contact_list){k=this.contact_list.get_selection();for(g=0,f=k.length;g<f;g++)h.push(k[g])}h.length?this.http_post("mailto",{_cid:h.join(","),_source:this.env.source},!0):this.env.group&&this.http_post("mailto",{_gid:this.env.group,_source:this.env.source},
+!0);break}else b&&(g+="&_to="+urlencode(b));this.redirect(g);break;case "spellcheck":window.tinyMCE&&tinyMCE.get(this.env.composebody)?tinyMCE.execCommand("mceSpellCheck",!0):this.env.spellcheck&&this.env.spellcheck.spellCheck&&this.spellcheck_ready&&(this.env.spellcheck.spellCheck(),this.set_spellcheck_state("checking"));break;case "savedraft":self.clearTimeout(this.save_timer);if(!this.gui_objects.messageform)break;if(!this.env.drafts_mailbox||this.cmp_hash==this.compose_field_hash())break;g=this.gui_objects.messageform;
+f=this.set_busy(!0,"savingmessage");g.target="savetarget";g._draft.value="1";g.action=this.add_url(g.action,"_unlock",f);g.submit();break;case "send":if(!this.gui_objects.messageform)break;if(!b.nocheck&&!this.check_compose_input(a))break;self.clearTimeout(this.save_timer);h=this.spellcheck_lang();g=this.gui_objects.messageform;f=this.set_busy(!0,"sendingmessage");g.target="savetarget";g._draft.value="";g.action=this.add_url(g.action,"_unlock",f);g.action=this.add_url(g.action,"_lang",h);g.submit();
+clearTimeout(this.request_timer);break;case "send-attachment":self.clearTimeout(this.save_timer);this.upload_file(b);break;case "insert-sig":this.change_identity($("[name='_from']")[0],!0);break;case "reply-all":case "reply-list":case "reply":if(f=this.get_single_uid())g="_reply_uid="+f+"&_mbox="+urlencode(this.env.mailbox),"reply-all"==a?g+="&_all="+(!b&&this.commands["reply-list"]?"list":"all"):"reply-list"==a&&(g+="&_all=list"),this.goto_url("compose",g,!0);break;case "forward-attachment":case "forward":if(f=
+this.get_single_uid()){g="_forward_uid="+f+"&_mbox="+urlencode(this.env.mailbox);if("forward-attachment"==a||!b&&this.env.forward_attachment)g+="&_attachment=1";this.goto_url("compose",g,!0)}break;case "print":if(f=this.get_single_uid())i.printwin=window.open(this.env.comm_path+"&_action=print&_uid="+f+"&_mbox="+urlencode(this.env.mailbox)+(this.env.safemode?"&_safe=1":"")),this.printwin&&(window.setTimeout(function(){i.printwin.focus()},20),"show"!=this.env.action&&this.mark_message("read",f));break;
+case "viewsource":if(f=this.get_single_uid())i.sourcewin=window.open(this.env.comm_path+"&_action=viewsource&_uid="+f+"&_mbox="+urlencode(this.env.mailbox)),this.sourcewin&&window.setTimeout(function(){i.sourcewin.focus()},20);break;case "download":(f=this.get_single_uid())&&this.goto_url("viewsource","&_uid="+f+"&_mbox="+urlencode(this.env.mailbox)+"&_save=1");break;case "search":if(!b&&this.gui_objects.qsearchbox)b=this.gui_objects.qsearchbox.value;if(b){this.qsearch(b);break}case "reset-search":f=
+this.env.search_request||this.env.qsearch;this.reset_qsearch();this.select_all_mode=!1;if(f&&this.env.mailbox)this.list_mailbox(this.env.mailbox,1);else if(f&&"addressbook"==this.task){if(""==this.env.source){for(g in this.env.address_sources)break;this.env.source=g;this.env.group=""}this.list_contacts(this.env.source,this.env.group,1)}break;case "listgroup":this.reset_qsearch();this.list_contacts(b.source,b.id);break;case "import":if("import"==this.env.action&&this.gui_objects.importform){if((g=
+document.getElementById("rcmimportfile"))&&!g.value){alert(this.get_label("selectimportfile"));break}this.gui_objects.importform.submit();this.set_busy(!0,"importwait");this.lock_form(this.gui_objects.importform,!0)}else this.goto_url("import",this.env.source?"_target="+urlencode(this.env.source)+"&":"");break;case "export":0<this.contact_list.rowcount&&this.goto_url("export",{_source:this.env.source,_gid:this.env.group,_search:this.env.search_request});break;case "upload-photo":this.upload_contact_photo(b);
+break;case "delete-photo":this.replace_contact_photo("-del-");break;case "preferences":case "identities":case "folders":this.goto_url("settings/"+a);break;case "undo":this.http_request("undo","",this.display_message("","loading"));break;default:g=a.replace(/-/g,"_"),this[g]&&"function"===typeof this[g]&&(e=this[g](b))}!1===this.triggerEvent("after"+a,b)&&(e=!1);this.triggerEvent("actionafter",{props:b,action:a});return!1===e?!1:d?!1:!0};this.enable_command=function(){var a,b,d=Array.prototype.slice.call(arguments),
+e=d.pop(),f;for(b=0;b<d.length;b++)if(f=d[b],"string"===typeof f)this.commands[f]=e,this.set_button(f,e?"act":"pas");else for(a in f)d.push(f[a])};this.set_busy=function(a,b,d){a&&b?(d=this.get_label(b),d==b&&(d="Loading..."),d=this.display_message(d,"loading")):!a&&d&&this.hide_message(d);this.busy=a;this.gui_objects.editform&&this.lock_form(this.gui_objects.editform,a);this.request_timer&&clearTimeout(this.request_timer);if(a&&this.env.request_timeout)this.request_timer=window.setTimeout(function(){i.request_timed_out()},
+1E3*this.env.request_timeout);return d};this.gettext=this.get_label=function(a,b){return b&&this.labels[b+"."+a]?this.labels[b+"."+a]:this.labels[a]?this.labels[a]:a};this.switch_task=function(a){if(!(this.task===a&&"mail"!=a)){var b=this.get_task_url(a);"mail"==a&&(b+="&_mbox=INBOX");this.redirect(b)}};this.get_task_url=function(a,b){if(!b)b=this.env.comm_path;return b.replace(/_task=[a-z]+/,"_task="+a)};this.request_timed_out=function(){this.set_busy(!1);this.display_message("Request timed out!",
+"error")};this.reload=function(a){if(this.is_framed())parent.rcmail.reload(a);else if(a)window.setTimeout(function(){rcmail.reload()},a);else if(window.location)location.href=this.env.comm_path+(this.env.action?"&_action="+this.env.action:"")};this.add_url=function(a,b,d){d=urlencode(d);if(/(\?.*)$/.test(a)){var e=RegExp.$1,f=RegExp("((\\?|&)"+RegExp.escape(b)+"=[^&]*)"),e=f.test(e)?e.replace(f,RegExp.$2+b+"="+d):e+("&"+b+"="+d);return a.replace(/(\?.*)$/,e)}return a+"?"+b+"="+d};this.is_framed=function(){return this.env.framed&&
+parent.rcmail&&parent.rcmail!=this&&parent.rcmail.command};this.save_pref=function(a){var b={_name:a.name,_value:a.value};if(a.session)b._session=a.session;if(a.env)this.env[a.env]=a.value;this.http_post("save-pref",b)};this.html_identifier=function(a,b){a=""+a;return b?Base64.encode(a).replace(/=+$/,"").replace(/\+/g,"-").replace(/\//g,"_"):a.replace(this.identifier_expr,"_")};this.html_identifier_decode=function(a){for(a=(""+a).replace(/-/g,"+").replace(/_/g,"/");a.length%4;)a+="=";return Base64.decode(a)};
+this.drag_menu=function(a,b){var d=rcube_event.get_modifier(a),e=this.gui_objects.message_dragmenu;return e&&d==SHIFT_KEY&&this.commands.copy?(d=rcube_event.get_mouse_pos(a),this.env.drag_target=b,$(e).css({top:d.y-10+"px",left:d.x-10+"px"}).show(),!0):!1};this.drag_menu_action=function(a){var b=this.gui_objects.message_dragmenu;b&&$(b).hide();this.command(a,this.env.drag_target);this.env.drag_target=null};this.drag_start=function(a){var b="mail"==this.task?this.env.mailboxes:this.env.contactfolders;
+this.drag_active=!0;this.preview_timer&&clearTimeout(this.preview_timer);this.preview_read_timer&&clearTimeout(this.preview_read_timer);if(this.gui_objects.folderlist&&b){this.initialBodyScrollTop=bw.ie?0:window.pageYOffset;this.initialListScrollTop=this.gui_objects.folderlist.parentNode.scrollTop;var d,e,a=$(this.gui_objects.folderlist);pos=a.offset();this.env.folderlist_coords={x1:pos.left,y1:pos.top,x2:pos.left+a.width(),y2:pos.top+a.height()};this.env.folder_coords=[];for(d in b)if(a=this.get_folder_li(d))if(e=
+a.firstChild.offsetHeight)pos=$(a.firstChild).offset(),this.env.folder_coords[d]={x1:pos.left,y1:pos.top,x2:pos.left+a.firstChild.offsetWidth,y2:pos.top+e,on:0}}};this.drag_end=function(){this.drag_active=!1;this.env.last_folder_target=null;if(this.folder_auto_timer)window.clearTimeout(this.folder_auto_timer),this.folder_auto_expand=this.folder_auto_timer=null;if(this.gui_objects.folderlist&&this.env.folder_coords)for(var a in this.env.folder_coords)this.env.folder_coords[a].on&&$(this.get_folder_li(a)).removeClass("droptarget")};
+this.drag_move=function(a){if(this.gui_objects.folderlist&&this.env.folder_coords){var b,d,e,f,h;d="draglayernormal";a=rcube_event.get_mouse_pos(a);f=this.env.folderlist_coords;e=bw.ie?-document.documentElement.scrollTop:this.initialBodyScrollTop;var g=this.initialListScrollTop-this.gui_objects.folderlist.parentNode.scrollTop;this.contact_list&&this.contact_list.draglayer&&(h=this.contact_list.draglayer.attr("class"));a.y+=-g-e;if(a.x<f.x1||a.x>=f.x2||a.y<f.y1||a.y>=f.y2){if(this.env.last_folder_target)$(this.get_folder_li(this.env.last_folder_target)).removeClass("droptarget"),
+this.env.folder_coords[this.env.last_folder_target].on=0,this.env.last_folder_target=null}else for(b in this.env.folder_coords)if(f=this.env.folder_coords[b],a.x>=f.x1&&a.x<f.x2&&a.y>=f.y1&&a.y<f.y2)if(f=this.check_droptarget(b)){d=this.get_folder_li(b);e=$(d.getElementsByTagName("div")[0]);if(e.hasClass("collapsed"))this.folder_auto_timer&&window.clearTimeout(this.folder_auto_timer),this.folder_auto_expand=b,this.folder_auto_timer=window.setTimeout(function(){rcmail.command("collapse-folder",rcmail.folder_auto_expand);
+rcmail.drag_start(null)},1E3);else if(this.folder_auto_timer)window.clearTimeout(this.folder_auto_timer),this.folder_auto_expand=this.folder_auto_timer=null;$(d).addClass("droptarget");this.env.folder_coords[b].on=1;this.env.last_folder_target=b;d="draglayer"+(1<f?"copy":"normal")}else this.env.last_folder_target=null;else if(f.on)$(this.get_folder_li(b)).removeClass("droptarget"),this.env.folder_coords[b].on=0;d!=h&&this.contact_list&&this.contact_list.draglayer&&this.contact_list.draglayer.attr("class",
+d)}};this.collapse_folder=function(a){var b=this.get_folder_li(a,"",!0),d=$("div:first",b),e=$("ul:first",b);if(d.hasClass("collapsed"))e.show(),d.removeClass("collapsed").addClass("expanded"),this.env.collapsed_folders=this.env.collapsed_folders.replace(RegExp("&"+urlencode(a)+"&"),"");else if(d.hasClass("expanded"))e.hide(),d.removeClass("expanded").addClass("collapsed"),this.env.collapsed_folders=this.env.collapsed_folders+"&"+urlencode(a)+"&",0==this.env.mailbox.indexOf(a+this.env.delimiter)&&
+this.command("list",a);else return;if(bw.ie6||bw.ie7)if((d=b.nextSibling?b.nextSibling.getElementsByTagName("ul"):null)&&d.length&&(b=d[0])&&b.style&&"none"!=b.style.display)b.style.display="none",b.style.display="";this.command("save-pref",{name:"collapsed_folders",value:this.env.collapsed_folders});this.set_unread_count_display(a,!1)};this.doc_mouse_up=function(a){var b,d,e;if(!$(rcube_event.get_target(a)).closest(".ui-dialog, .ui-widget-overlay").length){(d=this.message_list)?(rcube_mouse_is_over(a,
+d.list.parentNode)?d.focus():d.blur(),b=this.env.mailboxes):(d=this.contact_list)?(rcube_mouse_is_over(a,d.list.parentNode)?d.focus():d.blur(),b=this.env.contactfolders):this.ksearch_value&&this.ksearch_blur();if(this.drag_active&&b&&this.env.last_folder_target)b=b[this.env.last_folder_target],$(this.get_folder_li(this.env.last_folder_target)).removeClass("droptarget"),this.env.last_folder_target=null,d.draglayer.hide(),this.drag_menu(a,b)||this.command("moveto",b);if(this.buttons_sel){for(e in this.buttons_sel)"function"!==
+typeof e&&this.button_out(this.buttons_sel[e],e);this.buttons_sel={}}}};this.click_on_list=function(){this.gui_objects.qsearchbox&&this.gui_objects.qsearchbox.blur();this.message_list?this.message_list.focus():this.contact_list&&this.contact_list.focus();return!0};this.msglist_select=function(a){this.preview_timer&&clearTimeout(this.preview_timer);this.preview_read_timer&&clearTimeout(this.preview_read_timer);var b=null!=a.get_single_selection();this.enable_command(this.env.message_commands,b);b&&
+(this.env.mailbox==this.env.drafts_mailbox?this.enable_command("reply","reply-all","reply-list","forward","forward-attachment",!1):this.env.messages[a.get_single_selection()].ml||this.enable_command("reply-list",!1));this.enable_command("delete","moveto","copy","mark",0<a.selection.length?!0:!1);if(b||a.selection.length&&a.selection.length!=a.rowcount)this.select_all_mode=!1;b&&this.env.contentframe&&!a.multi_selecting&&!this.dummy_select?this.preview_timer=window.setTimeout(function(){i.msglist_get_preview()},
+200):this.env.contentframe&&this.show_contentframe(!1)};this.msglist_click=function(a){if(!a.multi_selecting&&this.env.contentframe&&a.get_single_selection()&&window.frames&&window.frames[this.env.contentframe]&&0<=window.frames[this.env.contentframe].location.href.indexOf(this.env.blankpage))this.preview_timer&&clearTimeout(this.preview_timer),this.preview_read_timer&&clearTimeout(this.preview_read_timer),this.preview_timer=window.setTimeout(function(){i.msglist_get_preview()},200)};this.msglist_dbl_click=
+function(a){this.preview_timer&&clearTimeout(this.preview_timer);this.preview_read_timer&&clearTimeout(this.preview_read_timer);(a=a.get_single_selection())&&this.env.mailbox==this.env.drafts_mailbox?this.goto_url("compose","_draft_uid="+a+"&_mbox="+urlencode(this.env.mailbox),!0):a&&this.show_message(a,!1,!1)};this.msglist_keypress=function(a){a.modkey!=CONTROL_KEY&&(a.key_pressed==a.ENTER_KEY?this.command("show"):a.key_pressed==a.DELETE_KEY||a.key_pressed==a.BACKSPACE_KEY?this.command("delete"):
+33==a.key_pressed?this.command("previouspage"):34==a.key_pressed&&this.command("nextpage"))};this.msglist_get_preview=function(){var a=this.get_single_uid();a&&this.env.contentframe&&!this.drag_active?this.show_message(a,!1,!0):this.env.contentframe&&this.show_contentframe(!1)};this.msglist_expand=function(a){if(this.env.messages[a.uid])this.env.messages[a.uid].expanded=a.expanded};this.msglist_set_coltypes=function(a){var b,d=a.list.tHead.rows[0].cells;this.env.coltypes=[];for(a=0;a<d.length;a++)d[a].id&&
+d[a].id.match(/^rcm/)&&(b=d[a].id.replace(/^rcm/,""),this.env.coltypes.push("to"==b?"from":b));if(0<=(a=$.inArray("flag",this.env.coltypes)))this.env.flagged_col=a;if(0<=(a=$.inArray("subject",this.env.coltypes)))this.env.subject_col=a;this.command("save-pref",{name:"list_cols",value:this.env.coltypes,session:"list_attrib/columns"})};this.check_droptarget=function(a){var b=!1,d=!1;if("mail"==this.task)b=this.env.mailboxes[a]&&this.env.mailboxes[a].id!=this.env.mailbox&&!this.env.mailboxes[a].virtual;
+else if("settings"==this.task)b=a!=this.env.mailbox;else if("addressbook"==this.task&&a!=this.env.source&&this.env.contactfolders[a])"group"==this.env.contactfolders[a].type?(d=this.env.contactfolders[a].source,b=this.env.contactfolders[a].id!=this.env.group&&!this.env.contactfolders[d].readonly,d=d!=this.env.source):(b=!this.env.contactfolders[a].readonly,d=!0);return b?d?2:1:0};this.init_message_row=function(a){var b,d=this,e=a.uid,f=(null!=this.env.status_col?"status":"msg")+"icn"+a.uid;e&&this.env.messages[e]&&
+$.extend(a,this.env.messages[e]);if(a.icon=document.getElementById(f))a.icon._row=a.obj,a.icon.onmousedown=function(a){d.command("toggle_status",this);rcube_event.cancel(a)};a.msgicon=null!=this.env.status_col?document.getElementById("msgicn"+a.uid):a.icon;if(null!=this.env.flagged_col&&(a.flagicon=document.getElementById("flagicn"+a.uid)))a.flagicon._row=a.obj,a.flagicon.onmousedown=function(a){d.command("toggle_flag",this);rcube_event.cancel(a)};if(!a.depth&&a.has_children&&(b=document.getElementById("rcmexpando"+
+a.uid)))a.expando=b,b.onmousedown=function(a){return d.expand_message_row(a,e)};this.triggerEvent("insertrow",{uid:e,row:a})};this.add_message_row=function(a,b,d,e){if(!this.gui_objects.messagelist||!this.message_list||d.mbox!=this.env.mailbox&&!d.skip_mbox_check)return!1;this.env.messages[a]||(this.env.messages[a]={});$.extend(this.env.messages[a],{deleted:d.deleted?1:0,replied:d.answered?1:0,unread:!d.seen?1:0,forwarded:d.forwarded?1:0,flagged:d.flagged?1:0,has_children:d.has_children?1:0,depth:d.depth?
+d.depth:0,unread_children:d.unread_children?d.unread_children:0,parent_uid:d.parent_uid?d.parent_uid:0,selected:this.select_all_mode||this.message_list.in_selection(a),ml:d.ml?1:0,ctype:d.ctype,flags:d.extra_flags});var f,h,g,k="",j="",l=this.message_list;g=l.rows;var i=this.env.messages[a];f="message"+(!d.seen?" unread":"")+(d.deleted?" deleted":"")+(d.flagged?" flagged":"")+(d.unread_children&&d.seen&&!this.env.autoexpand_threads?" unroot":"")+(i.selected?" selected":"");var m=document.createElement("tr");
+m.id="rcmrow"+a;m.className=f;f="msgicon";null===this.env.status_col&&(f+=" status",d.deleted?f+=" deleted":d.seen?0<d.unread_children&&(f+=" unreadchildren"):f+=" unread");d.answered&&(f+=" replied");d.forwarded&&(f+=" forwarded");i.selected&&!l.in_selection(a)&&l.selection.push(a);if(this.env.threading)if(i.depth)k+='<span id="rcmtab'+a+'" class="branch" style="width:'+15*i.depth+'px;">&nbsp;&nbsp;</span>',g[i.parent_uid]&&!1===g[i.parent_uid].expanded||(0==this.env.autoexpand_threads||2==this.env.autoexpand_threads)&&
+(!g[i.parent_uid]||!g[i.parent_uid].expanded)?(m.style.display="none",i.expanded=!1):i.expanded=!0;else if(i.has_children){if(void 0===i.expanded&&(1==this.env.autoexpand_threads||2==this.env.autoexpand_threads&&i.unread_children))i.expanded=!0;j='<div id="rcmexpando'+a+'" class="'+(i.expanded?"expanded":"collapsed")+'">&nbsp;&nbsp;</div>'}k+='<span id="msgicn'+a+'" class="'+f+'">&nbsp;</span>';if(!bw.ie&&b.subject)g=d.mbox==this.env.drafts_mailbox?"_draft_uid":"_uid",b.subject='<a href="./?_task=mail&_action='+
+(d.mbox==this.env.drafts_mailbox?"compose":"show")+"&_mbox="+urlencode(d.mbox)+"&"+g+"="+a+'" onclick="return rcube_event.cancel(event)" onmouseover="rcube_webmail.long_subject_title(this,'+(i.depth+1)+')">'+b.subject+"</a>";for(h in this.env.coltypes){f=this.env.coltypes[h];g=document.createElement("td");g.className=(""+f).toLowerCase();if("flag"==f)f=d.flagged?"flagged":"unflagged",f='<span id="flagicn'+a+'" class="'+f+'">&nbsp;</span>';else if("attachment"==f)f=/application\/|multipart\/m/.test(d.ctype)?
+'<span class="attachment">&nbsp;</span>':/multipart\/report/.test(d.ctype)?'<span class="report">&nbsp;</span>':"&nbsp;";else if("status"==f)f=d.deleted?"deleted":d.seen?0<d.unread_children?"unreadchildren":"msgicon":"unread",f='<span id="statusicn'+a+'" class="'+f+'">&nbsp;</span>';else if("threads"==f)f=j;else if("subject"==f){if(bw.ie)g.onmouseover=function(){rcube_webmail.long_subject_title_ie(this,i.depth+1)};f=k+b[f]}else f="priority"==f?0<d.prio&&6>d.prio?'<span class="prio'+d.prio+'">&nbsp;</span>':
+"&nbsp;":b[f];g.innerHTML=f;m.appendChild(g)}l.insert_row(m,e);e&&this.env.pagesize&&l.rowcount>this.env.pagesize&&(a=l.get_last_row(),l.remove_row(a),l.clear_selection(a))};this.set_list_sorting=function(a,b){$("#rcm"+this.env.sort_col).removeClass("sorted"+this.env.sort_order.toUpperCase());a&&$("#rcm"+a).addClass("sorted"+b);this.env.sort_col=a;this.env.sort_order=b};this.set_list_options=function(a,b,d,e){var f,h="";if(void 0===b)b=this.env.sort_col;if(!d)d=this.env.sort_order;if(this.env.sort_col!=
+b||this.env.sort_order!=d)f=1,this.set_list_sorting(b,d);this.env.threading!=e&&(f=1,h+="&_threads="+e);if(a&&a.length){for(var g,k,j=[],i=this.env.coltypes,e=0;e<i.length;e++)k="to"==i[e]?"from":i[e],g=$.inArray(k,a),-1!=g&&(j.push(k),delete a[g]);for(e=0;e<a.length;e++)a[e]&&j.push(a[e]);j.join()!=i.join()&&(f=1,h+="&_cols="+j.join(","))}f&&this.list_mailbox("","",b+"_"+d,h)};this.show_message=function(a,b,d){if(a){var e=window,f=d?"preview":"show",h="&_action="+f+"&_uid="+a+"&_mbox="+urlencode(this.env.mailbox);
+d&&this.env.contentframe&&window.frames&&window.frames[this.env.contentframe]&&(e=window.frames[this.env.contentframe],h+="&_framed=1");b&&(h+="&_safe=1");this.env.search_request&&(h+="&_search="+this.env.search_request);if("preview"==f&&0<=(""+e.location.href).indexOf(h))this.show_contentframe(!0);else if(this.location_href(this.env.comm_path+h,e,!0),"preview"==f&&this.message_list&&this.message_list.rows[a]&&this.message_list.rows[a].unread&&0<=this.env.preview_pane_mark_read)this.preview_read_timer=
+window.setTimeout(function(){i.set_message(a,"unread",!1);i.update_thread_root(a,"read");i.env.unread_counts[i.env.mailbox]&&(i.env.unread_counts[i.env.mailbox]-=1,i.set_unread_count(i.env.mailbox,i.env.unread_counts[i.env.mailbox],"INBOX"==i.env.mailbox));0<i.env.preview_pane_mark_read&&i.http_post("mark","_uid="+a+"&_flag=read&_quiet=1")},1E3*this.env.preview_pane_mark_read)}};this.show_contentframe=function(a){var b,d;if(this.env.contentframe&&(b=$("#"+this.env.contentframe))&&b.length)if(!a&&
+(d=window.frames[this.env.contentframe])){if(d.location&&0>d.location.href.indexOf(this.env.blankpage))d.location.href=this.env.blankpage}else if(!bw.safari&&!bw.konq)b[a?"show":"hide"]();!a&&this.busy&&this.set_busy(!1,null,this.env.frame_lock)};this.lock_frame=function(){if(!this.env.frame_lock)(this.is_framed()?parent.rcmail:this).env.frame_lock=this.set_busy(!0,"loading")};this.list_page=function(a){"next"==a?a=this.env.current_page+1:"last"==a?a=this.env.pagecount:"prev"==a&&1<this.env.current_page?
+a=this.env.current_page-1:"first"==a&&1<this.env.current_page&&(a=1);if(0<a&&a<=this.env.pagecount)this.env.current_page=a,"mail"==this.task?this.list_mailbox(this.env.mailbox,a):"addressbook"==this.task&&this.list_contacts(this.env.source,this.env.group,a)};this.filter_mailbox=function(a){var b=this.set_busy(!0,"searching");this.clear_message_list();this.env.current_page=1;this.http_request("search",this.search_params(!1,a),b)};this.list_mailbox=function(a,b,d,e){var f="",h=window;a||(a=this.env.mailbox?
+this.env.mailbox:"INBOX");e&&(f+=e);d&&(f+="&_sort="+d);this.env.search_request&&(f+="&_search="+this.env.search_request);if(this.env.mailbox!=a)b=1,this.env.current_page=b,this.select_all_mode=!1;this.clear_message_list();if(a!=this.env.mailbox||a==this.env.mailbox&&!b&&!d)f+="&_refresh=1";this.select_folder(a,"",!0);this.env.mailbox=a;this.gui_objects.messagelist?this.list_mailbox_remote(a,b,f):(this.env.contentframe&&window.frames&&window.frames[this.env.contentframe]&&(h=window.frames[this.env.contentframe],
+f+="&_framed=1"),a&&(this.set_busy(!0,"loading"),this.location_href(this.env.comm_path+"&_mbox="+urlencode(a)+(b?"&_page="+b:"")+f,h)))};this.clear_message_list=function(){this.env.messages={};this.last_selected=0;this.show_contentframe(!1);this.message_list&&this.message_list.clear(!0)};this.list_mailbox_remote=function(a,b,d){this.message_list.clear();a="_mbox="+urlencode(a)+(b?"&_page="+b:"");b=this.set_busy(!0,"loading");this.http_request("list",a+d,b)};this.update_selection=function(){var a=
+this.message_list.selection,b=this.message_list.rows,d,e=[];for(d in a)b[a[d]]&&e.push(a[d]);this.message_list.selection=e};this.expand_unread=function(){for(var a,b=this.gui_objects.messagelist.tBodies[0].firstChild;b;){if(1==b.nodeType&&(a=this.message_list.rows[b.uid])&&a.unread_children)this.message_list.expand_all(a),this.set_unread_children(a.uid);b=b.nextSibling}return!1};this.expand_message_row=function(a,b){var d=this.message_list.rows[b];d.expanded=!d.expanded;this.set_unread_children(b);
+d.expanded=!d.expanded;this.message_list.expand_row(a,b)};this.expand_threads=function(){if(this.env.threading&&this.env.autoexpand_threads&&this.message_list)switch(this.env.autoexpand_threads){case 2:this.expand_unread();break;case 1:this.message_list.expand_all()}};this.init_threads=function(a,b){if(b&&b!=this.env.mailbox)return!1;for(var d=0,e=a.length;d<e;d++)this.add_tree_icons(a[d]);this.expand_threads()};this.add_tree_icons=function(a){var b,d,e,f,h=[],g=[],k,j=this.message_list.rows;for(k=
+a?j[a]?j[a].obj:null:this.message_list.list.tBodies[0].firstChild;k;){if(1==k.nodeType&&(d=j[k.uid]))if(d.depth){for(b=h.length-1;0<=b&&!(e=h[b].length,e>d.depth?(f=e-d.depth,h[b][f]&2||(h[b][f]=h[b][f]?h[b][f]+2:2)):e==d.depth&&(h[b][0]&2||(h[b][0]+=2)),d.depth>e);b--);h.push(Array(d.depth));h[h.length-1][0]=1;g.push(d.uid)}else{if(h.length){for(b in h)this.set_tree_icons(g[b],h[b]);h=[];g=[]}if(a&&k!=j[a].obj)break}k=k.nextSibling}if(h.length)for(b in h)this.set_tree_icons(g[b],h[b])};this.set_tree_icons=
+function(a,b){var d,e=[],f="",h=b.length;for(d=0;d<h;d++)2<b[d]?e.push({"class":"l3",width:15}):1<b[d]?e.push({"class":"l2",width:15}):0<b[d]?e.push({"class":"l1",width:15}):e.length&&!e[e.length-1]["class"]?e[e.length-1].width+=15:e.push({"class":null,width:15});for(d=e.length-1;0<=d;d--)f=e[d]["class"]?f+('<div class="tree '+e[d]["class"]+'" />'):f+('<div style="width:'+e[d].width+'px" />');f&&$("#rcmtab"+a).html(f)};this.update_thread_root=function(a,b){if(this.env.threading){var d=this.message_list.find_root(a);
+if(a!=d){var e=this.message_list.rows[d];if("read"==b&&e.unread_children)e.unread_children--;else if("unread"==b&&e.has_children)e.unread_children=e.unread_children?e.unread_children+1:1;else return;this.set_message_icon(d);this.set_unread_children(d)}}};this.update_thread=function(a){if(!this.env.threading)return 0;var b,d=0,e=this.message_list.rows,f=e[a],h=e[a].depth,g=[];f.depth?f.unread&&(a=this.message_list.find_root(a),e[a].unread_children--,this.set_unread_children(a)):d--;a=f.parent_uid;
+for(f=f.obj.nextSibling;f;){if(1==f.nodeType&&(b=e[f.uid])){if(!b.depth||b.depth<=h)break;b.depth--;$("#rcmtab"+b.uid).width(15*b.depth).html("");if(b.depth){if(b.depth==h)b.parent_uid=a;b.unread&&g.length&&g[g.length-1].unread_children++}else{d++;b.parent_uid=0;if(b.has_children)$("#rcmrow"+b.uid+" .leaf:first").attr("id","rcmexpando"+b.uid).attr("class","none"!=b.obj.style.display?"expanded":"collapsed").bind("mousedown",{uid:b.uid,p:this},function(a){return a.data.p.expand_message_row(a,a.data.uid)}),
+b.unread_children=0,g.push(b);"none"==b.obj.style.display&&$(b.obj).show()}}f=f.nextSibling}for(b=0;b<g.length;b++)this.set_unread_children(g[b].uid);return d};this.delete_excessive_thread_rows=function(){for(var a=this.message_list.rows,b=this.message_list.list.tBodies[0].firstChild,d=this.env.pagesize+1;b;){if(1==b.nodeType&&(r=a[b.uid]))!r.depth&&d&&d--,d||this.message_list.remove_row(b.uid);b=b.nextSibling}};this.set_message_icon=function(a){var b=this.message_list.rows[a];if(!b)return!1;if(b.icon)a=
+"msgicon",b.deleted?a+=" deleted":b.unread?a+=" unread":b.unread_children&&(a+=" unreadchildren"),b.msgicon==b.icon&&(b.replied&&(a+=" replied"),b.forwarded&&(a+=" forwarded"),a+=" status"),b.icon.className=a;if(b.msgicon&&b.msgicon!=b.icon)a="msgicon",!b.unread&&b.unread_children&&(a+=" unreadchildren"),b.replied&&(a+=" replied"),b.forwarded&&(a+=" forwarded"),b.msgicon.className=a;if(b.flagicon)a=b.flagged?"flagged":"unflagged",b.flagicon.className=a};this.set_message_status=function(a,b,d){a=this.message_list.rows[a];
+if(!a)return!1;if("unread"==b)a.unread=d;else if("deleted"==b)a.deleted=d;else if("replied"==b)a.replied=d;else if("forwarded"==b)a.forwarded=d;else if("flagged"==b)a.flagged=d};this.set_message=function(a,b,d){var e=this.message_list.rows[a];if(!e)return!1;b&&this.set_message_status(a,b,d);b=$(e.obj);e.unread&&!b.hasClass("unread")?b.addClass("unread"):!e.unread&&b.hasClass("unread")&&b.removeClass("unread");e.deleted&&!b.hasClass("deleted")?b.addClass("deleted"):!e.deleted&&b.hasClass("deleted")&&
+b.removeClass("deleted");e.flagged&&!b.hasClass("flagged")?b.addClass("flagged"):!e.flagged&&b.hasClass("flagged")&&b.removeClass("flagged");this.set_unread_children(a);this.set_message_icon(a)};this.set_unread_children=function(a){a=this.message_list.rows[a];a.parent_uid||(!a.unread&&a.unread_children&&!a.expanded?$(a.obj).addClass("unroot"):$(a.obj).removeClass("unroot"))};this.copy_messages=function(a){if(a&&"object"===typeof a)a=a.id;if(a&&!(a==this.env.mailbox||!this.env.uid&&(!this.message_list||
+!this.message_list.get_selection().length))){var b=[],d=this.display_message(this.get_label("copyingmessage"),"loading"),a="&_target_mbox="+urlencode(a)+"&_from="+(this.env.action?this.env.action:"");if(this.env.uid)b[0]=this.env.uid;else{var e=this.message_list.get_selection(),f;for(f in e)b.push(e[f])}a+="&_uid="+this.uids_to_list(b);this.http_post("copy","_mbox="+urlencode(this.env.mailbox)+a,d)}};this.move_messages=function(a){if(a&&"object"===typeof a)a=a.id;if(a&&!(a==this.env.mailbox||!this.env.uid&&
+(!this.message_list||!this.message_list.get_selection().length))){var b=!1,a="&_target_mbox="+urlencode(a)+"&_from="+(this.env.action?this.env.action:"");"show"==this.env.action?b=this.set_busy(!0,"movingmessage"):this.show_contentframe(!1);this.enable_command(this.env.message_commands,!1);this._with_selected_messages("moveto",b,a)}};this.delete_messages=function(){var a,b,d,e=this.env.trash_mailbox,f=this.message_list,h=f?$.merge([],f.get_selection()):[];if(this.env.uid||h.length){for(b=0,d=h.length;b<
+d;b++)a=h[b],f.rows[a].has_children&&!f.rows[a].expanded&&f.select_childs(a);if(this.env.flag_for_deletion)return this.mark_message("delete"),!1;!e||this.env.mailbox==e?this.permanently_remove_messages():f&&f.modkey==SHIFT_KEY?confirm(this.get_label("deletemessagesconfirm"))&&this.permanently_remove_messages():this.move_messages(e);return!0}};this.permanently_remove_messages=function(){if(this.env.uid||this.message_list&&this.message_list.get_selection().length)this.show_contentframe(!1),this._with_selected_messages("delete",
+!1,"&_from="+(this.env.action?this.env.action:""))};this._with_selected_messages=function(a,b,d){var e=[],f=0;if(this.env.uid)e[0]=this.env.uid;else{var h,g,k,j=[],i=this.message_list.get_selection();for(h=0,len=i.length;h<len;h++)g=i[h],e.push(g),this.env.threading&&(f+=this.update_thread(g),k=this.message_list.find_root(g),k!=g&&0>$.inArray(k,j)&&j.push(k)),this.message_list.remove_row(g,this.env.display_next&&h==i.length-1);this.env.display_next||this.message_list.clear_selection();for(h=0,len=
+j.length;h<len;h++)this.add_tree_icons(j[h])}this.env.search_request&&(d+="&_search="+this.env.search_request);this.env.display_next&&this.env.next_uid&&(d+="&_next_uid="+this.env.next_uid);0>f?d+="&_count="+-1*f:0<f&&this.delete_excessive_thread_rows();d+="&_uid="+this.uids_to_list(e);b||(b=this.display_message(this.get_label("moveto"==a?"movingmessage":"deletingmessage"),"loading"));this.http_post(a,"_mbox="+urlencode(this.env.mailbox)+d,b)};this.mark_message=function(a,b){var d=[],e=[],f,h,g;g=
+this.message_list?this.message_list.get_selection():[];if(b)d[0]=b;else if(this.env.uid)d[0]=this.env.uid;else if(this.message_list)for(h=0,f=g.length;h<f;h++)d.push(g[h]);if(this.message_list)for(h=0,f=d.length;h<f;h++)g=d[h],("read"==a&&this.message_list.rows[g].unread||"unread"==a&&!this.message_list.rows[g].unread||"delete"==a&&!this.message_list.rows[g].deleted||"undelete"==a&&this.message_list.rows[g].deleted||"flagged"==a&&!this.message_list.rows[g].flagged||"unflagged"==a&&this.message_list.rows[g].flagged)&&
+e.push(g);else e=d;if(e.length||this.select_all_mode)switch(a){case "read":case "unread":this.toggle_read_status(a,e);break;case "delete":case "undelete":this.toggle_delete_status(e);break;case "flagged":case "unflagged":this.toggle_flagged_status(a,d)}};this.toggle_read_status=function(a,b){var d,e=b.length,f="_uid="+this.uids_to_list(b)+"&_flag="+a,h=this.display_message(this.get_label("markingmessage"),"loading");for(d=0;d<e;d++)this.set_message(b[d],"unread","unread"==a?!0:!1);this.env.search_request&&
+(f+="&_search="+this.env.search_request);this.http_post("mark",f,h);for(d=0;d<e;d++)this.update_thread_root(b[d],a)};this.toggle_flagged_status=function(a,b){var d,e=b.length,f="_uid="+this.uids_to_list(b)+"&_flag="+a,h=this.display_message(this.get_label("markingmessage"),"loading");for(d=0;d<e;d++)this.set_message(b[d],"flagged","flagged"==a?!0:!1);this.env.search_request&&(f+="&_search="+this.env.search_request);this.http_post("mark",f,h)};this.toggle_delete_status=function(a){var b=a.length,d,
+e,f=!0,h=this.message_list?this.message_list.rows:[];if(1==b)return!h.length||h[a[0]]&&!h[a[0]].deleted?this.flag_as_deleted(a):this.flag_as_undeleted(a),!0;for(d=0;d<b;d++)if(e=a[d],h[e]&&!h[e].deleted){f=!1;break}f?this.flag_as_undeleted(a):this.flag_as_deleted(a);return!0};this.flag_as_undeleted=function(a){var b,d=a.length,e="_uid="+this.uids_to_list(a)+"&_flag=undelete",f=this.display_message(this.get_label("markingmessage"),"loading");for(b=0;b<d;b++)this.set_message(a[b],"deleted",!1);this.env.search_request&&
+(e+="&_search="+this.env.search_request);this.http_post("mark",e,f);return!0};this.flag_as_deleted=function(a){for(var b="",d=[],b=this.message_list?this.message_list.rows:[],e=0,f=0,h=a.length;f<h;f++)uid=a[f],b[uid]&&(b[uid].unread&&(d[d.length]=uid),this.env.skip_deleted?(e+=this.update_thread(uid),this.message_list.remove_row(uid,this.env.display_next&&f==this.message_list.selection.length-1)):this.set_message(uid,"deleted",!0));this.env.skip_deleted&&this.message_list&&(this.env.display_next||
+this.message_list.clear_selection(),0>e||0<e&&this.delete_excessive_thread_rows());b="&_from="+(this.env.action?this.env.action:"");lock=this.display_message(this.get_label("markingmessage"),"loading");d.length&&(b+="&_ruid="+this.uids_to_list(d));this.env.skip_deleted&&this.env.display_next&&this.env.next_uid&&(b+="&_next_uid="+this.env.next_uid);this.env.search_request&&(b+="&_search="+this.env.search_request);this.http_post("mark","_uid="+this.uids_to_list(a)+"&_flag=delete"+b,lock);return!0};
+this.flag_deleted_as_read=function(a){var b,d,e,f=this.message_list?this.message_list.rows:[],a=(""+a).split(",");for(d=0,e=a.length;d<e;d++)b=a[d],f[b]&&this.set_message(b,"unread",!1)};this.uids_to_list=function(a){return this.select_all_mode?"*":a.join(",")};this.expunge_mailbox=function(a){var b,d="_mbox="+urlencode(a);a==this.env.mailbox&&(b=this.set_busy(!0,"loading"),d+="&_reload=1",this.env.search_request&&(d+="&_search="+this.env.search_request));this.http_post("expunge",d,b)};this.purge_mailbox=
+function(a){var b=!1,d="_mbox="+urlencode(a);if(!confirm(this.get_label("purgefolderconfirm")))return!1;a==this.env.mailbox&&(b=this.set_busy(!0,"loading"),d+="&_reload=1");this.http_post("purge",d,b)};this.purge_mailbox_test=function(){return this.env.messagecount&&(this.env.mailbox==this.env.trash_mailbox||this.env.mailbox==this.env.junk_mailbox||this.env.mailbox.match("^"+RegExp.escape(this.env.trash_mailbox)+RegExp.escape(this.env.delimiter))||this.env.mailbox.match("^"+RegExp.escape(this.env.junk_mailbox)+
+RegExp.escape(this.env.delimiter)))};this.login_user_keyup=function(a){var b=rcube_event.get_keycode(a),d=$("#rcmloginpwd");return 13==b&&d.length&&!d.val()?(d.focus(),rcube_event.cancel(a)):!0};this.init_messageform=function(){if(!this.gui_objects.messageform)return!1;var a=$("[name='_from']"),b=$("[name='_to']"),d=$("input[name='_subject']"),e=$("[name='_message']").get(0),f="1"==$("input[name='_is_html']").val(),h=["cc","bcc","replyto","followupto"],g;0<this.env.autocomplete_threads&&(g={threads:this.env.autocomplete_threads,
+sources:this.env.autocomplete_sources});this.init_address_input_events(b,g);for(var k in h)this.init_address_input_events($("[name='_"+h[k]+"']"),g);f||(this.set_caret_pos(e,this.env.top_posting?0:$(e).val().length),"select-one"==a.prop("type")&&""==$("input[name='_draft_saveid']").val()&&this.change_identity(a[0]));""==b.val()?b.focus():""==d.val()?d.focus():e&&e.focus();this.env.compose_focus_elem=document.activeElement;this.compose_field_hash(!0);this.auto_save_start()};this.init_address_input_events=
+function(a,b){this.env.recipients_delimiter=this.env.recipients_separator+" ";a[bw.ie||bw.safari||bw.chrome?"keydown":"keypress"](function(a){return i.ksearch_keydown(a,this,b)}).attr("autocomplete","off")};this.check_compose_input=function(a){var b,d=$("[name='_to']"),e=$("[name='_cc']"),f=$("[name='_bcc']"),h=$("[name='_from']"),g=$("[name='_subject']"),k=$("[name='_message']");if("text"==h.prop("type")&&!rcube_check_email(h.val(),!0))return alert(this.get_label("nosenderwarning")),h.focus(),!1;
+e=d.val()?d.val():e.val()?e.val():f.val();if(!rcube_check_email(e.replace(/^\s+/,"").replace(/[\s,;]+$/,""),!0))return alert(this.get_label("norecipientwarning")),d.focus(),!1;for(var j in this.env.attachments)if("object"===typeof this.env.attachments[j]&&!this.env.attachments[j].complete)return alert(this.get_label("notuploadedwarning")),!1;if(""==g.val()){b=$('<div class="prompt">').html('<div class="message">'+this.get_label("nosubjectwarning")+"</div>").appendTo(document.body);var l=$("<input>").attr("type",
+"text").attr("size",30).appendTo(b).val(this.get_label("nosubject")),d={};d[this.get_label("cancel")]=function(){g.focus();$(this).dialog("close")};d[this.get_label("sendmessage")]=function(){g.val(l.val());$(this).dialog("close");i.command(a,{nocheck:!0})};b.dialog({modal:!0,resizable:!1,buttons:d,close:function(){$(this).remove()}});l.select();return!1}this.stop_spellchecking();window.tinyMCE&&(b=tinyMCE.get(this.env.composebody));if(!b&&""==k.val()&&!confirm(this.get_label("nobodywarning")))return k.focus(),
+!1;if(b){if(!b.getContent()&&!confirm(this.get_label("nobodywarning")))return b.focus(),!1;tinyMCE.triggerSave()}return!0};this.toggle_editor=function(a){if("html"==a.mode)this.display_spellcheck_controls(!1),this.plain2html($("#"+a.id).val(),a.id),tinyMCE.execCommand("mceAddControl",!1,a.id);else{var b=tinyMCE.get(a.id);b.plugins.spellchecker&&b.plugins.spellchecker.active&&b.execCommand("mceSpellCheck",!1);if(b=b.getContent()){if(!confirm(this.get_label("editorwarning")))return!1;this.html2plain(b,
+a.id)}tinyMCE.execCommand("mceRemoveControl",!1,a.id);this.display_spellcheck_controls(!0)}return!0};this.stop_spellchecking=function(){var a;if(window.tinyMCE&&(a=tinyMCE.get(this.env.composebody)))a.plugins.spellchecker&&a.plugins.spellchecker.active&&a.execCommand("mceSpellCheck");else if((a=this.env.spellcheck)&&!this.spellcheck_ready)$(a.spell_span).trigger("click"),this.set_spellcheck_state("ready")};this.display_spellcheck_controls=function(a){this.env.spellcheck&&(a||this.stop_spellchecking(),
+$(this.env.spellcheck.spell_container).css("visibility",a?"visible":"hidden"))};this.set_spellcheck_state=function(a){this.spellcheck_ready="ready"==a||"no_error_found"==a;this.enable_command("spellcheck",this.spellcheck_ready)};this.spellcheck_lang=function(){var a;if(window.tinyMCE&&(a=tinyMCE.get(this.env.composebody))&&a.plugins.spellchecker)return a.plugins.spellchecker.selectedLang;if(this.env.spellcheck)return GOOGIE_CUR_LANG};this.spellcheck_resume=function(a,b){if(a){var d=tinyMCE.get(this.env.composebody),
+e=d.plugins.spellchecker;e.active=1;e._markWords(b);d.nodeChanged()}else{var e=this.env.spellcheck;e.prepare(!1,!0);e.processData(b)}};this.set_draft_id=function(a){$("input[name='_draft_saveid']").val(a)};this.auto_save_start=function(){if(this.env.draft_autosave)this.save_timer=self.setTimeout(function(){i.command("savedraft")},1E3*this.env.draft_autosave);this.busy=!1};this.compose_field_hash=function(a){var b,d="",e=$("[name='_to']").val(),f=$("[name='_cc']").val(),h=$("[name='_bcc']").val(),
+g=$("[name='_subject']").val();e&&(d+=e+":");f&&(d+=f+":");h&&(d+=h+":");g&&(d+=g+":");d=window.tinyMCE&&(b=tinyMCE.get(this.env.composebody))?d+b.getContent():d+$("[name='_message']").val();if(this.env.attachments)for(var k in this.env.attachments)d+=k;if(a)this.cmp_hash=d;return d};this.change_identity=function(a,b){if(!a||!a.options)return!1;if(!b)b=this.env.show_sig;var d,e=-1,f=a.options[a.selectedIndex].value,h=$("[name='_message']"),g=h.val(),k="1"==$("input[name='_is_html']").val(),j=this.env.identity;
+d=this.env.sig_above&&("reply"==this.env.compose_mode||"forward"==this.env.compose_mode)?"---":"-- ";this.env.signatures&&this.env.signatures[f]?(this.enable_command("insert-sig",!0),this.env.compose_commands.push("insert-sig")):this.enable_command("insert-sig",!1);if(k){if(b&&this.env.signatures&&(e=tinyMCE.get(this.env.composebody),h=e.dom.get("_rc_sig"),h||(j=e.getBody(),g=e.getDoc(),h=g.createElement("div"),h.setAttribute("id","_rc_sig"),this.env.sig_above?(e.getWin().focus(),e=e.selection.getNode(),
+"BODY"==e.nodeName?(j.insertBefore(h,j.firstChild),j.insertBefore(g.createElement("br"),j.firstChild)):(j.insertBefore(h,e.nextSibling),j.insertBefore(g.createElement("br"),e.nextSibling))):(bw.ie&&j.appendChild(g.createElement("br")),j.appendChild(h))),this.env.signatures[f]))this.env.signatures[f].is_html?(j=this.env.signatures[f].text,this.env.signatures[f].plain_text.match(/^--[ -]\r?\n/m)||(j=d+"<br />"+j)):(j=this.env.signatures[f].text,j.match(/^--[ -]\r?\n/m)||(j=d+"\n"+j),j="<pre>"+j+"</pre>"),
+h.innerHTML=j}else b&&j&&this.env.signatures&&this.env.signatures[j]&&(j=this.env.signatures[j].is_html?this.env.signatures[j].plain_text:this.env.signatures[j].text,j=j.replace(/\r\n/g,"\n"),j.match(/^--[ -]\n/m)||(j=d+"\n"+j),e=this.env.sig_above?g.indexOf(j):g.lastIndexOf(j),0<=e&&(g=g.substring(0,e)+g.substring(e+j.length,g.length))),b&&this.env.signatures&&this.env.signatures[f]?(j=this.env.signatures[f].is_html?this.env.signatures[f].plain_text:this.env.signatures[f].text,j=j.replace(/\r\n/g,
+"\n"),j.match(/^--[ -]\n/m)||(j=d+"\n"+j),this.env.sig_above?0<=e?(g=g.substring(0,e)+j+g.substring(e,g.length),d=e-1):(pos=this.get_caret_pos(h.get(0)))?(g=g.substring(0,pos)+"\n"+j+"\n\n"+g.substring(pos,g.length),d=pos):(d=0,g="\n\n"+j+"\n\n"+g.replace(/^[\r\n]+/,"")):(g=g.replace(/[\r\n]+$/,""),d=!this.env.top_posting&&g.length?g.length+1:0,g+="\n\n"+j)):d=this.env.top_posting?0:g.length,h.val(g),this.set_caret_pos(h.get(0),d);this.env.identity=f;return!0};this.upload_file=function(a){if(!a)return!1;
+var b,d=0,e=$("input[type=file]",a).get(0),f=e.files?e.files.length:e.value?1:0;if(f){if(e.files&&this.env.max_filesize&&this.env.filesizeerror){for(b=0;b<f;b++)d+=e.files[b].size;if(d&&d>this.env.max_filesize){this.display_message(this.env.filesizeerror,"error");return}}b=this.async_upload_form(a,"upload",function(a){var b,d="";try{if(this.contentDocument)b=this.contentDocument;else if(this.contentWindow)b=this.contentWindow.document;d=b.childNodes[0].innerHTML}catch(e){}if(!d.match(/add2attachment/)&&
+(!bw.opera||rcmail.env.uploadframe&&rcmail.env.uploadframe==a.data.ts))d.match(/display_message/)||rcmail.display_message(rcmail.get_label("fileuploaderror"),"error"),rcmail.remove_from_attachment_list(a.data.ts);if(bw.opera)rcmail.env.uploadframe=a.data.ts});f="<span>"+this.get_label("uploading"+(1<f?"many":""))+"</span>";d=b.replace(/^rcmupload/,"");this.env.loadingicon&&(f='<img src="'+this.env.loadingicon+'" alt="" />'+f);this.env.cancelicon&&(f='<a title="'+this.get_label("cancel")+'" onclick="return rcmail.cancel_attachment_upload(\''+
+d+"', '"+b+'\');" href="#cancelupload"><img src="'+this.env.cancelicon+'" alt="" /></a>'+f);this.add2attachment_list(d,{name:"",html:f,complete:!1});this.env.upload_progress_time&&this.upload_progress_start("upload",d)}this.gui_objects.attachmentform=a;return!0};this.add2attachment_list=function(a,b,d){if(!this.gui_objects.attachmentlist)return!1;var e,f=$("<li>").attr("id",a).html(b.html);d&&(e=document.getElementById(d))?f.replaceAll(e):f.appendTo(this.gui_objects.attachmentlist);d&&this.env.attachments[d]&&
+delete this.env.attachments[d];this.env.attachments[a]=b;return!0};this.remove_from_attachment_list=function(a){delete this.env.attachments[a];$("#"+a).remove()};this.remove_attachment=function(a){a&&this.env.attachments[a]&&this.http_post("remove-attachment",{_id:this.env.compose_id,_file:a});return!0};this.cancel_attachment_upload=function(a,b){if(!a||!b)return!1;this.remove_from_attachment_list(a);$("iframe[name='"+b+"']").remove();return!1};this.upload_progress_start=function(a,b){window.setTimeout(function(){rcmail.http_request(a,
+{_progress:b})},1E3*this.env.upload_progress_time)};this.upload_progress_update=function(a){var b=$("#"+a.name+"> span");b.length&&a.text&&(b.text(a.text),a.done||this.upload_progress_start(a.action,a.name))};this.add_contact=function(a){a&&this.http_post("addcontact","_address="+a);return!0};this.qsearch=function(a){if(""!=a){var b=this.set_busy(!0,"searching");this.message_list?this.clear_message_list():this.contact_list&&this.list_contacts_clear();this.env.current_page=1;r=this.http_request("search",
+this.search_params(a)+(this.env.source?"&_source="+urlencode(this.env.source):"")+(this.env.group?"&_gid="+urlencode(this.env.group):""),b);this.env.qsearch={lock:b,request:r}}};this.search_params=function(a,b){var d,e=[],f=[],h=this.env.search_mods,g=this.env.mailbox;if(!b&&this.gui_objects.search_filter)b=this.gui_objects.search_filter.value;if(!a&&this.gui_objects.qsearchbox)a=this.gui_objects.qsearchbox.value;b&&e.push("_filter="+urlencode(b));if(a&&(e.push("_q="+urlencode(a)),h&&this.message_list&&
+(h=h[g]?h[g]:h["*"]),h)){for(d in h)f.push(d);e.push("_headers="+f.join(","))}g&&e.push("_mbox="+urlencode(g));return e.join("&")};this.reset_qsearch=function(){if(this.gui_objects.qsearchbox)this.gui_objects.qsearchbox.value="";this.env.qsearch&&this.abort_request(this.env.qsearch);this.env.qsearch=null;this.env.search_request=null;this.env.search_id=null};this.sent_successfully=function(a,b){this.display_message(b,a);window.setTimeout(function(){i.list_mailbox()},500)};this.ksearch_keydown=function(a,
+b,d){this.ksearch_timer&&clearTimeout(this.ksearch_timer);var e=rcube_event.get_keycode(a),f=rcube_event.get_modifier(a);switch(e){case 38:case 40:if(!this.ksearch_pane)break;e=38==e?1:0;b=document.getElementById("rcmksearchSelected");if(!b)b=this.ksearch_pane.__ul.firstChild;b&&this.ksearch_select(e?b.previousSibling:b.nextSibling);return rcube_event.cancel(a);case 9:if(f==SHIFT_KEY||!this.ksearch_visible()){this.ksearch_hide();return}case 13:if(!this.ksearch_visible())return!1;this.insert_recipient(this.ksearch_selected);
+this.ksearch_hide();return rcube_event.cancel(a);case 27:this.ksearch_hide();return;case 37:case 39:if(f!=SHIFT_KEY)return}this.ksearch_timer=window.setTimeout(function(){i.ksearch_get_results(d)},200);this.ksearch_input=b;return!0};this.ksearch_visible=function(){return null!==this.ksearch_selected&&void 0!==this.ksearch_selected&&this.ksearch_value};this.ksearch_select=function(a){var b=$("#rcmksearchSelected");b[0]&&a&&b.removeAttr("id").removeClass("selected");if(a)$(a).attr("id","rcmksearchSelected").addClass("selected"),
+this.ksearch_selected=a._rcm_id};this.insert_recipient=function(a){if(!(null===a||!this.env.contacts[a]||!this.ksearch_input)){var b=this.ksearch_input.value,d=this.get_caret_pos(this.ksearch_input),d=b.lastIndexOf(this.ksearch_value,d),e=!1,f="",h=b.substring(0,d),b=b.substring(d+this.ksearch_value.length,b.length);this.ksearch_destroy();"object"===typeof this.env.contacts[a]&&this.env.contacts[a].id?(f+=this.env.contacts[a].name+this.env.recipients_delimiter,this.group2expand=$.extend({},this.env.contacts[a]),
+this.group2expand.input=this.ksearch_input,this.http_request("mail/group-expand","_source="+urlencode(this.env.contacts[a].source)+"&_gid="+urlencode(this.env.contacts[a].id),!1)):"string"===typeof this.env.contacts[a]&&(f=this.env.contacts[a]+this.env.recipients_delimiter,e=!0);this.ksearch_input.value=h+f+b;d+=f.length;this.ksearch_input.setSelectionRange&&this.ksearch_input.setSelectionRange(d,d);e&&this.triggerEvent("autocomplete_insert",{field:this.ksearch_input,insert:f})}};this.replace_group_recipients=
+function(a,b){if(this.group2expand&&this.group2expand.id==a)this.group2expand.input.value=this.group2expand.input.value.replace(this.group2expand.name,b),this.triggerEvent("autocomplete_insert",{field:this.group2expand.input,insert:b}),this.group2expand=null};this.ksearch_get_results=function(a){var b=this.ksearch_input?this.ksearch_input.value:null;if(null!==b){this.ksearch_pane&&this.ksearch_pane.is(":visible")&&this.ksearch_pane.hide();var d=this.get_caret_pos(this.ksearch_input),e=b.lastIndexOf(this.env.recipients_separator,
+d-1),b=b.substring(e+1,d),e=this.env.autocomplete_min_length,d=this.ksearch_data,b=$.trim(b);if(b!=this.ksearch_value)if(this.ksearch_destroy(),b.length&&b.length<e){if(!this.ksearch_info)this.ksearch_info=this.display_message(this.get_label("autocompletechars").replace("$min",e))}else if(e=this.ksearch_value,this.ksearch_value=b,b.length&&(!e||!e.length||!(0==b.indexOf(e)&&(!d||!d.num)&&this.env.contacts&&!this.env.contacts.length))){var f,h,g,d=(new Date).getTime(),e=a&&a.threads?a.threads:1;f=
+a&&a.sources?a.sources:[];a=a&&a.action?a.action:"mail/autocomplete";this.ksearch_data={id:d,sources:f.slice(),action:a,locks:[],requests:[],num:f.length};for(f=0;f<e;f++){g=this.ksearch_data.sources.shift();if(1<e&&null===g)break;h=this.display_message(this.get_label("searching"),"loading");g=this.http_post(a,"_search="+urlencode(b)+"&_id="+d+(g?"&_source="+urlencode(g):""),h);this.ksearch_data.locks.push(h);this.ksearch_data.requests.push(g)}}}};this.ksearch_query_results=function(a,b,d){if(this.ksearch_value&&
+!(this.ksearch_input&&b!=this.ksearch_value)){var e,f,h,g,k,b=this.ksearch_value,j=this.ksearch_data,l=this.env.autocomplete_max?this.env.autocomplete_max:15;if(!this.ksearch_pane)h=$("<ul>"),this.ksearch_pane=$("<div>").attr("id","rcmKSearchpane").css({position:"absolute","z-index":3E4}).append(h).appendTo(document.body),this.ksearch_pane.__ul=h[0];h=this.ksearch_pane.__ul;d&&this.ksearch_pane.data("reqid")==d?l-=h.childNodes.length:(this.ksearch_pane.data("reqid",d),h.innerHTML="",this.env.contacts=
+[],e=$(this.ksearch_input).offset(),this.ksearch_pane.css({left:e.left+"px",top:e.top+this.ksearch_input.offsetHeight+"px",display:"none"}));if(a&&(f=a.length))for(e=0;e<f&&0<l;e++)k="object"===typeof a[e]?a[e].name:a[e],g=document.createElement("LI"),g.innerHTML=k.replace(RegExp("("+RegExp.escape(b)+")","ig"),"##$1%%").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/##([^%]+)%%/g,"<b>$1</b>"),g.onmouseover=function(){i.ksearch_select(this)},g.onmouseup=function(){i.ksearch_click(this)},g._rcm_id=
+this.env.contacts.length+e,h.appendChild(g),l-=1;if(h.childNodes.length&&(this.ksearch_pane.show(),!this.env.contacts.length))$("li:first",h).attr("id","rcmksearchSelected").addClass("selected"),this.ksearch_selected=0;if(f)this.env.contacts=this.env.contacts.concat(a);if(j.id==d)if(j.num--,0<l&&j.sources.length){if(f=j.sources.shift())a=this.display_message(this.get_label("searching"),"loading"),d=this.http_post(j.action,"_search="+urlencode(b)+"&_id="+d+"&_source="+urlencode(f),a),this.ksearch_data.locks.push(a),
+this.ksearch_data.requests.push(d)}else if(!l){if(!this.ksearch_msg)this.ksearch_msg=this.display_message(this.get_label("autocompletemore"));this.ksearch_abort()}}};this.ksearch_click=function(a){this.ksearch_input&&this.ksearch_input.focus();this.insert_recipient(a._rcm_id);this.ksearch_hide()};this.ksearch_blur=function(){this.ksearch_timer&&clearTimeout(this.ksearch_timer);this.ksearch_input=null;this.ksearch_hide()};this.ksearch_hide=function(){this.ksearch_selected=null;this.ksearch_value="";
+this.ksearch_pane&&this.ksearch_pane.hide();this.ksearch_destroy()};this.ksearch_destroy=function(){this.ksearch_abort();this.ksearch_info&&this.hide_message(this.ksearch_info);this.ksearch_msg&&this.hide_message(this.ksearch_msg);this.ksearch_msg=this.ksearch_info=this.ksearch_data=null};this.ksearch_abort=function(){var a,b,d=this.ksearch_data;if(d)for(a=0,b=d.locks.length;a<b;a++)this.abort_request({request:d.requests[a],lock:d.locks[a]})};this.contactlist_keypress=function(a){a.key_pressed==a.DELETE_KEY&&
+this.command("delete")};this.contactlist_select=function(a){this.preview_timer&&clearTimeout(this.preview_timer);var b,d,e,f=this,h=!1;e=this.env.source?this.env.address_sources[this.env.source]:null;(d=a.get_single_selection())?this.preview_timer=window.setTimeout(function(){f.load_contact(d,"show")},200):this.env.contentframe&&this.show_contentframe(!1);if(a.selection.length)if(e)h=!e.readonly;else for(b in a.selection)if((e=(""+a.selection[b]).replace(/^[^-]+-/,""))&&this.env.address_sources[e]&&
+!this.env.address_sources[e].readonly){h=!0;break}this.enable_command("compose",this.env.group||0<a.selection.length);this.enable_command("edit",d&&h);this.enable_command("delete",a.selection.length&&h);return!1};this.list_contacts=function(a,b,d){var e="",f=window;if(!a)a=this.env.source;if(d&&this.current_page==d&&a==this.env.source&&b==this.env.group)return!1;if(a!=this.env.source)d=this.env.current_page=1,this.reset_qsearch();else if(b!=this.env.group)d=this.env.current_page=1;this.select_folder(this.env.search_id?
+"S"+this.env.search_id:b?"G"+a+b:a);this.env.source=a;this.env.group=b;this.gui_objects.contactslist?this.list_contacts_remote(a,b,d):(this.env.contentframe&&window.frames&&window.frames[this.env.contentframe]&&(f=window.frames[this.env.contentframe],e="&_framed=1"),b&&(e+="&_gid="+b),d&&(e+="&_page="+d),this.env.search_request&&(e+="&_search="+this.env.search_request),this.set_busy(!0,"loading"),this.location_href(this.env.comm_path+(a?"&_source="+urlencode(a):"")+e,f))};this.list_contacts_remote=
+function(a,b,d){this.list_contacts_clear();var d=(a?"_source="+urlencode(a):"")+(d?(a?"&":"")+"_page="+d:""),e=this.set_busy(!0,"loading");this.env.source=a;(this.env.group=b)&&(d+="&_gid="+b);this.env.search_request&&(d+="&_search="+this.env.search_request);this.http_request("list",d,e)};this.list_contacts_clear=function(){this.contact_list.clear(!0);this.show_contentframe(!1);this.enable_command("delete",!1);this.enable_command("compose",this.env.group?!0:!1)};this.load_contact=function(a,b,d){var e=
+"",f=window;if(this.env.contentframe&&window.frames&&window.frames[this.env.contentframe])e="&_framed=1",f=window.frames[this.env.contentframe],this.show_contentframe(!0),a||(this.contact_list.clear_selection(),this.enable_command("delete","compose",!1));else if(d)return!1;if(b&&(a||"add"==b)&&!this.drag_active)this.env.group&&(e+="&_gid="+urlencode(this.env.group)),this.location_href(this.env.comm_path+"&_action="+b+"&_source="+urlencode(this.env.source)+"&_cid="+urlencode(a)+e,f,!0);return!0};this.group_member_change=
+function(a,b,d,e){var a="add"==a?"add":"del",f=this.display_message(this.get_label("add"==a?"addingmember":"removingmember"),"loading");this.http_post("group-"+a+"members","_cid="+urlencode(b)+"&_source="+urlencode(d)+"&_gid="+urlencode(e),f)};this.copy_contact=function(a,b){a||(a=this.contact_list.get_selection().join(","));if("group"==b.type&&b.source==this.env.source)this.group_member_change("add",a,b.source,b.id);else if("group"==b.type&&!this.env.address_sources[b.source].readonly){var d=this.display_message(this.get_label("copyingcontact"),
+"loading");this.http_post("copy","_cid="+urlencode(a)+"&_source="+urlencode(this.env.source)+"&_to="+urlencode(b.source)+"&_togid="+urlencode(b.id)+(this.env.group?"&_gid="+urlencode(this.env.group):""),d)}else b.id!=this.env.source&&a&&this.env.address_sources[b.id]&&!this.env.address_sources[b.id].readonly&&(d=this.display_message(this.get_label("copyingcontact"),"loading"),this.http_post("copy","_cid="+urlencode(a)+"&_source="+urlencode(this.env.source)+"&_to="+urlencode(b.id)+(this.env.group?
+"&_gid="+urlencode(this.env.group):""),d))};this.delete_contacts=function(){var a=this.contact_list.get_selection(),b=this.env.source&&this.env.address_sources[this.env.source].undelete;if(!(!a.length&&!this.env.cid||!b&&!confirm(this.get_label("deletecontactconfirm")))){var d,e=[],f="";if(this.env.cid)e.push(this.env.cid);else{for(d=0;d<a.length;d++)b=a[d],e.push(b),this.contact_list.remove_row(b,d==a.length-1);1==a.length&&this.show_contentframe(!1)}this.env.group&&(f+="&_gid="+urlencode(this.env.group));
+this.env.search_request&&(f+="&_search="+this.env.search_request);this.http_post("delete","_cid="+urlencode(e.join(","))+"&_source="+urlencode(this.env.source)+"&_from="+(this.env.action?this.env.action:"")+f,this.display_message(this.get_label("contactdeleting"),"loading"));return!0}};this.update_contact_row=function(a,b,d,e){var f,h=this.contact_list,a=this.html_identifier(a);h.rows[a]||(a=a+"-"+e,d&&(d=d+"-"+e));if(h.rows[a]&&(f=h.rows[a].obj)){for(e=0;e<b.length;e++)f.cells[e]&&$(f.cells[e]).html(b[e]);
+if(d)d=this.html_identifier(d),f.id="rcmrow"+d,h.remove_row(a),h.init_row(f),h.selection[0]=d,f.style.display=""}};this.add_contact_row=function(a,b){if(!this.gui_objects.contactslist)return!1;var d,e=this.contact_list,f=document.createElement("tr");f.id="rcmrow"+this.html_identifier(a);f.className="contact";e.in_selection(a)&&(f.className+=" selected");for(d in b)col=document.createElement("td"),col.className=(""+d).toLowerCase(),col.innerHTML=b[d],f.appendChild(col);e.insert_row(f);this.enable_command("export",
+0<e.rowcount)};this.init_contact_form=function(){var a=this,b;this.set_photo_actions($("#ff_photo").val());for(b in this.env.coltypes)this.init_edit_field(b,null);$(".contactfieldgroup .row a.deletebutton").click(function(){a.delete_edit_field(this);return!1});$("select.addfieldmenu").change(function(){a.insert_edit_field($(this).val(),$(this).attr("rel"),this);this.selectedIndex=0});$.datepicker&&this.env.date_format&&($.datepicker.setDefaults({dateFormat:this.env.date_format,changeMonth:!0,changeYear:!0,
+yearRange:"-100:+10",showOtherMonths:!0,selectOtherMonths:!0,monthNamesShort:this.env.month_names,onSelect:function(a){$(this).focus().val(a)}}),$("input.datepicker").datepicker());$("input[type='text']:visible").first().focus()};this.group_create=function(){this.add_input_row("contactgroup")};this.group_rename=function(){if(this.env.group&&this.gui_objects.folderlist){if(!this.name_input){this.enable_command("list","listgroup",!1);this.name_input=$("<input>").attr("type","text").val(this.env.contactgroups["G"+
+this.env.source+this.env.group].name);this.name_input.bind("keydown",function(a){return rcmail.add_input_keydown(a)});this.env.group_renaming=!0;var a,b=this.get_folder_li(this.env.source+this.env.group,"rcmliG");b&&(a=b.firstChild)&&$(a).hide().before(this.name_input)}this.name_input.select().focus()}};this.group_delete=function(){if(this.env.group&&confirm(this.get_label("deletegroupconfirm"))){var a=this.set_busy(!0,"groupdeleting");this.http_post("group-delete","_source="+urlencode(this.env.source)+
+"&_gid="+urlencode(this.env.group),a)}};this.remove_group_item=function(a){var b,d="G"+a.source+a.id;if(b=this.get_folder_li(d))this.triggerEvent("group_delete",{source:a.source,id:a.id,li:b}),b.parentNode.removeChild(b),delete this.env.contactfolders[d],delete this.env.contactgroups[d];this.list_contacts(a.source,0)};this.add_input_row=function(a){if(this.gui_objects.folderlist){if(!this.name_input)this.name_input=$("<input>").attr("type","text").data("tt",a),this.name_input.bind("keydown",function(a){return rcmail.add_input_keydown(a)}),
+this.name_input_li=$("<li>").addClass(a).append(this.name_input),this.name_input_li.insertAfter("contactsearch"==a?$("li:last",this.gui_objects.folderlist):this.get_folder_li(this.env.source));this.name_input.select().focus()}};this.add_input_keydown=function(a){var b=rcube_event.get_keycode(a),d=$(a.target),a=d.data("tt");if(13==b){if(b=d.val())d=this.set_busy(!0,"loading"),"contactsearch"==a?this.http_post("search-create","_search="+urlencode(this.env.search_request)+"&_name="+urlencode(b),d):this.env.group_renaming?
+this.http_post("group-rename","_source="+urlencode(this.env.source)+"&_gid="+urlencode(this.env.group)+"&_name="+urlencode(b),d):this.http_post("group-create","_source="+urlencode(this.env.source)+"&_name="+urlencode(b),d);return!1}27==b&&this.reset_add_input();return!0};this.reset_add_input=function(){if(this.name_input){if(this.env.group_renaming)this.name_input.parent().children().last().show(),this.env.group_renaming=!1;this.name_input.remove();this.name_input_li&&this.name_input_li.remove();
+this.name_input=this.name_input_li=null}this.enable_command("list","listgroup",!0)};this.insert_contact_group=function(a){this.reset_add_input();a.type="group";var b="G"+a.source+a.id,d=$("<a>").attr("href","#").attr("rel",a.source+":"+a.id).click(function(){return rcmail.command("listgroup",a,this)}).html(a.name),d=$("<li>").attr({id:"rcmli"+this.html_identifier(b),"class":"contactgroup"}).append(d);this.env.contactfolders[b]=this.env.contactgroups[b]=a;this.add_contact_group_row(a,d);this.triggerEvent("group_insert",
+{id:a.id,source:a.source,name:a.name,li:d[0]})};this.update_contact_group=function(a){this.reset_add_input();var b="G"+a.source+a.id,d=this.get_folder_li(b),e;if(d&&a.newid){e="G"+a.source+a.newid;var f=$.extend({},a);d.id="rcmli"+this.html_identifier(e);this.env.contactfolders[e]=this.env.contactfolders[b];this.env.contactfolders[e].id=a.newid;this.env.group=a.newid;delete this.env.contactfolders[b];delete this.env.contactgroups[b];f.id=a.newid;f.type="group";e=$("<a>").attr("href","#").attr("rel",
+a.source+":"+a.newid).click(function(){return rcmail.command("listgroup",f,this)}).html(a.name);$(d).children().replaceWith(e)}else if(d&&(e=d.firstChild)&&"a"==e.tagName.toLowerCase())e.innerHTML=a.name;this.env.contactfolders[b].name=this.env.contactgroups[b].name=a.name;this.add_contact_group_row(a,$(d),!0);this.triggerEvent("group_update",{id:a.id,source:a.source,name:a.name,li:d[0],newid:a.newid})};this.add_contact_group_row=function(a,b,d){var e=a.name.toUpperCase(),f=this.get_folder_li(a.source),
+a="rcmliG"+this.html_identifier(a.source);d?(d=b.clone(!0),b.remove()):d=b;$('li[id^="'+a+'"]',this.gui_objects.folderlist).each(function(a,b){if(e>=$(this).text().toUpperCase())f=b;else return!1});d.insertAfter(f)};this.update_group_commands=function(){var a=""!=this.env.source?this.env.address_sources[this.env.source]:null;this.enable_command("group-create",a&&a.groups&&!a.readonly);this.enable_command("group-rename","group-delete",a&&a.groups&&this.env.group&&!a.readonly)};this.init_edit_field=
+function(a,b){b||(b=$(".ff_"+a));b.focus(function(){i.focus_textfield(this)}).blur(function(){i.blur_textfield(this)}).each(function(){this._placeholder=this.title=i.env.coltypes[a].label;i.blur_textfield(this)})};this.insert_edit_field=function(a,b,d){var e=$("#ff_"+a);if(e.length)e.show().focus(),$(d).children('option[value="'+a+'"]').prop("disabled",!0);else if($(".ff_"+a),e=$("#contactsection"+b+" .contactcontroller"+a),e.length||(e=$("<fieldset>").addClass("contactfieldgroup contactcontroller"+
+a).insertAfter($("#contactsection"+b+" .contactfieldgroup").last())),e.length&&"FIELDSET"==e.get(0).nodeName){var f,b=this.env.coltypes[a],h=$("<div>").addClass("row"),g=$("<div>").addClass("contactfieldcontent data"),k=$("<div>").addClass("contactfieldlabel label");b.subtypes_select?k.html(b.subtypes_select):k.html(b.label);var j=1!=b.limit?"[]":"";if("text"==b.type||"date"==b.type)f=$("<input>").addClass("ff_"+a).attr({type:"text",name:"_"+a+j,size:b.size}).appendTo(g),this.init_edit_field(a,f),
+"date"==b.type&&$.datepicker&&f.datepicker();else if("composite"==b.type){var l,n,m=[],o=[];if(f=this.env[a+"_template"])for(l=0;l<f.length;l++)m.push(f[l][1]),o.push(f[l][2]);else for(l in b.childs)m.push(l);for(var p=0;p<m.length;p++)l=m[p],f=b.childs[l],f=$("<input>").addClass("ff_"+l).attr({type:"text",name:"_"+l+j,size:f.size}).appendTo(g),g.append(o[p]||" "),this.init_edit_field(l,f),n||(n=f);f=n}else if("select"==b.type){f=$("<select>").addClass("ff_"+a).attr("name","_"+a+j).appendTo(g);var q=
+f.attr("options");q[q.length]=new Option("---","");b.options&&$.each(b.options,function(a,b){q[q.length]=new Option(b,a)})}if(f){$('<a href="#del"></a>').addClass("contactfieldbutton deletebutton").attr({title:this.get_label("delete"),rel:a}).html(this.env.delbutton).click(function(){i.delete_edit_field(this);return!1}).appendTo(g);h.append(k).append(g).appendTo(e.show());f.first().focus();if(!b.count)b.count=0;++b.count==b.limit&&b.limit&&$(d).children('option[value="'+a+'"]').prop("disabled",!0)}}};
+this.delete_edit_field=function(a){var b=$(a).attr("rel"),d=this.env.coltypes[b],e=$(a).parents("fieldset.contactfieldgroup"),f=e.parent().find("select.addfieldmenu");0>=--d.count&&d.visible?$(a).parent().children("input").val("").blur():($(a).parents("div.row").remove(),e.children("div.row").length||e.hide());f.length&&(a=f.children('option[value="'+b+'"]'),a.length?a.prop("disabled",!1):$("<option>").attr("value",b).html(d.label).appendTo(f),f.show())};this.upload_contact_photo=function(a){if(a&&
+a.elements._photo.value)this.async_upload_form(a,"upload-photo",function(){rcmail.set_busy(!1,null,rcmail.photo_upload_id)}),this.photo_upload_id=this.set_busy(!0,"uploading")};this.replace_contact_photo=function(a){var b="-del-"==a?this.env.photo_placeholder:this.env.comm_path+"&_action=photo&_source="+this.env.source+"&_cid="+this.env.cid+"&_photo="+a;this.set_photo_actions(a);$(this.gui_objects.contactphoto).children("img").attr("src",b)};this.photo_upload_end=function(){this.set_busy(!1,null,
+this.photo_upload_id);delete this.photo_upload_id};this.set_photo_actions=function(a){var b,d=this.buttons["upload-photo"];for(b=0;d&&b<d.length;b++)$("#"+d[b].id).html(this.get_label("-del-"==a?"addphoto":"replacephoto"));$("#ff_photo").val(a);this.enable_command("upload-photo",this.env.coltypes.photo?!0:!1);this.enable_command("delete-photo",this.env.coltypes.photo&&"-del-"!=a)};this.advanced_search=function(){var a="&_form=1",b=window;this.env.contentframe&&window.frames&&window.frames[this.env.contentframe]&&
+(a+="&_framed=1",b=window.frames[this.env.contentframe],this.contact_list.clear_selection());this.location_href(this.env.comm_path+"&_action=search"+a,b,!0);return!0};this.unselect_directory=function(){this.select_folder("");this.enable_command("search-delete",!1)};this.insert_saved_search=function(a,b){this.reset_add_input();var d="S"+b,e=$("<a>").attr("href","#").attr("rel",b).click(function(){return rcmail.command("listsearch",b,this)}).html(a),d=$("<li>").attr({id:"rcmli"+this.html_identifier(d),
+"class":"contactsearch"}).append(e),e={name:a,id:b,li:d[0]};this.add_saved_search_row(e,d);this.select_folder("S"+b);this.enable_command("search-delete",!0);this.env.search_id=b;this.triggerEvent("abook_search_insert",e)};this.add_saved_search_row=function(a,b,d){var e,f=a.name.toUpperCase();d?(a=b.clone(!0),b.remove()):a=b;$('li[class~="contactsearch"]',this.gui_objects.folderlist).each(function(a,b){if(!e)e=this.previousSibling;if(f>=$(this).text().toUpperCase())e=b;else return!1});e?a.insertAfter(e):
+a.appendTo(this.gui_objects.folderlist)};this.search_create=function(){this.add_input_row("contactsearch")};this.search_delete=function(){if(this.env.search_request){var a=this.set_busy(!0,"savedsearchdeleting");this.http_post("search-delete","_sid="+urlencode(this.env.search_id),a)}};this.remove_search_item=function(a){var b;if(b=this.get_folder_li("S"+a))this.triggerEvent("search_delete",{id:a,li:b}),b.parentNode.removeChild(b);this.env.search_id=null;this.env.search_request=null;this.list_contacts_clear();
+this.reset_qsearch();this.enable_command("search-delete","search-create",!1)};this.listsearch=function(a){var b=this.set_busy(!0,"searching");this.contact_list&&this.list_contacts_clear();this.reset_qsearch();this.select_folder("S"+a);this.env.current_page=1;this.http_request("search","_sid="+urlencode(a),b)};this.section_select=function(a){var a=a.get_single_selection(),b="",d=window;a&&(this.env.contentframe&&window.frames&&window.frames[this.env.contentframe]&&(b="&_framed=1",d=window.frames[this.env.contentframe]),
+this.location_href(this.env.comm_path+"&_action=edit-prefs&_section="+a+b,d,!0));return!0};this.identity_select=function(a){var b;(b=a.get_single_selection())&&this.load_identity(b,"edit-identity")};this.load_identity=function(a,b){if("edit-identity"==b&&(!a||a==this.env.iid))return!1;var d="",e=window;if(this.env.contentframe&&window.frames&&window.frames[this.env.contentframe])d="&_framed=1",e=window.frames[this.env.contentframe],document.getElementById(this.env.contentframe).style.visibility="inherit";
+if(b&&(a||"add-identity"==b))this.set_busy(!0),this.location_href(this.env.comm_path+"&_action="+b+"&_iid="+a+d,e);return!0};this.delete_identity=function(a){var b=this.identity_list.get_selection();if(b.length||this.env.iid)return a||(a=this.env.iid?this.env.iid:b[0]),this.goto_url("delete-identity","_iid="+a+"&_token="+this.env.request_token,!0),!0};this.init_subscription_list=function(){var a=this;this.subscription_list=new rcube_list_widget(this.gui_objects.subscriptionlist,{multiselect:!1,draggable:!0,
+keyboard:!1,toggleselect:!0});this.subscription_list.addEventListener("select",function(b){a.subscription_select(b)});this.subscription_list.addEventListener("dragstart",function(){a.drag_active=!0});this.subscription_list.addEventListener("dragend",function(b){a.subscription_move_folder(b)});this.subscription_list.row_init=function(b){b.obj.onmouseover=function(){a.focus_subscription(b.id)};b.obj.onmouseout=function(){a.unfocus_subscription(b.id)}};this.subscription_list.init();$("#mailboxroot").mouseover(function(){a.focus_subscription(this.id)}).mouseout(function(){a.unfocus_subscription(this.id)})};
+this.focus_subscription=function(a){var b,d,e=RegExp.escape(this.env.delimiter),e=RegExp("["+e+"]?[^"+e+"]+$");if(this.drag_active&&this.env.mailbox&&(b=document.getElementById(a)))if(this.env.subscriptionrows[a]&&null!==(d=this.env.subscriptionrows[a][0])&&this.check_droptarget(d)&&!this.env.subscriptionrows[this.get_folder_row_id(this.env.mailbox)][2]&&d!=this.env.mailbox.replace(e,"")&&!d.match(RegExp("^"+RegExp.escape(this.env.mailbox+this.env.delimiter))))this.env.dstfolder=d,$(b).addClass("droptarget")};
+this.unfocus_subscription=function(a){var b=$("#"+a);this.env.dstfolder=null;this.env.subscriptionrows[a]&&b[0]?b.removeClass("droptarget"):$(this.subscription_list.frame).removeClass("droptarget")};this.subscription_select=function(a){var b,d;a&&(b=a.get_single_selection())&&(d=this.env.subscriptionrows["rcmrow"+b])?(this.env.mailbox=d[0],this.show_folder(d[0]),this.enable_command("delete-folder",!d[2])):(this.env.mailbox=null,this.show_contentframe(!1),this.enable_command("delete-folder","purge",
+!1))};this.subscription_move_folder=function(){var a=RegExp.escape(this.env.delimiter);this.env.mailbox&&null!==this.env.dstfolder&&this.env.dstfolder!=this.env.mailbox&&this.env.dstfolder!=this.env.mailbox.replace(RegExp("["+a+"]?[^"+a+"]+$"),"")&&(a=this.env.mailbox.replace(RegExp("[^"+a+"]*["+a+"]","g"),""),a=""===this.env.dstfolder?a:this.env.dstfolder+this.env.delimiter+a,a!=this.env.mailbox&&(this.http_post("rename-folder","_folder_oldname="+urlencode(this.env.mailbox)+"&_folder_newname="+urlencode(a),
+this.set_busy(!0,"foldermoving")),this.subscription_list.draglayer.hide()));this.drag_active=!1;this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder))};this.create_folder=function(){this.show_folder("",this.env.mailbox)};this.delete_folder=function(a){if((a=this.env.subscriptionrows[this.get_folder_row_id(a?a:this.env.mailbox)][0])&&confirm(this.get_label("deletefolderconfirm"))){var b=this.set_busy(!0,"folderdeleting");this.http_post("delete-folder","_mbox="+urlencode(a),b)}};this.add_folder_row=
+function(a,b,d,e,f,h){if(!this.gui_objects.subscriptionlist)return!1;var g,k,j,i,n,m=[],o=[],p=this.gui_objects.subscriptionlist.tBodies[0];g=$("tr",p).get(1);var q="rcmrow"+(new Date).getTime();if(!g)return this.goto_url("folders"),!1;g=$(g).clone(!0);g.attr("id",q);g.attr("class",h);g.find("td:first").html(b);$('input[name="_subscribed[]"]',g).val(a).prop({checked:e?!0:!1,disabled:d?!0:!1});this.env.subscriptionrows[q]=[a,b,0];i=[];$.each(this.env.subscriptionrows,function(a,b){i.push(b)});i.sort(function(a,
+b){return a[0]<b[0]?-1:a[0]>b[0]?1:0});for(k in i)i[k][2]?(o.push(i[k][0]),j=i[k][0]+this.env.delimiter):j&&0==i[k][0].indexOf(j)?o.push(i[k][0]):(m.push(i[k][0]),j=null);for(k=0;k<o.length;k++)0==a.indexOf(o[k]+this.env.delimiter)&&(n=this.get_folder_row_id(o[k]));for(k=0;!n&&k<m.length;k++)k&&m[k]==a&&(n=this.get_folder_row_id(m[k-1]));n?$("#"+n).after(g):g.appendTo(p);this.subscription_list.clear_selection();f||this.init_subscription_list();g=g.get(0);g.scrollIntoView&&g.scrollIntoView();return g};
+this.replace_folder_row=function(a,b,d,e,f){if(!this.gui_objects.subscriptionlist)return!1;var h,g,i,j,l=this.get_folder_row_id(a),n=RegExp("^"+RegExp.escape(a));h=$('input[name="_subscribed[]"]',$("#"+l)).prop("checked");var m=this.get_subfolders(a);this._remove_folder_row(l);e=$(this.add_folder_row(b,d,e,h,!0,f));if(d=m.length)j=a.split(this.env.delimiter).length-b.split(this.env.delimiter).length;for(a=0;a<d;a++)if(l=m[a],h=this.env.subscriptionrows[l][0],f=this.env.subscriptionrows[l][1],g=$("#"+
+l),i=g.clone(!0),g.remove(),e.after(i),e=i,h=h.replace(n,b),$('input[name="_subscribed[]"]',e).val(h),this.env.subscriptionrows[l][0]=h,0!=j){if(0<j)for(h=j;0<h;h--)f=f.replace(/^&nbsp;&nbsp;&nbsp;&nbsp;/,"");else for(h=j;0>h;h++)f="&nbsp;&nbsp;&nbsp;&nbsp;"+f;e.find("td:first").html(f);this.env.subscriptionrows[l][1]=f}this.init_subscription_list()};this.remove_folder_row=function(a,b){var d,e,f=[];d=this.get_folder_row_id(a);b&&(f=this.get_subfolders(a));this._remove_folder_row(d);for(d=0,e=f.length;d<
+e;d++)this._remove_folder_row(f[d])};this._remove_folder_row=function(a){this.subscription_list.remove_row(a.replace(/^rcmrow/,""));$("#"+a).remove();delete this.env.subscriptionrows[a]};this.get_subfolders=function(a){for(var b=[],d=RegExp("^"+RegExp.escape(a)+RegExp.escape(this.env.delimiter)),e=$("#"+this.get_folder_row_id(a)).get(0);e=e.nextSibling;)if(e.id)if(a=this.env.subscriptionrows[e.id][0],d.test(a))b.push(e.id);else break;return b};this.subscribe=function(a){if(a){var b=this.display_message(this.get_label("foldersubscribing"),
+"loading");this.http_post("subscribe","_mbox="+urlencode(a),b)}};this.unsubscribe=function(a){if(a){var b=this.display_message(this.get_label("folderunsubscribing"),"loading");this.http_post("unsubscribe","_mbox="+urlencode(a),b)}};this.get_folder_row_id=function(a){var b,d=this.env.subscriptionrows;for(b in d)if(d[b]&&d[b][0]==a)break;return b};this.show_folder=function(a,b,d){var e=window,a="&_action=edit-folder&_mbox="+urlencode(a);b&&(a+="&_path="+urlencode(b));this.env.contentframe&&window.frames&&
+window.frames[this.env.contentframe]&&(e=window.frames[this.env.contentframe],a+="&_framed=1");0<=(""+e.location.href).indexOf(a)&&!d?this.show_contentframe(!0):this.location_href(this.env.comm_path+a,e,!0)};this.disable_subscription=function(a){(a=this.get_folder_row_id(a))&&$('input[name="_subscribed[]"]',$("#"+a)).prop("disabled",!0)};this.folder_size=function(a){var b=this.set_busy(!0,"loading");this.http_post("folder-size","_mbox="+urlencode(a),b)};this.folder_size_update=function(a){$("#folder-size").replaceWith(a)};
+var s=function(a,b){var d=document.getElementById(b.id);if(d){var e=!1;if("image"==b.type)d=d.parentNode,e=!0;d._command=a;d._id=b.id;if(b.sel&&(d.onmousedown=function(){return rcmail.button_sel(this._command,this._id)},d.onmouseup=function(){return rcmail.button_out(this._command,this._id)},e))(new Image).src=b.sel;if(b.over&&(d.onmouseover=function(){return rcmail.button_over(this._command,this._id)},d.onmouseout=function(){return rcmail.button_out(this._command,this._id)},e))(new Image).src=b.over}};
+this.set_page_buttons=function(){this.enable_command("nextpage","lastpage",this.env.pagecount>this.env.current_page);this.enable_command("previouspage","firstpage",1<this.env.current_page)};this.init_buttons=function(){for(var a in this.buttons)if("string"===typeof a)for(var b=0;b<this.buttons[a].length;b++)s(a,this.buttons[a][b]);this.set_button(this.task,"sel")};this.set_button=function(a,b){var d,e,f,h=this.buttons[a],g=h?h.length:0;for(d=0;d<g;d++){e=h[d];if((f=document.getElementById(e.id))&&
+"image"==e.type&&!e.status){if(e.pas=f._original_src?f._original_src:f.src,f.runtimeStyle&&f.runtimeStyle.filter&&f.runtimeStyle.filter.match(/src=['"]([^'"]+)['"]/))e.pas=RegExp.$1}else if(f&&!e.status)e.pas=""+f.className;if(f&&"image"==e.type&&e[b])e.status=b,f.src=e[b];else if(f&&void 0!==e[b])e.status=b,f.className=e[b];if(f&&"input"==e.type)e.status=b,f.disabled=!b}};this.set_alttext=function(a,b){var d,e,f,h,g=this.buttons[a],i=g?g.length:0;for(d=0;d<i;d++)e=g[d],f=document.getElementById(e.id),
+"image"==e.type&&f?(f.setAttribute("alt",this.get_label(b)),(h=f.parentNode)&&"a"==h.tagName.toLowerCase()&&h.setAttribute("title",this.get_label(b))):f&&f.setAttribute("title",this.get_label(b))};this.button_over=function(a,b){var d,e,f,h=this.buttons[a],g=h?h.length:0;for(d=0;d<g;d++)if(e=h[d],e.id==b&&"act"==e.status&&(f=document.getElementById(e.id))&&e.over)"image"==e.type?f.src=e.over:f.className=e.over};this.button_sel=function(a,b){var d,e,f,h=this.buttons[a],g=h?h.length:0;for(d=0;d<g;d++)if(e=
+h[d],e.id==b&&"act"==e.status){if((f=document.getElementById(e.id))&&e.sel)"image"==e.type?f.src=e.sel:f.className=e.sel;this.buttons_sel[b]=a}};this.button_out=function(a,b){var d,e,f,h=this.buttons[a],g=h?h.length:0;for(d=0;d<g;d++)if(e=h[d],e.id==b&&"act"==e.status&&(f=document.getElementById(e.id))&&e.act)"image"==e.type?f.src=e.act:f.className=e.act};this.focus_textfield=function(a){a._hasfocus=!0;var b=$(a);(b.hasClass("placeholder")||b.val()==a._placeholder)&&b.val("").removeClass("placeholder").attr("spellcheck",
+!0)};this.blur_textfield=function(a){a._hasfocus=!1;var b=$(a);a._placeholder&&(!b.val()||b.val()==a._placeholder)&&b.addClass("placeholder").attr("spellcheck",!1).val(a._placeholder)};this.set_pagetitle=function(a){if(a&&document.title)document.title=a};this.display_message=function(a,b,d){if(this.is_framed())return parent.rcmail.display_message(a,b,d);if(!this.gui_objects.message){if("loading"!=b)this.pending_message=[a,b,d];return!1}var b=b?b:"notice",e=this,f=this.html_identifier(a),h=b+(new Date).getTime();
+d||(d=this.message_time*("error"==b||"warning"==b?2:1));"loading"==b&&(f="loading",d=1E3*this.env.request_timeout,a||(a=this.get_label("loading")));if(this.messages[f])return this.messages[f].obj&&this.messages[f].obj.html(a),"loading"==b&&this.messages[f].labels.push({id:h,msg:a}),this.messages[f].elements.push(h),window.setTimeout(function(){e.hide_message(h,"loading"==b)},d),h;var g=$("<div>").addClass(b).html(a).data("key",f);$(this.gui_objects.message).append(g).show();this.messages[f]={obj:g,
+elements:[h]};"loading"==b?this.messages[f].labels=[{id:h,msg:a}]:g.click(function(){return e.hide_message(g)});0<d&&window.setTimeout(function(){e.hide_message(h,"loading"==b)},d);return h};this.hide_message=function(a,b){if(this.is_framed())return parent.rcmail.hide_message(a,b);var d,e,f,h,g=this.messages;if("object"===typeof a)$(a)[b?"fadeOut":"hide"](),h=$(a).data("key"),this.messages[h]&&delete this.messages[h];else for(d in g)for(e in g[d].elements)if(g[d]&&g[d].elements[e]==a)if(g[d].elements.splice(e,
+1),g[d].elements.length){if("loading"==d)for(f in g[d].labels)g[d].labels[f].id==a?delete g[d].labels[f]:h=g[d].labels[f].msg,g[d].obj.html(h)}else g[d].obj[b?"fadeOut":"hide"](),delete g[d]};this.select_folder=function(a,b,d){if(this.gui_objects.folderlist){var e,f;(e=$("li.selected",this.gui_objects.folderlist))&&e.removeClass("selected").addClass("unfocused");(f=this.get_folder_li(a,b,d))&&$(f).removeClass("unfocused").addClass("selected");this.triggerEvent("selectfolder",{folder:a,prefix:b})}};
+this.get_folder_li=function(a,b,d){b||(b="rcmli");return this.gui_objects.folderlist?(a=this.html_identifier(a,d),document.getElementById(b+a)):null};this.set_message_coltypes=function(a,b){var d=this.message_list,e=d?d.list.tHead:null,f,h,g,i;this.env.coltypes=a;if(e){if(b){h=document.createElement("thead");g=document.createElement("tr");for(c=0,i=b.length;c<i;c++){f=document.createElement("td");f.innerHTML=b[c].html;if(b[c].id)f.id=b[c].id;if(b[c].className)f.className=b[c].className;g.appendChild(f)}h.appendChild(g);
+e.parentNode.replaceChild(h,e);e=h}for(g=0,i=this.env.coltypes.length;g<i;g++)if(h=this.env.coltypes[g],(f=e.rows[0].cells[g])&&("from"==h||"to"==h)){f.id="rcm"+h;if(f.firstChild&&"a"==f.firstChild.tagName.toLowerCase())f=f.firstChild,f.onclick=function(){return rcmail.command("sort",this.__col,this)},f.__col=h;f.innerHTML=this.get_label(h)}}this.env.subject_col=null;this.env.flagged_col=null;this.env.status_col=null;if(0<=(g=$.inArray("subject",this.env.coltypes)))if(this.env.subject_col=g,d)d.subject_col=
+g;if(0<=(g=$.inArray("flag",this.env.coltypes)))this.env.flagged_col=g;if(0<=(g=$.inArray("status",this.env.coltypes)))this.env.status_col=g;d&&d.init_header()};this.set_rowcount=function(a,b){if(b&&b!=this.env.mailbox)return!1;$(this.gui_objects.countdisplay).html(a);this.set_page_buttons()};this.set_mailboxname=function(a){if(this.gui_objects.mailboxname&&a)this.gui_objects.mailboxname.innerHTML=a};this.set_quota=function(a){a&&this.gui_objects.quotadisplay&&("object"===typeof a&&"image"==a.type?
+this.percent_indicator(this.gui_objects.quotadisplay,a):$(this.gui_objects.quotadisplay).html(a))};this.set_unread_count=function(a,b,d){if(!this.gui_objects.mailboxlist)return!1;this.env.unread_counts[a]=b;this.set_unread_count_display(a,d)};this.set_unread_count_display=function(a,b){var d,e,f,h,g;if(f=this.get_folder_li(a,"",!0)){h=this.env.unread_counts[a]?this.env.unread_counts[a]:0;e=$(f).children("a").eq(0);d=e.children("span.unreadcount");!d.length&&h&&(d=$("<span>").addClass("unreadcount").appendTo(e));
+e=0;if((g=f.getElementsByTagName("div")[0])&&g.className.match(/collapsed/))for(var i in this.env.unread_counts)0==i.indexOf(a+this.env.delimiter)&&(e+=this.env.unread_counts[i]);h&&d.length?d.html(" ("+h+")"):d.length&&d.remove();d=RegExp(RegExp.escape(this.env.delimiter)+"[^"+RegExp.escape(this.env.delimiter)+"]+$");a.match(d)&&this.set_unread_count_display(a.replace(d,""),!1);0<h+e?$(f).addClass("unread"):$(f).removeClass("unread")}d=/^\([0-9]+\)\s+/i;b&&document.title&&(f="",f=""+document.title,
+f=h&&f.match(d)?f.replace(d,"("+h+") "):h?"("+h+") "+f:f.replace(d,""),this.set_pagetitle(f))};this.toggle_prefer_html=function(a){$("#rcmfd_show_images").prop("disabled",!a.checked).val(0)};this.toggle_preview_pane=function(a){$("#rcmfd_preview_pane_mark_read").prop("disabled",!a.checked)};this.set_headers=function(a){this.gui_objects.all_headers_row&&this.gui_objects.all_headers_box&&a&&$(this.gui_objects.all_headers_box).html(a).show()};this.load_headers=function(a){if(this.gui_objects.all_headers_row&&
+this.gui_objects.all_headers_box&&this.env.uid)$(a).removeClass("show-headers").addClass("hide-headers"),$(this.gui_objects.all_headers_row).show(),a.onclick=function(){rcmail.hide_headers(a)},this.gui_objects.all_headers_box.innerHTML||this.http_post("headers","_uid="+this.env.uid,this.display_message(this.get_label("loading"),"loading"))};this.hide_headers=function(a){if(this.gui_objects.all_headers_row&&this.gui_objects.all_headers_box)$(a).removeClass("hide-headers").addClass("show-headers"),
+$(this.gui_objects.all_headers_row).hide(),a.onclick=function(){rcmail.load_headers(a)}};this.percent_indicator=function(a,b){if(!b||!a)return!1;var d=b.width?b.width:this.env.indicator_width?this.env.indicator_width:100,e=b.height?b.height:this.env.indicator_height?this.env.indicator_height:14,f=b.percent?Math.abs(parseInt(b.percent)):0,h=parseInt(f/100*d),g=$(a).position();g.top=Math.max(0,g.top);g.left=Math.max(0,g.left);this.env.indicator_width=d;this.env.indicator_height=e;h>d&&(h=d,f=100);if(b.title)b.title=
+this.get_label("quota")+": "+b.title;var i=$("<div>");i.css({position:"absolute",top:g.top,left:g.left,width:d+"px",height:e+"px",zIndex:100,lineHeight:e+"px"}).attr("title",b.title).addClass("quota_text").html(f+"%");var j=$("<div>");j.css({position:"absolute",top:g.top+1,left:g.left+1,width:h+"px",height:e+"px",zIndex:99});h=$("<div>");h.css({position:"absolute",top:g.top+1,left:g.left+1,width:d+"px",height:e+"px",zIndex:98}).addClass("quota_bg");80<=f?(i.addClass(" quota_text_high"),j.addClass("quota_high")):
+55<=f?(i.addClass(" quota_text_mid"),j.addClass("quota_mid")):(i.addClass(" quota_text_low"),j.addClass("quota_low"));$(a).html("").append(j).append(h).append(i);$("#quotaimg").attr("title",b.title)};this.html2plain=function(a,b){var d=this,e=this.set_busy(!0,"converting");this.log("HTTP POST: ?_task=utils&_action=html2text");$.ajax({type:"POST",url:"?_task=utils&_action=html2text",data:a,contentType:"application/octet-stream",error:function(a,b,g){d.http_error(a,b,g,e)},success:function(a){d.set_busy(!1,
+null,e);$("#"+b).val(a);d.log(a)}})};this.plain2html=function(a,b){var d=this.set_busy(!0,"converting"),a=a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");$("#"+b).val(a?"<pre>"+a+"</pre>":"");this.set_busy(!1,null,d)};this.url=function(a,b){var d="string"===typeof b?"&"+b:"";if("string"!==typeof a)b=a;else if(!b||"object"!==typeof b)b={};b._action=a?a:this.env.action;var e=this.env.comm_path;if(b._action.match(/([a-z]+)\/([a-z0-9-_.]+)/))b._action=RegExp.$2,e=e.replace(/\_task=[a-z]+/,
+"_task="+RegExp.$1);var f={},h;for(h in b)void 0!==b[h]&&null!==b[h]&&(f[h]=b[h]);return e+"&"+$.param(f)+d};this.redirect=function(a,b){(b||null===b)&&this.set_busy(!0);this.is_framed()?parent.rcmail.redirect(a,b):this.location_href(a,window)};this.goto_url=function(a,b){this.redirect(this.url(a,b))};this.location_href=function(a,b,d){d&&this.lock_frame();bw.ie&&b==window?$("<a>").attr("href",a).appendTo(document.body).get(0).click():b.location.href=a};this.http_request=function(a,b,d){var e=this.url(a,
+b),a=this.triggerEvent("request"+a,b);if(void 0!==a){if(!1===a)return!1;b=a}e+="&_remote=1";this.log("HTTP GET: "+e);return $.ajax({type:"GET",url:e,data:{_unlock:d?d:0},dataType:"json",success:function(a){i.http_response(a)},error:function(a,b,e){rcmail.http_error(a,b,e,d)}})};this.http_post=function(a,b,d){var e=this.url(a);b&&"object"===typeof b?(b._remote=1,b._unlock=d?d:0):b+=(b?"&":"")+"_remote=1"+(d?"&_unlock="+d:"");a=this.triggerEvent("request"+a,b);if(void 0!==a){if(!1===a)return!1;b=a}this.log("HTTP POST: "+
+e);return $.ajax({type:"POST",url:e,data:b,dataType:"json",success:function(a){i.http_response(a)},error:function(a,b,e){rcmail.http_error(a,b,e,d)}})};this.abort_request=function(a){a.request&&a.request.abort();a.lock&&this.set_busy(!1,null,a.lock)};this.http_response=function(a){if(a){a.unlock&&this.set_busy(!1);this.triggerEvent("responsebefore",{response:a});this.triggerEvent("responsebefore"+a.action,{response:a});a.env&&this.set_env(a.env);if("object"===typeof a.texts)for(var b in a.texts)"string"===
+typeof a.texts[b]&&this.add_label(b,a.texts[b]);a.exec&&(this.log(a.exec),eval(a.exec));if(a.callbacks&&a.callbacks.length)for(b=0;b<a.callbacks.length;b++)this.triggerEvent(a.callbacks[b][0],a.callbacks[b][1]);switch(a.action){case "delete":if("addressbook"==this.task){var d;b=this.contact_list.get_selection();d=!1;b&&this.contact_list.rows[b]&&(d=""==this.env.source?(d=(""+b).replace(/^[^-]+-/,""))&&this.env.address_sources[d]&&!this.env.address_sources[d].readonly:!this.env.address_sources[this.env.source].readonly);
+this.enable_command("compose",b&&this.contact_list.rows[b]);this.enable_command("delete","edit",d);this.enable_command("export",this.contact_list&&0<this.contact_list.rowcount)}case "moveto":"show"==this.env.action?(this.enable_command(this.env.message_commands,!0),this.env.list_post||this.enable_command("reply-list",!1)):"addressbook"==this.task&&this.triggerEvent("listupdate",{folder:this.env.source,rowcount:this.contact_list.rowcount});case "purge":case "expunge":"mail"==this.task&&(this.env.messagecount||
+(this.env.contentframe&&this.show_contentframe(!1),this.enable_command(this.env.message_commands,"purge","expunge","select-all","select-none","sort","expand-all","expand-unread","collapse-all",!1)),this.message_list&&this.triggerEvent("listupdate",{folder:this.env.mailbox,rowcount:this.message_list.rowcount}));break;case "check-recent":case "getunread":case "search":this.env.qsearch=null;case "list":if("mail"==this.task){if(this.enable_command("show","expunge","select-all","select-none","sort",0<
+this.env.messagecount),this.enable_command("purge",this.purge_mailbox_test()),this.enable_command("expand-all","expand-unread","collapse-all",this.env.threading&&this.env.messagecount),"list"==a.action||"search"==a.action)this.msglist_select(this.message_list),this.triggerEvent("listupdate",{folder:this.env.mailbox,rowcount:this.message_list.rowcount})}else if("addressbook"==this.task&&(this.enable_command("export",this.contact_list&&0<this.contact_list.rowcount),"list"==a.action||"search"==a.action))this.enable_command("search-create",
+""==this.env.source),this.enable_command("search-delete",this.env.search_id),this.update_group_commands(),this.triggerEvent("listupdate",{folder:this.env.source,rowcount:this.contact_list.rowcount})}a.unlock&&this.hide_message(a.unlock);this.triggerEvent("responseafter",{response:a});this.triggerEvent("responseafter"+a.action,{response:a})}};this.http_error=function(a,b,d,e){b=a.statusText;this.set_busy(!1,null,e);a.abort();a.status&&b&&this.display_message(this.get_label("servererror")+" ("+b+")",
+"error")};this.async_upload_form=function(a,b,d){var e=(new Date).getTime(),f="rcmupload"+e;if(this.env.upload_progress_name){var h=this.env.upload_progress_name,g=$("input[name="+h+"]",a);g.length||(g=$("<input>").attr({type:"hidden",name:h}),g.prependTo(a));g.val(e)}document.all?document.body.insertAdjacentHTML("BeforeEnd",'<iframe name="'+f+'" src="program/blank.gif" style="width:0;height:0;visibility:hidden;"></iframe>'):(h=document.createElement("iframe"),h.name=f,h.style.border="none",h.style.width=
+0,h.style.height=0,h.style.visibility="hidden",document.body.appendChild(h));$(f).bind("load",{ts:e},d);$(a).attr({target:f,action:this.url(b,{_id:this.env.compose_id||"",_uploadid:e}),method:"POST"}).attr(a.encoding?"encoding":"enctype","multipart/form-data").submit();return f};this.start_keepalive=function(){this._int&&clearInterval(this._int);if(this.env.keep_alive&&!this.env.framed&&"mail"==this.task&&this.gui_objects.mailboxlist)this._int=setInterval(function(){i.check_for_recent(!1)},1E3*this.env.keep_alive);
+else if(this.env.keep_alive&&!this.env.framed&&"login"!=this.task&&"print"!=this.env.action)this._int=setInterval(function(){i.keep_alive()},1E3*this.env.keep_alive)};this.keep_alive=function(){this.busy||this.http_request("keep-alive")};this.check_for_recent=function(a){if(!this.busy){var b,d="_mbox="+urlencode(this.env.mailbox);a&&(b=this.set_busy(!0,"checkingmail"),d+="&_refresh=1",this.start_keepalive());this.gui_objects.messagelist&&(d+="&_list=1");this.gui_objects.quotadisplay&&(d+="&_quota=1");
+this.env.search_request&&(d+="&_search="+this.env.search_request);this.http_request("check-recent",d,b)}};this.get_single_uid=function(){return this.env.uid?this.env.uid:this.message_list?this.message_list.get_single_selection():null};this.get_single_cid=function(){return this.env.cid?this.env.cid:this.contact_list?this.contact_list.get_single_selection():null};this.get_caret_pos=function(a){if(void 0!==a.selectionEnd)return a.selectionEnd;if(document.selection&&document.selection.createRange){var b=
+document.selection.createRange();if(b.parentElement()!=a)return 0;var d=b.duplicate();"TEXTAREA"==a.tagName?d.moveToElementText(a):d.expand("textedit");d.setEndPoint("EndToStart",b);b=d.text.length;return b<=a.value.length?b:-1}return a.value.length};this.set_caret_pos=function(a,b){if(a.setSelectionRange)a.setSelectionRange(b,b);else if(a.createTextRange){var d=a.createTextRange();d.collapse(!0);d.moveEnd("character",b);d.moveStart("character",b);d.select()}};this.lock_form=function(a,b){if(a&&a.elements){var d,
+e,f;if(b)this.disabled_form_elements=[];for(d=0,e=a.elements.length;d<e;d++)if(f=a.elements[d],"hidden"!=f.type)if(b&&f.disabled)this.disabled_form_elements.push(f);else if(b||this.disabled_form_elements&&0>$.inArray(f,this.disabled_form_elements))f.disabled=b}}}rcube_webmail.long_subject_title=function(i,s){if(!i.title){var a=$(i);if(a.width()+15*s>a.parent().width())i.title=a.html()}};
+rcube_webmail.long_subject_title_ie=function(i,s){if(!i.title){var a=$(i),b=$.trim(a.text()),d=$("<span>").text(b).css({position:"absolute","float":"left",visibility:"hidden","font-size":a.css("font-size"),"font-weight":a.css("font-weight")}).appendTo($("body")),e=d.width();d.remove();if(e+15*s>a.width())i.title=b}};rcube_webmail.prototype.addEventListener=rcube_event_engine.prototype.addEventListener;rcube_webmail.prototype.removeEventListener=rcube_event_engine.prototype.removeEventListener;
+rcube_webmail.prototype.triggerEvent=rcube_event_engine.prototype.triggerEvent;
index a127b29331cc56a5f55817b54c3f91588492d940..15d8884a2443faccdec647b0c6b64d2569de4730 100644 (file)
@@ -3,7 +3,8 @@
  | Roundcube Webmail Client Script                                       |
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2005-2010, The Roundcube Dev Team                       |
+ | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
+ | Copyright (C) 2011, Kolab Systems AG                                  |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  +-----------------------------------------------------------------------+
  | Requires: jquery.js, common.js, list.js                               |
  +-----------------------------------------------------------------------+
 
-  $Id: app.js 5281 2011-09-27 07:29:49Z alec $
+  $Id: app.js 5554 2011-12-07 07:50:00Z alec $
 */
 
 function rcube_webmail()
 {
-  this.env = {};
+  this.env = { recipients_separator:',', recipients_delimiter:', ' };
   this.labels = {};
   this.buttons = {};
   this.buttons_sel = {};
@@ -36,7 +37,7 @@ function rcube_webmail()
 
   // webmail client settings
   this.dblclick_time = 500;
-  this.message_time = 2000;
+  this.message_time = 4000;
 
   this.identifier_expr = new RegExp('[^0-9a-z\-_]', 'gi');
 
@@ -85,7 +86,7 @@ function rcube_webmail()
     if (over) button_prop.over = over;
 
     this.buttons[command].push(button_prop);
-    
+
     if (this.loaded)
       init_button(command, button_prop);
   };
@@ -127,7 +128,7 @@ function rcube_webmail()
   // initialize webmail client
   this.init = function()
   {
-    var p = this;
+    var n, p = this;
     this.task = this.env.task;
 
     // check browser
@@ -137,13 +138,29 @@ function rcube_webmail()
     }
 
     // find all registered gui containers
-    for (var n in this.gui_containers)
+    for (n in this.gui_containers)
       this.gui_containers[n] = $('#'+this.gui_containers[n]);
 
     // find all registered gui objects
-    for (var n in this.gui_objects)
+    for (n in this.gui_objects)
       this.gui_objects[n] = rcube_find_object(this.gui_objects[n]);
 
+    // clickjacking protection
+    if (this.env.x_frame_options) {
+      try {
+        // bust frame if not allowed
+        if (this.env.x_frame_options == 'deny' && top.location.href != self.location.href)
+          top.location.href = self.location.href;
+        else if (top.location.hostname != self.location.hostname)
+          throw 1;
+      } catch (e) {
+        // possible clickjacking attack: disable all form elements
+        $('form').each(function(){ ref.lock_form(this, true); });
+        this.display_message("Blocked: possible clickjacking attack!", 'error');
+        return;
+      }
+    }
+
     // init registered buttons
     this.init_buttons();
 
@@ -154,7 +171,7 @@ function rcube_webmail()
     }
 
     // enable general commands
-    this.enable_command('logout', 'mail', 'addressbook', 'settings', 'save-pref', 'undo', true);
+    this.enable_command('logout', 'mail', 'addressbook', 'settings', 'save-pref', 'compose', 'undo', true);
 
     if (this.env.permaurl)
       this.enable_command('permaurl', true);
@@ -163,7 +180,7 @@ function rcube_webmail()
 
       case 'mail':
         // enable mail commands
-        this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', 'collapse-folder', true);
+        this.enable_command('list', 'checkmail', 'add-contact', 'search', 'reset-search', 'collapse-folder', true);
 
         if (this.gui_objects.messagelist) {
 
@@ -206,12 +223,13 @@ function rcube_webmail()
           'moveto', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', 'download',
           'print', 'load-attachment', 'load-headers', 'forward-attachment'];
 
-        if (this.env.action=='show' || this.env.action=='preview') {
+        if (this.env.action == 'show' || this.env.action == 'preview') {
           this.enable_command(this.env.message_commands, this.env.uid);
           this.enable_command('reply-list', this.env.list_post);
 
           if (this.env.action == 'show') {
-            this.http_request('pagenav', '_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox),
+            this.http_request('pagenav', '_uid='+this.env.uid+'&_mbox='+urlencode(this.env.mailbox)
+              + (this.env.search_request ? '&_search='+this.env.search_request : ''),
               this.display_message('', 'loading'));
           }
 
@@ -277,6 +295,9 @@ function rcube_webmail()
         if (this.gui_objects.folderlist)
           this.env.contactfolders = $.extend($.extend({}, this.env.address_sources), this.env.contactgroups);
 
+        this.enable_command('add', 'import', this.env.writable_source);
+        this.enable_command('list', 'listgroup', 'listsearch', 'advanced-search', true);
+
         if (this.gui_objects.contactslist) {
 
           this.contact_list = new rcube_list_widget(this.gui_objects.contactslist,
@@ -299,6 +320,7 @@ function rcube_webmail()
           }
 
           this.update_group_commands();
+          this.command('list');
         }
 
         this.set_page_buttons();
@@ -318,22 +340,13 @@ function rcube_webmail()
           if (this.env.action == 'add' || this.env.action == 'edit')
               this.init_contact_form();
         }
+
         if (this.gui_objects.qsearchbox) {
           this.enable_command('search', 'reset-search', 'moveto', true);
         }
 
-        if (this.contact_list && this.contact_list.rowcount > 0)
-          this.enable_command('export', true);
-
-        this.enable_command('add', 'import', this.env.writable_source);
-        this.enable_command('list', 'listgroup', 'advanced-search', true);
-
-        // load contacts of selected source
-        if (!this.env.action)
-          this.command('list', this.env.source);
         break;
 
-
       case 'settings':
         this.enable_command('preferences', 'identities', 'save', 'folders', true);
 
@@ -384,7 +397,12 @@ function rcube_webmail()
           $('#rcmloginpwd').focus();
 
         // detect client timezone
-        $('#rcmlogintz').val(new Date().getTimezoneOffset() / -60);
+        var dt = new Date(),
+          tz = dt.getTimezoneOffset() / -60,
+          stdtz = dt.getStdTimezoneOffset() / -60;
+
+        $('#rcmlogintz').val(stdtz);
+        $('#rcmlogindst').val(tz > stdtz ? 1 : 0);
 
         // display 'loading' message on form submit, lock submit button
         $('form').submit(function () {
@@ -443,6 +461,8 @@ function rcube_webmail()
   // execute a specific command on the web client
   this.command = function(command, props, obj)
   {
+    var ret, uid, cid, url, flag;
+
     if (obj && obj.blur)
       obj.blur();
 
@@ -459,32 +479,34 @@ function rcube_webmail()
     }
 
     // check input before leaving compose step
-    if (this.task=='mail' && this.env.action=='compose' && $.inArray(command, this.env.compose_commands)<0) {
+    if (this.task == 'mail' && this.env.action == 'compose' && $.inArray(command, this.env.compose_commands)<0) {
       if (this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning')))
         return false;
     }
 
     // process external commands
     if (typeof this.command_handlers[command] === 'function') {
-      var ret = this.command_handlers[command](props, obj);
+      ret = this.command_handlers[command](props, obj);
       return ret !== undefined ? ret : (obj ? false : true);
     }
     else if (typeof this.command_handlers[command] === 'string') {
-      var ret = window[this.command_handlers[command]](props, obj);
+      ret = window[this.command_handlers[command]](props, obj);
       return ret !== undefined ? ret : (obj ? false : true);
     }
 
     // trigger plugin hooks
     this.triggerEvent('actionbefore', {props:props, action:command});
-    var ret = this.triggerEvent('before'+command, props);
+    ret = this.triggerEvent('before'+command, props);
     if (ret !== undefined) {
-      // abort if one the handlers returned false
+      // abort if one of the handlers returned false
       if (ret === false)
         return false;
       else
         props = ret;
     }
 
+    ret = undefined;
+
     // process internal command
     switch (command) {
 
@@ -514,7 +536,6 @@ function rcube_webmail()
         return false;
 
       case 'open':
-        var uid;
         if (uid = this.get_single_uid()) {
           obj.href = '?_task='+this.env.task+'&_action=show&_mbox='+urlencode(this.env.mailbox)+'&_uid='+uid;
           return true;
@@ -522,21 +543,15 @@ function rcube_webmail()
         break;
 
       case 'list':
-        if (this.task=='mail') {
-          if (!this.env.search_request || (props && props != this.env.mailbox))
-            this.reset_qsearch();
-
+        this.reset_qsearch();
+        if (this.task == 'mail') {
           this.list_mailbox(props);
 
           if (this.env.trash_mailbox && !this.env.flag_for_deletion)
             this.set_alttext('delete', this.env.mailbox != this.env.trash_mailbox ? 'movemessagetotrash' : 'deletemessage');
         }
         else if (this.task == 'addressbook') {
-          if (!this.env.search_request || (props != this.env.source))
-            this.reset_qsearch();
-
           this.list_contacts(props);
-          this.enable_command('add', 'import', this.env.writable_source);
         }
         break;
 
@@ -589,7 +604,7 @@ function rcube_webmail()
       // common commands used in multiple tasks
       case 'show':
         if (this.task == 'mail') {
-          var uid = this.get_single_uid();
+          uid = this.get_single_uid();
           if (uid && (!this.env.uid || uid != this.env.uid)) {
             if (this.env.mailbox == this.env.drafts_mailbox)
               this.goto_url('compose', '_draft_uid='+uid+'&_mbox='+urlencode(this.env.mailbox), true);
@@ -598,7 +613,7 @@ function rcube_webmail()
           }
         }
         else if (this.task == 'addressbook') {
-          var cid = props ? props : this.get_single_cid();
+          cid = props ? props : this.get_single_cid();
           if (cid && !(this.env.action == 'show' && cid == this.env.cid))
             this.load_contact(cid, 'show');
         }
@@ -614,13 +629,12 @@ function rcube_webmail()
         break;
 
       case 'edit':
-        var cid;
         if (this.task=='addressbook' && (cid = this.get_single_cid()))
           this.load_contact(cid, 'edit');
         else if (this.task=='settings' && props)
           this.load_identity(props, 'edit-identity');
         else if (this.task=='mail' && (cid = this.get_single_uid())) {
-          var url = (this.env.mailbox == this.env.drafts_mailbox) ? '_draft_uid=' : '_uid=';
+          url = (this.env.mailbox == this.env.drafts_mailbox) ? '_draft_uid=' : '_uid=';
           this.goto_url('compose', url+cid+'&_mbox='+urlencode(this.env.mailbox), true);
         }
         break;
@@ -698,7 +712,7 @@ function rcube_webmail()
         if (props && !props._row)
           break;
 
-        var uid, flag = 'read';
+        flag = 'read';
 
         if (props._row.uid) {
           uid = props._row.uid;
@@ -718,7 +732,7 @@ function rcube_webmail()
         if (props && !props._row)
           break;
 
-        var uid, flag = 'flagged';
+        flag = 'flagged';
 
         if (props._row.uid) {
           uid = props._row.uid;
@@ -814,17 +828,11 @@ function rcube_webmail()
         break;
 
       case 'compose':
-        var url = this.env.comm_path+'&_action=compose';
+        url = this.url('mail/compose');
 
         if (this.task == 'mail') {
           url += '&_mbox='+urlencode(this.env.mailbox);
-
-          if (this.env.mailbox == this.env.drafts_mailbox) {
-            var uid;
-            if (uid = this.get_single_uid())
-              url += '&_draft_uid='+uid;
-          }
-          else if (props)
+          if (props)
              url += '&_to='+urlencode(props);
         }
         // modify url if we're in addressbook
@@ -848,10 +856,14 @@ function rcube_webmail()
           }
 
           if (a_cids.length)
-            this.http_post('mailto', {_cid: a_cids.join(','), _source: this.env.source}, true);
+            this.http_post('mailto', { _cid: a_cids.join(','), _source: this.env.source}, true);
+          else if (this.env.group)
+            this.http_post('mailto', { _gid: this.env.group, _source: this.env.source}, true);
 
           break;
         }
+        else if (props)
+          url += '&_to='+urlencode(props);
 
         this.redirect(url);
         break;
@@ -891,7 +903,7 @@ function rcube_webmail()
         if (!this.gui_objects.messageform)
           break;
 
-        if (!this.check_compose_input())
+        if (!props.nocheck && !this.check_compose_input(command))
           break;
 
         // Reset the auto-save timer
@@ -926,9 +938,8 @@ function rcube_webmail()
       case 'reply-all':
       case 'reply-list':
       case 'reply':
-        var uid;
         if (uid = this.get_single_uid()) {
-          var url = '_reply_uid='+uid+'&_mbox='+urlencode(this.env.mailbox);
+          url = '_reply_uid='+uid+'&_mbox='+urlencode(this.env.mailbox);
           if (command == 'reply-all')
             // do reply-list, when list is detected and popup menu wasn't used 
             url += '&_all=' + (!props && this.commands['reply-list'] ? 'list' : 'all');
@@ -941,7 +952,6 @@ function rcube_webmail()
 
       case 'forward-attachment':
       case 'forward':
-        var uid, url;
         if (uid = this.get_single_uid()) {
           url = '_forward_uid='+uid+'&_mbox='+urlencode(this.env.mailbox);
           if (command == 'forward-attachment' || (!props && this.env.forward_attachment))
@@ -951,7 +961,6 @@ function rcube_webmail()
         break;
 
       case 'print':
-        var uid;
         if (uid = this.get_single_uid()) {
           ref.printwin = window.open(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : ''));
           if (this.printwin) {
@@ -963,7 +972,6 @@ function rcube_webmail()
         break;
 
       case 'viewsource':
-        var uid;
         if (uid = this.get_single_uid()) {
           ref.sourcewin = window.open(this.env.comm_path+'&_action=viewsource&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox));
           if (this.sourcewin)
@@ -972,7 +980,6 @@ function rcube_webmail()
         break;
 
       case 'download':
-        var uid;
         if (uid = this.get_single_uid())
           this.goto_url('viewsource', '&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+'&_save=1');
         break;
@@ -1006,6 +1013,7 @@ function rcube_webmail()
         break;
 
       case 'listgroup':
+        this.reset_qsearch();
         this.list_contacts(props.source, props.id);
         break;
 
@@ -1052,24 +1060,26 @@ function rcube_webmail()
       // unified command call (command name == function name)
       default:
         var func = command.replace(/-/g, '_');
-        if (this[func] && typeof this[func] === 'function')
-          this[func](props);
+        if (this[func] && typeof this[func] === 'function') {
+          ret = this[func](props);
+        }
         break;
     }
 
-    this.triggerEvent('after'+command, props);
+    if (this.triggerEvent('after'+command, props) === false)
+      ret = false;
     this.triggerEvent('actionafter', {props:props, action:command});
 
-    return obj ? false : true;
+    return ret === false ? false : obj ? false : true;
   };
 
   // set command(s) enabled or disabled
   this.enable_command = function()
   {
-    var args = Array.prototype.slice.call(arguments),
+    var i, n, args = Array.prototype.slice.call(arguments),
       enable = args.pop(), cmd;
 
-    for (var n=0; n<args.length; n++) {
+    for (n=0; n<args.length; n++) {
       cmd = args[n];
       // argument of type array
       if (typeof cmd === 'string') {
@@ -1078,7 +1088,7 @@ function rcube_webmail()
       }
       // push array elements into commands array
       else {
-        for (var i in cmd)
+        for (i in cmd)
           args.push(cmd[i]);
       }
     }
@@ -1205,6 +1215,24 @@ function rcube_webmail()
     this.http_post('save-pref', request);
   };
 
+  this.html_identifier = function(str, encode)
+  {
+    str = String(str);
+    if (encode)
+      return Base64.encode(str).replace(/=+$/, '').replace(/\+/g, '-').replace(/\//g, '_');
+    else
+      return str.replace(this.identifier_expr, '_');
+  };
+
+  this.html_identifier_decode = function(str)
+  {
+    str = String(str).replace(/-/g, '+').replace(/_/g, '/');
+
+    while (str.length % 4) str += '=';
+
+    return Base64.decode(str);
+  };
+
 
   /*********************************************************/
   /*********        event handling methods         *********/
@@ -1251,13 +1279,14 @@ function rcube_webmail()
       this.initialBodyScrollTop = bw.ie ? 0 : window.pageYOffset;
       this.initialListScrollTop = this.gui_objects.folderlist.parentNode.scrollTop;
 
-      var li, pos, list, height;
-      list = $(this.gui_objects.folderlist);
-      pos = list.offset();
+      var k, li, height,
+        list = $(this.gui_objects.folderlist);
+        pos = list.offset();
+
       this.env.folderlist_coords = { x1:pos.left, y1:pos.top, x2:pos.left + list.width(), y2:pos.top + list.height() };
 
       this.env.folder_coords = [];
-      for (var k in model) {
+      for (k in model) {
         if (li = this.get_folder_li(k)) {
           // only visible folders
           if (height = li.firstChild.offsetHeight) {
@@ -1293,19 +1322,18 @@ function rcube_webmail()
   this.drag_move = function(e)
   {
     if (this.gui_objects.folderlist && this.env.folder_coords) {
-      // offsets to compensate for scrolling while dragging a message
-      var boffset = bw.ie ? -document.documentElement.scrollTop : this.initialBodyScrollTop;
-      var moffset = this.initialListScrollTop-this.gui_objects.folderlist.parentNode.scrollTop;
-      var toffset = -moffset-boffset;
-      var li, div, pos, mouse, check, oldclass,
-        layerclass = 'draglayernormal';
+      var k, li, div, check, oldclass,
+        layerclass = 'draglayernormal',
+        mouse = rcube_event.get_mouse_pos(e),
+        pos = this.env.folderlist_coords,
+        // offsets to compensate for scrolling while dragging a message
+        boffset = bw.ie ? -document.documentElement.scrollTop : this.initialBodyScrollTop,
+        moffset = this.initialListScrollTop-this.gui_objects.folderlist.parentNode.scrollTop;
 
       if (this.contact_list && this.contact_list.draglayer)
         oldclass = this.contact_list.draglayer.attr('class');
 
-      mouse = rcube_event.get_mouse_pos(e);
-      pos = this.env.folderlist_coords;
-      mouse.y += toffset;
+      mouse.y += -moffset-boffset;
 
       // if mouse pointer is outside of folderlist
       if (mouse.x < pos.x1 || mouse.x >= pos.x2 || mouse.y < pos.y1 || mouse.y >= pos.y2) {
@@ -1320,10 +1348,10 @@ function rcube_webmail()
       }
 
       // over the folders
-      for (var k in this.env.folder_coords) {
+      for (k in this.env.folder_coords) {
         pos = this.env.folder_coords[k];
         if (mouse.x >= pos.x1 && mouse.x < pos.x2 && mouse.y >= pos.y1 && mouse.y < pos.y2){
-         if ((check = this.check_droptarget(k))) {
+          if ((check = this.check_droptarget(k))) {
             li = this.get_folder_li(k);
             div = $(li.getElementsByTagName('div')[0]);
 
@@ -1334,9 +1362,9 @@ function rcube_webmail()
 
               this.folder_auto_expand = k;
               this.folder_auto_timer = window.setTimeout(function() {
-                  rcmail.command('collapse-folder', rcmail.folder_auto_expand);
-                  rcmail.drag_start(null);
-                }, 1000);
+                rcmail.command('collapse-folder', rcmail.folder_auto_expand);
+                rcmail.drag_start(null);
+              }, 1000);
             } else if (this.folder_auto_timer) {
               window.clearTimeout(this.folder_auto_timer);
               this.folder_auto_timer = null;
@@ -1362,31 +1390,29 @@ function rcube_webmail()
     }
   };
 
-  this.collapse_folder = function(id)
+  this.collapse_folder = function(name)
   {
-    var li = this.get_folder_li(id),
-      div = $(li.getElementsByTagName('div')[0]);
-
-    if (!div || (!div.hasClass('collapsed') && !div.hasClass('expanded')))
-      return;
-
-    var ul = $(li.getElementsByTagName('ul')[0]);
+    var li = this.get_folder_li(name, '', true),
+      div = $('div:first', li),
+      ul = $('ul:first', li);
 
     if (div.hasClass('collapsed')) {
       ul.show();
       div.removeClass('collapsed').addClass('expanded');
-      var reg = new RegExp('&'+urlencode(id)+'&');
+      var reg = new RegExp('&'+urlencode(name)+'&');
       this.env.collapsed_folders = this.env.collapsed_folders.replace(reg, '');
     }
-    else {
+    else if (div.hasClass('expanded')) {
       ul.hide();
       div.removeClass('expanded').addClass('collapsed');
-      this.env.collapsed_folders = this.env.collapsed_folders+'&'+urlencode(id)+'&';
+      this.env.collapsed_folders = this.env.collapsed_folders+'&'+urlencode(name)+'&';
 
       // select parent folder if one of its childs is currently selected
-      if (this.env.mailbox.indexOf(id + this.env.delimiter) == 0)
-        this.command('list', id);
+      if (this.env.mailbox.indexOf(name + this.env.delimiter) == 0)
+        this.command('list', name);
     }
+    else
+      return;
 
     // Work around a bug in IE6 and IE7, see #1485309
     if (bw.ie6 || bw.ie7) {
@@ -1398,13 +1424,17 @@ function rcube_webmail()
     }
 
     this.command('save-pref', { name: 'collapsed_folders', value: this.env.collapsed_folders });
-    this.set_unread_count_display(id, false);
+    this.set_unread_count_display(name, false);
   };
 
   this.doc_mouse_up = function(e)
   {
     var model, list, li, id;
 
+    // ignore event if jquery UI dialog is open
+    if ($(rcube_event.get_target(e)).closest('.ui-dialog, .ui-widget-overlay').length)
+      return;
+
     if (list = this.message_list) {
       if (!rcube_mouse_is_over(e, list.list.parentNode))
         list.blur();
@@ -1526,11 +1556,12 @@ function rcube_webmail()
 
   this.msglist_keypress = function(list)
   {
+    if (list.modkey == CONTROL_KEY)
+      return;
+
     if (list.key_pressed == list.ENTER_KEY)
       this.command('show');
-    else if (list.key_pressed == list.DELETE_KEY)
-      this.command('delete');
-    else if (list.key_pressed == list.BACKSPACE_KEY)
+    else if (list.key_pressed == list.DELETE_KEY || list.key_pressed == list.BACKSPACE_KEY)
       this.command('delete');
     else if (list.key_pressed == 33)
       this.command('previouspage');
@@ -1644,14 +1675,18 @@ function rcube_webmail()
     if (!this.gui_objects.messagelist || !this.message_list)
       return false;
 
+    // Prevent from adding messages from different folder (#1487752)
+    if (flags.mbox != this.env.mailbox && !flags.skip_mbox_check)
+      return false;
+
     if (!this.env.messages[uid])
       this.env.messages[uid] = {};
 
     // merge flags over local message object
     $.extend(this.env.messages[uid], {
       deleted: flags.deleted?1:0,
-      replied: flags.replied?1:0,
-      unread: flags.unread?1:0,
+      replied: flags.answered?1:0,
+      unread: !flags.seen?1:0,
       forwarded: flags.forwarded?1:0,
       flagged: flags.flagged?1:0,
       has_children: flags.has_children?1:0,
@@ -1665,23 +1700,18 @@ function rcube_webmail()
       flags: flags.extra_flags
     });
 
-    var c, html, tree = expando = '',
+    var c, n, col, html, tree = '', expando = '',
       list = this.message_list,
       rows = list.rows,
-      tbody = this.gui_objects.messagelist.tBodies[0],
-      rowcount = tbody.rows.length,
-      even = rowcount%2,
       message = this.env.messages[uid],
       css_class = 'message'
-        + (even ? ' even' : ' odd')
-        + (flags.unread ? ' unread' : '')
+        + (!flags.seen ? ' unread' : '')
         + (flags.deleted ? ' deleted' : '')
         + (flags.flagged ? ' flagged' : '')
-        + (flags.unread_children && !flags.unread && !this.env.autoexpand_threads ? ' unroot' : '')
+        + (flags.unread_children && flags.seen && !this.env.autoexpand_threads ? ' unroot' : '')
         + (message.selected ? ' selected' : ''),
       // for performance use DOM instead of jQuery here
-      row = document.createElement('tr'),
-      col = document.createElement('td');
+      row = document.createElement('tr');
 
     row.id = 'rcmrow'+uid;
     row.className = css_class;
@@ -1692,12 +1722,12 @@ function rcube_webmail()
       css_class += ' status';
       if (flags.deleted)
         css_class += ' deleted';
-      else if (flags.unread)
+      else if (!flags.seen)
         css_class += ' unread';
       else if (flags.unread_children > 0)
         css_class += ' unreadchildren';
     }
-    if (flags.replied)
+    if (flags.answered)
       css_class += ' replied';
     if (flags.forwarded)
       css_class += ' forwarded';
@@ -1708,9 +1738,10 @@ function rcube_webmail()
 
     // threads
     if (this.env.threading) {
-      // This assumes that div width is hardcoded to 15px,
-      var width = message.depth * 15;
       if (message.depth) {
+        // This assumes that div width is hardcoded to 15px,
+        tree += '<span id="rcmtab' + uid + '" class="branch" style="width:' + (message.depth * 15) + 'px;">&nbsp;&nbsp;</span>';
+
         if ((rows[message.parent_uid] && rows[message.parent_uid].expanded === false)
           || ((this.env.autoexpand_threads == 0 || this.env.autoexpand_threads == 2) &&
             (!rows[message.parent_uid] || !rows[message.parent_uid].expanded))
@@ -1725,13 +1756,9 @@ function rcube_webmail()
         if (message.expanded === undefined && (this.env.autoexpand_threads == 1 || (this.env.autoexpand_threads == 2 && message.unread_children))) {
           message.expanded = true;
         }
-      }
 
-      if (width)
-        tree += '<span id="rcmtab' + uid + '" class="branch" style="width:' + width + 'px;">&nbsp;&nbsp;</span>';
-
-      if (message.has_children && !message.depth)
         expando = '<div id="rcmexpando' + uid + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '">&nbsp;&nbsp;</div>';
+      }
     }
 
     tree += '<span id="msgicn'+uid+'" class="'+css_class+'">&nbsp;</span>';
@@ -1745,7 +1772,7 @@ function rcube_webmail()
     }
 
     // add each submitted col
-    for (var n in this.env.coltypes) {
+    for (n in this.env.coltypes) {
       c = this.env.coltypes[n];
       col = document.createElement('td');
       col.className = String(c).toLowerCase();
@@ -1765,7 +1792,7 @@ function rcube_webmail()
       else if (c == 'status') {
         if (flags.deleted)
           css_class = 'deleted';
-        else if (flags.unread)
+        else if (!flags.seen)
           css_class = 'unread';
         else if (flags.unread_children > 0)
           css_class = 'unreadchildren';
@@ -1775,8 +1802,17 @@ function rcube_webmail()
       }
       else if (c == 'threads')
         html = expando;
-      else if (c == 'subject')
+      else if (c == 'subject') {
+        if (bw.ie)
+          col.onmouseover = function() { rcube_webmail.long_subject_title_ie(this, message.depth+1); };
         html = tree + cols[c];
+      }
+      else if (c == 'priority') {
+        if (flags.prio > 0 && flags.prio < 6)
+          html = '<span class="prio'+flags.prio+'">&nbsp;</span>';
+        else
+          html = '&nbsp;';
+      }
       else
         html = cols[c];
 
@@ -1940,18 +1976,13 @@ function rcube_webmail()
   // list messages of a specific mailbox using filter
   this.filter_mailbox = function(filter)
   {
-    var search, lock = this.set_busy(true, 'searching');
-
-    if (this.gui_objects.qsearchbox)
-      search = this.gui_objects.qsearchbox.value;
+    var lock = this.set_busy(true, 'searching');
 
     this.clear_message_list();
 
     // reset vars
     this.env.current_page = 1;
-    this.http_request('search', '_filter='+filter
-        + (search ? '&_q='+urlencode(search) : '')
-        + (this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : ''), lock);
+    this.http_request('search', this.search_params(false, filter), lock);
   };
 
   // list messages of a specific mailbox
@@ -1986,7 +2017,7 @@ function rcube_webmail()
     if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort))
       url += '&_refresh=1';
 
-    this.select_folder(mbox, this.env.mailbox);
+    this.select_folder(mbox, '', true);
     this.env.mailbox = mbox;
 
     // load message list remotely
@@ -2050,8 +2081,7 @@ function rcube_webmail()
       new_row = tbody.firstChild;
 
     while (new_row) {
-      if (new_row.nodeType == 1 && (r = this.message_list.rows[new_row.uid])
-           && r.unread_children) {
+      if (new_row.nodeType == 1 && (r = this.message_list.rows[new_row.uid]) && r.unread_children) {
            this.message_list.expand_all(r);
            this.set_unread_children(r.uid);
       }
@@ -2086,8 +2116,12 @@ function rcube_webmail()
   };
 
   // Initializes threads indicators/expanders after list update
-  this.init_threads = function(roots)
+  this.init_threads = function(roots, mbox)
   {
+    // #1487752
+    if (mbox && mbox != this.env.mailbox)
+      return false;
+
     for (var n=0, len=roots.length; n<len; n++)
       this.add_tree_icons(roots[n]);
     this.expand_threads();
@@ -2491,7 +2525,7 @@ function rcube_webmail()
     // if there is a trash mailbox defined and we're not currently in it
     else {
       // if shift was pressed delete it immediately
-      if (list && list.shiftkey) {
+      if (list && list.modkey == SHIFT_KEY) {
         if (confirm(this.get_label('deletemessagesconfirm')))
           this.permanently_remove_messages();
       }
@@ -2914,12 +2948,14 @@ function rcube_webmail()
 
   this.init_address_input_events = function(obj, props)
   {
+    this.env.recipients_delimiter = this.env.recipients_separator + ' ';
+
     obj[bw.ie || bw.safari || bw.chrome ? 'keydown' : 'keypress'](function(e) { return ref.ksearch_keydown(e, this, props); })
       .attr('autocomplete', 'off');
   };
 
   // checks the input fields before sending a message
-  this.check_compose_input = function()
+  this.check_compose_input = function(cmd)
   {
     // check input fields
     var ed, input_to = $("[name='_to']"),
@@ -2954,15 +2990,28 @@ function rcube_webmail()
 
     // display localized warning for missing subject
     if (input_subject.val() == '') {
-      var subject = prompt(this.get_label('nosubjectwarning'), this.get_label('nosubject'));
+      var myprompt = $('<div class="prompt">').html('<div class="message">' + this.get_label('nosubjectwarning') + '</div>').appendTo(document.body);
+      var prompt_value = $('<input>').attr('type', 'text').attr('size', 30).appendTo(myprompt).val(this.get_label('nosubject'));
 
-      // user hit cancel, so don't send
-      if (!subject && subject !== '') {
+      var buttons = {};
+      buttons[this.get_label('cancel')] = function(){
         input_subject.focus();
-        return false;
-      }
-      else
-        input_subject.val((subject ? subject : this.get_label('nosubject')));
+        $(this).dialog('close');
+      };
+      buttons[this.get_label('sendmessage')] = function(){
+        input_subject.val(prompt_value.val());
+        $(this).dialog('close');
+        ref.command(cmd, { nocheck:true });  // repeat command which triggered this
+      };
+
+      myprompt.dialog({
+        modal: true,
+        resizable: false,
+        buttons: buttons,
+        close: function(event, ui) { $(this).remove() }
+      });
+      prompt_value.select();
+      return false;
     }
 
     // Apply spellcheck changes if spell checker is active
@@ -3151,7 +3200,7 @@ function rcube_webmail()
         sig = this.env.signatures[sig].is_html ? this.env.signatures[sig].plain_text : this.env.signatures[sig].text;
         sig = sig.replace(/\r\n/g, '\n');
 
-        if (!sig.match(/^--[ -]\n/))
+        if (!sig.match(/^--[ -]\n/m))
           sig = sig_separator + '\n' + sig;
 
         p = this.env.sig_above ? message.indexOf(sig) : message.lastIndexOf(sig);
@@ -3163,7 +3212,7 @@ function rcube_webmail()
         sig = this.env.signatures[id]['is_html'] ? this.env.signatures[id]['plain_text'] : this.env.signatures[id]['text'];
         sig = sig.replace(/\r\n/g, '\n');
 
-        if (!sig.match(/^--[ -]\n/))
+        if (!sig.match(/^--[ -]\n/m))
           sig = sig_separator + '\n' + sig;
 
         if (this.env.sig_above) {
@@ -3232,12 +3281,12 @@ function rcube_webmail()
       if (this.env.signatures[id]) {
         if (this.env.signatures[id].is_html) {
           sig = this.env.signatures[id].text;
-          if (!this.env.signatures[id].plain_text.match(/^--[ -]\r?\n/))
+          if (!this.env.signatures[id].plain_text.match(/^--[ -]\r?\n/m))
             sig = sig_separator + '<br />' + sig;
         }
         else {
           sig = this.env.signatures[id].text;
-          if (!sig.match(/^--[ -]\r?\n/))
+          if (!sig.match(/^--[ -]\r?\n/m))
             sig = sig_separator + '\n' + sig;
           sig = '<pre>' + sig + '</pre>';
         }
@@ -3341,16 +3390,8 @@ function rcube_webmail()
 
   this.remove_from_attachment_list = function(name)
   {
-    if (this.env.attachments[name])
-      delete this.env.attachments[name];
-
-    if (!this.gui_objects.attachmentlist)
-      return false;
-
-    var list = this.gui_objects.attachmentlist.getElementsByTagName("li");
-    for (i=0; i<list.length; i++)
-      if (list[i].id == name)
-        this.gui_objects.attachmentlist.removeChild(list[i]);
+    delete this.env.attachments[name];
+    $('#'+name).remove();
   };
 
   this.remove_attachment = function(name)
@@ -3403,40 +3444,58 @@ function rcube_webmail()
   this.qsearch = function(value)
   {
     if (value != '') {
-      var n, r, addurl = '', mods_arr = [],
-        mods = this.env.search_mods,
-        mbox = this.env.mailbox,
-        lock = this.set_busy(true, 'searching');
+      var n, lock = this.set_busy(true, 'searching');
 
-      if (this.message_list) {
+      if (this.message_list)
         this.clear_message_list();
-        if (mods)
-          mods = mods[mbox] ? mods[mbox] : mods['*'];
-      } else if (this.contact_list) {
+      else if (this.contact_list)
         this.list_contacts_clear();
-      }
-
-      if (mods) {
-        for (n in mods)
-          mods_arr.push(n);
-        addurl += '&_headers='+mods_arr.join(',');
-      }
-
-      if (this.gui_objects.search_filter)
-        addurl += '&_filter=' + this.gui_objects.search_filter.value;
 
       // reset vars
       this.env.current_page = 1;
-      r = this.http_request('search', '_q='+urlencode(value)
-        + (mbox ? '&_mbox='+urlencode(mbox) : '')
+      r = this.http_request('search', this.search_params(value)
         + (this.env.source ? '&_source='+urlencode(this.env.source) : '')
-        + (this.env.group ? '&_gid='+urlencode(this.env.group) : '')
-        + (addurl ? addurl : ''), lock);
+        + (this.env.group ? '&_gid='+urlencode(this.env.group) : ''), lock);
 
       this.env.qsearch = {lock: lock, request: r};
     }
   };
 
+  // build URL params for search
+  this.search_params = function(search, filter)
+  {
+    var n, url = [], mods_arr = [],
+      mods = this.env.search_mods,
+      mbox = this.env.mailbox;
+
+    if (!filter && this.gui_objects.search_filter)
+      filter = this.gui_objects.search_filter.value;
+
+    if (!search && this.gui_objects.qsearchbox)
+      search = this.gui_objects.qsearchbox.value;
+
+    if (filter)
+      url.push('_filter=' + urlencode(filter));
+
+    if (search) {
+      url.push('_q='+urlencode(search));
+
+      if (mods && this.message_list)
+        mods = mods[mbox] ? mods[mbox] : mods['*'];
+
+      if (mods) {
+        for (n in mods)
+          mods_arr.push(n);
+        url.push('_headers='+mods_arr.join(','));
+      }
+    }
+
+    if (mbox)
+      url.push('_mbox='+urlencode(mbox));
+
+    return url.join('&');
+  };
+
   // reset quick-search form
   this.reset_qsearch = function()
   {
@@ -3448,6 +3507,7 @@ function rcube_webmail()
 
     this.env.qsearch = null;
     this.env.search_request = null;
+    this.env.search_id = null;
   };
 
   this.sent_successfully = function(type, msg)
@@ -3542,7 +3602,7 @@ function rcube_webmail()
 
   this.insert_recipient = function(id)
   {
-    if (!this.env.contacts[id] || !this.ksearch_input)
+    if (id === null || !this.env.contacts[id] || !this.ksearch_input)
       return;
 
     // get cursor pos
@@ -3559,13 +3619,13 @@ function rcube_webmail()
 
     // insert all members of a group
     if (typeof this.env.contacts[id] === 'object' && this.env.contacts[id].id) {
-      insert += this.env.contacts[id].name + ', ';
+      insert += this.env.contacts[id].name + this.env.recipients_delimiter;
       this.group2expand = $.extend({}, this.env.contacts[id]);
       this.group2expand.input = this.ksearch_input;
       this.http_request('mail/group-expand', '_source='+urlencode(this.env.contacts[id].source)+'&_gid='+urlencode(this.env.contacts[id].id), false);
     }
     else if (typeof this.env.contacts[id] === 'string') {
-      insert = this.env.contacts[id] + ', ';
+      insert = this.env.contacts[id] + this.env.recipients_delimiter;
       trigger = true;
     }
 
@@ -3602,7 +3662,7 @@ function rcube_webmail()
 
     // get string from current cursor pos to last comma
     var cpos = this.get_caret_pos(this.ksearch_input),
-      p = inp_value.lastIndexOf(',', cpos-1),
+      p = inp_value.lastIndexOf(this.env.recipients_separator, cpos-1),
       q = inp_value.substring(p+1, cpos),
       min = this.env.autocomplete_min_length,
       ac = this.ksearch_data;
@@ -3614,22 +3674,19 @@ function rcube_webmail()
     if (q == this.ksearch_value)
       return;
 
+    this.ksearch_destroy();
+
     if (q.length && q.length < min) {
-      if (!this.env.acinfo) {
-        this.env.acinfo = this.display_message(
+      if (!this.ksearch_info) {
+        this.ksearch_info = this.display_message(
           this.get_label('autocompletechars').replace('$min', min));
       }
       return;
     }
-    else if (this.env.acinfo) {
-      this.hide_message(this.env.acinfo);
-    }
 
     var old_value = this.ksearch_value;
     this.ksearch_value = q;
 
-    this.ksearch_destroy();
-
     // ...string is empty
     if (!q.length)
       return;
@@ -3671,7 +3728,9 @@ function rcube_webmail()
       return;
 
     // display search results
-    var p, ul, li, text, init, s_val = this.ksearch_value,
+    var i, len, ul, li, text, init,
+      value = this.ksearch_value,
+      data = this.ksearch_data,
       maxlen = this.env.autocomplete_max ? this.env.autocomplete_max : 15;
 
     // create results pane if not present
@@ -3700,11 +3759,11 @@ function rcube_webmail()
     }
 
     // add each result line to list
-    if (results && results.length) {
-      for (i=0; i < results.length && maxlen > 0; i++) {
+    if (results && (len = results.length)) {
+      for (i=0; i < len && maxlen > 0; i++) {
         text = typeof results[i] === 'object' ? results[i].name : results[i];
         li = document.createElement('LI');
-        li.innerHTML = text.replace(new RegExp('('+RegExp.escape(s_val)+')', 'ig'), '##$1%%').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/##([^%]+)%%/g, '<b>$1</b>');
+        li.innerHTML = text.replace(new RegExp('('+RegExp.escape(value)+')', 'ig'), '##$1%%').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/##([^%]+)%%/g, '<b>$1</b>');
         li.onmouseover = function(){ ref.ksearch_select(this); };
         li.onmouseup = function(){ ref.ksearch_click(this) };
         li._rcm_id = this.env.contacts.length + i;
@@ -3722,20 +3781,28 @@ function rcube_webmail()
       }
     }
 
-    if (results && results.length)
+    if (len)
       this.env.contacts = this.env.contacts.concat(results);
 
     // run next parallel search
-    if (maxlen > 0 && this.ksearch_data.id == reqid && this.ksearch_data.sources.length) {
-      var lock, xhr, props = this.ksearch_data, source = props.sources.shift();
-      if (source) {
+    if (data.id == reqid) {
       data.num--;
-        lock = this.display_message(this.get_label('searching'), 'loading');
-        xhr = this.http_post(props.action, '_search='+urlencode(s_val)+'&_id='+reqid
-          +'&_source='+urlencode(source), lock);
-
-        this.ksearch_data.locks.push(lock);
-        this.ksearch_data.requests.push(xhr);
+      if (maxlen > 0 && data.sources.length) {
+        var lock, xhr, source = data.sources.shift();
+        if (source) {
+          lock = this.display_message(this.get_label('searching'), 'loading');
+          xhr = this.http_post(data.action, '_search='+urlencode(value)+'&_id='+reqid
+            +'&_source='+urlencode(source), lock);
+
+          this.ksearch_data.locks.push(lock);
+          this.ksearch_data.requests.push(xhr);
+        }
+      }
+      else if (!maxlen) {
+        if (!this.ksearch_msg)
+          this.ksearch_msg = this.display_message(this.get_label('autocompletemore'));
+        // abort pending searches
+        this.ksearch_abort();
       }
     }
   };
@@ -3769,8 +3836,24 @@ function rcube_webmail()
     this.ksearch_destroy();
   };
 
-  // Aborts pending autocomplete requests
+  // Clears autocomplete data/requests
   this.ksearch_destroy = function()
+  {
+    this.ksearch_abort();
+
+    if (this.ksearch_info)
+      this.hide_message(this.ksearch_info);
+
+    if (this.ksearch_msg)
+      this.hide_message(this.ksearch_msg);
+
+    this.ksearch_data = null;
+    this.ksearch_info = null;
+    this.ksearch_msg = null;
+  }
+
+  // Aborts pending autocomplete requests
+  this.ksearch_abort = function()
   {
     var i, len, ac = this.ksearch_data;
 
@@ -3779,9 +3862,8 @@ function rcube_webmail()
 
     for (i=0, len=ac.locks.length; i<len; i++)
       this.abort_request({request: ac.requests[i], lock: ac.locks[i]});
+  };
 
-    this.ksearch_data = null;
-  }
 
   /*********************************************************/
   /*********         address book methods          *********/
@@ -3823,7 +3905,7 @@ function rcube_webmail()
       }
     }
 
-    this.enable_command('compose', list.selection.length > 0);
+    this.enable_command('compose', this.env.group || list.selection.length > 0);
     this.enable_command('edit', id && writable);
     this.enable_command('delete', list.selection.length && writable);
 
@@ -3832,7 +3914,7 @@ function rcube_webmail()
 
   this.list_contacts = function(src, group, page)
   {
-    var add_url = '',
+    var folder, add_url = '',
       target = window;
 
     if (!src)
@@ -3848,7 +3930,12 @@ function rcube_webmail()
     else if (group != this.env.group)
       page = this.env.current_page = 1;
 
-    this.select_folder((group ? 'G'+src+group : src), (this.env.group ? 'G'+this.env.source+this.env.group : this.env.source));
+    if (this.env.search_id)
+      folder = 'S'+this.env.search_id;
+    else
+      folder = group ? 'G'+src+group : src;
+
+    this.select_folder(folder);
 
     this.env.source = src;
     this.env.group = group;
@@ -3893,8 +3980,8 @@ function rcube_webmail()
     if (group)
       url += '&_gid='+group;
 
-    // also send search request to get the right messages 
-    if (this.env.search_request) 
+    // also send search request to get the right messages
+    if (this.env.search_request)
       url += '&_search='+this.env.search_request;
 
     this.http_request('list', url, lock);
@@ -3904,7 +3991,8 @@ function rcube_webmail()
   {
     this.contact_list.clear(true);
     this.show_contentframe(false);
-    this.enable_command('delete', 'compose', false);
+    this.enable_command('delete', false);
+    this.enable_command('compose', this.env.group ? true : false);
   };
 
   // load contact record
@@ -3976,9 +4064,11 @@ function rcube_webmail()
 
   this.delete_contacts = function()
   {
+    var selection = this.contact_list.get_selection(),
+      undelete = this.env.source && this.env.address_sources[this.env.source].undelete;
+
     // exit if no mailbox specified or if selection is empty
-    var selection = this.contact_list.get_selection();
-    if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm')))
+    if (!(selection.length || this.env.cid) || (!undelete && !confirm(this.get_label('deletecontactconfirm'))))
       return;
 
     var id, n, a_cids = [], qs = '';
@@ -4005,7 +4095,10 @@ function rcube_webmail()
       qs += '&_search='+this.env.search_request;
 
     // send request to server
-    this.http_post('delete', '_cid='+urlencode(a_cids.join(','))+'&_source='+urlencode(this.env.source)+'&_from='+(this.env.action ? this.env.action : '')+qs);
+    this.http_post('delete', '_cid='+urlencode(a_cids.join(','))
+      +'&_source='+urlencode(this.env.source)
+      +'&_from='+(this.env.action ? this.env.action : '')+qs,
+      this.display_message(this.get_label('contactdeleting'), 'loading'));
 
     return true;
   };
@@ -4015,7 +4108,7 @@ function rcube_webmail()
   {
     var c, row, list = this.contact_list;
 
-    cid = String(cid).replace(this.identifier_expr, '_');
+    cid = this.html_identifier(cid);
 
     // when in searching mode, concat cid with the source name
     if (!list.rows[cid]) {
@@ -4031,7 +4124,7 @@ function rcube_webmail()
 
       // cid change
       if (newcid) {
-        newcid = String(newcid).replace(this.identifier_expr, '_');
+        newcid = this.html_identifier(newcid);
         row.id = 'rcmrow' + newcid;
         list.remove_row(cid);
         list.init_row(row);
@@ -4044,31 +4137,29 @@ function rcube_webmail()
   // add row to contacts list
   this.add_contact_row = function(cid, cols, select)
   {
-    if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0])
+    if (!this.gui_objects.contactslist)
       return false;
 
-    var tbody = this.gui_objects.contactslist.tBodies[0],
-      rowcount = tbody.rows.length,
-      even = rowcount%2,
+    var c, list = this.contact_list,
       row = document.createElement('tr');
 
-    row.id = 'rcmrow'+String(cid).replace(this.identifier_expr, '_');
-    row.className = 'contact '+(even ? 'even' : 'odd');
+    row.id = 'rcmrow'+this.html_identifier(cid);
+    row.className = 'contact';
 
-    if (this.contact_list.in_selection(cid))
+    if (list.in_selection(cid))
       row.className += ' selected';
 
     // add each submitted col
-    for (var c in cols) {
+    for (c in cols) {
       col = document.createElement('td');
       col.className = String(c).toLowerCase();
       col.innerHTML = cols[c];
       row.appendChild(col);
     }
 
-    this.contact_list.insert_row(row);
+    list.insert_row(row);
 
-    this.enable_command('export', (this.contact_list.rowcount > 0));
+    this.enable_command('export', list.rowcount > 0);
   };
 
   this.init_contact_form = function()
@@ -4090,24 +4181,27 @@ function rcube_webmail()
       this.selectedIndex = 0;
     });
 
+    // enable date pickers on date fields
+    if ($.datepicker && this.env.date_format) {
+      $.datepicker.setDefaults({
+        dateFormat: this.env.date_format,
+        changeMonth: true,
+        changeYear: true,
+        yearRange: '-100:+10',
+        showOtherMonths: true,
+        selectOtherMonths: true,
+        monthNamesShort: this.env.month_names,
+        onSelect: function(dateText) { $(this).focus().val(dateText) }
+      });
+      $('input.datepicker').datepicker();
+    }
+
     $("input[type='text']:visible").first().focus();
   };
 
   this.group_create = function()
   {
-    if (!this.gui_objects.folderlist)
-      return;
-
-    if (!this.name_input) {
-      this.name_input = $('<input>').attr('type', 'text');
-      this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); });
-      this.name_input_li = $('<li>').addClass('contactgroup').append(this.name_input);
-
-      var li = this.get_folder_li(this.env.source)
-      this.name_input_li.insertAfter(li);
-    }
-
-    this.name_input.select().focus();
+    this.add_input_row('contactgroup');
   };
 
   this.group_rename = function()
@@ -4153,18 +4247,40 @@ function rcube_webmail()
     this.list_contacts(prop.source, 0);
   };
 
+  // @TODO: maybe it would be better to use popup instead of inserting input to the list?
+  this.add_input_row = function(type)
+  {
+    if (!this.gui_objects.folderlist)
+      return;
+
+    if (!this.name_input) {
+      this.name_input = $('<input>').attr('type', 'text').data('tt', type);
+      this.name_input.bind('keydown', function(e){ return rcmail.add_input_keydown(e); });
+      this.name_input_li = $('<li>').addClass(type).append(this.name_input);
+
+      var li = type == 'contactsearch' ? $('li:last', this.gui_objects.folderlist) : this.get_folder_li(this.env.source);
+      this.name_input_li.insertAfter(li);
+    }
+
+    this.name_input.select().focus();
+  };
+
   // handler for keyboard events on the input field
   this.add_input_keydown = function(e)
   {
-    var key = rcube_event.get_keycode(e);
+    var key = rcube_event.get_keycode(e),
+      input = $(e.target), itype = input.data('tt');
 
     // enter
     if (key == 13) {
-      var newname = this.name_input.val();
+      var newname = input.val();
 
       if (newname) {
         var lock = this.set_busy(true, 'loading');
-        if (this.env.group_renaming)
+
+        if (itype == 'contactsearch')
+          this.http_post('search-create', '_search='+urlencode(this.env.search_request)+'&_name='+urlencode(newname), lock);
+        else if (this.env.group_renaming)
           this.http_post('group-rename', '_source='+urlencode(this.env.source)+'&_gid='+urlencode(this.env.group)+'&_name='+urlencode(newname), lock);
         else
           this.http_post('group-create', '_source='+urlencode(this.env.source)+'&_name='+urlencode(newname), lock);
@@ -4209,7 +4325,7 @@ function rcube_webmail()
         .attr('rel', prop.source+':'+prop.id)
         .click(function() { return rcmail.command('listgroup', prop, this); })
         .html(prop.name),
-      li = $('<li>').attr({id: 'rcmli'+key.replace(this.identifier_expr, '_'), 'class': 'contactgroup'})
+      li = $('<li>').attr({id: 'rcmli'+this.html_identifier(key), 'class': 'contactgroup'})
         .append(link);
 
     this.env.contactfolders[key] = this.env.contactgroups[key] = prop;
@@ -4232,7 +4348,7 @@ function rcube_webmail()
       var newkey = 'G'+prop.source+prop.newid,
         newprop = $.extend({}, prop);;
 
-      li.id = String('rcmli'+newkey).replace(this.identifier_expr, '_');
+      li.id = 'rcmli' + this.html_identifier(newkey);
       this.env.contactfolders[newkey] = this.env.contactfolders[key];
       this.env.contactfolders[newkey].id = prop.newid;
       this.env.group = prop.newid;
@@ -4264,7 +4380,7 @@ function rcube_webmail()
   {
     var row, name = prop.name.toUpperCase(),
       sibling = this.get_folder_li(prop.source),
-      prefix = 'rcmliG'+(prop.source).replace(this.identifier_expr, '_');
+      prefix = 'rcmliG' + this.html_identifier(prop.source);
 
     // When renaming groups, we need to remove it from DOM and insert it in the proper place
     if (reloc) {
@@ -4335,6 +4451,9 @@ function rcube_webmail()
             .appendTo(cell);
 
           this.init_edit_field(col, input);
+
+          if (colprop.type == 'date' && $.datepicker)
+            input.datepicker();
         }
         else if (colprop.type == 'composite') {
           var childcol, cp, first, templ, cols = [], suffices = [];
@@ -4480,11 +4599,106 @@ function rcube_webmail()
   // unselect directory/group
   this.unselect_directory = function()
   {
-    if (this.env.address_sources.length > 1 || this.env.group != '') {
-      this.select_folder('', (this.env.group ? 'G'+this.env.source+this.env.group : this.env.source));
-      this.env.group = '';
-      this.env.source = '';
+    this.select_folder('');
+    this.enable_command('search-delete', false);
+  };
+
+  // callback for creating a new saved search record
+  this.insert_saved_search = function(name, id)
+  {
+    this.reset_add_input();
+
+    var key = 'S'+id,
+      link = $('<a>').attr('href', '#')
+        .attr('rel', id)
+        .click(function() { return rcmail.command('listsearch', id, this); })
+        .html(name),
+      li = $('<li>').attr({id: 'rcmli' + this.html_identifier(key), 'class': 'contactsearch'})
+        .append(link),
+      prop = {name:name, id:id, li:li[0]};
+
+    this.add_saved_search_row(prop, li);
+    this.select_folder('S'+id);
+    this.enable_command('search-delete', true);
+    this.env.search_id = id;
+
+    this.triggerEvent('abook_search_insert', prop);
+  };
+
+  // add saved search row to the list, with sorting
+  this.add_saved_search_row = function(prop, li, reloc)
+  {
+    var row, sibling, name = prop.name.toUpperCase();
+
+    // When renaming groups, we need to remove it from DOM and insert it in the proper place
+    if (reloc) {
+      row = li.clone(true);
+      li.remove();
     }
+    else
+      row = li;
+
+    $('li[class~="contactsearch"]', this.gui_objects.folderlist).each(function(i, elem) {
+      if (!sibling)
+        sibling = this.previousSibling;
+
+      if (name >= $(this).text().toUpperCase())
+        sibling = elem;
+      else
+        return false;
+    });
+
+    if (sibling)
+      row.insertAfter(sibling);
+    else
+      row.appendTo(this.gui_objects.folderlist);
+  };
+
+  // creates an input for saved search name
+  this.search_create = function()
+  {
+    this.add_input_row('contactsearch');
+  };
+
+  this.search_delete = function()
+  {
+    if (this.env.search_request) {
+      var lock = this.set_busy(true, 'savedsearchdeleting');
+      this.http_post('search-delete', '_sid='+urlencode(this.env.search_id), lock);
+    }
+  };
+
+  // callback from server upon search-delete command
+  this.remove_search_item = function(id)
+  {
+    var li, key = 'S'+id;
+    if ((li = this.get_folder_li(key))) {
+      this.triggerEvent('search_delete', { id:id, li:li });
+
+      li.parentNode.removeChild(li);
+    }
+
+    this.env.search_id = null;
+    this.env.search_request = null;
+    this.list_contacts_clear();
+    this.reset_qsearch();
+    this.enable_command('search-delete', 'search-create', false);
+  };
+
+  this.listsearch = function(id)
+  {
+    var folder, lock = this.set_busy(true, 'searching');
+
+    if (this.contact_list) {
+      this.list_contacts_clear();
+    }
+
+    this.reset_qsearch();
+    this.select_folder('S'+id);
+
+    // reset vars
+    this.env.current_page = 1;
+    this.http_request('search', '_sid='+urlencode(id), lock);
   };
 
 
@@ -4970,17 +5184,18 @@ function rcube_webmail()
         init_button(cmd, this.buttons[cmd][i]);
       }
     }
+
+    // set active task button
+    this.set_button(this.task, 'sel');
   };
 
   // set button to a specific state
   this.set_button = function(command, state)
   {
-    var button, obj, a_buttons = this.buttons[command];
+    var n, button, obj, a_buttons = this.buttons[command],
+      len = a_buttons ? a_buttons.length : 0;
 
-    if (!a_buttons || !a_buttons.length)
-      return false;
-
-    for (var n=0; n<a_buttons.length; n++) {
+    for (n=0; n<len; n++) {
       button = a_buttons[n];
       obj = document.getElementById(button.id);
 
@@ -5015,15 +5230,14 @@ function rcube_webmail()
   // display a specific alttext
   this.set_alttext = function(command, label)
   {
-    if (!this.buttons[command] || !this.buttons[command].length)
-      return;
+    var n, button, obj, link, a_buttons = this.buttons[command],
+      len = a_buttons ? a_buttons.length : 0;
 
-    var button, obj, link;
-    for (var n=0; n<this.buttons[command].length; n++) {
-      button = this.buttons[command][n];
+    for (n=0; n<len; n++) {
+      button = a_buttons[n];
       obj = document.getElementById(button.id);
 
-      if (button.type=='image' && obj) {
+      if (button.type == 'image' && obj) {
         obj.setAttribute('alt', this.get_label(label));
         if ((link = obj.parentNode) && link.tagName.toLowerCase() == 'a')
           link.setAttribute('title', this.get_label(label));
@@ -5036,20 +5250,18 @@ function rcube_webmail()
   // mouse over button
   this.button_over = function(command, id)
   {
-    var button, elm, a_buttons = this.buttons[command];
-
-    if (!a_buttons || !a_buttons.length)
-      return false;
+    var n, button, obj, a_buttons = this.buttons[command],
+      len = a_buttons ? a_buttons.length : 0;
 
-    for (var n=0; n<a_buttons.length; n++) {
+    for (n=0; n<len; n++) {
       button = a_buttons[n];
       if (button.id == id && button.status == 'act') {
-        elm = document.getElementById(button.id);
-        if (elm && button.over) {
+        obj = document.getElementById(button.id);
+        if (obj && button.over) {
           if (button.type == 'image')
-            elm.src = button.over;
+            obj.src = button.over;
           else
-            elm.className = button.over;
+            obj.className = button.over;
         }
       }
     }
@@ -5058,20 +5270,18 @@ function rcube_webmail()
   // mouse down on button
   this.button_sel = function(command, id)
   {
-    var button, elm, a_buttons = this.buttons[command];
-
-    if (!a_buttons || !a_buttons.length)
-      return;
+    var n, button, obj, a_buttons = this.buttons[command],
+      len = a_buttons ? a_buttons.length : 0;
 
-    for (var n=0; n<a_buttons.length; n++) {
+    for (n=0; n<len; n++) {
       button = a_buttons[n];
       if (button.id == id && button.status == 'act') {
-        elm = document.getElementById(button.id);
-        if (elm && button.sel) {
+        obj = document.getElementById(button.id);
+        if (obj && button.sel) {
           if (button.type == 'image')
-            elm.src = button.sel;
+            obj.src = button.sel;
           else
-            elm.className = button.sel;
+            obj.className = button.sel;
         }
         this.buttons_sel[id] = command;
       }
@@ -5081,26 +5291,23 @@ function rcube_webmail()
   // mouse out of button
   this.button_out = function(command, id)
   {
-    var button, elm, a_buttons = this.buttons[command];
-
-    if (!a_buttons || !a_buttons.length)
-      return;
+    var n, button, obj, a_buttons = this.buttons[command],
+      len = a_buttons ? a_buttons.length : 0;
 
-    for (var n=0; n<a_buttons.length; n++) {
+    for (n=0; n<len; n++) {
       button = a_buttons[n];
       if (button.id == id && button.status == 'act') {
-        elm = document.getElementById(button.id);
-        if (elm && button.act) {
+        obj = document.getElementById(button.id);
+        if (obj && button.act) {
           if (button.type == 'image')
-            elm.src = button.act;
+            obj.src = button.act;
           else
-            elm.className = button.act;
+            obj.className = button.act;
         }
       }
     }
   };
 
-
   this.focus_textfield = function(elem)
   {
     elem._hasfocus = true;
@@ -5134,14 +5341,14 @@ function rcube_webmail()
     if (!this.gui_objects.message) {
       // save message in order to display after page loaded
       if (type != 'loading')
-        this.pending_message = new Array(msg, type, timeout);
+        this.pending_message = [msg, type, timeout];
       return false;
     }
 
     type = type ? type : 'notice';
 
     var ref = this,
-      key = String(msg).replace(this.identifier_expr, '_'),
+      key = this.html_identifier(msg),
       date = new Date(),
       id = type + date.getTime();
 
@@ -5234,31 +5441,31 @@ function rcube_webmail()
   };
 
   // mark a mailbox as selected and set environment variable
-  this.select_folder = function(name, old, prefix)
+  this.select_folder = function(name, prefix, encode)
   {
     if (this.gui_objects.folderlist) {
       var current_li, target_li;
 
-      if ((current_li = this.get_folder_li(old, prefix))) {
-        $(current_li).removeClass('selected').addClass('unfocused');
+      if ((current_li = $('li.selected', this.gui_objects.folderlist))) {
+        current_li.removeClass('selected').addClass('unfocused');
       }
-      if ((target_li = this.get_folder_li(name, prefix))) {
+      if ((target_li = this.get_folder_li(name, prefix, encode))) {
         $(target_li).removeClass('unfocused').addClass('selected');
       }
 
       // trigger event hook
-      this.triggerEvent('selectfolder', { folder:name, old:old, prefix:prefix });
+      this.triggerEvent('selectfolder', { folder:name, prefix:prefix });
     }
   };
 
   // helper method to find a folder list item
-  this.get_folder_li = function(name, prefix)
+  this.get_folder_li = function(name, prefix, encode)
   {
     if (!prefix)
       prefix = 'rcmli';
 
     if (this.gui_objects.folderlist) {
-      name = String(name).replace(this.identifier_expr, '_');
+      name = this.html_identifier(name, encode);
       return document.getElementById(prefix+name);
     }
 
@@ -5327,8 +5534,12 @@ function rcube_webmail()
   };
 
   // replace content of row count display
-  this.set_rowcount = function(text)
+  this.set_rowcount = function(text, mbox)
   {
+    // #1487752
+    if (mbox && mbox != this.env.mailbox)
+      return false;
+
     $(this.gui_objects.countdisplay).html(text);
 
     // update page navigation buttons
@@ -5368,7 +5579,7 @@ function rcube_webmail()
   {
     var reg, link, text_obj, item, mycount, childcount, div;
 
-    if (item = this.get_folder_li(mbox)) {
+    if (item = this.get_folder_li(mbox, '', true)) {
       mycount = this.env.unread_counts[mbox] ? this.env.unread_counts[mbox] : 0;
       link = $(item).children('a').eq(0);
       text_obj = link.children('span.unreadcount');
@@ -5380,7 +5591,7 @@ function rcube_webmail()
       if ((div = item.getElementsByTagName('div')[0]) &&
           div.className.match(/collapsed/)) {
         // add children's counters
-        for (var k in this.env.unread_counts) 
+        for (var k in this.env.unread_counts)
           if (k.indexOf(mbox + this.env.delimiter) == 0)
             childcount += this.env.unread_counts[k];
       }
@@ -5421,16 +5632,12 @@ function rcube_webmail()
 
   this.toggle_prefer_html = function(checkbox)
   {
-    var elem;
-    if (elem = document.getElementById('rcmfd_addrbook_show_images'))
-      elem.disabled = !checkbox.checked;
+    $('#rcmfd_show_images').prop('disabled', !checkbox.checked).val(0);
   };
 
   this.toggle_preview_pane = function(checkbox)
   {
-    var elem;
-    if (elem = document.getElementById('rcmfd_preview_pane_mark_read'))
-      elem.disabled = !checkbox.checked;
+    $('#rcmfd_preview_pane_mark_read').prop('disabled', !checkbox.checked);
   };
 
   // display fetched raw headers
@@ -5546,14 +5753,17 @@ function rcube_webmail()
 
     $.ajax({ type: 'POST', url: url, data: htmlText, contentType: 'application/octet-stream',
       error: function(o, status, err) { rcmail.http_error(o, status, err, lock); },
-      success: function(data) { rcmail.set_busy(false, null, lock); $(document.getElementById(id)).val(data); rcmail.log(data); }
+      success: function(data) { rcmail.set_busy(false, null, lock); $('#'+id).val(data); rcmail.log(data); }
     });
   };
 
-  this.plain2html = function(plainText, id)
+  this.plain2html = function(plain, id)
   {
     var lock = this.set_busy(true, 'converting');
-    $(document.getElementById(id)).val('<pre>'+plainText+'</pre>');
+
+    plain = plain.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+    $('#'+id).val(plain ? '<pre>'+plain+'</pre>' : '');
+
     this.set_busy(false, null, lock);
   };
 
@@ -5580,7 +5790,7 @@ function rcube_webmail()
     var base = this.env.comm_path;
 
     // overwrite task name
-    if (query._action.match(/([a-z]+)\/([a-z-_.]+)/)) {
+    if (query._action.match(/([a-z]+)\/([a-z0-9-_.]+)/)) {
       query._action = RegExp.$2;
       base = base.replace(/\_task=[a-z]+/, '_task='+RegExp.$1);
     }
@@ -5794,6 +6004,8 @@ function rcube_webmail()
           this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
 
           if (response.action == 'list' || response.action == 'search') {
+            this.enable_command('search-create', this.env.source == '');
+            this.enable_command('search-delete', this.env.search_id);
             this.update_group_commands();
             this.triggerEvent('listupdate', { folder:this.env.source, rowcount:this.contact_list.rowcount });
           }
@@ -6008,6 +6220,23 @@ rcube_webmail.long_subject_title = function(elem, indent)
   }
 };
 
+rcube_webmail.long_subject_title_ie = function(elem, indent)
+{
+  if (!elem.title) {
+    var $elem = $(elem),
+      txt = $.trim($elem.text()),
+      tmp = $('<span>').text(txt)
+        .css({'position': 'absolute', 'float': 'left', 'visibility': 'hidden',
+          'font-size': $elem.css('font-size'), 'font-weight': $elem.css('font-weight')})
+        .appendTo($('body')),
+      w = tmp.width();
+
+    tmp.remove();
+    if (w + indent * 15 > $elem.width())
+      elem.title = txt;
+  }
+};
+
 // copy event engine prototype
 rcube_webmail.prototype.addEventListener = rcube_event_engine.prototype.addEventListener;
 rcube_webmail.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener;
index a9b6f53addf81b5081fe2a11775a8c94066671aa..7d1f173396dddddfc6cdb60f3c2e5ae52c768e5c 100644 (file)
@@ -1,25 +1,28 @@
 var CONTROL_KEY=1,SHIFT_KEY=2,CONTROL_SHIFT_KEY=3;
-function roundcube_browser(){var a=navigator;this.ver=parseFloat(a.appVersion);this.appver=a.appVersion;this.agent=a.userAgent;this.agent_lc=a.userAgent.toLowerCase();this.name=a.appName;this.vendor=a.vendor?a.vendor:"";this.vendver=a.vendorSub?parseFloat(a.vendorSub):0;this.product=a.product?a.product:"";this.platform=String(a.platform).toLowerCase();this.lang=a.language?a.language.substring(0,2):a.browserLanguage?a.browserLanguage.substring(0,2):a.systemLanguage?a.systemLanguage.substring(0,2):
-"en";this.win=this.platform.indexOf("win")>=0;this.mac=this.platform.indexOf("mac")>=0;this.linux=this.platform.indexOf("linux")>=0;this.unix=this.platform.indexOf("unix")>=0;this.dom=document.getElementById?!0:!1;this.dom2=document.addEventListener&&document.removeEventListener;this.ie4=(this.ie=document.all&&!window.opera)&&!this.dom;this.ie5=this.dom&&this.appver.indexOf("MSIE 5")>0;this.ie8=this.dom&&this.appver.indexOf("MSIE 8")>0;this.ie7=this.dom&&this.appver.indexOf("MSIE 7")>0;this.ie6=this.dom&&
-!this.ie8&&!this.ie7&&this.appver.indexOf("MSIE 6")>0;this.ns=this.ver<5&&this.name=="Netscape"||this.ver>=5&&this.vendor.indexOf("Netscape")>=0;this.chrome=this.agent_lc.indexOf("chrome")>0;this.safari=!this.chrome&&(this.agent_lc.indexOf("safari")>0||this.agent_lc.indexOf("applewebkit")>0);this.mz=this.dom&&!this.ie&&!this.ns&&!this.chrome&&!this.safari&&this.agent.indexOf("Mozilla")>=0;this.konq=this.agent_lc.indexOf("konqueror")>0;this.iphone=this.safari&&this.agent_lc.indexOf("iphone")>0;this.ipad=
-this.safari&&this.agent_lc.indexOf("ipad")>0;if((this.opera=window.opera?!0:!1)&&window.RegExp)this.vendver=/opera(\s|\/)([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$2):-1;else if(this.chrome&&window.RegExp)this.vendver=/chrome\/([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$1):0;else if(!this.vendver&&this.safari)this.vendver=/(safari|applewebkit)\/([0-9]+)/.test(this.agent_lc)?parseInt(RegExp.$2):0;else if(!this.vendver&&this.mz||this.agent.indexOf("Camino")>0)this.vendver=/rv:([0-9\.]+)/.test(this.agent)?
-parseFloat(RegExp.$1):0;else if(this.ie&&window.RegExp)this.vendver=/msie\s+([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$1):0;else if(this.konq&&window.RegExp)this.vendver=/khtml\/([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$1):0;if(this.safari&&/;\s+([a-z]{2})-[a-z]{2}\)/.test(this.agent_lc))this.lang=RegExp.$1;this.dhtml=this.ie4&&this.win||this.ie5||this.ie6||this.ns4||this.mz;this.vml=this.win&&this.ie&&this.dom&&!this.opera;this.pngalpha=this.mz||this.opera&&this.vendver>=6||this.ie&&
-this.mac&&this.vendver>=5||this.ie&&this.win&&this.vendver>=5.5||this.safari;this.opacity=this.mz||this.ie&&this.vendver>=5.5&&!this.opera||this.safari&&this.vendver>=100;this.cookies=a.cookieEnabled;this.xmlhttp_test=function(){var a=new Function("try{var o=new ActiveXObject('Microsoft.XMLHTTP');return true;}catch(err){return false;}");return this.xmlhttp=window.XMLHttpRequest||window.ActiveXObject&&a()};this.set_html_class=function(){var a=" js";this.ie?(a+=" ie",this.ie5?a+=" ie5":this.ie6?a+=
+function roundcube_browser(){var a=navigator;this.ver=parseFloat(a.appVersion);this.appver=a.appVersion;this.agent=a.userAgent;this.agent_lc=a.userAgent.toLowerCase();this.name=a.appName;this.vendor=a.vendor?a.vendor:"";this.vendver=a.vendorSub?parseFloat(a.vendorSub):0;this.product=a.product?a.product:"";this.platform=(""+a.platform).toLowerCase();this.lang=a.language?a.language.substring(0,2):a.browserLanguage?a.browserLanguage.substring(0,2):a.systemLanguage?a.systemLanguage.substring(0,2):"en";
+this.win=0<=this.platform.indexOf("win");this.mac=0<=this.platform.indexOf("mac");this.linux=0<=this.platform.indexOf("linux");this.unix=0<=this.platform.indexOf("unix");this.dom=document.getElementById?!0:!1;this.dom2=document.addEventListener&&document.removeEventListener;this.ie4=(this.ie=document.all&&!window.opera)&&!this.dom;this.ie5=this.dom&&0<this.appver.indexOf("MSIE 5");this.ie8=this.dom&&0<this.appver.indexOf("MSIE 8");this.ie7=this.dom&&0<this.appver.indexOf("MSIE 7");this.ie6=this.dom&&
+!this.ie8&&!this.ie7&&0<this.appver.indexOf("MSIE 6");this.ns=5>this.ver&&"Netscape"==this.name||5<=this.ver&&0<=this.vendor.indexOf("Netscape");this.chrome=0<this.agent_lc.indexOf("chrome");this.safari=!this.chrome&&(0<this.agent_lc.indexOf("safari")||0<this.agent_lc.indexOf("applewebkit"));this.mz=this.dom&&!this.ie&&!this.ns&&!this.chrome&&!this.safari&&0<=this.agent.indexOf("Mozilla");this.konq=0<this.agent_lc.indexOf("konqueror");this.iphone=this.safari&&0<this.agent_lc.indexOf("iphone");this.ipad=
+this.safari&&0<this.agent_lc.indexOf("ipad");if((this.opera=window.opera?!0:!1)&&window.RegExp)this.vendver=/opera(\s|\/)([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$2):-1;else if(this.chrome&&window.RegExp)this.vendver=/chrome\/([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$1):0;else if(!this.vendver&&this.safari)this.vendver=/(safari|applewebkit)\/([0-9]+)/.test(this.agent_lc)?parseInt(RegExp.$2):0;else if(!this.vendver&&this.mz||0<this.agent.indexOf("Camino"))this.vendver=/rv:([0-9\.]+)/.test(this.agent)?
+parseFloat(RegExp.$1):0;else if(this.ie&&window.RegExp)this.vendver=/msie\s+([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$1):0;else if(this.konq&&window.RegExp)this.vendver=/khtml\/([0-9\.]+)/.test(this.agent_lc)?parseFloat(RegExp.$1):0;if(this.safari&&/;\s+([a-z]{2})-[a-z]{2}\)/.test(this.agent_lc))this.lang=RegExp.$1;this.dhtml=this.ie4&&this.win||this.ie5||this.ie6||this.ns4||this.mz;this.vml=this.win&&this.ie&&this.dom&&!this.opera;this.pngalpha=this.mz||this.opera&&6<=this.vendver||this.ie&&
+this.mac&&5<=this.vendver||this.ie&&this.win&&5.5<=this.vendver||this.safari;this.opacity=this.mz||this.ie&&5.5<=this.vendver&&!this.opera||this.safari&&100<=this.vendver;this.cookies=a.cookieEnabled;this.xmlhttp_test=function(){var a=new Function("try{var o=new ActiveXObject('Microsoft.XMLHTTP');return true;}catch(err){return false;}");return this.xmlhttp=window.XMLHttpRequest||window.ActiveXObject&&a()};this.set_html_class=function(){var a=" js";this.ie?(a+=" ie",this.ie5?a+=" ie5":this.ie6?a+=
 " ie6":this.ie7?a+=" ie7":this.ie8&&(a+=" ie8")):this.opera?a+=" opera":this.konq?a+=" konqueror":this.safari&&(a+=" safari");this.chrome?a+=" chrome":this.iphone?a+=" iphone":this.ipad&&(a+=" ipad");document.documentElement&&(document.documentElement.className+=a)}}
-var rcube_event={get_target:function(a){return(a=a||window.event)&&a.target?a.target:a.srcElement},get_keycode:function(a){return(a=a||window.event)&&a.keyCode?a.keyCode:a&&a.which?a.which:0},get_button:function(a){return(a=a||window.event)&&a.button!==void 0?a.button:a&&a.which?a.which:0},get_modifier:function(a){var b=0,a=a||window.event;if(bw.mac&&a)return b+=(a.metaKey&&CONTROL_KEY)+(a.shiftKey&&SHIFT_KEY),b;if(a)return b+=(a.ctrlKey&&CONTROL_KEY)+(a.shiftKey&&SHIFT_KEY),b},get_mouse_pos:function(a){if(!a)a=
-window.event;var b=a.pageX?a.pageX:a.clientX,c=a.pageY?a.pageY:a.clientY;document.body&&document.all&&(b+=document.body.scrollLeft,c+=document.body.scrollTop);a._offset&&(b+=a._offset.left,c+=a._offset.top);return{x:b,y:c}},add_listener:function(a){if(a.object&&a.method){if(!a.element)a.element=document;if(!a.object._rc_events)a.object._rc_events=[];var b=a.event+"*"+a.method;a.object._rc_events[b]||(a.object._rc_events[b]=function(c){return a.object[a.method](c)});a.element.addEventListener?a.element.addEventListener(a.event,
+var rcube_event={get_target:function(a){return(a=a||window.event)&&a.target?a.target:a.srcElement},get_keycode:function(a){return(a=a||window.event)&&a.keyCode?a.keyCode:a&&a.which?a.which:0},get_button:function(a){return(a=a||window.event)&&void 0!==a.button?a.button:a&&a.which?a.which:0},get_modifier:function(a){var b=0,a=a||window.event;bw.mac&&a?b+=(a.metaKey&&CONTROL_KEY)+(a.shiftKey&&SHIFT_KEY):a&&(b+=(a.ctrlKey&&CONTROL_KEY)+(a.shiftKey&&SHIFT_KEY));return b},get_mouse_pos:function(a){if(!a)a=
+window.event;var b=a.pageX?a.pageX:a.clientX,c=a.pageY?a.pageY:a.clientY;document.body&&document.all&&(b+=document.body.scrollLeft,c+=document.body.scrollTop);a._offset&&(b+=a._offset.left,c+=a._offset.top);return{x:b,y:c}},add_listener:function(a){if(a.object&&a.method){if(!a.element)a.element=document;if(!a.object._rc_events)a.object._rc_events=[];var b=a.event+"*"+a.method;a.object._rc_events[b]||(a.object._rc_events[b]=function(b){return a.object[a.method](b)});a.element.addEventListener?a.element.addEventListener(a.event,
 a.object._rc_events[b],!1):a.element.attachEvent?(a.element.detachEvent("on"+a.event,a.object._rc_events[b]),a.element.attachEvent("on"+a.event,a.object._rc_events[b])):a.element["on"+a.event]=a.object._rc_events[b]}},remove_listener:function(a){if(!a.element)a.element=document;var b=a.event+"*"+a.method;a.object&&a.object._rc_events&&a.object._rc_events[b]&&(a.element.removeEventListener?a.element.removeEventListener(a.event,a.object._rc_events[b],!1):a.element.detachEvent?a.element.detachEvent("on"+
 a.event,a.object._rc_events[b]):a.element["on"+a.event]=null)},cancel:function(a){a=a?a:window.event;a.preventDefault&&a.preventDefault();a.stopPropagation&&a.stopPropagation();a.cancelBubble=!0;return a.returnValue=!1},touchevent:function(a){return{pageX:a.pageX,pageY:a.pageY,offsetX:a.pageX-a.target.offsetLeft,offsetY:a.pageY-a.target.offsetTop,target:a.target,istouch:!0}}};function rcube_event_engine(){this._events={}}
-rcube_event_engine.prototype={addEventListener:function(a,b,c){if(!this._events)this._events={};this._events[a]||(this._events[a]=[]);this._events[a][this._events[a].length]={func:b,obj:c?c:window}},removeEventListener:function(a,b,c){c===void 0&&(c=window);for(var d,e=0;this._events&&this._events[a]&&e<this._events[a].length;e++)if((d=this._events[a][e])&&d.func==b&&d.obj==c)this._events[a][e]=null},triggerEvent:function(a,b){var c,d;if(b===void 0)b=this;else if(typeof b==="object")b.event=a;if(this._events&&
-this._events[a]&&!this._event_exec){this._event_exec=!0;for(var e=0;e<this._events[a].length;e++)if(d=this._events[a][e])if(typeof d.func==="function"?c=d.func.call?d.func.call(d.obj,b):d.func(b):typeof d.obj[d.func]==="function"&&(c=d.obj[d.func](b)),c!==void 0&&!c)break;if(c&&c.event)try{delete c.event}catch(g){$(c).removeAttr("event")}}this._event_exec=!1;if(b.event)try{delete b.event}catch(h){$(b).removeAttr("event")}return c}};
-function rcube_layer(a,b){this.name=a;this.create=function(a){var b=a.x?a.x:0,e=a.y?a.y:0,g=a.width,h=a.height,i=a.zindex,j=a.vis,a=a.parent,f=document.createElement("DIV");f.id=this.name;f.style.position="absolute";f.style.visibility=j?j==2?"inherit":"visible":"hidden";f.style.left=b+"px";f.style.top=e+"px";if(g)f.style.width=g.toString().match(/\%$/)?g:g+"px";if(h)f.style.height=h.toString().match(/\%$/)?h:h+"px";if(i)f.style.zIndex=i;a?a.appendChild(f):document.body.appendChild(f);this.elm=f};
-b!=null?(this.create(b),this.name=this.elm.id):this.elm=document.getElementById(a);if(!this.elm)return!1;this.css=this.elm.style;this.event=this.elm;this.width=this.elm.offsetWidth;this.height=this.elm.offsetHeight;this.x=parseInt(this.elm.offsetLeft);this.y=parseInt(this.elm.offsetTop);this.visible=this.css.visibility=="visible"||this.css.visibility=="show"||this.css.visibility=="inherit"?!0:!1;this.move=function(a,b){this.x=a;this.y=b;this.css.left=Math.round(this.x)+"px";this.css.top=Math.round(this.y)+
-"px"};this.resize=function(a,b){this.css.width=a+"px";this.css.height=b+"px";this.width=a;this.height=b};this.show=function(a){a==1?(this.css.visibility="visible",this.visible=!0):a==2?(this.css.visibility="inherit",this.visible=!0):(this.css.visibility="hidden",this.visible=!1)};this.write=function(a){this.elm.innerHTML=a}}
+rcube_event_engine.prototype={addEventListener:function(a,b,c){if(!this._events)this._events={};this._events[a]||(this._events[a]=[]);this._events[a][this._events[a].length]={func:b,obj:c?c:window}},removeEventListener:function(a,b,c){void 0===c&&(c=window);for(var d,e=0;this._events&&this._events[a]&&e<this._events[a].length;e++)if((d=this._events[a][e])&&d.func==b&&d.obj==c)this._events[a][e]=null},triggerEvent:function(a,b){var c,d;if(void 0===b)b=this;else if("object"===typeof b)b.event=a;if(this._events&&
+this._events[a]&&!this._event_exec){this._event_exec=!0;for(var e=0;e<this._events[a].length;e++)if(d=this._events[a][e])if("function"===typeof d.func?c=d.func.call?d.func.call(d.obj,b):d.func(b):"function"===typeof d.obj[d.func]&&(c=d.obj[d.func](b)),void 0!==c&&!c)break;if(c&&c.event)try{delete c.event}catch(f){$(c).removeAttr("event")}}this._event_exec=!1;if(b.event)try{delete b.event}catch(h){$(b).removeAttr("event")}return c}};
+function rcube_layer(a,b){this.name=a;this.create=function(a){var b=a.x?a.x:0,e=a.y?a.y:0,f=a.width,h=a.height,i=a.zindex,g=a.vis,a=a.parent,j=document.createElement("DIV");j.id=this.name;j.style.position="absolute";j.style.visibility=g?2==g?"inherit":"visible":"hidden";j.style.left=b+"px";j.style.top=e+"px";if(f)j.style.width=f.toString().match(/\%$/)?f:f+"px";if(h)j.style.height=h.toString().match(/\%$/)?h:h+"px";if(i)j.style.zIndex=i;a?a.appendChild(j):document.body.appendChild(j);this.elm=j};
+null!=b?(this.create(b),this.name=this.elm.id):this.elm=document.getElementById(a);if(!this.elm)return!1;this.css=this.elm.style;this.event=this.elm;this.width=this.elm.offsetWidth;this.height=this.elm.offsetHeight;this.x=parseInt(this.elm.offsetLeft);this.y=parseInt(this.elm.offsetTop);this.visible="visible"==this.css.visibility||"show"==this.css.visibility||"inherit"==this.css.visibility?!0:!1;this.move=function(a,b){this.x=a;this.y=b;this.css.left=Math.round(this.x)+"px";this.css.top=Math.round(this.y)+
+"px"};this.resize=function(a,b){this.css.width=a+"px";this.css.height=b+"px";this.width=a;this.height=b};this.show=function(a){1==a?(this.css.visibility="visible",this.visible=!0):2==a?(this.css.visibility="inherit",this.visible=!0):(this.css.visibility="hidden",this.visible=!1)};this.write=function(a){this.elm.innerHTML=a}}
 function rcube_check_email(a,b){return a&&window.RegExp?(b?RegExp("(^|<|[,;s\n])((([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22)(\\x2e([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22))*\\x40([^@\\x2e]+\\x2e)+([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))|(mailtest\\x40(\\u0645\\u062b\\u0627\\u0644\\x2e\\u0625\\u062e\\u062a\\u0628\\u0627\\u0631|\\u4f8b\\u5b50\\x2e\\u6d4b\\u8bd5|\\u4f8b\\u5b50\\x2e\\u6e2c\\u8a66|\\u03c0\\u03b1\\u03c1\\u03ac\\u03b4\\u03b5\\u03b9\\u03b3\\u03bc\\u03b1\\x2e\\u03b4\\u03bf\\u03ba\\u03b9\\u03bc\\u03ae|\\u0909\\u0926\\u093e\\u0939\\u0930\\u0923\\x2e\\u092a\\u0930\\u0940\\u0915\\u094d\\u0937\\u093e|\\u4f8b\\u3048\\x2e\\u30c6\\u30b9\\u30c8|\\uc2e4\\ub840\\x2e\\ud14c\\uc2a4\\ud2b8|\\u0645\\u062b\\u0627\\u0644\\x2e\\u0622\\u0632\\u0645\\u0627\\u06cc\\u0634\u06cc|\\u043f\\u0440\\u0438\\u043c\\u0435\\u0440\\x2e\\u0438\\u0441\\u043f\\u044b\\u0442\\u0430\\u043d\\u0438\\u0435|\\u0b89\\u0ba4\\u0bbe\\u0bb0\\u0ba3\\u0bae\\u0bcd\\x2e\\u0baa\\u0bb0\\u0bbf\\u0b9f\\u0bcd\\u0b9a\\u0bc8|\\u05d1\\u05f2\\u05b7\\u05e9\\u05e4\\u05bc\\u05d9\\u05dc\\x2e\\u05d8\\u05e2\\u05e1\\u05d8)))($|>|[,;s\n])","i"):
 RegExp("^((([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22)(\\x2e([^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+|\\x22([^\\x0d\\x22\\x5c\\x80-\\xff]|\\x5c[\\x00-\\x7f])*\\x22))*\\x40([^@\\x2e]+\\x2e)+([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))|(mailtest\\x40(\\u0645\\u062b\\u0627\\u0644\\x2e\\u0625\\u062e\\u062a\\u0628\\u0627\\u0631|\\u4f8b\\u5b50\\x2e\\u6d4b\\u8bd5|\\u4f8b\\u5b50\\x2e\\u6e2c\\u8a66|\\u03c0\\u03b1\\u03c1\\u03ac\\u03b4\\u03b5\\u03b9\\u03b3\\u03bc\\u03b1\\x2e\\u03b4\\u03bf\\u03ba\\u03b9\\u03bc\\u03ae|\\u0909\\u0926\\u093e\\u0939\\u0930\\u0923\\x2e\\u092a\\u0930\\u0940\\u0915\\u094d\\u0937\\u093e|\\u4f8b\\u3048\\x2e\\u30c6\\u30b9\\u30c8|\\uc2e4\\ub840\\x2e\\ud14c\\uc2a4\\ud2b8|\\u0645\\u062b\\u0627\\u0644\\x2e\\u0622\\u0632\\u0645\\u0627\\u06cc\\u0634\u06cc|\\u043f\\u0440\\u0438\\u043c\\u0435\\u0440\\x2e\\u0438\\u0441\\u043f\\u044b\\u0442\\u0430\\u043d\\u0438\\u0435|\\u0b89\\u0ba4\\u0bbe\\u0bb0\\u0ba3\\u0bae\\u0bcd\\x2e\\u0baa\\u0bb0\\u0bbf\\u0b9f\\u0bcd\\u0b9a\\u0bc8|\\u05d1\\u05f2\\u05b7\\u05e9\\u05e4\\u05bc\\u05d9\\u05dc\\x2e\\u05d8\\u05e2\\u05e1\\u05d8)))$",
-"i")).test(a)?!0:!1:!1}function rcube_clone_object(a){var b={},c;for(c in a)b[c]=a[c]&&typeof a[c]==="object"?clone_object(a[c]):a[c];return b}function urlencode(a){return window.encodeURIComponent?encodeURIComponent(a):escape(a)}
+"i")).test(a)?!0:!1:!1}function rcube_clone_object(a){var b={},c;for(c in a)b[c]=a[c]&&"object"===typeof a[c]?clone_object(a[c]):a[c];return b}function urlencode(a){return window.encodeURIComponent?encodeURIComponent(a).replace("*","%2A"):escape(a).replace("+","%2B").replace("*","%2A").replace("/","%2F").replace("@","%40")}
 function rcube_find_object(a,b){var c,d;b||(b=document);if(b.getElementsByName&&(c=b.getElementsByName(a)))d=c[0];!d&&b.getElementById&&(d=b.getElementById(a));!d&&b.all&&(d=b.all[a]);!d&&b.images.length&&(d=b.images[a]);if(!d&&b.forms.length)for(c=0;c<b.forms.length;c++)b.forms[c].name==a?d=b.forms[c]:b.forms[c].elements[a]&&(d=b.forms[c].elements[a]);if(!d&&b.layers){b.layers[a]&&(d=b.layers[a]);for(c=0;!d&&c<b.layers.length;c++)d=rcube_find_object(a,b.layers[c].document)}return d}
-function rcube_mouse_is_over(a,b){var c=rcube_event.get_mouse_pos(a),d=$(b).offset();return c.x>=d.left&&c.x<d.left+b.offsetWidth&&c.y>=d.top&&c.y<d.top+b.offsetHeight}function setCookie(a,b,c,d,e,g){a=a+"="+escape(b)+(c?"; expires="+c.toGMTString():"")+(d?"; path="+d:"")+(e?"; domain="+e:"")+(g?"; secure":"");document.cookie=a}
-function getCookie(a){var b=document.cookie;a+="=";var c=b.indexOf("; "+a);if(c==-1){if(c=b.indexOf(a),c!=0)return null}else c+=2;var d=b.indexOf(";",c);if(d==-1)d=b.length;return unescape(b.substring(c+a.length,d))}roundcube_browser.prototype.set_cookie=setCookie;roundcube_browser.prototype.get_cookie=getCookie;
-function rcube_console(){this.log=function(a){var b=rcube_find_object("dbgconsole");if(b)a+=a.charAt(a.length-1)=="\n"?"--------------------------------------\n":"\n--------------------------------------\n",bw.konq?(b.innerText+=a,b.value=b.innerText):b.value+=a};this.reset=function(){var a=rcube_find_object("dbgconsole");if(a)a.innerText=a.value=""}}var bw=new roundcube_browser;bw.set_html_class();RegExp.escape=function(a){return String(a).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")};
-if(bw.ie)document._getElementById=document.getElementById,document.getElementById=function(a){var b=0,c=document._getElementById(a);if(c&&c.id!=a)for(;(c=document.all[b])&&c.id!=a;)b++;return c};
+function rcube_mouse_is_over(a,b){var c=rcube_event.get_mouse_pos(a),d=$(b).offset();return c.x>=d.left&&c.x<d.left+b.offsetWidth&&c.y>=d.top&&c.y<d.top+b.offsetHeight}function setCookie(a,b,c,d,e,f){a=a+"="+escape(b)+(c?"; expires="+c.toGMTString():"")+(d?"; path="+d:"")+(e?"; domain="+e:"")+(f?"; secure":"");document.cookie=a}
+function getCookie(a){var b=document.cookie,a=a+"=",c=b.indexOf("; "+a);if(-1==c){if(c=b.indexOf(a),0!=c)return null}else c+=2;var d=b.indexOf(";",c);if(-1==d)d=b.length;return unescape(b.substring(c+a.length,d))}roundcube_browser.prototype.set_cookie=setCookie;roundcube_browser.prototype.get_cookie=getCookie;
+function rcube_console(){this.log=function(a){var b=rcube_find_object("dbgconsole");if(b)a="\n"==a.charAt(a.length-1)?a+"--------------------------------------\n":a+"\n--------------------------------------\n",bw.konq?(b.innerText+=a,b.value=b.innerText):b.value+=a};this.reset=function(){var a=rcube_find_object("dbgconsole");if(a)a.innerText=a.value=""}}var bw=new roundcube_browser;bw.set_html_class();RegExp.escape=function(a){return(""+a).replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")};
+Date.prototype.getStdTimezoneOffset=function(){for(var a=12,b=new Date(null,a,1),c=b.getTimezoneOffset();--a;)if(b.setUTCMonth(a),c!=b.getTimezoneOffset())return Math.max(c,b.getTimezoneOffset());return c};if(bw.ie)document._getElementById=document.getElementById,document.getElementById=function(a){var b=0,c=document._getElementById(a);if(c&&c.id!=a)for(;(c=document.all[b])&&c.id!=a;)b++;return c};
+var Base64=function(){return{encode:function(a){if("function"===typeof window.btoa)return btoa(a);var b,c,d,e,f,h,i=0,g="",j=a.length;do b=a.charCodeAt(i++),c=a.charCodeAt(i++),d=a.charCodeAt(i++),e=b>>2,b=(b&3)<<4|c>>4,f=(c&15)<<2|d>>6,h=d&63,isNaN(c)?f=h=64:isNaN(d)&&(h=64),g=g+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(e)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(b)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(f)+
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(h);while(i<j);return g},decode:function(a){if("function"===typeof window.atob)return atob(a);var b,c,d,e,f,h,i=0,g="",a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");h=a.length;do b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(i++)),c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(i++)),e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(i++)),
+f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(i++)),b=b<<2|c>>4,c=(c&15)<<4|e>>2,d=(e&3)<<6|f,g+=String.fromCharCode(b),64!=e&&(g+=String.fromCharCode(c)),64!=f&&(g+=String.fromCharCode(d));while(i<h);return g}}}();
index 4a9b9aab9ecf5e744369d718d4edd9000d6a3447..d2558ce88f0fa3e77ce4e4abaa130e4a217a3c19 100644 (file)
@@ -10,7 +10,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
  
- $Id: common.js 5281 2011-09-27 07:29:49Z alec $
+ $Id: common.js 5481 2011-11-24 07:53:00Z alec $
 */
 
 // Constants
@@ -171,14 +171,12 @@ get_modifier: function(e)
   var opcode = 0;
   e = e || window.event;
 
-  if (bw.mac && e) {
+  if (bw.mac && e)
     opcode += (e.metaKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
-    return opcode;
-  }
-  if (e) {
+  else if (e)
     opcode += (e.ctrlKey && CONTROL_KEY) + (e.shiftKey && SHIFT_KEY);
-    return opcode;
-  }
+
+  return opcode;
 },
 
 /**
@@ -544,10 +542,17 @@ function rcube_clone_object(obj)
   return out;
 };
 
-// make a string URL safe
+// make a string URL safe (and compatible with PHP's rawurlencode())
 function urlencode(str)
 {
-  return window.encodeURIComponent ? encodeURIComponent(str) : escape(str);
+  if (window.encodeURIComponent)
+    return encodeURIComponent(str).replace('*', '%2A');
+
+  return escape(str)
+    .replace('+', '%2B')
+    .replace('*', '%2A')
+    .replace('/', '%2F')
+    .replace('@', '%40');
 };
 
 
@@ -673,6 +678,23 @@ RegExp.escape = function(str)
   return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
 };
 
+// Extend Date prototype to detect Standard timezone without DST
+// from http://www.michaelapproved.com/articles/timezone-detect-and-ignore-daylight-saving-time-dst/
+Date.prototype.getStdTimezoneOffset = function()
+{
+  var m = 12,
+    d = new Date(null, m, 1),
+    tzo = d.getTimezoneOffset();
+
+    while (--m) {
+      d.setUTCMonth(m);
+      if (tzo != d.getTimezoneOffset()) {
+        return Math.max(tzo, d.getTimezoneOffset());
+    }
+  }
+
+  return tzo;
+}
 
 // Make getElementById() case-sensitive on IE
 if (bw.ie)
@@ -689,3 +711,82 @@ if (bw.ie)
     return obj;
   }
 }
+
+// This code was written by Tyler Akins and has been placed in the
+// public domain.  It would be nice if you left this header intact.
+// Base64 code from Tyler Akins -- http://rumkin.com
+var Base64 = (function () {
+  var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+  var obj = {
+    /**
+     * Encodes a string in base64
+     * @param {String} input The string to encode in base64.
+     */
+    encode: function (input) {
+      if (typeof(window.btoa) === 'function')
+        return btoa(input);
+
+      var chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0, output = '', len = input.length;
+
+      do {
+        chr1 = input.charCodeAt(i++);
+        chr2 = input.charCodeAt(i++);
+        chr3 = input.charCodeAt(i++);
+
+        enc1 = chr1 >> 2;
+        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+        enc4 = chr3 & 63;
+
+        if (isNaN(chr2))
+          enc3 = enc4 = 64;
+        else if (isNaN(chr3))
+          enc4 = 64;
+
+        output = output
+          + keyStr.charAt(enc1) + keyStr.charAt(enc2)
+          + keyStr.charAt(enc3) + keyStr.charAt(enc4);
+      } while (i < len);
+
+      return output;
+    },
+
+    /**
+     * Decodes a base64 string.
+     * @param {String} input The string to decode.
+     */
+    decode: function (input) {
+      if (typeof(window.atob) === 'function')
+         return atob(input);
+
+      var chr1, chr2, chr3, enc1, enc2, enc3, enc4, len, i = 0, output = '';
+
+      // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
+      input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+      len = input.length;
+
+      do {
+        enc1 = keyStr.indexOf(input.charAt(i++));
+        enc2 = keyStr.indexOf(input.charAt(i++));
+        enc3 = keyStr.indexOf(input.charAt(i++));
+        enc4 = keyStr.indexOf(input.charAt(i++));
+
+        chr1 = (enc1 << 2) | (enc2 >> 4);
+        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+        chr3 = ((enc3 & 3) << 6) | enc4;
+
+        output = output + String.fromCharCode(chr1);
+
+        if (enc3 != 64)
+          output = output + String.fromCharCode(chr2);
+        if (enc4 != 64)
+          output = output + String.fromCharCode(chr3);
+      } while (i < len);
+
+      return output;
+    }
+  };
+
+  return obj;
+})();
index a3aef72a2f33a56e122f5d7ec504c038e620dd6f..63186fb02e5de3c6e90cfefe82b2a8c9d3537f35 100644 (file)
 */
 
 // Initialize HTML editor
-function rcmail_editor_init(skin_path, editor_lang, spellcheck, mode)
+function rcmail_editor_init(config)
 {
   var ret, conf = {
       mode: 'textareas',
       editor_selector: 'mce_editor',
       apply_source_formatting: true,
       theme: 'advanced',
-      language: editor_lang,
-      content_css: skin_path + '/editor_content.css',
+      language: config.lang,
+      content_css: config.skin_path + '/editor_content.css',
       theme_advanced_toolbar_location: 'top',
       theme_advanced_toolbar_align: 'left',
       theme_advanced_buttons3: '',
@@ -35,7 +35,7 @@ function rcmail_editor_init(skin_path, editor_lang, spellcheck, mode)
       rc_client: rcmail
     };
 
-  if (mode == 'identity')
+  if (config.mode == 'identity')
     $.extend(conf, {
       plugins: 'paste,tabfocus',
       theme_advanced_buttons1: 'bold,italic,underline,strikethrough,justifyleft,justifycenter,justifyright,justifyfull,separator,outdent,indent,charmap,hr,link,unlink,code,forecolor',
@@ -43,11 +43,12 @@ function rcmail_editor_init(skin_path, editor_lang, spellcheck, mode)
     });
   else // mail compose
     $.extend(conf, {
-      plugins: 'paste,emotions,media,nonbreaking,table,searchreplace,visualchars,directionality,tabfocus' + (spellcheck ? ',spellchecker' : ''),
+      plugins: 'paste,emotions,media,nonbreaking,table,searchreplace,visualchars,directionality,tabfocus' + (config.spellcheck ? ',spellchecker' : ''),
       theme_advanced_buttons1: 'bold,italic,underline,|,justifyleft,justifycenter,justifyright,justifyfull,|,bullist,numlist,outdent,indent,ltr,rtl,blockquote,|,forecolor,backcolor,fontselect,fontsizeselect',
-      theme_advanced_buttons2: 'link,unlink,table,|,emotions,charmap,image,media,|,code,search' + (spellcheck ? ',spellchecker' : '') + ',undo,redo',
+      theme_advanced_buttons2: 'link,unlink,table,|,emotions,charmap,image,media,|,code,search' + (config.spellcheck ? ',spellchecker' : '') + ',undo,redo',
       spellchecker_languages: (rcmail.env.spellcheck_langs ? rcmail.env.spellcheck_langs : 'Dansk=da,Deutsch=de,+English=en,Espanol=es,Francais=fr,Italiano=it,Nederlands=nl,Polski=pl,Portugues=pt,Suomi=fi,Svenska=sv'),
       spellchecker_rpc_url: '?_task=utils&_action=spell_html',
+      spellchecker_enable_learn_rpc: config.spelldict,
       accessibility_focus: false,
       oninit: 'rcmail_editor_callback'
     });
@@ -114,13 +115,17 @@ function rcmail_toggle_editor(select, textAreaId, flagElement)
     if (flagElement && (flag = rcube_find_object(flagElement)))
       flag.value = '1';
   }
-  else {
-    if (!res && select.tagName == 'SELECT')
-      select.value = 'html';
+  else if (res) {
     if (flagElement && (flag = rcube_find_object(flagElement)))
       flag.value = '0';
 
     if (rcmail.env.composebody)
       rcube_find_object(rcmail.env.composebody).focus();
   }
+  else { // !res
+    if (select.tagName == 'SELECT')
+      select.value = 'html';
+    else if (select.tagName == 'INPUT')
+      select.checked = true;
+  }
 }
index 411740f01cf9a208e485ecea594638392c0e3c59..3618fe10a3c99e12da6542b720cdfb22995a504e 100644 (file)
@@ -1,38 +1,39 @@
 var GOOGIE_CUR_LANG,GOOGIE_DEFAULT_LANG="en";
-function GoogieSpell(s,t){var l=this,r=getCookie("language");GOOGIE_CUR_LANG=r!=null?r:GOOGIE_DEFAULT_LANG;this.array_keys=function(a){var b=[],c;for(c in a)b.push([c]);return b};this.img_dir=s;this.server_url=t;this.lang_to_word=this.org_lang_to_word={da:"Dansk",de:"Deutsch",en:"English",es:"Espa&#241;ol",fr:"Fran&#231;ais",it:"Italiano",nl:"Nederlands",pl:"Polski",pt:"Portugu&#234;s",fi:"Suomi",sv:"Svenska"};this.langlist_codes=this.array_keys(this.lang_to_word);this.show_change_lang_pic=!0;this.change_lang_pic_placement=
-"right";this.report_state_change=!0;this.el_scroll_top=this.ta_scroll_top=0;this.lang_chck_spell="Check spelling";this.lang_revert="Revert to";this.lang_close="Close";this.lang_rsm_edt="Resume editing";this.lang_no_error_found="No spelling errors found";this.lang_no_suggestions="No suggestions";this.show_spell_img=!1;this.decoration=!0;this.use_close_btn=!1;this.report_ta_not_found=this.edit_layer_dbl_click=!0;this.custom_no_spelling_error=this.custom_ajax_error=null;this.custom_menu_builder=[];this.custom_item_evaulator=
-null;this.extra_menu_items=[];this.custom_spellcheck_starter=null;this.main_controller=!0;this.all_errors_fixed_observer=this.show_menu_observer=this.spelling_state_observer=this.lang_state_observer=null;this.use_focus=!1;this.focus_link_b=this.focus_link_t=null;this.cnt_errors_fixed=this.cnt_errors=0;$(document).bind("click",function(a){a=$(a.target);a.attr("googie_action_btn")!="1"&&l.isLangWindowShown()&&l.hideLangWindow();a.attr("googie_action_btn")!="1"&&l.isErrorWindowShown()&&l.hideErrorWindow()});
-this.decorateTextarea=function(a){if(this.text_area=typeof a==="string"?document.getElementById(a):a){if(!this.spell_container&&this.decoration){var a=document.createElement("table"),b=document.createElement("tbody"),c=document.createElement("tr"),d=document.createElement("td"),e=this.isDefined(this.force_width)?this.force_width:this.text_area.offsetWidth,f=this.isDefined(this.force_height)?this.force_height:16;c.appendChild(d);b.appendChild(c);$(a).append(b).insertBefore(this.text_area).width("100%").height(f);
-$(d).height(f).width(e).css("text-align","right");this.spell_container=d}this.checkSpellingState()}else this.report_ta_not_found&&alert("Text area not found")};this.setSpellContainer=function(a){this.spell_container=typeof a==="string"?document.getElementById(a):a};this.setLanguages=function(a){this.lang_to_word=a;this.langlist_codes=this.array_keys(a)};this.setCurrentLanguage=function(a){GOOGIE_CUR_LANG=a;var b=new Date;b.setTime(b.getTime()+31536E6);setCookie("language",a,b)};this.setForceWidthHeight=
-function(a,b){this.force_width=a;this.force_height=b};this.setDecoration=function(a){this.decoration=a};this.dontUseCloseButtons=function(){this.use_close_btn=!1};this.appendNewMenuItem=function(a,b,c){this.extra_menu_items.push([a,b,c])};this.appendCustomMenuBuilder=function(a,b){this.custom_menu_builder.push([a,b])};this.setFocus=function(){try{return this.focus_link_b.focus(),this.focus_link_t.focus(),!0}catch(a){return!1}};this.setStateChanged=function(a){this.state=a;this.spelling_state_observer!=
-null&&this.report_state_change&&this.spelling_state_observer(a,this)};this.setReportStateChange=function(a){this.report_state_change=a};this.getUrl=function(){return this.server_url+GOOGIE_CUR_LANG};this.escapeSpecial=function(a){return a?a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"):""};this.createXMLReq=function(a){return'<?xml version="1.0" encoding="utf-8" ?><spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1"><text>'+a+"</text></spellrequest>"};
-this.spellCheck=function(a){this.prepare(a);var a=this.escapeSpecial(this.orginal_text),b=this;$.ajax({type:"POST",url:this.getUrl(),data:this.createXMLReq(a),dataType:"text",error:function(){b.custom_ajax_error?b.custom_ajax_error(b):alert("An error was encountered on the server. Please try again later.");b.main_controller&&($(b.spell_span).remove(),b.removeIndicator());b.checkSpellingState()},success:function(a){b.processData(a);b.results.length||(b.custom_no_spelling_error?b.custom_no_spelling_error(b):
-b.flashNoSpellingErrorState());b.removeIndicator()}})};this.prepare=function(a,b){this.cnt_errors=this.cnt_errors_fixed=0;this.setStateChanged("checking_spell");!b&&this.main_controller&&this.appendIndicator(this.spell_span);this.error_links=[];this.ta_scroll_top=this.text_area.scrollTop;this.ignore=a;this.hideLangWindow();if($(this.text_area).val()==""||a)this.custom_no_spelling_error?this.custom_no_spelling_error(this):this.flashNoSpellingErrorState(),this.removeIndicator();else{this.createEditLayer(this.text_area.offsetWidth,
-this.text_area.offsetHeight);this.createErrorWindow();$("body").append(this.error_window);try{netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead")}catch(c){}this.main_controller&&$(this.spell_span).unbind("click");this.orginal_text=$(this.text_area).val()}};this.parseResult=function(a){var b=/\w+="(\d+|true)"/g,c=/\t/g,a=a.match(/<c[^>]*>[^<]*<\/c>/g),d=[];if(a==null)return d;for(var e=0,f=a.length;e<f;e++){var j=[];this.errorFound();j.attrs=[];for(var g,h,l=a[e].match(b),k=
-0;k<l.length;k++)g=l[k].split(/=/),h=g[1].replace(/"/g,""),j.attrs[g[0]]=h!="true"?parseInt(h):h;j.suggestions=[];g=a[e].replace(/<[^>]*>/g,"").split(c);for(h=0;h<g.length;h++)g[h]!=""&&j.suggestions.push(g[h]);d.push(j)}return d};this.processData=function(a){this.results=this.parseResult(a);this.results.length&&(this.showErrorsInIframe(),this.resumeEditingState())};this.createErrorWindow=function(){this.error_window=document.createElement("div");$(this.error_window).addClass("googie_window popupmenu").attr("googie_action_btn",
-"1")};this.isErrorWindowShown=function(){return $(this.error_window).is(":visible")};this.hideErrorWindow=function(){$(this.error_window).hide();$(this.error_window_iframe).hide()};this.updateOrginalText=function(a,b,c,d){var e=this.orginal_text.substring(0,a),a=this.orginal_text.substring(a+b.length),b=c.length-b.length;this.orginal_text=e+c+a;$(this.text_area).val(this.orginal_text);c=0;for(e=this.results.length;c<e;c++)c!=d&&c>d&&(this.results[c].attrs.o+=b)};this.saveOldValue=function(a,b){a.is_changed=
-!0;a.old_value=b};this.createListSeparator=function(){var a=document.createElement("td"),b=document.createElement("tr");$(a).html(" ").attr("googie_action_btn","1").css({cursor:"default","font-size":"3px","border-top":"1px solid #ccc","padding-top":"3px"});b.appendChild(a);return b};this.correctError=function(a,b,c,d){var e=b.innerHTML,c=c.nodeType==3?c.nodeValue:c.innerHTML,f=this.results[a].attrs.o;if(d)d=b.previousSibling.innerHTML,b.previousSibling.innerHTML=d.slice(0,d.length-1),e=" "+e,f--;
-this.hideErrorWindow();this.updateOrginalText(f,e,c,a);$(b).html(c).css("color","green").attr("is_corrected",!0);this.results[a].attrs.l=c.length;this.isDefined(b.old_value)||this.saveOldValue(b,e);this.errorFixed()};this.showErrorWindow=function(a,b){this.show_menu_observer&&this.show_menu_observer(this);var c=this,d=$(a).offset(),e=document.createElement("table"),f=document.createElement("tbody");$(this.error_window).html("");$(e).addClass("googie_list").attr("googie_action_btn","1");for(var j=
-!1,g=0;g<this.custom_menu_builder.length;g++){var h=this.custom_menu_builder[g];if(h[0](this.results[b])){j=h[1](this,f,a);break}}if(!j){var j=this.results[b].suggestions,l=this.results[b].attrs.o,g=this.results[b].attrs.l,k,m;j.length==0&&(h=document.createElement("tr"),k=document.createElement("td"),m=document.createElement("span"),$(m).text(this.lang_no_suggestions),$(k).attr("googie_action_btn","1").css("cursor","default"),k.appendChild(m),h.appendChild(k),f.appendChild(h));for(var o=0,g=j.length;o<
-g;o++)h=document.createElement("tr"),k=document.createElement("td"),m=document.createElement("span"),$(m).html(j[o]),$(k).bind("mouseover",this.item_onmouseover).bind("mouseout",this.item_onmouseout).bind("click",function(d){c.correctError(b,a,d.target.firstChild)}),k.appendChild(m),h.appendChild(k),f.appendChild(h);if(a.is_changed&&a.innerHTML!=a.old_value){var p=a.old_value,j=document.createElement("tr"),g=document.createElement("td"),h=document.createElement("span");$(h).addClass("googie_list_revert").html(this.lang_revert+
-" "+p);$(g).bind("mouseover",this.item_onmouseover).bind("mouseout",this.item_onmouseout).bind("click",function(){c.updateOrginalText(l,a.innerHTML,p,b);$(a).attr("is_corrected",!0).css("color","#b91414").html(p);c.hideErrorWindow()});g.appendChild(h);j.appendChild(g);f.appendChild(j)}var j=document.createElement("tr"),g=document.createElement("td"),n=document.createElement("input"),h=document.createElement("img");k=document.createElement("form");m=function(){n.value!=""&&(c.isDefined(a.old_value)||
-c.saveOldValue(a,a.innerHTML),c.updateOrginalText(l,a.innerHTML,n.value,b),$(a).attr("is_corrected",!0).css("color","green").html(n.value),c.hideErrorWindow());return!1};$(n).width(120).css({margin:0,padding:0});$(n).val(a.innerHTML).attr("googie_action_btn","1");$(g).css("cursor","default").attr("googie_action_btn","1");$(h).attr("src",this.img_dir+"ok.gif").width(32).height(16).css({cursor:"pointer","margin-left":"2px","margin-right":"2px"}).bind("click",m);$(k).attr("googie_action_btn","1").css({margin:0,
-padding:0,cursor:"default","white-space":"nowrap"}).bind("submit",m);k.appendChild(n);k.appendChild(h);g.appendChild(k);j.appendChild(g);f.appendChild(j);this.extra_menu_items.length>0&&f.appendChild(this.createListSeparator());var q=function(b){if(b<c.extra_menu_items.length){var d=c.extra_menu_items[b];if(!d[2]||d[2](a,c)){var e=document.createElement("tr"),g=document.createElement("td");$(g).html(d[0]).bind("mouseover",c.item_onmouseover).bind("mouseout",c.item_onmouseout).bind("click",function(){return d[1](a,
+function GoogieSpell(s,t,u){var l=this,r=getCookie("language");GOOGIE_CUR_LANG=null!=r?r:GOOGIE_DEFAULT_LANG;this.array_keys=function(a){var b=[],c;for(c in a)b.push([c]);return b};this.img_dir=s;this.server_url=t;this.lang_to_word=this.org_lang_to_word={da:"Dansk",de:"Deutsch",en:"English",es:"Espa&#241;ol",fr:"Fran&#231;ais",it:"Italiano",nl:"Nederlands",pl:"Polski",pt:"Portugu&#234;s",fi:"Suomi",sv:"Svenska"};this.langlist_codes=this.array_keys(this.lang_to_word);this.show_change_lang_pic=!0;this.change_lang_pic_placement=
+"right";this.report_state_change=!0;this.el_scroll_top=this.ta_scroll_top=0;this.lang_chck_spell="Check spelling";this.lang_revert="Revert to";this.lang_close="Close";this.lang_rsm_edt="Resume editing";this.lang_no_error_found="No spelling errors found";this.lang_no_suggestions="No suggestions";this.lang_learn_word="Add to dictionary";this.show_spell_img=!1;this.decoration=!0;this.use_close_btn=!1;this.report_ta_not_found=this.edit_layer_dbl_click=!0;this.custom_no_spelling_error=this.custom_ajax_error=
+null;this.custom_menu_builder=[];this.custom_item_evaulator=null;this.extra_menu_items=[];this.custom_spellcheck_starter=null;this.main_controller=!0;this.has_dictionary=u;this.all_errors_fixed_observer=this.show_menu_observer=this.spelling_state_observer=this.lang_state_observer=null;this.use_focus=!1;this.focus_link_b=this.focus_link_t=null;this.cnt_errors_fixed=this.cnt_errors=0;$(document).bind("click",function(a){a=$(a.target);"1"!=a.attr("googie_action_btn")&&l.isLangWindowShown()&&l.hideLangWindow();
+"1"!=a.attr("googie_action_btn")&&l.isErrorWindowShown()&&l.hideErrorWindow()});this.decorateTextarea=function(a){if(this.text_area="string"===typeof a?document.getElementById(a):a){if(!this.spell_container&&this.decoration){var a=document.createElement("table"),b=document.createElement("tbody"),c=document.createElement("tr"),d=document.createElement("td"),e=this.isDefined(this.force_width)?this.force_width:this.text_area.offsetWidth,f=this.isDefined(this.force_height)?this.force_height:16;c.appendChild(d);
+b.appendChild(c);$(a).append(b).insertBefore(this.text_area).width("100%").height(f);$(d).height(f).width(e).css("text-align","right");this.spell_container=d}this.checkSpellingState()}else this.report_ta_not_found&&alert("Text area not found")};this.setSpellContainer=function(a){this.spell_container="string"===typeof a?document.getElementById(a):a};this.setLanguages=function(a){this.lang_to_word=a;this.langlist_codes=this.array_keys(a)};this.setCurrentLanguage=function(a){GOOGIE_CUR_LANG=a;var b=
+new Date;b.setTime(b.getTime()+31536E6);setCookie("language",a,b)};this.setForceWidthHeight=function(a,b){this.force_width=a;this.force_height=b};this.setDecoration=function(a){this.decoration=a};this.dontUseCloseButtons=function(){this.use_close_btn=!1};this.appendNewMenuItem=function(a,b,c){this.extra_menu_items.push([a,b,c])};this.appendCustomMenuBuilder=function(a,b){this.custom_menu_builder.push([a,b])};this.setFocus=function(){try{return this.focus_link_b.focus(),this.focus_link_t.focus(),!0}catch(a){return!1}};
+this.setStateChanged=function(a){this.state=a;null!=this.spelling_state_observer&&this.report_state_change&&this.spelling_state_observer(a,this)};this.setReportStateChange=function(a){this.report_state_change=a};this.getUrl=function(){return this.server_url+GOOGIE_CUR_LANG};this.escapeSpecial=function(a){return a?a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"):""};this.createXMLReq=function(a){return'<?xml version="1.0" encoding="utf-8" ?><spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1"><text>'+
+a+"</text></spellrequest>"};this.spellCheck=function(a){this.prepare(a);var a=this.escapeSpecial(this.orginal_text),b=this;$.ajax({type:"POST",url:this.getUrl(),data:this.createXMLReq(a),dataType:"text",error:function(){b.custom_ajax_error?b.custom_ajax_error(b):alert("An error was encountered on the server. Please try again later.");b.main_controller&&($(b.spell_span).remove(),b.removeIndicator());b.checkSpellingState()},success:function(a){b.processData(a);b.results.length||(b.custom_no_spelling_error?
+b.custom_no_spelling_error(b):b.flashNoSpellingErrorState());b.removeIndicator()}})};this.learnWord=function(a){var a=this.escapeSpecial(a.innerHTML),b=this,a='<?xml version="1.0" encoding="utf-8" ?><learnword><text>'+a+"</text></learnword>";$.ajax({type:"POST",url:this.getUrl(),data:a,dataType:"text",error:function(){b.custom_ajax_error?b.custom_ajax_error(b):alert("An error was encountered on the server. Please try again later.")},success:function(){}})};this.prepare=function(a,b){this.cnt_errors=
+this.cnt_errors_fixed=0;this.setStateChanged("checking_spell");!b&&this.main_controller&&this.appendIndicator(this.spell_span);this.error_links=[];this.ta_scroll_top=this.text_area.scrollTop;this.ignore=a;this.hideLangWindow();if(""==$(this.text_area).val()||a)this.custom_no_spelling_error?this.custom_no_spelling_error(this):this.flashNoSpellingErrorState(),this.removeIndicator();else{this.createEditLayer(this.text_area.offsetWidth,this.text_area.offsetHeight);this.createErrorWindow();$("body").append(this.error_window);
+try{netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead")}catch(c){}this.main_controller&&$(this.spell_span).unbind("click");this.orginal_text=$(this.text_area).val()}};this.parseResult=function(a){var b=/\w+="(\d+|true)"/g,c=/\t/g,a=a.match(/<c[^>]*>[^<]*<\/c>/g),d=[];if(null==a)return d;for(var e=0,f=a.length;e<f;e++){var j=[];this.errorFound();j.attrs=[];for(var g,h,l=a[e].match(b),k=0;k<l.length;k++)g=l[k].split(/=/),h=g[1].replace(/"/g,""),j.attrs[g[0]]="true"!=h?parseInt(h):
+h;j.suggestions=[];g=a[e].replace(/<[^>]*>/g,"").split(c);for(h=0;h<g.length;h++)""!=g[h]&&j.suggestions.push(g[h]);d.push(j)}return d};this.processData=function(a){this.results=this.parseResult(a);this.results.length&&(this.showErrorsInIframe(),this.resumeEditingState())};this.createErrorWindow=function(){this.error_window=document.createElement("div");$(this.error_window).addClass("googie_window popupmenu").attr("googie_action_btn","1")};this.isErrorWindowShown=function(){return $(this.error_window).is(":visible")};
+this.hideErrorWindow=function(){$(this.error_window).hide();$(this.error_window_iframe).hide()};this.updateOrginalText=function(a,b,c,d){var e=this.orginal_text.substring(0,a),a=this.orginal_text.substring(a+b.length),b=c.length-b.length;this.orginal_text=e+c+a;$(this.text_area).val(this.orginal_text);c=0;for(e=this.results.length;c<e;c++)c!=d&&c>d&&(this.results[c].attrs.o+=b)};this.saveOldValue=function(a,b){a.is_changed=!0;a.old_value=b};this.createListSeparator=function(){var a=document.createElement("td"),
+b=document.createElement("tr");$(a).html(" ").attr("googie_action_btn","1").css({cursor:"default","font-size":"3px","border-top":"1px solid #ccc","padding-top":"3px"});b.appendChild(a);return b};this.correctError=function(a,b,c,d){var e=b.innerHTML,c=3==c.nodeType?c.nodeValue:c.innerHTML,f=this.results[a].attrs.o;if(d)d=b.previousSibling.innerHTML,b.previousSibling.innerHTML=d.slice(0,d.length-1),e=" "+e,f--;this.hideErrorWindow();this.updateOrginalText(f,e,c,a);$(b).html(c).css("color","green").attr("is_corrected",
+!0);this.results[a].attrs.l=c.length;this.isDefined(b.old_value)||this.saveOldValue(b,e);this.errorFixed()};this.ignoreError=function(a){$(a).removeAttr("class").css("color","").unbind();this.hideErrorWindow()};this.showErrorWindow=function(a,b){this.show_menu_observer&&this.show_menu_observer(this);var c=this,d=$(a).offset(),e=document.createElement("table"),f=document.createElement("tbody");$(this.error_window).html("");$(e).addClass("googie_list").attr("googie_action_btn","1");for(var j=!1,g=0;g<
+this.custom_menu_builder.length;g++){var h=this.custom_menu_builder[g];if(h[0](this.results[b])){j=h[1](this,f,a);break}}if(!j){var j=this.results[b].suggestions,l=this.results[b].attrs.o,g=this.results[b].attrs.l,k,m;this.has_dictionary&&!$(a).attr("is_corrected")&&(h=document.createElement("tr"),k=document.createElement("td"),m=document.createElement("span"),$(m).text(this.lang_learn_word),$(k).attr("googie_action_btn","1").css("cursor","default").mouseover(c.item_onmouseover).mouseout(c.item_onmouseout).click(function(){c.learnWord(a,
+b);c.ignoreError(a,b)}),k.appendChild(m),h.appendChild(k),f.appendChild(h));for(var o=0,g=j.length;o<g;o++)h=document.createElement("tr"),k=document.createElement("td"),m=document.createElement("span"),$(m).html(j[o]),$(k).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout).click(function(d){c.correctError(b,a,d.target.firstChild)}),k.appendChild(m),h.appendChild(k),f.appendChild(h);if(a.is_changed&&a.innerHTML!=a.old_value){var p=a.old_value,j=document.createElement("tr"),g=document.createElement("td"),
+h=document.createElement("span");$(h).addClass("googie_list_revert").html(this.lang_revert+" "+p);$(g).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout).click(function(){c.updateOrginalText(l,a.innerHTML,p,b);$(a).removeAttr("is_corrected").css("color","#b91414").html(p);c.hideErrorWindow()});g.appendChild(h);j.appendChild(g);f.appendChild(j)}var j=document.createElement("tr"),g=document.createElement("td"),n=document.createElement("input"),h=document.createElement("img");k=document.createElement("form");
+m=function(){""!=n.value&&(c.isDefined(a.old_value)||c.saveOldValue(a,a.innerHTML),c.updateOrginalText(l,a.innerHTML,n.value,b),$(a).attr("is_corrected",!0).css("color","green").html(n.value),c.hideErrorWindow());return!1};$(n).width(120).css({margin:0,padding:0});$(n).val(a.innerHTML).attr("googie_action_btn","1");$(g).css("cursor","default").attr("googie_action_btn","1");$(h).attr("src",this.img_dir+"ok.gif").width(32).height(16).css({cursor:"pointer","margin-left":"2px","margin-right":"2px"}).click(m);
+$(k).attr("googie_action_btn","1").css({margin:0,padding:0,cursor:"default","white-space":"nowrap"}).submit(m);k.appendChild(n);k.appendChild(h);g.appendChild(k);j.appendChild(g);f.appendChild(j);0<this.extra_menu_items.length&&f.appendChild(this.createListSeparator());var q=function(b){if(b<c.extra_menu_items.length){var d=c.extra_menu_items[b];if(!d[2]||d[2](a,c)){var e=document.createElement("tr"),g=document.createElement("td");$(g).html(d[0]).mouseover(c.item_onmouseover).mouseout(c.item_onmouseout).click(function(){return d[1](a,
 c)});e.appendChild(g);f.appendChild(e)}q(b+1)}};q(0);q=null;this.use_close_btn&&f.appendChild(this.createCloseButton(this.hideErrorWindow))}e.appendChild(f);this.error_window.appendChild(e);g=$(this.error_window).height();e=$(this.error_window).width();h=$(document).height();j=$(document).width();g=d.top+g+20<h?d.top+20:d.top-g;d=d.left+e<j?d.left:d.left-e;$(this.error_window).css({top:g+"px",left:d+"px"}).show();if($.browser.msie){if(!this.error_window_iframe)d=$("<iframe>").css({position:"absolute",
-"z-index":-1}),$("body").append(d),this.error_window_iframe=d;$(this.error_window_iframe).css({top:this.error_window.offsetTop,left:this.error_window.offsetLeft,width:this.error_window.offsetWidth,height:this.error_window.offsetHeight}).show()}};this.createEditLayer=function(a,b){this.edit_layer=document.createElement("div");$(this.edit_layer).addClass("googie_edit_layer").attr("id","googie_edit_layer").width("auto").height(b);this.text_area.nodeName.toLowerCase()!="input"||$(this.text_area).val()==
-""?$(this.edit_layer).css("overflow","auto").height(b-4):$(this.edit_layer).css("overflow","hidden");var c=this;this.edit_layer_dbl_click&&$(this.edit_layer).dblclick(function(a){if(a.target.className!="googie_link"&&!c.isErrorWindowShown()){c.resumeEditing();var b=function(){$(c.text_area).focus();b=null};window.setTimeout(b,10)}return!1})};this.resumeEditing=function(){this.setStateChanged("ready");if(this.edit_layer)this.el_scroll_top=this.edit_layer.scrollTop;this.hideErrorWindow();this.main_controller&&
-$(this.spell_span).removeClass().addClass("googie_no_style");if(!this.ignore&&(this.use_focus&&($(this.focus_link_t).remove(),$(this.focus_link_b).remove()),$(this.edit_layer).remove(),$(this.text_area).show(),this.el_scroll_top!=void 0))this.text_area.scrollTop=this.el_scroll_top;this.checkSpellingState(!1)};this.createErrorLink=function(a,b){var c=document.createElement("span"),d=this,e=function(){d.showErrorWindow(c,b);e=null;return!1};$(c).html(a).addClass("googie_link").bind("click",e).attr({googie_action_btn:"1",
-g_id:b,is_corrected:!1});return c};this.createPart=function(a){if(a==" ")return document.createTextNode(" ");var a=this.escapeSpecial(a),a=a.replace(/\n/g,"<br>"),a=a.replace(/    /g," &nbsp;"),a=a.replace(/^ /g,"&nbsp;"),a=a.replace(/ $/g,"&nbsp;"),b=document.createElement("span");$(b).html(a);return b};this.showErrorsInIframe=function(){var a=document.createElement("div"),b=0,c=this.results;if(c.length>0){for(var d=0,e=c.length;d<e;d++){var f=c[d].attrs.o,j=c[d].attrs.l,g=this.createPart(this.orginal_text.substring(b,
+"z-index":-1}),$("body").append(d),this.error_window_iframe=d;$(this.error_window_iframe).css({top:this.error_window.offsetTop,left:this.error_window.offsetLeft,width:this.error_window.offsetWidth,height:this.error_window.offsetHeight}).show()}};this.createEditLayer=function(a,b){this.edit_layer=document.createElement("div");$(this.edit_layer).addClass("googie_edit_layer").attr("id","googie_edit_layer").width("auto").height(b);"input"!=this.text_area.nodeName.toLowerCase()||""==$(this.text_area).val()?
+$(this.edit_layer).css("overflow","auto").height(b-4):$(this.edit_layer).css("overflow","hidden");var c=this;this.edit_layer_dbl_click&&$(this.edit_layer).dblclick(function(a){if("googie_link"!=a.target.className&&!c.isErrorWindowShown()){c.resumeEditing();var b=function(){$(c.text_area).focus();b=null};window.setTimeout(b,10)}return!1})};this.resumeEditing=function(){this.setStateChanged("ready");if(this.edit_layer)this.el_scroll_top=this.edit_layer.scrollTop;this.hideErrorWindow();this.main_controller&&
+$(this.spell_span).removeClass().addClass("googie_no_style");if(!this.ignore&&(this.use_focus&&($(this.focus_link_t).remove(),$(this.focus_link_b).remove()),$(this.edit_layer).remove(),$(this.text_area).show(),void 0!=this.el_scroll_top))this.text_area.scrollTop=this.el_scroll_top;this.checkSpellingState(!1)};this.createErrorLink=function(a,b){var c=document.createElement("span"),d=this,e=function(){d.showErrorWindow(c,b);e=null;return!1};$(c).html(a).addClass("googie_link").click(e).removeAttr("is_corrected").attr({googie_action_btn:"1",
+g_id:b});return c};this.createPart=function(a){if(" "==a)return document.createTextNode(" ");var a=this.escapeSpecial(a),a=a.replace(/\n/g,"<br>"),a=a.replace(/    /g," &nbsp;"),a=a.replace(/^ /g,"&nbsp;"),a=a.replace(/ $/g,"&nbsp;"),b=document.createElement("span");$(b).html(a);return b};this.showErrorsInIframe=function(){var a=document.createElement("div"),b=0,c=this.results;if(0<c.length){for(var d=0,e=c.length;d<e;d++){var f=c[d].attrs.o,j=c[d].attrs.l,g=this.createPart(this.orginal_text.substring(b,
 f));a.appendChild(g);b+=f-b;f=this.createErrorLink(this.orginal_text.substr(f,j),d);this.error_links.push(f);a.appendChild(f);b+=j}b=this.createPart(this.orginal_text.substr(b,this.orginal_text.length));a.appendChild(b)}else a.innerHTML=this.orginal_text;$(a).css("text-align","left");var h=this;this.custom_item_evaulator&&$.map(this.error_links,function(a){h.custom_item_evaulator(h,a)});$(this.edit_layer).append(a);$(this.text_area).hide();$(this.edit_layer).insertBefore(this.text_area);if(this.use_focus)this.focus_link_t=
 this.createFocusLink("focus_t"),this.focus_link_b=this.createFocusLink("focus_b"),$(this.focus_link_t).insertBefore(this.edit_layer),$(this.focus_link_b).insertAfter(this.edit_layer)};this.createLangWindow=function(){this.language_window=document.createElement("div");$(this.language_window).addClass("googie_window popupmenu").width(100).attr("googie_action_btn","1");var a=document.createElement("table"),b=document.createElement("tbody"),c=this,d,e,f;$(a).addClass("googie_list").width("100%");this.lang_elms=
-[];for(i=0;i<this.langlist_codes.length;i++)d=document.createElement("tr"),e=document.createElement("td"),f=document.createElement("span"),$(f).text(this.lang_to_word[this.langlist_codes[i]]),this.lang_elms.push(e),$(e).attr("googieId",this.langlist_codes[i]).bind("click",function(){c.deHighlightCurSel();c.setCurrentLanguage($(this).attr("googieId"));c.lang_state_observer!=null&&c.lang_state_observer();c.highlightCurSel();c.hideLangWindow()}).bind("mouseover",function(){if(this.className!="googie_list_selected")this.className=
-"googie_list_onhover"}).bind("mouseout",function(){if(this.className!="googie_list_selected")this.className="googie_list_onout"}),e.appendChild(f),d.appendChild(e),b.appendChild(d);this.use_close_btn&&b.appendChild(this.createCloseButton(function(){c.hideLangWindow.apply(c)}));this.highlightCurSel();a.appendChild(b);this.language_window.appendChild(a)};this.isLangWindowShown=function(){return $(this.language_window).is(":visible")};this.hideLangWindow=function(){$(this.language_window).hide();$(this.switch_lan_pic).removeClass().addClass("googie_lang_3d_on")};
-this.showLangWindow=function(a){this.show_menu_observer&&this.show_menu_observer(this);this.createLangWindow();$("body").append(this.language_window);var b=$(a).offset(),c=$(a).height(),d=$(a).width(),a=$(this.language_window).height(),e=$(document).height(),d=this.change_lang_pic_placement=="right"?b.left-100+d:b.left+d,b=b.top+a<e?b.top+c:b.top-a-4;$(this.language_window).css({top:b+"px",left:d+"px"}).show();this.highlightCurSel()};this.deHighlightCurSel=function(){$(this.lang_cur_elm).removeClass().addClass("googie_list_onout")};
-this.highlightCurSel=function(){GOOGIE_CUR_LANG==null&&(GOOGIE_CUR_LANG=GOOGIE_DEFAULT_LANG);for(var a=0;a<this.lang_elms.length;a++)$(this.lang_elms[a]).attr("googieId")==GOOGIE_CUR_LANG?(this.lang_elms[a].className="googie_list_selected",this.lang_cur_elm=this.lang_elms[a]):this.lang_elms[a].className="googie_list_onout"};this.createChangeLangPic=function(){var a=$("<img>").attr({src:this.img_dir+"change_lang.gif",alt:"Change language",googie_action_btn:"1"}),b=document.createElement("span");l=
-this;$(b).addClass("googie_lang_3d_on").append(a).bind("click",function(){var a=this.tagName.toLowerCase()=="img"?this.parentNode:this;$(a).hasClass("googie_lang_3d_click")?(a.className="googie_lang_3d_on",l.hideLangWindow()):(a.className="googie_lang_3d_click",l.showLangWindow(a))});return b};this.createSpellDiv=function(){var a=document.createElement("span");$(a).addClass("googie_check_spelling_link").text(this.lang_chck_spell);this.show_spell_img&&$(a).append(" ").append($("<img>").attr("src",
-this.img_dir+"spellc.gif"));return a};this.flashNoSpellingErrorState=function(a){this.setStateChanged("no_error_found");var b=this;if(this.main_controller){var c;c=a?function(){a();b.checkSpellingState()}:function(){b.checkSpellingState()};var d=$("<span>").text(this.lang_no_error_found);$(this.switch_lan_pic).hide();$(this.spell_span).empty().append(d).removeClass().addClass("googie_check_spelling_ok");window.setTimeout(c,1E3)}};this.resumeEditingState=function(){this.setStateChanged("resume_editing");
-if(this.main_controller){var a=$("<span>").text(this.lang_rsm_edt),b=this;$(this.switch_lan_pic).hide();$(this.spell_span).empty().unbind().append(a).bind("click",function(){b.resumeEditing()}).removeClass().addClass("googie_resume_editing")}try{this.edit_layer.scrollTop=this.ta_scroll_top}catch(c){}};this.checkSpellingState=function(a){a&&this.setStateChanged("ready");this.switch_lan_pic=this.show_change_lang_pic?this.createChangeLangPic():document.createElement("span");var a=this.createSpellDiv(),
-b=this;this.custom_spellcheck_starter?$(a).bind("click",function(){b.custom_spellcheck_starter()}):$(a).bind("click",function(){b.spellCheck()});this.main_controller&&(this.change_lang_pic_placement=="left"?$(this.spell_container).empty().append(this.switch_lan_pic).append(" ").append(a):$(this.spell_container).empty().append(a).append(" ").append(this.switch_lan_pic));this.spell_span=a};this.isDefined=function(a){return a!==void 0&&a!==null};this.errorFixed=function(){this.cnt_errors_fixed++;this.all_errors_fixed_observer&&
-this.cnt_errors_fixed==this.cnt_errors&&(this.hideErrorWindow(),this.all_errors_fixed_observer())};this.errorFound=function(){this.cnt_errors++};this.createCloseButton=function(a){return this.createButton(this.lang_close,"googie_list_close",a)};this.createButton=function(a,b,c){var d=document.createElement("tr"),e=document.createElement("td"),f;b?(f=document.createElement("span"),$(f).addClass(b).html(a)):f=document.createTextNode(a);$(e).bind("click",c).bind("mouseover",this.item_onmouseover).bind("mouseout",
-this.item_onmouseout);e.appendChild(f);d.appendChild(e);return d};this.removeIndicator=function(){window.rcmail&&rcmail.set_busy(!1,null,this.rc_msg_id)};this.appendIndicator=function(){if(window.rcmail)this.rc_msg_id=rcmail.set_busy(!0,"checking")};this.createFocusLink=function(a){var b=document.createElement("a");$(b).attr({href:"javascript:;",name:a});return b};this.item_onmouseover=function(){this.className!="googie_list_revert"&&this.className!="googie_list_close"?this.className="googie_list_onhover":
-this.parentNode.className="googie_list_onhover"};this.item_onmouseout=function(){this.className!="googie_list_revert"&&this.className!="googie_list_close"?this.className="googie_list_onout":this.parentNode.className="googie_list_onout"}};
+[];for(i=0;i<this.langlist_codes.length;i++)d=document.createElement("tr"),e=document.createElement("td"),f=document.createElement("span"),$(f).text(this.lang_to_word[this.langlist_codes[i]]),this.lang_elms.push(e),$(e).attr("googieId",this.langlist_codes[i]).bind("click",function(){c.deHighlightCurSel();c.setCurrentLanguage($(this).attr("googieId"));null!=c.lang_state_observer&&c.lang_state_observer();c.highlightCurSel();c.hideLangWindow()}).bind("mouseover",function(){if("googie_list_selected"!=
+this.className)this.className="googie_list_onhover"}).bind("mouseout",function(){if("googie_list_selected"!=this.className)this.className="googie_list_onout"}),e.appendChild(f),d.appendChild(e),b.appendChild(d);this.use_close_btn&&b.appendChild(this.createCloseButton(function(){c.hideLangWindow.apply(c)}));this.highlightCurSel();a.appendChild(b);this.language_window.appendChild(a)};this.isLangWindowShown=function(){return $(this.language_window).is(":visible")};this.hideLangWindow=function(){$(this.language_window).hide();
+$(this.switch_lan_pic).removeClass().addClass("googie_lang_3d_on")};this.showLangWindow=function(a){this.show_menu_observer&&this.show_menu_observer(this);this.createLangWindow();$("body").append(this.language_window);var b=$(a).offset(),c=$(a).height(),d=$(a).width(),a=$(this.language_window).height(),e=$(document).height(),d="right"==this.change_lang_pic_placement?b.left-100+d:b.left+d,b=b.top+a<e?b.top+c:b.top-a-4;$(this.language_window).css({top:b+"px",left:d+"px"}).show();this.highlightCurSel()};
+this.deHighlightCurSel=function(){$(this.lang_cur_elm).removeClass().addClass("googie_list_onout")};this.highlightCurSel=function(){null==GOOGIE_CUR_LANG&&(GOOGIE_CUR_LANG=GOOGIE_DEFAULT_LANG);for(var a=0;a<this.lang_elms.length;a++)$(this.lang_elms[a]).attr("googieId")==GOOGIE_CUR_LANG?(this.lang_elms[a].className="googie_list_selected",this.lang_cur_elm=this.lang_elms[a]):this.lang_elms[a].className="googie_list_onout"};this.createChangeLangPic=function(){var a=$("<img>").attr({src:this.img_dir+
+"change_lang.gif",alt:"Change language",googie_action_btn:"1"}),b=document.createElement("span");l=this;$(b).addClass("googie_lang_3d_on").append(a).bind("click",function(){var a="img"==this.tagName.toLowerCase()?this.parentNode:this;$(a).hasClass("googie_lang_3d_click")?(a.className="googie_lang_3d_on",l.hideLangWindow()):(a.className="googie_lang_3d_click",l.showLangWindow(a))});return b};this.createSpellDiv=function(){var a=document.createElement("span");$(a).addClass("googie_check_spelling_link").text(this.lang_chck_spell);
+this.show_spell_img&&$(a).append(" ").append($("<img>").attr("src",this.img_dir+"spellc.gif"));return a};this.flashNoSpellingErrorState=function(a){this.setStateChanged("no_error_found");var b=this;if(this.main_controller){var c;c=a?function(){a();b.checkSpellingState()}:function(){b.checkSpellingState()};var d=$("<span>").text(this.lang_no_error_found);$(this.switch_lan_pic).hide();$(this.spell_span).empty().append(d).removeClass().addClass("googie_check_spelling_ok");window.setTimeout(c,1E3)}};
+this.resumeEditingState=function(){this.setStateChanged("resume_editing");if(this.main_controller){var a=$("<span>").text(this.lang_rsm_edt),b=this;$(this.switch_lan_pic).hide();$(this.spell_span).empty().unbind().append(a).bind("click",function(){b.resumeEditing()}).removeClass().addClass("googie_resume_editing")}try{this.edit_layer.scrollTop=this.ta_scroll_top}catch(c){}};this.checkSpellingState=function(a){a&&this.setStateChanged("ready");this.switch_lan_pic=this.show_change_lang_pic?this.createChangeLangPic():
+document.createElement("span");var a=this.createSpellDiv(),b=this;this.custom_spellcheck_starter?$(a).bind("click",function(){b.custom_spellcheck_starter()}):$(a).bind("click",function(){b.spellCheck()});this.main_controller&&("left"==this.change_lang_pic_placement?$(this.spell_container).empty().append(this.switch_lan_pic).append(" ").append(a):$(this.spell_container).empty().append(a).append(" ").append(this.switch_lan_pic));this.spell_span=a};this.isDefined=function(a){return void 0!==a&&null!==
+a};this.errorFixed=function(){this.cnt_errors_fixed++;this.all_errors_fixed_observer&&this.cnt_errors_fixed==this.cnt_errors&&(this.hideErrorWindow(),this.all_errors_fixed_observer())};this.errorFound=function(){this.cnt_errors++};this.createCloseButton=function(a){return this.createButton(this.lang_close,"googie_list_close",a)};this.createButton=function(a,b,c){var d=document.createElement("tr"),e=document.createElement("td"),f;b?(f=document.createElement("span"),$(f).addClass(b).html(a)):f=document.createTextNode(a);
+$(e).bind("click",c).bind("mouseover",this.item_onmouseover).bind("mouseout",this.item_onmouseout);e.appendChild(f);d.appendChild(e);return d};this.removeIndicator=function(){window.rcmail&&rcmail.set_busy(!1,null,this.rc_msg_id)};this.appendIndicator=function(){if(window.rcmail)this.rc_msg_id=rcmail.set_busy(!0,"checking")};this.createFocusLink=function(a){var b=document.createElement("a");$(b).attr({href:"javascript:;",name:a});return b};this.item_onmouseover=function(){"googie_list_revert"!=this.className&&
+"googie_list_close"!=this.className?this.className="googie_list_onhover":this.parentNode.className="googie_list_onhover"};this.item_onmouseout=function(){"googie_list_revert"!=this.className&&"googie_list_close"!=this.className?this.className="googie_list_onout":this.parentNode.className="googie_list_onout"}};
index de8890f5fb95e6ef010833a16b0a9dc255564c11..96d612ca23e1a812be247b06654744b8e0228ed2 100644 (file)
@@ -1,8 +1,11 @@
 /*
  SpellCheck
     jQuery'fied spell checker based on GoogieSpell 4.0
- Copyright Amir Salihefendic 2006
- Copyright Aleksander Machniak 2009
+      (which was published under GPL "version 2 or any later version")
+
+ Copyright (C) 2006 Amir Salihefendic
+ Copyright (C) 2009 Aleksander Machniak
+ Copyright (C) 2011 Kolab Systems AG
      LICENSE
          GPL
      AUTHORS
@@ -13,7 +16,8 @@
 var GOOGIE_CUR_LANG,
     GOOGIE_DEFAULT_LANG = 'en';
 
-function GoogieSpell(img_dir, server_url) {
+function GoogieSpell(img_dir, server_url, has_dict)
+{
     var ref = this,
         cookie_value = getCookie('language');
 
@@ -49,6 +53,7 @@ function GoogieSpell(img_dir, server_url) {
     this.lang_rsm_edt = "Resume editing";
     this.lang_no_error_found = "No spelling errors found";
     this.lang_no_suggestions = "No suggestions";
+    this.lang_learn_word = "Add to dictionary";
 
     this.show_spell_img = false; // roundcube mod.
     this.decoration = true;
@@ -64,6 +69,7 @@ function GoogieSpell(img_dir, server_url) {
     this.extra_menu_items = [];
     this.custom_spellcheck_starter = null;
     this.main_controller = true;
+    this.has_dictionary = has_dict;
 
     // Observers
     this.lang_state_observer = null;
@@ -90,7 +96,8 @@ function GoogieSpell(img_dir, server_url) {
     });
 
 
-this.decorateTextarea = function(id) {
+this.decorateTextarea = function(id)
+{
     this.text_area = typeof id === 'string' ? document.getElementById(id) : id;
 
     if (this.text_area) {
@@ -119,16 +126,19 @@ this.decorateTextarea = function(id) {
 //////
 // API Functions (the ones that you can call)
 /////
-this.setSpellContainer = function(id) {
+this.setSpellContainer = function(id)
+{
     this.spell_container = typeof id === 'string' ? document.getElementById(id) : id;
 };
 
-this.setLanguages = function(lang_dict) {
+this.setLanguages = function(lang_dict)
+{
     this.lang_to_word = lang_dict;
     this.langlist_codes = this.array_keys(lang_dict);
 };
 
-this.setCurrentLanguage = function(lan_code) {
+this.setCurrentLanguage = function(lan_code)
+{
     GOOGIE_CUR_LANG = lan_code;
 
     //Set cookie
@@ -137,29 +147,35 @@ this.setCurrentLanguage = function(lan_code) {
     setCookie('language', lan_code, now);
 };
 
-this.setForceWidthHeight = function(width, height) {
+this.setForceWidthHeight = function(width, height)
+{
     // Set to null if you want to use one of them
     this.force_width = width;
     this.force_height = height;
 };
 
-this.setDecoration = function(bool) {
+this.setDecoration = function(bool)
+{
     this.decoration = bool;
 };
 
-this.dontUseCloseButtons = function() {
+this.dontUseCloseButtons = function()
+{
     this.use_close_btn = false;
 };
 
-this.appendNewMenuItem = function(name, call_back_fn, checker) {
+this.appendNewMenuItem = function(name, call_back_fn, checker)
+{
     this.extra_menu_items.push([name, call_back_fn, checker]);
 };
 
-this.appendCustomMenuBuilder = function(eval_fn, builder) {
+this.appendCustomMenuBuilder = function(eval_fn, builder)
+{
     this.custom_menu_builder.push([eval_fn, builder]);
 };
 
-this.setFocus = function() {
+this.setFocus = function()
+{
     try {
         this.focus_link_b.focus();
         this.focus_link_t.focus();
@@ -174,13 +190,15 @@ this.setFocus = function() {
 //////
 // Set functions (internal)
 /////
-this.setStateChanged = function(current_state) {
+this.setStateChanged = function(current_state)
+{
     this.state = current_state;
     if (this.spelling_state_observer != null && this.report_state_change)
         this.spelling_state_observer(current_state, this);
 };
 
-this.setReportStateChange = function(bool) {
+this.setReportStateChange = function(bool)
+{
     this.report_state_change = bool;
 };
 
@@ -188,28 +206,31 @@ this.setReportStateChange = function(bool) {
 //////
 // Request functions
 /////
-this.getUrl = function() {
+this.getUrl = function()
+{
     return this.server_url + GOOGIE_CUR_LANG;
 };
 
-this.escapeSpecial = function(val) {
+this.escapeSpecial = function(val)
+{
     return val ? val.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") : '';
 };
 
-this.createXMLReq = function (text) {
+this.createXMLReq = function (text)
+{
     return '<?xml version="1.0" encoding="utf-8" ?>'
        + '<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">'
        + '<text>' + text + '</text></spellrequest>';
 };
 
-this.spellCheck = function(ignore) {
+this.spellCheck = function(ignore)
+{
     this.prepare(ignore);
 
     var req_text = this.escapeSpecial(this.orginal_text),
         ref = this;
 
-    $.ajax({ type: 'POST', url: this.getUrl(),
-       data: this.createXMLReq(req_text), dataType: 'text',
+    $.ajax({ type: 'POST', url: this.getUrl(), data: this.createXMLReq(req_text), dataType: 'text',
            error: function(o) {
             if (ref.custom_ajax_error)
                    ref.custom_ajax_error(ref);
@@ -234,6 +255,25 @@ this.spellCheck = function(ignore) {
     });
 };
 
+this.learnWord = function(word, id)
+{
+    word = this.escapeSpecial(word.innerHTML);
+
+    var ref = this,
+        req_text = '<?xml version="1.0" encoding="utf-8" ?><learnword><text>' + word + '</text></learnword>';
+
+    $.ajax({ type: 'POST', url: this.getUrl(), data: req_text, dataType: 'text',
+           error: function(o) {
+            if (ref.custom_ajax_error)
+                   ref.custom_ajax_error(ref);
+            else
+                   alert('An error was encountered on the server. Please try again later.');
+           },
+        success: function(data) {
+           }
+    });
+};
+
 
 //////
 // Spell checking functions
@@ -274,7 +314,8 @@ this.prepare = function(ignore, no_indicator)
     this.orginal_text = $(this.text_area).val();
 };
 
-this.parseResult = function(r_text) {
+this.parseResult = function(r_text)
+{
     // Returns an array: result[item] -> ['attrs'], ['suggestions']
     var re_split_attr_c = /\w+="(\d+|true)"/g,
         re_split_text = /\t/g,
@@ -324,21 +365,25 @@ this.processData = function(data)
 //////
 // Error menu functions
 /////
-this.createErrorWindow = function() {
+this.createErrorWindow = function()
+{
     this.error_window = document.createElement('div');
     $(this.error_window).addClass('googie_window popupmenu').attr('googie_action_btn', '1');
 };
 
-this.isErrorWindowShown = function() {
+this.isErrorWindowShown = function()
+{
     return $(this.error_window).is(':visible');
 };
 
-this.hideErrorWindow = function() {
+this.hideErrorWindow = function()
+{
     $(this.error_window).hide();
     $(this.error_window_iframe).hide();
 };
 
-this.updateOrginalText = function(offset, old_value, new_value, id) {
+this.updateOrginalText = function(offset, old_value, new_value, id)
+{
     var part_1 = this.orginal_text.substring(0, offset),
         part_2 = this.orginal_text.substring(offset+old_value.length),
         add_2_offset = new_value.length - old_value.length;
@@ -357,18 +402,20 @@ this.saveOldValue = function(elm, old_value) {
     elm.old_value = old_value;
 };
 
-this.createListSeparator = function() {
+this.createListSeparator = function()
+{
     var td = document.createElement('td'),
         tr = document.createElement('tr');
 
     $(td).html(' ').attr('googie_action_btn', '1')
-       .css({'cursor': 'default', 'font-size': '3px', 'border-top': '1px solid #ccc', 'padding-top': '3px'});
+           .css({'cursor': 'default', 'font-size': '3px', 'border-top': '1px solid #ccc', 'padding-top': '3px'});
     tr.appendChild(td);
 
     return tr;
 };
 
-this.correctError = function(id, elm, l_elm, rm_pre_space) {
+this.correctError = function(id, elm, l_elm, rm_pre_space)
+{
     var old_value = elm.innerHTML,
         new_value = l_elm.nodeType == 3 ? l_elm.nodeValue : l_elm.innerHTML,
         offset = this.results[id]['attrs']['o'];
@@ -393,7 +440,15 @@ this.correctError = function(id, elm, l_elm, rm_pre_space) {
     this.errorFixed();
 };
 
-this.showErrorWindow = function(elm, id) {
+this.ignoreError = function(elm, id)
+{
+    // @TODO: ignore all same words
+    $(elm).removeAttr('class').css('color', '').unbind();
+    this.hideErrorWindow();
+};
+
+this.showErrorWindow = function(elm, id)
+{
     if (this.show_menu_observer)
         this.show_menu_observer(this);
 
@@ -414,6 +469,7 @@ this.showErrorWindow = function(elm, id) {
             break;
         }
     }
+
     if (!changed) {
         // Build up the result list
         var suggestions = this.results[id]['suggestions'],
@@ -421,6 +477,26 @@ this.showErrorWindow = function(elm, id) {
             len = this.results[id]['attrs']['l'],
             row, item, dummy;
 
+        // [Add to dictionary] button
+        if (this.has_dictionary && !$(elm).attr('is_corrected')) {
+            row = document.createElement('tr'),
+            item = document.createElement('td'),
+            dummy = document.createElement('span');
+
+            $(dummy).text(this.lang_learn_word);
+            $(item).attr('googie_action_btn', '1').css('cursor', 'default')
+                .mouseover(ref.item_onmouseover)
+                .mouseout(ref.item_onmouseout)
+                           .click(function(e) {
+                               ref.learnWord(elm, id);
+                               ref.ignoreError(elm, id);
+                           });
+
+            item.appendChild(dummy);
+            row.appendChild(item);
+            list.appendChild(row);
+        }
+/*
         if (suggestions.length == 0) {
             row = document.createElement('tr'),
             item = document.createElement('td'),
@@ -433,7 +509,7 @@ this.showErrorWindow = function(elm, id) {
             row.appendChild(item);
             list.appendChild(row);
         }
-
+*/
         for (var i=0, len=suggestions.length; i < len; i++) {
             row = document.createElement('tr'),
             item = document.createElement('td'),
@@ -441,16 +517,15 @@ this.showErrorWindow = function(elm, id) {
 
             $(dummy).html(suggestions[i]);
 
-            $(item).bind('mouseover', this.item_onmouseover)
-                   .bind('mouseout', this.item_onmouseout)
-                   .bind('click', function(e) { ref.correctError(id, elm, e.target.firstChild) });
+            $(item).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout)
+                   .click(function(e) { ref.correctError(id, elm, e.target.firstChild) });
 
             item.appendChild(dummy);
             row.appendChild(item);
             list.appendChild(row);
         }
 
-        //The element is changed, append the revert
+        // The element is changed, append the revert
         if (elm.is_changed && elm.innerHTML != elm.old_value) {
             var old_value = elm.old_value,
                 revert_row = document.createElement('tr'),
@@ -459,11 +534,10 @@ this.showErrorWindow = function(elm, id) {
 
                $(rev_span).addClass('googie_list_revert').html(this.lang_revert + ' ' + old_value);
 
-            $(revert).bind('mouseover', this.item_onmouseover)
-                   .bind('mouseout', this.item_onmouseout)
-                   .bind('click', function(e) {
+            $(revert).mouseover(this.item_onmouseover).mouseout(this.item_onmouseout)
+                   .click(function(e) {
                    ref.updateOrginalText(offset, elm.innerHTML, old_value, id);
-                   $(elm).attr('is_corrected', true).css('color', '#b91414').html(old_value);
+                   $(elm).removeAttr('is_corrected').css('color', '#b91414').html(old_value);
                    ref.hideErrorWindow();
                    });
 
@@ -498,11 +572,11 @@ this.showErrorWindow = function(elm, id) {
            $(ok_pic).attr('src', this.img_dir + 'ok.gif')
                .width(32).height(16)
            .css({'cursor': 'pointer', 'margin-left': '2px', 'margin-right': '2px'})
-               .bind('click', onsub);
+               .click(onsub);
 
         $(edit_form).attr('googie_action_btn', '1')
                .css({'margin': 0, 'padding': 0, 'cursor': 'default', 'white-space': 'nowrap'})
-               .bind('submit', onsub);
+               .submit(onsub);
 
            edit_form.appendChild(edit_input);
            edit_form.appendChild(ok_pic);
@@ -523,9 +597,9 @@ this.showErrorWindow = function(elm, id) {
                       e_col = document.createElement('td');
 
                                $(e_col).html(e_elm[0])
-                        .bind('mouseover', ref.item_onmouseover)
-                       .bind('mouseout', ref.item_onmouseout)
-                                   .bind('click', function() { return e_elm[1](elm, ref) });
+                        .mouseover(ref.item_onmouseover)
+                       .mouseout(ref.item_onmouseout)
+                                   .click(function() { return e_elm[1](elm, ref) });
 
                                e_row.appendChild(e_col);
                     list.appendChild(e_row);
@@ -575,7 +649,8 @@ this.showErrorWindow = function(elm, id) {
 //////
 // Edit layer (the layer where the suggestions are stored)
 //////
-this.createEditLayer = function(width, height) {
+this.createEditLayer = function(width, height)
+{
     this.edit_layer = document.createElement('div');
     $(this.edit_layer).addClass('googie_edit_layer').attr('id', 'googie_edit_layer')
         .width('auto').height(height);
@@ -603,7 +678,8 @@ this.createEditLayer = function(width, height) {
     }
 };
 
-this.resumeEditing = function() {
+this.resumeEditing = function()
+{
     this.setStateChanged('ready');
 
     if (this.edit_layer)
@@ -629,7 +705,8 @@ this.resumeEditing = function() {
     this.checkSpellingState(false);
 };
 
-this.createErrorLink = function(text, id) {
+this.createErrorLink = function(text, id)
+{
     var elm = document.createElement('span'),
         ref = this,
         d = function (e) {
@@ -638,13 +715,14 @@ this.createErrorLink = function(text, id) {
            return false;
         };
 
-    $(elm).html(text).addClass('googie_link').bind('click', d)
-           .attr({'googie_action_btn' : '1', 'g_id' : id, 'is_corrected' : false});
+    $(elm).html(text).addClass('googie_link').click(d).removeAttr('is_corrected')
+           .attr({'googie_action_btn' : '1', 'g_id' : id});
 
     return elm;
 };
 
-this.createPart = function(txt_part) {
+this.createPart = function(txt_part)
+{
     if (txt_part == " ")
         return document.createTextNode(" ");
 
@@ -659,7 +737,8 @@ this.createPart = function(txt_part) {
     return span;
 };
 
-this.showErrorsInIframe = function() {
+this.showErrorsInIframe = function()
+{
     var output = document.createElement('div'),
         pointer = 0,
         results = this.results;
@@ -717,7 +796,8 @@ this.showErrorsInIframe = function() {
 //////
 // Choose language menu
 //////
-this.createLangWindow = function() {
+this.createLangWindow = function()
+{
     this.language_window = document.createElement('div');
     $(this.language_window).addClass('googie_window popupmenu')
            .width(100).attr('googie_action_btn', '1');
@@ -776,16 +856,19 @@ this.createLangWindow = function() {
     this.language_window.appendChild(table);
 };
 
-this.isLangWindowShown = function() {
+this.isLangWindowShown = function()
+{
     return $(this.language_window).is(':visible');
 };
 
-this.hideLangWindow = function() {
+this.hideLangWindow = function()
+{
     $(this.language_window).hide();
     $(this.switch_lan_pic).removeClass().addClass('googie_lang_3d_on');
 };
 
-this.showLangWindow = function(elm) {
+this.showLangWindow = function(elm)
+{
     if (this.show_menu_observer)
         this.show_menu_observer(this);
 
@@ -806,11 +889,13 @@ this.showLangWindow = function(elm) {
     this.highlightCurSel();
 };
 
-this.deHighlightCurSel = function() {
+this.deHighlightCurSel = function()
+{
     $(this.lang_cur_elm).removeClass().addClass('googie_list_onout');
 };
 
-this.highlightCurSel = function() {
+this.highlightCurSel = function()
+{
     if (GOOGIE_CUR_LANG == null)
         GOOGIE_CUR_LANG = GOOGIE_DEFAULT_LANG;
     for (var i=0; i < this.lang_elms.length; i++) {
@@ -824,7 +909,8 @@ this.highlightCurSel = function() {
     }
 };
 
-this.createChangeLangPic = function() {
+this.createChangeLangPic = function()
+{
     var img = $('<img>')
            .attr({src: this.img_dir + 'change_lang.gif', 'alt': 'Change language', 'googie_action_btn': '1'}),
         switch_lan = document.createElement('span');
@@ -847,7 +933,8 @@ this.createChangeLangPic = function() {
     return switch_lan;
 };
 
-this.createSpellDiv = function() {
+this.createSpellDiv = function()
+{
     var span = document.createElement('span');
 
     $(span).addClass('googie_check_spelling_link').text(this.lang_chck_spell);
@@ -862,7 +949,8 @@ this.createSpellDiv = function() {
 //////
 // State functions
 /////
-this.flashNoSpellingErrorState = function(on_finish) {
+this.flashNoSpellingErrorState = function(on_finish)
+{
     this.setStateChanged('no_error_found');
 
     var ref = this;
@@ -888,7 +976,8 @@ this.flashNoSpellingErrorState = function(on_finish) {
     }
 };
 
-this.resumeEditingState = function() {
+this.resumeEditingState = function()
+{
     this.setStateChanged('resume_editing');
 
     //Change link text to resume
@@ -906,7 +995,8 @@ this.resumeEditingState = function() {
     catch (e) {};
 };
 
-this.checkSpellingState = function(fire) {
+this.checkSpellingState = function(fire)
+{
     if (fire)
         this.setStateChanged('ready');
 
@@ -939,12 +1029,14 @@ this.checkSpellingState = function(fire) {
 //////
 // Misc. functions
 /////
-this.isDefined = function(o) {
+this.isDefined = function(o)
+{
     return (o !== undefined && o !== null)
 };
 
-this.errorFixed = function() { 
-    this.cnt_errors_fixed++; 
+this.errorFixed = function()
+{
+    this.cnt_errors_fixed++;
     if (this.all_errors_fixed_observer)
         if (this.cnt_errors_fixed == this.cnt_errors) {
             this.hideErrorWindow();
@@ -952,15 +1044,18 @@ this.errorFixed = function() {
         }
 };
 
-this.errorFound = function() {
+this.errorFound = function()
+{
     this.cnt_errors++;
 };
 
-this.createCloseButton = function(c_fn) {
+this.createCloseButton = function(c_fn)
+{
     return this.createButton(this.lang_close, 'googie_list_close', c_fn);
 };
 
-this.createButton = function(name, css_class, c_fn) {
+this.createButton = function(name, css_class, c_fn)
+{
     var btn_row = document.createElement('tr'),
         btn = document.createElement('td'),
         spn_btn;
@@ -982,14 +1077,16 @@ this.createButton = function(name, css_class, c_fn) {
     return btn_row;
 };
 
-this.removeIndicator = function(elm) {
+this.removeIndicator = function(elm)
+{
     //$(this.indicator).remove();
     // roundcube mod.
     if (window.rcmail)
         rcmail.set_busy(false, null, this.rc_msg_id);
 };
 
-this.appendIndicator = function(elm) {
+this.appendIndicator = function(elm)
+{
     // modified by roundcube
     if (window.rcmail)
            this.rc_msg_id = rcmail.set_busy(true, 'checking');
@@ -1005,19 +1102,23 @@ this.appendIndicator = function(elm) {
 */
 }
 
-this.createFocusLink = function(name) {
+this.createFocusLink = function(name)
+{
     var link = document.createElement('a');
     $(link).attr({'href': 'javascript:;', 'name': name});
     return link;
 };
 
-this.item_onmouseover = function(e) {
+this.item_onmouseover = function(e)
+{
     if (this.className != 'googie_list_revert' && this.className != 'googie_list_close')
         this.className = 'googie_list_onhover';
     else
         this.parentNode.className = 'googie_list_onhover';
 };
-this.item_onmouseout = function(e) {
+
+this.item_onmouseout = function(e)
+{
     if (this.className != 'googie_list_revert' && this.className != 'googie_list_close')
         this.className = 'googie_list_onout';
     else
index 1e326e27426058d3e1eeb8f7991ba6b93a99f3d3..5116df78b21ad40da321c2e59c88935530df1406 100644 (file)
@@ -1,39 +1,39 @@
-function rcube_list_widget(a,b){this.ENTER_KEY=13;this.DELETE_KEY=46;this.BACKSPACE_KEY=8;this.list=a?a:null;this.frame=null;this.rows=[];this.selection=[];this.colcount=this.rowcount=0;this.subject_col=-1;this.col_drag_active=this.drag_active=this.dont_select=this.toggleselect=this.keyboard=this.column_movable=this.draggable=this.multi_selecting=this.multiexpand=this.multiselect=this.shiftkey=!1;this.column_fixed=null;this.shift_start=this.last_selected=0;this.focused=this.in_selection_before=!1;
-this.drag_mouse_start=null;this.dblclick_time=600;this.row_init=function(){};if(b&&typeof b==="object")for(var c in b)this[c]=b[c]}
-rcube_list_widget.prototype={init:function(){if(this.list&&this.list.tBodies[0]){this.rows=[];this.rowcount=0;var a,b,c=this.list.tBodies[0].rows;for(a=0,b=c.length;a<b;a++)this.init_row(c[a]),this.rowcount++;this.init_header();this.frame=this.list.parentNode;this.keyboard&&(rcube_event.add_listener({event:bw.opera?"keypress":"keydown",object:this,method:"key_press"}),rcube_event.add_listener({event:"keydown",object:this,method:"key_down"}))}},init_row:function(a){if(a&&String(a.id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i)){var b=
-this,c=RegExp.$1;a.uid=c;this.rows[c]={uid:c,id:a.id,obj:a};a.onmousedown=function(a){return b.drag_row(a,this.uid)};a.onmouseup=function(a){return b.click_row(a,this.uid)};if(bw.iphone||bw.ipad)a.addEventListener("touchstart",function(a){a.touches.length==1&&(b.drag_row(rcube_event.touchevent(a.touches[0]),this.uid)||a.preventDefault())},!1),a.addEventListener("touchend",function(a){a.changedTouches.length==1&&(b.click_row(rcube_event.touchevent(a.changedTouches[0]),this.uid)||a.preventDefault())},
+function rcube_list_widget(a,b){this.ENTER_KEY=13;this.DELETE_KEY=46;this.BACKSPACE_KEY=8;this.list=a?a:null;this.frame=null;this.rows=[];this.selection=[];this.colcount=this.rowcount=0;this.subject_col=-1;this.modkey=0;this.col_drag_active=this.drag_active=this.dont_select=this.toggleselect=this.keyboard=this.column_movable=this.draggable=this.multi_selecting=this.multiexpand=this.multiselect=!1;this.column_fixed=null;this.shift_start=this.last_selected=0;this.focused=this.in_selection_before=!1;
+this.drag_mouse_start=null;this.dblclick_time=600;this.row_init=function(){};if(b&&"object"===typeof b)for(var c in b)this[c]=b[c]}
+rcube_list_widget.prototype={init:function(){if(this.list&&this.list.tBodies[0]){this.rows=[];this.rowcount=0;var a,b,c=this.list.tBodies[0].rows;for(a=0,b=c.length;a<b;a++)this.init_row(c[a]),this.rowcount++;this.init_header();this.frame=this.list.parentNode;this.keyboard&&(rcube_event.add_listener({event:bw.opera?"keypress":"keydown",object:this,method:"key_press"}),rcube_event.add_listener({event:"keydown",object:this,method:"key_down"}))}},init_row:function(a){if(a&&(""+a.id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i)){var b=
+this,c=RegExp.$1;a.uid=c;this.rows[c]={uid:c,id:a.id,obj:a};a.onmousedown=function(a){return b.drag_row(a,this.uid)};a.onmouseup=function(a){return b.click_row(a,this.uid)};if(bw.iphone||bw.ipad)a.addEventListener("touchstart",function(a){1==a.touches.length&&(b.drag_row(rcube_event.touchevent(a.touches[0]),this.uid)||a.preventDefault())},!1),a.addEventListener("touchend",function(a){1==a.changedTouches.length&&(b.click_row(rcube_event.touchevent(a.changedTouches[0]),this.uid)||a.preventDefault())},
 !1);if(document.all)a.onselectstart=function(){return!1};this.row_init(this.rows[c])}},init_header:function(){if(this.list&&this.list.tHead){this.colcount=0;var a,b,c=this;if(this.column_movable&&this.list.tHead&&this.list.tHead.rows)for(b=0;b<this.list.tHead.rows[0].cells.length;b++)if(this.column_fixed!=b)a=this.list.tHead.rows[0].cells[b],a.onmousedown=function(a){return c.drag_column(a,this)},this.colcount++}},clear:function(a){this.list.insertBefore(document.createElement("tbody"),this.list.tBodies[0]);
 this.list.removeChild(this.list.tBodies[1]);this.rows=[];this.rowcount=0;a&&this.clear_selection();if(this.frame)this.frame.scrollTop=0},remove_row:function(a,b){if(this.rows[a].obj)this.rows[a].obj.style.display="none";b&&this.select_next();delete this.rows[a];this.rowcount--},insert_row:function(a,b){var c=this.list.tBodies[0];b&&c.rows.length?c.insertBefore(a,c.firstChild):c.appendChild(a);this.init_row(a);this.rowcount++},focus:function(a){var b,c;this.focused=!0;for(b in this.selection)c=this.selection[b],
-this.rows[c]&&this.rows[c].obj&&$(this.rows[c].obj).addClass("selected").removeClass("unfocused");$("*:focus",window).blur();$("iframe").each(function(){this.blur()});(a||(a=window.event))&&rcube_event.cancel(a)},blur:function(){var a,b;this.focused=!1;for(a in this.selection)b=this.selection[a],this.rows[b]&&this.rows[b].obj&&$(this.rows[b].obj).removeClass("selected").addClass("unfocused")},drag_column:function(a,b){if(this.colcount>1){this.drag_start=!0;this.drag_mouse_start=rcube_event.get_mouse_pos(a);
-rcube_event.add_listener({event:"mousemove",object:this,method:"column_drag_mouse_move"});rcube_event.add_listener({event:"mouseup",object:this,method:"column_drag_mouse_up"});this.add_dragfix();for(var c=0;c<this.list.tHead.rows[0].cells.length;c++)if(b==this.list.tHead.rows[0].cells[c]){this.selected_column=c;break}}return!1},drag_row:function(a,b){var c=rcube_event.get_target(a),d=c.tagName.toLowerCase();if(this.dont_select||c&&(d=="input"||d=="img"))return!0;if(rcube_event.get_button(a)==2)return!0;
-this.in_selection_before=this.in_selection(b)?b:!1;this.in_selection_before||(c=rcube_event.get_modifier(a),this.select_row(b,c,!1));if(this.draggable&&this.selection.length){this.drag_start=!0;this.drag_mouse_start=rcube_event.get_mouse_pos(a);rcube_event.add_listener({event:"mousemove",object:this,method:"drag_mouse_move"});rcube_event.add_listener({event:"mouseup",object:this,method:"drag_mouse_up"});if(bw.iphone||bw.ipad)rcube_event.add_listener({event:"touchmove",object:this,method:"drag_mouse_move"}),
-rcube_event.add_listener({event:"touchend",object:this,method:"drag_mouse_up"});this.add_dragfix()}return!1},click_row:function(a,b){var c=(new Date).getTime(),d=rcube_event.get_modifier(a),e=rcube_event.get_target(a),f=e.tagName.toLowerCase();if(e&&(f=="input"||f=="img"))return!0;if(this.dont_select)return this.dont_select=!1;e=c-this.rows[b].clicked<this.dblclick_time;!this.drag_active&&this.in_selection_before==b&&!e&&this.select_row(b,d,!1);this.in_selection_before=this.drag_start=!1;this.rows&&
-e&&this.in_selection(b)?this.triggerEvent("dblclick"):this.triggerEvent("click");this.drag_active||(this.del_dragfix(),rcube_event.cancel(a));this.rows[b].clicked=c;return!1},find_root:function(a){var b=this.rows[a];return b&&b.parent_uid?this.find_root(b.parent_uid):a},expand_row:function(a,b){var c=this.rows[b],d=rcube_event.get_target(a),e=rcube_event.get_modifier(a);this.dont_select=!0;c.clicked=0;c.expanded?(d.className="collapsed",e==CONTROL_KEY||this.multiexpand?this.collapse_all(c):this.collapse(c)):
-(d.className="expanded",e==CONTROL_KEY||this.multiexpand?this.expand_all(c):this.expand(c))},collapse:function(a){a.expanded=!1;this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded});for(var b=a.depth,a=a?a.obj.nextSibling:null,c;a;){if(a.nodeType==1){if((c=this.rows[a.uid])&&c.depth<=b)break;$(a).css("display","none");if(c.expanded)c.expanded=!1,this.triggerEvent("expandcollapse",{uid:c.uid,expanded:c.expanded})}a=a.nextSibling}return!1},expand:function(a){var b,c,d,e,f;a?(a.expanded=
-!0,d=a.depth,e=a.obj.nextSibling,this.update_expando(a.uid,!0),this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded})):(e=this.list.tBodies[0].firstChild,f=d=0);for(;e;){if(e.nodeType==1&&(b=this.rows[e.uid])){if(a&&(!b.depth||b.depth<=d))break;if(b.parent_uid)if((c=this.rows[b.parent_uid])&&c.expanded){if(a&&c==a||f>=c.depth-1)f=c.depth,$(e).css("display",""),b.expanded=!0,this.triggerEvent("expandcollapse",{uid:b.uid,expanded:b.expanded})}else if(a&&(!c||c.depth<=d))break}e=e.nextSibling}return!1},
-collapse_all:function(a){var b,c,d;if(a){if(a.expanded=!1,b=a.depth,c=a.obj.nextSibling,this.update_expando(a.uid),this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded}),b&&this.multiexpand)return!1}else c=this.list.tBodies[0].firstChild,b=0;for(;c;){if(c.nodeType==1&&(d=this.rows[c.uid])){if(a&&(!d.depth||d.depth<=b))break;(a||d.depth)&&$(c).css("display","none");if(d.has_children&&d.expanded)d.expanded=!1,this.update_expando(d.uid,!1),this.triggerEvent("expandcollapse",{uid:d.uid,expanded:d.expanded})}c=
-c.nextSibling}return!1},expand_all:function(a){var b,c,d;a?(a.expanded=!0,b=a.depth,c=a.obj.nextSibling,this.update_expando(a.uid,!0),this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded})):(c=this.list.tBodies[0].firstChild,b=0);for(;c;){if(c.nodeType==1&&(d=this.rows[c.uid])){if(a&&d.depth<=b)break;$(c).css("display","");if(d.has_children&&!d.expanded)d.expanded=!0,this.update_expando(d.uid,!0),this.triggerEvent("expandcollapse",{uid:d.uid,expanded:d.expanded})}c=c.nextSibling}return!1},
-update_expando:function(a,b){var c=document.getElementById("rcmexpando"+a);if(c)c.className=b?"expanded":"collapsed"},get_next_row:function(){if(!this.rows)return!1;for(var a=this.rows[this.last_selected],a=a?a.obj.nextSibling:null;a&&(a.nodeType!=1||a.style.display=="none");)a=a.nextSibling;return a},get_prev_row:function(){if(!this.rows)return!1;for(var a=this.rows[this.last_selected],a=a?a.obj.previousSibling:null;a&&(a.nodeType!=1||a.style.display=="none");)a=a.previousSibling;return a},get_first_row:function(){if(this.rowcount){var a,
-b,c=this.list.tBodies[0].rows;for(a=0,b=c.length-1;a<b;a++)if(c[a].id&&String(c[a].id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i)&&this.rows[RegExp.$1]!=null)return RegExp.$1}return null},get_last_row:function(){if(this.rowcount){var a,b=this.list.tBodies[0].rows;for(a=b.length-1;a>=0;a--)if(b[a].id&&String(b[a].id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i)&&this.rows[RegExp.$1]!=null)return RegExp.$1}return null},select_row:function(a,b,c){var d=this.selection.join(",");this.multiselect||(b=0);if(!this.shift_start)this.shift_start=
-a;if(b){switch(b){case SHIFT_KEY:this.shift_select(a,!1);break;case CONTROL_KEY:c||this.highlight_row(a,!0);break;case CONTROL_SHIFT_KEY:this.shift_select(a,!0);break;default:this.highlight_row(a,!1)}this.multi_selecting=!0}else this.shift_start=a,this.highlight_row(a,!1),this.multi_selecting=!1;this.selection.join(",")!=d&&this.triggerEvent("select");this.last_selected!=0&&this.rows[this.last_selected]&&$(this.rows[this.last_selected].obj).removeClass("focused");this.toggleselect&&this.last_selected==
-a?(this.clear_selection(),a=null):$(this.rows[a].obj).addClass("focused");if(!this.selection.length)this.shift_start=null;this.last_selected=a},select:function(a){this.select_row(a,!1);this.scrollto(a)},select_next:function(){var a=this.get_next_row(),b=this.get_prev_row();(a=a?a:b)&&this.select_row(a.uid,!1,!1)},select_first:function(a){var b=this.get_first_row();b&&(a?(this.shift_select(b,a),this.triggerEvent("select"),this.scrollto(b)):this.select(b))},select_last:function(a){var b=this.get_last_row();
-b&&(a?(this.shift_select(b,a),this.triggerEvent("select"),this.scrollto(b)):this.select(b))},select_childs:function(a){if(this.rows[a]&&this.rows[a].has_children)for(var b=this.rows[a].depth,a=this.rows[a].obj.nextSibling;a;){if(a.nodeType==1&&(r=this.rows[a.uid])){if(!r.depth||r.depth<=b)break;this.in_selection(r.uid)||this.select_row(r.uid,CONTROL_KEY)}a=a.nextSibling}},shift_select:function(a,b){if(!this.rows[this.shift_start]||!this.selection.length)this.shift_start=a;var c,d=this.rows[this.shift_start].obj.rowIndex,
-e=this.rows[a].obj.rowIndex,f=d<e?d:e,d=d>e?d:e;for(c in this.rows)this.rows[c].obj.rowIndex>=f&&this.rows[c].obj.rowIndex<=d?this.in_selection(c)||this.highlight_row(c,!0):this.in_selection(c)&&!b&&this.highlight_row(c,!0)},in_selection:function(a){for(var b in this.selection)if(this.selection[b]==a)return!0;return!1},select_all:function(a){if(!this.rows||!this.rows.length)return!1;var b,c=this.selection.join(",");this.selection=[];for(b in this.rows)!a||this.rows[b][a]==!0?(this.last_selected=b,
-this.highlight_row(b,!0)):$(this.rows[b].obj).removeClass("selected").removeClass("unfocused");this.selection.join(",")!=c&&this.triggerEvent("select");this.focus();return!0},invert_selection:function(){if(!this.rows||!this.rows.length)return!1;var a,b=this.selection.join(",");for(a in this.rows)this.highlight_row(a,!0);this.selection.join(",")!=b&&this.triggerEvent("select");this.focus();return!0},clear_selection:function(a){var b,c=this.selection.length;if(a)for(b in this.selection){if(this.selection[b]==
-a){this.selection.splice(b,1);break}}else{for(b in this.selection)this.rows[this.selection[b]]&&$(this.rows[this.selection[b]].obj).removeClass("selected").removeClass("unfocused");this.selection=[]}c&&!this.selection.length&&this.triggerEvent("select")},get_selection:function(){return this.selection},get_single_selection:function(){return this.selection.length==1?this.selection[0]:null},highlight_row:function(a,b){if(this.rows[a]&&!b){if(this.selection.length>1||!this.in_selection(a))this.clear_selection(),
-this.selection[0]=a,$(this.rows[a].obj).addClass("selected")}else if(this.rows[a])if(this.in_selection(a)){var c=$.inArray(a,this.selection),d=this.selection.slice(0,c),c=this.selection.slice(c+1,this.selection.length);this.selection=d.concat(c);$(this.rows[a].obj).removeClass("selected").removeClass("unfocused")}else this.selection[this.selection.length]=a,$(this.rows[a].obj).addClass("selected")},key_press:function(a){if(this.focused!=!0)return!0;var b=rcube_event.get_keycode(a),c=rcube_event.get_modifier(a);
-switch(b){case 40:case 38:case 63233:case 63232:return rcube_event.cancel(a),this.use_arrow_key(b,c);case 61:case 107:case 109:case 32:return rcube_event.cancel(a),a=this.use_plusminus_key(b,c),this.key_pressed=b,this.triggerEvent("keypress"),a;case 36:return this.select_first(c),rcube_event.cancel(a);case 35:return this.select_last(c),rcube_event.cancel(a);default:if(this.shiftkey=a.shiftKey,this.key_pressed=b,this.triggerEvent("keypress"),this.shiftkey=!1,this.key_pressed==this.BACKSPACE_KEY)return rcube_event.cancel(a)}return!0},
-key_down:function(a){switch(rcube_event.get_keycode(a)){case 27:if(this.drag_active)return this.drag_mouse_up(a);if(this.col_drag_active)return this.selected_column=null,this.column_drag_mouse_up(a);case 40:case 38:case 63233:case 63232:case 61:case 107:case 109:case 32:if(!rcube_event.get_modifier(a)&&this.focused)return rcube_event.cancel(a)}return!0},use_arrow_key:function(a,b){var c;if(a==40||a==63233)c=this.get_next_row();else if(a==38||a==63232)c=this.get_prev_row();c&&(this.select_row(c.uid,
-b,!0),this.scrollto(c.uid));return!1},use_plusminus_key:function(a,b){var c=this.rows[this.last_selected];if(c)return a==32&&(a=c.expanded?109:61),a==61||a==107?b==CONTROL_KEY||this.multiexpand?this.expand_all(c):this.expand(c):b==CONTROL_KEY||this.multiexpand?this.collapse_all(c):this.collapse(c),this.update_expando(c.uid,c.expanded),!1},scrollto:function(a){var b=this.rows[a].obj;if(b&&this.frame){var c=Number(b.offsetTop);!c&&this.rows[a].parent_uid&&(this.expand_all(this.rows[this.find_root(this.rows[a].uid)]),
-c=Number(b.offsetTop));if(c<Number(this.frame.scrollTop))this.frame.scrollTop=c;else if(c+Number(b.offsetHeight)>Number(this.frame.scrollTop)+Number(this.frame.offsetHeight))this.frame.scrollTop=c+Number(b.offsetHeight)-Number(this.frame.offsetHeight)}},drag_mouse_move:function(a){if(a.type=="touchmove")if(a.changedTouches.length==1)a=rcube_event.touchevent(a.changedTouches[0]);else return rcube_event.cancel(a);if(this.drag_start){var b=rcube_event.get_mouse_pos(a);if(!this.drag_mouse_start||Math.abs(b.x-
-this.drag_mouse_start.x)<3&&Math.abs(b.y-this.drag_mouse_start.y)<3)return!1;if(!this.draglayer)this.draglayer=$("<div>").attr("id","rcmdraglayer").css({position:"absolute",display:"none","z-index":2E3}).appendTo(document.body);var c,d,e=$.merge([],this.selection);for(c in e)d=e[c],this.rows[d].has_children&&!this.rows[d].expanded&&this.select_childs(d);this.draglayer.html("");for(c=0;c<this.selection.length;c++){if(c>12){this.draglayer.append("...");break}if(e=this.rows[this.selection[c]].obj)for(b=
-0,d=0;d<e.childNodes.length;d++)if(e.childNodes[d].nodeName=="TD"){if(c==0)this.drag_start_pos=$(e.childNodes[d]).offset();if(this.subject_col<0||this.subject_col>=0&&this.subject_col==b){for(var f,g,h=e.childNodes[d].childNodes,b=0;b<h.length;b++)if((g=e.childNodes[d].childNodes[b])&&(g.nodeType==3||g.nodeName=="A"))f=g;if(!f)break;d=$(f).text();d=$.trim(d);d=d.length>50?d.substring(0,50)+"...":d;this.draglayer.append($("<div>").text(d));break}b++}}this.draglayer.show();this.drag_active=!0;this.triggerEvent("dragstart")}this.drag_active&&
-this.draglayer&&(c=rcube_event.get_mouse_pos(a),this.draglayer.css({left:c.x+20+"px",top:c.y-5+(bw.ie?document.documentElement.scrollTop:0)+"px"}),this.triggerEvent("dragmove",a?a:window.event));return this.drag_start=!1},drag_mouse_up:function(a){document.onmousemove=null;if(a.type=="touchend"&&a.changedTouches.length!=1)return rcube_event.cancel(a);this.draglayer&&this.draglayer.is(":visible")&&(this.drag_start_pos?this.draglayer.animate(this.drag_start_pos,300,"swing").hide(20):this.draglayer.hide());
-this.drag_active&&this.focus();this.drag_active=!1;rcube_event.remove_listener({event:"mousemove",object:this,method:"drag_mouse_move"});rcube_event.remove_listener({event:"mouseup",object:this,method:"drag_mouse_up"});if(bw.iphone||bw.ipad)rcube_event.remove_listener({event:"touchmove",object:this,method:"drag_mouse_move"}),rcube_event.remove_listener({event:"touchend",object:this,method:"drag_mouse_up"});this.del_dragfix();this.triggerEvent("dragend");return rcube_event.cancel(a)},column_drag_mouse_move:function(a){if(this.drag_start){var b;
-b=rcube_event.get_mouse_pos(a);if(!this.drag_mouse_start||Math.abs(b.x-this.drag_mouse_start.x)<3&&Math.abs(b.y-this.drag_mouse_start.y)<3)return!1;if(!this.col_draglayer){b=$(this.list).offset();var c=this.list.tHead.rows[0].cells;this.col_draglayer=$("<div>").attr("id","rcmcoldraglayer").css(b).css({position:"absolute","z-index":2001,"background-color":"white",opacity:0.75,height:this.frame.offsetHeight-2+"px",width:this.frame.offsetWidth-2+"px"}).appendTo(document.body).append($("<div>").attr("id",
-"rcmcolumnindicator").css({position:"absolute","border-right":"2px dotted #555","z-index":2002,height:this.frame.offsetHeight-2+"px"}));this.cols=[];this.list_pos=this.list_min_pos=b.left;for(b=0;b<c.length;b++)this.cols[b]=c[b].offsetWidth,this.column_fixed!==null&&b<=this.column_fixed&&(this.list_min_pos+=this.cols[b])}this.col_draglayer.show();this.col_drag_active=!0;this.triggerEvent("column_dragstart")}if(this.col_drag_active&&this.col_draglayer){var c=0,d=rcube_event.get_mouse_pos(a);for(b=
-0;b<this.cols.length;b++)if(d.x>=this.cols[b]/2+this.list_pos+c)c+=this.cols[b];else break;b==0&&this.list_min_pos>d.x?c=this.list_min_pos-this.list_pos:!this.list.rowcount&&b==this.cols.length&&(c-=2);$("#rcmcolumnindicator").css({width:c+"px"});this.triggerEvent("column_dragmove",a?a:window.event)}return this.drag_start=!1},column_drag_mouse_up:function(a){document.onmousemove=null;if(this.col_draglayer)this.col_draglayer.remove(),this.col_draglayer=null;this.col_drag_active&&this.focus();this.col_drag_active=
-!1;rcube_event.remove_listener({event:"mousemove",object:this,method:"column_drag_mouse_move"});rcube_event.remove_listener({event:"mouseup",object:this,method:"column_drag_mouse_up"});this.del_dragfix();if(this.selected_column!==null&&this.cols&&this.cols.length){var b,c=0,d=rcube_event.get_mouse_pos(a);for(b=0;b<this.cols.length;b++)if(d.x>=this.cols[b]/2+this.list_pos+c)c+=this.cols[b];else break;b!=this.selected_column&&b!=this.selected_column+1&&this.column_replace(this.selected_column,b)}this.triggerEvent("column_dragend");
-return rcube_event.cancel(a)},add_dragfix:function(){$("iframe").each(function(){$('<div class="iframe-dragdrop-fix"></div>').css({background:"#fff",width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css($(this).offset()).appendTo(document.body)})},del_dragfix:function(){$("div.iframe-dragdrop-fix").each(function(){this.parentNode.removeChild(this)})},column_replace:function(a,b){var c;c=this.list.tHead.rows[0].cells;var d=c[a],e=c[b],f=document.createElement("td");
-e?c[0].parentNode.insertBefore(f,e):c[0].parentNode.appendChild(f);c[0].parentNode.replaceChild(d,f);for(r=0,c=this.list.tBodies[0].rows.length;r<c;r++)row=this.list.tBodies[0].rows[r],d=row.cells[a],e=row.cells[b],f=document.createElement("td"),e?row.insertBefore(f,e):row.appendChild(f),row.replaceChild(d,f);this.subject_col==a?this.subject_col=b>a?b-1:b:this.subject_col<a&&b<=this.subject_col?this.subject_col++:this.subject_col>a&&b>=this.subject_col&&this.subject_col--;this.triggerEvent("column_replace")}};
-rcube_list_widget.prototype.addEventListener=rcube_event_engine.prototype.addEventListener;rcube_list_widget.prototype.removeEventListener=rcube_event_engine.prototype.removeEventListener;rcube_list_widget.prototype.triggerEvent=rcube_event_engine.prototype.triggerEvent;
+this.rows[c]&&this.rows[c].obj&&$(this.rows[c].obj).addClass("selected").removeClass("unfocused");$("*:focus",window).blur();$("iframe").each(function(){this.blur()});(a||(a=window.event))&&rcube_event.cancel(a)},blur:function(){var a,b;this.focused=!1;for(a in this.selection)b=this.selection[a],this.rows[b]&&this.rows[b].obj&&$(this.rows[b].obj).removeClass("selected").addClass("unfocused")},drag_column:function(a,b){if(1<this.colcount){this.drag_start=!0;this.drag_mouse_start=rcube_event.get_mouse_pos(a);
+rcube_event.add_listener({event:"mousemove",object:this,method:"column_drag_mouse_move"});rcube_event.add_listener({event:"mouseup",object:this,method:"column_drag_mouse_up"});this.add_dragfix();for(var c=0;c<this.list.tHead.rows[0].cells.length;c++)if(b==this.list.tHead.rows[0].cells[c]){this.selected_column=c;break}}return!1},drag_row:function(a,b){var c=rcube_event.get_target(a),d=c.tagName.toLowerCase();if(this.dont_select||c&&("input"==d||"img"==d)||2==rcube_event.get_button(a))return!0;this.in_selection_before=
+this.in_selection(b)?b:!1;this.in_selection_before||(c=rcube_event.get_modifier(a),this.select_row(b,c,!1));if(this.draggable&&this.selection.length){this.drag_start=!0;this.drag_mouse_start=rcube_event.get_mouse_pos(a);rcube_event.add_listener({event:"mousemove",object:this,method:"drag_mouse_move"});rcube_event.add_listener({event:"mouseup",object:this,method:"drag_mouse_up"});if(bw.iphone||bw.ipad)rcube_event.add_listener({event:"touchmove",object:this,method:"drag_mouse_move"}),rcube_event.add_listener({event:"touchend",
+object:this,method:"drag_mouse_up"});this.add_dragfix()}return!1},click_row:function(a,b){var c=(new Date).getTime(),d=rcube_event.get_modifier(a),e=rcube_event.get_target(a),f=e.tagName.toLowerCase();if(e&&("input"==f||"img"==f))return!0;if(this.dont_select)return this.dont_select=!1;e=c-this.rows[b].clicked<this.dblclick_time;!this.drag_active&&this.in_selection_before==b&&!e&&this.select_row(b,d,!1);this.in_selection_before=this.drag_start=!1;this.rows&&e&&this.in_selection(b)?this.triggerEvent("dblclick"):
+this.triggerEvent("click");this.drag_active||(this.del_dragfix(),rcube_event.cancel(a));this.rows[b].clicked=c;return!1},find_root:function(a){var b=this.rows[a];return b&&b.parent_uid?this.find_root(b.parent_uid):a},expand_row:function(a,b){var c=this.rows[b],d=rcube_event.get_target(a),e=rcube_event.get_modifier(a);this.dont_select=!0;c.clicked=0;c.expanded?(d.className="collapsed",e==CONTROL_KEY||this.multiexpand?this.collapse_all(c):this.collapse(c)):(d.className="expanded",e==CONTROL_KEY||this.multiexpand?
+this.expand_all(c):this.expand(c))},collapse:function(a){a.expanded=!1;this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded});for(var b=a.depth,a=a?a.obj.nextSibling:null,c;a;){if(1==a.nodeType){if((c=this.rows[a.uid])&&c.depth<=b)break;$(a).css("display","none");if(c.expanded)c.expanded=!1,this.triggerEvent("expandcollapse",{uid:c.uid,expanded:c.expanded})}a=a.nextSibling}return!1},expand:function(a){var b,c,d,e,f;a?(a.expanded=!0,d=a.depth,e=a.obj.nextSibling,this.update_expando(a.uid,
+!0),this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded})):(e=this.list.tBodies[0].firstChild,f=d=0);for(;e;){if(1==e.nodeType&&(b=this.rows[e.uid])){if(a&&(!b.depth||b.depth<=d))break;if(b.parent_uid)if((c=this.rows[b.parent_uid])&&c.expanded){if(a&&c==a||f>=c.depth-1)f=c.depth,$(e).css("display",""),b.expanded=!0,this.triggerEvent("expandcollapse",{uid:b.uid,expanded:b.expanded})}else if(a&&(!c||c.depth<=d))break}e=e.nextSibling}return!1},collapse_all:function(a){var b,c,d;if(a){if(a.expanded=
+!1,b=a.depth,c=a.obj.nextSibling,this.update_expando(a.uid),this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded}),b&&this.multiexpand)return!1}else c=this.list.tBodies[0].firstChild,b=0;for(;c;){if(1==c.nodeType&&(d=this.rows[c.uid])){if(a&&(!d.depth||d.depth<=b))break;(a||d.depth)&&$(c).css("display","none");if(d.has_children&&d.expanded)d.expanded=!1,this.update_expando(d.uid,!1),this.triggerEvent("expandcollapse",{uid:d.uid,expanded:d.expanded})}c=c.nextSibling}return!1},expand_all:function(a){var b,
+c,d;a?(a.expanded=!0,b=a.depth,c=a.obj.nextSibling,this.update_expando(a.uid,!0),this.triggerEvent("expandcollapse",{uid:a.uid,expanded:a.expanded})):(c=this.list.tBodies[0].firstChild,b=0);for(;c;){if(1==c.nodeType&&(d=this.rows[c.uid])){if(a&&d.depth<=b)break;$(c).css("display","");if(d.has_children&&!d.expanded)d.expanded=!0,this.update_expando(d.uid,!0),this.triggerEvent("expandcollapse",{uid:d.uid,expanded:d.expanded})}c=c.nextSibling}return!1},update_expando:function(a,b){var c=document.getElementById("rcmexpando"+
+a);if(c)c.className=b?"expanded":"collapsed"},get_next_row:function(){if(!this.rows)return!1;for(var a=this.rows[this.last_selected],a=a?a.obj.nextSibling:null;a&&(1!=a.nodeType||"none"==a.style.display);)a=a.nextSibling;return a},get_prev_row:function(){if(!this.rows)return!1;for(var a=this.rows[this.last_selected],a=a?a.obj.previousSibling:null;a&&(1!=a.nodeType||"none"==a.style.display);)a=a.previousSibling;return a},get_first_row:function(){if(this.rowcount){var a,b,c=this.list.tBodies[0].rows;
+for(a=0,b=c.length-1;a<b;a++)if(c[a].id&&(""+c[a].id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i)&&null!=this.rows[RegExp.$1])return RegExp.$1}return null},get_last_row:function(){if(this.rowcount){var a,b=this.list.tBodies[0].rows;for(a=b.length-1;0<=a;a--)if(b[a].id&&(""+b[a].id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i)&&null!=this.rows[RegExp.$1])return RegExp.$1}return null},select_row:function(a,b,c){var d=this.selection.join(",");this.multiselect||(b=0);if(!this.shift_start)this.shift_start=a;if(b){switch(b){case SHIFT_KEY:this.shift_select(a,
+!1);break;case CONTROL_KEY:c||this.highlight_row(a,!0);break;case CONTROL_SHIFT_KEY:this.shift_select(a,!0);break;default:this.highlight_row(a,!1)}this.multi_selecting=!0}else this.shift_start=a,this.highlight_row(a,!1),this.multi_selecting=!1;this.selection.join(",")!=d&&this.triggerEvent("select");0!=this.last_selected&&this.rows[this.last_selected]&&$(this.rows[this.last_selected].obj).removeClass("focused");this.toggleselect&&this.last_selected==a?(this.clear_selection(),a=null):$(this.rows[a].obj).addClass("focused");
+if(!this.selection.length)this.shift_start=null;this.last_selected=a},select:function(a){this.select_row(a,!1);this.scrollto(a)},select_next:function(){var a=this.get_next_row(),b=this.get_prev_row();(a=a?a:b)&&this.select_row(a.uid,!1,!1)},select_first:function(a){var b=this.get_first_row();b&&(a?(this.shift_select(b,a),this.triggerEvent("select"),this.scrollto(b)):this.select(b))},select_last:function(a){var b=this.get_last_row();b&&(a?(this.shift_select(b,a),this.triggerEvent("select"),this.scrollto(b)):
+this.select(b))},select_childs:function(a){if(this.rows[a]&&this.rows[a].has_children)for(var b=this.rows[a].depth,a=this.rows[a].obj.nextSibling;a;){if(1==a.nodeType&&(r=this.rows[a.uid])){if(!r.depth||r.depth<=b)break;this.in_selection(r.uid)||this.select_row(r.uid,CONTROL_KEY)}a=a.nextSibling}},shift_select:function(a,b){if(!this.rows[this.shift_start]||!this.selection.length)this.shift_start=a;var c,d=this.rows[this.shift_start].obj.rowIndex,e=this.rows[a].obj.rowIndex,f=d<e?d:e,d=d>e?d:e;for(c in this.rows)this.rows[c].obj.rowIndex>=
+f&&this.rows[c].obj.rowIndex<=d?this.in_selection(c)||this.highlight_row(c,!0):this.in_selection(c)&&!b&&this.highlight_row(c,!0)},in_selection:function(a){for(var b in this.selection)if(this.selection[b]==a)return!0;return!1},select_all:function(a){if(!this.rows||!this.rows.length)return!1;var b,c=this.selection.join(",");this.selection=[];for(b in this.rows)!a||!0==this.rows[b][a]?(this.last_selected=b,this.highlight_row(b,!0)):$(this.rows[b].obj).removeClass("selected").removeClass("unfocused");
+this.selection.join(",")!=c&&this.triggerEvent("select");this.focus();return!0},invert_selection:function(){if(!this.rows||!this.rows.length)return!1;var a,b=this.selection.join(",");for(a in this.rows)this.highlight_row(a,!0);this.selection.join(",")!=b&&this.triggerEvent("select");this.focus();return!0},clear_selection:function(a){var b,c=this.selection.length;if(a)for(b in this.selection){if(this.selection[b]==a){this.selection.splice(b,1);break}}else{for(b in this.selection)this.rows[this.selection[b]]&&
+$(this.rows[this.selection[b]].obj).removeClass("selected").removeClass("unfocused");this.selection=[]}c&&!this.selection.length&&this.triggerEvent("select")},get_selection:function(){return this.selection},get_single_selection:function(){return 1==this.selection.length?this.selection[0]:null},highlight_row:function(a,b){if(this.rows[a]&&!b){if(1<this.selection.length||!this.in_selection(a))this.clear_selection(),this.selection[0]=a,$(this.rows[a].obj).addClass("selected")}else if(this.rows[a])if(this.in_selection(a)){var c=
+$.inArray(a,this.selection),d=this.selection.slice(0,c),c=this.selection.slice(c+1,this.selection.length);this.selection=d.concat(c);$(this.rows[a].obj).removeClass("selected").removeClass("unfocused")}else this.selection[this.selection.length]=a,$(this.rows[a].obj).addClass("selected")},key_press:function(a){var b=a.target||{};if(!0!=this.focused||"INPUT"==b.nodeName||"TEXTAREA"==b.nodeName||"SELECT"==b.nodeName)return!0;var b=rcube_event.get_keycode(a),c=rcube_event.get_modifier(a);switch(b){case 40:case 38:case 63233:case 63232:return rcube_event.cancel(a),
+this.use_arrow_key(b,c);case 61:case 107:case 109:case 32:return rcube_event.cancel(a),a=this.use_plusminus_key(b,c),this.key_pressed=b,this.modkey=c,this.triggerEvent("keypress"),this.modkey=0,a;case 36:return this.select_first(c),rcube_event.cancel(a);case 35:return this.select_last(c),rcube_event.cancel(a);default:if(this.key_pressed=b,this.modkey=c,this.triggerEvent("keypress"),this.modkey=0,this.key_pressed==this.BACKSPACE_KEY)return rcube_event.cancel(a)}return!0},key_down:function(a){var b=
+a.target||{};if(!0!=this.focused||"INPUT"==b.nodeName||"TEXTAREA"==b.nodeName||"SELECT"==b.nodeName)return!0;switch(rcube_event.get_keycode(a)){case 27:if(this.drag_active)return this.drag_mouse_up(a);if(this.col_drag_active)return this.selected_column=null,this.column_drag_mouse_up(a);case 40:case 38:case 63233:case 63232:case 61:case 107:case 109:case 32:if(!rcube_event.get_modifier(a)&&this.focused)return rcube_event.cancel(a)}return!0},use_arrow_key:function(a,b){var c;if(40==a||63233==a)c=this.get_next_row();
+else if(38==a||63232==a)c=this.get_prev_row();c&&(this.select_row(c.uid,b,!1),this.scrollto(c.uid));return!1},use_plusminus_key:function(a,b){var c=this.rows[this.last_selected];if(c)return 32==a&&(a=c.expanded?109:61),61==a||107==a?b==CONTROL_KEY||this.multiexpand?this.expand_all(c):this.expand(c):b==CONTROL_KEY||this.multiexpand?this.collapse_all(c):this.collapse(c),this.update_expando(c.uid,c.expanded),!1},scrollto:function(a){var b=this.rows[a].obj;if(b&&this.frame){var c=Number(b.offsetTop);
+!c&&this.rows[a].parent_uid&&(this.expand_all(this.rows[this.find_root(this.rows[a].uid)]),c=Number(b.offsetTop));if(c<Number(this.frame.scrollTop))this.frame.scrollTop=c;else if(c+Number(b.offsetHeight)>Number(this.frame.scrollTop)+Number(this.frame.offsetHeight))this.frame.scrollTop=c+Number(b.offsetHeight)-Number(this.frame.offsetHeight)}},drag_mouse_move:function(a){if("touchmove"==a.type)if(1==a.changedTouches.length)a=rcube_event.touchevent(a.changedTouches[0]);else return rcube_event.cancel(a);
+if(this.drag_start){var b=rcube_event.get_mouse_pos(a);if(!this.drag_mouse_start||3>Math.abs(b.x-this.drag_mouse_start.x)&&3>Math.abs(b.y-this.drag_mouse_start.y))return!1;if(!this.draglayer)this.draglayer=$("<div>").attr("id","rcmdraglayer").css({position:"absolute",display:"none","z-index":2E3}).appendTo(document.body);var c,d,e=$.merge([],this.selection);for(c in e)d=e[c],this.rows[d].has_children&&!this.rows[d].expanded&&this.select_childs(d);this.draglayer.html("");for(c=0;c<this.selection.length;c++){if(12<
+c){this.draglayer.append("...");break}if(e=this.rows[this.selection[c]].obj)for(b=0,d=0;d<e.childNodes.length;d++)if("TD"==e.childNodes[d].nodeName){if(0==c)this.drag_start_pos=$(e.childNodes[d]).offset();if(0>this.subject_col||0<=this.subject_col&&this.subject_col==b){for(var f,g,h=e.childNodes[d].childNodes,b=0;b<h.length;b++)if((g=e.childNodes[d].childNodes[b])&&(3==g.nodeType||"A"==g.nodeName))f=g;if(!f)break;d=$(f).text();d=$.trim(d);d=50<d.length?d.substring(0,50)+"...":d;this.draglayer.append($("<div>").text(d));
+break}b++}}this.draglayer.show();this.drag_active=!0;this.triggerEvent("dragstart")}this.drag_active&&this.draglayer&&(c=rcube_event.get_mouse_pos(a),this.draglayer.css({left:c.x+20+"px",top:c.y-5+(bw.ie?document.documentElement.scrollTop:0)+"px"}),this.triggerEvent("dragmove",a?a:window.event));return this.drag_start=!1},drag_mouse_up:function(a){document.onmousemove=null;if("touchend"==a.type&&1!=a.changedTouches.length)return rcube_event.cancel(a);this.draglayer&&this.draglayer.is(":visible")&&
+(this.drag_start_pos?this.draglayer.animate(this.drag_start_pos,300,"swing").hide(20):this.draglayer.hide());this.drag_active&&this.focus();this.drag_active=!1;rcube_event.remove_listener({event:"mousemove",object:this,method:"drag_mouse_move"});rcube_event.remove_listener({event:"mouseup",object:this,method:"drag_mouse_up"});if(bw.iphone||bw.ipad)rcube_event.remove_listener({event:"touchmove",object:this,method:"drag_mouse_move"}),rcube_event.remove_listener({event:"touchend",object:this,method:"drag_mouse_up"});
+this.del_dragfix();this.triggerEvent("dragend");return rcube_event.cancel(a)},column_drag_mouse_move:function(a){if(this.drag_start){var b;b=rcube_event.get_mouse_pos(a);if(!this.drag_mouse_start||3>Math.abs(b.x-this.drag_mouse_start.x)&&3>Math.abs(b.y-this.drag_mouse_start.y))return!1;if(!this.col_draglayer){b=$(this.list).offset();var c=this.list.tHead.rows[0].cells;this.col_draglayer=$("<div>").attr("id","rcmcoldraglayer").css(b).css({position:"absolute","z-index":2001,"background-color":"white",
+opacity:0.75,height:this.frame.offsetHeight-2+"px",width:this.frame.offsetWidth-2+"px"}).appendTo(document.body).append($("<div>").attr("id","rcmcolumnindicator").css({position:"absolute","border-right":"2px dotted #555","z-index":2002,height:this.frame.offsetHeight-2+"px"}));this.cols=[];this.list_pos=this.list_min_pos=b.left;for(b=0;b<c.length;b++)this.cols[b]=c[b].offsetWidth,null!==this.column_fixed&&b<=this.column_fixed&&(this.list_min_pos+=this.cols[b])}this.col_draglayer.show();this.col_drag_active=
+!0;this.triggerEvent("column_dragstart")}if(this.col_drag_active&&this.col_draglayer){var c=0,d=rcube_event.get_mouse_pos(a);for(b=0;b<this.cols.length;b++)if(d.x>=this.cols[b]/2+this.list_pos+c)c+=this.cols[b];else break;0==b&&this.list_min_pos>d.x?c=this.list_min_pos-this.list_pos:!this.list.rowcount&&b==this.cols.length&&(c-=2);$("#rcmcolumnindicator").css({width:c+"px"});this.triggerEvent("column_dragmove",a?a:window.event)}return this.drag_start=!1},column_drag_mouse_up:function(a){document.onmousemove=
+null;if(this.col_draglayer)this.col_draglayer.remove(),this.col_draglayer=null;this.col_drag_active&&this.focus();this.col_drag_active=!1;rcube_event.remove_listener({event:"mousemove",object:this,method:"column_drag_mouse_move"});rcube_event.remove_listener({event:"mouseup",object:this,method:"column_drag_mouse_up"});this.del_dragfix();if(null!==this.selected_column&&this.cols&&this.cols.length){var b,c=0,d=rcube_event.get_mouse_pos(a);for(b=0;b<this.cols.length;b++)if(d.x>=this.cols[b]/2+this.list_pos+
+c)c+=this.cols[b];else break;b!=this.selected_column&&b!=this.selected_column+1&&this.column_replace(this.selected_column,b)}this.triggerEvent("column_dragend");return rcube_event.cancel(a)},add_dragfix:function(){$("iframe").each(function(){$('<div class="iframe-dragdrop-fix"></div>').css({background:"#fff",width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css($(this).offset()).appendTo(document.body)})},del_dragfix:function(){$("div.iframe-dragdrop-fix").each(function(){this.parentNode.removeChild(this)})},
+column_replace:function(a,b){var c;c=this.list.tHead.rows[0].cells;var d=c[a],e=c[b],f=document.createElement("td");e?c[0].parentNode.insertBefore(f,e):c[0].parentNode.appendChild(f);c[0].parentNode.replaceChild(d,f);for(r=0,c=this.list.tBodies[0].rows.length;r<c;r++)row=this.list.tBodies[0].rows[r],d=row.cells[a],e=row.cells[b],f=document.createElement("td"),e?row.insertBefore(f,e):row.appendChild(f),row.replaceChild(d,f);this.subject_col==a?this.subject_col=b>a?b-1:b:this.subject_col<a&&b<=this.subject_col?
+this.subject_col++:this.subject_col>a&&b>=this.subject_col&&this.subject_col--;this.triggerEvent("column_replace")}};rcube_list_widget.prototype.addEventListener=rcube_event_engine.prototype.addEventListener;rcube_list_widget.prototype.removeEventListener=rcube_event_engine.prototype.removeEventListener;rcube_list_widget.prototype.triggerEvent=rcube_event_engine.prototype.triggerEvent;
index e47b5470d6692c222a1392f0c67fc2501e7fdda0..c8864c31897f815afcfdff63d065eb23059683ee 100644 (file)
@@ -13,7 +13,7 @@
  | Requires: common.js                                                   |
  +-----------------------------------------------------------------------+
 
-  $Id: list.js 4750 2011-05-12 09:27:17Z alec $
+  $Id: list.js 5271 2011-09-22 20:51:42Z thomasb $
 */
 
 
@@ -36,7 +36,7 @@ function rcube_list_widget(list, p)
   this.colcount = 0;
 
   this.subject_col = -1;
-  this.shiftkey = false;
+  this.modkey = 0;
   this.multiselect = false;
   this.multiexpand = false;
   this.multi_selecting = false;
@@ -648,7 +648,7 @@ select_row: function(id, mod_key, with_mouse)
       case CONTROL_KEY:
         if (!with_mouse)
           this.highlight_row(id, true);
-        break; 
+        break;
 
       case CONTROL_SHIFT_KEY:
         this.shift_select(id, true);
@@ -954,7 +954,8 @@ highlight_row: function(id, multiple)
  */
 key_press: function(e)
 {
-  if (this.focused != true)
+  var target = e.target || {};
+  if (this.focused != true || target.nodeName == 'INPUT' || target.nodeName == 'TEXTAREA' || target.nodeName == 'SELECT')
     return true;
 
   var keyCode = rcube_event.get_keycode(e),
@@ -962,7 +963,7 @@ key_press: function(e)
 
   switch (keyCode) {
     case 40:
-    case 38: 
+    case 38:
     case 63233: // "down", in safari keypress
     case 63232: // "up", in safari keypress
       // Stop propagation so that the browser doesn't scroll
@@ -976,7 +977,9 @@ key_press: function(e)
       rcube_event.cancel(e);
       var ret = this.use_plusminus_key(keyCode, mod_key);
       this.key_pressed = keyCode;
+      this.modkey = mod_key;
       this.triggerEvent('keypress');
+      this.modkey = 0;
       return ret;
     case 36: // Home
       this.select_first(mod_key);
@@ -985,11 +988,10 @@ key_press: function(e)
       this.select_last(mod_key);
       return rcube_event.cancel(e);
     default:
-      this.shiftkey = e.shiftKey;
       this.key_pressed = keyCode;
+      this.modkey = mod_key;
       this.triggerEvent('keypress');
-      // reset shiftkey flag, we need it only for registered events
-      this.shiftkey = false;
+      this.modkey = 0;
 
       if (this.key_pressed == this.BACKSPACE_KEY)
         return rcube_event.cancel(e);
@@ -1003,13 +1005,17 @@ key_press: function(e)
  */
 key_down: function(e)
 {
+  var target = e.target || {};
+  if (this.focused != true || target.nodeName == 'INPUT' || target.nodeName == 'TEXTAREA' || target.nodeName == 'SELECT')
+    return true;
+  
   switch (rcube_event.get_keycode(e)) {
     case 27:
       if (this.drag_active)
-           return this.drag_mouse_up(e);
+      return this.drag_mouse_up(e);
       if (this.col_drag_active) {
         this.selected_column = null;
-           return this.column_drag_mouse_up(e);
+        return this.column_drag_mouse_up(e);
       }
 
     case 40:
@@ -1044,7 +1050,7 @@ use_arrow_key: function(keyCode, mod_key)
     new_row = this.get_prev_row();
 
   if (new_row) {
-    this.select_row(new_row.uid, mod_key, true);
+    this.select_row(new_row.uid, mod_key, false);
     this.scrollto(new_row.uid);
   }
 
index 1ab16055bebb5aeb88733e37bc082fa101b2d34a..9fc96eac7bb519aeb951639babf4e1e5242d721c 100644 (file)
@@ -145,7 +145,6 @@ class html2text
     var $search = array(
         "/\r/",                                  // Non-legal carriage return
         "/[\n\t]+/",                             // Newlines and tabs
-        '/[ ]{2,}/',                             // Runs of spaces, pre-handling
         '/<script[^>]*>.*?<\/script>/i',         // <script>s -- which strip_tags supposedly has problems with
         '/<style[^>]*>.*?<\/style>/i',           // <style>s -- which strip_tags supposedly has problems with
         '/<p[^>]*>/i',                           // <P>
@@ -161,22 +160,6 @@ class html2text
         '/(<table[^>]*>|<\/table>)/i',           // <table> and </table>
         '/(<tr[^>]*>|<\/tr>)/i',                 // <tr> and </tr>
         '/<td[^>]*>(.*?)<\/td>/i',               // <td> and </td>
-        '/&(nbsp|#160);/i',                      // Non-breaking space
-        '/&(quot|rdquo|ldquo|#8220|#8221|#147|#148);/i',
-                                                        // Double quotes
-        '/&(apos|rsquo|lsquo|#8216|#8217);/i',   // Single quotes
-        '/&gt;/i',                               // Greater-than
-        '/&lt;/i',                               // Less-than
-        '/&(copy|#169);/i',                      // Copyright
-        '/&(trade|#8482|#153);/i',               // Trademark
-        '/&(reg|#174);/i',                       // Registered
-        '/&(mdash|#151|#8212);/i',               // mdash
-        '/&(ndash|minus|#8211|#8722);/i',        // ndash
-        '/&(bull|#149|#8226);/i',                // Bullet
-        '/&(pound|#163);/i',                     // Pound sign
-        '/&(euro|#8364);/i',                     // Euro sign
-        '/&(amp|#38);/i',                        // Ampersand: see _converter()
-        '/[ ]{2,}/'                              // Runs of spaces, post-handling
     );
 
     /**
@@ -189,7 +172,6 @@ class html2text
     var $replace = array(
         '',                                     // Non-legal carriage return
         ' ',                                    // Newlines and tabs
-        ' ',                                    // Runs of spaces, pre-handling
         '',                                     // <script>s -- which strip_tags supposedly has problems with
         '',                                     // <style>s -- which strip_tags supposedly has problems with
         "\n\n",                                 // <P>
@@ -205,6 +187,43 @@ class html2text
         "\n\n",                                 // <table> and </table>
         "\n",                                   // <tr> and </tr>
         "\t\t\\1\n",                            // <td> and </td>
+    );
+
+    /**
+     *  List of preg* regular expression patterns to search for,
+     *  used in conjunction with $ent_replace.
+     *
+     *  @var array $ent_search
+     *  @access public
+     *  @see $ent_replace
+     */
+    var $ent_search = array(
+        '/&(nbsp|#160);/i',                      // Non-breaking space
+        '/&(quot|rdquo|ldquo|#8220|#8221|#147|#148);/i',
+                                                        // Double quotes
+        '/&(apos|rsquo|lsquo|#8216|#8217);/i',   // Single quotes
+        '/&gt;/i',                               // Greater-than
+        '/&lt;/i',                               // Less-than
+        '/&(copy|#169);/i',                      // Copyright
+        '/&(trade|#8482|#153);/i',               // Trademark
+        '/&(reg|#174);/i',                       // Registered
+        '/&(mdash|#151|#8212);/i',               // mdash
+        '/&(ndash|minus|#8211|#8722);/i',        // ndash
+        '/&(bull|#149|#8226);/i',                // Bullet
+        '/&(pound|#163);/i',                     // Pound sign
+        '/&(euro|#8364);/i',                     // Euro sign
+        '/&(amp|#38);/i',                        // Ampersand: see _converter()
+        '/[ ]{2,}/',                             // Runs of spaces, post-handling
+    );
+
+    /**
+     *  List of pattern replacements corresponding to patterns searched.
+     *
+     *  @var array $ent_replace
+     *  @access public
+     *  @see $ent_search
+     */
+    var $ent_replace = array(
         ' ',                                    // Non-breaking space
         '"',                                    // Double quotes
         "'",                                    // Single quotes
@@ -219,7 +238,7 @@ class html2text
         '£',
         'EUR',                                  // Euro sign. \80 ?
         '|+|amp|+|',                            // Ampersand: see _converter()
-        ' '                                     // Runs of spaces, post-handling
+        ' ',                                    // Runs of spaces, post-handling
     );
 
     /**
@@ -303,7 +322,7 @@ class html2text
      *  @see _build_link_list()
      */
     var $_link_list = '';
-    
+
     /**
      *  Number of valid links detected in the text, used for plain text
      *  display (rendered similar to footnotes).
@@ -314,15 +333,15 @@ class html2text
      */
     var $_link_count = 0;
 
-    /** 
-     * Boolean flag, true if a table of link URLs should be listed after the text. 
-     *  
-     * @var boolean $_do_links 
-     * @access private 
-     * @see html2text() 
+    /**
+     * Boolean flag, true if a table of link URLs should be listed after the text.
+     *
+     * @var boolean $_do_links
+     * @access private
+     * @see html2text()
      */
     var $_do_links = true;
+
     /**
      *  Constructor.
      *
@@ -492,15 +511,21 @@ class html2text
         // Convert <PRE>
         $this->_convert_pre($text);
 
-        // Run our defined search-and-replace
+        // Run our defined tags search-and-replace
         $text = preg_replace($this->search, $this->replace, $text);
 
+        // Run our defined tags search-and-replace with callback
+        $text = preg_replace_callback($this->callback_search, array('html2text', '_preg_callback'), $text);
+
+        // Strip any other HTML tags
+        $text = strip_tags($text, $this->allowed_tags);
+
+        // Run our defined entities/characters search-and-replace
+        $text = preg_replace($this->ent_search, $this->ent_replace, $text);
+
         // Replace known html entities
         $text = html_entity_decode($text, ENT_COMPAT, 'UTF-8');
 
-        // Run our defined search-and-replace with callback
-        $text = preg_replace_callback($this->callback_search, array('html2text', '_preg_callback'), $text);
-
         // Remove unknown/unhandled entities (this cannot be done in search-and-replace block)
         $text = preg_replace('/&([a-zA-Z0-9]{2,6}|#[0-9]{2,4});/', '', $text);
 
@@ -508,15 +533,12 @@ class html2text
         // This properly handles situation of "&amp;quot;" in input string
         $text = str_replace('|+|amp|+|', '&', $text);
 
-        // Strip any other HTML tags
-        $text = strip_tags($text, $this->allowed_tags);
-
         // Bring down number of empty lines to 2 max
         $text = preg_replace("/\n\s+\n/", "\n\n", $text);
         $text = preg_replace("/[\n]{3,}/", "\n\n", $text);
 
         // remove leading empty lines (can be produced by eg. P tag on the beginning)
-        $text = preg_replace('/^\n+/', '', $text);
+        $text = ltrim($text, "\n");
 
         // Wrap the text to a readable format
         // for PHP versions >= 4.0.2. Default width is 75
@@ -544,9 +566,7 @@ class html2text
            if ( !$this->_do_links )
                return $display;
 
-           if ( substr($link, 0, 7) == 'http://' || substr($link, 0, 8) == 'https://' ||
-            substr($link, 0, 7) == 'mailto:'
-        ) {
+           if ( preg_match('!^(https?://|mailto:)!', $link) ) {
             $this->_link_count++;
             $this->_link_list .= '[' . $this->_link_count . "] $link\n";
             $additional = ' [' . $this->_link_count . ']';
index a5eeb841b880413b213a9b1d24f6103627044f1e..f8c3251ad93eb9377ceb117b02061f2e3ec0f19e 100644 (file)
 class washtml
 {
   /* Allowed HTML elements (default) */
-  static $html_elements = array('a', 'abbr', 'acronym', 'address', 'area', 'b', 'basefont', 'bdo', 'big', 'blockquote', 'br', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'ins', 'label', 'legend', 'li', 'map', 'menu', 'nobr', 'ol', 'p', 'pre', 'q', 's', 'samp', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'wbr', 'img');
-  
+  static $html_elements = array('a', 'abbr', 'acronym', 'address', 'area', 'b',
+    'basefont', 'bdo', 'big', 'blockquote', 'br', 'caption', 'center',
+    'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl',
+    'dt', 'em', 'fieldset', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i',
+    'ins', 'label', 'legend', 'li', 'map', 'menu', 'nobr', 'ol', 'p', 'pre', 'q',
+    's', 'samp', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'table',
+    'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'wbr', 'img',
+    // form elements
+    'button', 'input', 'textarea', 'select', 'option', 'optgroup'
+  );
+
   /* Ignore these HTML tags and their content */
   static $ignore_elements = array('script', 'applet', 'embed', 'object', 'style');
-  
+
   /* Allowed HTML attributes */
-  static $html_attribs = array('name', 'class', 'title', 'alt', 'width', 'height', 'align', 'nowrap', 'col', 'row', 'id', 'rowspan', 'colspan', 'cellspacing', 'cellpadding', 'valign', 'bgcolor', 'color', 'border', 'bordercolorlight', 'bordercolordark', 'face', 'marginwidth', 'marginheight', 'axis', 'border', 'abbr', 'char', 'charoff', 'clear', 'compact', 'coords', 'vspace', 'hspace', 'cellborder', 'size', 'lang', 'dir');  
+  static $html_attribs = array('name', 'class', 'title', 'alt', 'width', 'height',
+    'align', 'nowrap', 'col', 'row', 'id', 'rowspan', 'colspan', 'cellspacing',
+    'cellpadding', 'valign', 'bgcolor', 'color', 'border', 'bordercolorlight',
+    'bordercolordark', 'face', 'marginwidth', 'marginheight', 'axis', 'border',
+    'abbr', 'char', 'charoff', 'clear', 'compact', 'coords', 'vspace', 'hspace',
+    'cellborder', 'size', 'lang', 'dir',
+    // attributes of form elements
+    'type', 'rows', 'cols', 'disabled', 'readonly', 'checked', 'multiple', 'value'
+  );
 
   /* Block elements which could be empty but cannot be returned in short form (<tag />) */
-  static $block_elements = array('div', 'p', 'pre', 'blockquote', 'a', 'font', 'center', 'table', 'ul', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'dl', 'strong', 'i', 'b');
-  
+  static $block_elements = array('div', 'p', 'pre', 'blockquote', 'a', 'font', 'center',
+    'table', 'ul', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'dl', 'strong', 'i', 'b', 'u');
+
   /* State for linked objects in HTML */
   public $extlinks = false;
 
@@ -100,7 +118,7 @@ class washtml
 
   /* Registered callback functions for tags */
   private $handlers = array();
-  
+
   /* Allowed HTML elements */
   private $_html_elements = array();
 
@@ -112,7 +130,7 @@ class washtml
 
   /* Allowed HTML attributes */
   private $_html_attribs = array();
-  
+
 
   /* Constructor */
   public function __construct($p = array()) {
@@ -123,13 +141,13 @@ class washtml
     unset($p['html_elements'], $p['html_attribs'], $p['ignore_elements'], $p['block_elements']);
     $this->config = $p + array('show_washed'=>true, 'allow_remote'=>false, 'cid_map'=>array());
   }
-  
+
   /* Register a callback function for a certain tag */
   public function add_callback($tagName, $callback)
   {
     $this->handlers[$tagName] = $callback;
   }
-  
+
   /* Check CSS style */
   private function wash_style($style) {
     $s = '';
@@ -143,14 +161,14 @@ class washtml
           preg_match('/^(url\(\s*[\'"]?([^\'"\)]*)[\'"]?\s*\)'./*1,2*/
                  '|rgb\(\s*[0-9]+\s*,\s*[0-9]+\s*,\s*[0-9]+\s*\)'.
                  '|-?[0-9.]+\s*(em|ex|px|cm|mm|in|pt|pc|deg|rad|grad|ms|s|hz|khz|%)?'.
-                 '|#[0-9a-f]{3,6}|[a-z0-9\-]+'.
+                 '|#[0-9a-f]{3,6}|[a-z0-9", -]+'.
                  ')\s*/i', $str, $match)) {
           if ($match[2]) {
             if (($src = $this->config['cid_map'][$match[2]])
                 || ($src = $this->config['cid_map'][$this->config['base_url'].$match[2]])) {
               $value .= ' url('.htmlspecialchars($src, ENT_QUOTES) . ')';
             }
-            else if (preg_match('/^(http|https|ftp):.*$/i', $match[2], $url)) {
+            else if (preg_match('!^(https?:)?//[a-z0-9/._+-]+$!i', $match[2], $url)) {
               if ($this->config['allow_remote'])
                 $value .= ' url('.htmlspecialchars($url[0], ENT_QUOTES).')';
               else
@@ -160,8 +178,9 @@ class washtml
               $value .= ' url('.htmlspecialchars($match[2], ENT_QUOTES).')';
             }
           }
-          else if ($match[0] != 'url' && $match[0] != 'rbg') //whitelist ?
+          else if ($match[0] != 'url' && $match[0] != 'rgb') //whitelist ?
             $value .= ' ' . $match[0];
+
           $str = substr($str, strlen($match[0]));
         }
         if ($value)
@@ -182,8 +201,10 @@ class washtml
       if (isset($this->_html_attribs[$key]) ||
          ($key == 'href' && preg_match('/^(http:|https:|ftp:|mailto:|#).+/i', $value)))
         $t .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES) . '"';
-      else if ($key == 'style' && ($style = $this->wash_style($value)))
-        $t .= ' style="' . $style . '"';
+      else if ($key == 'style' && ($style = $this->wash_style($value))) {
+        $quot = strpos($style, '"') !== false ? "'" : '"';
+        $t .= ' style=' . $quot . $style . $quot;
+      }
       else if ($key == 'background' || ($key == 'src' && strtolower($node->tagName) == 'img')) { //check tagName anyway
         if (($src = $this->config['cid_map'][$value])
             || ($src = $this->config['cid_map'][$this->config['base_url'].$value])) {
@@ -222,7 +243,7 @@ class washtml
       case XML_ELEMENT_NODE: //Check element
         $tagName = strtolower($node->tagName);
         if ($callback = $this->handlers[$tagName]) {
-          $dump .= call_user_func($callback, $tagName, $this->wash_attribs($node), $this->dumpHtml($node));
+          $dump .= call_user_func($callback, $tagName, $this->wash_attribs($node), $this->dumpHtml($node), $this);
         }
         else if (isset($this->_html_elements[$tagName])) {
           $content = $this->dumpHtml($node);
@@ -280,6 +301,14 @@ class washtml
     return $this->dumpHtml($node);
   }
 
+  /**
+   * Getter for config parameters
+   */
+  public function get_config($prop)
+  {
+      return $this->config[$prop];
+  }
+
 }
 
 ?>
index 657917a2583af5fd9d74fec659faf4f760af2dca..04a48794a4e7bff1a1ca084f937935797c13e004 100644 (file)
@@ -13,7 +13,7 @@
 | Author: Ossama Khayat <okhayat@yahoo.com>                             |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -117,7 +117,7 @@ $labels['markread'] = 'كمقروءة';
 $labels['markunread'] = 'كغير مقروءة';
 $labels['markflagged'] = 'كموْسومة';
 $labels['markunflagged'] = 'كغير موْسومة';
-$labels['messageactions'] = 'إجراءات إضافية...';
+$labels['moreactions'] = 'إجراءات إضافية...';
 $labels['select'] = 'تحديد';
 $labels['all'] = 'الكل';
 $labels['none'] = 'لا شيء';
@@ -170,6 +170,8 @@ $labels['charset'] = 'مجموعة المحارف';
 $labels['editortype'] = 'نوع المُحرّر';
 $labels['returnreceipt'] = 'ايصال استلام';
 $labels['dsn'] = 'تنويه حالة التوصيل';
+$labels['mailreplyintro'] = 'كتب $sender في $date:';
+$labels['originalmessage'] = 'الرسالة الأساسية';
 $labels['editidents'] = 'تعديل الهويات';
 $labels['checkspelling'] = 'التدقيق الإملائي';
 $labels['resumeediting'] = 'متابعة التحرير';
@@ -187,6 +189,7 @@ $labels['highest'] = 'الأعلى';
 $labels['nosubject'] = '(دون موضوع)';
 $labels['showimages'] = 'إظهار الصور';
 $labels['alwaysshow'] = 'دائماً أظهر الصّور القادمة من $sender';
+$labels['isdraft'] = 'هذه مسودّة رسالة';
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'نص مجرد';
 $labels['savesentmessagein'] = 'احفظ الرسالة المُرسلة في';
@@ -245,6 +248,8 @@ $labels['typepager'] = 'الرنان';
 $labels['typevideo'] = 'الفيديو';
 $labels['typeassistant'] = 'المساعد';
 $labels['typehomepage'] = 'الرئيسية';
+$labels['typeblog'] = 'المدوّنة';
+$labels['typeprofile'] = 'الملف الشخصي';
 $labels['addfield'] = 'إضافة حقل...';
 $labels['addcontact'] = 'إضافة المراسل المحدد إلى دفتر عناويني';
 $labels['editcontact'] = 'تحرير بيانات المراسل';
@@ -266,7 +271,6 @@ $labels['print'] = 'طباعة';
 $labels['export'] = 'تصدير';
 $labels['exportvcards'] = 'صدّر المراسلين بنسق vCard';
 $labels['newcontactgroup'] = 'إنشاء مجموعة مراسلين جديدة';
-$labels['groupactions'] = 'إجراءات مجموعات المراسلين...';
 $labels['grouprename'] = 'تغيير اسم المجموعة';
 $labels['groupdelete'] = 'حذف المجموعة';
 $labels['previouspage'] = 'عرض المجموعة السابقة';
@@ -276,6 +280,8 @@ $labels['lastpage'] = 'عرض المجموعة الأخيرة';
 $labels['group'] = 'مجموعة';
 $labels['groups'] = 'المجموعات';
 $labels['personaladrbook'] = 'العناوين الشخصية';
+$labels['searchsave'] = 'حفظ البحث';
+$labels['searchdelete'] = 'حذف البحث';
 $labels['import'] = 'استورد';
 $labels['importcontacts'] = 'استورد المراسلين';
 $labels['importfromfile'] = 'استورد من ملف:';
@@ -284,6 +290,7 @@ $labels['importreplace'] = 'استبدل دفتر العناوين بأكمله'
 $labels['importtext'] = 'يمكنك رفع قائمة المراسلين من دفتر عناوين موجود.<br/>ندعم حالياً استيراد العناوين من نسق بيانات <a href="http://en.wikipedia.org/wiki/VCard">vCard</a>.';
 $labels['done'] = 'تم';
 $labels['settingsfor'] = 'إعدادات';
+$labels['about'] = 'نبذة';
 $labels['preferences'] = 'التفضيلات';
 $labels['userpreferences'] = 'تفضيلات المستخدم';
 $labels['editpreferences'] = 'تحرير تفضيلات المستخدم';
@@ -295,6 +302,8 @@ $labels['edititem'] = 'تحرير عنصر';
 $labels['preferhtml'] = 'تفضيل صيغة HTML';
 $labels['defaultcharset'] = 'ترميز المحارف الافتراضي';
 $labels['htmlmessage'] = 'رسالة بنسق HTML';
+$labels['dateformat'] = 'نسق التاريخ';
+$labels['timeformat'] = 'نسق الوقت';
 $labels['prettydate'] = 'تواريخ منمقة';
 $labels['setdefault'] = 'تعيين كإفتراضي';
 $labels['autodetect'] = 'آلي';
@@ -366,7 +375,13 @@ $labels['reqmdn'] = 'طلب إشعار استلام دائماً';
 $labels['reqdsn'] = 'طلب تنويه لحالة التوصيل دائماً';
 $labels['replysamefolder'] = 'وضع الردود في المجلد الذي يحوي الرسالة المردود عليها';
 $labels['defaultaddressbook'] = 'أضف المراسلين الجديد إلى دفتر العناوين المحدد';
+$labels['autocompletesingle'] = 'تجاهل عنوان البريد الإلكتروني البديل في التكملة التلقائية';
 $labels['spellcheckbeforesend'] = 'تحقق من الإملاء قبل إرسال الرسائل';
+$labels['spellcheckoptions'] = 'خيارات التدقيق الإملاءي';
+$labels['spellcheckignoresyms'] = 'تجاهل الكلمات التي تحتوي رموز';
+$labels['spellcheckignorenums'] = 'تجاهل الكلمات التي تحتوي أرقام';
+$labels['spellcheckignorecaps'] = 'تجاهل الكلمات التي بها أحرف كبيرة';
+$labels['addtodict'] = 'إضافة إلى القامس';
 $labels['folder'] = 'مجلد';
 $labels['folders'] = 'مجلدات';
 $labels['foldername'] = 'اسم المجلد';
@@ -391,6 +406,11 @@ $labels['sortby'] = 'ترتيب بحسب';
 $labels['sortasc'] = 'ترتيب تصاعدي';
 $labels['sortdesc'] = 'ترتيب تنازلي';
 $labels['undo'] = 'تراجع';
+$labels['plugin'] = 'إضافة';
+$labels['version'] = 'النسخة';
+$labels['source'] = 'المصدر';
+$labels['license'] = 'الترخيص';
+$labels['support'] = 'احصل على الدعم الفني';
 $labels['B'] = 'ب';
 $labels['KB'] = 'ك.ب';
 $labels['MB'] = 'م.ب';
index 8678cc28ce699ca19f98633d2892b0079748047a..8215d33b8a9a545802b59e041891da368bd34930 100644 (file)
@@ -13,7 +13,7 @@
 | Author: Ossama Khayat <okhayat@yahoo.com>                             |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -24,7 +24,7 @@ $messages['sessionerror'] = 'انتهت صلاحية الجلسة الحالية
 $messages['imaperror'] = 'فشل الاتصال بخادم IMAP';
 $messages['servererror'] = 'خطأ في الخادم!';
 $messages['servererrormsg'] = 'خطأ خادم: $msg';
-$messages['databaserror'] = 'خطأ في قاعدة البيانات!';
+$messages['dberror'] = 'خطأ في قاعدة البيانات!';
 $messages['errorreadonly'] = 'تعذر تنفيذ العملية. المجلد للقراءة فقط.';
 $messages['errornoperm'] = 'تعذر تنفيذ العملية. ليست لديك الصلاحية.';
 $messages['invalidrequest'] = 'طلب غير صالح! لم تحفظ أية بيانات.';
@@ -48,6 +48,7 @@ $messages['blockedimages'] = 'لحماية خصوصيتك، تم حجب الصو
 $messages['encryptedmessage'] = 'هذه الرسالة مشفرة ولا يمكن عرضها. عذراً!';
 $messages['nocontactsfound'] = 'لم يعثر على أي مراسل';
 $messages['contactnotfound'] = 'تعذر العثور على المراسل المطلوب';
+$messages['contactsearchonly'] = 'اكتب بعض كلمات البحث للعثور على المُراسلين';
 $messages['sendingfailed'] = 'فشل إرسال الرسالة';
 $messages['senttooquickly'] = 'رجاء انتظر $sec ثوان قبل إرسال هذه الرسالة';
 $messages['errorsavingsent'] = 'حدث خطأ أثناء حفظ الرسالة المُرسلة';
@@ -61,6 +62,7 @@ $messages['deletegroupconfirm'] = 'هل تريد فعلاً حذف المجمو
 $messages['deletemessagesconfirm'] = 'هل تريد حذف الرسائل المحددة؟';
 $messages['deletefolderconfirm'] = 'هل تريد حقاً حذف هذا المجلد؟';
 $messages['purgefolderconfirm'] = 'هل تريد حقاً حذف جميع الرسائل في هذا المجلد؟';
+$messages['contactdeleting'] = 'جاري حذف المُراسلين...';
 $messages['groupdeleting'] = 'جاري حذف المجموعة...';
 $messages['folderdeleting'] = 'جاري حذف المجلد...';
 $messages['foldermoving'] = 'جاري نقل المجلد...';
@@ -76,10 +78,10 @@ $messages['nosubjectwarning'] = 'حقل \"الموضوع\" فارغ. هل تري
 $messages['nobodywarning'] = 'إرسال هذه الرسالة دون نص؟';
 $messages['notsentwarning'] = 'لم يتم إرسال الرسالة. هل تريد تجاهل الرسالة؟';
 $messages['noldapserver'] = 'الرجاء اختيار خادم ldap للبحث فيه';
-$messages['nocontactsreturned'] = 'لم يعثر على أي مراسلين';
 $messages['nosearchname'] = 'الرجاء إدخال اسم مراسل أو عنوان بريد إلكتروني';
 $messages['notuploadedwarning'] = 'لم يتم رفع جميع المرفقات بعد. رجاءً الانتظار أو إلغاء عملية الرفع.';
 $messages['searchsuccessful'] = 'عثر على $nr رسائل';
+$messages['contactsearchsuccessful'] = 'عُثر على $nr مُراسَل';
 $messages['searchnomatch'] = 'لم يعثر على شيء';
 $messages['searching'] = 'جاري البحث...';
 $messages['checking'] = 'جاري التحقق...';
@@ -126,7 +128,6 @@ $messages['smtpautherror'] = 'خطأ SMTP ($code): تعذر التحقق من ه
 $messages['smtpfromerror'] = 'خطأ SMTP ($code): تعذر تحديد المرسل "$from" ($msg)';
 $messages['smtptoerror'] = 'خطأ SMTP ($code): تعذرت إضافة المستلم "$to" ($msg)';
 $messages['smtprecipientserror'] = 'خطأ SMTP: تعذرت قراءة قائمة المرسل إليهم';
-$messages['smtpdsnerror'] = 'خطأ SMTP: لا يوجد دعم لتنويهات حالة التوصيل';
 $messages['smtperror'] = 'خطأ SMTP: $msg';
 $messages['emailformaterror'] = 'عنوان بريد إلكتروني غير صالح: $email';
 $messages['toomanyrecipients'] = 'عدد المرسل إليهم كبير. قلص العدد إلى $max.';
@@ -139,11 +140,16 @@ $messages['contactrestored'] = 'تم استعادة المُراسل بنجاح.
 $messages['groupdeleted'] = 'تم حذف المجموعة بنجاح';
 $messages['grouprenamed'] = 'تم تغيير إسم المجموعة بنجاح';
 $messages['groupcreated'] = 'تم إنشاء المجموعة بنجاح';
+$messages['savedsearchdeleted'] = 'تم بنجاح حذف البحث المحفوظ.';
+$messages['savedsearchdeleteerror'] = 'تعذر حذف البحث المحفوظ';
+$messages['savedsearchcreated'] = 'تم بنجاح إنشاء البحث المحفوظ.';
+$messages['savedsearchcreateerror'] = 'تعذر إنشاء بحث محفوظ.';
 $messages['messagedeleted'] = 'تم حذف الرسائل بنجاح';
 $messages['messagemoved'] = 'تم نقل الرسائل بنجاح';
 $messages['messagecopied'] = 'تم نسخ الرسائل بنجاح';
 $messages['messagemarked'] = 'تم تحديد الرسائل بنجاح';
 $messages['autocompletechars'] = 'إكتب $min أحرف على الأقل للحصول على التكملة التلقائية';
+$messages['autocompletemore'] = 'هناك المزيد من النتائج المطابقة. رجاء اكتب كلمات بحث أكثر.';
 $messages['namecannotbeempty'] = 'لا يمكن ترك الإسم فارغاً';
 $messages['nametoolong'] = 'الإسم طويل جداً';
 $messages['folderupdated'] = 'تم تحديث المجلد بنجاح';
@@ -151,5 +157,6 @@ $messages['foldercreated'] = 'تم إنشاء المجلد بنجاح';
 $messages['invalidimageformat'] = 'ليست صيغة صورة صحيحة';
 $messages['mispellingsfound'] = 'عُثر على أخطاء إملائية في الرسالة';
 $messages['parentnotwritable'] = 'تعذر إنشاء/نقل المجلد إلى المجلد المحدد. ليست لديك الصلاحية.';
+$messages['messagetoobig'] = 'جزء الرسالة أكبر بكثير مما يمكن معالجته.';
 
 ?>
index da8c9c3a3bfa35a13803ec81fc5fa82b911012e6..dffa2c608aa2f74c895afee198be81ecb20bde69 100644 (file)
@@ -14,7 +14,7 @@
 |   Nickolay Bunev <just4nick@gmail.com>                                |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 4463 2011-01-28 16:03:31Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -101,18 +101,16 @@ $labels['replytomessage'] = 'Отговори на писмото';
 $labels['replytoallmessage'] = 'Отговор до изпращача и всички получатели';
 $labels['replyall'] = 'Отговор на всички';
 $labels['replylist'] = 'Списък за отговор';
+$labels['forwardinline'] = 'Препрати като част от съобщението';
+$labels['forwardattachment'] = 'Препрати като прикачен файл';
 $labels['forwardmessage'] = 'Препрати писмото';
 $labels['deletemessage'] = 'Изтрий писмото';
 $labels['movemessagetotrash'] = 'Премести писмото в кошчето';
 $labels['printmessage'] = 'Разпечатай писмото';
 $labels['previousmessage'] = 'Предишно писмо';
-$labels['previousmessages'] = 'Предишна страница';
 $labels['firstmessage'] = 'Първо писмо';
-$labels['firstmessages'] = 'Първа страница';
 $labels['nextmessage'] = 'Следващо писмо';
-$labels['nextmessages'] = 'Следваща страница';
 $labels['lastmessage'] = 'Последно писмо';
-$labels['lastmessages'] = 'Последна страница';
 $labels['backtolist'] = 'Обратно към списъка';
 $labels['viewsource'] = 'Виж като код';
 $labels['markmessages'] = 'Маркирай писмата';
@@ -120,7 +118,7 @@ $labels['markread'] = 'Като прочетени';
 $labels['markunread'] = 'Като нови';
 $labels['markflagged'] = 'Като отбелязани';
 $labels['markunflagged'] = 'Като неотбелязани';
-$labels['messageactions'] = 'Още действия';
+$labels['moreactions'] = 'Повече действия...';
 $labels['select'] = 'Избери';
 $labels['all'] = 'Всички';
 $labels['none'] = 'Нищо';
@@ -152,7 +150,7 @@ $labels['listcolumns'] = 'Колони за списък';
 $labels['listsorting'] = 'Колона за сортиране';
 $labels['listorder'] = 'Режим на сортиране';
 $labels['listmode'] = 'Кратък списък';
-$labels['folderactions'] = 'Десйтвия за папки...';
+$labels['folderactions'] = 'Действия за папки...';
 $labels['compact'] = 'Свий';
 $labels['empty'] = 'Изпразни';
 $labels['quota'] = 'Използвано място';
@@ -173,12 +171,15 @@ $labels['charset'] = 'Кодировка';
 $labels['editortype'] = 'Вид редактор';
 $labels['returnreceipt'] = 'Обратна разписка';
 $labels['dsn'] = 'Информиране при доставка';
+$labels['mailreplyintro'] = 'На $date, $sender написа:';
+$labels['originalmessage'] = 'Първоначалното съобщение';
 $labels['editidents'] = 'Редактиране на самоличности';
 $labels['checkspelling'] = 'Провери правописа';
 $labels['resumeediting'] = 'Продължи черновата';
 $labels['revertto'] = 'Върни се към';
 $labels['attachments'] = 'Прикачени файлове';
 $labels['upload'] = 'Качи';
+$labels['uploadprogress'] = '$percent ($current от $total)';
 $labels['close'] = 'Затвори';
 $labels['messageoptions'] = 'Настройки на съобщения...';
 $labels['low'] = 'Нисък';
@@ -189,6 +190,7 @@ $labels['highest'] = 'Най-висок';
 $labels['nosubject'] = '(няма заглавие)';
 $labels['showimages'] = 'Показвай изображения';
 $labels['alwaysshow'] = 'Винаги показвай изображения от $ ';
+$labels['isdraft'] = 'Това съобщение е чернова';
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'текстов';
 $labels['savesentmessagein'] = 'Запази съобщението в';
@@ -231,6 +233,24 @@ $labels['female'] = 'Жена';
 $labels['manager'] = 'Мениджър';
 $labels['assistant'] = 'Асистент';
 $labels['spouse'] = 'Съпруг (а)';
+$labels['allfields'] = 'Всички полета';
+$labels['search'] = 'Търсене';
+$labels['advsearch'] = 'Разширено търсене';
+$labels['other'] = 'Други';
+$labels['typehome'] = 'Начало';
+$labels['typework'] = 'Работа';
+$labels['typeother'] = 'Други';
+$labels['typemobile'] = 'Мобилни';
+$labels['typemain'] = 'Главни';
+$labels['typehomefax'] = 'Домашен факс';
+$labels['typeworkfax'] = 'Служебен факс';
+$labels['typecar'] = 'Автомобил';
+$labels['typepager'] = 'Пейджър';
+$labels['typevideo'] = 'Видео';
+$labels['typeassistant'] = 'Асистент';
+$labels['typehomepage'] = 'Начална страница';
+$labels['typeblog'] = 'Блог';
+$labels['typeprofile'] = 'Профил';
 $labels['addfield'] = 'Добави поле';
 $labels['addcontact'] = 'Добави';
 $labels['editcontact'] = 'Редактирай';
@@ -252,7 +272,8 @@ $labels['print'] = 'Разпечатай';
 $labels['export'] = 'Изнасяне';
 $labels['exportvcards'] = 'Изнасяне във vCard формат';
 $labels['newcontactgroup'] = 'Създаване на нова група';
-$labels['groupactions'] = 'Действия за групи от контакти...';
+$labels['grouprename'] = 'Преименувай група';
+$labels['groupdelete'] = 'Изтриване на група';
 $labels['previouspage'] = 'Предна страница';
 $labels['firstpage'] = 'Първа страница';
 $labels['nextpage'] = 'Следваща страница';
@@ -260,9 +281,12 @@ $labels['lastpage'] = 'Последна страница';
 $labels['group'] = 'Група';
 $labels['groups'] = 'Групи';
 $labels['personaladrbook'] = 'Лични адреси';
+$labels['searchsave'] = 'Запази търсенето';
+$labels['searchdelete'] = 'Изтрий търсенето';
 $labels['import'] = 'Внасяне';
 $labels['importcontacts'] = 'Внасяне';
 $labels['importfromfile'] = 'Внасяне от файл';
+$labels['importtarget'] = 'Добави нови контакти в книгата с адреси';
 $labels['importreplace'] = 'Замени цялата книга с адреси';
 $labels['importtext'] = 'Можете да внесете контакти от съществуваща книга с адреси. В момента поддържане внасянето на адреси от vCard формат за данни.';
 $labels['done'] = 'Извършено';
@@ -278,6 +302,8 @@ $labels['edititem'] = 'Редактиране на самоличност';
 $labels['preferhtml'] = 'Показвай първо HTML версия';
 $labels['defaultcharset'] = 'Подразбиращо се кодиране';
 $labels['htmlmessage'] = 'HTML съобщение';
+$labels['dateformat'] = 'Формат на датата';
+$labels['timeformat'] = 'Формат на времето';
 $labels['prettydate'] = 'Кратки дати';
 $labels['setdefault'] = 'По подразбиране';
 $labels['autodetect'] = 'Автоматично';
@@ -348,6 +374,14 @@ $labels['afternseconds'] = 'след $n секунди';
 $labels['reqmdn'] = 'Винаги връщай обратна разписка';
 $labels['reqdsn'] = 'Винаги изисквай отговор при доставка на съобщение';
 $labels['replysamefolder'] = 'Постави отговора в папката на съобщението, на което се отговаря';
+$labels['defaultaddressbook'] = 'Добави нови контакти в избраната книга с адреси';
+$labels['autocompletesingle'] = 'Пропусни алтернативните email адреси при автоматично попълване';
+$labels['spellcheckbeforesend'] = 'Провери за правописни грешки преди изпращане на съобщението';
+$labels['spellcheckoptions'] = 'Настройки на проверката за правопис';
+$labels['spellcheckignoresyms'] = 'Игнорирай думи съдържащи символи';
+$labels['spellcheckignorenums'] = 'Игнорирай думи съдържащи числа';
+$labels['spellcheckignorecaps'] = 'Игнорирай думи съдържащи единствено главни букви';
+$labels['addtodict'] = 'Добави в речника';
 $labels['folder'] = 'Папка';
 $labels['folders'] = 'Папки';
 $labels['foldername'] = 'Име на папката';
@@ -364,9 +398,14 @@ $labels['location'] = 'Местоположение';
 $labels['info'] = 'Информация';
 $labels['getfoldersize'] = 'Щракни за големина на папката';
 $labels['changesubscription'] = 'Щракни за промяна на абонамент';
+$labels['foldertype'] = 'Вид на папката';
+$labels['personalfolder'] = 'Лична папка';
+$labels['otherfolder'] = 'Папка на друг потребител';
+$labels['sharedfolder'] = 'Публична папка';
 $labels['sortby'] = 'Сортирай по';
 $labels['sortasc'] = 'Сортирай възходящо';
 $labels['sortdesc'] = 'Сортирай низходящо';
+$labels['undo'] = 'Отмени';
 $labels['B'] = 'Б';
 $labels['KB'] = 'КБ';
 $labels['MB'] = 'МБ';
index a23a63c9b6bf419e5a7f22f51ecbb50399f8841b..17eb50c92b31896338b0cf6e885eebdf3d17b326 100644 (file)
@@ -14,7 +14,7 @@
 |        Nickolay Bunev <just4nick@gmail.com>                                                          |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 4463 2011-01-28 16:03:31Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -25,6 +25,7 @@ $messages['sessionerror'] = 'Невалидна или изтекла сесия
 $messages['imaperror'] = 'Неуспешно свързване към IMAP сървъра';
 $messages['servererror'] = 'Грешка!';
 $messages['servererrormsg'] = 'Сървърна грешка: $msg';
+$messages['dberror'] = 'Грешка с базата данни!';
 $messages['errorreadonly'] = 'Операцията не може да бъде изпълнена. Папката е с права само за четене';
 $messages['errornoperm'] = 'Операцията не може да бъде изпълнена. Отказани права за достъп';
 $messages['invalidrequest'] = 'Невалидна заявка! Данните не са съхранени.';
@@ -33,6 +34,7 @@ $messages['loggedout'] = 'Довиждане!';
 $messages['mailboxempty'] = 'Кутията е празна';
 $messages['loading'] = 'Зареждане...';
 $messages['uploading'] = 'Качване на файла...';
+$messages['uploadingmany'] = 'Качване на файлове...';
 $messages['loadingdata'] = 'Зареждане на данни...';
 $messages['checkingmail'] = 'Проверка за нови писма...';
 $messages['sendingmessage'] = 'Изпращане на писмото...';
@@ -42,10 +44,12 @@ $messages['messagesaved'] = 'Писмото е записано в Чернов
 $messages['successfullysaved'] = 'Записано';
 $messages['addedsuccessfully'] = 'Контакта е добавен в адресната книга';
 $messages['contactexists'] = 'Контакт с този e-mail адрес вече същестува';
+$messages['contactnameexists'] = 'Контакт със същото име вече съществува';
 $messages['blockedimages'] = 'С оглед на Вашата сигурност, изображенията в това писмо са блокирани.';
 $messages['encryptedmessage'] = 'Това е кодирано писмо и не може да бъде показано. Съжаляваме!';
-$messages['nocontactsfound'] = 'Няма контакти';
+$messages['nocontactsfound'] = 'Ð\9dÑ\8fма Ð½Ð°Ð¼ÐµÑ\80ени ÐºÐ¾Ð½Ñ\82акÑ\82и';
 $messages['contactnotfound'] = 'Исканият контакт не е намерен';
+$messages['contactsearchonly'] = 'Въведете нещо в полето за търсене за да търсите контакти';
 $messages['sendingfailed'] = 'Изпращането неуспешно';
 $messages['senttooquickly'] = 'Моля, изчакайте $sec секунда(и) преди да изпратите съобщението';
 $messages['errorsavingsent'] = 'Възникна грешка при записването на съобщението';
@@ -55,9 +59,12 @@ $messages['errorcopying'] = 'Съобщенията не могат да бъд
 $messages['errordeleting'] = 'Писмото не може да бъде изтрито';
 $messages['errormarking'] = 'Съобщението не може да бъде маркирано';
 $messages['deletecontactconfirm'] = 'Искате ли да изтриете маркираните контакти?';
+$messages['deletegroupconfirm'] = 'Искате ли да изтриете избраната група?';
 $messages['deletemessagesconfirm'] = 'Искате ли да изтриете маркираните съобщения?';
 $messages['deletefolderconfirm'] = 'Искате ли да изтриете тази папка?';
 $messages['purgefolderconfirm'] = 'Искате ли да изтриете всички писма в тази папка?';
+$messages['contactdeleting'] = 'Изтриване на контакт(и)...';
+$messages['groupdeleting'] = 'Изтриване на група...';
 $messages['folderdeleting'] = 'Изтриване на папка...';
 $messages['foldermoving'] = 'Преместване на папка...';
 $messages['foldersubscribing'] = 'Абониране...';
@@ -72,10 +79,10 @@ $messages['nosubjectwarning'] = 'Полето "Заглавие" е празно
 $messages['nobodywarning'] = 'Изпрати това писмо без текст?';
 $messages['notsentwarning'] = 'Писмото не е изпратено. Искате ли да бъде унищожено?';
 $messages['noldapserver'] = 'Изберете LDAP сървър за търсене';
-$messages['nocontactsreturned'] = 'Не са намерени контакти';
 $messages['nosearchname'] = 'Моля, въведете Име на контакта или e-mail адрес';
 $messages['notuploadedwarning'] = 'Все още не са качени всички прикачени файлове. Моля изчакайте или откажете качването.';
 $messages['searchsuccessful'] = '$nr намерени писма';
+$messages['contactsearchsuccessful'] = '$nr намерени контакти.';
 $messages['searchnomatch'] = 'Търсенето не откри съвпадения';
 $messages['searching'] = 'Търсене...';
 $messages['checking'] = 'Проверка...';
@@ -94,10 +101,13 @@ $messages['copysuccess'] = 'Успешно копирани $nr адреса';
 $messages['copyerror'] = 'Грешка при копирането на адресите';
 $messages['sourceisreadonly'] = 'Този източник на адреси е само за четене';
 $messages['errorsavingcontact'] = 'Грешка при записването на адреса';
-$messages['movingmessage'] = 'Преместване на писмото...';
-$messages['copyingmessage'] = 'Копиране на съобщение...';
-$messages['deletingmessage'] = 'Изтриване на съобщение';
-$messages['markingmessage'] = 'Маркиране на съобщение';
+$messages['movingmessage'] = 'Преместване на съобщение...';
+$messages['copyingmessage'] = 'Копиране на съобщение(я)...';
+$messages['copyingcontact'] = 'Копиране на контакт(и)...';
+$messages['deletingmessage'] = 'Изтриване на съобщение(я)...';
+$messages['markingmessage'] = 'Маркиране на съобщение(я)...';
+$messages['addingmember'] = 'Добавяне на контакт(и) в групата...';
+$messages['removingmember'] = 'Премахване на контакт(и) от групата...';
 $messages['receiptsent'] = 'Обратната разписка е изпратена.';
 $messages['errorsendingreceipt'] = 'Грешка при изпращането на обратна разписка.';
 $messages['nodeletelastidentity'] = 'Не можете да изтриете тази самоличност, трябва да имате поне една.';
@@ -109,6 +119,7 @@ $messages['contactremovedfromgroup'] = 'Контактите бяха успеш
 $messages['importwait'] = 'Внасяне, моля изчакайте...';
 $messages['importerror'] = 'Внасянето неуспешно! Каченият файл не е във валиден vCard формат.';
 $messages['importconfirm'] = '<b>Успешно са внесени $inserted контакта, вече съществуващите $skipped контакта са пропуснати</b>:<p><em>$names</em></p>';
+$messages['importconfirmskipped'] = 'b>Съществуващите записи $skipped са пропуснати</b>';
 $messages['opnotpermitted'] = 'Операцията не е позволена!';
 $messages['nofromaddress'] = 'Липсва e-mail адрес за избраната самоличност';
 $messages['editorwarning'] = 'Превключването на редактора в текстов режим ще доведе до загуба на форматирането на текса. Сигурни ли сте, че искате да продължите?';
@@ -118,7 +129,6 @@ $messages['smtpautherror'] = 'SMTP грешка ($code): Грешни потре
 $messages['smtpfromerror'] = 'SMTP грешка ($code): Не може да бъде изпратено писмо от "$from" ($msg)';
 $messages['smtptoerror'] = 'SMTP грешка ($code): Не може да бъде изпратено писмо до "$to" ($msg)';
 $messages['smtprecipientserror'] = 'SMTP грешка: Не може да бъде обработен списъка с получатели';
-$messages['smtpdsnerror'] = 'SMTP грешка: Не се поддържа съобщаване за успешни получено съобщение';
 $messages['smtperror'] = 'SMTP грешка: $msg';
 $messages['emailformaterror'] = 'Невалиден e-mail адрес: $email';
 $messages['toomanyrecipients'] = 'Прекалено много адреси за изпращане (максимум: $max).';
@@ -126,18 +136,27 @@ $messages['maxgroupmembersreached'] = 'Броя на членовете на г
 $messages['internalerror'] = 'Възникна вътрешна грешка. Моля опитайте отново';
 $messages['contactdelerror'] = 'Не мога да изтрия контакта';
 $messages['contactdeleted'] = 'Контактът беше изтрит';
+$messages['contactrestoreerror'] = 'Неуспешно възстановяване на изтрите контакт(и).';
+$messages['contactrestored'] = 'Контактът(-ите) са възстановени успешно';
 $messages['groupdeleted'] = 'Групата беше  изтрита';
 $messages['grouprenamed'] = 'Групата беше преименувана ';
 $messages['groupcreated'] = 'Групата беше създадена';
+$messages['savedsearchdeleted'] = 'Запазаните търсения са изтрити успешно.';
+$messages['savedsearchdeleteerror'] = 'Изтриването на запазените търсения е неуспешно.';
+$messages['savedsearchcreated'] = 'Запазените търсения са създадени успешно.';
+$messages['savedsearchcreateerror'] = 'Създаването на запазено търсене е неуспешно';
 $messages['messagedeleted'] = 'Съобщението  беше  изтрито';
 $messages['messagemoved'] = 'Съобщението беше преместено';
 $messages['messagecopied'] = 'Съобщението беше копирано';
 $messages['messagemarked'] = 'Съобщението беше маркирано';
 $messages['autocompletechars'] = 'Въведете минимум $min знака, за да започне автоматичното попълване';
+$messages['autocompletemore'] = 'Намерените съвпадения са прекалено много. Моля напишете още символи.';
 $messages['namecannotbeempty'] = 'Полето за име не може да бъде празно';
 $messages['nametoolong'] = 'Името е прекалено дълго';
 $messages['folderupdated'] = 'Папката е обновена';
 $messages['foldercreated'] = 'Папката е създадена';
 $messages['invalidimageformat'] = 'Невалиден формат на изображението';
+$messages['mispellingsfound'] = 'В съобщението са намерени правописни грешки.';
+$messages['parentnotwritable'] = 'Създаването/преместването на папка в избраната родителска папка е неуспешно. Няма права за достъп.';
 
 ?>
index 3127dd0d56b71cbcb323c5d662ec87f928c725a8..d4d99db5061a8837daf2f5a0a1658e5aaf821198 100644 (file)
@@ -15,7 +15,7 @@
 |         Jordi Sanfeliu <jordi@fibranet.cat>                           |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5291 2011-09-28 17:10:45Z thomasb $
+@version $Id: labels.inc 5290 2011-09-28 17:09:50Z thomasb $
 
 */
 
index 30817b5827a4cccf420e75faf8c4a4cef6073224..e7b3d29e8df5b0ea507dfd63bb8de5f70b2bab22 100644 (file)
@@ -15,7 +15,7 @@
 |         Jordi Sanfeliu <jordi@fibranet.cat>                           |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5291 2011-09-28 17:10:45Z thomasb $
+@version $Id: messages.inc 5290 2011-09-28 17:09:50Z thomasb $
 
 */
 
index f63271d7c825acbab45572fa2fff4ea12b3d247f..cf66e1bce1d7c980a438ee61fd9f8d38ecbb9f24 100644 (file)
@@ -17,7 +17,7 @@
 |        Ales Pospichal <ales@pospichalales.info>                       |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 4671 2011-04-20 08:47:44Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -103,6 +103,8 @@ $labels['replytomessage'] = 'Odpovědět odesílateli';
 $labels['replytoallmessage'] = 'Odpovědět všem';
 $labels['replyall'] = 'Odpovědět všem';
 $labels['replylist'] = 'Odpovědět do e-mailové konference';
+$labels['forwardinline'] = 'Přeposlat v textu';
+$labels['forwardattachment'] = 'Přeposlat jako přílohu';
 $labels['forwardmessage'] = 'Předat zprávu';
 $labels['deletemessage'] = 'Odstranit zprávu';
 $labels['movemessagetotrash'] = 'Přesunout zprávu do koše';
@@ -118,7 +120,7 @@ $labels['markread'] = 'Jako přečtené';
 $labels['markunread'] = 'Jako nepřečtené';
 $labels['markflagged'] = 'Jako označené';
 $labels['markunflagged'] = 'Jako neoznačené';
-$labels['messageactions'] = 'Další akce...';
+$labels['moreactions'] = 'Další akce...';
 $labels['select'] = 'Vybrat';
 $labels['all'] = 'Vše';
 $labels['none'] = 'Nic';
@@ -171,12 +173,15 @@ $labels['charset'] = 'Znaková sada';
 $labels['editortype'] = 'Typ editoru zpráv';
 $labels['returnreceipt'] = 'Doručenka';
 $labels['dsn'] = 'Stav doručení';
+$labels['mailreplyintro'] = 'Dne $date, $sender napsal:';
+$labels['originalmessage'] = 'Původní zpráva';
 $labels['editidents'] = 'Editovat identity';
 $labels['checkspelling'] = 'Zkontrolovat pravopis';
 $labels['resumeediting'] = 'Pokračovat v úpravách';
 $labels['revertto'] = 'Přejít na';
 $labels['attachments'] = 'Přílohy';
 $labels['upload'] = 'Nahrát';
+$labels['uploadprogress'] = '$percent ($current z $total)';
 $labels['close'] = 'Zavřít';
 $labels['messageoptions'] = 'Nastavení zprávy...';
 $labels['low'] = 'Nízká';
@@ -223,11 +228,16 @@ $labels['birthday'] = 'Datum narození';
 $labels['anniversary'] = 'Výročí';
 $labels['website'] = 'WWW';
 $labels['instantmessenger'] = 'Komunikace (ICQ, messenger...)';
+$labels['notes'] = 'Poznámky';
 $labels['male'] = 'Muž';
 $labels['female'] = 'Žena';
 $labels['manager'] = 'Manažer';
 $labels['assistant'] = 'Asistent';
 $labels['spouse'] = 'Manžel/ka';
+$labels['allfields'] = 'Všechny položky';
+$labels['search'] = 'Hledat';
+$labels['advsearch'] = 'Pokročilé vyhledávání';
+$labels['other'] = 'Jiné';
 $labels['typehome'] = 'Domací';
 $labels['typework'] = 'Pracovní';
 $labels['typeother'] = 'Ostatní';
@@ -239,6 +249,9 @@ $labels['typecar'] = 'Automobil';
 $labels['typepager'] = 'Pager';
 $labels['typevideo'] = 'Video';
 $labels['typeassistant'] = 'Asistent';
+$labels['typehomepage'] = 'Domácí stránka';
+$labels['typeblog'] = 'Blog';
+$labels['typeprofile'] = 'Profil';
 $labels['addfield'] = 'Přidat položku';
 $labels['addcontact'] = 'Přidat kontakt';
 $labels['editcontact'] = 'Upravit kontakt';
@@ -260,7 +273,8 @@ $labels['print'] = 'Tisk';
 $labels['export'] = 'Export';
 $labels['exportvcards'] = 'Exportovat kontakty ve formátu vCard';
 $labels['newcontactgroup'] = 'Vytvořit novou skupinu kontaktů';
-$labels['groupactions'] = 'Akce pro skupiny kontaktů...';
+$labels['grouprename'] = 'Přejmenovat skupinu';
+$labels['groupdelete'] = 'Smazat skupinu';
 $labels['previouspage'] = 'Předchozí';
 $labels['firstpage'] = 'Zobrazit první zprávy';
 $labels['nextpage'] = 'Další';
@@ -268,9 +282,12 @@ $labels['lastpage'] = 'Zobrazit poslední zprávy';
 $labels['group'] = 'Skupina';
 $labels['groups'] = 'Skupiny';
 $labels['personaladrbook'] = 'Osobní kontakty';
+$labels['searchsave'] = 'Uložit hledání';
+$labels['searchdelete'] = 'Smazat hledání';
 $labels['import'] = 'Import';
 $labels['importcontacts'] = 'Importovat kontakty';
 $labels['importfromfile'] = 'Importovat ze souboru';
+$labels['importtarget'] = 'Přidat nové kontakty do seznamu kontaktů:';
 $labels['importreplace'] = 'Nahradit celý seznam kontaktů';
 $labels['importtext'] = 'Můžete nahrát kontakty z existujícího seznamu kontaktů. Podporujeme formát vCard.';
 $labels['done'] = 'Hotovo';
@@ -286,6 +303,8 @@ $labels['edititem'] = 'Upravit položku';
 $labels['preferhtml'] = 'Upřednostňovat HTML zobrazení';
 $labels['defaultcharset'] = 'Výchozí kódování';
 $labels['htmlmessage'] = 'HTML zpráva';
+$labels['dateformat'] = 'Formát data';
+$labels['timeformat'] = 'Formát času';
 $labels['prettydate'] = 'Hezčí datum';
 $labels['setdefault'] = 'Nastavit výchozí';
 $labels['autodetect'] = 'Automaticky';
@@ -356,6 +375,13 @@ $labels['afternseconds'] = 'po $n sekundách';
 $labels['reqmdn'] = 'Vždy požadovat doručenku';
 $labels['reqdsn'] = 'Vždy požádat o oznámení o stavu doručení';
 $labels['replysamefolder'] = 'Ukládat odpovědi ve stejné složce jako je zodpovězená zpráva';
+$labels['defaultaddressbook'] = 'Přidávat nové kontakty do seznamu kontaktů';
+$labels['spellcheckbeforesend'] = 'Před odesláním zkontrolovat pravopis';
+$labels['spellcheckoptions'] = 'Nastavení pravopisu';
+$labels['spellcheckignoresyms'] = 'Ignorovat slova obsahující symboly';
+$labels['spellcheckignorenums'] = 'Ignorovat slova obsahující čísla';
+$labels['spellcheckignorecaps'] = 'Ignorovat slova psaná velkými písmeny';
+$labels['addtodict'] = 'Přidat do slovníku';
 $labels['folder'] = 'Složka';
 $labels['folders'] = 'Složky';
 $labels['foldername'] = 'Jméno složky';
@@ -379,6 +405,7 @@ $labels['sharedfolder'] = 'Sdílená složka';
 $labels['sortby'] = 'Seřadit podle';
 $labels['sortasc'] = 'Seřadit vzestupně';
 $labels['sortdesc'] = 'Seřadit sestupně';
+$labels['undo'] = 'Vrátit zpět';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index 13c4dd44d47a04b16e96515c5e47be26f08754bf..a47b8d8d50debec1b39f9f5bcd98f72e76e597d5 100644 (file)
@@ -16,7 +16,7 @@
 |        Ales Pospichal <ales@pospichalales.info>                       |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 4671 2011-04-20 08:47:44Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -27,6 +27,7 @@ $messages['sessionerror'] = 'Vaše přihlášení je neplatné nebo vypršelo';
 $messages['imaperror'] = 'Připojení na IMAP server selhalo';
 $messages['servererror'] = 'Chyba serveru!';
 $messages['servererrormsg'] = 'Chyba serveru: $msg';
+$messages['dberror'] = 'Chyba v databázi!';
 $messages['errorreadonly'] = 'Příkaz nelze provést, složka je určena jen ke čtení.';
 $messages['errornoperm'] = 'Příkaz nelze provést, nemáte oprávnění.';
 $messages['invalidrequest'] = 'Nesprávný požadavek. Data nebyla uložena.';
@@ -35,6 +36,7 @@ $messages['loggedout'] = 'Byli jste úspěšně odhlášeni. Nashledanou!';
 $messages['mailboxempty'] = 'Schránka je prázdná';
 $messages['loading'] = 'Načítám...';
 $messages['uploading'] = 'Nahrávám soubor...';
+$messages['uploadingmany'] = 'Nahrávám soubory...';
 $messages['loadingdata'] = 'Načítám data...';
 $messages['checkingmail'] = 'Kontroluji nové zprávy...';
 $messages['sendingmessage'] = 'Odesílám zprávu...';
@@ -44,10 +46,12 @@ $messages['messagesaved'] = 'Zpráva uložena do Rozepsané';
 $messages['successfullysaved'] = 'Uloženo';
 $messages['addedsuccessfully'] = 'Kontakt byl úspěšně přidán do adresáře';
 $messages['contactexists'] = 'Kontakt se zadanou e-mailovou adresou již existuje';
+$messages['contactnameexists'] = 'Kontakt se stejným jménem již existuje';
 $messages['blockedimages'] = 'Z bezpečnostních důvodů byly zablokovány obrázky ve zprávě.';
 $messages['encryptedmessage'] = 'Tato zpráva je zašifrovaná a nelze ji zobrazit.';
-$messages['nocontactsfound'] = 'Nemáte žádné kontakty';
+$messages['nocontactsfound'] = 'Kontakty nebyly nalezeny';
 $messages['contactnotfound'] = 'Požadovaný kontakt nebyl nalezen.';
+$messages['contactsearchonly'] = 'Vyplňte některou z položek';
 $messages['sendingfailed'] = 'Odesílání zprávy selhalo';
 $messages['senttooquickly'] = 'Prosím počkejte $sec sekund před odesláním zprávy';
 $messages['errorsavingsent'] = 'Nastala chyba při ukládání odeslané zprávy';
@@ -57,9 +61,12 @@ $messages['errorcopying'] = 'Nemohu zkopírovat zprávu';
 $messages['errordeleting'] = 'Nemohu smazat zprávu';
 $messages['errormarking'] = 'Zprávu nelze označit';
 $messages['deletecontactconfirm'] = 'Opravdu chcete smazat označené kontakty?';
+$messages['deletegroupconfirm'] = 'Opravdu chcete smazat skupinu?';
 $messages['deletemessagesconfirm'] = 'Opravdu chcete smazat označené zprávy?';
 $messages['deletefolderconfirm'] = 'Chcete opravdu smazat tento adresář?';
 $messages['purgefolderconfirm'] = 'Opravdu chcete smazat všechny zprávy v této složce?';
+$messages['contactdeleting'] = 'Mažu kontakty...';
+$messages['groupdeleting'] = 'Mažu skupinu...';
 $messages['folderdeleting'] = 'Odstraňuji složku...';
 $messages['foldermoving'] = 'Přesouvám složku...';
 $messages['foldersubscribing'] = 'Připojuji složku...';
@@ -74,10 +81,10 @@ $messages['nosubjectwarning'] = 'Předmět nebyl vyplňen. Přejete si jej zadat
 $messages['nobodywarning'] = 'Opravdu chtete odeslat prázdnou zprávu?';
 $messages['notsentwarning'] = 'Zpráva nebyla odeslána. Přejete si zprávu zahodit?';
 $messages['noldapserver'] = 'Zvolte, prosím, LDAP server k hledání';
-$messages['nocontactsreturned'] = 'Nebyly nalezeny žádné kontakty';
 $messages['nosearchname'] = 'Zadejte, prosím, jméno nebo e-mail kontaktu';
 $messages['notuploadedwarning'] = 'Ještě nebyly nahrány všechny přílohy. Počkejte prosím nebo nahrávání zrušte.';
-$messages['searchsuccessful'] = '$nr zpráv nalezeno';
+$messages['searchsuccessful'] = 'Nalezeno $nr zpráv';
+$messages['contactsearchsuccessful'] = 'Nalezeno $nr kontaktů';
 $messages['searchnomatch'] = 'Nenalezena žádná zpráva';
 $messages['searching'] = 'Vyhledávám...';
 $messages['checking'] = 'Kontroluji...';
@@ -98,8 +105,11 @@ $messages['sourceisreadonly'] = 'Tento zdroj adres je pouze pro čtení';
 $messages['errorsavingcontact'] = 'Nemohu uložit adresu kontaktu';
 $messages['movingmessage'] = 'Přesouvám zprávu...';
 $messages['copyingmessage'] = 'Kopíruji zprávu...';
+$messages['copyingcontact'] = 'Kopíruji kontakty...';
 $messages['deletingmessage'] = 'Odstraňuji zprávu...';
 $messages['markingmessage'] = 'Označuji zprávu...';
+$messages['addingmember'] = 'Přidávám kontakty do skupiny...';
+$messages['removingmember'] = 'Odstraňuji kontakty ze skupiny...';
 $messages['receiptsent'] = 'Potvrzení o přřijetí zprávy odesláno';
 $messages['errorsendingreceipt'] = 'Potvrzení o přijetí zprávy nebylo možné odeslat';
 $messages['nodeletelastidentity'] = 'Alespoň jedna identita musí být ponechána. Identitu nelze odstranit.';
@@ -111,7 +121,7 @@ $messages['contactremovedfromgroup'] = 'Kontakty byly odstraněny z této skupin
 $messages['importwait'] = 'Importuji, prosím čekejte...';
 $messages['importerror'] = 'Během importu nastala chyba! Nahraný soubor není ve formátu vCard.';
 $messages['importconfirm'] = 'Úspěšně naimportováno $inserted kontaktů, $skipped existujících záznamů přeskočeno: $names';
-$messages['importconfirmskipped'] = '<b>Přeskočeno $skipped existing entries</b>';
+$messages['importconfirmskipped'] = '<b>Přeskočeno $skipped existujících položek</b>';
 $messages['opnotpermitted'] = 'Operace není povolena!';
 $messages['nofromaddress'] = 'Chybějící e-mailová adresa v označeném profilu';
 $messages['editorwarning'] = 'Přepnutím do režimu prostého textu ztratíte veškeré formátování. Chcete pokračovat?';
@@ -129,18 +139,27 @@ $messages['maxgroupmembersreached'] = 'Počet členských skupin dosáhl maximum
 $messages['internalerror'] = 'Došlo k interní chybě. Zkuste to znovu';
 $messages['contactdelerror'] = 'Kontakty nelze odstranit';
 $messages['contactdeleted'] = 'Kontakty byly odstraněny';
+$messages['contactrestoreerror'] = 'Nelze obnovit smazané kontakty';
+$messages['contactrestored'] = 'Kontakty byly obnoveny';
 $messages['groupdeleted'] = 'Skupina byla odstraněna';
 $messages['grouprenamed'] = 'Skupina byla přejmenována';
 $messages['groupcreated'] = 'Skupina vytvořena';
+$messages['savedsearchdeleted'] = 'Uložené hledání bylo ostraněno';
+$messages['savedsearchdeleteerror'] = 'Nelze odstranit uložené hledání';
+$messages['savedsearchcreated'] = 'Nové hledání bylo vytvořeno';
+$messages['savedsearchcreateerror'] = 'Nelze vytvořit uložené hledání';
 $messages['messagedeleted'] = 'Zpráva odstraněna';
 $messages['messagemoved'] = 'Zpráva byla přesunuta';
 $messages['messagecopied'] = 'Zpráva byla zkopirována';
 $messages['messagemarked'] = 'Zpráva označena';
 $messages['autocompletechars'] = 'Napište alespoň $min znaků pro automatické doplnění';
+$messages['autocompletemore'] = 'Napište více znaků. Nalezeno příliš mnoho položek';
 $messages['namecannotbeempty'] = 'Jméno musí být vyplněno';
 $messages['nametoolong'] = 'Jméno je příliš dlouhé';
 $messages['folderupdated'] = 'Složka byla úspěšně aktualizována';
 $messages['foldercreated'] = 'Složka byla úspěšně vytvořena';
 $messages['invalidimageformat'] = 'Formát obrázku není podporován';
+$messages['mispellingsfound'] = 'Ve zprávě byly zjištěny pravopisné chyby';
+$messages['parentnotwritable'] = 'Nelze vytvořit/přesunout složku do vybrané rodičovské složky. Nemáte práva.';
 
 ?>
index e1588d03319f4e603e86c5acfb6a479355db26e0..f3d521b9ba9e2d9aaa654055bb9b1e6b7204561a 100644 (file)
@@ -114,7 +114,7 @@ $labels['markread'] = 'Wedi eu darllen';
 $labels['markunread'] = 'Heb eu darllen';
 $labels['markflagged'] = 'Wedi eu fflagio';
 $labels['markunflagged'] = 'Heb eu fflagio';
-$labels['messageactions'] = 'Mwy o weithredoedd...';
+$labels['moreactions'] = 'Mwy o weithredoedd...';
 $labels['select'] = 'Dewis';
 $labels['all'] = 'Popeth';
 $labels['none'] = 'Dim byd';
@@ -186,6 +186,7 @@ $labels['highest'] = 'Uchaf';
 $labels['nosubject'] = '(dim pwnc)';
 $labels['showimages'] = 'Dangos lluniau';
 $labels['alwaysshow'] = 'Dangos lluniau bob amser o $sender';
+$labels['isdraft'] = 'Mae hwn yn neges ddrafft';
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'Testun plaen';
 $labels['savesentmessagein'] = 'Cadw negeseuon a ddanfonir yn';
@@ -267,7 +268,6 @@ $labels['print'] = 'Argraffu';
 $labels['export'] = 'Allforio';
 $labels['exportvcards'] = 'Allforio cysylltiadau mewn fformat vCard';
 $labels['newcontactgroup'] = 'Creu grŵp cysylltiadau newydd';
-$labels['groupactions'] = 'Gweithredoedd ar gyfer grwpiau cysylltiadau...';
 $labels['grouprename'] = 'Ail-enwi grŵp';
 $labels['groupdelete'] = 'Dileu grŵp';
 $labels['previouspage'] = 'Dangos y set flaenorol';
@@ -277,6 +277,8 @@ $labels['lastpage'] = 'Dangos y set olaf';
 $labels['group'] = 'Grŵp';
 $labels['groups'] = 'Grwpiau';
 $labels['personaladrbook'] = 'Cyfeiriadau Personol';
+$labels['searchsave'] = 'Cadw chwiliad';
+$labels['searchdelete'] = 'Dileu chwiliad';
 $labels['import'] = 'Mewnforio';
 $labels['importcontacts'] = 'Mewnforio cysylltiadau';
 $labels['importfromfile'] = 'Mewnforio o ffeil:';
@@ -285,6 +287,7 @@ $labels['importreplace'] = 'Dileu y llyfr cyfeiriadau cyfan wrth lwytho';
 $labels['importtext'] = 'Fe allwch chi lwytho fyny cysylltiadau o lyfr cyfeiriadau sy\'n bodoli yn barod. Ar hyn o bryd rydyn\' ni\'n cefnogi mewnforio cyfeiriadau yn y fformat <a href="http://en.wikipedia.org/wiki/VCard">vCard</a>.';
 $labels['done'] = 'Wedi gorffen';
 $labels['settingsfor'] = 'Gosodiadau ar gyfer';
+$labels['about'] = 'Amdan';
 $labels['preferences'] = 'Dewisiadau';
 $labels['userpreferences'] = 'Dewisiadau\'r defnyddiwr';
 $labels['editpreferences'] = 'Golygu dewisiadau\'r defnyddiwr';
@@ -296,6 +299,8 @@ $labels['edititem'] = 'Golygu eitem';
 $labels['preferhtml'] = 'Dangos HTML';
 $labels['defaultcharset'] = 'Set Nodau Diofyn';
 $labels['htmlmessage'] = 'Neges HTML';
+$labels['dateformat'] = 'Fformat dyddiad';
+$labels['timeformat'] = 'Fformat amser';
 $labels['prettydate'] = 'Dyddiadau pert';
 $labels['setdefault'] = 'Rhagosod';
 $labels['autodetect'] = 'Awto';
@@ -367,7 +372,13 @@ $labels['reqmdn'] = 'Bob amser gofyn am dderbynneb danfon';
 $labels['reqdsn'] = 'Gofyn am hysbysiad statws danfon bob amser';
 $labels['replysamefolder'] = 'Rhoi atebion yn yr un ffolder i\'r neges sy\'n cael ei ateb';
 $labels['defaultaddressbook'] = 'Ychwanegu cysylltiadau newydd i\'r llyfr cyfeiriad dewiswyd';
+$labels['autocompletesingle'] = 'Hepgor cyfeiriadau ebost amgen wrth awto-gwblhau';
 $labels['spellcheckbeforesend'] = 'Gwirio sillafu cyn danfon neges';
+$labels['spellcheckoptions'] = 'Dewisiadau gwirio sillafu';
+$labels['spellcheckignoresyms'] = 'Anwybyddu geiriau gyda symbolau';
+$labels['spellcheckignorenums'] = 'Anwybyddu geiriau gyda rhifau';
+$labels['spellcheckignorecaps'] = 'Anwybyddu geiriau sy\'n briflythrennau yn gyfangwbl';
+$labels['addtodict'] = 'Ychwanegu i\'r geiriadur';
 $labels['folder'] = 'Ffolder';
 $labels['folders'] = 'Ffolderi';
 $labels['foldername'] = 'Enw ffolder';
@@ -392,6 +403,11 @@ $labels['sortby'] = 'Trefnu yn ôl';
 $labels['sortasc'] = 'Trefn esgynnol';
 $labels['sortdesc'] = 'Trefn ddisgynnol';
 $labels['undo'] = 'Dad-wneud';
+$labels['plugin'] = 'Ategyn';
+$labels['version'] = 'Fersiwn';
+$labels['source'] = 'Ffynhonnell';
+$labels['license'] = 'Trwydded';
+$labels['support'] = 'Gofyn am gymorth';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index 0e965971d30f9d944d578185e6824806c76f49ee..51223011bacd2d460257dffb40d0246788a5dd54 100644 (file)
@@ -45,6 +45,7 @@ $messages['blockedimages'] = 'I amddiffyn eich preifatrwydd, fe ataliwyd lluniau
 $messages['encryptedmessage'] = 'Mae hon yn neges amgryptedig a felly ni ellir ei ddangos. Flin iawn!';
 $messages['nocontactsfound'] = 'Ni gafwyd hyd i unrhyw gysylltiadau';
 $messages['contactnotfound'] = 'Ni gafwyd hyd i\'r cysylltiad gofynnwyd amdano';
+$messages['contactsearchonly'] = 'Rhowch dermau chwilio i ganfod cysylltiadau';
 $messages['sendingfailed'] = 'Methwyd danfon y neges';
 $messages['senttooquickly'] = 'Arhoswch $sec eiliad cyn danfon y neges';
 $messages['errorsavingsent'] = 'Fe gafwyd gwall wrth cadw\'r neges ddanfonwyd';
@@ -58,6 +59,7 @@ $messages['deletegroupconfirm'] = 'Ydych chi wir am ddileu\'r grŵp dewiswyd?';
 $messages['deletemessagesconfirm'] = 'Ydych chi wir am ddileu y neges(euon) ddewiswyd?';
 $messages['deletefolderconfirm'] = 'Ydych chi wir am ddileu y ffolder yma?';
 $messages['purgefolderconfirm'] = 'Ydych chi wir am ddileu yr holl negeseuon yn y ffolder yma?';
+$messages['contactdeleting'] = 'Dileu cyswllt/cysylltiadau...';
 $messages['groupdeleting'] = 'Yn dileu grŵp...';
 $messages['folderdeleting'] = 'Yn dileu ffolder...';
 $messages['foldermoving'] = 'Yn symud ffolder...';
@@ -73,10 +75,10 @@ $messages['nosubjectwarning'] = 'Mae\'r pennawd \"Pwnc\" yn wag. Hoffech chi roi
 $messages['nobodywarning'] = 'Danfon y neges hwn heb destun?';
 $messages['notsentwarning'] = 'Ni ddanfonwyd y neges. Hoffech chi gael gwared a\'r neges?';
 $messages['noldapserver'] = 'Dewiswch weinydd ldap i chwilio';
-$messages['nocontactsreturned'] = 'Ni gafwyd hyd i unrhyw gysylltiadau';
 $messages['nosearchname'] = 'Rhowch enw cyswllt neu gyfeiriad e-bost';
 $messages['notuploadedwarning'] = 'Nid yw pob atodiad wedi eu llwytho i fyny eto. Triwch eto neu canslo.';
 $messages['searchsuccessful'] = 'Cafwyd hyd i $nr neges';
+$messages['contactsearchsuccessful'] = 'Canfuwyd $nr cyswllt.';
 $messages['searchnomatch'] = 'Ni gafwyd hyd i unrhyw ganlyniadau chwilio';
 $messages['searching'] = 'Yn chwilio...';
 $messages['checking'] = 'Yn gofyn...';
@@ -123,7 +125,6 @@ $messages['smtpautherror'] = 'Gwall SMTP ($code): Methwyd dilysu\'r cyfrif';
 $messages['smtpfromerror'] = 'Gwall SMTP ($code): Methwyd gosod y danfonwr "$from" ($msg)';
 $messages['smtptoerror'] = 'Gwall SMTP ($code): Methwyd ychwanegu derbynwr "$to" ($msg)';
 $messages['smtprecipientserror'] = 'Gwall SMTP: Nid oedd yn bosib darllen y rhestr o dderbynnwyr';
-$messages['smtpdsnerror'] = 'Gwall SMTP: Dim cefnogaeth i Hysbysiadau Statws Danfon';
 $messages['smtperror'] = 'Gwall SMTP: $msg';
 $messages['emailformaterror'] = 'Cyfeiriad e-bost anghywir: $email';
 $messages['toomanyrecipients'] = 'Gormod o dderbynnwyr. Lleihewch y nifer i $max';
@@ -136,11 +137,16 @@ $messages['contactrestored'] = 'Adferwyd y cyswllt/cysylltiadau';
 $messages['groupdeleted'] = 'Grŵp wedi ei ddileu yn llwyddiannus';
 $messages['grouprenamed'] = 'Grŵp wedi ei ailenwi yn llwyddiannus';
 $messages['groupcreated'] = 'Grŵp wedi ei greu yn llwyddiannus';
+$messages['savedsearchdeleted'] = 'Dilëwyd y chwiliad yn llwyddiannus.';
+$messages['savedsearchdeleteerror'] = 'Methwyd dileu y chwiliad.';
+$messages['savedsearchcreated'] = 'Crëwyd y chwiliad yn llwyddiannus.';
+$messages['savedsearchcreateerror'] = 'Methwyd creu y chwiliad.';
 $messages['messagedeleted'] = 'Neges(euon) wedi eu dileu yn llwyddiannus';
 $messages['messagemoved'] = 'Neges(euon) wedi eu symud yn llwyddiannus';
 $messages['messagecopied'] = 'Neges(euon) wedi eu copïo yn llwyddiannus';
 $messages['messagemarked'] = 'Neges(euon) wedi eu marcio yn llwyddiannus';
 $messages['autocompletechars'] = 'Rhowch o leia $min llythyren ar gyfer awto-gwblhau';
+$messages['autocompletemore'] = 'Canfuwyd mwy o gofnodion sy\'n cyfateb. Teipiwch fwy o lythrennau.';
 $messages['namecannotbeempty'] = 'Ni all yr enw fod yn wag';
 $messages['nametoolong'] = 'Mae\'r enw yn rhy hir';
 $messages['folderupdated'] = 'Diweddarwyd y ffolder yn llwyddiannus';
@@ -148,5 +154,6 @@ $messages['foldercreated'] = 'Crëwyd y ffolder yn llwyddiannus';
 $messages['invalidimageformat'] = 'Ddim yn fformat llun dilys.';
 $messages['mispellingsfound'] = 'Gwelwyd camsillafu yn y neges.';
 $messages['parentnotwritable'] = 'Methwyd creu/symud ffolder i\'r ffolder rhiant ddewiswyd. Dim hawl mynediad.';
+$messages['messagetoobig'] = 'Mae darn y neges yn rhy fawr i\'w brosesu.';
 
 ?>
index cf766eed901f63a886d6521c2e58acd651d43bd5..5922e6d7036a31f171485198b5be054d955afb22 100644 (file)
 | Author: Martin Moeller <martin@liga.dk>                               |
 |         Jesper R. Meyer <jrm@upthere.dk>                              |
 |         Søren Aggeboe <soren@aggeboe.dk>                              |
+|         John Loft Christiansen <john@nansensvej.dk>                   |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 4462 2011-01-28 16:01:03Z thomasb $
-
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 */
 
 $labels = array();
+
+// login page
 $labels['welcome'] = 'Velkommen til $product';
 $labels['username'] = 'Brugernavn';
 $labels['password'] = 'Adgangskode';
 $labels['server'] = 'Server';
 $labels['login'] = 'Log på';
+
+// Taskbar
 $labels['logout'] = 'Log af';
 $labels['mail'] = 'E-mail';
 $labels['settings'] = 'Personlige indstillinger';
 $labels['addressbook'] = 'Adressebog';
+
+// mailbox names
 $labels['inbox'] = 'Indbakke';
 $labels['drafts'] = 'Kladder';
 $labels['sent'] = 'Sendt post';
 $labels['trash'] = 'Skrald';
 $labels['junk'] = 'Ragelse';
+
+// message listing
 $labels['subject'] = 'Emne';
 $labels['from'] = 'Afsender';
 $labels['to'] = 'Modtager';
@@ -45,17 +53,23 @@ $labels['size'] = 'Størrelse';
 $labels['priority'] = 'Prioritet';
 $labels['organization'] = 'Organisation';
 $labels['readstatus'] = 'Læst status';
+
 $labels['mailboxlist'] = 'Mapper';
 $labels['messagesfromto'] = 'Besked $from til $to af $count';
 $labels['threadsfromto'] = 'Tråd $from til $to af $count';
 $labels['messagenrof'] = 'Besked $nr af $count';
+
 $labels['copy'] = 'Kopier';
 $labels['move'] = 'Flyt';
 $labels['moveto'] = 'Flyt til...';
 $labels['download'] = 'Download';
+
 $labels['filename'] = 'Filnavn';
 $labels['filesize'] = 'Filstørrelse';
+
 $labels['addtoaddressbook'] = 'Tilføj til adressebogen';
+
+// weekdays short
 $labels['sun'] = 'Søn';
 $labels['mon'] = 'Man';
 $labels['tue'] = 'Tir';
@@ -63,6 +77,8 @@ $labels['wed'] = 'Ons';
 $labels['thu'] = 'Tor';
 $labels['fri'] = 'Fre';
 $labels['sat'] = 'Lør';
+
+// weekdays long
 $labels['sunday'] = 'Søndag';
 $labels['monday'] = 'Mandag';
 $labels['tuesday'] = 'Tirsdag';
@@ -70,6 +86,8 @@ $labels['wednesday'] = 'Onsdag';
 $labels['thursday'] = 'Torsdag';
 $labels['friday'] = 'Fredag';
 $labels['saturday'] = 'Lørdag';
+
+// month short
 $labels['jan'] = 'Jan';
 $labels['feb'] = 'Feb';
 $labels['mar'] = 'Mar';
@@ -82,6 +100,8 @@ $labels['sep'] = 'Sep';
 $labels['oct'] = 'Okt';
 $labels['nov'] = 'Nov';
 $labels['dec'] = 'Dec';
+
+//months long
 $labels['longjan'] = 'Januar';
 $labels['longfeb'] = 'Februar';
 $labels['longmar'] = 'Marts';
@@ -94,25 +114,26 @@ $labels['longsep'] = 'September';
 $labels['longoct'] = 'Oktober';
 $labels['longnov'] = 'November';
 $labels['longdec'] = 'December';
+
 $labels['today'] = 'I dag';
+
+// toolbar buttons
 $labels['checkmail'] = 'Se efter nye beskeder';
 $labels['writenewmessage'] = 'Skriv en ny besked';
 $labels['replytomessage'] = 'Svar på denne besked';
 $labels['replytoallmessage'] = 'Svar til alle modtagere';
 $labels['replyall'] = 'Svar alle';
 $labels['replylist'] = 'Svar til listen';
+$labels['forwardinline']    = 'Videresend';
+$labels['forwardattachment'] = 'Videresend som vedhæftning';
 $labels['forwardmessage'] = 'Videresend denne besked';
 $labels['deletemessage'] = 'Slet besked';
 $labels['movemessagetotrash'] = 'Flyt besked til skrald';
 $labels['printmessage'] = 'Udskriv denne besked';
 $labels['previousmessage'] = 'Vis forrige besked';
-$labels['previousmessages'] = 'Vis forrige sæt beskeder';
 $labels['firstmessage'] = 'Vis første besked';
-$labels['firstmessages'] = 'Vis første sæt beskeder';
 $labels['nextmessage'] = 'Vis næste besked';
-$labels['nextmessages'] = 'Vis næste sæt beskeder';
 $labels['lastmessage'] = 'Vis sidste besked';
-$labels['lastmessages'] = 'Vis sidste sæt beskeder';
 $labels['backtolist'] = 'Tilbage til beskedlisten';
 $labels['viewsource'] = 'Vis rå besked';
 $labels['markmessages'] = 'Markér beskeder';
@@ -120,7 +141,9 @@ $labels['markread'] = 'Som læst';
 $labels['markunread'] = 'Som ulæst';
 $labels['markflagged'] = 'Som markeret';
 $labels['markunflagged'] = 'Som umarkeret';
+$labels['moreactions'] = 'Flere funktioner...';
 $labels['messageactions'] = 'Flere funktioner...';
+
 $labels['select'] = 'Vælg';
 $labels['all'] = 'Alle';
 $labels['none'] = 'Ingen';
@@ -136,7 +159,8 @@ $labels['threads'] = 'Tråde';
 $labels['expand-all'] = 'Udfold alle';
 $labels['expand-unread'] = 'Udfold ulæste';
 $labels['collapse-all'] = 'Fold alle';
-$labels['threaded'] = 'Tråded';
+$labels['threaded'] = 'Trådet';
+
 $labels['autoexpand_threads'] = 'Udfold besked tråde';
 $labels['do_expand'] = 'alle tråde';
 $labels['expand_only_unread'] = 'kun dem med ulæste beskeder';
@@ -152,18 +176,24 @@ $labels['listcolumns'] = 'Vist kolonne';
 $labels['listsorting'] = 'Sorterings kolonne';
 $labels['listorder'] = 'Sorter efter';
 $labels['listmode'] = 'Listevisningsmode';
+
 $labels['folderactions'] = 'Mappe handlinger...';
 $labels['compact'] = 'Ryd op';
 $labels['empty'] = 'Tøm';
+
 $labels['quota'] = 'Disk forbrug';
 $labels['unknown'] = 'ukendt';
 $labels['unlimited'] = 'ubegrænset';
+
 $labels['quicksearch'] = 'Hurtigsøgning';
 $labels['resetsearch'] = 'Nulstil søgning';
 $labels['searchmod'] = 'Søgeparametere';
 $labels['msgtext'] = 'Hele beskeden';
+
 $labels['openinextwin'] = 'Åbn i nyt vindue';
 $labels['emlsave'] = 'Download (.eml)';
+
+// message compose
 $labels['compose'] = 'Forfat en besked';
 $labels['editasnew'] = 'Redigér som ny';
 $labels['savemessage'] = 'Gem denne kladde';
@@ -173,41 +203,58 @@ $labels['charset'] = 'Tegnsæt';
 $labels['editortype'] = 'Tekstbehandler';
 $labels['returnreceipt'] = 'Anmod om kvittering';
 $labels['dsn'] = 'Notifikation om leveringstatus';
+$labels['mailreplyintro'] = 'Den $date, $sender skrev:';
+$labels['originalmessage'] = 'Original besked';
+
 $labels['editidents'] = 'Ret identiteter';
 $labels['checkspelling'] = 'Stavekontrol';
 $labels['resumeediting'] = 'Genoptag redigering';
 $labels['revertto'] = 'Vend tilbage til';
+
 $labels['attachments'] = 'Vedhæftninger';
 $labels['upload'] = 'Upload';
+$labels['uploadprogress'] = '$percent ($current af $total)';
 $labels['close'] = 'Luk';
 $labels['messageoptions'] = 'Besked muligheder...';
+
 $labels['low'] = 'Lav';
 $labels['lowest'] = 'Lavest';
 $labels['normal'] = 'Normal';
 $labels['high'] = 'Høj';
 $labels['highest'] = 'Højest';
+
 $labels['nosubject'] = '(intet emne)';
 $labels['showimages'] = 'Vis billeder';
 $labels['alwaysshow'] = 'Vis altid billeder fra $sender';
+$labels['isdraft']    = 'Dette er en kladde.';
+
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'Almindelig tekst';
 $labels['savesentmessagein'] = 'Gem afsendt besked i';
 $labels['dontsave'] = 'gem ikke';
 $labels['maxuploadsize'] = 'Maksimale tilladte filstørrelse er $size';
+
 $labels['addcc'] = 'Tilføj Cc';
 $labels['addbcc'] = 'Tilføj Bcc';
 $labels['addreplyto'] = 'Tilføj Svar-Til adresse';
 $labels['addfollowupto'] = 'Tilføj følg-op adresse';
+
+// mdn
 $labels['mdnrequest'] = 'Afsenderen af denne besked har bedt om at modtage en bekræftelse når du læser beskeden. Vil du sende kvittering for læsning?';
 $labels['receiptread'] = 'Send kvittering for læsning';
 $labels['yourmessage'] = 'Dette er en kvittering for at din besked er blevet vist';
 $labels['receiptnote'] = 'Bemærk: Denne kvittering bekræfter udelukkende at beskeden blev vist på modtagerens computer. Der er ingen garanti for at modtageren har læst eller forstået beskedens indhold.';
+
+// address book
 $labels['name'] = 'Vist navn';
 $labels['firstname'] = 'Fornavn';
 $labels['surname'] = 'Efternavn';
 $labels['middlename'] = 'Mellemnavn';
+$labels['nameprefix']   = 'Præfiks';
+$labels['namesuffix']   = 'Suffiks';
 $labels['nickname'] = 'Alias/Kaldenavn';
 $labels['jobtitle'] = 'Job titel';
+$labels['organization'] = 'Firma';
 $labels['department'] = 'Afdeling';
 $labels['gender'] = 'Køn';
 $labels['maidenname'] = 'Pigenavn';
@@ -229,11 +276,33 @@ $labels['female'] = 'Kvinde';
 $labels['manager'] = 'Manager';
 $labels['assistant'] = 'Assistent';
 $labels['spouse'] = 'Ægtefælle';
+$labels['allfields'] = 'Alle felter';
+$labels['search'] = 'Søg';
+$labels['advsearch'] = 'Avanceret søg';
+$labels['other'] = 'Andet';
+
+$labels['typehome']   = 'Hjem';
+$labels['typework']   = 'Arbejde';
+$labels['typeother']  = 'Andet';
+$labels['typemobile']  = 'Mobil';
+$labels['typemain']  = 'Fastnet';
+$labels['typehomefax']  = 'Hjemme fax';
+$labels['typeworkfax']  = 'Arbejde Fax';
+$labels['typecar']  = 'Bil';
+$labels['typepager']  = 'Personsøger';
+$labels['typevideo']  = 'Video';
+$labels['typeassistant']  = 'Assistent';
+$labels['typehomepage']  = 'Hjemmeside';
+$labels['typeblog'] = 'Blog';
+$labels['typeprofile'] = 'Profil';
+
 $labels['addfield'] = 'Tilføj felt...';
 $labels['addcontact'] = 'Tilføj en ny kontakt';
 $labels['editcontact'] = 'Redigér kontakt';
 $labels['contacts'] = 'Kontakter';
 $labels['contactproperties'] = 'Kontakt egenskaber';
+$labels['personalinfo'] = 'Personlig information';
+
 $labels['edit'] = 'Redigér';
 $labels['cancel'] = 'Afbryd';
 $labels['save'] = 'Gem';
@@ -241,6 +310,7 @@ $labels['delete'] = 'Slet';
 $labels['rename'] = 'Omdøb';
 $labels['addphoto'] = 'Tilføj';
 $labels['replacephoto'] = 'Erstat';
+
 $labels['newcontact'] = 'Opret nyt kontaktkort';
 $labels['deletecontact'] = 'Slet valgte kontakter';
 $labels['composeto'] = 'Skriv brev til';
@@ -250,31 +320,47 @@ $labels['export'] = 'Eksport';
 $labels['exportvcards'] = 'Eksportér kontakter i vCard format';
 $labels['newcontactgroup'] = 'Opret ny kontaktgruppe';
 $labels['groupactions'] = 'Funktioner for kontaktgrupper...';
+$labels['groupdelete']    = 'Slet gruppe';
+
 $labels['previouspage'] = 'Vis forrige sæt';
 $labels['firstpage'] = 'Vis første sæt';
 $labels['nextpage'] = 'Vis næste sæt';
 $labels['lastpage'] = 'Vis sidste sæt';
+
 $labels['group'] = 'Gruppe';
 $labels['groups'] = 'Grupper';
 $labels['personaladrbook'] = 'Personlige Adresser';
+
+$labels['searchsave'] = 'Gem søgning';
+$labels['searchdelete'] = 'Slet søgning';
+
 $labels['import'] = 'Importér';
 $labels['importcontacts'] = 'Importér kontakter';
 $labels['importfromfile'] = 'Importér fra fil:';
+$labels['importtarget'] = 'Tilføj nye kontakter til adressebogen:';
 $labels['importreplace'] = 'Overskriv hele adressebogen';
 $labels['importtext'] = 'Du kan uploade kontakter fra en eksisterende adressebog.Vi understøtter i øjeblikket import af adresser i vCard formatet.';
 $labels['done'] = 'Færdig';
+
+// settings
 $labels['settingsfor'] = 'Indstillinger for';
+$labels['about'] = 'Om';
 $labels['preferences'] = 'Præferencer';
 $labels['userpreferences'] = 'Brugerpræferencer';
 $labels['editpreferences'] = 'Redigér brugerpræferencer';
+
 $labels['identities'] = 'Identiteter';
 $labels['manageidentities'] = 'Styr identiteterne for denne konto';
 $labels['newidentity'] = 'Ny identitet';
+
 $labels['newitem'] = 'Nyt punkt';
 $labels['edititem'] = 'Redigér punkt';
+
 $labels['preferhtml'] = 'Foretræk HTML';
 $labels['defaultcharset'] = 'Standard tegnkodning';
 $labels['htmlmessage'] = 'HTML-besked';
+$labels['dateformat'] = 'Dato format';
+$labels['timeformat'] = 'Tid format';
 $labels['prettydate'] = 'Pæn datovisning';
 $labels['setdefault'] = 'Sæt standard';
 $labels['autodetect'] = 'Automatisk';
@@ -345,6 +431,15 @@ $labels['afternseconds'] = 'efter $n sekunder';
 $labels['reqmdn'] = 'Bed altid om at få besked om læsning';
 $labels['reqdsn'] = 'Bed altid om at få en status på levering';
 $labels['replysamefolder'] = 'Placer svar til en besked i samme mappe som beskeden der besvares';
+$labels['defaultaddressbook'] = 'Tilføj nye kontakter til den valgte adressebog';
+$labels['autocompletesingle'] = 'Undgå alternative emailadresser under autofuldførelse';
+$labels['spellcheckbeforesend'] = 'Tjek stavning inden beskeden sendes';
+$labels['spellcheckoptions'] = 'Stavning muligheder';
+$labels['spellcheckignoresyms'] = 'Ignorer ord med symboler';
+$labels['spellcheckignorenums'] = 'Ignorer ord med tal';
+$labels['spellcheckignorecaps'] = 'Ignorer ord hvor alle bogstaver er versaler';
+$labels['addtodict'] = 'Tilføj til ordbog';
+
 $labels['folder'] = 'Mappe';
 $labels['folders'] = 'Mapper';
 $labels['foldername'] = 'Mappenavn';
@@ -361,13 +456,29 @@ $labels['location'] = 'Placering';
 $labels['info'] = 'Information';
 $labels['getfoldersize'] = 'Klik for at hente mappestørrelse';
 $labels['changesubscription'] = 'Klik for at ændre abonnement';
+$labels['foldertype'] = 'Mappe Type';
+$labels['personalfolder']  = 'Privat mappe';
+$labels['otherfolder']  = 'Anden brugers mappe';
+$labels['sharedfolder']  = 'Offentlig mappe';
+
 $labels['sortby'] = 'Sortér efter';
 $labels['sortasc'] = 'Ældste først';
 $labels['sortdesc'] = 'Nyeste først';
+$labels['undo'] = 'Fortryd';
+
+$labels['plugin'] = 'Plugin';
+$labels['version'] = 'Version';
+$labels['source'] = 'Kilde';
+$labels['license'] = 'Licens';
+$labels['support'] = 'Få support';
+
+// units
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
 $labels['GB'] = 'GB';
+
+// character sets
 $labels['unicode'] = 'Unicode';
 $labels['english'] = 'Engelsk';
 $labels['westerneuropean'] = 'Vestlig europæisk';
index c8ff45117d162522c845d91a5a7db3698ee604a9..0eb72dd0962f5d3ecb22e294858ef1716cdfa83e 100644 (file)
 | Author : Martin Moeller <martin@liga.dk>                              |
 |         Jesper R. Meyer <jesper@upthere.dk>                           |
 |         Søren Aggeboe <soren@aggeboe.dk>                              |
+|         John Loft Christiansen <john@nansensvej.dk>                   |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 4462 2011-01-28 16:01:03Z thomasb $
-
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 */
 
 $messages = array();
@@ -25,6 +25,7 @@ $messages['sessionerror'] = 'Din session er ugyldig eller udløbet';
 $messages['imaperror'] = 'Forbindelse til IMAP serveren fejlede';
 $messages['servererror'] = 'Server fejl!';
 $messages['servererrormsg'] = 'Server fejl: $msg';
+$messages['dberror'] = 'Database fejl!';
 $messages['errorreadonly'] = 'Kunne ikke udføre den ønskede handling. Mappen er skrivebeskyttet';
 $messages['errornoperm'] = 'Kunne ikke udføre den ønskede handling. Adgang nægtet';
 $messages['invalidrequest'] = 'Ugyldig forespørgsel! Ingen data blev gemt.';
@@ -33,6 +34,7 @@ $messages['loggedout'] = 'Du er nu logget af webmail. Farvel så længe!';
 $messages['mailboxempty'] = 'Postkassen er tom!';
 $messages['loading'] = 'Indlæser...';
 $messages['uploading'] = 'Uploader fil...';
+$messages['uploadingmany'] = 'Uploader filer...';
 $messages['loadingdata'] = 'Indlæser data...';
 $messages['checkingmail'] = 'Tjekker for nye beskeder...';
 $messages['sendingmessage'] = 'Sender besked...';
@@ -42,10 +44,12 @@ $messages['messagesaved'] = 'Beskeden er gemt i kladdemappen';
 $messages['successfullysaved'] = 'Det lykkedes at gemme';
 $messages['addedsuccessfully'] = 'Kontakten blev tilføjet adressebogen';
 $messages['contactexists'] = 'Der er allerede en kontakt med denne e-mail adresse';
+$messages['contactnameexists'] = 'En kontakt med samme navn eksisterer allerede.';
 $messages['blockedimages'] = 'For at beskytte dit privatliv er billeder fra internetservere blokeret i denne besked.';
 $messages['encryptedmessage'] = 'Beskeden er krypteret og kan ikke vises. Beklager!';
 $messages['nocontactsfound'] = 'Ingen kontakter blev fundet';
 $messages['contactnotfound'] = 'Den søgte kontakt blev ikke fundet';
+$messages['contactsearchonly'] = 'Indtast ord for at finde kontakten';
 $messages['sendingfailed'] = 'Beskeden kunne ikke sendes';
 $messages['senttooquickly'] = 'Vent venligst $sec sekunder før du sender denne besked';
 $messages['errorsavingsent'] = 'Der opstod en fejl da den sendte besked blev gemt';
@@ -55,9 +59,12 @@ $messages['errorcopying'] = 'Beskeden kunne ikke kopieres';
 $messages['errordeleting'] = 'Beskeden kunne ikke slettes';
 $messages['errormarking'] = 'Beskeden kunne ikke markeres';
 $messages['deletecontactconfirm'] = 'Vil du virkelig slette den/de valgte kontakt(er)?';
+$messages['deletegroupconfirm']  = 'Vil du virkelig slette den/de valgte gruppe(r)?';
 $messages['deletemessagesconfirm'] = 'Vil du virkelig slette den/de valgte besked(er)?';
 $messages['deletefolderconfirm'] = 'Vil du virkelig slette den valgte mappe';
 $messages['purgefolderconfirm'] = 'Vil du virkelig slette alle beskeder i denne mappe?';
+$messages['contactdeleting'] = 'Sletter kontakt(er)...';
+$messages['groupdeleting'] = 'Sletter gruppe...';
 $messages['folderdeleting'] = 'Sletter mappen...';
 $messages['foldermoving'] = 'Flytter mappen...';
 $messages['foldersubscribing'] = 'Abonnere på mappen...';
@@ -96,8 +103,11 @@ $messages['sourceisreadonly'] = 'Denne adressekilde er kun til læsning';
 $messages['errorsavingcontact'] = 'Kunne ikke gemme kontakt adressen';
 $messages['movingmessage'] = 'Flytter besked...';
 $messages['copyingmessage'] = 'Kopierer besked...';
+$messages['copyingcontact'] = 'Kopierer kontakt(er)...';
 $messages['deletingmessage'] = 'Sletter besked(er)...';
 $messages['markingmessage'] = 'Markerer besked(er)...';
+$messages['addingmember'] = 'Tilføjer kontakt(er) til gruppen...';
+$messages['removingmember'] = 'Fjerner kontakt(er) fra gruppen...';
 $messages['receiptsent'] = 'Kvittering for læsning er sendt';
 $messages['errorsendingreceipt'] = 'Kvitteringen kunne ikke sendes';
 $messages['nodeletelastidentity'] = 'Du kan ikke slette denne identitet, da det er den eneste der er tilbage.';
@@ -109,6 +119,7 @@ $messages['contactremovedfromgroup'] = 'Succesfuldt fjernet kontakten fra denne
 $messages['importwait'] = 'Importerer, vent venligst...';
 $messages['importerror'] = 'Fejl i importen! Den uploadede fil er ikke en gyldig vCard fil.';
 $messages['importconfirm'] = '<b>Importerede $inserted kontakter, sprang over $skipped allerede eksisterende kontakter</b>:<p><em>$names</em></p>';
+$messages['importconfirmskipped'] = '<b>Skipped $skipped existing entries</b>';
 $messages['opnotpermitted'] = 'Handlingen er ikke tilladt!';
 $messages['nofromaddress'] = 'Der mangler en email-adresse i den valgte identitet';
 $messages['editorwarning'] = 'Al formatering af teksten forsvinder, hvis der skiftes til ren tekst. Vil du fortsætte?';
@@ -118,7 +129,6 @@ $messages['smtpautherror'] = 'SMTP fejl ($code): Autenticering fejlede';
 $messages['smtpfromerror'] = 'SMTP fejl ($code): Kunne ikke afsende som "$from" ($msg)';
 $messages['smtptoerror'] = 'SMTP fejl ($code): Kunne ikke tilføje modtageren "$to" ($msg)';
 $messages['smtprecipientserror'] = 'SMTP fejl: kan ikke fortolke listen af modtagere';
-$messages['smtpdsnerror'] = 'SMTP fejl: Ingen understøttelse af leveringsnotifikation';
 $messages['smtperror'] = 'SMTP fejl: $msg';
 $messages['emailformaterror'] = 'Ugyldig email-adresse: $email';
 $messages['toomanyrecipients'] = 'For mange modtagere. Reducer antallet af modtagere til $max';
@@ -126,18 +136,28 @@ $messages['maxgroupmembersreached'] = 'Antallet af gruppemedlemmer overstiger ma
 $messages['internalerror'] = 'Der opstod en intern fejl - prøv venligst igen';
 $messages['contactdelerror'] = 'Kunne ikke slette kontakt(er)';
 $messages['contactdeleted'] = 'Kontakt(er) slettet';
+$messages['contactrestoreerror'] = 'Kunne ikke gendanne slettede kontakt(er).';
+$messages['contactrestored'] = 'Kontakt(er) gendannet.';
 $messages['groupdeleted'] = 'Gruppen er slettet';
 $messages['grouprenamed'] = 'Gruppen er omdøbt';
 $messages['groupcreated'] = 'Gruppen er oprettet';
+$messages['savedsearchdeleted'] = 'Gemt søgning slettet.';
+$messages['savedsearchdeleteerror'] = 'Kunne ikke slette Gemt søgning.';
+$messages['savedsearchcreated'] = 'Gemt søgning oprettet.';
+$messages['savedsearchcreateerror'] = 'Kunne ikke oprette Gemt søgning.';
 $messages['messagedeleted'] = 'Besked(er) slettet';
 $messages['messagemoved'] = 'Besked(er) flyttet';
 $messages['messagecopied'] = 'Besked(er) kopieret';
 $messages['messagemarked'] = 'Besked(er) markeret';
 $messages['autocompletechars'] = 'Du skal min. indtaste $min for at benytte autoopslag';
+$messages['autocompletemore'] = 'Flere emner fundet. Task flere bogstaver.';
 $messages['namecannotbeempty'] = 'Navnet kan ikke været tomt';
 $messages['nametoolong'] = 'Navnet er for langt';
 $messages['folderupdated'] = 'Mappen er opdateret';
 $messages['foldercreated'] = 'Mappen er oprettet';
 $messages['invalidimageformat'] = 'Ikke et gyldigt billedformat';
+$messages['mispellingsfound'] = 'Der er fundet stavefejl i beskeden.';
+$messages['parentnotwritable'] = 'Kan ikke oprette/flytte mappe ind i valgt mappe. Ingen adgangsrettigheder.';
+$messages['messagetoobig'] = 'Besked delen er for stor til at behandle.';
 
 ?>
index c01cb7f1a390448eda9dcef38f75a1b046bec071..7a27bbeb5ff239dfbd5fa80e646d01028118dbc1 100644 (file)
@@ -13,7 +13,7 @@
 | Author:      Thomas Bruederli <roundcube@gmail.com>                   |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -285,6 +285,7 @@ $labels['importreplace'] = 'Bestehendes Adressbuch komplett ersetzen';
 $labels['importtext'] = 'Sie können Kontakte aus einem bestehenden Adressbuch hochladen.<br/>Es können Adressbücher im <a href="http://de.wikipedia.org/wiki/VCard">vCard-Format</a> importiert werden.';
 $labels['done'] = 'Fertig';
 $labels['settingsfor'] = 'Einstellungen für';
+$labels['about'] = 'Über';
 $labels['preferences'] = 'Einstellungen';
 $labels['userpreferences'] = 'Benutzereinstellungen';
 $labels['editpreferences'] = 'Einstellungen bearbeiten';
@@ -296,6 +297,8 @@ $labels['edititem'] = 'Eintrag bearbeiten';
 $labels['preferhtml'] = 'HTML bevorzugen';
 $labels['defaultcharset'] = 'Standard-Zeichensatz';
 $labels['htmlmessage'] = 'HTML Nachricht';
+$labels['dateformat'] = 'Datumsformat';
+$labels['timeformat'] = 'Zeitformat';
 $labels['prettydate'] = 'Kurze Datumsanzeige';
 $labels['setdefault'] = 'Als Standard';
 $labels['autodetect'] = 'Automatisch';
@@ -392,6 +395,11 @@ $labels['sortby'] = 'Sortieren nach';
 $labels['sortasc'] = 'aufsteigend sortieren';
 $labels['sortdesc'] = 'absteigend sortieren';
 $labels['undo'] = 'Rückgängig';
+$labels['plugin'] = 'Plugin';
+$labels['version'] = 'Version';
+$labels['source'] = 'Quellcode';
+$labels['license'] = 'Lizenz';
+$labels['support'] = 'Support';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index b112ec958ad9ed5e04286b142d77c47cf41caa3b..fe4a07bc73fef77f9e4c1880f1eceb1f25b95632 100644 (file)
@@ -13,7 +13,7 @@
 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5079 2011-08-16 19:57:45Z thomasb $
+@version $Id: messages.inc 5258 2011-09-21 11:17:46Z thomasb $
 
 */
 
@@ -24,7 +24,7 @@ $messages['sessionerror'] = 'Ihre Session ist ungültig oder abgelaufen';
 $messages['imaperror'] = 'Keine Verbindung zum IMAP Server';
 $messages['servererror'] = 'Serverfehler!';
 $messages['servererrormsg'] = 'Serverfehler: $msg';
-$messages['databaserror'] = 'Datenbankfehler!';
+$messages['dberror'] = 'Datenbankfehler!';
 $messages['errorreadonly'] = 'Die Aktion kann nicht ausgeführt werden. Der Ordner ist schreibgeschützt.';
 $messages['errornoperm'] = 'Die Aktion kann nicht ausgeführt werden. Zugriff verweigert.';
 $messages['invalidrequest'] = 'Ungültige Anfrage! Es wurden keine Daten gespeichert.';
@@ -48,6 +48,7 @@ $messages['blockedimages'] = 'Um Ihre Privatsphäre zur schützen, wurden extern
 $messages['encryptedmessage'] = 'Dies ist eine verschlüsselte Nachricht und kann leider nicht angezeigt werden.';
 $messages['nocontactsfound'] = 'Keine Kontakte gefunden';
 $messages['contactnotfound'] = 'Die gewählte Adresse wurde nicht gefunden';
+$messages['contactsearchonly'] = 'Geben Sie einen Suchbegriff ein, um Kontakte zu finden';
 $messages['sendingfailed'] = 'Versand der Nachricht fehlgeschlagen';
 $messages['senttooquickly'] = 'Bitte warten Sie $sec Sekunde(n) vor dem Senden dieser Nachricht';
 $messages['errorsavingsent'] = 'Ein Fehler ist beim Speichern der gesendeten Nachricht aufgetreten';
index 1015185ef9cb3bd7b471825596c0f6dab047bfce..ea7e0a0a827354d2ff7a413345e15ccedf3bceb0 100644 (file)
 +-----------------------------------------------------------------------+
 | Author:      Thomas Bruederli <roundcube@gmail.com>                   |
 | Author: Marcel Schlesinger <info@marcel-schlesinger.de>               |
+| Author: Roland Liebl <myroundcube@mail4us.net>                        |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5040 2011-08-10 11:09:16Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -101,18 +102,16 @@ $labels['replytomessage'] = 'Antwort verfassen';
 $labels['replytoallmessage'] = 'Antwort an Absender und alle Empfänger verfassen';
 $labels['replyall'] = 'Allen antworten';
 $labels['replylist'] = 'Liste antworten';
+$labels['forwardinline'] = 'innerhalb der Nachricht';
+$labels['forwardattachment'] = 'als Dateianhang';
 $labels['forwardmessage'] = 'Nachricht weiterleiten';
 $labels['deletemessage'] = 'Nachricht löschen';
 $labels['movemessagetotrash'] = 'Nachricht in den Papierkorb verschieben';
 $labels['printmessage'] = 'Nachricht drucken';
 $labels['previousmessage'] = 'Vorherige Nachricht anzeigen';
-$labels['previousmessages'] = 'Vorherige Nachrichten anzeigen';
 $labels['firstmessage'] = 'Die erste Nachricht anzeigen';
-$labels['firstmessages'] = 'Die ersten Nachrichten anzeigen';
 $labels['nextmessage'] = 'Nächste Nachricht anzeigen';
-$labels['nextmessages'] = 'Weitere Nachrichten anzeigen';
 $labels['lastmessage'] = 'Die letzte Nachricht anzeigen';
-$labels['lastmessages'] = 'Die letzten Nachrichten anzeigen';
 $labels['backtolist'] = 'Zurück zur Liste';
 $labels['viewsource'] = 'Quelltext anzeigen';
 $labels['markmessages'] = 'Nachrichten markieren';
@@ -120,7 +119,7 @@ $labels['markread'] = 'Als gelesen';
 $labels['markunread'] = 'Als ungelesen';
 $labels['markflagged'] = 'Stern hinzufügen';
 $labels['markunflagged'] = 'Stern entfernen';
-$labels['messageactions'] = 'Weitere Aktionen...';
+$labels['moreactions'] = 'Mehr ...';
 $labels['select'] = 'Auswählen';
 $labels['all'] = 'Alle';
 $labels['none'] = 'Keine';
@@ -174,12 +173,14 @@ $labels['editortype'] = 'Editor Typ';
 $labels['returnreceipt'] = 'Empfangsbestätigung (MSN)';
 $labels['dsn'] = 'Übermittlungsbestätigung (DSN)';
 $labels['mailreplyintro'] = 'Am $date, schrieb $sender:';
+$labels['originalmessage'] = 'Originalnachricht';
 $labels['editidents'] = 'Absender ändern';
 $labels['checkspelling'] = 'Rechtschreibung prüfen';
 $labels['resumeediting'] = 'Bearbeitung fortsetzen';
 $labels['revertto'] = 'Zurück zu';
 $labels['attachments'] = 'Anhänge';
 $labels['upload'] = 'Hochladen';
+$labels['uploadprogress'] = '$percent ($current von $total)';
 $labels['close'] = 'Schließen';
 $labels['messageoptions'] = 'Optionen...';
 $labels['low'] = 'Niedrig';
@@ -232,6 +233,24 @@ $labels['female'] = 'weiblich';
 $labels['manager'] = 'Vorgesetze(r)';
 $labels['assistant'] = 'Assistent';
 $labels['spouse'] = 'Partner/in';
+$labels['allfields'] = 'Alle Felder';
+$labels['search'] = 'Suche';
+$labels['advsearch'] = 'Detaillierte Suche';
+$labels['other'] = 'Sonstiges';
+$labels['typehome'] = 'Privat';
+$labels['typework'] = 'Dienstlich';
+$labels['typeother'] = 'Andere';
+$labels['typemobile'] = 'Mobil';
+$labels['typemain'] = 'Hauptnummer';
+$labels['typehomefax'] = 'Fax Privat';
+$labels['typeworkfax'] = 'Fax Dienst';
+$labels['typecar'] = 'Auto';
+$labels['typepager'] = 'Pager';
+$labels['typevideo'] = 'Video';
+$labels['typeassistant'] = 'Assistenz';
+$labels['typehomepage'] = 'Internet';
+$labels['typeblog'] = 'Blog';
+$labels['typeprofile'] = 'Profil';
 $labels['addfield'] = 'Feld hinzufügen...';
 $labels['addcontact'] = 'Kontakt hinzufügen';
 $labels['editcontact'] = 'Kontakt bearbeiten';
@@ -253,7 +272,8 @@ $labels['print'] = 'Drucken';
 $labels['export'] = 'Exportieren';
 $labels['exportvcards'] = 'Kontakte im vCard-Format exportieren';
 $labels['newcontactgroup'] = 'Neue Kontaktgruppen erstellen';
-$labels['groupactions'] = 'Aktionen für Kontaktgruppen...';
+$labels['grouprename'] = 'Gruppe umbenennen';
+$labels['groupdelete'] = 'Gruppe löschen';
 $labels['previouspage'] = 'Seite zurück';
 $labels['firstpage'] = 'Erste Seite';
 $labels['nextpage'] = 'Nächste Seite';
@@ -261,9 +281,12 @@ $labels['lastpage'] = 'Letzte Seite';
 $labels['group'] = 'Gruppe';
 $labels['groups'] = 'Gruppen';
 $labels['personaladrbook'] = 'Persönliches Adressbuch';
+$labels['searchsave'] = 'Suchergebnisse speichern';
+$labels['searchdelete'] = 'Suchergebnisse löschen';
 $labels['import'] = 'Importieren';
 $labels['importcontacts'] = 'Kontakte importieren';
 $labels['importfromfile'] = 'Import aus Datei:';
+$labels['importtarget'] = 'Einen neuen Kontakt hinzufügen:';
 $labels['importreplace'] = 'Bestehendes Adressbuch komplett ersetzen';
 $labels['importtext'] = 'Sie können Kontakte aus einem bestehenden Adressbuch hochladen. Zur Zeit können Adressbücher im vCard-Format importiert werden.';
 $labels['done'] = 'Fertig';
@@ -279,6 +302,8 @@ $labels['edititem'] = 'Eintrag bearbeiten';
 $labels['preferhtml'] = 'HTML anzeigen';
 $labels['defaultcharset'] = 'Standard Zeichensatz';
 $labels['htmlmessage'] = 'HTML-Nachricht';
+$labels['dateformat'] = 'Datumsformatierung';
+$labels['timeformat'] = 'Zeitformatierung';
 $labels['prettydate'] = 'Kurze Datumsanzeige';
 $labels['setdefault'] = 'Als Standard';
 $labels['autodetect'] = 'Automatisch';
@@ -349,6 +374,13 @@ $labels['afternseconds'] = 'nach $n Sekunden';
 $labels['reqmdn'] = 'Empfangsbestätigung (MSN) immer anfordern';
 $labels['reqdsn'] = 'Übermittlungsbestätigung (DSN) immer anfordern';
 $labels['replysamefolder'] = 'Antworten im selben Ordner wie Original speichern';
+$labels['defaultaddressbook'] = 'Neue Kontakte zum ausgewählten Adressbuch hinzufügen';
+$labels['spellcheckbeforesend'] = 'Rechtschreibprüfung vor dem Absenden der Nachricht';
+$labels['spellcheckoptions'] = 'Rechtschreibprüfungsoptionen';
+$labels['spellcheckignoresyms'] = 'Wörter mit Symbolen überspringen';
+$labels['spellcheckignorenums'] = 'Wörter mit Ziffern überspringen';
+$labels['spellcheckignorecaps'] = 'Wörter überspringen, die nur aus Großbuchstaben bestehen';
+$labels['addtodict'] = 'Zum Wörterbuch hinzufügen';
 $labels['folder'] = 'Ordner';
 $labels['folders'] = 'Ordner';
 $labels['foldername'] = 'Ordnername';
@@ -365,9 +397,14 @@ $labels['location'] = 'Speicherort';
 $labels['info'] = 'Informationen';
 $labels['getfoldersize'] = 'Ordnergröße anzeigen';
 $labels['changesubscription'] = 'Abonnieren';
+$labels['foldertype'] = 'Ordnertyp';
+$labels['personalfolder'] = 'Privater Ordner';
+$labels['otherfolder'] = 'Ordner eines anderen Benutzers';
+$labels['sharedfolder'] = 'Öffentlicher Ordner';
 $labels['sortby'] = 'Sortieren nach';
 $labels['sortasc'] = 'Aufsteigend sortieren';
 $labels['sortdesc'] = 'Absteigend sortieren';
+$labels['undo'] = 'Rückgängig machen';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index 104f60c3c759232a7bf4aa29d35b4330a7c28123..584146dda28e6eaa68dc870530ec605bd86f2403 100644 (file)
 +-----------------------------------------------------------------------+
 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
 | Author: Marcel Schlesinger <info@marcel-schlesinger.de>               |
+| Author: Roland Liebl <myroundcube@mail4us.net>                        |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5079 2011-08-16 19:57:45Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -25,7 +26,7 @@ $messages['sessionerror'] = 'Ihre Session ist ungültig oder abgelaufen';
 $messages['imaperror'] = 'Keine Verbindung zum IMAP Server';
 $messages['servererror'] = 'Serverfehler!';
 $messages['servererrormsg'] = 'Serverfehler: $msg';
-$messages['databaserror'] = 'Datenbankfehler!';
+$messages['dberror'] = 'Datenbankfehler!';
 $messages['errorreadonly'] = 'Die Aktion kann nicht ausgeführt werden. Der Ordner ist schreibgeschützt.';
 $messages['errornoperm'] = 'Die Aktion kann nicht ausgeführt werden. Zugriff verweigert.';
 $messages['invalidrequest'] = 'Ungültige Anfrage! Es wurden keine Daten gespeichert.';
@@ -49,6 +50,7 @@ $messages['blockedimages'] = 'Um Ihre Privatsphäre zur schützen, wurden extern
 $messages['encryptedmessage'] = 'Dies ist eine verschlüsselte Nachricht und kann leider nicht angezeigt werden.';
 $messages['nocontactsfound'] = 'Keine Kontakte gefunden';
 $messages['contactnotfound'] = 'Der angeforderte Kontakt wurde nicht gefunden';
+$messages['contactsearchonly'] = 'Geben Sie einen Suchbegriff ein, um Kontakte zu finden';
 $messages['sendingfailed'] = 'Versenden der Nachricht fehlgeschlagen';
 $messages['senttooquickly'] = 'Bitte warten Sie $sec Sekunde(n) vor dem Senden dieser Nachricht';
 $messages['errorsavingsent'] = 'Ein Fehler ist beim Speichern der gesendeten Nachricht aufgetreten';
@@ -77,10 +79,10 @@ $messages['nosubjectwarning'] = 'Die Betreffzeile ist leer. Möchten Sie jetzt e
 $messages['nobodywarning'] = 'Diese Nachricht ohne Inhalt senden?';
 $messages['notsentwarning'] = 'Ihre Nachricht wurde nicht gesendet. Wollen Sie die Nachricht verwerfen?';
 $messages['noldapserver'] = 'Bitte wählen Sie einen LDAP-Server aus';
-$messages['nocontactsreturned'] = 'Es wurden keine Kontakte gefunden';
 $messages['nosearchname'] = 'Bitte geben Sie einen Namen oder eine E-Mail-Adresse ein';
 $messages['notuploadedwarning'] = 'Es wurden noch nicht alle Dateien hochgeladen. Bitte warten oder Upload abbrechen.';
 $messages['searchsuccessful'] = '$nr Nachrichten gefunden';
+$messages['contactsearchsuccessful'] = '$nr Kontakte gefunden';
 $messages['searchnomatch'] = 'Die Suche lieferte keine Treffer';
 $messages['searching'] = 'Suche...';
 $messages['checking'] = 'Prüfe...';
@@ -127,7 +129,6 @@ $messages['smtpautherror'] = 'SMTP Fehler ($code): Die Authentisierung ist fehlg
 $messages['smtpfromerror'] = 'SMTP Fehler ($code): Der Absender "$from" konnte nicht gesetzt werden ($msg)';
 $messages['smtptoerror'] = 'SMTP Fehler ($code): Der Empfänger "$to" konnte nicht gesetzt werden ($msg)';
 $messages['smtprecipientserror'] = 'SMTP Fehler: Die Empfängerliste konnte nicht verarbeitet werden';
-$messages['smtpdsnerror'] = 'SMTP-Fehler: Übermittlungsbestätigungen werden nicht unterstützt';
 $messages['smtperror'] = 'SMTP Fehler: $msg';
 $messages['emailformaterror'] = 'Ungültige E-Mail-Adresse: $email';
 $messages['toomanyrecipients'] = 'Zuviele Empfänger. Reduzieren Sie die Anzahl Empfängeradressen auf $max.';
@@ -140,11 +141,16 @@ $messages['contactrestored'] = 'Kontakte erfolgreich wiederhergestellt.';
 $messages['groupdeleted'] = 'Gruppe erfolgreich gelöscht';
 $messages['grouprenamed'] = 'Gruppe erlogreich umbenannt';
 $messages['groupcreated'] = 'Gruppe erlogreich erstellt';
+$messages['savedsearchdeleted'] = 'Suchergebnisse erfolgreich gelöscht';
+$messages['savedsearchdeleteerror'] = 'Suchergebnisse konnten nicht gelöscht werden';
+$messages['savedsearchcreated'] = 'Suchergebnisse wurden erfolgreich wiederherstellt';
+$messages['savedsearchcreateerror'] = 'Suchergebnisse konnten nicht wiederhergestellt werden';
 $messages['messagedeleted'] = 'Nachricht(en) erfolgreich gelöscht';
 $messages['messagemoved'] = 'Nachricht(en) erfolgreich verschoben';
 $messages['messagecopied'] = 'Nachricht(en) erfolgreich kopiert';
 $messages['messagemarked'] = 'Nachricht(en) erfolgreich markiert';
 $messages['autocompletechars'] = 'Geben Sie mind. $min Zeichen für die Auto-Vervollständigung ein';
+$messages['autocompletemore'] = 'Mehrere Treffer. Bitte geben Sie mehr Buchstaben ein';
 $messages['namecannotbeempty'] = 'Der Name darf nicht leer sein';
 $messages['nametoolong'] = 'Der Name ist zu lang';
 $messages['folderupdated'] = 'Der Ordner wurde erfolgreich aktualisiert';
index 70fd646d0045d53b950d6ab1c195b2bd498c7c9a..9bb70394ffef9ad4b6acebec85449b1e95195c7f 100644 (file)
 |                                                                       |
 +-----------------------------------------------------------------------+
 | Author: Weiran Zhang (weiran@weiran.co.uk)                            |
+|         Phil Weir                                                     |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: labels.inc 5184 2011-09-07 06:33:15Z alec $
 
 */
 
@@ -33,8 +34,8 @@ $labels['sent'] = 'Sent';
 $labels['trash'] = 'Deleted Items';
 $labels['junk'] = 'Junk';
 $labels['subject'] = 'Subject';
-$labels['from'] = 'Sender';
-$labels['to'] = 'Recipient';
+$labels['from'] = 'From';
+$labels['to'] = 'To';
 $labels['cc'] = 'Copy';
 $labels['bcc'] = 'Bcc';
 $labels['replyto'] = 'Reply-To';
@@ -137,7 +138,7 @@ $labels['threaded'] = 'Threaded';
 $labels['autoexpand_threads'] = 'Expand message threads';
 $labels['do_expand'] = 'all threads';
 $labels['expand_only_unread'] = 'only with unread messages';
-$labels['fromto'] = 'Sender/Recipient';
+$labels['fromto'] = 'From/To';
 $labels['flag'] = 'Flag';
 $labels['attachment'] = 'Attachment';
 $labels['nonesort'] = 'None';
index 4a0807f33bc87462019b0eaddb5fdf2af6475342..6d344be5899a27597c450dd8261d06d0b20d1844 100644 (file)
 |                                                                       |
 +-----------------------------------------------------------------------+
 | Author: Weiran Zhang (weiran@weiran.co.uk)                            |
+|         Phil Weir                                                     |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: messages.inc 5173 2011-09-05 18:41:04Z thomasb $
 
 */
 
index e6dd58c49d6b835fb169bb1e3d5d06f27b34b813..4fbed2929cd885ecdf1a3fe16eff155b3ddee2e9 100644 (file)
@@ -13,7 +13,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- @version $Id: labels.inc 5165 2011-09-05 08:49:04Z thomasb $
+ @version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -41,9 +41,9 @@ $labels['junk']   = 'Junk';
 
 // message listing
 $labels['subject'] = 'Subject';
-$labels['from']    = 'Sender';
-$labels['to']      = 'Recipient';
-$labels['cc']      = 'Copy';
+$labels['from']    = 'From';
+$labels['to']      = 'To';
+$labels['cc']      = 'Cc';
 $labels['bcc']     = 'Bcc';
 $labels['replyto'] = 'Reply-To';
 $labels['followupto'] = 'Followup-To';
@@ -140,7 +140,7 @@ $labels['markread']         = 'As read';
 $labels['markunread']       = 'As unread';
 $labels['markflagged']      = 'As flagged';
 $labels['markunflagged']    = 'As unflagged';
-$labels['messageactions']   = 'More actions...';
+$labels['moreactions']      = 'More actions...';
 
 $labels['select'] = 'Select';
 $labels['all'] = 'All';
@@ -152,7 +152,6 @@ $labels['unanswered'] = 'Unanswered';
 $labels['deleted'] = 'Deleted';
 $labels['invert'] = 'Invert';
 $labels['filter'] = 'Filter';
-
 $labels['list'] = 'List';
 $labels['threads'] = 'Threads';
 $labels['expand-all'] = 'Expand All';
@@ -163,7 +162,7 @@ $labels['threaded'] = 'Threaded';
 $labels['autoexpand_threads'] = 'Expand message threads';
 $labels['do_expand'] = 'all threads';
 $labels['expand_only_unread'] = 'only with unread messages';
-$labels['fromto'] = 'Sender/Recipient';
+$labels['fromto'] = 'From/To';
 $labels['flag'] = 'Flag';
 $labels['attachment'] = 'Attachment';
 $labels['nonesort'] = 'None';
@@ -203,6 +202,7 @@ $labels['editortype']     = 'Editor type';
 $labels['returnreceipt']  = 'Return receipt';
 $labels['dsn']            = 'Delivery status notification';
 $labels['mailreplyintro'] = 'On $date, $sender wrote:';
+$labels['originalmessage'] = 'Original Message';
 
 $labels['editidents']    = 'Edit identities';
 $labels['checkspelling'] = 'Check spelling';
@@ -224,6 +224,7 @@ $labels['highest'] = 'Highest';
 $labels['nosubject']  = '(no subject)';
 $labels['showimages'] = 'Display images';
 $labels['alwaysshow'] = 'Always show images from $sender';
+$labels['isdraft']    = 'This is a draft message.';
 
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'Plain text';
@@ -316,7 +317,6 @@ $labels['print']          = 'Print';
 $labels['export']         = 'Export';
 $labels['exportvcards']   = 'Export contacts in vCard format';
 $labels['newcontactgroup'] = 'Create new contact group';
-$labels['groupactions']   = 'Actions for contact groups...';
 $labels['grouprename']    = 'Rename group';
 $labels['groupdelete']    = 'Delete group';
 
@@ -329,6 +329,9 @@ $labels['group'] = 'Group';
 $labels['groups'] = 'Groups';
 $labels['personaladrbook'] = 'Personal Addresses';
 
+$labels['searchsave'] = 'Save search';
+$labels['searchdelete'] = 'Delete search';
+
 $labels['import'] = 'Import';
 $labels['importcontacts'] = 'Import contacts';
 $labels['importfromfile'] = 'Import from file:';
@@ -339,7 +342,7 @@ $labels['done'] = 'Done';
 
 // settings
 $labels['settingsfor']  = 'Settings for';
-
+$labels['about'] = 'About';
 $labels['preferences']  = 'Preferences';
 $labels['userpreferences']  = 'User preferences';
 $labels['editpreferences']  = 'Edit user preferences';
@@ -354,6 +357,8 @@ $labels['edititem']  = 'Edit item';
 $labels['preferhtml'] = 'Display HTML';
 $labels['defaultcharset'] = 'Default Character Set';
 $labels['htmlmessage'] = 'HTML Message';
+$labels['dateformat'] = 'Date format';
+$labels['timeformat'] = 'Time format';
 $labels['prettydate'] = 'Pretty dates';
 $labels['setdefault']  = 'Set default';
 $labels['autodetect']  = 'Auto';
@@ -402,6 +407,7 @@ $labels['advancedoptions'] = 'Advanced options';
 $labels['focusonnewmessage'] = 'Focus browser window on new message';
 $labels['checkallfolders'] = 'Check all folders for new messages';
 $labels['displaynext'] = 'After message delete/move display the next message';
+$labels['defaultfont'] = 'Default font of HTML message';
 $labels['mainoptions'] = 'Main Options';
 $labels['section'] = 'Section';
 $labels['maintenance'] = 'Maintenance';
@@ -425,7 +431,13 @@ $labels['reqmdn'] = 'Always request a return receipt';
 $labels['reqdsn'] = 'Always request a delivery status notification';
 $labels['replysamefolder'] = 'Place replies in the folder of the message being replied to';
 $labels['defaultaddressbook'] = 'Add new contacts to the selected addressbook';
+$labels['autocompletesingle'] = 'Skip alternative email addresses in autocompletion';
 $labels['spellcheckbeforesend'] = 'Check spelling before sending a message';
+$labels['spellcheckoptions'] = 'Spellcheck Options';
+$labels['spellcheckignoresyms'] = 'Ignore words with symbols';
+$labels['spellcheckignorenums'] = 'Ignore words with numbers';
+$labels['spellcheckignorecaps'] = 'Ignore words with all letters capitalized';
+$labels['addtodict'] = 'Add to dictionary';
 
 $labels['folder']  = 'Folder';
 $labels['folders']  = 'Folders';
@@ -453,6 +465,12 @@ $labels['sortasc']  = 'Sort ascending';
 $labels['sortdesc'] = 'Sort descending';
 $labels['undo'] = 'Undo';
 
+$labels['plugin'] = 'Plugin';
+$labels['version'] = 'Version';
+$labels['source'] = 'Source';
+$labels['license'] = 'License';
+$labels['support'] = 'Get support';
+
 // units
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
index 5da8458f08515c597ddb584e9672e3cbd4efe2ef..10a7a4503b1b73ecbb8ab45faa52944080e3b53f 100644 (file)
@@ -13,7 +13,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- @version $Id: messages.inc 5022 2011-08-04 09:01:36Z alec $
+ @version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -24,7 +24,7 @@ $messages['sessionerror'] = 'Your session is invalid or expired.';
 $messages['imaperror'] = 'Connection to IMAP server failed.';
 $messages['servererror'] = 'Server Error!';
 $messages['servererrormsg'] = 'Server Error: $msg';
-$messages['databaserror'] = 'Database Error!';
+$messages['dberror'] = 'Database Error!';
 $messages['errorreadonly'] = 'Unable to perform operation. Folder is read-only.';
 $messages['errornoperm'] = 'Unable to perform operation. Permission denied.';
 $messages['invalidrequest'] = 'Invalid request! No data was saved.';
@@ -48,6 +48,7 @@ $messages['blockedimages'] = 'To protect your privacy, remote images are blocked
 $messages['encryptedmessage'] = 'This is an encrypted message and can not be displayed. Sorry!';
 $messages['nocontactsfound'] = 'No contacts found.';
 $messages['contactnotfound'] = 'The requested contact was not found.';
+$messages['contactsearchonly'] = 'Enter some search terms to find contacts';
 $messages['sendingfailed'] = 'Failed to send message.';
 $messages['senttooquickly'] = 'Please wait $sec sec(s). before sending this message.';
 $messages['errorsavingsent'] = 'An error occured while saving sent message.';
@@ -61,6 +62,7 @@ $messages['deletegroupconfirm']  = 'Do you really want to delete selected group?
 $messages['deletemessagesconfirm'] = 'Do you really want to delete selected message(s)?';
 $messages['deletefolderconfirm']  = 'Do you really want to delete this folder?';
 $messages['purgefolderconfirm']  = 'Do you really want to delete all messages in this folder?';
+$messages['contactdeleting'] = 'Deleting contact(s)...';
 $messages['groupdeleting'] = 'Deleting group...';
 $messages['folderdeleting'] = 'Deleting folder...';
 $messages['foldermoving'] = 'Moving folder...';
@@ -76,10 +78,10 @@ $messages['nosubjectwarning']  = 'The "Subject" field is empty. Would you like t
 $messages['nobodywarning'] = 'Send this message without text?';
 $messages['notsentwarning'] = 'Message has not been sent. Do you want to discard your message?';
 $messages['noldapserver'] = 'Please select an ldap server to search.';
-$messages['nocontactsreturned'] = 'No contacts were found.';
 $messages['nosearchname'] = 'Please enter a contact name or email address.';
 $messages['notuploadedwarning'] = 'Not all attachments have been uploaded yet. Please wait or cancel the upload.';
 $messages['searchsuccessful'] = '$nr messages found.';
+$messages['contactsearchsuccessful'] = '$nr contacts found.';
 $messages['searchnomatch'] = 'Search returned no matches.';
 $messages['searching'] = 'Searching...';
 $messages['checking'] = 'Checking...';
@@ -126,7 +128,6 @@ $messages['smtpautherror'] = 'SMTP Error ($code): Authentication failed.';
 $messages['smtpfromerror'] = 'SMTP Error ($code): Failed to set sender "$from" ($msg).';
 $messages['smtptoerror'] = 'SMTP Error ($code): Failed to add recipient "$to" ($msg).';
 $messages['smtprecipientserror'] = 'SMTP Error: Unable to parse recipients list.';
-$messages['smtpdsnerror'] = 'SMTP Error: No support for Delivery Status Notifications.';
 $messages['smtperror'] = 'SMTP Error: $msg';
 $messages['emailformaterror'] = 'Invalid e-mail address: $email';
 $messages['toomanyrecipients'] = 'Too many recipients. Reduce the number of recipients to $max.';
@@ -139,11 +140,16 @@ $messages['contactrestored'] = 'Contact(s) restored successfully.';
 $messages['groupdeleted'] = 'Group deleted successfully.';
 $messages['grouprenamed'] = 'Group renamed successfully.';
 $messages['groupcreated'] = 'Group created successfully.';
+$messages['savedsearchdeleted'] = 'Saved search deleted successfully.';
+$messages['savedsearchdeleteerror'] = 'Could not delete saved search.';
+$messages['savedsearchcreated'] = 'Saved search created successfully.';
+$messages['savedsearchcreateerror'] = 'Could not create saved search.';
 $messages['messagedeleted'] = 'Message(s) deleted successfully.';
 $messages['messagemoved'] = 'Message(s) moved successfully.';
 $messages['messagecopied'] = 'Message(s) copied successfully.';
 $messages['messagemarked'] = 'Message(s) marked successfully.';
 $messages['autocompletechars'] = 'Enter at least $min characters for autocompletion.';
+$messages['autocompletemore'] = 'More matching entries found. Please type more characters.';
 $messages['namecannotbeempty'] = 'Name cannot be empty.';
 $messages['nametoolong'] = 'Name is too long.';
 $messages['folderupdated'] = 'Folder updated successfully.';
@@ -151,5 +157,6 @@ $messages['foldercreated'] = 'Folder created successfully.';
 $messages['invalidimageformat'] = 'Not a valid image format.';
 $messages['mispellingsfound'] = 'Spelling errors detected in the message.';
 $messages['parentnotwritable'] = 'Unable to create/move folder into selected parent folder. No access rights.';
+$messages['messagetoobig'] = 'The message part is too big to process it.';
 
 ?>
index fa0d824528a094870a50411b56aac864fcd45812..2e1a49da1ec7c69f358ef8f91fc004f4c50dbf88 100644 (file)
@@ -16,7 +16,7 @@
 |         José M. Ciordia <ciordia@gmail.com>                           |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 4671 2011-04-20 08:47:44Z thomasb $
+@version $Id: labels.inc 5290 2011-09-28 17:09:50Z thomasb $
 
 */
 
@@ -103,6 +103,8 @@ $labels['replytomessage'] = 'Responder mensaje';
 $labels['replytoallmessage'] = 'Responder al emisor y a todos los destinatarios';
 $labels['replyall'] = 'Responder a todos';
 $labels['replylist'] = 'Responder a la lista';
+$labels['forwardinline'] = 'Reenviar directamente';
+$labels['forwardattachment'] = 'Reenviar como adjunto';
 $labels['forwardmessage'] = 'Reenviar mensaje';
 $labels['deletemessage'] = 'Eliminar mensaje';
 $labels['movemessagetotrash'] = 'Mover mensaje a la papelera';
@@ -118,7 +120,7 @@ $labels['markread'] = 'Como leído';
 $labels['markunread'] = 'Como no leído';
 $labels['markflagged'] = 'Como marcado';
 $labels['markunflagged'] = 'Como no marcado';
-$labels['messageactions'] = 'Más acciones...';
+$labels['moreactions'] = 'Más acciones…';
 $labels['select'] = 'Elija';
 $labels['all'] = 'Todos';
 $labels['none'] = 'Ninguno';
@@ -171,12 +173,15 @@ $labels['charset'] = 'Codificación';
 $labels['editortype'] = 'Tipo de editor';
 $labels['returnreceipt'] = 'Acuse de recibo';
 $labels['dsn'] = 'Entrega de notificaciones de estado';
+$labels['mailreplyintro'] = 'El $date, $sender escribió:';
+$labels['originalmessage'] = 'Mensaje original';
 $labels['editidents'] = 'Editar identidades';
 $labels['checkspelling'] = 'Revisar ortografía';
 $labels['resumeediting'] = 'Continuar edición';
 $labels['revertto'] = 'Revertir a';
 $labels['attachments'] = 'Adjuntos';
 $labels['upload'] = 'Subir';
+$labels['uploadprogress'] = '$percent ($current de $total)';
 $labels['close'] = 'Cerrar';
 $labels['messageoptions'] = 'Opciones de mensaje...';
 $labels['low'] = 'Bajo';
@@ -229,6 +234,10 @@ $labels['female'] = 'Mujer';
 $labels['manager'] = 'Director';
 $labels['assistant'] = 'Auxiliar';
 $labels['spouse'] = 'Cónyuge';
+$labels['allfields'] = 'Todos los campos';
+$labels['search'] = 'Buscar';
+$labels['advsearch'] = 'Búsqueda avanzada';
+$labels['other'] = 'Otro';
 $labels['typehome'] = 'Casa';
 $labels['typework'] = 'Trabajo';
 $labels['typeother'] = 'Otro';
@@ -240,6 +249,9 @@ $labels['typecar'] = 'Coche';
 $labels['typepager'] = 'Busca';
 $labels['typevideo'] = 'Vídeo';
 $labels['typeassistant'] = 'Auxiliar';
+$labels['typehomepage'] = 'Inicio';
+$labels['typeblog'] = 'Blog';
+$labels['typeprofile'] = 'Perfil';
 $labels['addfield'] = 'Añadir campo...';
 $labels['addcontact'] = 'Añadir nuevo contacto';
 $labels['editcontact'] = 'Editar contacto';
@@ -261,7 +273,8 @@ $labels['print'] = 'Imprimir';
 $labels['export'] = 'Exportar';
 $labels['exportvcards'] = 'Exportar contactos en formato vCard';
 $labels['newcontactgroup'] = 'Crear un nuevo grupo de contactos';
-$labels['groupactions'] = 'Acciones para grupos de contactos...';
+$labels['grouprename'] = 'Renombrar grupo';
+$labels['groupdelete'] = 'Borrar grupo';
 $labels['previouspage'] = 'Mostrar grupo anterior';
 $labels['firstpage'] = 'Mostrar primer grupo';
 $labels['nextpage'] = 'Mostrar grupo siguiente';
@@ -269,9 +282,12 @@ $labels['lastpage'] = 'Mostrar último grupo';
 $labels['group'] = 'Grupo';
 $labels['groups'] = 'Grupos';
 $labels['personaladrbook'] = 'Direcciones personales';
+$labels['searchsave'] = 'Guardar búsqueda';
+$labels['searchdelete'] = 'Borrar búsqueda';
 $labels['import'] = 'Importar';
 $labels['importcontacts'] = 'Importar contactos';
 $labels['importfromfile'] = 'Importar desde archivo:';
+$labels['importtarget'] = 'Añadir un nuevo contacto a la los contactos:';
 $labels['importreplace'] = 'Reemplazar toda la lista de contactos';
 $labels['importtext'] = 'Puede importar contactos desde una lista existente.<br/>Actualmente sólo soportamos el formato <a href="http://es.wikipedia.org/wiki/VCard">vCard</a>.';
 $labels['done'] = 'Hecho';
@@ -287,6 +303,8 @@ $labels['edititem'] = 'Editar';
 $labels['preferhtml'] = 'Prefiero HTML';
 $labels['defaultcharset'] = 'Juego de caracteres por defecto';
 $labels['htmlmessage'] = 'Mensaje HTML';
+$labels['dateformat'] = 'Formato de fecha';
+$labels['timeformat'] = 'Formato de hora';
 $labels['prettydate'] = 'Fecha detallada';
 $labels['setdefault'] = 'Seleccionar opción por defecto';
 $labels['autodetect'] = 'Automático';
@@ -357,6 +375,13 @@ $labels['afternseconds'] = 'después de $n segundos';
 $labels['reqmdn'] = 'Solicitar siempre un acuse de recibo';
 $labels['reqdsn'] = 'Solicitar siempre la entrega de notificaciones de estado';
 $labels['replysamefolder'] = 'Coloque las respuestas en la bandeja del mensaje que se responde';
+$labels['defaultaddressbook'] = 'Añadir nuevos contactos a la lista de contactos seleccionada';
+$labels['spellcheckbeforesend'] = 'Comprobar ortografía antes de enviar un mensaje';
+$labels['spellcheckoptions'] = 'Opciones de ortografía';
+$labels['spellcheckignoresyms'] = 'Ignorar palabras con símbolos';
+$labels['spellcheckignorenums'] = 'Ignorar palabras con números';
+$labels['spellcheckignorecaps'] = 'Ignorar palabras con todo mayúsculas';
+$labels['addtodict'] = 'Añadir al diccionario';
 $labels['folder'] = 'Bandeja';
 $labels['folders'] = 'Bandejas';
 $labels['foldername'] = 'Nombre de bandeja';
@@ -380,6 +405,7 @@ $labels['sharedfolder'] = 'Bandeja pública';
 $labels['sortby'] = 'Ordenar por';
 $labels['sortasc'] = 'Orden ascendente';
 $labels['sortdesc'] = 'Orden descendente';
+$labels['undo'] = 'Deshacer';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index c23f3faa5fe1a5f5621a04f79ba6c31415086cd4..4ed0e5debb5ca1e8e90288cdc4a2f8187ec0849a 100644 (file)
@@ -17,7 +17,7 @@
 |          José M. Ciordia <ciordia@gmail.com>                           |
 +------------------------------------------------------------------------+
 
-@version $Id: messages.inc 4671 2011-04-20 08:47:44Z thomasb $
+@version $Id: messages.inc 5290 2011-09-28 17:09:50Z thomasb $
 */
 
 $messages = array();
@@ -27,6 +27,7 @@ $messages['sessionerror'] = 'Su sesión no es válida o ha expirado';
 $messages['imaperror'] = 'Error de conexión con el servidor IMAP';
 $messages['servererror'] = '¡Error del servidor!';
 $messages['servererrormsg'] = 'Error de servidor: $msg';
+$messages['dberror'] = '¡Error de base de datos!';
 $messages['errorreadonly'] = 'No se ha podido hacer. La bandeja es sólo de lectura';
 $messages['errornoperm'] = 'No se ha podido hacer. Permiso denegado';
 $messages['invalidrequest'] = '¡Petición no válida! No se han guardado los datos.';
@@ -35,6 +36,7 @@ $messages['loggedout'] = 'Ha cerrado bien la sesión. ¡Hasta pronto!';
 $messages['mailboxempty'] = 'La casilla está vacía';
 $messages['loading'] = 'Cargando...';
 $messages['uploading'] = 'Subiendo fichero...';
+$messages['uploadingmany'] = 'Subiendo archivos…';
 $messages['loadingdata'] = 'Cargando datos...';
 $messages['checkingmail'] = 'Verificando si hay nuevos mensajes...';
 $messages['sendingmessage'] = 'Enviando mensaje...';
@@ -44,6 +46,7 @@ $messages['messagesaved'] = 'Mensaje guardado en borradores';
 $messages['successfullysaved'] = 'Guardado correctamente';
 $messages['addedsuccessfully'] = 'Contacto añadido correctamente a la libreta de direcciones';
 $messages['contactexists'] = 'Ya existe un contacto con esta dirección de correo';
+$messages['contactnameexists'] = 'Ya existe un contacto con el mismo nombre.';
 $messages['blockedimages'] = 'Para proteger su privacidad, las imágenes externas han sido bloqueadas en este mensaje';
 $messages['encryptedmessage'] = 'Este es un mensaje cifrado y no puede ser mostrado. ¡Lo siento!';
 $messages['nocontactsfound'] = 'No hay contactos';
@@ -57,9 +60,11 @@ $messages['errorcopying'] = 'No se ha podido copiar el/los mensaje(s)';
 $messages['errordeleting'] = 'No se ha podido eliminar el mensaje';
 $messages['errormarking'] = 'No se ha podido marcar el mensaje';
 $messages['deletecontactconfirm'] = '¿Realmente quiere eliminar los contactos seleccionados?';
+$messages['deletegroupconfirm'] = '¿De verdad quiere borrar el grupo seleccionado?';
 $messages['deletemessagesconfirm'] = '¿Realmente quiere eliminar los mensajes seleccionados?';
 $messages['deletefolderconfirm'] = '¿Realmente quiere eliminar esta bandeja?';
 $messages['purgefolderconfirm'] = '¿Realmente quiere eliminar todos los mensajes de esta bandeja?';
+$messages['groupdeleting'] = 'Borrando grupo…';
 $messages['folderdeleting'] = 'Borrando bandeja...';
 $messages['foldermoving'] = 'Moviendo bandeja...';
 $messages['foldersubscribing'] = 'Suscribiendo bandeja...';
@@ -74,10 +79,10 @@ $messages['nosubjectwarning'] = 'El campo "Asunto" está vacío. ¿Desea complet
 $messages['nobodywarning'] = '¿Quiere enviar este mensaje sin texto?';
 $messages['notsentwarning'] = 'El mensaje no ha sido enviado. ¿Desea descartar su mensaje?';
 $messages['noldapserver'] = 'Por favor, seleccione un servidor LDAP para buscar';
-$messages['nocontactsreturned'] = 'No se han encontrado contactos';
 $messages['nosearchname'] = 'Por favor, introduzca un nombre o la dirección de e-mail';
 $messages['notuploadedwarning'] = 'No se han subido aún todos los adjuntos. Por favor espere o cancele la subida.';
 $messages['searchsuccessful'] = 'Se encontraron $nr mensajes';
+$messages['contactsearchsuccessful'] = 'Encontrados $nr contactos.';
 $messages['searchnomatch'] = 'No se obtuvieron resultados';
 $messages['searching'] = 'Buscando...';
 $messages['checking'] = 'Revisando...';
@@ -98,8 +103,11 @@ $messages['sourceisreadonly'] = 'Esta dirección es de sólo lectura';
 $messages['errorsavingcontact'] = 'No se pudo guardar la dirección de contacto';
 $messages['movingmessage'] = 'Moviendo mensaje...';
 $messages['copyingmessage'] = 'Copiando mensaje...';
+$messages['copyingcontact'] = 'Copiando contacto(s)…';
 $messages['deletingmessage'] = 'Eliminando mensaje(s)...';
 $messages['markingmessage'] = 'Marcando mensaje(s)...';
+$messages['addingmember'] = 'Añadiendo contacto(s) al grupo…';
+$messages['removingmember'] = 'Quitando contacto(s) del grupo…';
 $messages['receiptsent'] = 'La notificación de lectura se ha enviado correctamente.';
 $messages['errorsendingreceipt'] = 'No se ha podido enviar la notificación de lectura.';
 $messages['nodeletelastidentity'] = 'No se puede borrar esta identidad puesto que es la última.';
@@ -129,9 +137,15 @@ $messages['maxgroupmembersreached'] = 'El número de miembros del grupo excede e
 $messages['internalerror'] = 'Ocurrió un error interno. Por favor, inténtalo de nuevo';
 $messages['contactdelerror'] = 'No se ha podido eliminar el contacto(s)';
 $messages['contactdeleted'] = 'Contacto(s) eliminado(s) correctamente';
+$messages['contactrestoreerror'] = 'No se han podido restaurar los contactos borrados.';
+$messages['contactrestored'] = 'Los contactos se han restaurado con éxito.';
 $messages['groupdeleted'] = 'Grupo eliminado correctamente';
 $messages['grouprenamed'] = 'Grupo renombrado correctamente';
 $messages['groupcreated'] = 'Grupo creado correctamente';
+$messages['savedsearchdeleted'] = 'La búsqueda guardada se ha borrado con éxito.';
+$messages['savedsearchdeleteerror'] = 'No se ha podido borrar la búsqueda guardada.';
+$messages['savedsearchcreated'] = 'Se ha guardado correctamente la búsqueda.';
+$messages['savedsearchcreateerror'] = 'No se ha podido guardar la búsqueda.';
 $messages['messagedeleted'] = 'Mensaje(s) eliminado(s) correctamente';
 $messages['messagemoved'] = 'Mensaje(s) movido(s) correctamente';
 $messages['messagecopied'] = 'Mensaje(s) copiado(s) correctamente';
@@ -142,5 +156,7 @@ $messages['nametoolong'] = 'El nombre es demasiado largo';
 $messages['folderupdated'] = 'Bandeja actualizada correctamente';
 $messages['foldercreated'] = 'Bandeja creada correctamente';
 $messages['invalidimageformat'] = 'No es un formato de imagen válido';
+$messages['mispellingsfound'] = 'Se han detectado errores ortográficos en el mensaje.';
+$messages['parentnotwritable'] = 'No se puede crear/mover la carpeta a la carpeta superior elegida. No tiene permisos de acceso.';
 
 ?>
index 70cc69f776f6632d99cca3931474ad942ac3128d..40e0082cdf07eeb3cca85ddd32818d0e53b2c700 100644 (file)
@@ -15,7 +15,7 @@
 |         Elan Ruusamäe <glen@delfi.ee>                                 |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: messages.inc 5139 2011-08-28 09:47:15Z alec $
 
 */
 
@@ -26,7 +26,7 @@ $messages['sessionerror'] = 'Sinu seanss on aegunud või vigane';
 $messages['imaperror'] = 'Ei õnnestunud IMAP serveriga ühendust luua';
 $messages['servererror'] = 'Serveri tõrge!';
 $messages['servererrormsg'] = 'Serveri tõrge: $msg';
-$messages['databaserror'] = 'Andmebaasi tõrge!';
+$messages['dberror'] = 'Andmebaasi tõrge!';
 $messages['errorreadonly'] = 'Operatsioon nurjus. Kaustale on vaid lugemisõigus';
 $messages['errornoperm'] = 'Operatsioon nurjus. Ligipääsu õigused puuduvad';
 $messages['invalidrequest'] = 'Lubamatu päring! Andmeid ei salvestatud.';
index b56a6c90351c261507b6da1e820c3f04aaa17204..205dcaaf14568d8dae67119dbe573a2a9c267abe 100644 (file)
@@ -6,7 +6,7 @@
 | language/fr_FR/labels.inc                                             |
 |                                                                       |
 | Language file of the Roundcube Webmail client                         |
-| Copyright (C) 2005-2010, The Roundcube Dev Team                       |
+| Copyright (C) 2005-2011, The Roundcube Dev Team                       |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 +-----------------------------------------------------------------------+
@@ -15,7 +15,7 @@
 |         Maximilien Cuony <theglu@theglu.org>                          |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5291 2011-09-28 17:10:45Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -119,7 +119,7 @@ $labels['markread'] = 'Comme lus';
 $labels['markunread'] = 'Comme non lus';
 $labels['markflagged'] = 'Comme suivi';
 $labels['markunflagged'] = 'Comme non-suivi';
-$labels['messageactions'] = 'Actions supplémentaires...';
+$labels['moreactions'] = 'Plus d\'actions...';
 $labels['select'] = 'Sélectionner';
 $labels['all'] = 'Tous';
 $labels['none'] = 'Aucun';
@@ -191,6 +191,7 @@ $labels['highest'] = 'La plus élevée';
 $labels['nosubject'] = '(pas de sujet)';
 $labels['showimages'] = 'Afficher les images';
 $labels['alwaysshow'] = 'Toujours afficher les images de $sender';
+$labels['isdraft'] = 'Ceci est un brouillon.';
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'texte brut';
 $labels['savesentmessagein'] = 'Enregistrer le message envoyé dans';
@@ -226,7 +227,7 @@ $labels['country'] = 'Pays';
 $labels['birthday'] = 'Date de naissance';
 $labels['anniversary'] = 'Anniversaire';
 $labels['website'] = 'Site Web';
-$labels['instantmessenger'] = 'Messagerie instantannée';
+$labels['instantmessenger'] = 'Messagerie instantanée';
 $labels['notes'] = 'Notes';
 $labels['male'] = 'Homme';
 $labels['female'] = 'Femme';
@@ -234,7 +235,7 @@ $labels['manager'] = 'Manager';
 $labels['assistant'] = 'Assistant';
 $labels['spouse'] = 'Épouse';
 $labels['allfields'] = 'Tous les champs';
-$labels['search'] = 'Rechecher';
+$labels['search'] = 'Rechercher';
 $labels['advsearch'] = 'Recherche avancée';
 $labels['other'] = 'Autre';
 $labels['typehome'] = 'Domicile';
@@ -272,7 +273,6 @@ $labels['print'] = 'Imprimer';
 $labels['export'] = 'Exporter';
 $labels['exportvcards'] = 'Exporter les contacts au format vCard';
 $labels['newcontactgroup'] = 'Créer un nouveau groupe de contacts';
-$labels['groupactions'] = 'Actions pour les groupes de contacts';
 $labels['grouprename'] = 'Renommer le groupe';
 $labels['groupdelete'] = 'Supprimer le groupe';
 $labels['previouspage'] = 'Montrer page précédente';
@@ -282,6 +282,8 @@ $labels['lastpage'] = 'Voir la dernière page';
 $labels['group'] = 'Groupe';
 $labels['groups'] = 'Groupes';
 $labels['personaladrbook'] = 'Adresses personnelles';
+$labels['searchsave'] = 'Enregistrer la recherche';
+$labels['searchdelete'] = 'Supprimer la recherche';
 $labels['import'] = 'Importer';
 $labels['importcontacts'] = 'Importer les contacts';
 $labels['importfromfile'] = 'Importer depuis un fichier :';
@@ -290,6 +292,7 @@ $labels['importreplace'] = 'Remplacer le carnet d\'adresse entier';
 $labels['importtext'] = 'Vous pouvez envoyer des contacts depuis un carnet d\'adresse existant. Nous supportons actuellement l\'importation d\'adresses au format vCard.';
 $labels['done'] = 'Terminé';
 $labels['settingsfor'] = 'Paramètres pour';
+$labels['about'] = 'A propos';
 $labels['preferences'] = 'Préférences';
 $labels['userpreferences'] = 'Préférences utilisateur';
 $labels['editpreferences'] = 'Modifier les préférences utilisateur';
@@ -301,6 +304,8 @@ $labels['edititem'] = 'Modifier l\'élément';
 $labels['preferhtml'] = 'Afficher en HTML';
 $labels['defaultcharset'] = 'Encodage par défaut';
 $labels['htmlmessage'] = 'Message en HTML';
+$labels['dateformat'] = 'Format de la date';
+$labels['timeformat'] = 'Format de l\'heure';
 $labels['prettydate'] = 'Affichage court des dates';
 $labels['setdefault'] = 'Paramètres par défaut';
 $labels['autodetect'] = 'Automatique';
@@ -372,7 +377,13 @@ $labels['reqmdn'] = 'Toujours demander un avis de réception';
 $labels['reqdsn'] = 'Toujours demander une notification d\'état de distribution';
 $labels['replysamefolder'] = 'Placer les réponses dans le dossier du message auquel il est répondu';
 $labels['defaultaddressbook'] = 'Ajouter de nouveaux contacts au carnet d’adresses sélectionné';
+$labels['autocompletesingle'] = 'Ne pas tenir compte des adresses emails alternatives dans l\'autoremplissage';
 $labels['spellcheckbeforesend'] = 'Vérifier l’orthographe avant l’envoie d’un message';
+$labels['spellcheckoptions'] = 'Options du vérificateur d\'orthographe';
+$labels['spellcheckignoresyms'] = 'Ignorer les mots avec des symboles';
+$labels['spellcheckignorenums'] = 'Ignorer les mots avec des nombres';
+$labels['spellcheckignorecaps'] = 'Ignorer les mots entièrement en majuscule';
+$labels['addtodict'] = 'Ajouter au dictionnaire';
 $labels['folder'] = 'Dossier';
 $labels['folders'] = 'Dossiers';
 $labels['foldername'] = 'Nom du dossier';
@@ -397,6 +408,11 @@ $labels['sortby'] = 'Trier par';
 $labels['sortasc'] = 'Tri ascendant';
 $labels['sortdesc'] = 'Tri descendant';
 $labels['undo'] = 'Annuler';
+$labels['plugin'] = 'Plugin';
+$labels['version'] = 'Version';
+$labels['source'] = 'Source';
+$labels['license'] = 'Licence';
+$labels['support'] = 'Support';
 $labels['B'] = 'o';
 $labels['KB'] = 'ko';
 $labels['MB'] = 'Mo';
@@ -420,4 +436,4 @@ $labels['japanese'] = 'Japonais';
 $labels['korean'] = 'Coréen';
 $labels['chinese'] = 'Chinois';
 
-?>
+?>
\ No newline at end of file
index 41f3108866c73adccb0f02a808912e107bb73417..8df142d86286c34223b77db70b8e7dd9fac384d1 100644 (file)
@@ -6,7 +6,7 @@
 | language/fr_FR/messages.inc                                           |
 |                                                                       |
 | Language file of the Roundcube Webmail client                         |
-| Copyright (C) 2005-2010, The Roundcube Dev Team                       |
+| Copyright (C) 2005-2011, The Roundcube Dev Team                       |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 +-----------------------------------------------------------------------+
@@ -15,7 +15,7 @@
 |         Maximilien Cuony <theglu@theglu.org>                          |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5291 2011-09-28 17:10:45Z thomasb $
+@version $Id: messages.inc 5609 2011-12-14 12:58:13Z thomasb $
 
 */
 
@@ -50,6 +50,7 @@ $messages['blockedimages'] = 'Afin de préserver votre vie privée, les images d
 $messages['encryptedmessage'] = 'Ceci est un message crypté et il ne peut pas être affiché. Désolé !';
 $messages['nocontactsfound'] = 'Aucun contact n\'a pu être trouvé';
 $messages['contactnotfound'] = 'Le contact demandé n\'a pas été trouvé';
+$messages['contactsearchonly'] = 'Entrez un ou plusieurs mots clés pour trouver des contacts';
 $messages['sendingfailed'] = 'L\'envoi du message a échoué';
 $messages['senttooquickly'] = 'Vous devez attendre $sec sec. pour envoyer le message';
 $messages['errorsavingsent'] = 'Une erreur est survenue pendant la sauvegarde du message envoyé';
@@ -63,6 +64,7 @@ $messages['deletegroupconfirm'] = 'Êtes-vous sûr de vouloir supprimer le group
 $messages['deletemessagesconfirm'] = 'Voulez-vous vraiment supprimer le(s) message(s) sélectionné(s) ?';
 $messages['deletefolderconfirm'] = 'Voulez-vous vraiment effacer ce dossier ?';
 $messages['purgefolderconfirm'] = 'Voulez-vous vraiment effacer tous les messages de ce dossier ?';
+$messages['contactdeleting'] = 'Suppression de contact(s)...';
 $messages['groupdeleting'] = 'Suppression du groupe ...';
 $messages['folderdeleting'] = 'Suppression du dossier...';
 $messages['foldermoving'] = 'Déplacement du dossier...';
@@ -74,14 +76,14 @@ $messages['nonamewarning'] = 'Veuillez fournir un nom';
 $messages['nopagesizewarning'] = 'Veuillez indiquer une taille de page';
 $messages['nosenderwarning'] = 'Veuillez renseigner l\'adresse d\'expéditeur';
 $messages['norecipientwarning'] = 'Veuillez ajouter au moins un destinataire';
-$messages['nosubjectwarning'] = 'Le champ « Objet » est vide. Souhaitez-vous le renseigner maintenant ?';
+$messages['nosubjectwarning'] = 'Le champ «Objet» est vide. Souhaitez-vous le renseigner maintenant ?';
 $messages['nobodywarning'] = 'Envoyer ce message sans texte ?';
 $messages['notsentwarning'] = 'Le message n\'a pas été envoyé. Voulez-vous abandonner ce message ?';
 $messages['noldapserver'] = 'Choisissez un serveur LDAP pour la recherche';
-$messages['nocontactsreturned'] = 'Aucun contact trouvé';
 $messages['nosearchname'] = 'Entrez un nom de contact ou un courriel';
 $messages['notuploadedwarning'] = 'Toutes les pièces jointes ne sont pas encore envoyées pour le moment. Merci d\'attendre ou d\'annuler l\'envoi.';
 $messages['searchsuccessful'] = '$nr messages trouvés';
+$messages['contactsearchsuccessful'] = '$nr contact(s) trouvé(s).';
 $messages['searchnomatch'] = 'La recherche ne donne aucun résultat';
 $messages['searching'] = 'En cours de recherche...';
 $messages['checking'] = 'Vérification...';
@@ -90,7 +92,7 @@ $messages['folderdeleted'] = 'Dossier effacé';
 $messages['foldersubscribed'] = 'Le dossier a bien été inscrit';
 $messages['folderunsubscribed'] = 'Le dossier a bien été désinscrit';
 $messages['folderpurged'] = 'Le dossier a bien été vidé';
-$messages['folderexpunged'] = 'Le dossier a bien été comptacté';
+$messages['folderexpunged'] = 'Le dossier a bien été compacté';
 $messages['deletedsuccessfully'] = 'Supprimé(s) avec succès';
 $messages['converting'] = 'Suppression de la mise en forme...';
 $messages['messageopenerror'] = 'Impossible de charger le message depuis serveur';
@@ -128,7 +130,6 @@ $messages['smtpautherror'] = 'Erreur SMTP ($code): Echec de l\'authentification'
 $messages['smtpfromerror'] = 'Erreur SMTP ($code): Impossible de définir l\'expéditeur "$from" ($msg)';
 $messages['smtptoerror'] = 'Erreur SMTP ($code): Impossible d\'ajouter le destinataire "$to" ($msg)';
 $messages['smtprecipientserror'] = 'Erreur SMTP: Impossible de lire la liste des destinataires';
-$messages['smtpdsnerror'] = 'Erreur SMTP: Pas de support des notifications d\'état de distribution';
 $messages['smtperror'] = 'Erreur SMTP: $msg';
 $messages['emailformaterror'] = 'Adresse email incorrecte: $email';
 $messages['toomanyrecipients'] = 'Trop de destinataires. Réduisez leur nombre à $max maximum.';
@@ -141,11 +142,16 @@ $messages['contactrestored'] = 'Les contacts ont bien été restaurés.';
 $messages['groupdeleted'] = 'Le groupe a bien été supprimé';
 $messages['grouprenamed'] = 'Le groupe a bien été renommé';
 $messages['groupcreated'] = 'Le groupe a bien été créé';
+$messages['savedsearchdeleted'] = 'La recherche enregistrée a bien été supprimée.';
+$messages['savedsearchdeleteerror'] = 'Impossible de supprimer la recherche enregistrée.';
+$messages['savedsearchcreated'] = 'La recherche enregistrée a bien été crée.';
+$messages['savedsearchcreateerror'] = 'Impossible de créer la recherche enregistrée.';
 $messages['messagedeleted'] = 'Les messages ont bien été supprimés';
 $messages['messagemoved'] = 'Les messages ont bien été déplacés';
 $messages['messagecopied'] = 'Les messages ont bien été copiés';
 $messages['messagemarked'] = 'Les messages ont bien été marqués';
 $messages['autocompletechars'] = 'Entrez au moins $min caractères pour l\'auto-complétion';
+$messages['autocompletemore'] = 'Plusieurs entrées trouvées. Tapez plus de caractères.';
 $messages['namecannotbeempty'] = 'Le nom ne peut pas être vide';
 $messages['nametoolong'] = 'Le nom est trop long';
 $messages['folderupdated'] = 'Le dossier a bien été mis à jour';
@@ -153,5 +159,6 @@ $messages['foldercreated'] = 'Le dossier a bien été créé';
 $messages['invalidimageformat'] = 'Format d\'image invalide';
 $messages['mispellingsfound'] = 'Des fautes d\'orthographe ont été détectées dans le message.';
 $messages['parentnotwritable'] = 'Impossible de créer/déplacer le dossier dans le dossier parent sélectionné. Aucun droit d\'accès.';
+$messages['messagetoobig'] = 'Le message est trop gros pour être traité.';
 
-?>
+?>
\ No newline at end of file
index 17f1fba66868852f9a84097cf349a7251b083202..bdceb7ef0fd6d73b742dd341977e2f86630bbbe7 100644 (file)
@@ -5,7 +5,7 @@
 | language/gl_ES/labels.inc                                             |
 |                                                                       |
 | Language file of the Roundcube Webmail client                         |
-| Copyright (C) 2005-2010, The Roundcube Dev Team                       |
+| Copyright (C) 2005-2011, The Roundcube Dev Team                       |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 +-----------------------------------------------------------------------+
@@ -26,14 +26,14 @@ $labels['settings'] = 'Axustes persoais';
 $labels['addressbook'] = 'Caderno de enderezos';
 $labels['inbox'] = 'Caixa de entrada';
 $labels['drafts'] = 'Borradores';
-$labels['sent'] = 'Enviado';
+$labels['sent'] = 'Enviados';
 $labels['trash'] = 'Cubo do lixo';
 $labels['junk'] = 'Correo lixo';
 $labels['subject'] = 'Asunto';
 $labels['from'] = 'Remitente';
 $labels['to'] = 'Destinatario';
-$labels['cc'] = 'Copia (CC)';
-$labels['bcc'] = 'Copia oculta (BCC)';
+$labels['cc'] = 'Copia (Cc)';
+$labels['bcc'] = 'Copia oculta (Cco)';
 $labels['replyto'] = 'Respostar a (Reply-To)';
 $labels['followupto'] = 'Respostar a todos (Followup-To)';
 $labels['date'] = 'Data';
@@ -42,7 +42,7 @@ $labels['priority'] = 'Prioridade';
 $labels['organization'] = 'Organización';
 $labels['readstatus'] = 'Estado da lectura';
 $labels['mailboxlist'] = 'Cartafoles';
-$labels['messagesfromto'] = 'Mensaxes do $from ao $to de $count';
+$labels['messagesfromto'] = 'Mensaxes da $from á $to de $count';
 $labels['threadsfromto'] = 'Fíos do $from ao $to de $count';
 $labels['messagenrof'] = 'Mensaxe $nr de $count';
 $labels['copy'] = 'Copiar';
@@ -92,31 +92,29 @@ $labels['longnov'] = 'Novembro';
 $labels['longdec'] = 'Decembro';
 $labels['today'] = 'Hoxe';
 $labels['checkmail'] = 'Procurar novas mensaxes';
-$labels['writenewmessage'] = 'Crear unha nova mensaxe';
+$labels['writenewmessage'] = 'Redactar unha mensaxe nova';
 $labels['replytomessage'] = 'Respostar a mensaxe';
 $labels['replytoallmessage'] = 'Respostar á lista ou ao remitente e a tódolos destinatarios';
 $labels['replyall'] = 'Respostar a todos';
 $labels['replylist'] = 'Respostar á lista de correo';
+$labels['forwardinline'] = 'Reenviar inserido';
+$labels['forwardattachment'] = 'Reenviar como anexo';
 $labels['forwardmessage'] = 'Reenviar a mensaxe';
 $labels['deletemessage'] = 'Eliminar a mensaxe';
 $labels['movemessagetotrash'] = 'Mover a mensaxe ao cubo do lixo';
 $labels['printmessage'] = 'Imprimir esta mensaxe';
 $labels['previousmessage'] = 'Amosar a mensaxe anterior';
-$labels['previousmessages'] = 'Amosar o grupo anterior de mensaxes';
 $labels['firstmessage'] = 'Amosar a primeira mensaxe';
-$labels['firstmessages'] = 'Amosar o primeiro grupo de mensaxes';
 $labels['nextmessage'] = 'Amosar a seguinte mensaxe';
-$labels['nextmessages'] = 'Amosar o seguinte grupo de mensaxes';
 $labels['lastmessage'] = 'Amosar a última mensaxe';
-$labels['lastmessages'] = 'Amosar o último grupo de mensaxes';
 $labels['backtolist'] = 'Voltar á lista de mensaxes';
-$labels['viewsource'] = 'Ver fonte';
+$labels['viewsource'] = 'Ver código fonte';
 $labels['markmessages'] = 'Marcar mensaxes';
 $labels['markread'] = 'Como lidas';
 $labels['markunread'] = 'Como non lidas';
 $labels['markflagged'] = 'Como marcadas';
 $labels['markunflagged'] = 'Como non marcadas';
-$labels['messageactions'] = 'Máis accións...';
+$labels['moreactions'] = 'Máis accións...';
 $labels['select'] = 'Seleccionar';
 $labels['all'] = 'Todas';
 $labels['none'] = 'Ningunha';
@@ -126,14 +124,14 @@ $labels['flagged'] = 'Marcadas';
 $labels['unanswered'] = 'Non respostadas';
 $labels['deleted'] = 'Marcadas como eliminadas';
 $labels['invert'] = 'Inverter';
-$labels['filter'] = 'Filtro';
+$labels['filter'] = 'Filtrar';
 $labels['list'] = 'Lista';
 $labels['threads'] = 'Fíos';
 $labels['expand-all'] = 'Expandir todos';
 $labels['expand-unread'] = 'Expandir os non lidos';
 $labels['collapse-all'] = 'Contraer todos';
-$labels['threaded'] = 'Como fíos de discusión';
-$labels['autoexpand_threads'] = 'Expandir os fíos das mensaxes';
+$labels['threaded'] = 'Agrupar conversas';
+$labels['autoexpand_threads'] = 'Expandir os fíos de mensaxes';
 $labels['do_expand'] = 'tódolos fíos';
 $labels['expand_only_unread'] = 'só con mensaxes non lidas';
 $labels['fromto'] = 'Remitente/Destinatario';
@@ -169,12 +167,15 @@ $labels['charset'] = 'Codificación';
 $labels['editortype'] = 'Tipo de editor';
 $labels['returnreceipt'] = 'Notificación da entrega';
 $labels['dsn'] = 'Notificación do estado de envío (DSN)';
+$labels['mailreplyintro'] = 'O $date, $sender escribiu:';
+$labels['originalmessage'] = 'Mensaxe orixinal';
 $labels['editidents'] = 'Modificar identidades';
 $labels['checkspelling'] = 'Revisar a ortografía';
 $labels['resumeediting'] = 'Voltar á edición';
 $labels['revertto'] = 'Voltar a';
 $labels['attachments'] = 'Ficheiros anexos';
 $labels['upload'] = 'Cargar';
+$labels['uploadprogress'] = '$percent ($current de $total)';
 $labels['close'] = 'Pechar';
 $labels['messageoptions'] = 'Opcións da mensaxe...';
 $labels['low'] = 'Baixa';
@@ -183,13 +184,14 @@ $labels['normal'] = 'Normal';
 $labels['high'] = 'Alta';
 $labels['highest'] = 'A máis alta';
 $labels['nosubject'] = '(Sen asunto)';
-$labels['showimages'] = 'Amosar imaxes';
-$labels['alwaysshow'] = 'Amosar sempre as imaxes nas mensaxes remitidas por $sender';
+$labels['showimages'] = 'Amosar as imaxes';
+$labels['alwaysshow'] = 'Amosar sempre as imaxes nas mensaxes de $sender';
+$labels['isdraft'] = 'Esta mensaxe é un borrador';
 $labels['htmltoggle'] = 'HTML';
-$labels['plaintoggle'] = 'Texto claro';
+$labels['plaintoggle'] = 'Só texto';
 $labels['savesentmessagein'] = 'Gardar a mensaxe enviada en';
 $labels['dontsave'] = 'non gardar';
-$labels['maxuploadsize'] = 'O tamaño máximo permitido de ficheiro é de $size';
+$labels['maxuploadsize'] = 'O tamaño máximo permitido por ficheiro é de $size';
 $labels['addcc'] = 'Engadir copia (CC)';
 $labels['addbcc'] = 'Engadir copia oculta (BCC)';
 $labels['addreplyto'] = 'Engadir respostar a (Reply-To)';
@@ -197,29 +199,77 @@ $labels['addfollowupto'] = 'Engadir respostar a todos (Followup-To)';
 $labels['mdnrequest'] = 'O remitente desta mensaxe pediu ser notificado cando vostede a lea. Quere notificar ao remitente?';
 $labels['receiptread'] = 'Notificación da entrega da mensaxe (lectura)';
 $labels['yourmessage'] = 'Esta é unha notificación da entrega da súa mensaxe';
-$labels['receiptnote'] = 'Nota: Esta notificación só confirma que a mensaxe se abriu no computador do destinatario. Non asegura que o destinatario o lera ou entendera o seu contido.';
+$labels['receiptnote'] = 'Nota: Esta notificación só confirma que a mensaxe se abriu no computador do destinatario. Non asegura que o destinatario a lera ou entendera o seu contido.';
 $labels['name'] = 'Nome completo';
 $labels['firstname'] = 'Nome';
 $labels['surname'] = 'Apelidos';
+$labels['middlename'] = 'Segundo nome';
+$labels['nameprefix'] = 'Prefixo';
+$labels['namesuffix'] = 'Sufixo';
+$labels['nickname'] = 'Alcume';
+$labels['jobtitle'] = 'Titulación';
+$labels['department'] = 'Departamento';
+$labels['gender'] = 'Xénero';
+$labels['maidenname'] = 'Nome de solteira';
 $labels['email'] = 'Correo Electrónico';
+$labels['phone'] = 'Teléfono';
+$labels['address'] = 'Enderezo';
+$labels['street'] = 'Rúa';
+$labels['locality'] = 'Poboación';
+$labels['zipcode'] = 'Código postal';
+$labels['region'] = 'Provincia';
+$labels['country'] = 'País';
+$labels['birthday'] = 'Aniversario';
+$labels['anniversary'] = 'Aniversario';
+$labels['website'] = 'Sitio web';
+$labels['instantmessenger'] = 'Mensaxería instantánea';
+$labels['notes'] = 'Notes';
+$labels['male'] = 'home';
+$labels['female'] = 'muller';
+$labels['manager'] = 'Xerente';
+$labels['assistant'] = 'Asistente/a';
+$labels['spouse'] = 'Cónxuxe';
+$labels['allfields'] = 'Tódolos campos';
+$labels['search'] = 'Procurar';
+$labels['advsearch'] = 'Procura avanzada';
+$labels['other'] = 'Outro';
+$labels['typehome'] = 'Casa';
+$labels['typework'] = 'Traballo';
+$labels['typeother'] = 'Outro';
+$labels['typemobile'] = 'Móbil';
+$labels['typemain'] = 'Principal';
+$labels['typehomefax'] = 'Fax da casa';
+$labels['typeworkfax'] = 'Fax do traballo';
+$labels['typecar'] = 'Coche';
+$labels['typepager'] = 'Buscapersonas';
+$labels['typevideo'] = 'Vídeo';
+$labels['typeassistant'] = 'Asistente';
+$labels['typehomepage'] = 'Páxina principal';
+$labels['typeblog'] = 'Blogue';
+$labels['typeprofile'] = 'Perfil';
+$labels['addfield'] = 'Engadir campo';
 $labels['addcontact'] = 'Engadir novo contacto';
-$labels['editcontact'] = 'Editar contacto';
+$labels['editcontact'] = 'Modificar este contacto';
 $labels['contacts'] = 'Contactos';
 $labels['contactproperties'] = 'Propiedades do contacto';
+$labels['personalinfo'] = 'Información persoal';
 $labels['edit'] = 'Editar';
 $labels['cancel'] = 'Cancelar';
 $labels['save'] = 'Gardar';
 $labels['delete'] = 'Eliminar';
 $labels['rename'] = 'Mudar o nome';
-$labels['newcontact'] = 'Crear novo contacto';
+$labels['addphoto'] = 'Engadir';
+$labels['replacephoto'] = 'Substituír';
+$labels['newcontact'] = 'Novo contacto';
 $labels['deletecontact'] = 'Eliminar os contactos seleccionados';
-$labels['composeto'] = 'Redactar mensaxe a';
+$labels['composeto'] = 'Redactar mensaxe para';
 $labels['contactsfromto'] = 'Contactos do $from ao $to de $count';
 $labels['print'] = 'Imprimir';
 $labels['export'] = 'Exportar';
 $labels['exportvcards'] = 'Exportar os contactos en formato vCard';
 $labels['newcontactgroup'] = 'Crear novo grupo de contactos';
-$labels['groupactions'] = 'Accións cos grupos de contactos...';
+$labels['grouprename'] = 'Mudar o nome do grupo';
+$labels['groupdelete'] = 'Borrar o grupo';
 $labels['previouspage'] = 'Amosar o grupo anterior';
 $labels['firstpage'] = 'Amosar o primeiro grupo';
 $labels['nextpage'] = 'Amosar o seguinte grupo';
@@ -227,13 +277,17 @@ $labels['lastpage'] = 'Amosar o último grupo';
 $labels['group'] = 'Grupo';
 $labels['groups'] = 'Grupos';
 $labels['personaladrbook'] = 'Enderezos persoais';
+$labels['searchsave'] = 'Gardar procura';
+$labels['searchdelete'] = 'Eliminar procura';
 $labels['import'] = 'Importar';
 $labels['importcontacts'] = 'Importar contactos';
 $labels['importfromfile'] = 'Importar desde ficheiro:';
+$labels['importtarget'] = 'Engadir contactos ao caderno de enderezos:';
 $labels['importreplace'] = 'Substituír completamente o caderno de enderezos';
 $labels['importtext'] = 'Pode cargar contactos desde un caderno de enderezos xa existente.<br/>Polo de agora pódense importar enderezos usando o formato <a href="http://en.wikipedia.org/wiki/VCard">vCard</a>  ';
 $labels['done'] = 'Rematado';
 $labels['settingsfor'] = 'Axustes de';
+$labels['about'] = 'Acerca de';
 $labels['preferences'] = 'Preferencias';
 $labels['userpreferences'] = 'Preferencias de usuario';
 $labels['editpreferences'] = 'Editar preferencias de usuario';
@@ -245,6 +299,8 @@ $labels['edititem'] = 'Editar';
 $labels['preferhtml'] = 'Prefiro HTML';
 $labels['defaultcharset'] = 'Xogo de caracteres por omisión';
 $labels['htmlmessage'] = 'Mensaxe HTML';
+$labels['dateformat'] = 'Formato da data';
+$labels['timeformat'] = 'Formato da hora';
 $labels['prettydate'] = 'Data decorada';
 $labels['setdefault'] = 'Usar como predeterminada';
 $labels['autodetect'] = 'Detectar automáticamente';
@@ -252,11 +308,11 @@ $labels['language'] = 'Linguaxe';
 $labels['timezone'] = 'Zona horaria';
 $labels['pagesize'] = 'Liñas por páxina';
 $labels['signature'] = 'Sinatura';
-$labels['dstactive'] = 'Cambio de hora';
+$labels['dstactive'] = 'Cambio de hora según horario de verán';
 $labels['htmleditor'] = 'Redactar mensaxes HTML';
 $labels['htmlonreply'] = 'só cando se resposte a unha mensaxe HTML';
 $labels['htmlsignature'] = 'Sinatura HTML';
-$labels['previewpane'] = 'Amosar panel de previsualización';
+$labels['previewpane'] = 'Amosar previsualización';
 $labels['skin'] = 'Aspecto da interface';
 $labels['logoutclear'] = 'Baleirar o cubo do lixo ao saír';
 $labels['logoutcompact'] = 'Compactar a caixa de entrada ao saír';
@@ -272,8 +328,8 @@ $labels['ignore'] = 'ignorar';
 $labels['readwhendeleted'] = 'Marcar a mensaxe como lida ao eliminar';
 $labels['flagfordeletion'] = 'Marcar a mensaxe para eliminar no canto de eliminar';
 $labels['skipdeleted'] = 'Non amosar as mensaxes marcadas como eliminadas';
-$labels['deletealways'] = 'Borrar as mensaxes aínda se non se poden gardar na papeleira';
-$labels['showremoteimages'] = 'Amosar imaxes anexas remotas';
+$labels['deletealways'] = 'Eliminar as mensaxes aínda que non se poidan gardar no cubo do lixo';
+$labels['showremoteimages'] = 'Amosar as imaxes remotas';
 $labels['fromknownsenders'] = 'de remitentes coñecidos';
 $labels['always'] = 'sempre';
 $labels['showinlineimages'] = 'Amosar as imaxes anexas baixo a mensaxe';
@@ -287,26 +343,26 @@ $labels['messagescomposition'] = 'Composición das mensaxes';
 $labels['mimeparamfolding'] = 'Nomes dos ficheiros anexos';
 $labels['2231folding'] = 'Conforme ao RFC 2231 (Thunderbird)';
 $labels['miscfolding'] = 'RFC 2047/2231 (MS Outlook)';
-$labels['2047folding'] = 'Conforme ao RFC 2047 (other)';
+$labels['2047folding'] = 'Conforme ao RFC 2047 (outros)';
 $labels['force7bit'] = 'Usar MIME para codificar caracteres de 8 bits';
 $labels['advancedoptions'] = 'Opcións avanzadas';
 $labels['focusonnewmessage'] = 'Enfocar o navegador se hai mensaxes novas';
 $labels['checkallfolders'] = 'Procurar novas mensaxes en tódolos cartafoles';
-$labels['displaynext'] = 'Amosar a seguinte mensaxe despois de borrar ou mover unha mensaxe';
+$labels['displaynext'] = 'Logo de eliminar ou mover unha mensaxe ir á mensaxe seguinte';
 $labels['mainoptions'] = 'Opcións principais';
 $labels['section'] = 'Sección';
 $labels['maintenance'] = 'Mantemento';
-$labels['newmessage'] = 'Nova mensaxe';
+$labels['newmessage'] = 'Mensaxes novas';
 $labels['listoptions'] = 'Opcións de lista';
 $labels['signatureoptions'] = 'Opcións da firma';
 $labels['whenreplying'] = 'Ao respostar';
 $labels['replytopposting'] = 'comezar a nova mensaxe enriba da orixinal';
 $labels['replybottomposting'] = 'comezar a nova mensaxe embaixo da orixinal';
-$labels['replyremovesignature'] = 'Eliminar a firma orixinal da mensaxe ao respostar';
+$labels['replyremovesignature'] = 'Eliminar a firma do remitente ao respostar';
 $labels['autoaddsignature'] = 'Engadir firma automáticamente';
-$labels['newmessageonly'] = 'só nas novas mensaxes';
+$labels['newmessageonly'] = 'só nas mensaxes novas';
 $labels['replyandforwardonly'] = 'só nas respostas e reenvíos';
-$labels['replysignaturepos'] = 'Ao  respostar ou reenviar colocar a firma ';
+$labels['replysignaturepos'] = 'Ao respostar ou reenviar colocar a firma ';
 $labels['belowquote'] = 'embaixo do texto citado';
 $labels['abovequote'] = 'enriba do texto citado';
 $labels['insertsignature'] = 'Engadir firma';
@@ -315,6 +371,14 @@ $labels['afternseconds'] = 'logo de $n segundos';
 $labels['reqmdn'] = 'Solicitar sempre unha notificación da entrega';
 $labels['reqdsn'] = 'Solicitar sempre unha notificación do estado de envío';
 $labels['replysamefolder'] = 'Deixar as respostas no cartafol onde está a mensaxe á que se responde';
+$labels['defaultaddressbook'] = 'Engadir contactos ao caderno de enderezos seleccionado';
+$labels['autocompletesingle'] = 'Ignorar as direccións alternativas no autocompletado';
+$labels['spellcheckbeforesend'] = 'Revisar a ortografía antes de enviar unha mensaxe';
+$labels['spellcheckoptions'] = 'Opción da revisión ortográfica';
+$labels['spellcheckignoresyms'] = 'Ignorar as palabras con símbolos';
+$labels['spellcheckignorenums'] = 'Ignorar as palabras con números';
+$labels['spellcheckignorecaps'] = 'Ignorar as palabras escritas en maiúsculas';
+$labels['addtodict'] = 'Engadir ao diccionario';
 $labels['folder'] = 'Cartafol';
 $labels['folders'] = 'Cartafoles';
 $labels['foldername'] = 'Nome do cartafol';
@@ -331,9 +395,19 @@ $labels['location'] = 'Situación';
 $labels['info'] = 'Información';
 $labels['getfoldersize'] = 'Prema para calcular o tamaño do cartafol';
 $labels['changesubscription'] = 'Prema para trocar a subscrición';
+$labels['foldertype'] = 'Tipo do cartafol';
+$labels['personalfolder'] = 'Cartafol privado';
+$labels['otherfolder'] = 'Cartafol de outro usuario';
+$labels['sharedfolder'] = 'Cartafol público';
 $labels['sortby'] = 'Ordenar por';
 $labels['sortasc'] = 'Orde ascendente';
 $labels['sortdesc'] = 'Orde descendente';
+$labels['undo'] = 'Desfacer';
+$labels['plugin'] = 'Complemento';
+$labels['version'] = 'Versión';
+$labels['source'] = 'Fonte';
+$labels['license'] = 'Licencia';
+$labels['support'] = 'Obter axuda';
 $labels['B'] = 'B';
 $labels['KB'] = 'KiB';
 $labels['MB'] = 'MiB';
index 521a67a8bbf0df8589098a9bfda185cecf0d200b..c872c8e83b5bb29aa8b39df7f1e615ac51d9e154 100644 (file)
@@ -5,7 +5,7 @@
 | language/gl_ES/messages.inc                                           |
 |                                                                       |
 | Language file of the Roundcube Webmail client                         |
-| Copyright (C) 2005-2010, The Roundcube Dev Team                       |
+| Copyright (C) 2005-2011, The Roundcube Dev Team                       |
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 +-----------------------------------------------------------------------+
 */
 
 $messages = array();
-$messages['loginfailed'] = 'Fallou o acceso';
-$messages['cookiesdisabled'] = 'O seu navegador non acepta galletas';
-$messages['sessionerror'] = 'A súa sesión non é válida ou expirou';
-$messages['imaperror'] = 'Fallou a conexión co servidor IMAP';
+$messages['loginfailed'] = 'O contrasinal ou o nome de usuario son incorrectos.';
+$messages['cookiesdisabled'] = 'O seu navegador non acepta galletas.';
+$messages['sessionerror'] = 'A súa sesión non é válida ou expirou.';
+$messages['imaperror'] = 'Fallou a conexión co servidor IMAP.';
 $messages['servererror'] = 'Erro do servidor!';
 $messages['servererrormsg'] = 'Erro do servidor: $msg';
-$messages['errorreadonly'] = 'Non foi posible realizar a operación. O cartafol é de só lectura';
+$messages['dberror'] = 'Erro na base de datos!';
+$messages['errorreadonly'] = 'Non foi posible realizar a operación. O cartafol é de só lectura.';
 $messages['errornoperm'] = 'Non foi posible realizar a operación. Permiso denegado.';
 $messages['invalidrequest'] = 'Petición inválida!. Non se gardou ningún dato.';
-$messages['nomessagesfound'] = 'Non se atoparon mensaxes nesta caixa de correo';
+$messages['nomessagesfound'] = 'Non se atoparon mensaxes nesta caixa de correo.';
 $messages['loggedout'] = 'Rematou correctamente a súa sesión. Ata logo!';
-$messages['mailboxempty'] = 'A caixa de correo está vacía';
+$messages['mailboxempty'] = 'A caixa de correo está vacía.';
 $messages['loading'] = 'Cargando...';
 $messages['uploading'] = 'Cargando ficheiro...';
+$messages['uploadingmany'] = 'Cargando ficheiros...';
 $messages['loadingdata'] = 'Cargando datos...';
 $messages['checkingmail'] = 'Á procura de novas mensaxes...';
 $messages['sendingmessage'] = 'Enviando mensaxe...';
-$messages['messagesent'] = 'A mensaxe enviouse correctamente';
+$messages['messagesent'] = 'A mensaxe enviouse correctamente.';
 $messages['savingmessage'] = 'Gardando mensaxe...';
 $messages['messagesaved'] = 'A mensaxe gardouse en "Borradores"';
-$messages['successfullysaved'] = 'Gardada correctamente';
-$messages['addedsuccessfully'] = 'O contacto engadiuse correctamente ao caderno de enderezos';
-$messages['contactexists'] = 'Xa existe un contacto con este enderezo de correo electrónico';
-$messages['blockedimages'] = 'Estanse a bloquear as imaxes remotas para protexer a súa privacidade';
-$messages['encryptedmessage'] = 'Síntoo!. Non se pode amosar a mensaxe porque está cifrada';
-$messages['nocontactsfound'] = 'Non se atoparon contactos';
-$messages['contactnotfound'] = 'Non se atopou o contacto solicitado';
-$messages['sendingfailed'] = 'Fallou o envío da mensaxe';
-$messages['senttooquickly'] = 'Por favor, espere $sec segundos antes de enviar esta mensaxe';
-$messages['errorsavingsent'] = 'Ocurriu un erro mentres se gardaba a mensaxe enviada';
-$messages['errorsaving'] = 'Ocurriu un erro mentres se gardaba';
-$messages['errormoving'] = 'Non foi posible mover a(s) mensaxe(s)';
-$messages['errorcopying'] = 'Non foi posible copiar a(s) mensaxe(s)';
-$messages['errordeleting'] = 'Non foi posible eliminar a(s) mensaxe(s)';
-$messages['errormarking'] = 'Non foi posible marcar a(s) mensaxe(s)';
+$messages['successfullysaved'] = 'Gardado correctamente.';
+$messages['addedsuccessfully'] = 'O contacto engadiuse correctamente ao caderno de enderezos.';
+$messages['contactexists'] = 'Xa existe un contacto con este enderezo de correo electrónico.';
+$messages['contactnameexists'] = 'Xa existe un contacto con este nome.';
+$messages['blockedimages'] = 'Estanse a bloquear as imaxes remotas para protexer a súa privacidade.';
+$messages['encryptedmessage'] = 'Non se pode amosar a mensaxe porque está cifrada.';
+$messages['nocontactsfound'] = 'Non se atoparon contactos.';
+$messages['contactnotfound'] = 'Non se atopou o contacto solicitado.';
+$messages['contactsearchonly'] = 'Introduza algún termo para atopar contactos';
+$messages['sendingfailed'] = 'Fallou o envío da mensaxe.';
+$messages['senttooquickly'] = 'Por favor, espere $sec segundos antes de enviar esta mensaxe.';
+$messages['errorsavingsent'] = 'Ocurriu un erro mentres se gardaba a mensaxe enviada.';
+$messages['errorsaving'] = 'Ocurriu un erro mentres se gardaba.';
+$messages['errormoving'] = 'Non foi posible mover a(s) mensaxe(s).';
+$messages['errorcopying'] = 'Non foi posible copiar a(s) mensaxe(s).';
+$messages['errordeleting'] = 'Non foi posible eliminar a(s) mensaxe(s).';
+$messages['errormarking'] = 'Non foi posible marcar a(s) mensaxe(s).';
 $messages['deletecontactconfirm'] = 'Quere eliminar o(s) contacto(s) seleccionado(s)?';
+$messages['deletegroupconfirm'] = 'Quere eliminar o grupo seleccionado?';
 $messages['deletemessagesconfirm'] = 'Quere eliminar a(s) mensaxe(s) seleccionadas?';
 $messages['deletefolderconfirm'] = 'Quere eliminar este cartafol?';
 $messages['purgefolderconfirm'] = 'Quere eliminar tódalas mensaxes neste cartafol?';
+$messages['contactdeleting'] = 'Eliminando o(s) contacto(s)...';
+$messages['groupdeleting'] = 'Eliminando o grupo...';
 $messages['folderdeleting'] = 'Eliminando o cartafol...';
 $messages['foldermoving'] = 'Movendo o cartafol...';
 $messages['foldersubscribing'] = 'Subscribindo o cartafol...';
 $messages['folderunsubscribing'] = 'Desubscribindo o cartafol...';
-$messages['formincomplete'] = 'Non se cumprimentou completamente o formulario';
-$messages['noemailwarning'] = 'Por favor, introduza un enderezo de correo electrónico válido';
-$messages['nonamewarning'] = 'Por favor, introduza un nome';
-$messages['nopagesizewarning'] = 'Por favor, introduza un tamaño de páxina';
-$messages['nosenderwarning'] = 'Por favor, introduza o enderezo de correo electrónico do remitente';
-$messages['norecipientwarning'] = 'Por favor, introduza polo menos un destinatario';
-$messages['nosubjectwarning'] = 'O campo "Asunto" está baleiro. Quere introducir un?';
+$messages['formincomplete'] = 'Non se cumprimentou completamente o formulario.';
+$messages['noemailwarning'] = 'Por favor, introduza un enderezo de correo electrónico válido.';
+$messages['nonamewarning'] = 'Por favor, introduza un nome.';
+$messages['nopagesizewarning'] = 'Por favor, introduza un tamaño de páxina.';
+$messages['nosenderwarning'] = 'Por favor, introduza o enderezo de correo electrónico do remitente.';
+$messages['norecipientwarning'] = 'Por favor, introduza polo menos un destinatario.';
+$messages['nosubjectwarning'] = 'O campo "Asunto" está baleiro. Desexa completalo?';
 $messages['nobodywarning'] = 'Quere enviar esta mensaxe sen texto?';
 $messages['notsentwarning'] = 'A mensaxe non se enviou. Quere descartala?';
-$messages['noldapserver'] = 'Por favor, elixa un servidor LDAP para buscar';
-$messages['nocontactsreturned'] = 'Non se atoparon contactos';
-$messages['nosearchname'] = 'Por favor, introduza un contacto ou un enderezo de correo electrónico';
+$messages['noldapserver'] = 'Por favor, elixa un servidor LDAP para buscar.';
+$messages['nosearchname'] = 'Por favor, introduza un nome ou un enderezo de correo electrónico.';
 $messages['notuploadedwarning'] = 'Aínda non se cargaron tódolos ficheiros. Por favor, agarde ou cancele a carga.';
-$messages['searchsuccessful'] = 'Atopáronse $nr mensaxes';
-$messages['searchnomatch'] = 'A busca non atopou coincidencias';
+$messages['searchsuccessful'] = 'Atopáronse $nr mensaxes.';
+$messages['contactsearchsuccessful'] = 'Atopáronse $nr contactos.';
+$messages['searchnomatch'] = 'A busca non atopou coincidencias.';
 $messages['searching'] = 'Buscando...';
 $messages['checking'] = 'Comprobando...';
-$messages['nospellerrors'] = 'Non hai erros ortográficos';
-$messages['folderdeleted'] = 'O cartafol eliminouse correctamente';
-$messages['foldersubscribed'] = 'O cartafol suscribiuse correctamente';
-$messages['folderunsubscribed'] = 'O cartafol desuscribiuse correctamente';
-$messages['folderpurged'] = 'O cartafol purgouse correctamente';
-$messages['folderexpunged'] = 'O cartafol baleirouse correctamente';
-$messages['deletedsuccessfully'] = 'Eliminouse correctamente';
+$messages['nospellerrors'] = 'Non hai erros ortográficos.';
+$messages['folderdeleted'] = 'O cartafol eliminouse correctamente.';
+$messages['foldersubscribed'] = 'O cartafol suscribiuse correctamente.';
+$messages['folderunsubscribed'] = 'O cartafol desuscribiuse correctamente.';
+$messages['folderpurged'] = 'O cartafol purgouse correctamente.';
+$messages['folderexpunged'] = 'O cartafol baleirouse correctamente.';
+$messages['deletedsuccessfully'] = 'Eliminouse correctamente.';
 $messages['converting'] = 'Eliminando o formato da mensaxe...';
-$messages['messageopenerror'] = 'Non foi posible cargar a mensaxe desde o servidor';
-$messages['fileuploaderror'] = 'Fallou a carga do ficheiro';
-$messages['filesizeerror'] = 'O ficheiro cargado é máis grande que o tamaño máximo de $size';
-$messages['copysuccess'] = 'Copiáronse correctamente $nr enderezos';
-$messages['copyerror'] = 'Non foi posible copiar ningún enderezo';
-$messages['sourceisreadonly'] = 'A orixe é de só lectura';
-$messages['errorsavingcontact'] = 'Non foi posible gardar o contacto';
+$messages['messageopenerror'] = 'Non foi posible cargar a mensaxe desde o servidor.';
+$messages['fileuploaderror'] = 'Fallou a carga do ficheiro.';
+$messages['filesizeerror'] = 'O ficheiro cargado é máis grande que o tamaño máximo de $size.';
+$messages['copysuccess'] = 'Copiáronse correctamente $nr enderezos.';
+$messages['copyerror'] = 'Non foi posible copiar ningún enderezo.';
+$messages['sourceisreadonly'] = 'A orixe é de só lectura.';
+$messages['errorsavingcontact'] = 'Non foi posible gardar o contacto.';
 $messages['movingmessage'] = 'Movendo a(s) mensaxe(s)...';
 $messages['copyingmessage'] = 'Copiando a(s) mensaxe(s)...';
+$messages['copyingcontact'] = 'Copiando o(s) contacto(s)...';
 $messages['deletingmessage'] = 'Eliminando a(s) mensaxe(s)...';
 $messages['markingmessage'] = 'Marcando a(s) mensaxe(s)...';
-$messages['receiptsent'] = 'A notificación da entrega enviouse correctamente';
-$messages['errorsendingreceipt'] = 'Non foi posible enviar a notificación da entrega';
-$messages['nodeletelastidentity'] = 'Non pode eliminar esta identidade, é a última';
-$messages['forbiddencharacter'] = 'O nome do cartafol contén un carácter non válido';
-$messages['selectimportfile'] = 'Por favor, escolla un ficheiro para cargar';
-$messages['addresswriterror'] = 'Non se pode escribir no caderno de enderezos que escolleu';
-$messages['contactaddedtogroup'] = 'Engadíronse correctamente os contactos a este grupo';
-$messages['contactremovedfromgroup'] = 'Suprimíronse correctamente os contactos de este grupo';
-$messages['importwait'] = 'Estou a importar. Por favor, agarde...';
-$messages['importerror'] = 'Fallou a importación! O ficheiro que cargou non é un vCard correcto.';
+$messages['addingmember'] = 'Engadindo o(s) contacto(s) ao grupo...';
+$messages['removingmember'] = 'Eliminando o(s) contacto(s) do grupo...';
+$messages['receiptsent'] = 'A notificación da entrega enviouse correctamente.';
+$messages['errorsendingreceipt'] = 'Non foi posible enviar a notificación da entrega.';
+$messages['nodeletelastidentity'] = 'Non pode eliminar esta identidade, é a última.';
+$messages['forbiddencharacter'] = 'O nome do cartafol contén un carácter non válido.';
+$messages['selectimportfile'] = 'Por favor, escolla un ficheiro para cargar.';
+$messages['addresswriterror'] = 'Non se pode escribir no caderno de enderezos que escolleu.';
+$messages['contactaddedtogroup'] = 'Engadiuse correctamente o contacto a este grupo.';
+$messages['contactremovedfromgroup'] = 'Suprimiuse correctamente o contacto deste grupo.';
+$messages['importwait'] = 'A importar. Por favor, agarde...';
+$messages['importerror'] = 'Produciuse un erro na importación. O ficheiro que cargou non é un vCard correcto.';
 $messages['importconfirm'] = '<b>Importáronse correctamente $inserted contactos. Ignoráronse $skipped contactos que xa existían</b>:<p><em>$names</em></p>';
-$messages['opnotpermitted'] = 'Operación non permitida';
-$messages['nofromaddress'] = 'Falta o enderezo de correo electrónico na identidade que escolleu';
+$messages['importconfirmskipped'] = '<b>Ignoráronse $skipped existing entradas</b>';
+$messages['opnotpermitted'] = 'Operación non permitida!';
+$messages['nofromaddress'] = 'Falta o enderezo de correo electrónico na identidade que escolleu.';
 $messages['editorwarning'] = 'Se troca neste intre ao editor de texto plano, vai perder todo o formato do texto. Quere continuar?';
-$messages['httpreceivedencrypterror'] = 'Produciuse un erro fatal de configuración. Contacte ao administrador inmediatamente. <b>Non foi posible enviar a súa mensaxe.</b>';
-$messages['smtpconnerror'] = 'Erro SMTP ($code): Fallou a conexión co servidor';
-$messages['smtpautherror'] = 'Erro SMTP ($code): Fallou a autenticación';
-$messages['smtpfromerror'] = 'Erro SMTP ($code): Non foi posible establecer o remitente "$from" ($msg)';
-$messages['smtptoerror'] = 'Erro SMTP ($code): Non foi posible engadir o destinatario "$to" ($msg)';
-$messages['smtprecipientserror'] = 'Erro SMTP: Non se pode analizar a lista de destinatarios';
-$messages['smtpdsnerror'] = 'Erro SMTP: Non hai soporte para notificacións do estado de envío (DSN)';
+$messages['httpreceivedencrypterror'] = 'Produciuse un erro fatal de configuración. Contacte ao administrador inmediatamente. <b>Non se enviou a súa mensaxe.</b>';
+$messages['smtpconnerror'] = 'Erro SMTP ($code): Fallou a conexión co servidor.';
+$messages['smtpautherror'] = 'Erro SMTP ($code): Fallou a autenticación.';
+$messages['smtpfromerror'] = 'Erro SMTP ($code): Non foi posible establecer o remitente "$from" ($msg).';
+$messages['smtptoerror'] = 'Erro SMTP ($code): Non foi posible engadir o destinatario "$to" ($msg).';
+$messages['smtprecipientserror'] = 'Erro SMTP: Non se pode analizar a lista de destinatarios.';
+$messages['smtpdsnerror'] = 'Erro SMTP: Non hai soporte para notificacións do estado de envío (DSN).';
 $messages['smtperror'] = 'Erro SMTP: $msg';
-$messages['emailformaterror'] = 'O enderezo de correo electrónico é incorrecto: $email';
+$messages['emailformaterror'] = 'O enderezo de correo electrónico é incorrecto: $email.';
 $messages['toomanyrecipients'] = 'Especificou destinatarios de máis. Por favor, redúzaos a un máximo de $max.';
 $messages['maxgroupmembersreached'] = 'O número de membros do grupo excede o máximo de $max.';
 $messages['internalerror'] = 'Ocurriu un erro interno. Por favor, probe outra vez.';
-$messages['contactdelerror'] = 'Non foi posible borrar o(s) contacto(s)';
-$messages['contactdeleted'] = 'Borráronse correctamente o(s) contacto(s)';
-$messages['groupdeleted'] = 'Borrouse correctamente o grupo';
-$messages['grouprenamed'] = 'Mudouse correctamente o nome do grupo';
-$messages['groupcreated'] = 'Creouse correctamente o grupo';
-$messages['messagedeleted'] = 'Borráronse correctamente a(s) mensaxe(s)';
-$messages['messagemoved'] = 'Movéronse correctamente a(s) mensaxe(s)';
-$messages['messagecopied'] = 'Copiáronse correctamente a(s) mensaxe(s)';
-$messages['messagemarked'] = 'Marcáronse correctamente a(s) mensaxe(s)';
-$messages['autocompletechars'] = 'Introduza polo menos $min caracteres para autocompletar';
-$messages['namecannotbeempty'] = 'O nome non pode estar baleiro';
-$messages['nametoolong'] = 'O nome é longo de máis';
-$messages['folderupdated'] = 'O cartafol actualizouse correctamente';
-$messages['foldercreated'] = 'O cartafol creouse correctamente';
+$messages['contactdelerror'] = 'Non foi posible eliminar o(s) contacto(s).';
+$messages['contactdeleted'] = 'Borráronse correctamente o(s) contacto(s).';
+$messages['contactrestoreerror'] = 'Non foi posible restaurar o(s) contacto(s) borrado(s).';
+$messages['contactrestored'] = 'Restauráronse correctamente o(s) contacto(s).';
+$messages['groupdeleted'] = 'Borrouse correctamente o grupo.';
+$messages['grouprenamed'] = 'Mudouse correctamente o nome do grupo.';
+$messages['groupcreated'] = 'Creouse correctamente o grupo.';
+$messages['savedsearchdeleted'] = 'Borrouse correctamente a procura gardada. ';
+$messages['savedsearchdeleteerror'] = 'Non foi posible borrar a procura gardada.';
+$messages['savedsearchcreated'] = 'Creouse correctamente a procura gardada.';
+$messages['savedsearchcreateerror'] = 'Non foi posible crear a procura gardada.';
+$messages['messagedeleted'] = 'Borráronse correctamente a(s) mensaxe(s).';
+$messages['messagemoved'] = 'Movéronse correctamente a(s) mensaxe(s).';
+$messages['messagecopied'] = 'Copiáronse correctamente a(s) mensaxe(s).';
+$messages['messagemarked'] = 'Marcáronse correctamente a(s) mensaxe(s).';
+$messages['autocompletechars'] = 'Introduza polo menos $min caracteres para autocompletar.';
+$messages['autocompletemore'] = 'Atopáronse máis entradas concidintes. Por favor, introduza máis caracteres.';
+$messages['namecannotbeempty'] = 'O nome non pode estar baleiro.';
+$messages['nametoolong'] = 'O nome é longo de máis.';
+$messages['folderupdated'] = 'O cartafol actualizouse correctamente.';
+$messages['foldercreated'] = 'O cartafol creouse correctamente.';
+$messages['invalidimageformat'] = 'O formato da imaxe non é válido.';
+$messages['mispellingsfound'] = 'Atopáronse erros ortográficos na mensaxe.';
+$messages['parentnotwritable'] = 'Non foi posible crear/mover o cartafol no cartafol padre escollido porque non ten permisos.';
 
 ?>
index 105897998cebf0432fca70bae136dcd22c2f63dc..1dd15f45e5411a09a9acfb76a80e84ce097f4397 100644 (file)
@@ -11,7 +11,7 @@
 +-----------------------------------------------------------------------+
 | Author: Moshe Leibovitch  <moish@mln.co.il>                           |
 | Updates: Noor Dawod <noor@comrax.com>                                 |
-| Updates: Moshe Leibovitch  <moish@mln.co.il>  05092011                |
+| Updates: Moshe Leibovitch  <moish@mln.co.il>  28112011                |
 +-----------------------------------------------------------------------+
 */
 
@@ -115,7 +115,7 @@ $labels['markread'] = 'כנקראו';
 $labels['markunread'] = 'כלא נקראו';
 $labels['markflagged'] = 'כמסומן';
 $labels['markunflagged'] = 'כלא מסומן';
-$labels['messageactions'] = 'פעולות נוספות';
+$labels['moreactions'] = 'פעולות נוספות...';
 $labels['select'] = 'בחירה';
 $labels['all'] = 'הכל';
 $labels['none'] = 'כלום';
@@ -187,6 +187,7 @@ $labels['highest'] = 'גבוה ביותר';
 $labels['nosubject'] = '(ללא נושא)';
 $labels['showimages'] = 'הצגת תמונות';
 $labels['alwaysshow'] = '$sender תמיד להציג תמונות מאת ';
+$labels['isdraft'] = 'זו טיוטה של הודעה';
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'ללא עיצוב';
 $labels['savesentmessagein'] = 'היכן לשמור ההודעה?';
@@ -268,7 +269,6 @@ $labels['print'] = 'הדפסה';
 $labels['export'] = 'ייצוא';
 $labels['exportvcards'] = 'ייצוא אנשי קשר בפורמט  vCard ';
 $labels['newcontactgroup'] = 'יצירת קבוצה חדשה של אנשי קשר';
-$labels['groupactions'] = 'פעולות על קבוצת אנשי הקשר...';
 $labels['grouprename'] = 'שינוי שם קבוצה';
 $labels['groupdelete'] = 'מחיקת קבוצה';
 $labels['previouspage'] = 'הצגת הקבוצה הקודמת';
@@ -278,6 +278,8 @@ $labels['lastpage'] = 'הצגת הקבוצה האחרונה';
 $labels['group'] = 'קבוצה';
 $labels['groups'] = 'קבוצות';
 $labels['personaladrbook'] = 'כתובות פרטיות';
+$labels['searchsave'] = 'שמירת החיפוש';
+$labels['searchdelete'] = 'מחיקת החיפוש';
 $labels['import'] = 'ייבוא';
 $labels['importcontacts'] = 'ייבוא אנשי קשר';
 $labels['importfromfile'] = 'ייבוא מקובץ';
@@ -286,6 +288,7 @@ $labels['importreplace'] = 'החלפת כל פנקס הכתובות';
 $labels['importtext'] = '<a href="http://en.wikipedia.org/wiki/VCard">vCard</a>  ניתן לייבא אנשי קשר מפנקס כתובות קיים.  אנו תומכים בפורמט';
 $labels['done'] = 'בוצע';
 $labels['settingsfor'] = 'הגדרות עבור';
+$labels['about'] = 'אודות';
 $labels['preferences'] = 'העדפות';
 $labels['userpreferences'] = 'העדפות משתמש';
 $labels['editpreferences'] = 'עריכה של העדפות משתמש';
@@ -297,6 +300,8 @@ $labels['edititem'] = 'עריכת רשומה';
 $labels['preferhtml'] = 'הצגת HTML';
 $labels['defaultcharset'] = 'ברירת מחדל של תווים';
 $labels['htmlmessage'] = 'תוכן HTML';
+$labels['dateformat'] = 'פורמט התאריך';
+$labels['timeformat'] = 'פורמט השעה';
 $labels['prettydate'] = 'תאריכים מעוצבים';
 $labels['setdefault'] = 'קביעה כברירת מחדל';
 $labels['autodetect'] = 'אוטומטי';
@@ -368,7 +373,13 @@ $labels['reqmdn'] = 'תמיד יש לדרוש אישור קבלה';
 $labels['reqdsn'] = 'תמיד לבקש הודעה על מצב המסירה';
 $labels['replysamefolder'] = 'המענה ישמר בתיקיה של ההודעה המקורית';
 $labels['defaultaddressbook'] = 'הוספת אנשי קשר לספר הכתובות שנבחר';
+$labels['autocompletesingle'] = 'לדלג על כתובת דואר חילופית בעת שימוש בהשלמה אוטומטית של הקלדה';
 $labels['spellcheckbeforesend'] = 'בדיקת איות לפני המשלוח';
+$labels['spellcheckoptions'] = 'אפשריות בדיקת איות';
+$labels['spellcheckignoresyms'] = 'התעלמות ממילים המכילות סמלים';
+$labels['spellcheckignorenums'] = 'התעלמות ממילים המכילות מספרים';
+$labels['spellcheckignorecaps'] = 'התעלמות ממילים עם אותציות ראשיות';
+$labels['addtodict'] = 'הוספה למילון';
 $labels['folder'] = 'תיק';
 $labels['folders'] = 'תיקים';
 $labels['foldername'] = 'שם תיק';
@@ -393,6 +404,11 @@ $labels['sortby'] = 'מיון לפי';
 $labels['sortasc'] = 'מיון בסדר עולה';
 $labels['sortdesc'] = 'מיון בסדר יורד';
 $labels['undo'] = 'ביטול שינוי';
+$labels['plugin'] = 'תוסף תוכנה';
+$labels['version'] = 'גירסה';
+$labels['source'] = 'מקור';
+$labels['license'] = 'רשיון';
+$labels['support'] = 'קבלת תמיכה';
 $labels['B'] = 'בייט';
 $labels['KB'] = 'ק"ב';
 $labels['MB'] = 'מ"ב';
index affa0d996e566db9bf54e5f1c0540e31fb38effa..51de7c79d7feb7f6ac1fb690539234de0e00599b 100644 (file)
@@ -11,7 +11,7 @@
 +-----------------------------------------------------------------------+
 | Author: Moshe Leibovitch    <moish@mln.co.il>                         |
 | Updates: Noor Dawod <noor@comrax.com>                                 |
-| Updates: Moshe Leibovitch  <moish@mln.co.il>  08082011                |
+| Updates: Moshe Leibovitch  <moish@mln.co.il>  28112011                |
 +-----------------------------------------------------------------------+
 
 */
@@ -47,6 +47,7 @@ $messages['blockedimages'] = 'תמונות משרת אחר נחסמו כדי ל
 $messages['encryptedmessage'] = 'זו הודעת מוצפנת ולא ניתן להציגה';
 $messages['nocontactsfound'] = 'לא נמצאו אנשי קשר';
 $messages['contactnotfound'] = 'איש הקשר המבוקש לא נמצא';
+$messages['contactsearchonly'] = 'יש להקיש מפתחות חיפוש כדי למצוא אנשי קשר';
 $messages['sendingfailed'] = 'שליחת ההודעה נכשלה';
 $messages['senttooquickly'] = 'נא להמתין $sec שניות לפני מסירת הודעה זו';
 $messages['errorsavingsent'] = 'נגרמה שגיאה במהלך שמירת ההודעה בתיק הודעות יוצאות';
@@ -60,6 +61,7 @@ $messages['deletegroupconfirm'] = 'האם אכן ברצונך למחוק את ה
 $messages['deletemessagesconfirm'] = 'האם למחוק את ההודעות המסומנות?';
 $messages['deletefolderconfirm'] = 'האם למחוק תיק זה?';
 $messages['purgefolderconfirm'] = 'האם למחוק את כל ההודעות בתיק זה?';
+$messages['contactdeleting'] = 'אנשי קשר נמחקים...';
 $messages['groupdeleting'] = 'מחיקת קבוצה...';
 $messages['folderdeleting'] = 'התיקיה נמחקת...';
 $messages['foldermoving'] = 'העברת תיקיה...';
@@ -75,10 +77,10 @@ $messages['nosubjectwarning'] = 'שורת הנושא ריקה. האם ברצונ
 $messages['nobodywarning'] = 'האם לשלוח הודעה ללא תוכן?';
 $messages['notsentwarning'] = 'ההודעה לא נשלחה. האם לבטל?';
 $messages['noldapserver'] = 'נא לבחור שרת כתובות לחיפוש';
-$messages['nocontactsreturned'] = 'לא נמצאו אנשי קשר';
 $messages['nosearchname'] = 'נא להוסיף איש קשר או כתובת דוא\"ל';
 $messages['notuploadedwarning'] = 'עדיין לא הועלו כל הקבצים. נא לחכות או לבטל הפעולה.';
 $messages['searchsuccessful'] = 'נמצאו $nr הודעות';
+$messages['contactsearchsuccessful'] = 'נמצאו $nr אנשי קשר';
 $messages['searchnomatch'] = 'תוצאת החיפוש ריקה';
 $messages['searching'] = 'חיפוש...';
 $messages['checking'] = 'בדיקה...';
@@ -125,7 +127,6 @@ $messages['smtpautherror'] = 'SMTP ($code): ההזדהות נכשלה';
 $messages['smtpfromerror'] = ' ($msg) SMTP ($code): "$from" לא נרשמה כתובת השולח';
 $messages['smtptoerror'] = ' ($msg) SMTP ($code): "$to" לא נרשמה כתובת המקבל';
 $messages['smtprecipientserror'] = 'SMTP : לא ניתן לפענח רשימת נמענים';
-$messages['smtpdsnerror'] = 'שגיאת SMTP: אין תמיכה בהודעה על מצב מסירה';
 $messages['smtperror'] = 'SMTP: $msg';
 $messages['emailformaterror'] = '$email  כתובת דוא"ל שגויה';
 $messages['toomanyrecipients'] = 'יותר מדי נמענים. יש להקטין מספרם ל - $max';
@@ -138,11 +139,16 @@ $messages['contactrestored'] = 'איש הקשר שוחזר בהצלחה';
 $messages['groupdeleted'] = 'הקבוצה נמחקה בהצלחה';
 $messages['grouprenamed'] = 'שם הקבוצה שונה בהצלחה';
 $messages['groupcreated'] = 'הקבוצה נוצרה בהצלחה';
+$messages['savedsearchdeleted'] = 'נמחק החיפוש שנשמר';
+$messages['savedsearchdeleteerror'] = 'לא ניתן למחוק החיפוש שנשמר';
+$messages['savedsearchcreated'] = 'החיפוש נשמר בהצלחה';
+$messages['savedsearchcreateerror'] = 'לא ניתן לשמור את החיפוש';
 $messages['messagedeleted'] = 'ההודעה נמחקה בהצלחה';
 $messages['messagemoved'] = 'ההודעה הועברה בהצלחה';
 $messages['messagecopied'] = 'ההודעה הועתקה בהצלחה';
 $messages['messagemarked'] = 'ההודעה סומנה בהצלחה';
 $messages['autocompletechars'] = 'יש להקיש לפחות $min תווים להשלמה אוטומטית של המחרוזת';
+$messages['autocompletemore'] = 'נמצאו רשומות נוספות. נא להקיש תווים נוספים.';
 $messages['namecannotbeempty'] = 'השם אינו יכול להיות ריק';
 $messages['nametoolong'] = 'השם ארוך מדי';
 $messages['folderupdated'] = 'התיק עודכן בהצלחה';
@@ -150,5 +156,6 @@ $messages['foldercreated'] = 'התיק נוצר בהצלחה';
 $messages['invalidimageformat'] = 'פורמט תמונה לא חוקי';
 $messages['mispellingsfound'] = 'התגלו שגיאות כתיב בהודעה';
 $messages['parentnotwritable'] = 'לא ניתן ליצור/להעביר תיקיה לתוך תיקית האב שנבחרה. אין הרשאה לגישה.';
+$messages['messagetoobig'] = 'ההודעה גדולה מעבר ליכולת העיבוד של התוכנה';
 
 ?>
index ad0974e0f5387b248bcc9dbb4e0fdafedd96e492..725a7eb895a718fc3854e56ea02f9c28a91929e6 100644 (file)
@@ -14,7 +14,7 @@
 |       Tanja Dobričić <tanja.dobricic@everymail.info>                  |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: messages.inc 5139 2011-08-28 09:47:15Z alec $
 
 */
 
@@ -25,7 +25,7 @@ $messages['sessionerror'] = 'Morate se ponovno ulogirati';
 $messages['imaperror'] = 'Neuspješna veza na IMAP server';
 $messages['servererror'] = 'Greška poslužitelja';
 $messages['servererrormsg'] = 'Greška na serveru: $msg';
-$messages['databaserror'] = 'Greška baze podataka!';
+$messages['dberror'] = 'Greška baze podataka!';
 $messages['errorreadonly'] = 'Nemoguća izvedba operacije. Mapa je samo za čitanje.';
 $messages['errornoperm'] = 'Nemoguća izvedba operacije. Dozvola odbijena.';
 $messages['invalidrequest'] = 'Nepravilan zahtijev! Podaci nisu spremljeni.';
index 467371734b2cc62500077125530d595aa8b1ff36..3982a64f9d076cb46c72bcfd8ac36d3cce48e241 100644 (file)
 | Author: Zark Bonfire <ikkhares at gmail dot com>                      |
 |         Peter Szabo <pete at teamlupus dot hu>                        |
 |         Barnabas Bona <bsi at hosember dot hu>                        |
+|         Gabor Veliczky <hosting at jugiter dot net>                   |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -27,7 +28,7 @@ $labels['server'] = 'Szerver';
 $labels['login'] = 'Belépés';
 $labels['logout'] = 'Kijelentkezés';
 $labels['mail'] = 'E-mail';
-$labels['settings'] = 'Egyéni beállítások';
+$labels['settings'] = 'Beállítások';
 $labels['addressbook'] = 'Címjegyzék';
 $labels['inbox'] = 'Érkezett levelek';
 $labels['drafts'] = 'Piszkozatok';
@@ -119,7 +120,7 @@ $labels['markread'] = 'Olvasottként';
 $labels['markunread'] = 'Olvasatlanként';
 $labels['markflagged'] = 'Jelöltként';
 $labels['markunflagged'] = 'Jelöletlenként';
-$labels['messageactions'] = 'További lehetőségek...';
+$labels['moreactions'] = 'További műveletek...';
 $labels['select'] = 'Kijelölés';
 $labels['all'] = 'Összes';
 $labels['none'] = 'Nincs';
@@ -172,12 +173,15 @@ $labels['charset'] = 'Karakterkészlet';
 $labels['editortype'] = 'Szerkesztő típusa';
 $labels['returnreceipt'] = 'Olvasási visszaigazolás';
 $labels['dsn'] = 'Kézbesítési visszaigazolás';
+$labels['mailreplyintro'] = '$date időpontban $sender ezt írta:';
+$labels['originalmessage'] = 'Eredeti üzenet';
 $labels['editidents'] = 'Azonosítók szerkesztése';
 $labels['checkspelling'] = 'Helyesírás-ellenőrzés';
 $labels['resumeediting'] = 'Helyesírás-ellenőrzés vége';
 $labels['revertto'] = 'Visszaállítás erre';
 $labels['attachments'] = 'Csatolmányok';
 $labels['upload'] = 'Feltöltés';
+$labels['uploadprogress'] = '$percent ($current / $total)';
 $labels['close'] = 'Bezárás';
 $labels['messageoptions'] = 'Üzenetbeállítások...';
 $labels['low'] = 'Alacsony';
@@ -187,7 +191,8 @@ $labels['high'] = 'Magas';
 $labels['highest'] = 'Legmagasabb';
 $labels['nosubject'] = '(nincs tárgy)';
 $labels['showimages'] = 'Képek megjelenítése';
-$labels['alwaysshow'] = 'Képek megjelenítése mindig ha a feladó $sender';
+$labels['alwaysshow'] = 'Képek megjelenítése mindig, ha a feladó $sender';
+$labels['isdraft'] = 'TRANSLATE!';
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'Egyszerű szöveg';
 $labels['savesentmessagein'] = 'Elküldött üzenet mentése ide';
@@ -238,14 +243,16 @@ $labels['typehome'] = 'Otthon';
 $labels['typework'] = 'Munkahely';
 $labels['typeother'] = 'Egyéb';
 $labels['typemobile'] = 'Mobil';
-$labels['typemain'] = 'Fővonal';
+$labels['typemain'] = 'Vezetékes';
 $labels['typehomefax'] = 'Otthoni fax';
 $labels['typeworkfax'] = 'Munkahelyi fax';
 $labels['typecar'] = 'Gépkocsi';
 $labels['typepager'] = 'Személyhívó';
 $labels['typevideo'] = 'Videó';
 $labels['typeassistant'] = 'Asszisztens';
-$labels['typehomepage']  = 'Weboldal';
+$labels['typehomepage'] = 'Weboldal';
+$labels['typeblog'] = 'Blog';
+$labels['typeprofile'] = 'Profil';
 $labels['addfield'] = 'Új mező hozzáadása...';
 $labels['addcontact'] = 'Új kapcsolat hozzáadása';
 $labels['editcontact'] = 'Kapcsolat szerkesztése';
@@ -267,16 +274,17 @@ $labels['print'] = 'Nyomtatás';
 $labels['export'] = 'Exportálás';
 $labels['exportvcards'] = 'Kapcsolatok exportálása vCard formátumban';
 $labels['newcontactgroup'] = 'Új csoport';
-$labels['groupactions'] = 'Műveletek csoportokkal...';
-$labels['grouprename']    = 'Csoport átnevezése';
-$labels['groupdelete']    = 'Csoport törlése';
-$labels['previouspage'] = 'Előző lap';
-$labels['firstpage'] = 'Első lap';
-$labels['nextpage'] = 'Következő lap';
-$labels['lastpage'] = 'Utolsó lap';
+$labels['grouprename'] = 'Csoport átnevezése';
+$labels['groupdelete'] = 'Csoport törlése';
+$labels['previouspage'] = 'Előző oldal';
+$labels['firstpage'] = 'Első oldal';
+$labels['nextpage'] = 'Következő oldal';
+$labels['lastpage'] = 'Utolsó oldal';
 $labels['group'] = 'Csoport';
 $labels['groups'] = 'Csoportok';
 $labels['personaladrbook'] = 'Személyes címjegyzék';
+$labels['searchsave'] = 'Keresés mentése';
+$labels['searchdelete'] = 'Keresés törlése';
 $labels['import'] = 'Importálás';
 $labels['importcontacts'] = 'Kapcsolatok importálása';
 $labels['importfromfile'] = 'Importálás fájlból:';
@@ -296,6 +304,8 @@ $labels['edititem'] = 'Elem szerkesztése';
 $labels['preferhtml'] = 'HTML megjelenítés';
 $labels['defaultcharset'] = 'Alapértelmezett karakterkódolás';
 $labels['htmlmessage'] = 'HTML üzenet';
+$labels['dateformat'] = 'Dátumformátum';
+$labels['timeformat'] = 'Időformátum';
 $labels['prettydate'] = 'Rövid dátumok';
 $labels['setdefault'] = 'Beállítás alapértelmezettnek';
 $labels['autodetect'] = 'Automatikus';
@@ -321,7 +331,7 @@ $labels['autosendknown'] = 'visszaigazolás küldése csak a kapcsolataimnak, eg
 $labels['autosendknownignore'] = 'visszaigazolás küldése csak a kapcsolataimnak, egyéb esetben hagyja figyelmen kívül';
 $labels['ignore'] = 'hagyja figyelmen kívül';
 $labels['readwhendeleted'] = 'Üzenet olvasottként jelölése törléskor';
-$labels['flagfordeletion'] = 'Üzenet törlendőnek jelölése törlés helyett ';
+$labels['flagfordeletion'] = 'Üzenet törlendőnek jelölése törlés helyett';
 $labels['skipdeleted'] = 'Törölt üzenetek ne legyenek megjelenítve';
 $labels['deletealways'] = 'Üzenet törlése, ha nem sikerül áthelyezni a törölt elemek mappába';
 $labels['showremoteimages'] = 'Távoli beágyazott képek mutatása';
@@ -367,7 +377,13 @@ $labels['reqmdn'] = 'Olvasási visszaigazolás megkövetelése';
 $labels['reqdsn'] = 'Kézbesítési visszaigazolás megkövetelése';
 $labels['replysamefolder'] = 'Válaszok azonos mappába helyezése';
 $labels['defaultaddressbook'] = 'Új kapcsolatok hozzáadása a kiválasztott címjegyzékhez';
+$labels['autocompletesingle'] = 'TRANSLATE!';
 $labels['spellcheckbeforesend'] = 'Helyesírás-ellenőrzés az üzenet elküldése előtt';
+$labels['spellcheckoptions'] = 'Helyesírás-ellenőrzés opciók';
+$labels['spellcheckignoresyms'] = 'Speciális karaktereket tartalmazó szavak kihagyása';
+$labels['spellcheckignorenums'] = 'Számot tartalmazó szavak kihagyása';
+$labels['spellcheckignorecaps'] = 'Csak nagybetűt tartalmazó szavak kihagyása';
+$labels['addtodict'] = 'Hozzáadás a szótárhoz';
 $labels['folder'] = 'Mappa';
 $labels['folders'] = 'Mappák';
 $labels['foldername'] = 'Mappa neve';
@@ -386,11 +402,12 @@ $labels['getfoldersize'] = 'Mappa mérete';
 $labels['changesubscription'] = 'Feliratkozás megváltoztatása';
 $labels['foldertype'] = 'Mappa típusa';
 $labels['personalfolder'] = 'Privát mappa';
-$labels['otherfolder'] = 'Más felhasználó mappája';
+$labels['otherfolder'] = 'Más felhasználó(k) mappája';
 $labels['sharedfolder'] = 'Megosztott mappa';
 $labels['sortby'] = 'Rendezés';
 $labels['sortasc'] = 'növekvő';
 $labels['sortdesc'] = 'csökkenő';
+$labels['undo'] = 'Visszavonás';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index 1c070ced0ad6ab56622c474d295f60ca21337302..321486e4a88f894599258a65d4fb01cbb7586dfb 100644 (file)
 | Author: Zark Bonfire <ikkhares at gmail dot com>                      |
 |         Peter Szabo <pete at teamlupus dot hu>                        |
 |         Barnabas Bona <bsi at hosember dot hu>                        |
+|         Gabor Veliczky <hosting at jugiter dot net>                   |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -23,12 +24,12 @@ $messages = array();
 $messages['loginfailed'] = 'Sikertelen belépés';
 $messages['cookiesdisabled'] = 'A böngésző nem támogatja a sütik használatát';
 $messages['sessionerror'] = 'Érvénytelen vagy lejárt munkamenet';
-$messages['imaperror'] = 'Nem sikerült a kapcsolódás az IMAP szerverhez';
+$messages['imaperror'] = 'Nem sikerült csatlakozni az IMAP szerverhez';
 $messages['servererror'] = 'Szerverhiba!';
 $messages['servererrormsg'] = 'Szerverhiba: $msg';
-$messages['databaserror'] = 'Adatbázishiba!';
-$messages['errorreadonly'] = 'A műveletet nem sikerült végrehajtani. A mappa írásvédett';
-$messages['errornoperm'] = 'A műveletet nem sikerült végrehajtani. Hozzáférés megtagadva';
+$messages['dberror'] = 'Adatbázishiba!';
+$messages['errorreadonly'] = 'A műveletet nem sikerült végrehajtani. A mappa írásvédett.';
+$messages['errornoperm'] = 'A műveletet nem sikerült végrehajtani. Hozzáférés megtagadva.';
 $messages['invalidrequest'] = 'Érvénytelen kérés! Az adatok nem lettek elmentve.';
 $messages['nomessagesfound'] = 'A fiók nem tartalmaz leveleket';
 $messages['loggedout'] = 'Sikeres kijelentkezés. Viszontlátásra!';
@@ -44,11 +45,13 @@ $messages['savingmessage'] = 'Az üzenet mentése...';
 $messages['messagesaved'] = 'Az üzenet elmentve a Piszkozatokhoz';
 $messages['successfullysaved'] = 'A mentés sikerült';
 $messages['addedsuccessfully'] = 'A kapcsolat hozzáadása a címjegyzékhez megtörtént';
-$messages['contactexists'] = 'A kapcsolat már létezik ezzel az e-mail címmel';
-$messages['blockedimages'] = 'Biztonsági okokból a távoli képek letöltése tiltott.';
-$messages['encryptedmessage'] = 'Az üzenet titkosított, ezért nem megjeleníthető.';
+$messages['contactexists'] = 'Ezzel az e-mail címmel már létezik kapcsolat';
+$messages['contactnameexists'] = 'Ezzel a névvel már létezik kapcsolat';
+$messages['blockedimages'] = 'Biztonsági okokból a távoli képek letöltése tiltott';
+$messages['encryptedmessage'] = 'Az üzenet titkosított, ezért nem megjeleníthető';
 $messages['nocontactsfound'] = 'Nem találhatóak kapcsolatok';
 $messages['contactnotfound'] = 'A kiválasztott kapcsolat nem található';
+$messages['contactsearchonly'] = 'Adjon meg keresőkifejezéseket a kapcsolatok közti kereséshez';
 $messages['sendingfailed'] = 'Az üzenet elküldése nem sikerült';
 $messages['senttooquickly'] = 'Kérem várjon még $sec másodpercet az üzenet elküldése előtt';
 $messages['errorsavingsent'] = 'Hiba történt az elküldött üzenet mentése közben';
@@ -58,9 +61,12 @@ $messages['errorcopying'] = 'Az üzenet(ek)et nem sikerült másolni';
 $messages['errordeleting'] = 'Az üzenet(ek)et nem sikerült törölni';
 $messages['errormarking'] = 'Az üzenet(ek)et nem sikerült megjelölni';
 $messages['deletecontactconfirm'] = 'Biztos, hogy törölni szeretné a kijelölt kapcsolato(ka)t?';
+$messages['deletegroupconfirm'] = 'Biztos, hogy törölni szeretné a kijelölt csoportot?';
 $messages['deletemessagesconfirm'] = 'Biztos, hogy törölni szeretné a kijelölt üzenete(ke)t?';
 $messages['deletefolderconfirm'] = 'Biztos, hogy törölni szeretné ezt a mappát?';
 $messages['purgefolderconfirm'] = 'Biztos benne, hogy az összes üzenet törölhető?';
+$messages['contactdeleting'] = 'Kapcsolat(ok) törlése...';
+$messages['groupdeleting'] = 'Csoport törlése...';
 $messages['folderdeleting'] = 'Mappa törlése...';
 $messages['foldermoving'] = 'Mappa mozgatása...';
 $messages['foldersubscribing'] = 'Feliratkozás a mappára...';
@@ -75,17 +81,17 @@ $messages['nosubjectwarning'] = 'A Tárgy mező üres. Szeretné most kitölteni
 $messages['nobodywarning'] = 'Elküldi az üzenetet üresen?';
 $messages['notsentwarning'] = 'Az üzenet még nem lett elküldve. Eldobja az üzenetet?';
 $messages['noldapserver'] = 'Adjon meg egy LDAP szervert a kereséshez';
-$messages['nocontactsreturned'] = 'Nem található kapcsolat';
 $messages['nosearchname'] = 'Adja meg a kapcsolat nevét vagy e-mail címét';
 $messages['notuploadedwarning'] = 'Még nem került feltöltésre minden csatolmány. Kérem várjon vagy állítsa le a feltöltést!';
 $messages['searchsuccessful'] = '$nr üzenet található';
+$messages['contactsearchsuccessful'] = '$nr találat';
 $messages['searchnomatch'] = 'Nincs találat';
 $messages['searching'] = 'Keresés...';
 $messages['checking'] = 'Ellenőrzés...';
 $messages['nospellerrors'] = 'Nem található helyesírási hiba';
 $messages['folderdeleted'] = 'A mappa sikeresen törölve';
-$messages['foldersubscribed'] = 'A feliratkozás a mappára sikeres';
-$messages['folderunsubscribed'] = 'A leiratkozás a mappáról sikeres';
+$messages['foldersubscribed'] = 'Sikerült a mappára feliratkozni';
+$messages['folderunsubscribed'] = 'Sikerült a mappáról leiratkozni';
 $messages['folderpurged'] = 'Mappa sikeresen törölve';
 $messages['folderexpunged'] = 'Mappa sikeresen kiürítve';
 $messages['deletedsuccessfully'] = 'Törölve';
@@ -99,11 +105,14 @@ $messages['sourceisreadonly'] = 'Ez a címforrás csak olvasható';
 $messages['errorsavingcontact'] = 'A kapcsolat címe nem menthető';
 $messages['movingmessage'] = 'Üzenet(ek) mozgatása...';
 $messages['copyingmessage'] = 'Üzenet(ek) másolása...';
+$messages['copyingcontact'] = 'Kapcsolat(ok) másolása...';
 $messages['deletingmessage'] = 'Üzenet(ek) törlése....';
 $messages['markingmessage'] = 'Üzenet(ek) megjelölése...';
+$messages['addingmember'] = 'Kapcsolat(ok) hozzáadása a csoporthoz...';
+$messages['removingmember'] = 'Kapcsolat(ok) törlése a csoportból...';
 $messages['receiptsent'] = 'Az olvasási visszaigazolás el lett küldve';
 $messages['errorsendingreceipt'] = 'Az olvasási visszaigazolást nem sikerült elküldeni';
-$messages['nodeletelastidentity'] = 'Nem törölheti ezt az azonosítót, ez az egyetlen';
+$messages['nodeletelastidentity'] = 'Az azonosító nem törölhető, lennie kell legalább egy azonosítónak!';
 $messages['forbiddencharacter'] = 'A mappa neve tiltott karaktert tartalmaz';
 $messages['selectimportfile'] = 'Kérjük válassza ki a feltölteni kívánt fájlt';
 $messages['addresswriterror'] = 'A kiválasztott címjegyzék nem írható';
@@ -114,7 +123,7 @@ $messages['importerror'] = 'Az importálás sikertelen! A feltöltött állomán
 $messages['importconfirm'] = '<b>Sikeresen importálásra került $inserted kapcsolat, kihagyva $skipped már létező bejegyzés</b>:<p><em>$names</em></p>';
 $messages['importconfirmskipped'] = '<b>Kihagyva $skipped már létező bejegyzés</b>';
 $messages['opnotpermitted'] = 'A művelet nem megengedett!';
-$messages['nofromaddress'] = 'Hiányzó e-mail cím a kiválasztott feladónál';
+$messages['nofromaddress'] = 'A kiválasztott feladónál hiányzik az e-mail cím!';
 $messages['editorwarning'] = 'Az egyszerű szöveges formátumra való váltás az összes formázás elvesztésével jár. Biztosan folytatja?';
 $messages['httpreceivedencrypterror'] = 'Végzetes konfigurációs hiba történt, azonnal lépjen kapcsolatba az üzemeltetővel. <b>Az üzenet nem küldhető el.</b>';
 $messages['smtpconnerror'] = 'SMTP hiba ($code): Sikertelen kapcsolódás a szerverhez';
@@ -122,27 +131,34 @@ $messages['smtpautherror'] = 'SMTP hiba ($code): Sikertelen bejelentkezés';
 $messages['smtpfromerror'] = 'SMTP hiba ($code): Nem sikerült a feladó beállítása: "$from" ($msg)';
 $messages['smtptoerror'] = 'SMTP hiba ($code): Nem sikerült a következő címzett hozzáadása: "$to" ($msg)';
 $messages['smtprecipientserror'] = 'SMTP hiba ($code): A címzettlista feldolgozása sikertelen';
-$messages['smtpdsnerror'] = 'SMTP hiba: A kézbesítési visszaigazolásokat a szerver nem támogatja';
 $messages['smtperror'] = 'SMTP hiba ($code): $msg';
 $messages['emailformaterror'] = 'Helytelen formátumú e-mail cím: $email';
-$messages['toomanyrecipients'] = 'Túl sok a címzett. Csökkentse a címzettek számát $max címre.';
-$messages['maxgroupmembersreached'] = 'A csoport létszáma meghaladja a maximum $max értéket';
+$messages['toomanyrecipients'] = 'Túl sok a címzett. Csökkentse a címzettek számát maximum $max címre!';
+$messages['maxgroupmembersreached'] = 'A csoport létszáma meghaladja a maximum $max t';
 $messages['internalerror'] = 'Belső hiba történt, kérjük próbálja újra!';
 $messages['contactdelerror'] = 'Hiba a kapcsolat(ok) törlésekor';
 $messages['contactdeleted'] = 'Kapcsolat(ok) sikeresen törölve';
+$messages['contactrestoreerror'] = 'Nem sikerült a törölt kapcsolat(ok) helyreállítása';
+$messages['contactrestored'] = 'Kapcsolat(ok) sikeresen helyreállítva';
 $messages['groupdeleted'] = 'Csoport sikeresen törölve';
 $messages['grouprenamed'] = 'Csoport sikeresen átnevezve';
 $messages['groupcreated'] = 'Csoport sikeresen létrehozva';
+$messages['savedsearchdeleted'] = 'Mentett keresés sikeresen törölve';
+$messages['savedsearchdeleteerror'] = 'Nem sikerült törölni a mentett keresést';
+$messages['savedsearchcreated'] = 'Keresés sikeresen mentve';
+$messages['savedsearchcreateerror'] = 'Nem sikerült létrehozni mentett keresést';
 $messages['messagedeleted'] = 'Üzenet(ek) sikeresen törölve';
 $messages['messagemoved'] = 'Üzenet(ek) sikeresen átmozgatva';
 $messages['messagecopied'] = 'Üzenet(ek) sikeresen másolva';
 $messages['messagemarked'] = 'Üzenet(ek) sikeresen megjelölve';
 $messages['autocompletechars'] = 'Az automatikus kiegészítéshez legalább $min karakter szükséges';
+$messages['autocompletemore'] = 'Több egyezés található. Kérem adjon meg további karaktereket!';
 $messages['namecannotbeempty'] = 'A név nem lehet üres';
 $messages['nametoolong'] = 'A név túl hosszú';
 $messages['folderupdated'] = 'Mappa sikeresen frissítve';
 $messages['foldercreated'] = 'Mappa sikeresen létrehozva';
 $messages['invalidimageformat'] = 'Érvénytelen képformátum';
 $messages['mispellingsfound'] = 'Az üzenetben helyesírási hibák találhatók';
+$messages['parentnotwritable'] = 'Nem sikerült a mappa létrehozása/mozgatása a kijelölt mappába. Nincs jogosultsága a művelethez!';
 
 ?>
index c16a1ca464bbb9e6e687a8ab2c326a9726a0d6b5..bf1f25269433ae3f1ab99bef7e478c264a619639 100644 (file)
@@ -13,7 +13,7 @@
 | Author: Putu Arya Sabda Wijaya <ptaryasw@isi-dps.ac.id>                    |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 4748 2011-05-11 13:17:43Z yllar $
+@version $Id: messages.inc 5139 2011-08-28 09:47:15Z alec $
 
 */
 
@@ -24,7 +24,7 @@ $messages['sessionerror'] = 'Session Anda invalid atau kadaluwarsa';
 $messages['imaperror'] = 'Koneksi ke IMAP server gagal';
 $messages['servererror'] = 'Server Error!';
 $messages['servererrormsg'] = 'Server Error: $msg';
-$messages['databaserror'] = 'Basis data Error!';
+$messages['dberror'] = 'Basis data Error!';
 $messages['errorreadonly'] = 'Tidak bisa melakukan operasi. Folder hanya bisa dibaca';
 $messages['errornoperm'] = 'Tidak bisa melakukan operasi. Ijin ditolak';
 $messages['invalidrequest'] = 'Permintaan tidak valid! Tidak ada data yang tersimpan.';
index 74da7e50520af2bdd14d81b01ceb445ae64e9b56..52950a38d743153bab1060b83273d90fb00f8e3e 100644 (file)
@@ -14,7 +14,7 @@
 |         Yusef Maali <contact@yusefmaali.net>                          |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -118,7 +118,7 @@ $labels['markread'] = 'Letti';
 $labels['markunread'] = 'Non letti';
 $labels['markflagged'] = 'Contrassegnato';
 $labels['markunflagged'] = 'Non contrassegnato';
-$labels['messageactions'] = 'Altre azioni...';
+$labels['moreactions'] = 'Altre operazioni...';
 $labels['select'] = 'Seleziona';
 $labels['all'] = 'Tutti';
 $labels['none'] = 'Nessuno';
@@ -150,7 +150,7 @@ $labels['listcolumns'] = 'Elenco Colonne';
 $labels['listsorting'] = 'Ordina per';
 $labels['listorder'] = 'Ordinamento';
 $labels['listmode'] = 'Modalità di visualizzazione';
-$labels['folderactions'] = 'Azione cartella';
+$labels['folderactions'] = 'Operazioni cartella';
 $labels['compact'] = 'Compatta';
 $labels['empty'] = 'Svuota';
 $labels['quota'] = 'Spazio utilizzato';
@@ -172,6 +172,7 @@ $labels['editortype'] = 'Tipo editor';
 $labels['returnreceipt'] = 'Ricevuta di ritorno';
 $labels['dsn'] = 'Notifica di consegna';
 $labels['mailreplyintro'] = 'Il $date $sender ha scritto:';
+$labels['originalmessage'] = 'Messaggio originale';
 $labels['editidents'] = 'Modifica indentità';
 $labels['checkspelling'] = 'Controlla ortografia';
 $labels['resumeediting'] = 'Torna al messaggio';
@@ -189,6 +190,7 @@ $labels['highest'] = 'Molto alta';
 $labels['nosubject'] = '(nessun oggetto)';
 $labels['showimages'] = 'Visualizza immagini';
 $labels['alwaysshow'] = 'Mostra sempre immagini da $sender';
+$labels['isdraft'] = 'Questa è una bozza.';
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'Testo semplice';
 $labels['savesentmessagein'] = 'Salva i messaggi inviati in';
@@ -247,6 +249,8 @@ $labels['typepager'] = 'Teledrin';
 $labels['typevideo'] = 'Video';
 $labels['typeassistant'] = 'Assistente';
 $labels['typehomepage'] = 'Home page';
+$labels['typeblog'] = 'Blog';
+$labels['typeprofile'] = 'Profilo';
 $labels['addfield'] = 'Aggiungi campo...';
 $labels['addcontact'] = 'Aggiungi contatto alla rubrica';
 $labels['editcontact'] = 'Modifica contatto';
@@ -268,7 +272,6 @@ $labels['print'] = 'Stampa';
 $labels['export'] = 'Esporta';
 $labels['exportvcards'] = 'Esporta i contatti in formato vCard';
 $labels['newcontactgroup'] = 'Crea un nuovo gruppo';
-$labels['groupactions'] = 'Azioni per i gruppi';
 $labels['grouprename'] = 'Rinomina il gruppo';
 $labels['groupdelete'] = 'Cancella il gruppo';
 $labels['previouspage'] = 'Pagina precedente';
@@ -278,6 +281,8 @@ $labels['lastpage'] = 'Ultima pagina';
 $labels['group'] = 'Gruppo';
 $labels['groups'] = 'Gruppi';
 $labels['personaladrbook'] = 'Rubrica Personale';
+$labels['searchsave'] = 'Salva ricerca';
+$labels['searchdelete'] = 'Elimina ricerca';
 $labels['import'] = 'Importa';
 $labels['importcontacts'] = 'Importa contatti';
 $labels['importfromfile'] = 'Importa da file:';
@@ -286,6 +291,7 @@ $labels['importreplace'] = 'Sostituisci l\'intera rubrica';
 $labels['importtext'] = 'Puoi caricare i contatti da una rubrica esistente. Al momento è supportata l\'importazione dei contatti dal formato vCard.';
 $labels['done'] = 'Fatto';
 $labels['settingsfor'] = 'Impostazioni per ';
+$labels['about'] = 'Informazioni';
 $labels['preferences'] = 'Preferenze';
 $labels['userpreferences'] = 'Preferenze utente';
 $labels['editpreferences'] = 'Modifica le preferenze per l\'utente';
@@ -297,6 +303,8 @@ $labels['edititem'] = 'Modifica elemento';
 $labels['preferhtml'] = 'Mostra HTML';
 $labels['defaultcharset'] = 'Set di caratteri predefinito';
 $labels['htmlmessage'] = 'Messaggio HTML';
+$labels['dateformat'] = 'Formato data';
+$labels['timeformat'] = 'Formato orario';
 $labels['prettydate'] = 'Date più leggibili';
 $labels['setdefault'] = 'Imposta predefinita';
 $labels['autodetect'] = 'Auto';
@@ -368,7 +376,13 @@ $labels['reqmdn'] = 'Richiedi sempre la ricevuta di ritorno';
 $labels['reqdsn'] = 'Richiedi sempre la notifica di consegna';
 $labels['replysamefolder'] = 'Salva risposta nella cartella del messaggio a cui si risponde';
 $labels['defaultaddressbook'] = 'Aggiungi nuovi contatti alla rubrica selezionata';
+$labels['autocompletesingle'] = 'Non considerare gli indirizzi secondari nell\'autocompletamento';
 $labels['spellcheckbeforesend'] = 'Esegui il controllo ortografico prima di inviare un messaggio';
+$labels['spellcheckoptions'] = 'Opzioni controllo ortografico';
+$labels['spellcheckignoresyms'] = 'Ignora le parole contenenti simboli';
+$labels['spellcheckignorenums'] = 'Ignora le parole contenenti numeri';
+$labels['spellcheckignorecaps'] = 'Ignora le parole con tutte le lettere maiuscole';
+$labels['addtodict'] = 'Aggiungi al dizionario';
 $labels['folder'] = 'Cartella';
 $labels['folders'] = 'Cartelle';
 $labels['foldername'] = 'Nome cartella';
@@ -393,6 +407,11 @@ $labels['sortby'] = 'Ordina per';
 $labels['sortasc'] = 'Ordinamento crescente';
 $labels['sortdesc'] = 'Ordinamento decrescente';
 $labels['undo'] = 'Annulla';
+$labels['plugin'] = 'Plugin';
+$labels['version'] = 'Versione';
+$labels['source'] = 'Sorgente';
+$labels['license'] = 'Licenza';
+$labels['support'] = 'Ottieni supporto';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index 8e2c118e95e4fda270119eaafd3eff0912bbd353..bafcbb073206729f3fb1dd9ec70f8c46fda0ba68 100644 (file)
@@ -14,7 +14,7 @@
 |         Yusef Maali <contact@yusefmaali.net>                          |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -25,7 +25,7 @@ $messages['sessionerror'] = 'Sessione non valida o scaduta';
 $messages['imaperror'] = 'Impossibile connettersi al server IMAP';
 $messages['servererror'] = 'Errore del server!';
 $messages['servererrormsg'] = 'Errore del server: $msg';
-$messages['databaserror'] = 'Errore del database!';
+$messages['dberror'] = 'Errore del database!';
 $messages['errorreadonly'] = 'Impossibile eseguire l\'operazione. Cartella in sola lettura';
 $messages['errornoperm'] = 'Impossibile eseguire l\'operazione. Permesso negato';
 $messages['invalidrequest'] = 'Richiesta non valida! Nessun dato salvato.';
@@ -49,6 +49,7 @@ $messages['blockedimages'] = 'Per proteggere la tua privacy, le immagini remote
 $messages['encryptedmessage'] = 'Questo messaggio é cifrato e non può essere visualizzato. Spiacenti!';
 $messages['nocontactsfound'] = 'Nessun contatto trovato';
 $messages['contactnotfound'] = 'Il contatto richiesto non è stato trovato';
+$messages['contactsearchonly'] = 'Inserisci dei termini per cercare i contatti';
 $messages['sendingfailed'] = 'Impossibile inviare il messaggio';
 $messages['senttooquickly'] = 'Per favore, attendi $sec secondi prima di inviare questo messaggio';
 $messages['errorsavingsent'] = 'C\'è stato un errore nel salvare il messaggio inviato';
@@ -62,6 +63,7 @@ $messages['deletegroupconfirm'] = 'Sei sicuro di voler eliminare i gruppi selezi
 $messages['deletemessagesconfirm'] = 'Sei sicuro di voler eliminare i messaggi selezionati?';
 $messages['deletefolderconfirm'] = 'Sei sicuro di voler eliminare la cartella selezionata?';
 $messages['purgefolderconfirm'] = 'Sei sicuro di voler eliminare tutti i messaggi in questa cartella?';
+$messages['contactdeleting'] = 'Eliminazione contatti...';
 $messages['groupdeleting'] = 'Eliminazione gruppo...';
 $messages['folderdeleting'] = 'Eliminazione cartella...';
 $messages['foldermoving'] = 'Spostamento cartella...';
@@ -77,10 +79,10 @@ $messages['nosubjectwarning'] = 'L\'oggetto è vuoto. Vuoi inserirlo adesso?';
 $messages['nobodywarning'] = 'Inviare il messaggio senza testo?';
 $messages['notsentwarning'] = 'Il messaggio non è stato inviato. Vuoi annullare questo messaggio?';
 $messages['noldapserver'] = 'Per favore, scegli un server LDAP in cui ricercare';
-$messages['nocontactsreturned'] = 'Nessun contatto trovato';
 $messages['nosearchname'] = 'Per favore, immetti un nome o un indirizzo e-mail';
 $messages['notuploadedwarning'] = 'Non tutti gli allegati sono stati ancora caricati. Prego attendere, oppure cancellare il caricamento.';
 $messages['searchsuccessful'] = '$nr messaggi trovati';
+$messages['contactsearchsuccessful'] = '$nr contatti trovati';
 $messages['searchnomatch'] = 'La ricerca non ha dato nessun risultato';
 $messages['searching'] = 'Ricerca...';
 $messages['checking'] = 'Controllo...';
@@ -127,7 +129,6 @@ $messages['smtpautherror'] = 'Errore SMTP ($code): Autenticazione fallita';
 $messages['smtpfromerror'] = 'Errore SMTP ($code): Fallita l\'impostazione del mittente "$from" ($msg)';
 $messages['smtptoerror'] = 'Errore SMTP ($code): Fallito l\'inserimento del destinatario "$to" ($msg)';
 $messages['smtprecipientserror'] = 'Errore SMTP: Impossibile processare la lista dei destinatari';
-$messages['smtpdsnerror'] = 'Errore SMTP: Ricevuta di consegna non supportata';
 $messages['smtperror'] = 'Errore SMTP: $msg';
 $messages['emailformaterror'] = 'Indirizzo e-mail non corretto: $email';
 $messages['toomanyrecipients'] = 'Numero eccessivo di destinatari, ridurlo a $max';
@@ -140,11 +141,16 @@ $messages['contactrestored'] = 'Contatto/i ripristinato/i';
 $messages['groupdeleted'] = 'Gruppo correttamente eliminato';
 $messages['grouprenamed'] = 'Gruppo correttamente rinominato';
 $messages['groupcreated'] = 'Gruppo creato correttamente';
+$messages['savedsearchdeleted'] = 'Le ricerca salvata è stata eliminata con successo.';
+$messages['savedsearchdeleteerror'] = 'Impossibile eliminare la ricerca salvata.';
+$messages['savedsearchcreated'] = 'Ricerca salvata creata con successo.';
+$messages['savedsearchcreateerror'] = 'Impossibile creare la ricerca salvata.';
 $messages['messagedeleted'] = 'Messaggi/o cancellato correttamente';
 $messages['messagemoved'] = 'Messaggi/o spostato correttamente';
 $messages['messagecopied'] = 'Messaggi/o copiato correttamente';
 $messages['messagemarked'] = 'Messaggi/o marcato correttamente';
 $messages['autocompletechars'] = 'Inserisci almeno $min caratteri per l\'autocompletamento';
+$messages['autocompletemore'] = 'Più risultati ottenuti. Inserisci per favore più caratteri.';
 $messages['namecannotbeempty'] = 'Il nome non può essere vuoto';
 $messages['nametoolong'] = 'Nome troppo lungo';
 $messages['folderupdated'] = 'Cartella aggiornata correttamente';
@@ -152,5 +158,6 @@ $messages['foldercreated'] = 'Cartella creata correttamente';
 $messages['invalidimageformat'] = 'Formato immagine non valido';
 $messages['mispellingsfound'] = 'Sono stati riscontrati errori ortografici nel messaggio';
 $messages['parentnotwritable'] = 'Impossibile creare o muovere la cartella: accesso negato';
+$messages['messagetoobig'] = 'La parte del messaggio è troppo grande per essere processata.';
 
 ?>
index 5a3f3ec3ca4228a79984b8745733666ca58e31d1..f9e854830245904032caade1edec0146f3966c2d 100644 (file)
@@ -14,9 +14,9 @@
 |         Takashi Takamatsu <taka717@gmail.com>                         |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5291 2011-09-28 17:10:45Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
-EN-Revision: 5210
+EN-Revision: 5490
 
 */
 
@@ -154,7 +154,6 @@ $labels['unanswered'] = '未返信';
 $labels['deleted'] = '削除済み';
 $labels['invert'] = '反転';
 $labels['filter'] = 'フィルター';
-
 $labels['list'] = '一覧';
 $labels['threads'] = 'スレッド';
 $labels['expand-all'] = 'すべて展開';
@@ -227,6 +226,7 @@ $labels['highest'] = '最高';
 $labels['nosubject'] = '(件名なし)';
 $labels['showimages'] = '画像の表示';
 $labels['alwaysshow'] = '$sender から届いた画像は常に表示';
+$labels['isdraft']    = 'これは下書きのメッセージです。';
 
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'テキスト';
@@ -344,7 +344,7 @@ $labels['done'] = '完了';
 
 // settings
 $labels['settingsfor']  = '次の設定:';
-
+$labels['about'] = 'Roundcube Webmail について';
 $labels['preferences']  = '設定';
 $labels['userpreferences']  = 'ユーザー設定';
 $labels['editpreferences']  = 'ユーザー設定の変更';
@@ -432,6 +432,7 @@ $labels['reqmdn'] = '常に開封確認通知を要求する';
 $labels['reqdsn'] = '常に配送状況の通知を要求する';
 $labels['replysamefolder'] = '返信されたメールを返信元メールと同じフォルダに保存';
 $labels['defaultaddressbook'] = '次のアドレス帳に新規連絡先を追加する';
+$labels['autocompletesingle'] = '自動補完で代替メールアドレスを飛ばす';
 $labels['spellcheckbeforesend'] = 'メールの送信前にスペル チェック';
 $labels['spellcheckoptions'] = 'スペルチェックのオプション';
 $labels['spellcheckignoresyms'] = '記号の単語を無視する';
@@ -465,6 +466,12 @@ $labels['sortasc']  = '昇順で並び替え';
 $labels['sortdesc'] = '降順で並び替え';
 $labels['undo'] = '取り消し';
 
+$labels['plugin'] = 'プラグイン';
+$labels['version'] = 'バージョン';
+$labels['source'] = 'ソース';
+$labels['license'] = 'ライセンス';
+$labels['support'] = 'ヘルプを受ける';
+
 // units
 $labels['B'] = 'バイト';
 $labels['KB'] = 'KB';
index fefd7a507705d0c272adf635aa0923bfebc16aab..e728b03fc1d0f8875cf87368dd69227c33ddc1a2 100644 (file)
@@ -14,9 +14,9 @@
 |         Takashi Takamatsu <taka717@gmail.com>                         |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5291 2011-09-28 17:10:45Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
-//  EN-Revision: 5276
+//  EN-Revision: 5490
 
 */
 
@@ -136,7 +136,6 @@ $messages['smtpautherror'] = 'SMTP Error ($code): 認証に失敗しました。
 $messages['smtpfromerror'] = 'SMTP Error ($code): 差出人 "$from" を設定できません ($msg)';
 $messages['smtptoerror'] = 'SMTP Error ($code): 宛先 "$to" を追加できません ($msg)';
 $messages['smtprecipientserror'] = 'SMTP Error: 宛先の一覧を解析できません。';
-$messages['smtpdsnerror'] = 'SMTP エラー: 配送状況通知をサポートしていません。';
 $messages['smtperror'] = 'SMTP エラー: $msg';
 $messages['emailformaterror'] = 'メールアドレスが正しくありません: $email';
 $messages['toomanyrecipients'] = '宛先が多すぎます。$max 件以内にしてください。';
@@ -166,5 +165,6 @@ $messages['foldercreated'] = 'フォルダーを作成しました。';
 $messages['invalidimageformat'] = '画像の形式が正しくありません。';
 $messages['mispellingsfound'] = 'メッセージにスペル エラーを見つけました。';
 $messages['parentnotwritable'] = '選択した親フォルダーへの作成、移動に失敗しました。アクセス権限がありません。';
+$messages['messagetoobig'] = 'その処理をするにはメッセージ部分が大きすぎます。';
 
 ?>
index 2a9701c18e4e891de7fd87c50ac9621e39a4197e..fd3811467418251dbd4d6a439e97743883f50aa2 100755 (executable)
@@ -9,7 +9,7 @@
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 +-----------------------------------------------------------------------+
-| Author: Zaza Zviadadze                                                                       |
+| Author: Zaza Zviadadze, George Machitidze                            |
 +-----------------------------------------------------------------------+
 
 @version $Id: labels.inc 842 2009-10-03 19:00:00  zaza$
@@ -23,38 +23,35 @@ $labels['password'] = 'პაროლი';
 $labels['server'] = 'სერვერი';
 $labels['login'] = 'შესვლა';
 $labels['logout'] = 'გამოსვლა';
-$labels['mail'] = 'ელფოსტა';
+$labels['mail'] = 'ელფოსტა';
 $labels['settings'] = 'პარამეტრები';
 $labels['addressbook'] = 'მისამართები';
 $labels['inbox'] = 'მიღებული';
 $labels['drafts'] = 'დროებითი';
-$labels['sent'] = 'á\83\92á\83\90á\83\92á\83\96á\83\90á\83\95á\83\9cá\83\98á\83\9aá\83\94á\83\91á\83\98';
-$labels['trash'] = 'á\83¬á\83\90á\83¨á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\98';
+$labels['sent'] = 'გაგზავნილი';
+$labels['trash'] = 'წაშლილი';
 $labels['junk'] = 'სპამი';
 $labels['subject'] = 'სათაური';
 $labels['from'] = 'გამგზავნი';
-$labels['to'] = 'á\83\9bá\83\98á\83\9bá\83¦á\83\94á\83\91á\83\98';
-$labels['cc'] = 'á\83\99á\83\9dá\83\9eá\83\98á\83\90';
+$labels['to'] = 'á\83\95á\83\98á\83¡';
+$labels['cc'] = 'á\83\90á\83¡á\83\9aá\83\98';
 $labels['bcc'] = 'ფარული';
-$labels['replyto'] = 'á\83\93á\83\90á\83\91á\83 á\83£á\83\9cá\83\94á\83\91á\83\90';
+$labels['replyto'] = 'á\83\9eá\83\90á\83¡á\83£á\83®á\83\98';
 $labels['date'] = 'თარიღი';
 $labels['size'] = 'ზომა';
 $labels['priority'] = 'პრიორიტეტი';
 $labels['organization'] = 'ორგანიზაცია';
-$labels['reply-to'] = 'დაბრუნება';
 $labels['mailboxlist'] = 'საქაღალდეები';
-$labels['messagesfromto'] = 'შეტყობინება $from  $to დან $count';
 $labels['messagenrof'] = 'შეტყობინება $nr დან $count';
+$labels['copy'] = 'ასლი';
+$labels['move'] = 'გადატანა';
 $labels['moveto'] = 'გადატანა...';
 $labels['download'] = 'გადმოწერა';
 $labels['filename'] = 'ფაილის სახელი';
 $labels['filesize'] = 'ფაილის ზომა';
-$labels['preferhtml'] = 'როგორც HTML';
-$labels['htmlmessage'] = 'HTML შეტყობინება';
-$labels['prettydate'] = 'კარგი თარიღები';
 $labels['addtoaddressbook'] = 'მისამართებში დამატება';
-$labels['sun'] = 'კვ';
-$labels['mon'] = 'ორ';
+$labels['sun'] = 'კვ';
+$labels['mon'] = 'ორ';
 $labels['tue'] = 'სამ';
 $labels['wed'] = 'ოთხ';
 $labels['thu'] = 'ხუთ';
@@ -101,13 +98,9 @@ $labels['deletemessage'] = 'წაშლა';
 $labels['movemessagetotrash'] = 'შეტყობინების წაშლილებში გადატანა';
 $labels['printmessage'] = 'შეტყობინების ამობეჭვდა';
 $labels['previousmessage'] = 'წინა შეტყობინების ნახვა';
-$labels['previousmessages'] = 'წინა შეტყობინებების ნახვა';
 $labels['firstmessage'] = 'პირველი შეტყობინების ნახვა';
-$labels['firstmessages'] = 'პრველი შეტყობინებები';
 $labels['nextmessage'] = 'შემდეგი შეტყობინების ნახვა';
-$labels['nextmessages'] = 'შემდეგი შეტყობინებების ნახვა';
 $labels['lastmessage'] = 'ბოლო შეტყობინების ნახვა';
-$labels['lastmessages'] = 'ბოლო შეტყობინებების ნახვა';
 $labels['backtolist'] = 'უკან ჩამონათვალში';
 $labels['viewsource'] = 'შიგთავსი';
 $labels['markmessages'] = 'მონიშნეთ შეტყობინებები';
@@ -115,19 +108,30 @@ $labels['markread'] = 'როგორც წაკითხული';
 $labels['markunread'] = 'როგორც წაუკითხავი';
 $labels['markflagged'] = 'როგორც მონიშნული';
 $labels['markunflagged'] = 'როგორც მოუნიშნავი';
-$labels['messageactions'] = 'მეტი ფუნქციები';
+$labels['moreactions'] = 'მეტი მოქმედება...';
 $labels['select'] = 'მონიშვნა';
 $labels['all'] = 'ყველა';
 $labels['none'] = 'არცერთი';
+$labels['currpage'] = 'მიმდინარე გვერდი';
 $labels['unread'] = 'წაუკითხავი';
 $labels['flagged'] = 'მონიშნული';
 $labels['unanswered'] = 'უპასუხო';
 $labels['deleted'] = 'წაშლილი';
 $labels['invert'] = 'შებრუნებული';
 $labels['filter'] = 'ფილტრი';
+$labels['list'] = 'სია';
+$labels['expand-all'] = 'ყველას გაშლა';
+$labels['expand-unread'] = 'წაუკითხავის გაშლა';
+$labels['collapse-all'] = 'ყველას დაკეცვა';
+$labels['fromto'] = 'ვისგან/ვის';
+$labels['flag'] = 'დროშა';
+$labels['attachment'] = 'მიმაგრებული ფაილი';
+$labels['sentdate'] = 'გაგზავნის თარიღი';
+$labels['asc'] = 'ზრდადობით';
+$labels['desc'] = 'კლებადობით';
+$labels['listorder'] = 'დალაგება';
 $labels['compact'] = 'შეკუმშვა';
 $labels['empty'] = 'გაცარიელება';
-$labels['purge'] = 'გასუფთავება';
 $labels['quota'] = 'შეზღუდვა';
 $labels['unknown'] = 'უცნობი';
 $labels['unlimited'] = 'შეუზღუდავი';
@@ -174,13 +178,49 @@ $labels['receiptnote'] = 'განმარტება: ეს ნიშნა
 $labels['name'] = 'სრული სახელი';
 $labels['firstname'] = 'სახელი';
 $labels['surname'] = 'გვარი';
+$labels['department'] = 'დეპარტამენტი';
+$labels['gender'] = 'სქესი';
 $labels['email'] = 'ელ–ფოსტა';
+$labels['phone'] = 'ტელეფონი';
+$labels['address'] = 'მისამართი';
+$labels['street'] = 'ქუჩა';
+$labels['locality'] = 'ქალაქი';
+$labels['zipcode'] = 'ZIP კოდი';
+$labels['region'] = 'შტატი/პროვინცია';
+$labels['country'] = 'ქვეყანა';
+$labels['birthday'] = 'დაბადების დღე';
+$labels['website'] = 'ვებგვერდი';
+$labels['male'] = 'მამრობითი';
+$labels['female'] = 'მდედრობით';
+$labels['manager'] = 'მენეჯერი';
+$labels['allfields'] = 'ყველა ველი';
+$labels['search'] = 'ძებნა';
+$labels['advsearch'] = 'გაფართოებული ძებნა';
+$labels['other'] = 'სხვა';
+$labels['typehome'] = 'სახლი';
+$labels['typework'] = 'სამსახური';
+$labels['typeother'] = 'სხვა';
+$labels['typemobile'] = 'მობილური';
+$labels['typemain'] = 'ძირითადი';
+$labels['typehomefax'] = 'სახლის ფაქსი';
+$labels['typeworkfax'] = 'სამსახურის ფაქსი';
+$labels['typecar'] = 'მანქანა';
+$labels['typepager'] = 'პეიჯერი';
+$labels['typevideo'] = 'ვიდეო';
+$labels['typeblog'] = 'ბლოგი';
+$labels['typeprofile'] = 'პროფილი';
+$labels['addfield'] = 'ველის დამატება...';
 $labels['addcontact'] = 'კონტაქტებში დამატება';
 $labels['editcontact'] = 'კონტაქტის რედაქტირება';
+$labels['contacts'] = 'კონტაქტები';
+$labels['personalinfo'] = 'პირადი ინფორმაცია';
 $labels['edit'] = 'რედაქტირება';
 $labels['cancel'] = 'გაუქმება';
 $labels['save'] = 'შენახვა';
 $labels['delete'] = 'წაშლა';
+$labels['rename'] = 'გადარქმევა';
+$labels['addphoto'] = 'დამატება';
+$labels['replacephoto'] = 'გამოცვლა';
 $labels['newcontact'] = 'კონტაქტის შექმნა';
 $labels['deletecontact'] = 'მონიშნელი კონტაქტის წაშლა';
 $labels['composeto'] = 'ფოსტის გაგზავნა შერჩეულ ადრესატებთან';
@@ -188,12 +228,18 @@ $labels['contactsfromto'] = 'კონტაქტები $from დან $to
 $labels['print'] = 'დაბეჭვდა';
 $labels['export'] = 'ექსპორტი';
 $labels['exportvcards'] = 'კონტაქტების ექსპორტი vCard ფორმატში';
+$labels['newcontactgroup'] = 'ახალი საკონტაქტო ჯგუფის შექმნა';
+$labels['grouprename'] = 'ჯგუფის სახელის შეცვლა';
+$labels['groupdelete'] = 'ჯგუფის წაშლა';
 $labels['previouspage'] = 'წინას ჩვენება';
 $labels['firstpage'] = 'პირველის ჩვენება';
 $labels['nextpage'] = 'შემდეგის ჩვენება';
 $labels['lastpage'] = 'ბოლოს ჩვენება';
+$labels['group'] = 'ჯგუფი';
 $labels['groups'] = 'ჯგუფები';
 $labels['personaladrbook'] = 'პერსონალური მისამართები';
+$labels['searchsave'] = 'ძებნის შენახვა';
+$labels['searchdelete'] = 'ძებნის წაშლა';
 $labels['import'] = 'შემოტანა';
 $labels['importcontacts'] = 'კონტაქტების შემოტანა';
 $labels['importfromfile'] = 'შემოტანა ფაილიდან:';
@@ -209,6 +255,12 @@ $labels['manageidentities'] = 'პროფილების მართვა
 $labels['newidentity'] = 'ახალი პროფილი';
 $labels['newitem'] = 'ახალი';
 $labels['edititem'] = 'რედაქტირება';
+$labels['preferhtml'] = 'როგორც HTML';
+$labels['defaultcharset'] = 'ნაგულისხმები სიმბოლოთა ნაკრები';
+$labels['htmlmessage'] = 'HTML შეტყობინება';
+$labels['dateformat'] = 'თარიღის ფორმატი';
+$labels['timeformat'] = 'დროის ფორმატი';
+$labels['prettydate'] = 'კარგი თარიღები';
 $labels['setdefault'] = 'როგორც ზოგაგად';
 $labels['autodetect'] = 'ავტომატური';
 $labels['language'] = 'ენა';
@@ -240,12 +292,14 @@ $labels['autosavedraft'] = 'ავტომატური შენახვა
 $labels['everynminutes'] = 'ყოველ $n წუთში';
 $labels['keepalive'] = 'შეამოწმე ახალ შეტყობინებაზე';
 $labels['never'] = 'არასდროს';
+$labels['immediately'] = 'დაუყონებლივ';
 $labels['messagesdisplaying'] = 'გამოაჩინე შეტყობინებები';
 $labels['messagescomposition'] = 'შეტყობინებების შექმნა';
 $labels['mimeparamfolding'] = 'ბმულის სახელები';
 $labels['2231folding'] = 'Full RFC 2231 (Thunderbird)';
 $labels['miscfolding'] = 'RFC 2047/2231 (MS Outlook)';
 $labels['2047folding'] = 'Full RFC 2047 (other)';
+$labels['force7bit'] = 'MIME კოდირების გამოყენება 8 ბიტიანი სიმბოლოებისთვის';
 $labels['advancedoptions'] = 'დამატებითი პარამეტრები';
 $labels['focusonnewmessage'] = 'ფოკუსირება ახალ შეტყობინებებზე';
 $labels['checkallfolders'] = 'შეამოწმე ყველა საქაღალდე ახალ შეტყობინებაზე';
@@ -267,6 +321,8 @@ $labels['replysignaturepos'] = 'მხოლოდ პასუხის ან
 $labels['belowquote'] = 'ციტატის შემდეგ';
 $labels['abovequote'] = 'ციტატამდე';
 $labels['insertsignature'] = 'ხელმოცერის ჩასმა';
+$labels['afternseconds'] = '$n წამის შემდეგ';
+$labels['addtodict'] = 'ლექსიკონში დამატება';
 $labels['folder'] = 'საქაღალდე';
 $labels['folders'] = 'საქაღალდეები';
 $labels['foldername'] = 'საქაღალდის დასახელება';
@@ -276,12 +332,34 @@ $labels['create'] = 'შექმნა';
 $labels['createfolder'] = 'საქაღალდის შექმნა';
 $labels['managefolders'] = 'საქაღალდეების მართვა';
 $labels['specialfolders'] = 'სპეციალური საქაღალდე';
+$labels['location'] = 'მდებარეობა';
+$labels['info'] = 'ინფორმაცია';
+$labels['foldertype'] = 'დასტის ტიპი';
+$labels['personalfolder'] = 'კერძო დასტა';
+$labels['sharedfolder'] = 'საჯარო დასტა';
 $labels['sortby'] = 'დალაგება';
 $labels['sortasc'] = 'დალაგება ზრდადობით';
 $labels['sortdesc'] = 'დალაგება კლებადობით';
-$labels['B'] = 'ბ';
-$labels['KB'] = 'კბ';
-$labels['MB'] = 'მბ';
-$labels['GB'] = 'გბ';
+$labels['undo'] = 'დაბრუნება';
+$labels['B'] = 'ბტ';
+$labels['KB'] = 'კბტ';
+$labels['MB'] = 'მბტ';
+$labels['GB'] = 'გბტ';
+$labels['unicode'] = 'უნიკოდი';
+$labels['english'] = 'ინსგლისური';
+$labels['westerneuropean'] = 'დასავლურევროპული';
+$labels['easterneuropean'] = 'აღმოსავლეთევროპული';
+$labels['baltic'] = 'ბალტიური';
+$labels['cyrillic'] = 'კირილიცა';
+$labels['arabic'] = 'არაბული';
+$labels['greek'] = 'ბერძნული';
+$labels['hebrew'] = 'ებრაული';
+$labels['turkish'] = 'თურქული';
+$labels['thai'] = 'ტაი';
+$labels['celtic'] = 'კელტური';
+$labels['vietnamese'] = 'ვიეტნამური';
+$labels['japanese'] = 'იაპონური';
+$labels['korean'] = 'კორეული';
+$labels['chinese'] = 'ჩინური';
 
 ?>
index 4865847aad35d289bdf06e2a3003247526bd9bda..55d3333d82ccdd073c4cec823392b3bf88789b93 100755 (executable)
@@ -9,7 +9,7 @@
 | Licensed under the GNU GPL                                            |
 |                                                                       |
 +-----------------------------------------------------------------------+
-| Author: Zaza Zviadadze                                                |
+| Author: Zaza Zviadadze, George Machitidze                             |
 +-----------------------------------------------------------------------+
 
 @version $Id: messages.inc 842 2009-10-03 19:30:00  zaza$
@@ -22,12 +22,16 @@ $messages['cookiesdisabled'] = 'თქვენი ბროუზერი კ
 $messages['sessionerror'] = 'თქვენი სესია არის მცდარი ან ვადაგასული';
 $messages['imaperror'] = 'IMAP სერვერთან დაკავშირება შეუძლებელია';
 $messages['servererror'] = 'შეცდომა სერვერზე!';
+$messages['servererrormsg'] = 'სერვერის შეცდომა: $msg';
+$messages['dberror'] = 'მონაცემთა ბაზის შეცდომა!';
+$messages['errornoperm'] = 'მოქმედების შესრულება შეუძლებალია. წვდომა აკრძალულია.';
 $messages['invalidrequest'] = 'არასწორი მოთხოვნა! მონაცემების შენახვა არ მოხერხდა.';
 $messages['nomessagesfound'] = 'არ არის ახალი შეტყობინება';
 $messages['loggedout'] = 'წარმატებით დაიხურა თქვენი სესია';
 $messages['mailboxempty'] = 'საფოსტო ყუთი ცარიელია';
 $messages['loading'] = 'იტვირთება...';
 $messages['uploading'] = 'ფაილი იტვირთება...';
+$messages['uploadingmany'] = 'ფაილების ატვირთვა...';
 $messages['loadingdata'] = 'მონაცემების ჩატვირთვა...';
 $messages['checkingmail'] = 'ახალი შეტყობინების ნახვა';
 $messages['sendingmessage'] = 'შეტყობინების გაგზავნა';
@@ -52,6 +56,7 @@ $messages['deletecontactconfirm'] = 'ნამდვილად გსურთ
 $messages['deletemessagesconfirm'] = 'ნამდვილად გსურთ მონიშნული შეტყობინებების წაშლა?';
 $messages['deletefolderconfirm'] = 'ნამდვილად გსურთ ამ საქაღალდის წაშლა?';
 $messages['purgefolderconfirm'] = 'ნამდვილად გსურთ ყველა შეტყობინების წაშლა აღნიშნულ საქაღალდეში?';
+$messages['groupdeleting'] = 'ჯგუფის წაშლა...';
 $messages['folderdeleting'] = 'საქაღალდის წაშლა...';
 $messages['foldermoving'] = 'საქაღალდის გადატანა...';
 $messages['formincomplete'] = 'ყველა ველი არ არის შევსებული';
@@ -64,10 +69,10 @@ $messages['nosubjectwarning'] = 'გსურთ თუ არა შეტყ
 $messages['nobodywarning'] = 'გსურთ თუ არა შეტყობინების გაგზავნა უტექსტოდ?';
 $messages['notsentwarning'] = 'შეტყობინება ვერ იქნა გაგზავნილი. გსურთ თუ არა გზავნილის გაუქმება?';
 $messages['noldapserver'] = 'მიუთითეთ LDAP სერვერი ძიებისთვის';
-$messages['nocontactsreturned'] = 'კონტაქტები ვერ მოძებნა';
 $messages['nosearchname'] = 'მიუთითეთ სახელი ან ელ–ფოსტის მისამართი';
 $messages['notuploadedwarning'] = 'ჯერ არ ატვირთულა ყველა მიმაგრებული ფაილი. დაელოდეთ ან გააუქმეთ ატვირთვა.';
 $messages['searchsuccessful'] = '$nr შეტყობინეა მოიძებნა';
+$messages['contactsearchsuccessful'] = 'ნაპოვნია $nr კონტაქტი';
 $messages['searchnomatch'] = 'შეტყობინება ვერ მოიძებნა';
 $messages['searching'] = 'ძიება...';
 $messages['checking'] = 'შემოწმება...';
@@ -103,5 +108,17 @@ $messages['smtptoerror'] = 'SMTP Error ($code): ვერ დაემატა
 $messages['smtprecipientserror'] = 'SMTP Error: მიმღებთა ჩამონათვალის დამუშავება ვერ მოხერხდა';
 $messages['smtperror'] = 'SMTP Error: $msg';
 $messages['emailformaterror'] = 'ელ-ფოსტის მისამართი არასწორია $email';
+$messages['contactdeleted'] = 'კონტაქტ(ებ)ი წარმატებით წაიშალა.';
+$messages['contactrestored'] = 'კონრაქტ(ებ)ი წარმატებით აღდგა,';
+$messages['groupdeleted'] = 'ჯგუფი წარმატებით წაიშალა.';
+$messages['groupcreated'] = 'ჯგუფი წარმატებით შეიქმნა.';
+$messages['messagedeleted'] = 'შეტყობინებები წარმატებით წაიშალა.';
+$messages['messagemoved'] = 'შეტყობინებები გადატანილია წარმატებით.';
+$messages['messagemarked'] = 'შეტყობინებები წარმატებით მოინიშნა.';
+$messages['namecannotbeempty'] = 'სახელი არ შეიძლება იყოს ცარიელი.';
+$messages['nametoolong'] = 'სახელი ძალიან მოკლეა.';
+$messages['folderupdated'] = 'დასტა წარმატებით განახლდა.';
+$messages['foldercreated'] = 'დასტა წარმატებით შეიქმნა.';
+$messages['invalidimageformat'] = 'გამოსახულების ფორმატი არასწორია.';
 
 ?>
index 623fd1e23ea232c9dfbf895495109378ed5bc9c3..9f9af61f8780cef86f1bbd8b0d226cc134628b0d 100644 (file)
@@ -14,7 +14,7 @@
 |          Rimas Kudelis <rq@akl.lt>                                    |
 +-----------------------------------------------------------------------+'
 
-@version $Id: labels.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -118,7 +118,7 @@ $labels['markread'] = 'Kaip skaitytus';
 $labels['markunread'] = 'Kaip neskaitytus';
 $labels['markflagged'] = 'Pažymėti gairele';
 $labels['markunflagged'] = 'Pašalinti gairelę';
-$labels['messageactions'] = 'Daugiau veiksmų…';
+$labels['moreactions'] = 'Kiti veiksmai…';
 $labels['select'] = 'Pažymėti';
 $labels['all'] = 'visus';
 $labels['none'] = 'nieko';
@@ -151,7 +151,7 @@ $labels['listsorting'] = 'Stulpelis rikiavimui';
 $labels['listorder'] = 'Rikiavimo tvarka';
 $labels['listmode'] = 'Sąrašo rodymo veiksena';
 $labels['folderactions'] = 'Veiksmai su aplankais…';
-$labels['compact'] = 'Suspausti';
+$labels['compact'] = 'Suglaudinti';
 $labels['empty'] = 'Ištuštinti';
 $labels['quota'] = 'Disko naudojimas';
 $labels['unknown'] = 'nežinomas';
@@ -172,6 +172,7 @@ $labels['editortype'] = 'Laiško tipas';
 $labels['returnreceipt'] = 'Prašyti pristatymo pažymos';
 $labels['dsn'] = 'Laiško pristatymo pažyma';
 $labels['mailreplyintro'] = '$date, $sender rašė:';
+$labels['originalmessage'] = 'Originalus laiškas';
 $labels['editidents'] = 'Tvarkyti tapatybes';
 $labels['checkspelling'] = 'Tikrinti rašybą';
 $labels['resumeediting'] = 'Tęsti redagavimą';
@@ -189,6 +190,7 @@ $labels['highest'] = 'Aukščiausias';
 $labels['nosubject'] = '(tema nenurodyta)';
 $labels['showimages'] = 'Rodyti paveikslėlius';
 $labels['alwaysshow'] = 'Visada rodyti paveikslėlius $sender laiškuose';
+$labels['isdraft'] = 'Tai – laiško juodraštis.';
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'Grynasis tekstas';
 $labels['savesentmessagein'] = 'Išsiųstus laiškus įrašyti į';
@@ -247,6 +249,8 @@ $labels['typepager'] = 'Pranešimų gaviklis';
 $labels['typevideo'] = 'Vaizdo';
 $labels['typeassistant'] = 'Padėjėjo(-os)';
 $labels['typehomepage'] = 'Tinklalapis';
+$labels['typeblog'] = 'Tinklaraštis';
+$labels['typeprofile'] = 'Profilis';
 $labels['addfield'] = 'Pridėti lauką…';
 $labels['addcontact'] = 'Pridėti adresatą';
 $labels['editcontact'] = 'Taisyti adresatą';
@@ -268,7 +272,6 @@ $labels['print'] = 'Spausdinti';
 $labels['export'] = 'Eksportuoti';
 $labels['exportvcards'] = 'Eksportuoti adresatus  „vCard“ formatu';
 $labels['newcontactgroup'] = 'Kurti adresatų grupę';
-$labels['groupactions'] = 'Adresatų grupėms taikomi veiksmai…';
 $labels['grouprename'] = 'Pervardinti grupę';
 $labels['groupdelete'] = 'Pašalinti grupę';
 $labels['previouspage'] = 'Rodyti ankstesnį puslapį';
@@ -278,6 +281,8 @@ $labels['lastpage'] = 'Rodyti paskutinį puslapį';
 $labels['group'] = 'Grupė';
 $labels['groups'] = 'Grupės';
 $labels['personaladrbook'] = 'Asmeniniai adresai';
+$labels['searchsave'] = 'Įrašyti kaip radinių aplanką';
+$labels['searchdelete'] = 'Pašalinti radinių aplanką';
 $labels['import'] = 'Importuoti';
 $labels['importcontacts'] = 'Importuoti adresatus';
 $labels['importfromfile'] = 'Importuoti iš failo:';
@@ -286,6 +291,7 @@ $labels['importreplace'] = 'Perrašyti visą adresų knygą';
 $labels['importtext'] = 'Jūs galite įkelti adresatus iš egzistuojančios adresų knygos.<br/>Šiuo metu galima importuoti adresus iš <a href="http://en.wikipedia.org/wiki/VCard">vCard</a> tipo failų.';
 $labels['done'] = 'Baigta';
 $labels['settingsfor'] = 'Nustatymai';
+$labels['about'] = 'Apie';
 $labels['preferences'] = 'Nustatymai';
 $labels['userpreferences'] = 'Naudotojo nustatymai';
 $labels['editpreferences'] = 'Redaguoti naudotojo nustatymus';
@@ -297,6 +303,8 @@ $labels['edititem'] = 'Redaguoti elementą';
 $labels['preferhtml'] = 'Rodyti HTML';
 $labels['defaultcharset'] = 'Numatytoji koduotė';
 $labels['htmlmessage'] = 'HTML laiškas';
+$labels['dateformat'] = 'Datos formatas';
+$labels['timeformat'] = 'Laiko formatas';
 $labels['prettydate'] = 'Dailios datos';
 $labels['setdefault'] = 'Laikyti numatytąja';
 $labels['autodetect'] = 'Aptikti automatiškai';
@@ -311,7 +319,7 @@ $labels['htmlsignature'] = 'HTML parašas';
 $labels['previewpane'] = 'Rodyti laiško peržiūros polangį';
 $labels['skin'] = 'Grafinis apvalkalas';
 $labels['logoutclear'] = 'Išvalyti Šiukšlinę atsijungiant';
-$labels['logoutcompact'] = 'Suspausti Gautų laiškų aplanką atsijungiant';
+$labels['logoutcompact'] = 'Suglaudinti gautųjų laiškų aplanką atsijungiant';
 $labels['uisettings'] = 'Naudotojo sąsaja';
 $labels['serversettings'] = 'Serverio nustatymai';
 $labels['mailboxview'] = 'Pašto dėžutės rodymas';
@@ -368,7 +376,13 @@ $labels['reqmdn'] = 'Visuomet prašyti patvirtinimo, jog laiškas perskaitytas';
 $labels['reqdsn'] = 'Visuomet prašyti laiško pristatymo pažymos';
 $labels['replysamefolder'] = 'Atsakymus talpinti į tą patį aplanką, kuriame yra pirminis laiškas';
 $labels['defaultaddressbook'] = 'Pridėti naujus adresatus į pasirinktąją adresų knygą';
+$labels['autocompletesingle'] = 'Užbaigiant adresus, praleisti antrinius';
 $labels['spellcheckbeforesend'] = 'Tikrinti rašybą prieš išsiunčiant laišką';
+$labels['spellcheckoptions'] = 'Rašybos tikrinimo nuostatos';
+$labels['spellcheckignoresyms'] = 'Nepaisyti žodžių su spec. simboliais';
+$labels['spellcheckignorenums'] = 'Nepaisyti žodžių su skaitmenimis';
+$labels['spellcheckignorecaps'] = 'Nepaisyti žodžių vien iš didžiųjų raidžių';
+$labels['addtodict'] = 'Įtraukti į žodyną';
 $labels['folder'] = 'Aplankas';
 $labels['folders'] = 'Aplankai';
 $labels['foldername'] = 'Aplanko vardas';
@@ -393,6 +407,11 @@ $labels['sortby'] = 'Rikiuoti pagal';
 $labels['sortasc'] = 'Rikiuoti didėjančiai';
 $labels['sortdesc'] = 'Rikiuoti mažėjančiai';
 $labels['undo'] = 'Atšaukti';
+$labels['plugin'] = 'Papildinys';
+$labels['version'] = 'Versija';
+$labels['source'] = 'Šaltinis';
+$labels['license'] = 'Licencija';
+$labels['support'] = 'Gauti pagalbos';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index 151277ea29f211ddd345517ed9b4a2796444c531..4cff2b0139ea54f6c17b8eb840e3b3fca78b1773 100644 (file)
@@ -14,7 +14,7 @@
 |          Rimas Kudelis <rq@akl.lt>                                    |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -25,7 +25,7 @@ $messages['sessionerror'] = 'Jūsų sesija negaliojanti.';
 $messages['imaperror'] = 'Nepavyko prisijungti prie IMAP serverio.';
 $messages['servererror'] = 'Serverio klaida!';
 $messages['servererrormsg'] = 'Serverio klaida: $msg';
-$messages['databaserror'] = 'Duomenų bazės klaida!';
+$messages['dberror'] = 'Duomenų bazės klaida!';
 $messages['errorreadonly'] = 'Nepavyko atlikti veiksmo – aplankas prieinamas tik skaitymui.';
 $messages['errornoperm'] = 'Nepavyko atlikti veiksmo – nepakanka teisių.';
 $messages['invalidrequest'] = 'Netinkama užklausa! Duomenys neišsaugoti.';
@@ -49,6 +49,7 @@ $messages['blockedimages'] = 'Siekiant apsaugoti Jūsų privatumą, paveikslėli
 $messages['encryptedmessage'] = 'Apgailestaujame, tačiau šis laiškas užšifruotas ir negali būti parodytas.';
 $messages['nocontactsfound'] = 'Adresatų nerasta.';
 $messages['contactnotfound'] = 'Ieškotas adresatas nerastas.';
+$messages['contactsearchonly'] = 'Įveskite reikšminius žodžius adresatų paieškai';
 $messages['sendingfailed'] = 'Laiško išsiųsti nepavyko.';
 $messages['senttooquickly'] = 'Turite luktelėti $sec sek., kad galėtumėte išsiųsti laišką.';
 $messages['errorsavingsent'] = 'Įrašant išsiųstą laišką įvyko klaida.';
@@ -62,6 +63,7 @@ $messages['deletegroupconfirm'] = 'Ar tikrai pašalinti pažymėtą grupę?';
 $messages['deletemessagesconfirm'] = 'Ar tikrai pašalinti pažymėtą(-us) laišką(-us)?';
 $messages['deletefolderconfirm'] = 'Ar tikrai pašalinti šį aplanką?';
 $messages['purgefolderconfirm'] = 'Ar tikrai pašalinti visus šiame aplanke esančius laiškus?';
+$messages['contactdeleting'] = 'Adresatas(-ai) šalinamas(-i)…';
 $messages['groupdeleting'] = 'Grupė šalinama…';
 $messages['folderdeleting'] = 'Aplankas šalinamas…';
 $messages['foldermoving'] = 'Aplankas perkeliamas…';
@@ -77,10 +79,10 @@ $messages['nosubjectwarning'] = 'Temos laukelis tuščias. Ar norite temą įves
 $messages['nobodywarning'] = 'Išsiųsti šį laišką be teksto?';
 $messages['notsentwarning'] = 'Laiškas neišsiųstas. Ar jūs tikrai norite jo atsikratyti?';
 $messages['noldapserver'] = 'Prašome pasirinkti LDAP serverį paieškai.';
-$messages['nocontactsreturned'] = 'Jokių adresatų nerasta.';
 $messages['nosearchname'] = 'Prašom įvesti vardą arba el. pašto adresą.';
 $messages['notuploadedwarning'] = 'Įkelti dar ne visi priedai. Prašom palaukti arba atšaukti jų įkėlimą.';
 $messages['searchsuccessful'] = 'Rasta laiškų: $nr.';
+$messages['contactsearchsuccessful'] = 'Rasta adresatų: $nr.';
 $messages['searchnomatch'] = 'Paiešką atitinkančių rezultatų nėra.';
 $messages['searching'] = 'Ieškoma…';
 $messages['checking'] = 'Tikrinama…';
@@ -127,7 +129,6 @@ $messages['smtpautherror'] = 'SMTP klaida ($code): nepavyko autentikuotis.';
 $messages['smtpfromerror'] = 'SMTP klaida ($code): nepavyko nurodyti siuntėjo „$from“ ($msg).';
 $messages['smtptoerror'] = 'SMTP klaida ($code): nepavyko pridėti gavėjo „$to“ ($msg).';
 $messages['smtprecipientserror'] = 'SMTP klaida: nepavyko apdoroti gavėjų sąrašo.';
-$messages['smtpdsnerror'] = 'SMTP klaida: laiškų pristatymo pažymos nepalaikomos.';
 $messages['smtperror'] = 'SMTP klaida: $msg';
 $messages['emailformaterror'] = 'Netinkamas el. pašto adresas: $email';
 $messages['toomanyrecipients'] = 'Per daug gavėjų. Sumažinkite jų bent iki $max.';
@@ -140,11 +141,16 @@ $messages['contactrestored'] = 'Adresatas(-ai) sėkmingai atkurtas(-i).';
 $messages['groupdeleted'] = 'Grupė sėkmingai pašalinta.';
 $messages['grouprenamed'] = 'Grupė sėkmingai pervardinta.';
 $messages['groupcreated'] = 'Grupė sėkmingai sukurta.';
+$messages['savedsearchdeleted'] = 'Radinių aplankas sėkmingai pašalintas.';
+$messages['savedsearchdeleteerror'] = 'Radinių aplanko pašalinti nepavyko.';
+$messages['savedsearchcreated'] = 'Radinių aplankas sėkmingai sukurtas.';
+$messages['savedsearchcreateerror'] = 'Radinių aplanko sukurti nepavyko.';
 $messages['messagedeleted'] = 'Laiškas(-ai) sėkmingai pašalintas(-i).';
 $messages['messagemoved'] = 'Laiškas(-ai) sėkmingai perkeltas(-i).';
 $messages['messagecopied'] = 'Laiškas(-ai) sėkmingai nukopijuotas(-i).';
 $messages['messagemarked'] = 'Laiškas(-ai) sėkmingai pažymėtas(-i).';
 $messages['autocompletechars'] = 'Automatiniam užbaigimui būtini bent $min simboliai.';
+$messages['autocompletemore'] = 'Rasta daugiau atitikmenų. Įveskite dar porą simbolių.';
 $messages['namecannotbeempty'] = 'Vardas negali būti tuščias.';
 $messages['nametoolong'] = 'Vardas yra per ilgas.';
 $messages['folderupdated'] = 'Aplanko savybės sėkmingai atnaujintos.';
@@ -152,5 +158,6 @@ $messages['foldercreated'] = 'Aplankas sėkmingai sukurtas.';
 $messages['invalidimageformat'] = 'Paveikslo formatas netinkamas.';
 $messages['mispellingsfound'] = 'Laiške rasta rašybos klaidų.';
 $messages['parentnotwritable'] = 'Nepavyko sukurti arba perkelti aplanko į parinktą tėvinį aplanką. Trūksta prieigos teisių.';
+$messages['messagetoobig'] = 'Laiško dalis yra per didelė, kad galėtų būti apdorota.';
 
 ?>
index 46bcf80428d5b6d0fa4409a80fe4913ea82374c4..d0df66678610318226aa735e4b3c067b0207b32a 100644 (file)
@@ -16,7 +16,7 @@
 |         Kaspars Tenters <kaspars.tenters@va.lv>                       |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: labels.inc 5173 2011-09-05 18:41:04Z thomasb $
 
 */
 
index 8c6ab5a158772ea0c304904337f120cf4a5861a8..698baca05bd62a1156e9c9735f552f1242824f28 100644 (file)
@@ -16,7 +16,7 @@
 |         Kaspars Tenters <kaspars.tenters@va.lv>                       |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: messages.inc 5173 2011-09-05 18:41:04Z thomasb $
 
 */
 
index 1c616a179a15fd414cdc4ff2f5b663a515e96bf9..0186f472c1702fc975126bdd4f055b1ddf570d5a 100644 (file)
@@ -17,7 +17,7 @@
 |         Justin van Beusekom <j.v.beusekom@beus-it.nl>                 |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 4549 2011-02-15 14:33:45Z robin $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -104,18 +104,16 @@ $labels['replytomessage'] = 'Beantwoord het bericht';
 $labels['replytoallmessage'] = 'Beantwoord lijst of afzender en alle ontvangers';
 $labels['replyall'] = 'Beantwoord alle ontvangers';
 $labels['replylist'] = 'Beantwoord lijst';
+$labels['forwardinline'] = 'Doorsturen in bericht';
+$labels['forwardattachment'] = 'Doorsturen als bijlage';
 $labels['forwardmessage'] = 'Bericht doorsturen';
 $labels['deletemessage'] = 'Verwijder het bericht';
 $labels['movemessagetotrash'] = 'Verplaats het bericht naar de prullenbak';
 $labels['printmessage'] = 'Dit bericht afdrukken';
 $labels['previousmessage'] = 'Toon het vorige bericht';
-$labels['previousmessages'] = 'Toon vorige lijst met berichten';
 $labels['firstmessage'] = 'Toon het eerste bericht';
-$labels['firstmessages'] = 'Toon eerste lijst met berichten';
 $labels['nextmessage'] = 'Toon het volgende bericht';
-$labels['nextmessages'] = 'Toon volgende lijst met berichten';
 $labels['lastmessage'] = 'Toon het laatste bericht';
-$labels['lastmessages'] = 'Toon laatste lijst met berichten';
 $labels['backtolist'] = 'Terug naar berichtenoverzicht';
 $labels['viewsource'] = 'Toon bron';
 $labels['markmessages'] = 'Markeer berichten';
@@ -123,7 +121,7 @@ $labels['markread'] = 'Gelezen';
 $labels['markunread'] = 'Ongelezen';
 $labels['markflagged'] = 'Selecteren';
 $labels['markunflagged'] = 'Niet selecteren';
-$labels['messageactions'] = 'Meer acties...';
+$labels['moreactions'] = 'Meer acties';
 $labels['select'] = 'Selecteer';
 $labels['all'] = 'Allemaal';
 $labels['none'] = 'Geen';
@@ -176,12 +174,15 @@ $labels['charset'] = 'Karakterset';
 $labels['editortype'] = 'Opmaak';
 $labels['returnreceipt'] = 'Ontvangstbevestiging';
 $labels['dsn'] = 'Afleverings status notificatie (DSN)';
+$labels['mailreplyintro'] = '$sender schreef op $date:';
+$labels['originalmessage'] = 'Originele bericht';
 $labels['editidents'] = 'Bewerk identiteiten';
 $labels['checkspelling'] = 'Controleer spelling';
 $labels['resumeediting'] = 'Doorgaan met opstellen';
 $labels['revertto'] = 'Wijzig terug in';
 $labels['attachments'] = 'Bijlages';
 $labels['upload'] = 'Toevoegen';
+$labels['uploadprogress'] = '$percent ($current van $total)';
 $labels['close'] = 'Sluit';
 $labels['messageoptions'] = 'Acties voor bericht...';
 $labels['low'] = 'Laag';
@@ -192,6 +193,7 @@ $labels['highest'] = 'Hoogste';
 $labels['nosubject'] = '(geen onderwerp)';
 $labels['showimages'] = 'Toon afbeeldingen';
 $labels['alwaysshow'] = 'Afbeeldingen van $sender altijd tonen';
+$labels['isdraft'] = 'Dit is een concept.';
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'Gewone tekst';
 $labels['savesentmessagein'] = 'Bewaar verzonden bericht in';
@@ -234,6 +236,10 @@ $labels['female'] = 'Vrouw';
 $labels['manager'] = 'Manager';
 $labels['assistant'] = 'Assistent';
 $labels['spouse'] = 'Echtgenoot';
+$labels['allfields'] = 'Alle velden';
+$labels['search'] = 'Zoeken';
+$labels['advsearch'] = 'Geavanceerd zoeken';
+$labels['other'] = 'Anders';
 $labels['typehome'] = 'Thuis';
 $labels['typework'] = 'Werk';
 $labels['typeother'] = 'Anders';
@@ -245,6 +251,9 @@ $labels['typecar'] = 'Auto';
 $labels['typepager'] = 'Pieper';
 $labels['typevideo'] = 'Video';
 $labels['typeassistant'] = 'Assistent';
+$labels['typehomepage'] = 'Website';
+$labels['typeblog'] = 'Blog';
+$labels['typeprofile'] = 'Profiel';
 $labels['addfield'] = 'Veld toevoegen...';
 $labels['addcontact'] = 'Nieuwe contactpersoon toevoegen';
 $labels['editcontact'] = 'Contactpersoon wijzigen';
@@ -266,7 +275,8 @@ $labels['print'] = 'Afdrukken';
 $labels['export'] = 'Exporteren';
 $labels['exportvcards'] = 'Exporteer contactpersonen in vCard formaat';
 $labels['newcontactgroup'] = 'Maak een nieuwe contactgroep';
-$labels['groupactions'] = 'Acties voor contactgroepen...';
+$labels['grouprename'] = 'Groep hernoemen';
+$labels['groupdelete'] = 'Groep verwijderen';
 $labels['previouspage'] = 'Vorige pagina';
 $labels['firstpage'] = 'Eerste pagina';
 $labels['nextpage'] = 'Volgende pagina';
@@ -274,9 +284,12 @@ $labels['lastpage'] = 'Laatste pagina';
 $labels['group'] = 'Groep';
 $labels['groups'] = 'Groepen';
 $labels['personaladrbook'] = 'Persoonlijk Adresboek';
+$labels['searchsave'] = 'Zoekopdracht opslaan';
+$labels['searchdelete'] = 'Zoekopdracht verwijderen';
 $labels['import'] = 'Importeer';
 $labels['importcontacts'] = 'Contactpersonen importeren';
 $labels['importfromfile'] = 'Importeer van bestand:';
+$labels['importtarget'] = 'Voeg nieuwe contactpersonen toe aan adresboek:';
 $labels['importreplace'] = 'Vervang het complete adresboek';
 $labels['importtext'] = 'U kunt hier contactpersonen importeren vanuit een bestaand adresboek.<br />Op dit moment ondersteunen wij het <a href="http://nl.wikipedia.org/wiki/VCard">vCard</a> bestandsformaat.';
 $labels['done'] = 'Klaar';
@@ -292,6 +305,8 @@ $labels['edititem'] = 'Wijzig item';
 $labels['preferhtml'] = 'Toon HTML';
 $labels['defaultcharset'] = 'Standaard karakterset';
 $labels['htmlmessage'] = 'HTML-Bericht';
+$labels['dateformat'] = 'Datum formaat';
+$labels['timeformat'] = 'Tijd formaat';
 $labels['prettydate'] = 'Gebruiksvriendelijke data';
 $labels['setdefault'] = 'Stel in als standaard';
 $labels['autodetect'] = 'Automatisch';
@@ -362,6 +377,14 @@ $labels['afternseconds'] = 'Na $n seconden';
 $labels['reqmdn'] = 'Vraag altijd een ontvangstbevestiging';
 $labels['reqdsn'] = 'Vraag altijd een afleverings status notificatie (DSN)';
 $labels['replysamefolder'] = 'Plaats antwoorden in de folder van het beantwoorde bericht';
+$labels['defaultaddressbook'] = 'Voeg nieuw contactpersoon toe aan geselecteerd adresboek';
+$labels['autocompletesingle'] = 'Sla alternatieve E-mail adressen over bij automatisch aanvullen';
+$labels['spellcheckbeforesend'] = 'Controleer slepping voordat het bericht wordt verzonden';
+$labels['spellcheckoptions'] = 'Instellingen spellingscontrole';
+$labels['spellcheckignoresyms'] = 'Negeer woorden met symbolen';
+$labels['spellcheckignorenums'] = 'Negeer woorden met cijfers';
+$labels['spellcheckignorecaps'] = 'Negeer woorden welke volledig uit hoofdletters bestaan';
+$labels['addtodict'] = 'Voeg toe aan woordenboek';
 $labels['folder'] = 'Map';
 $labels['folders'] = 'Mappen';
 $labels['foldername'] = 'Mapnaam';
@@ -378,9 +401,14 @@ $labels['location'] = 'Locatie';
 $labels['info'] = 'Informatie';
 $labels['getfoldersize'] = 'Klik hier voor de map grootte';
 $labels['changesubscription'] = 'Klik voor het wijzigen van de inschrijving';
+$labels['foldertype'] = 'Mapgrootte';
+$labels['personalfolder'] = 'Privé map';
+$labels['otherfolder'] = 'Map van andere gebruiker';
+$labels['sharedfolder'] = 'Publieke map';
 $labels['sortby'] = 'Sorteer op';
 $labels['sortasc'] = 'Sorteer oplopend';
 $labels['sortdesc'] = 'Sorteer aflopend';
+$labels['undo'] = 'Ongedaan maken';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index 8c5038362330cf657a988267301e27ec32716a20..37ece88eb6f63c2009e56fb57c9abd27b66f2095 100644 (file)
 |         Justin van Beusekom <j.v.beusekom@beus-it.nl>                 |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
 $messages = array();
-$messages['loginfailed'] = 'Inloggen mislukt';
-$messages['cookiesdisabled'] = 'Uw browser accepteert geen cookies';
-$messages['sessionerror'] = 'Uw sessie is verlopen of ongeldig';
-$messages['imaperror'] = 'Connectie met IMAP server mislukt';
+$messages['loginfailed'] = 'Inloggen mislukt.';
+$messages['cookiesdisabled'] = 'Uw browser accepteert geen cookies.';
+$messages['sessionerror'] = 'Uw sessie is verlopen of ongeldig.';
+$messages['imaperror'] = 'Connectie met IMAP server mislukt.';
 $messages['servererror'] = 'Server Fout!';
 $messages['servererrormsg'] = 'Server Fout: $msg';
-$messages['databaserror'] = 'Database Fout!';
-$messages['errorreadonly'] = 'Niet in staat om de bewerking uit te voeren. Map is alleen-lezen';
-$messages['errornoperm'] = 'Niet in staat om de bewerking uit te voeren. Toestemming geweigerd';
+$messages['dberror'] = 'Database Fout!';
+$messages['errorreadonly'] = 'Niet in staat om de bewerking uit te voeren. Map is alleen-lezen.';
+$messages['errornoperm'] = 'Niet in staat om de bewerking uit te voeren. Toestemming geweigerd.';
 $messages['invalidrequest'] = 'Ongeldige aanvraag! Er zijn geen gegevens opgeslagen.';
-$messages['nomessagesfound'] = 'Geen berichten gevonden in deze mailbox';
+$messages['nomessagesfound'] = 'Geen berichten gevonden in deze mailbox.';
 $messages['loggedout'] = 'Succesvol uitgelogd. Tot ziens!';
-$messages['mailboxempty'] = 'Mailbox is leeg';
+$messages['mailboxempty'] = 'Mailbox is leeg.';
 $messages['loading'] = 'Laden...';
-$messages['uploading'] = 'Bezig met uploaden...';
+$messages['uploading'] = 'Bestand wordt geüpload...';
+$messages['uploadingmany'] = 'Bestanden worden geüpload...';
 $messages['loadingdata'] = 'Laden van data...';
 $messages['checkingmail'] = 'Controleren op nieuwe berichten...';
 $messages['sendingmessage'] = 'Bezig met bericht versturen...';
-$messages['messagesent'] = 'Bericht succesvol verstuurd';
+$messages['messagesent'] = 'Bericht succesvol verstuurd.';
 $messages['savingmessage'] = 'Bezig met bericht opslaan...';
-$messages['messagesaved'] = 'Bericht bewaard als concept';
-$messages['successfullysaved'] = 'Succesvol opgeslagen';
-$messages['addedsuccessfully'] = 'Contact is succesvol toegevoegd aan het adresboek';
-$messages['contactexists'] = 'Er bestaat al een contactpersoon met dit e-mailadres';
+$messages['messagesaved'] = 'Bericht bewaard als concept.';
+$messages['successfullysaved'] = 'Succesvol opgeslagen.';
+$messages['addedsuccessfully'] = 'Contact is succesvol toegevoegd aan het adresboek.';
+$messages['contactexists'] = 'Er bestaat al een contactpersoon met dit e-mailadres.';
+$messages['contactnameexists'] = 'Er bestaat al een contactpersoon met deze naam.';
 $messages['blockedimages'] = 'Uit privacyoverwegingen zijn niet bijgevoegde afbeeldingen geblokkeerd in dit bericht.';
 $messages['encryptedmessage'] = 'Dit is een gecodeerd bericht en kan niet weergegeven worden. Excuses!';
-$messages['nocontactsfound'] = 'Geen contactpersonen gevonden';
-$messages['contactnotfound'] = 'Contactpersoon niet gevonden';
-$messages['sendingfailed'] = 'Verzenden van bericht is mislukt';
+$messages['nocontactsfound'] = 'Geen contactpersonen gevonden.';
+$messages['contactnotfound'] = 'Contactpersoon niet gevonden.';
+$messages['contactsearchonly'] = 'Voer enkele zoektermen in om contactpersonen te zoeken.';
+$messages['sendingfailed'] = 'Verzenden van bericht is mislukt.';
 $messages['senttooquickly'] = 'Je moet $sec seconden wachten om het bericht te versturen.';
-$messages['errorsavingsent'] = 'Er is een fout opgetreden tijdens het opslaan van het verzonden bericht';
-$messages['errorsaving'] = 'Er is een fout opgetreden tijdens het opslaan';
-$messages['errormoving'] = 'Kan het bericht niet verplaatsen';
-$messages['errorcopying'] = 'Kan de bericht(en) niet kopiëren';
-$messages['errordeleting'] = 'Kan het bericht niet verwijderen';
-$messages['errormarking'] = 'Kon bericht niet markeren';
+$messages['errorsavingsent'] = 'Er is een fout opgetreden tijdens het opslaan van het verzonden bericht.';
+$messages['errorsaving'] = 'Er is een fout opgetreden tijdens het opslaan.';
+$messages['errormoving'] = 'Kan het bericht niet verplaatsen.';
+$messages['errorcopying'] = 'Kan de bericht(en) niet kopiëren.';
+$messages['errordeleting'] = 'Kan het bericht niet verwijderen.';
+$messages['errormarking'] = 'Kon bericht niet markeren.';
 $messages['deletecontactconfirm'] = 'Weet u zeker dat u de geselecteerde contactperso(o)n/en wilt verwijderen?';
+$messages['deletegroupconfirm'] = 'Weet u zeker dat u de geselecteerde groep wilt verwijderen?';
 $messages['deletemessagesconfirm'] = 'Weet u zeker dat u de geselecteerde bericht(en) wilt verwijderen?';
 $messages['deletefolderconfirm'] = 'Weet u zeker dat u deze map wilt verwijderen?';
 $messages['purgefolderconfirm'] = 'Weet u zeker dat u alle berichten in deze map wilt verwijderen?';
-$messages['folderdeleting'] = 'Map verwijderen...';
-$messages['foldermoving'] = 'Map verplaatsen...';
-$messages['foldersubscribing'] = 'Abonneren map...';
-$messages['folderunsubscribing'] = 'Afmelding map ...';
-$messages['formincomplete'] = 'Het formulier was niet volledig ingevuld';
-$messages['noemailwarning'] = 'Geef een geldig e-mailadres op';
-$messages['nonamewarning'] = 'Vul een naam in';
-$messages['nopagesizewarning'] = 'Geef een paginagrootte op';
-$messages['nosenderwarning'] = 'Geef een e-mailadres van de afzender op';
-$messages['norecipientwarning'] = 'Geef tenminste één ontvanger op';
+$messages['contactdeleting'] = 'Bezig met verwijderen één of meer contactpersonen...';
+$messages['groupdeleting'] = 'Groep wordt verwijderd...';
+$messages['folderdeleting'] = 'Map wordt verwijderd...';
+$messages['foldermoving'] = 'Map wordt verplaatst...';
+$messages['foldersubscribing'] = 'Bezig met abonneren op map...';
+$messages['folderunsubscribing'] = 'Abonnement op map wordt opgezegd...';
+$messages['formincomplete'] = 'Het formulier was niet volledig ingevuld.';
+$messages['noemailwarning'] = 'Geef een geldig e-mailadres op.';
+$messages['nonamewarning'] = 'Vul een naam in.';
+$messages['nopagesizewarning'] = 'Geef een paginagrootte op.';
+$messages['nosenderwarning'] = 'Geef een e-mailadres van de afzender op.';
+$messages['norecipientwarning'] = 'Geef tenminste één ontvanger op.';
 $messages['nosubjectwarning'] = 'Het \'Onderwerp\' veld is leeg. Wilt u nu alsnog een onderwerp opgeven?';
 $messages['nobodywarning'] = 'Dit bericht zonder inhoud versturen?';
 $messages['notsentwarning'] = 'Bericht is niet verstuurd. Weet u zeker dat u het niet wilt bewaren?';
-$messages['noldapserver'] = 'Geef een LDAP adresboek server op om te doorzoeken';
-$messages['nocontactsreturned'] = 'Er zijn geen contactpersonen gevonden';
-$messages['nosearchname'] = 'Geef de naam of e-mail op van de contactpersoon';
+$messages['noldapserver'] = 'Geef een LDAP adresboek server op om te doorzoeken.';
+$messages['nosearchname'] = 'Geef de naam of e-mail op van de contactpersoon.';
 $messages['notuploadedwarning'] = 'Nog niet alle bijlagen zijn geüpload. Wacht even of annuleer de upload.';
-$messages['searchsuccessful'] = '$nr berichten gevonden';
-$messages['searchnomatch'] = 'Zoekopdracht heeft geen resultaten opgeleverd';
+$messages['searchsuccessful'] = '$nr berichten gevonden.';
+$messages['contactsearchsuccessful'] = '$nr contactpersonen gevonden.';
+$messages['searchnomatch'] = 'Zoekopdracht heeft geen resultaten opgeleverd.';
 $messages['searching'] = 'Zoeken...';
 $messages['checking'] = 'Controleren...';
-$messages['nospellerrors'] = 'Geen spelfouten gevonden';
-$messages['folderdeleted'] = 'Map successvol verwijderd';
-$messages['foldersubscribed'] = 'Folder succesvol geabonneerd';
-$messages['folderunsubscribed'] = 'Folder succesvol uitgeschreven';
-$messages['folderpurged'] = 'Folder met succes geleegd';
-$messages['folderexpunged'] = 'Folder succesvol geleegd';
-$messages['deletedsuccessfully'] = 'Successvol verwijderd';
+$messages['nospellerrors'] = 'Geen spelfouten gevonden.';
+$messages['folderdeleted'] = 'Map successvol verwijderd.';
+$messages['foldersubscribed'] = 'Folder succesvol geabonneerd.';
+$messages['folderunsubscribed'] = 'Folder succesvol uitgeschreven.';
+$messages['folderpurged'] = 'Folder met succes geleegd.';
+$messages['folderexpunged'] = 'Folder succesvol geleegd.';
+$messages['deletedsuccessfully'] = 'Successvol verwijderd.';
 $messages['converting'] = 'Opmaak van bericht verwijderen...';
-$messages['messageopenerror'] = 'Kan het bericht niet van de server laden';
-$messages['fileuploaderror'] = 'Bestand uploaden mislukt';
-$messages['filesizeerror'] = 'Het bestand overschrijdt de maximale grootte van $size';
-$messages['copysuccess'] = '$nr adressen succesvol gekopieerd';
-$messages['copyerror'] = 'Kan geen adressen kopiëren';
-$messages['sourceisreadonly'] = 'Het adres is alleen-lezen';
-$messages['errorsavingcontact'] = 'Kan het ontvanger-adres niet opslaan';
+$messages['messageopenerror'] = 'Kan het bericht niet van de server laden.';
+$messages['fileuploaderror'] = 'Bestand uploaden mislukt.';
+$messages['filesizeerror'] = 'Het bestand overschrijdt de maximale grootte van $size.';
+$messages['copysuccess'] = '$nr adressen succesvol gekopieerd.';
+$messages['copyerror'] = 'Kan geen adressen kopiëren.';
+$messages['sourceisreadonly'] = 'Het adres is alleen-lezen.';
+$messages['errorsavingcontact'] = 'Kan het ontvanger-adres niet opslaan.';
 $messages['movingmessage'] = 'Bericht verplaatsen...';
 $messages['copyingmessage'] = 'Kopiëren bericht...';
+$messages['copyingcontact'] = 'Kopiëren contactpersonen...';
 $messages['deletingmessage'] = 'Verwijderen bericht(en)...';
 $messages['markingmessage'] = 'Markeren bericht(en)...';
-$messages['receiptsent'] = 'Ontvangstbevestiging is verstuurd';
-$messages['errorsendingreceipt'] = 'Kan de ontvangstbevestiging niet versturen';
-$messages['nodeletelastidentity'] = 'U kunt uw enige identiteit niet verwijderen';
-$messages['forbiddencharacter'] = 'De naam van de map bevat een karakter dat niet is toegestaan';
-$messages['selectimportfile'] = 'Selecteer een bestand om de uploaden';
-$messages['addresswriterror'] = 'Het geselecteerde adresboek is alleen lezen';
-$messages['contactaddedtogroup'] = 'Contact(en) met succes toegevoegd aan de groep';
-$messages['contactremovedfromgroup'] = 'Contact(en) met succes verwijderd aan de groep';
+$messages['addingmember'] = 'Contactpersonen worden toegevoegd aan de groep...';
+$messages['removingmember'] = 'Contactpersonen worden verwijderd uit de groep...';
+$messages['receiptsent'] = 'Ontvangstbevestiging is verstuurd.';
+$messages['errorsendingreceipt'] = 'Kan de ontvangstbevestiging niet versturen.';
+$messages['nodeletelastidentity'] = 'U kunt uw enige identiteit niet verwijderen.';
+$messages['forbiddencharacter'] = 'De naam van de map bevat een karakter dat niet is toegestaan.';
+$messages['selectimportfile'] = 'Selecteer een bestand om de uploaden.';
+$messages['addresswriterror'] = 'Het geselecteerde adresboek is alleen lezen.';
+$messages['contactaddedtogroup'] = 'Contact(en) met succes toegevoegd aan de groep.';
+$messages['contactremovedfromgroup'] = 'Contact(en) met succes verwijderd aan de groep.';
 $messages['importwait'] = 'Importeren, even geduld...';
 $messages['importerror'] = 'Importeren mislukt! Het verstuurde bestand is geen geldig vCard bestand.';
 $messages['importconfirm'] = '<b>Er zijn $inserted contactpersonen succesvol geïmporteerd, en $skipped bestaande overgeslagen</b>:<p><em>$names</em></p>';
 $messages['importconfirmskipped'] = '<b>$skipped bestaande contactpersonen overgeslagen</b>';
 $messages['opnotpermitted'] = 'Deze verrichting is niet toegestaan!';
-$messages['nofromaddress'] = 'Het e-mailadres mist in de geselecteerde identiteit';
+$messages['nofromaddress'] = 'Het e-mailadres mist in de geselecteerde identiteit.';
 $messages['editorwarning'] = 'Door het overschakelen naar de platte tekst editor gaat alle opmaak verloren. Weet je zeker dat je verder wil gaan?';
 $messages['httpreceivedencrypterror'] = 'Er is een fatale fout opgetreden. Neem direct contact op met uw systeembeheerder. <b>Uw bericht kon niet worden verzonden.</b>';
-$messages['smtpconnerror'] = 'SMTP Fout ($code): Verbinding met server mislukt';
-$messages['smtpautherror'] = 'SMTP Fout ($code): Autenticatie mislukt';
-$messages['smtpfromerror'] = 'SMTP Fout ($code): Kon afzender "$from" niet instellen ($msg)';
-$messages['smtptoerror'] = 'SMTP Fout ($code): Kon ontvanger "$to" niet toevoegen ($msg)';
-$messages['smtprecipientserror'] = 'SMTP Fout: Kon lijst van ontvangers niet verwerken';
-$messages['smtpdsnerror'] = 'SMTP Fout: Delivery Status Notifications worden niet ondersteund';
+$messages['smtpconnerror'] = 'SMTP Fout ($code): Verbinding met server mislukt.';
+$messages['smtpautherror'] = 'SMTP Fout ($code): Autenticatie mislukt.';
+$messages['smtpfromerror'] = 'SMTP Fout ($code): Kon afzender "$from" niet instellen ($msg).';
+$messages['smtptoerror'] = 'SMTP Fout ($code): Kon ontvanger "$to" niet toevoegen ($msg).';
+$messages['smtprecipientserror'] = 'SMTP Fout: Kon lijst van ontvangers niet verwerken.';
 $messages['smtperror'] = 'SMTP Fout: $msg';
 $messages['emailformaterror'] = 'Ongeldig e-mailadres: $email';
-$messages['toomanyrecipients'] = 'Te veel geadresseerden. Verminder de hoeveelheid geadresseerden tot $max';
-$messages['maxgroupmembersreached'] = 'Het aantal leden van de groep groter is dan de maximale van $max';
-$messages['internalerror'] = 'Er is een interne fout opgetreden. Probeer het nogmaals';
-$messages['contactdelerror'] = 'Kon contact(en) niet verwijderen';
-$messages['contactdeleted'] = 'Contact(en) succesvol verwijderd';
+$messages['toomanyrecipients'] = 'Te veel geadresseerden. Verminder de hoeveelheid geadresseerden tot $max.';
+$messages['maxgroupmembersreached'] = 'Het aantal leden van de groep groter is dan de maximale van $max.';
+$messages['internalerror'] = 'Er is een interne fout opgetreden. Probeer het nogmaals.';
+$messages['contactdelerror'] = 'Kon contact(en) niet verwijderen.';
+$messages['contactdeleted'] = 'Contact(en) succesvol verwijderd.';
 $messages['contactrestoreerror'] = 'Verwijderde contactpersonen konden niet hersteld worden';
-$messages['contactrestored'] = 'Contactpersonen succesvol hersteld';
-$messages['groupdeleted'] = 'Groep succesvol verwijderd';
-$messages['grouprenamed'] = 'Groep succesvol hernoemd';
-$messages['groupcreated'] = 'Groep succesvol aangemaakt';
-$messages['messagedeleted'] = 'Bericht(en) succesvol verwijderd';
-$messages['messagemoved'] = 'Bericht(en) succesvol verplaatst';
-$messages['messagecopied'] = 'Bericht(en) succesvol gekopieerd';
-$messages['messagemarked'] = 'Bericht(en) succesvol gemarkeerd';
-$messages['autocompletechars'] = 'Voer ten minste $min karakters in voor automatisch aanvullen';
+$messages['contactrestored'] = 'Contactpersonen succesvol hersteld.';
+$messages['groupdeleted'] = 'Groep succesvol verwijderd.';
+$messages['grouprenamed'] = 'Groep succesvol hernoemd.';
+$messages['groupcreated'] = 'Groep succesvol aangemaakt.';
+$messages['savedsearchdeleted'] = 'Opgeslagen zoekopdracht successvol verwijderd.';
+$messages['savedsearchdeleteerror'] = 'Opgeslagen zoekopdracht kon niet worden verwijderd.';
+$messages['savedsearchcreated'] = 'Zoekopdracht successvol opgeslagen.';
+$messages['savedsearchcreateerror'] = 'Zoekopdracht kon niet worden opgeslagen.';
+$messages['messagedeleted'] = 'Bericht(en) succesvol verwijderd.';
+$messages['messagemoved'] = 'Bericht(en) succesvol verplaatst.';
+$messages['messagecopied'] = 'Bericht(en) succesvol gekopieerd.';
+$messages['messagemarked'] = 'Bericht(en) succesvol gemarkeerd.';
+$messages['autocompletechars'] = 'Voer ten minste $min karakters in voor automatisch aanvullen.';
+$messages['autocompletemore'] = 'Meerdere resultaten gevonden. Verfijn uw zoekopdracht.';
 $messages['namecannotbeempty'] = 'Naam kan niet leeg zijn';
 $messages['nametoolong'] = 'Naam is te lang';
 $messages['folderupdated'] = 'Map succesvol bijgewerkt';
 $messages['foldercreated'] = 'Map succesvol aangemaakt';
 $messages['invalidimageformat'] = 'Geen geldig afbeeldings formaat';
 $messages['mispellingsfound'] = 'Spelfouten gedetecteerd in bericht';
-$messages['itemsdeleted'] = '$num items zijn verwijderd';
+$messages['parentnotwritable'] = 'U heeft niet voldoende rechten een map te maken / verplaatsen in deze map.';
 
 ?>
index a4e6d31eaa20ad86aba73941e9de17d0ef2cb749..9146517fb93808648f6c1bd8b428a71ed6edf4fa 100644 (file)
@@ -14,7 +14,7 @@
 | Update: Odin Omdal Hørthe <odin.omdal@gmail.com>                      |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 4410 2011-01-12 18:25:02Z thomasb $
+@version $Id: labels.inc 5290 2011-09-28 17:09:50Z thomasb $
 
 */
 
@@ -39,11 +39,11 @@ $labels['to'] = 'Mottakar';
 $labels['cc'] = 'Kopi';
 $labels['bcc'] = 'Blindkopi';
 $labels['replyto'] = 'Svar-til';
+$labels['followupto'] = 'Oppfylging-til';
 $labels['date'] = 'Dato';
 $labels['size'] = 'Storleik';
 $labels['priority'] = 'Prioritet';
 $labels['organization'] = 'Organisasjon';
-$labels['reply-to'] = 'Svar-til';
 $labels['mailboxlist'] = 'Katalogar';
 $labels['messagesfromto'] = 'Meldingar $from til $to av $count';
 $labels['threadsfromto'] = 'Trådar $from til $to ($count stk)';
@@ -98,18 +98,18 @@ $labels['checkmail'] = 'Sjå etter nye meldingar';
 $labels['writenewmessage'] = 'Skriv ei ny melding';
 $labels['replytomessage'] = 'Svar på meldinga';
 $labels['replytoallmessage'] = 'Svar til sendar og alle mottakarar';
+$labels['replyall'] = 'Svar til alle';
+$labels['replylist'] = 'Svar til liste';
+$labels['forwardinline'] = 'Send vidare innebygd';
+$labels['forwardattachment'] = 'Send vidare som vedlegg';
 $labels['forwardmessage'] = 'Vidaresend meldinga';
 $labels['deletemessage'] = 'Slett melding';
 $labels['movemessagetotrash'] = 'Flytt melding til «sletta element»';
 $labels['printmessage'] = 'Skriv ut meldinga';
 $labels['previousmessage'] = 'Vis føregåande melding';
-$labels['previousmessages'] = 'Vis føregåande side med meldingar';
 $labels['firstmessage'] = 'Vis den fyrste meldinga';
-$labels['firstmessages'] = 'Vis fyrste side med meldingar';
 $labels['nextmessage'] = 'Vis den neste meldinga';
-$labels['nextmessages'] = 'Vis neste side med med meldingar';
 $labels['lastmessage'] = 'Vis den siste meldinga';
-$labels['lastmessages'] = 'Vis den siste sida med meldingar';
 $labels['backtolist'] = 'Tilbake til meldingslista';
 $labels['viewsource'] = 'Vis kjelde';
 $labels['markmessages'] = 'Marker meldingar';
@@ -117,7 +117,7 @@ $labels['markread'] = 'Som lese';
 $labels['markunread'] = 'Som ulese';
 $labels['markflagged'] = 'Som flagga';
 $labels['markunflagged'] = 'Som uflagga';
-$labels['messageactions'] = 'Fleire handlingar...';
+$labels['moreactions'] = 'Fleire handlingar';
 $labels['select'] = 'Vel';
 $labels['all'] = 'Alle';
 $labels['none'] = 'Ingen';
@@ -149,9 +149,9 @@ $labels['listcolumns'] = 'Vis kolonner';
 $labels['listsorting'] = 'Sorteringskolonne';
 $labels['listorder'] = 'Sorteringsveg';
 $labels['listmode'] = 'Listevisningmodus';
+$labels['folderactions'] = 'Mappehandlingar';
 $labels['compact'] = 'Kompakt';
 $labels['empty'] = 'Tom';
-$labels['purge'] = 'Reinska opp';
 $labels['quota'] = 'Plassbruk';
 $labels['unknown'] = 'ukjend';
 $labels['unlimited'] = 'ubegrensa';
@@ -169,12 +169,18 @@ $labels['addattachment'] = 'Legg ved ei fil';
 $labels['charset'] = 'Teiknkoding';
 $labels['editortype'] = 'Redigeringstype';
 $labels['returnreceipt'] = 'Epost-er-lest-kvittering';
+$labels['dsn'] = 'Leveringsstatus';
+$labels['mailreplyintro'] = 'Den $date skreiv $sender:';
+$labels['originalmessage'] = 'Opphavleg melding';
+$labels['editidents'] = 'Redigere identitetar';
 $labels['checkspelling'] = 'Stavekontroll';
-$labels['resumeediting'] = 'Fortsett redigering';
+$labels['resumeediting'] = 'Hald fram redigering';
 $labels['revertto'] = 'Tilbakestill til';
 $labels['attachments'] = 'Vedlegg';
 $labels['upload'] = 'Last opp';
+$labels['uploadprogress'] = '$percent ($current frå $total)';
 $labels['close'] = 'Steng';
+$labels['messageoptions'] = 'Meldingsalternativ';
 $labels['low'] = 'Låg';
 $labels['lowest'] = 'Lågast';
 $labels['normal'] = 'Normal';
@@ -191,6 +197,7 @@ $labels['maxuploadsize'] = 'Maksimalt tillatte storleik på filar er $size';
 $labels['addcc'] = 'Legg til kopi';
 $labels['addbcc'] = 'Legg til blindkopi';
 $labels['addreplyto'] = 'Legg til svaradressa';
+$labels['addfollowupto'] = 'Legg til oppfylgjing';
 $labels['mdnrequest'] = 'Sendaren av denne meldinga ynskjer å få kvittering når du har lest meldinga. Ynskjer du å senda denne kvitteringa?';
 $labels['receiptread'] = 'Svarkvittering (eposten-er-lest-kvittering)';
 $labels['yourmessage'] = 'Dette er ein retur-kvittering for meldinga di.';
@@ -198,13 +205,55 @@ $labels['receiptnote'] = 'Hugs: Denne kvitteringa seier berre at meldinga di var
 $labels['name'] = 'Visingsnamn';
 $labels['firstname'] = 'Førenamn';
 $labels['surname'] = 'Etternamn';
+$labels['middlename'] = 'Mellomnamn';
+$labels['nickname'] = 'Kallenamn';
+$labels['jobtitle'] = 'Jobbtittel';
+$labels['department'] = 'Avdeling';
+$labels['gender'] = 'Kjønn';
+$labels['maidenname'] = 'Pikenamn';
 $labels['email'] = 'E-post';
+$labels['phone'] = 'Telefon';
+$labels['address'] = 'Adresse';
+$labels['street'] = 'Gate';
+$labels['locality'] = 'By';
+$labels['zipcode'] = 'Postnummer';
+$labels['country'] = 'Land';
+$labels['birthday'] = 'Fødselsdag';
+$labels['website'] = 'Internettside';
+$labels['notes'] = 'Notatar';
+$labels['male'] = 'Mann';
+$labels['female'] = 'Kvinne';
+$labels['assistant'] = 'Assistent';
+$labels['allfields'] = 'Alle felt';
+$labels['search'] = 'Søk';
+$labels['advsearch'] = 'Avansert søk';
+$labels['other'] = 'Andre';
+$labels['typehome'] = 'Heim';
+$labels['typework'] = 'Jobb';
+$labels['typeother'] = 'Andre';
+$labels['typemobile'] = 'Mobil';
+$labels['typemain'] = 'Hovud';
+$labels['typehomefax'] = 'Heimefax';
+$labels['typeworkfax'] = 'Jobbfax';
+$labels['typecar'] = 'Bil';
+$labels['typepager'] = 'Personsøkjar';
+$labels['typevideo'] = 'VIdeo';
+$labels['typeassistant'] = 'Assistent';
+$labels['typehomepage'] = 'Heimeside';
+$labels['typeblog'] = 'Blogg';
+$labels['typeprofile'] = 'Profil';
+$labels['addfield'] = 'Legg til felt...';
 $labels['addcontact'] = 'Legg til ny kontakt';
 $labels['editcontact'] = 'Redigere kontakt';
+$labels['contacts'] = 'Kontakter';
+$labels['personalinfo'] = 'Personleg informasjon';
 $labels['edit'] = 'Redigere';
 $labels['cancel'] = 'Avbryt';
 $labels['save'] = 'Lagre';
 $labels['delete'] = 'Slette';
+$labels['rename'] = 'Gi nytt namn';
+$labels['addphoto'] = 'Legg til';
+$labels['replacephoto'] = 'Erstatt';
 $labels['newcontact'] = 'Opprett nytt kontaktkort';
 $labels['deletecontact'] = 'Slett valde kontaktar';
 $labels['composeto'] = 'Ny melding til';
@@ -213,7 +262,8 @@ $labels['print'] = 'Skriv ut';
 $labels['export'] = 'Eksport';
 $labels['exportvcards'] = 'Eksporter kontaktar i vCard-format';
 $labels['newcontactgroup'] = 'Lag ny kontaktgruppe';
-$labels['groupactions'] = 'Handlingar for kontaktgruppar...';
+$labels['grouprename'] = 'Endre namn på gruppe';
+$labels['groupdelete'] = 'Slett gruppe';
 $labels['previouspage'] = 'Førre side';
 $labels['firstpage'] = 'Fyrste side';
 $labels['nextpage'] = 'Neste side';
@@ -221,9 +271,12 @@ $labels['lastpage'] = 'Siste side';
 $labels['group'] = 'Gruppe';
 $labels['groups'] = 'Gruppar';
 $labels['personaladrbook'] = 'Personlege adresser';
+$labels['searchsave'] = 'Lagre søk';
+$labels['searchdelete'] = 'Slett søk';
 $labels['import'] = 'Importer';
 $labels['importcontacts'] = 'Importer kontaktar';
 $labels['importfromfile'] = 'Importer frå fil:';
+$labels['importtarget'] = 'Legg til kontakter i adresseboka';
 $labels['importreplace'] = 'Byt ut heile adresseboka';
 $labels['importtext'] = 'Du kan lasta opp kontaktar frå ei eksisterande adressebok.Me stør innlasting frå vCard-formatet.';
 $labels['done'] = 'Ferdig';
@@ -239,6 +292,8 @@ $labels['edititem'] = 'Redigere element';
 $labels['preferhtml'] = 'Føretrekk HTML';
 $labels['defaultcharset'] = 'Standard karaktersett (charset)';
 $labels['htmlmessage'] = 'HTML-melding';
+$labels['dateformat'] = 'Datoformat';
+$labels['timeformat'] = 'Tidsformat';
 $labels['prettydate'] = 'Pene datoar';
 $labels['setdefault'] = 'Set som standard';
 $labels['autodetect'] = 'Automatisk';
@@ -312,9 +367,16 @@ $labels['create'] = 'Opprette';
 $labels['createfolder'] = 'Opprett ny mappe';
 $labels['managefolders'] = 'Mappehandsaming';
 $labels['specialfolders'] = 'Spesialmapper';
+$labels['getfoldersize'] = 'Klikk for å få mappestørrelse';
+$labels['changesubscription'] = 'Klikk for å endre abonnement';
+$labels['foldertype'] = 'Mappetype';
+$labels['personalfolder'] = 'Privat mappe';
+$labels['otherfolder'] = 'Annan brukar si mappe';
+$labels['sharedfolder'] = 'Offentleg mappe';
 $labels['sortby'] = 'Sorter etter';
 $labels['sortasc'] = 'Sorter stigande';
 $labels['sortdesc'] = 'Sorter søkkjande';
+$labels['undo'] = 'Gjer om';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index 85cb94d0d5d7bbf0b458f559043fe604ee19243d..ed3c4c425333b64976f05ff20424d4b4225a768b 100644 (file)
@@ -18,7 +18,7 @@
 |         Aleksander Machniak <alec@alec.pl>                            |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5165 2011-09-05 08:49:04Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -38,8 +38,8 @@ $labels['sent'] = 'Wysłane';
 $labels['trash'] = 'Kosz';
 $labels['junk'] = 'Spam';
 $labels['subject'] = 'Temat';
-$labels['from'] = 'Nadawca';
-$labels['to'] = 'Odbiorca';
+$labels['from'] = 'Od';
+$labels['to'] = 'Do';
 $labels['cc'] = 'Kopia';
 $labels['bcc'] = 'Ukryta kopia';
 $labels['replyto'] = 'Odpowiedź do';
@@ -137,7 +137,7 @@ $labels['threaded'] = 'Powątkowane';
 $labels['autoexpand_threads'] = 'Rozwijaj wątki';
 $labels['do_expand'] = 'wszystkie';
 $labels['expand_only_unread'] = 'tylko nieprzeczytane';
-$labels['fromto'] = 'Nadawca/Odbiorca';
+$labels['fromto'] = 'Od/Do';
 $labels['flag'] = 'Flaga';
 $labels['attachment'] = 'Załącznik';
 $labels['nonesort'] = 'Brak';
@@ -419,5 +419,27 @@ $labels['importtarget'] = 'Dodaj nowe kontakty do książki adresowej:';
 $labels['grouprename'] = 'Zmień nazwę grupy';
 $labels['groupdelete'] = 'Usuń grupę';
 $labels['undo'] = 'Cofnij';
+$labels['mailreplyintro'] = 'W dniu $date, $sender napisał(a):';
+$labels['uploadprogress'] = '$percent ($current z $total)';
+$labels['originalmessage'] = 'Wiadomość oryginalna';
+$labels['moreactions'] = 'Więcej akcji...';
+$labels['searchsave'] = 'Zapisz wyszukiwanie';
+$labels['searchdelete'] = 'Usuń wyszukiwanie';
+$labels['spellcheckoptions'] = 'Opcje sprawdzania pisowni';
+$labels['spellcheckignoresyms'] = 'Ignoruj słowa zawierające symbole';
+$labels['spellcheckignorenums'] = 'Ignoruj słowa zawierające cyfry';
+$labels['spellcheckignorecaps'] = 'Ignoruj słowa pisane wielkimi literami';
+$labels['addtodict'] = 'Dodaj do słownika';
+$labels['dateformat'] = 'Format daty';
+$labels['timeformat'] = 'Format czasu';
+$labels['isdraft'] = 'To jest kopia robocza wiadomości.';
+$labels['autocompletesingle'] = 'Nie pokazuj alternatywnych adresów przy autouzupełnianiu';
+$labels['plugin'] = 'Wtyczka';
+$labels['version'] = 'Wersja';
+$labels['source'] = 'Źródła';
+$labels['license'] = 'Licencja';
+$labels['about'] = 'O programie';
+$labels['support'] = 'Wsparcie techniczne';
+$labels['defaultfont'] = 'Czcionka wiadomości HTML';
 
 ?>
index 6593a80538a4ab0f110c722db090dd611e9ef196..6a509a6e3eca30484c53e39215497241f6ba06f0 100644 (file)
@@ -18,7 +18,7 @@
 |         Aleksander Machniak <alec@alec.pl>                            |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5003 2011-08-02 09:22:02Z alec $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -117,7 +117,6 @@ $messages['smtpautherror'] = 'Błąd SMTP ($code): Uwierzytelnianie nie powiodł
 $messages['smtpfromerror'] = 'Błąd SMTP ($code): Nie można ustawić nadawcy "$from" ($msg).';
 $messages['smtptoerror'] = 'Błąd SMTP ($code): Nie można dodać odbiorcy "$to" ($msg).';
 $messages['smtprecipientserror'] = 'Błąd SMTP: Parsowanie listy odbiorców nie powiodło się.';
-$messages['smtpdsnerror'] = 'Błąd SMTP: Statusy dostarczenia (DSN) nie są obsługiwane przez serwer.';
 $messages['smtperror'] = 'Błąd SMTP: $msg';
 $messages['emailformaterror'] = 'Błędny adres e-mail: $email';
 $messages['toomanyrecipients'] = 'Zbyt wielu odbiorców. Zmniejsz ich liczbę do $max.';
@@ -150,5 +149,20 @@ $messages['invalidimageformat'] = 'Niepoprawny format obrazka.';
 $messages['mispellingsfound'] = 'Wykryto błędy pisowni w tej wiadomości.';
 $messages['contactrestoreerror'] = 'Przywracanie kontaktów nie powiodło się.';
 $messages['contactrestored'] = 'Kontakt(y) zostały przywrócone.';
+$messages['dberror'] = 'Błąd bazy danych!';
+$messages['contactnameexists'] = 'Kontakt z podaną nazwą już istnieje!';
+$messages['copyingcontact'] = 'Kopiowanie kontaktów...';
+$messages['addingmember'] = 'Dodawanie kontaktów do grupy...';
+$messages['removingmember'] = 'Usuwanie kontaktów z grupy...';
+$messages['parentnotwritable'] = 'Nie można utworzyć/przenieść folderu w wybrane miejsce. Brak upranień.';
+$messages['contactsearchsuccessful'] = 'Znaleziono $nr kontaktów.';
+$messages['savedsearchdeleted'] = 'Wyszukiwanie usunięto pomyślnie.';
+$messages['savedsearchdeleteerror'] = 'Nie można usunąć wyszukiwania.';
+$messages['savedsearchcreated'] = 'Wyszukiwanie zapisano pomyślnie.';
+$messages['savedsearchcreateerror'] = 'Nie można zapisać wyszukiwania.';
+$messages['contactsearchonly'] = 'Użyj wyszukiwarki aby wyświetlić kontakty.';
+$messages['contactdeleting'] = 'Usuwanie kontaktów...';
+$messages['autocompletemore'] = 'Znaleziono więcej pasujących wpisów. Wprowadź więcej znaków.';
+$messages['messagetoobig'] = 'Ta część wiadomości jest zbyt duża aby ją przetworzyć.';
 
 ?>
index 0255286723c2e27b70b5650608a619ed4859944f..6a2d9db8abc594529197229f87fd92b29307dd72 100644 (file)
@@ -18,7 +18,7 @@
 |         Jarbas Peixoto Junior <jarbas.peixoto@gmail.com>              |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: labels.inc 5326 2011-10-10 09:39:58Z netbit $
 
 */
 
@@ -122,7 +122,7 @@ $labels['markread'] = 'como lidas';
 $labels['markunread'] = 'como não lidas';
 $labels['markflagged'] = 'como sinalizadas';
 $labels['markunflagged'] = 'como não sinalizadas';
-$labels['messageactions'] = 'Mais ações...';
+$labels['moreactions'] = 'Mais ações...';
 $labels['select'] = 'Selecionar';
 $labels['all'] = 'Todas';
 $labels['none'] = 'Nenhuma';
@@ -175,6 +175,8 @@ $labels['charset'] = 'Codificação';
 $labels['editortype'] = 'Tipo de editor';
 $labels['returnreceipt'] = 'Solicitar confirmação de recebimento';
 $labels['dsn'] = 'Recibo de entrega';
+$labels['mailreplyintro'] = 'Em $date, $sender escreveu:';
+$labels['originalmessage'] = 'Mensagem original';
 $labels['editidents'] = 'Editar identidades';
 $labels['checkspelling'] = 'Verificar ortografia';
 $labels['resumeediting'] = 'Continuar a edição';
@@ -222,7 +224,7 @@ $labels['address'] = 'Endereço';
 $labels['street'] = 'Rua';
 $labels['locality'] = 'Cidade';
 $labels['zipcode'] = 'CEP';
-$labels['region'] = 'Região';
+$labels['region'] = 'Estado';
 $labels['country'] = 'País';
 $labels['birthday'] = 'Nascimento';
 $labels['anniversary'] = 'Aniversário';
@@ -249,6 +251,9 @@ $labels['typecar'] = 'Carro';
 $labels['typepager'] = 'Pager';
 $labels['typevideo'] = 'Video';
 $labels['typeassistant'] = 'Assistente';
+$labels['typehomepage'] = 'Página pessoal';
+$labels['typeblog'] = 'Blog';
+$labels['typeprofile'] = 'Perfil';
 $labels['addfield'] = 'Adicionar campo...';
 $labels['addcontact'] = 'Incluir novo contato';
 $labels['editcontact'] = 'Editar contato';
@@ -270,7 +275,6 @@ $labels['print'] = 'Imprimir';
 $labels['export'] = 'Exportar';
 $labels['exportvcards'] = 'Exportar os contatos em formato vCard';
 $labels['newcontactgroup'] = 'Criar novo grupo de contatos';
-$labels['groupactions'] = 'Ações para grupos de contatos...';
 $labels['grouprename'] = 'Renomear grupo';
 $labels['groupdelete'] = 'Excluir grupo';
 $labels['previouspage'] = 'Página Anterior';
@@ -280,6 +284,8 @@ $labels['lastpage'] = 'Última Página';
 $labels['group'] = 'Grupo';
 $labels['groups'] = 'Grupos';
 $labels['personaladrbook'] = 'Endereços pessoais';
+$labels['searchsave'] = 'Salvar pesquisa';
+$labels['searchdelete'] = 'Excluir pesquisa';
 $labels['import'] = 'Importar';
 $labels['importcontacts'] = 'Importar contatos';
 $labels['importfromfile'] = 'Importar de arquivo:';
@@ -299,6 +305,8 @@ $labels['edititem'] = 'Editar item';
 $labels['preferhtml'] = 'Exibir formatação HTML em mensagens';
 $labels['defaultcharset'] = 'Conjunto padrão de caracteres';
 $labels['htmlmessage'] = 'Mensagem HTML';
+$labels['dateformat'] = 'Formato da data';
+$labels['timeformat'] = 'Formato da hora';
 $labels['prettydate'] = 'Exibir datas amigáveis';
 $labels['setdefault'] = 'Padrão';
 $labels['autodetect'] = 'Automático';
@@ -371,6 +379,11 @@ $labels['reqdsn'] = 'Sempre pedir confirmação de entrega';
 $labels['replysamefolder'] = 'Guardar as respostas na mesma pasta da mensagem original';
 $labels['defaultaddressbook'] = 'Adicionar novos contatos para o catálogo de endereços:';
 $labels['spellcheckbeforesend'] = 'Verificar ortografia antes de enviar uma mensagem';
+$labels['spellcheckoptions'] = 'Opções do Verificador de Ortografia';
+$labels['spellcheckignoresyms'] = 'Ignorar palavras com símbolos';
+$labels['spellcheckignorenums'] = 'Ignorar palavras com números';
+$labels['spellcheckignorecaps'] = 'Ignorar palavras em maiúsculo';
+$labels['addtodict'] = 'Adicionar ao dicionário';
 $labels['folder'] = 'Pasta';
 $labels['folders'] = 'Pastas';
 $labels['foldername'] = 'Nome da pasta';
index f0c99076b71d533f0a6630b48fe5cffda6ed2141..5f2df76416af6f95ae87b87820c9115f1ee9642a 100644 (file)
@@ -18,7 +18,7 @@
 |         Jarbas Peixoto Junior <jarbas.peixoto@gmail.com>              |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: messages.inc 5326 2011-10-10 09:39:58Z netbit $
 
 */
 
@@ -29,7 +29,7 @@ $messages['sessionerror'] = 'A sessão do seu navegador é inválida ou expirou'
 $messages['imaperror'] = 'Falha na conexão com o servidor IMAP';
 $messages['servererror'] = 'Erro no Servidor!';
 $messages['servererrormsg'] = 'Erro no Servidor: $msg';
-$messages['databaserror'] = 'Erro no banco de dados!';
+$messages['dberror'] = 'Erro no banco de dados!';
 $messages['errorreadonly'] = 'Não foi possível realizar a operação. Pasta somente leitura';
 $messages['errornoperm'] = 'Não foi possível realizar a operação. Acesso negado';
 $messages['invalidrequest'] = 'Requisição inválida! Nenhum dado foi salvo.';
@@ -46,24 +46,29 @@ $messages['messagesent'] = 'Mensagem enviada com sucesso';
 $messages['savingmessage'] = 'Salvando Mensagem...';
 $messages['messagesaved'] = 'Mensagem gravada como rascunho';
 $messages['successfullysaved'] = 'Gravado com sucesso';
-$messages['addedsuccessfully'] = 'Contato incluído com sucesso';
-$messages['contactexists'] = 'Já existe um contato com esse e-mail';
-$messages['blockedimages'] = 'Para proteger sua privacidade, as imagens desta mensagem foram bloqueadas';
+$messages['addedsuccessfully'] = 'Contato incluído com sucesso.';
+$messages['contactexists'] = 'Já existe um contato com esse mesmo e-mail.';
+$messages['contactnameexists'] = 'Já existe um contato com o mesmo nome.';
+$messages['blockedimages'] = 'Para proteger sua privacidade, as imagens desta mensagem foram bloqueadas.';
 $messages['encryptedmessage'] = 'Esta mensagem está criptografada e não pode ser exibida. Desculpe.';
-$messages['nocontactsfound'] = 'Nenhum contato foi encontrado';
-$messages['contactnotfound'] = 'O contato solicitado não foi encontrado';
-$messages['sendingfailed'] = 'Falha no envio da mensagem';
-$messages['senttooquickly'] = 'Aguarde $sec s para enviar a mensagem';
-$messages['errorsavingsent'] = 'Ocorreu um erro ao salvar a mensagem enviada';
-$messages['errorsaving'] = 'Ocorreu um erro ao salvar';
-$messages['errormoving'] = 'Não foi possível mover a mensagem';
-$messages['errorcopying'] = 'Não foi possível copiar a(s) mensagem(ns)';
-$messages['errordeleting'] = 'Não foi possível apagar a mensagem';
-$messages['errormarking'] = 'Não foi possível marcar a mensagem';
+$messages['nocontactsfound'] = 'Nenhum contato foi encontrado.';
+$messages['contactnotfound'] = 'O contato solicitado não foi encontrado.';
+$messages['contactsearchonly'] = 'Informe os termos de pesquisa para localizar os contatos';
+$messages['sendingfailed'] = 'Falha no envio da mensagem.';
+$messages['senttooquickly'] = 'Aguarde $sec s para enviar a mensagem.';
+$messages['errorsavingsent'] = 'Ocorreu um erro ao salvar a mensagem enviada.';
+$messages['errorsaving'] = 'Ocorreu um erro ao salvar.';
+$messages['errormoving'] = 'Não foi possível mover a mensagem.';
+$messages['errorcopying'] = 'Não foi possível copiar a(s) mensagem(ns).';
+$messages['errordeleting'] = 'Não foi possível apagar a mensagem.';
+$messages['errormarking'] = 'Não foi possível marcar a mensagem.';
 $messages['deletecontactconfirm'] = 'Deseja realmente excluir o(s) contato(s) selecionado(s)?';
+$messages['deletegroupconfirm'] = 'Deseja realmente excluir o grupo selecionado?';
 $messages['deletemessagesconfirm'] = 'Deseja realmente excluir a(s) mensagem(s) selecionada(s)?';
 $messages['deletefolderconfirm'] = 'Deseja realmente excluir esta pasta?';
 $messages['purgefolderconfirm'] = 'Deseja realmente excluir todas mensagens desta pasta?';
+$messages['contactdeleting'] = 'Apagando contato(s)...';
+$messages['groupdeleting'] = 'Excluindo grupo...';
 $messages['folderdeleting'] = 'Apagando pasta...';
 $messages['foldermoving'] = 'Movendo pasta...';
 $messages['foldersubscribing'] = 'Ativando pasta...';
@@ -78,10 +83,10 @@ $messages['nosubjectwarning'] = 'O campo "Assunto" não foi preenchido. Deseja i
 $messages['nobodywarning'] = 'Enviar a mensagem sem texto?';
 $messages['notsentwarning'] = 'A mensagem não foi enviada, deseja excluí-la?';
 $messages['noldapserver'] = 'Por favor, selecione um servidor LDAP para a pesquisa';
-$messages['nocontactsreturned'] = 'Nenhum contato foi encontrado';
 $messages['nosearchname'] = 'Por favor, informe o nome do contado ou seu endereço de e-mail';
 $messages['notuploadedwarning'] = 'Há anexos ainda não enviados. Aguarde ou cancele o envio.';
-$messages['searchsuccessful'] = '$nr mensagens encontradas';
+$messages['searchsuccessful'] = '$nr mensagens encontradas.';
+$messages['contactsearchsuccessful'] = '$nr contatos encontrados.';
 $messages['searchnomatch'] = 'A pesquisa não encontrou resultados';
 $messages['searching'] = 'Pesquisando...';
 $messages['checking'] = 'Verificando...';
@@ -102,8 +107,11 @@ $messages['sourceisreadonly'] = 'Esta fonte de endereço é somente leitura';
 $messages['errorsavingcontact'] = 'Não foi possível salvar o endereço de contato';
 $messages['movingmessage'] = 'Movendo mensagem...';
 $messages['copyingmessage'] = 'Copiando mensagem...';
+$messages['copyingcontact'] = 'Copiando contato(s)...';
 $messages['deletingmessage'] = 'Excluindo mensagem(s)...';
 $messages['markingmessage'] = 'Marcando mensagem(s)...';
+$messages['addingmember'] = 'Adicionando contato(s) para o grupo...';
+$messages['removingmember'] = 'Removendo contato(s) do grupo...';
 $messages['receiptsent'] = 'Confirmação de recebimento enviada com sucesso';
 $messages['errorsendingreceipt'] = 'Não foi possível enviar a confirmação de recebimento';
 $messages['nodeletelastidentity'] = 'Você não pode excluir a única identidade';
@@ -125,7 +133,6 @@ $messages['smtpautherror'] = 'Erro SMTP ($code): Falha na autenticação';
 $messages['smtpfromerror'] = 'Erro SMTP ($code): Falha ao definir o remetente "$from" ($msg)';
 $messages['smtptoerror'] = 'Erro SMTP ($code): Falha ao adicionar o destinatário "$to" ($msg)';
 $messages['smtprecipientserror'] = 'Erro SMTP: Não é possível processar a lista destinatários';
-$messages['smtpdsnerror'] = 'Erro SMTP: Sem suporte para Confirmação de Entrega (DSN)';
 $messages['smtperror'] = 'Erro SMTP: $msg';
 $messages['emailformaterror'] = 'Endereço de e-mail inválido: $email';
 $messages['toomanyrecipients'] = 'Muitos destinatários. Reduza o número de destinatários para $max.';
@@ -138,16 +145,22 @@ $messages['contactrestored'] = 'Contato(s) recuperado(s) com sucesso.';
 $messages['groupdeleted'] = 'Grupo excluído com sucesso';
 $messages['grouprenamed'] = 'Grupo renomeado com sucesso';
 $messages['groupcreated'] = 'Grupo criado com sucesso';
+$messages['savedsearchdeleted'] = 'Pesquisa excluída com sucesso.';
+$messages['savedsearchdeleteerror'] = 'Não foi possível excluir a pesquisa.';
+$messages['savedsearchcreated'] = 'Pesquisa criada com sucesso.';
+$messages['savedsearchcreateerror'] = 'Não foi possível criar a pesquisa.';
 $messages['messagedeleted'] = 'Mensagem(s) excluída(s) com sucesso';
 $messages['messagemoved'] = 'Mensagem(s) movida(s) com sucesso';
 $messages['messagecopied'] = 'Mensagem(s) copiada(s) com sucesso';
 $messages['messagemarked'] = 'Mensagem(s) marcada(s) com sucesso';
 $messages['autocompletechars'] = 'Digite pelo menos $min caractere(s) para auto-completar';
+$messages['autocompletemore'] = 'Muitos registros encontrados. Por favor, digite mais caracteres.';
 $messages['namecannotbeempty'] = 'Nome não pode ser vazio';
 $messages['nametoolong'] = 'Nome é muito longo';
 $messages['folderupdated'] = 'Pasta atualizada com sucesso';
 $messages['foldercreated'] = 'Pasta criada com sucesso';
 $messages['invalidimageformat'] = 'Formato de imagem inválido.';
 $messages['mispellingsfound'] = 'Foram detectados erros de ortografia.';
+$messages['parentnotwritable'] = 'Sem permissão para criar/mover a pasta dentro da pasta selecionada.';
 
 ?>
index 9c047dd26900630b2244644f8146d39b95a504a0..e864c65a3dee2cea870dff24951bdc8d56d521de 100644 (file)
@@ -17,7 +17,7 @@
 |          Teotónio Ricardo <teotonio.ricardo@webtuga.pt>              |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -121,7 +121,7 @@ $labels['markread'] = 'Como lidas';
 $labels['markunread'] = 'Como não lidas';
 $labels['markflagged'] = 'Com sinalização';
 $labels['markunflagged'] = 'Sem sinalização';
-$labels['messageactions'] = 'Mais acções...';
+$labels['moreactions'] = 'Mais acções...';
 $labels['select'] = 'Seleccionar';
 $labels['all'] = 'Todas';
 $labels['none'] = 'Nenhuma';
@@ -193,6 +193,7 @@ $labels['highest'] = 'Mais alta';
 $labels['nosubject'] = '(sem assunto)';
 $labels['showimages'] = 'Exibir imagens';
 $labels['alwaysshow'] = 'Mostrar sempre imagens a partir de $sender';
+$labels['isdraft'] = 'Esta é uma mensagem de rascunho.';
 $labels['htmltoggle'] = 'HTML';
 $labels['plaintoggle'] = 'Texto simples';
 $labels['savesentmessagein'] = 'Guardar mensagem enviada em';
@@ -274,7 +275,6 @@ $labels['print'] = 'Imprimir';
 $labels['export'] = 'Exportar';
 $labels['exportvcards'] = 'Exportar contactos no formato vCard';
 $labels['newcontactgroup'] = 'Criar novo grupo de contactos';
-$labels['groupactions'] = 'Acções para grupo de contactos...';
 $labels['grouprename'] = 'Renomear grupo';
 $labels['groupdelete'] = 'Eliminar grupo';
 $labels['previouspage'] = 'Exibir página anterior';
@@ -284,6 +284,8 @@ $labels['lastpage'] = 'Exibir última página';
 $labels['group'] = 'Grupo';
 $labels['groups'] = 'Grupos';
 $labels['personaladrbook'] = 'Endereços pessoais';
+$labels['searchsave'] = 'Guardar pesquisa';
+$labels['searchdelete'] = 'Eliminar pesquisa';
 $labels['import'] = 'Importar';
 $labels['importcontacts'] = 'Importar contactos';
 $labels['importfromfile'] = 'Importar do ficheiro:';
@@ -292,6 +294,7 @@ $labels['importreplace'] = 'Substituir todo o livro de endereços';
 $labels['importtext'] = 'Pode submeter contactos a partir de um livro de endereços. Actualmente suportamos importação de endereços no formato vCard.';
 $labels['done'] = 'Terminado';
 $labels['settingsfor'] = 'Configurações para';
+$labels['about'] = 'Acerca';
 $labels['preferences'] = 'Preferências';
 $labels['userpreferences'] = 'Preferências do utilizador';
 $labels['editpreferences'] = 'Editar preferências do utilizador';
@@ -303,6 +306,8 @@ $labels['edititem'] = 'Editar item';
 $labels['preferhtml'] = 'Exibir mensagens em HTML';
 $labels['defaultcharset'] = 'Conjunto de caracteres predefinido';
 $labels['htmlmessage'] = 'Mensagem em HTML';
+$labels['dateformat'] = 'Formato da data';
+$labels['timeformat'] = 'Formato da hora';
 $labels['prettydate'] = 'Formatar datas';
 $labels['setdefault'] = 'Marcar como predefinido';
 $labels['autodetect'] = 'Auto';
@@ -374,7 +379,13 @@ $labels['reqmdn'] = 'Pedir sempre um recibo de leitura';
 $labels['reqdsn'] = 'Pedir sempre um recibo de entrega';
 $labels['replysamefolder'] = 'Guardar as respostas na mesma pasta da mensagem original';
 $labels['defaultaddressbook'] = 'Adicionar novo contacto ao livro de endereços selecionado';
+$labels['autocompletesingle'] = 'Ignorar endereços de email alternativos no preenchimento automático';
 $labels['spellcheckbeforesend'] = 'Verificar ortografia antes de enviar a mensagem';
+$labels['spellcheckoptions'] = 'Opções de verificação ortográfica';
+$labels['spellcheckignoresyms'] = 'Ignorar palavras com símbolos';
+$labels['spellcheckignorenums'] = 'Ignorar palavras com números';
+$labels['spellcheckignorecaps'] = 'Ignorar palavras em maiúsculas';
+$labels['addtodict'] = 'Adicionar ao dicionário';
 $labels['folder'] = 'Pasta';
 $labels['folders'] = 'Pastas';
 $labels['foldername'] = 'Nome da pasta';
@@ -399,6 +410,11 @@ $labels['sortby'] = 'Ordenar por';
 $labels['sortasc'] = 'Ordenação Ascendente';
 $labels['sortdesc'] = 'Ordenação Descendente';
 $labels['undo'] = 'Anular';
+$labels['plugin'] = 'Plugin';
+$labels['version'] = 'Versão';
+$labels['source'] = 'Fonte';
+$labels['license'] = 'Licença';
+$labels['support'] = 'Obter suporte';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index 65bea81614d45f41e6f0cb5387c552ddd8f1780d..ccf8791022ff5df3a717cbbf569e1f82e16b0ac2 100644 (file)
 +-----------------------------------------------------------------------+
 | Authors: Sergio Rocha <sergio.rocha@makeitsimple.pt>                  |
 |          João Vale <jpvale@gmail.com>                                |
-|         Nuno Costa <nuno@criacaoweb.net>                              |
-|         Teotónio Ricardo <teotonio.ricardo@webtuga.pt>               |
+|          Nuno Costa <nuno@criacaoweb.net>                              |
+|          Teotónio Ricardo <teotonio.ricardo@webtuga.pt>               |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -27,7 +27,7 @@ $messages['sessionerror'] = 'A sua sessão é inválida ou expirou';
 $messages['imaperror'] = 'A ligação ao servidor IMAP falhou';
 $messages['servererror'] = 'Erro do Servidor!';
 $messages['servererrormsg'] = 'Erro do Servidor: $msg';
-$messages['databaserror'] = 'Erro de base de dados!';
+$messages['dberror'] = 'Erro na base de dados!';
 $messages['errorreadonly'] = 'Não foi possível efectuar a operação. A pasta é só de leitura.';
 $messages['errornoperm'] = 'Não tem permissões para efectuar esta operação.';
 $messages['invalidrequest'] = 'Pedido inválido! Não foi guardada nenhuma informação.';
@@ -51,6 +51,7 @@ $messages['blockedimages'] = 'Para proteger a sua privacidade, as imagens desta
 $messages['encryptedmessage'] = 'Esta mensagem está encriptada e não pode ser exibida. Desculpe.';
 $messages['nocontactsfound'] = 'Nenhum contacto encontrado';
 $messages['contactnotfound'] = 'O contacto especificado não foi encontrado';
+$messages['contactsearchonly'] = 'Insira os termos de pesquisa para localizar os contactos';
 $messages['sendingfailed'] = 'Falha no envio da mensagem';
 $messages['senttooquickly'] = 'Por favor espere $sec seg(s). antes de enviar esta mensagem';
 $messages['errorsavingsent'] = 'Ocorreu um erro ao guardar a mensagem enviada';
@@ -64,6 +65,7 @@ $messages['deletegroupconfirm'] = 'Tem a certeza que pretende eliminar o grupo s
 $messages['deletemessagesconfirm'] = 'Deseja realmente eliminar a(s) mensagem(ns) selecionada(s)?';
 $messages['deletefolderconfirm'] = 'Deseja realmente eliminar esta pasta?';
 $messages['purgefolderconfirm'] = 'Deseja realmente eliminar todas mensagens desta pasta?';
+$messages['contactdeleting'] = 'A eliminar contacto(s)...';
 $messages['groupdeleting'] = 'A eliminar grupo...';
 $messages['folderdeleting'] = 'A eliminar pasta...';
 $messages['foldermoving'] = 'A mover pasta...';
@@ -79,10 +81,10 @@ $messages['nosubjectwarning'] = 'O campo "Assunto" não foi preenchido. Deseja i
 $messages['nobodywarning'] = 'Enviar a mensagem sem texto?';
 $messages['notsentwarning'] = 'A mensagem não foi enviada, deseja apagá-la?';
 $messages['noldapserver'] = 'Seleccione um servidor LDAP para a pesquisa';
-$messages['nocontactsreturned'] = 'Não foi encontrado nenhum contacto';
 $messages['nosearchname'] = 'Por favor, escreva o nome do contacto ou endereço de e-mail';
 $messages['notuploadedwarning'] = 'Nem todos os anexos foram ainda enviados. Por favor aguarde ou cancele o envio.';
 $messages['searchsuccessful'] = '$nr mensagens encontradas';
+$messages['contactsearchsuccessful'] = '$nr contactos encontrados.';
 $messages['searchnomatch'] = 'A pesquisa não devolveu resultados';
 $messages['searching'] = 'A pesquisar...';
 $messages['checking'] = 'A verificar...';
@@ -129,7 +131,6 @@ $messages['smtpautherror'] = 'Erro SMTP ($code): A autenticação falhou';
 $messages['smtpfromerror'] = 'Erro SMTP ($code): Falha ao definir o remetente "$from" ($msg)';
 $messages['smtptoerror'] = 'Erro SMTP ($code): Falha ao adicionar o destinatário: "$to" ($msg)';
 $messages['smtprecipientserror'] = 'Erro SMTP: Impossível analisar a lista de destinatários';
-$messages['smtpdsnerror'] = 'Erro SMTP: Não há suporte para recibo de entrega';
 $messages['smtperror'] = 'Erro SMTP: $msg';
 $messages['emailformaterror'] = 'Endereço de e-mail incorrecto: $email';
 $messages['toomanyrecipients'] = 'Demasiados destinatários. Reduza o numero de destinatários para o máximo de $max.';
@@ -142,11 +143,16 @@ $messages['contactrestored'] = 'Contacto(s) restaurado(s) com sucesso.';
 $messages['groupdeleted'] = 'Grupo eliminado com sucesso';
 $messages['grouprenamed'] = 'Grupo renomeado com sucesso';
 $messages['groupcreated'] = 'Grupo criado com sucesso';
+$messages['savedsearchdeleted'] = 'Pesquisa guardada eliminada com sucesso.';
+$messages['savedsearchdeleteerror'] = 'Não foi possível eliminar a pesquisa guardada.';
+$messages['savedsearchcreated'] = 'Pesquisa guardada criada com sucesso.';
+$messages['savedsearchcreateerror'] = 'Não foi possível criar a pesquisa guardada.';
 $messages['messagedeleted'] = 'Mensagens eliminadas com sucesso';
 $messages['messagemoved'] = 'Mensagens movidas com sucesso';
 $messages['messagecopied'] = 'Mensagens copiadas com sucesso';
 $messages['messagemarked'] = 'Mensagens marcadas com sucesso';
 $messages['autocompletechars'] = 'Indique pelo menos $min caracteres para auto completar';
+$messages['autocompletemore'] = 'Muitos registros encontrados. Por favor, insira mais caracteres.';
 $messages['namecannotbeempty'] = 'O nome não pode estar em branco';
 $messages['nametoolong'] = 'O nome é demasiado longo';
 $messages['folderupdated'] = 'Actualização da pasta concluída com sucesso';
@@ -154,5 +160,6 @@ $messages['foldercreated'] = 'Pasta criada com sucesso';
 $messages['invalidimageformat'] = 'Formato de imagem não suportado';
 $messages['mispellingsfound'] = 'Foram detectados erros de ortografia nesta mensagem';
 $messages['parentnotwritable'] = 'Não é possível criar/mover esta pasta para a pasta seleccionada. Acesso negado.';
+$messages['messagetoobig'] = 'A mensagem é demasiado grande para ser processada.';
 
 ?>
index d4649c2ab9950da3fc794a9c1ffc5530f31d7ab1..a89e530361c56adfd638f1626c9b983f63b729f3 100644 (file)
@@ -15,7 +15,7 @@
 | Support: www.roundcube.ru                                             |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5291 2011-09-28 17:10:45Z thomasb $
+@version $Id: labels.inc 5290 2011-09-28 17:09:50Z thomasb $
 
 */
 
index c6b2aab75352cd961911e5410c7f6a114bf81eb1..52e848403a710c8421a67544a8d98ad16067b47c 100644 (file)
@@ -14,7 +14,7 @@
 | Updates: Sergey Dukachev <iam@dukess.ru>                              |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5291 2011-09-28 17:10:45Z thomasb $
+@version $Id: messages.inc 5290 2011-09-28 17:09:50Z thomasb $
 
 */
 
index 367b23dc2cdf6a7c5a2e6550d7782921d9934790..367338991872e6faadcb187db718e4ee078f7f16 100644 (file)
@@ -17,7 +17,7 @@
 |         Martin Lacina        <martin@whistler.sk>                            |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: labels.inc 5585 2011-12-09 19:28:20Z alec $
 
 */
 
@@ -365,7 +365,7 @@ $labels['belowquote'] = 'nad citáciu';
 $labels['abovequote'] = 'pod citáciu';
 $labels['insertsignature'] = 'Vložit podpis';
 $labels['previewpanemarkread'] = 'Označiť zobrazenej správy ako prečítané';
-$labels['afternseconds'] = 'po $ sekundách';
+$labels['afternseconds'] = 'po $n sekundách';
 $labels['reqmdn'] = 'Vždy požadovať doručenku';
 $labels['reqdsn'] = 'Vždy vyžadovať potvrdenie o doručení správy';
 $labels['replysamefolder'] = 'Umietniť odpoveď do adresára, kde je umiestnená správa, na ktorú sa odpovedalo';
index 3365111b2b8d325d88f0f9853c06bc6002fbfb0e..ef5bbf403bf76e4a12a00bc0973428d3492877e4 100644 (file)
@@ -17,7 +17,7 @@
 |         Martin Lacina        <martin@whistler.sk>                            |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: messages.inc 5139 2011-08-28 09:47:15Z alec $
 
 */
 
@@ -28,7 +28,7 @@ $messages['sessionerror'] = 'Vaše prihlásenie je neplatné alebo vypršala jeh
 $messages['imaperror'] = 'Nepodarilo sa spojiť s IMAP serverom';
 $messages['servererror'] = 'Chyba servera!';
 $messages['servererrormsg'] = 'Chyba servera: $msg';
-$messages['databaserror'] = 'Databázová chyba!';
+$messages['dberror'] = 'Databázová chyba!';
 $messages['errorreadonly'] = 'Nemožno vykonať operáciu. Adresár je len na čítanie';
 $messages['errornoperm'] = 'Nemožno vykonať operáciu. Prístup odmietnutý';
 $messages['invalidrequest'] = 'Chybný požiadavek. Žiadne údaje neboli uložené.';
index ad437cb18829679fb50378269b3e4648f1376528..8a460f0b2531b80289d4f81e0e5ad8581bc18c59 100644 (file)
@@ -14,7 +14,7 @@
 |         Barbara Krasovec <barbarak@arnes.si>                          |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -118,7 +118,7 @@ $labels['markread'] = 'Kot prebrano';
 $labels['markunread'] = 'Kot neprebrano';
 $labels['markflagged'] = 'Kot označeno';
 $labels['markunflagged'] = 'Kot neoznačeno';
-$labels['messageactions'] = 'Več možnosti';
+$labels['moreactions'] = 'Več možnosti...';
 $labels['select'] = 'Izberi';
 $labels['all'] = 'Vse';
 $labels['none'] = 'Ničesar';
@@ -171,12 +171,15 @@ $labels['charset'] = 'Nabor znakov';
 $labels['editortype'] = 'Vrsta urejevalnika';
 $labels['returnreceipt'] = 'Potrdilo prejemnika';
 $labels['dsn'] = 'Status dostave sporočila';
+$labels['mailreplyintro'] = '$date, je $sender napisal';
+$labels['originalmessage'] = 'Izvorno sporočilo';
 $labels['editidents'] = 'Uredi identitete';
 $labels['checkspelling'] = 'Preglej pravopis';
 $labels['resumeediting'] = 'Nadaljuj z urejanjem';
 $labels['revertto'] = 'Razveljavi';
 $labels['attachments'] = 'Priponke';
 $labels['upload'] = 'Naloži';
+$labels['uploadprogress'] = '$percent ($current od $total)';
 $labels['close'] = 'Zapri';
 $labels['messageoptions'] = 'Možnosti';
 $labels['low'] = 'Nizka';
@@ -187,6 +190,7 @@ $labels['highest'] = 'Najvišja';
 $labels['nosubject'] = '(brez naslova)';
 $labels['showimages'] = 'Prikaži slike';
 $labels['alwaysshow'] = 'Vedno prikaži slike od $sender';
+$labels['isdraft'] = 'To je osnutek sporočila.';
 $labels['htmltoggle'] = 'Obogateno besedilo (HTML)';
 $labels['plaintoggle'] = 'Samo besedilo';
 $labels['savesentmessagein'] = 'Shrani poslana sporočila v';
@@ -245,6 +249,8 @@ $labels['typepager'] = 'Pozivnik';
 $labels['typevideo'] = 'Video';
 $labels['typeassistant'] = 'Pomoč';
 $labels['typehomepage'] = 'Domača stran';
+$labels['typeblog'] = 'Blog';
+$labels['typeprofile'] = 'Profil';
 $labels['addfield'] = 'Dodaj polje...';
 $labels['addcontact'] = 'Dodaj izbrane stike v imenik';
 $labels['editcontact'] = 'Uredi vizitko';
@@ -266,7 +272,6 @@ $labels['print'] = 'Natisni';
 $labels['export'] = 'Izvozi';
 $labels['exportvcards'] = 'Izvozi stike v formatu vCard';
 $labels['newcontactgroup'] = 'Ustvari novo skupino stikov';
-$labels['groupactions'] = 'Dejanja za skupine stikov...';
 $labels['grouprename'] = 'Preimenuj skupino';
 $labels['groupdelete'] = 'Izbriši skupino';
 $labels['previouspage'] = 'Prejšnja stran';
@@ -276,6 +281,8 @@ $labels['lastpage'] = 'Zadnja stran';
 $labels['group'] = 'Skupina';
 $labels['groups'] = 'Skupine';
 $labels['personaladrbook'] = 'Stiki';
+$labels['searchsave'] = 'Shrani iskanje';
+$labels['searchdelete'] = 'Izbriši iskanje';
 $labels['import'] = 'Uvozi';
 $labels['importcontacts'] = 'Uvozi stike';
 $labels['importfromfile'] = 'Uvozi iz datoteke:';
@@ -284,6 +291,7 @@ $labels['importreplace'] = 'Zamenjaj celoten imenik';
 $labels['importtext'] = 'Stike lahko prenesete iz obstoječega imenika<br/> Trenutno je podprto uvažanje naslovov v  formatu <a href="http://en.wikipedia.org/wiki/VCard">vCard</a>';
 $labels['done'] = 'Dokončano';
 $labels['settingsfor'] = 'Nastavitve za';
+$labels['about'] = 'Vizitka';
 $labels['preferences'] = 'Nastavitve';
 $labels['userpreferences'] = 'Uporabniške nastavitve';
 $labels['editpreferences'] = 'Uredi uporabniške nastavitve';
@@ -295,6 +303,8 @@ $labels['edititem'] = 'Uredi predmet';
 $labels['preferhtml'] = 'Prednostno HTML';
 $labels['defaultcharset'] = 'Privzeto kodiranje znakov';
 $labels['htmlmessage'] = 'HTML sporočilo';
+$labels['dateformat'] = 'Prikaz datuma';
+$labels['timeformat'] = 'Prikaz časa';
 $labels['prettydate'] = 'Bolj čitljivi datumi';
 $labels['setdefault'] = 'Nastavi privzeto';
 $labels['autodetect'] = 'Samodejno';
@@ -366,7 +376,13 @@ $labels['reqmdn'] = 'Vedno zahtevaj povratnico';
 $labels['reqdsn'] = 'Vedno zahtevaj obvestilo o statusu dostave';
 $labels['replysamefolder'] = 'Shrani odgovore na sporočilo v mapo izvornega sporočila';
 $labels['defaultaddressbook'] = 'Dodaj nove stike v izbran imenik';
+$labels['autocompletesingle'] = 'Preskoči nadomestni e-naslov pri samodejnem dokončanju';
 $labels['spellcheckbeforesend'] = 'Preveri črkovanje pred pošiljanjem sporočila';
+$labels['spellcheckoptions'] = 'Možnosti črkovanja';
+$labels['spellcheckignoresyms'] = 'Ne upoštevaj besed, ki vsebujejo simbole';
+$labels['spellcheckignorenums'] = 'Ne upoštevaj besed, ki vsebujejo številke';
+$labels['spellcheckignorecaps'] = 'Ne upoštevaj besed, ki vsebujejo samo velike črke';
+$labels['addtodict'] = 'Dodaj v slovar';
 $labels['folder'] = 'Mapa';
 $labels['folders'] = 'Mape';
 $labels['foldername'] = 'Ime mape';
@@ -390,6 +406,12 @@ $labels['sharedfolder'] = 'Javna mapa';
 $labels['sortby'] = 'Uredi po';
 $labels['sortasc'] = 'Uredi naraščajoče';
 $labels['sortdesc'] = 'Uredi padajoče';
+$labels['undo'] = 'Prekliči';
+$labels['plugin'] = 'Vtičnik';
+$labels['version'] = 'Različica';
+$labels['source'] = 'Vir';
+$labels['license'] = 'Licenca';
+$labels['support'] = 'Podpora';
 $labels['B'] = 'B';
 $labels['KB'] = 'KB';
 $labels['MB'] = 'MB';
index 996a0fd40590c5382e7d1f91a316f9432a049ade..1aa199ce392d3b08127af9a6f65343d02520ea68 100644 (file)
@@ -21,7 +21,7 @@ $messages['sessionerror'] = 'Seja je neveljavna ali je potekla.';
 $messages['imaperror'] = 'Povezava z IMAP strežnikom je spodletela.';
 $messages['servererror'] = 'Napaka strežnika!';
 $messages['servererrormsg'] = 'Napaka strežnikaČ $msg';
-$messages['databaserror'] = 'Napaka v bazi';
+$messages['dberror'] = 'Napaka v bazi';
 $messages['errorreadonly'] = 'Operacije ni bilo mogoče izvršiti. Mapa je na voljo samo za branje.';
 $messages['errornoperm'] = 'Operacije ni bilo mogoče izvršiti. Dostop zavrnjen.';
 $messages['invalidrequest'] = 'Neveljavna zahteva. Podatki niso bili shranjeni.';
@@ -40,10 +40,12 @@ $messages['messagesaved'] = 'Sporočilo je bilo shranjeno v Osnutke';
 $messages['successfullysaved'] = 'Uspešno shranjeno.';
 $messages['addedsuccessfully'] = 'Stik je bil uspešno dodan v imenik.';
 $messages['contactexists'] = 'Stik s tem elektronskim naslovom že obstaja.';
+$messages['contactnameexists'] = 'Stik s tem imenom že obstaja';
 $messages['blockedimages'] = 'Zaradi zaščite zasebnosti je prikaz slik v sporočilu onemogočen.';
 $messages['encryptedmessage'] = 'Sporočilo je šifrirano in ne more biti prikazano.';
 $messages['nocontactsfound'] = 'Ni stikov.';
 $messages['contactnotfound'] = 'Iskanega stika ni bilo mogoče najti.';
+$messages['contactsearchonly'] = 'Vpišite iskalni parameter za iskanje stika';
 $messages['sendingfailed'] = 'Sporočila ni bilo mogoče poslati.';
 $messages['senttooquickly'] = 'Počakajte $sec sekund in nato znova poskusite s pošiljanjem sporočila.';
 $messages['errorsavingsent'] = 'Pri shranjevanju poslanega sporočila je prišlo do napake.';
@@ -53,9 +55,12 @@ $messages['errorcopying'] = 'Sporočila ni bilo mogoče kopirati.';
 $messages['errordeleting'] = 'Sporočila ni bilo mogoče izbrisati.';
 $messages['errormarking'] = 'Sporočila ni bilo mogoče označiti.';
 $messages['deletecontactconfirm'] = 'Ste prepričani, da želite izbrisati označen(e) stik(e)?';
+$messages['deletegroupconfirm'] = 'Ste prepričani, da želite izbrisati izbrano skupino?';
 $messages['deletemessagesconfirm'] = 'Ste prepričani, da želite izbrisati označeno(a) sporočilo(a)?';
 $messages['deletefolderconfirm'] = 'Ste prepričani, da želite to mapo izbrisati?';
 $messages['purgefolderconfirm'] = 'Ste prepričani, da želite izbrisati vsa sporočila v mapi?';
+$messages['contactdeleting'] = 'Brisanje stika/ov...';
+$messages['groupdeleting'] = 'Brisanje skupine...';
 $messages['folderdeleting'] = 'Brisanje mape...';
 $messages['foldermoving'] = 'Premikanje mape...';
 $messages['foldersubscribing'] = 'Prijavljanje mape...';
@@ -70,10 +75,10 @@ $messages['nosubjectwarning'] = 'Polje "Zadeva" je prazno. Želite dodati tekst
 $messages['nobodywarning'] = 'Želite poslati sporočilo brez vsebine?';
 $messages['notsentwarning'] = 'Sporočilo ni bilo poslano. Želite zavreči to sporočilo?';
 $messages['noldapserver'] = 'Izberite LDAP strežnik, v katerem želite iskati?';
-$messages['nocontactsreturned'] = 'Nobenega stika ni bilo mogoče najti';
 $messages['nosearchname'] = 'Vnesite ime ali elektronski naslov stika';
 $messages['notuploadedwarning'] = 'Priponke se še nalagajo na strežnik. Počakajte ali prekinite prenos.';
 $messages['searchsuccessful'] = '$nr najdenih sporočil';
+$messages['contactsearchsuccessful'] = '$nr najdenih stikov.';
 $messages['searchnomatch'] = 'Ni zadetkov';
 $messages['searching'] = 'Iskanje...';
 $messages['checking'] = 'Preverjanje...';
@@ -94,8 +99,11 @@ $messages['sourceisreadonly'] = 'Ta naslov je na voljo samo za branje';
 $messages['errorsavingcontact'] = 'Stika ni bilo mogoče shraniti';
 $messages['movingmessage'] = 'Premikanje sporočila...';
 $messages['copyingmessage'] = 'Kopiranje sporočila...';
+$messages['copyingcontact'] = 'Kopiranje stika/ov';
 $messages['deletingmessage'] = 'Brisanje sporočil/a';
 $messages['markingmessage'] = 'Označevanje sporočil/a';
+$messages['addingmember'] = 'Dodajanje stika/ov v skupino...';
+$messages['removingmember'] = 'Odstranjevanje stika/ov iz skupine..';
 $messages['receiptsent'] = 'Sporočilo o branju je bilo uspešno poslano';
 $messages['errorsendingreceipt'] = 'Sporočila o branju ni bilo mogoče poslati';
 $messages['nodeletelastidentity'] = 'Te identitete ni mogoče izbrisati, saj je edina.';
@@ -117,7 +125,6 @@ $messages['smtpautherror'] = 'Napaka pri pošiljanju($code): Avtentikacija je sp
 $messages['smtpfromerror'] = 'Napaka pri pošiljanju ($code): Neveljaven elektronski naslov pošiljatelja "$from" ($msg)';
 $messages['smtptoerror'] = 'Napaka pri pošiljanju ($code): Neveljaven elektronski naslov prejemnika "$to" ($msg)';
 $messages['smtprecipientserror'] = 'Napaka pri pošiljanju: Seznama prejemnikov ni bilo mogoče razčleniti';
-$messages['smtpdsnerror'] = 'Napaka pri pošiljanju: Ni podpore za pošiljanje obvestil o statusu dostave';
 $messages['smtperror'] = 'Napaka pri pošiljanju:$msg';
 $messages['emailformaterror'] = 'Nepravilen elektronski naslov: $email';
 $messages['toomanyrecipients'] = 'Navedli ste preveč prejemnikov. Zmanjšajte število prejemnikov na $max';
@@ -125,19 +132,27 @@ $messages['maxgroupmembersreached'] = 'Število članov skupine presega največj
 $messages['internalerror'] = 'Prišlo je do napake. Poskusite znova.';
 $messages['contactdelerror'] = 'Stika/ov ni bilo mogoče izbrisati';
 $messages['contactdeleted'] = 'Stik/i so bili uspešno izbrisani';
+$messages['contactrestoreerror'] = 'Ni bilo mogoče obnoviti izbrisanih stikov.';
+$messages['contactrestored'] = 'Stiki so bili uspešno obnovljeni.';
 $messages['groupdeleted'] = 'Skupina je bila uspešno izbrisana';
 $messages['grouprenamed'] = 'Skupina je bila uspešno preimenovana';
 $messages['groupcreated'] = 'Skupina je bila uspešno ustvarjena';
+$messages['savedsearchdeleted'] = 'Shranjeno iskanje je bilo uspešno izbrisano.';
+$messages['savedsearchdeleteerror'] = 'Ni bilo mogoče izbrisati shranjenega iskanja.';
+$messages['savedsearchcreated'] = 'Iskanje je bilo uspešno shranjeno.';
+$messages['savedsearchcreateerror'] = 'Iskanja ni bilo mogoče shraniti.';
 $messages['messagedeleted'] = 'Sporočila so bila uspešno izbrisana';
 $messages['messagemoved'] = 'Sporočila so bila uspešno premaknjena';
 $messages['messagecopied'] = 'Sporočila so bila uspešno kopirana';
 $messages['messagemarked'] = 'Sporočila so bila uspešno označena';
 $messages['autocompletechars'] = 'Vnesite vsaj $min znakov za samodejno dokončanje';
+$messages['autocompletemore'] = 'Z iskanjem se ujema več vnosov. Vnesite dodatne črke.';
 $messages['namecannotbeempty'] = 'Imena ni mogoče izbrisati';
 $messages['nametoolong'] = 'Ime je predolgo';
 $messages['folderupdated'] = 'Mapa je bila uspešno posodobljena';
 $messages['foldercreated'] = 'Mapa je bila uspešno ustvarjena';
 $messages['invalidimageformat'] = 'Neveljaven format slike';
 $messages['mispellingsfound'] = 'V sporočilu so bile najdene napake v črkovanju';
+$messages['parentnotwritable'] = 'Podmape v tej mapi ni bilo mogoče ustvariti/premakniti. Nimate pravic.';
 
 ?>
index c864c5ed68b7f23c30d6103f9739b3f5a4ac35d3..947a5aedc62e279b3ced65eaf66e0225b1d6d831 100644 (file)
@@ -1,4 +1,4 @@
-<?php
+a<?php
 
 /*
 
@@ -15,7 +15,7 @@
 |         Andreas Henriksson <andreas@fatal.se>                         |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: labels.inc 5290 2011-09-28 17:09:50Z thomasb $
 
 */
 
@@ -119,7 +119,7 @@ $labels['markread'] = 'Läst';
 $labels['markunread'] = 'Oläst';
 $labels['markflagged'] = 'Flaggat';
 $labels['markunflagged'] = 'Oflaggat';
-$labels['messageactions'] = 'Hantera meddelande';
+$labels['moreactions'] = 'Ytterligare hantering...';
 $labels['select'] = 'Välj';
 $labels['all'] = 'Alla';
 $labels['none'] = 'Ingen';
@@ -173,6 +173,7 @@ $labels['editortype'] = 'Textredigerare';
 $labels['returnreceipt'] = 'Mottagarkvitto';
 $labels['dsn'] = 'Leveransstatusmeddelande';
 $labels['mailreplyintro'] = '$date skrev $sender:';
+$labels['originalmessage'] = 'Ursprungligt meddelande';
 $labels['editidents'] = 'Ändra identiteter';
 $labels['checkspelling'] = 'Kontrollera stavning';
 $labels['resumeediting'] = 'Återuppta redigering';
@@ -248,6 +249,8 @@ $labels['typepager'] = 'Personsökare';
 $labels['typevideo'] = 'Video';
 $labels['typeassistant'] = 'Assistent';
 $labels['typehomepage'] = 'Webbsida';
+$labels['typeblog'] = 'Blog';
+$labels['typeprofile'] = 'Profil';
 $labels['addfield'] = 'Lägg till fält...';
 $labels['addcontact'] = 'Lägg till ny kontakt';
 $labels['editcontact'] = 'Ändra kontakt';
@@ -269,7 +272,6 @@ $labels['print'] = 'Skriv ut';
 $labels['export'] = 'Exportera';
 $labels['exportvcards'] = 'Exportera kontakter';
 $labels['newcontactgroup'] = 'Ny kontaktgrupp';
-$labels['groupactions'] = 'Hantera kontaktgrupper';
 $labels['grouprename'] = 'Ändra gruppnamn';
 $labels['groupdelete'] = 'Ta bort grupp';
 $labels['previouspage'] = 'Visa föregående sida';
@@ -279,6 +281,8 @@ $labels['lastpage'] = 'Visa sista sidan';
 $labels['group'] = 'Grupp';
 $labels['groups'] = 'Kontaktgrupper';
 $labels['personaladrbook'] = 'Personliga adresser';
+$labels['searchsave'] = 'Lägg till sökning';
+$labels['searchdelete'] = 'Ta bort sökning';
 $labels['import'] = 'Importera';
 $labels['importcontacts'] = 'Importera kontakter';
 $labels['importfromfile'] = 'Importera från fil:';
@@ -298,6 +302,8 @@ $labels['edititem'] = 'Ändra information';
 $labels['preferhtml'] = 'Visa meddelanden i HTML-format';
 $labels['defaultcharset'] = 'Förvald teckenkodning';
 $labels['htmlmessage'] = 'HTML-meddelande';
+$labels['dateformat'] = 'Datumformat';
+$labels['timeformat'] = 'Tidformat';
 $labels['prettydate'] = 'Fina datum';
 $labels['setdefault'] = 'Sätt som standard';
 $labels['autodetect'] = 'Automatiskt';
@@ -370,6 +376,11 @@ $labels['reqdsn'] = 'Begär alltid leveransstatusmeddelande';
 $labels['replysamefolder'] = 'Placera svar i samma katalog som besvarat meddelande';
 $labels['defaultaddressbook'] = 'Lägg till nya kontakter i vald adressbok';
 $labels['spellcheckbeforesend'] = 'Kontrollera stavning innan meddelanden skickas';
+$labels['spellcheckoptions'] = 'Rättstavningsalternativ';
+$labels['spellcheckignoresyms'] = 'Ignorera ord med symboler';
+$labels['spellcheckignorenums'] = 'Ignorera ord med siffror';
+$labels['spellcheckignorecaps'] = 'Ignorera ord med enbart stora bokstäver';
+$labels['addtodict'] = 'Lägg till i ordlista';
 $labels['folder'] = 'Katalog';
 $labels['folders'] = 'Kataloger';
 $labels['foldername'] = 'Katalognamn';
index 3e1cf346388904574e62873ddca1114822787d1b..5af843f1ad61a9c74bf1c8977bc057f53d39afb2 100644 (file)
@@ -15,7 +15,7 @@
 |         Andreas Henriksson <andreas@fatal.se>                         |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5175 2011-09-05 18:42:24Z thomasb $
+@version $Id: messages.inc 5290 2011-09-28 17:09:50Z thomasb $
 
 */
 
@@ -26,7 +26,7 @@ $messages['sessionerror'] = 'Din inloggning är felaktig eller har gått ut';
 $messages['imaperror'] = 'Anslutning till IMAP-servern misslyckades';
 $messages['servererror'] = 'Serverfel!';
 $messages['servererrormsg'] = 'Serverfel: $msg';
-$messages['databaserror'] = 'Databasfel!';
+$messages['dberror'] = 'Databasfel!';
 $messages['errorreadonly'] = 'Åtgärden kunde inte utföras. Katalogen är skrivskyddad';
 $messages['errornoperm'] = 'Åtgärden kunde inte utföras. Otillräcklig befogenhet';
 $messages['invalidrequest'] = 'Ogiltig begäran! Informationen sparades inte.';
@@ -50,6 +50,7 @@ $messages['blockedimages'] = 'Externt länkade bilder i meddelandet har blockera
 $messages['encryptedmessage'] = 'Meddelandet är krypterat och kan tyvärr inte visas.';
 $messages['nocontactsfound'] = 'Inga kontakter hittades';
 $messages['contactnotfound'] = 'Efterfrågad kontakt hittades inte';
+$messages['contactsearchonly'] = 'Ange sökord för att hitta kontakter';
 $messages['sendingfailed'] = 'Meddelandet kunde inte skickas';
 $messages['senttooquickly'] = 'Vänta ytterligare $sec sekunder med att skicka meddelandet';
 $messages['errorsavingsent'] = 'Ett fel inträffade när det skickade meddelandet skulle sparas';
@@ -63,6 +64,7 @@ $messages['deletegroupconfirm'] = 'Vill du verkligen ta bort den valda gruppen?'
 $messages['deletemessagesconfirm'] = 'Vill du verkligen ta bort valda meddelanden?';
 $messages['deletefolderconfirm'] = 'Vill du verkligen ta bort den här katalogen?';
 $messages['purgefolderconfirm'] = 'Vill du verkligen ta bort alla meddelanden i den här katalogen?';
+$messages['contactdeleting'] = 'Tar bort kontakt...';
 $messages['groupdeleting'] = 'Tar bort grupp...';
 $messages['folderdeleting'] = 'Tar bort katalog...';
 $messages['foldermoving'] = 'Flyttar katalog...';
@@ -78,10 +80,10 @@ $messages['nosubjectwarning'] = 'Ämnesraden är tom. Vill du ange ämne nu?';
 $messages['nobodywarning'] = 'Skicka det här meddelandet utan text?';
 $messages['notsentwarning'] = 'Meddelandet har inte skickats. Vill du avbryta meddelandet?';
 $messages['noldapserver'] = 'Ange en LDAP-server för att söka';
-$messages['nocontactsreturned'] = 'Inga kontakter hittades';
 $messages['nosearchname'] = 'Ange ett kontaktnamn eller en adress';
 $messages['notuploadedwarning'] = 'Alla bilagor har inte överförts ännu. Vänligen vänta eller avbryt överföringen.';
 $messages['searchsuccessful'] = '$nr meddelanden hittades';
+$messages['contactsearchsuccessful'] = '$nr kontakter hittades';
 $messages['searchnomatch'] = 'Sökningen gav inget resultat';
 $messages['searching'] = 'Söker...';
 $messages['checking'] = 'Kontrollerar...';
@@ -141,11 +143,16 @@ $messages['contactrestored'] = 'Kontakter återskapade';
 $messages['groupdeleted'] = 'Grupp borttagen';
 $messages['grouprenamed'] = 'Gruppnamn ändrat';
 $messages['groupcreated'] = 'Grupp skapad';
+$messages['savedsearchdeleted'] = 'Sparad sökning borttagen';
+$messages['savedsearchdeleteerror'] = 'Kunde inte ta bort sparad sökning';
+$messages['savedsearchcreated'] = 'Sparad sökning tillagd';
+$messages['savedsearchcreateerror'] = 'Kunde inte lägga till sparad sökning';
 $messages['messagedeleted'] = 'Meddelande borttaget';
 $messages['messagemoved'] = 'Meddelande flyttat';
 $messages['messagecopied'] = 'Meddelande kopierat';
 $messages['messagemarked'] = 'Meddelande markerat';
 $messages['autocompletechars'] = 'Ange minst $min tecken för automatisk komplettering';
+$messages['autocompletemore'] = 'Flera passande informationsposter funna. Skriv fler tecken.';
 $messages['namecannotbeempty'] = 'Namnet får inte vara tomt';
 $messages['nametoolong'] = 'Namnet är för långt';
 $messages['folderupdated'] = 'Katalog uppdaterad';
index 089e0b121990b04c6ac3b15f772a7b3cff342cca..9409f953e4c54285661b810b7b55ee1a623a7d4e 100644 (file)
@@ -14,7 +14,7 @@
 | Author: Gökdeniz Karadağ <gokdenizk@gmail.com>                        |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5291 2011-09-28 17:10:45Z thomasb $
+@version $Id: labels.inc 5290 2011-09-28 17:09:50Z thomasb $
 
 */
 
index 004644f58e9756b02eb9e357cfb7f03f10528427..aed1172060fb8ab76618bffe760d2eb2a3178652 100644 (file)
@@ -14,7 +14,7 @@
 | Author: Gökdeniz Karadağ <gokdenizk@gmail.com>                        |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5291 2011-09-28 17:10:45Z thomasb $
+@version $Id: messages.inc 5290 2011-09-28 17:09:50Z thomasb $
 
 */
 
index 99f2d2eb0c91ef51550d853c2c4efa24496bf6ef..d4fbd75ad21f841dfe5e90c77aef6986039d300f 100644 (file)
@@ -13,7 +13,7 @@
 | Author: Volodymyr M. Kononenko aka kvm <vmkononenko@gmail.com>        |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 4671 2011-04-20 08:47:44Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -100,6 +100,8 @@ $labels['replytomessage'] = 'Відповісти відправнику';
 $labels['replytoallmessage'] = 'Відповісти до листа або відправнику та усім отримувачам';
 $labels['replyall'] = 'Відповісти усім';
 $labels['replylist'] = 'Відповісти до листа';
+$labels['forwardinline'] = 'Переслати у тілі листа';
+$labels['forwardattachment'] = 'Переслати як прикріплення';
 $labels['forwardmessage'] = 'Переслати повідомлення';
 $labels['deletemessage'] = 'У кошик';
 $labels['movemessagetotrash'] = 'Перемістити лист у кошик';
@@ -115,7 +117,7 @@ $labels['markread'] = 'Позначити як прочитане';
 $labels['markunread'] = 'Позначити як непрочитане';
 $labels['markflagged'] = 'Додати зірочку';
 $labels['markunflagged'] = 'Зняти зірочку';
-$labels['messageactions'] = 'Додаткові дії...';
+$labels['moreactions'] = 'Інші дії...';
 $labels['select'] = 'Вибрати';
 $labels['all'] = 'Всі';
 $labels['none'] = 'Жодного';
@@ -168,12 +170,15 @@ $labels['charset'] = 'Кодування';
 $labels['editortype'] = 'Редактор';
 $labels['returnreceipt'] = 'Запит відповіді';
 $labels['dsn'] = 'Повідомлення про доставку';
+$labels['mailreplyintro'] = '$date, $sender написав:';
+$labels['originalmessage'] = 'Оригінальне повідомлення';
 $labels['editidents'] = 'Змінити данні';
 $labels['checkspelling'] = 'Перевірити орфографію';
 $labels['resumeediting'] = 'Продовжити редагування';
 $labels['revertto'] = 'Відмінити редагування';
 $labels['attachments'] = 'Вкладені файли';
 $labels['upload'] = 'Вкласти';
+$labels['uploadprogress'] = '$percent ($current з $total)';
 $labels['close'] = 'Закрити';
 $labels['messageoptions'] = 'Налаштування листів';
 $labels['low'] = 'Низький';
@@ -226,6 +231,10 @@ $labels['female'] = 'жіночий';
 $labels['manager'] = 'Менеждер';
 $labels['assistant'] = 'Помічник';
 $labels['spouse'] = 'Шлюбний партнер';
+$labels['allfields'] = 'Усі поля';
+$labels['search'] = 'Пошук';
+$labels['advsearch'] = 'Розширений пошук';
+$labels['other'] = 'Інше';
 $labels['typehome'] = 'Дім';
 $labels['typework'] = 'Робота';
 $labels['typeother'] = 'Інше';
@@ -237,6 +246,9 @@ $labels['typecar'] = 'Автомобіль';
 $labels['typepager'] = 'Пейджер';
 $labels['typevideo'] = 'Відео';
 $labels['typeassistant'] = 'Помічник';
+$labels['typehomepage'] = 'Домашня сторінка';
+$labels['typeblog'] = 'Блог';
+$labels['typeprofile'] = 'Профіль';
 $labels['addfield'] = 'Додати поле...';
 $labels['addcontact'] = 'Додати вибрані контакти до списку контактів';
 $labels['editcontact'] = 'Редагувати контакт';
@@ -258,7 +270,8 @@ $labels['print'] = 'Друкувати';
 $labels['export'] = 'Експортувати';
 $labels['exportvcards'] = 'Експортувати контакти у формат vCard';
 $labels['newcontactgroup'] = 'Створити нову групу контактів';
-$labels['groupactions'] = 'Дії з групами контактів';
+$labels['grouprename'] = 'Перейменувати групу';
+$labels['groupdelete'] = 'Видалити групу';
 $labels['previouspage'] = 'Попередня сторінка';
 $labels['firstpage'] = 'Перша сторінка';
 $labels['nextpage'] = 'Наступна сторінка';
@@ -266,9 +279,12 @@ $labels['lastpage'] = 'Остання сторінка';
 $labels['group'] = 'Група';
 $labels['groups'] = 'Групи';
 $labels['personaladrbook'] = 'Персональні адреси';
+$labels['searchsave'] = 'Зберегти пошук';
+$labels['searchdelete'] = 'Видалити пошук';
 $labels['import'] = 'Імпорт';
 $labels['importcontacts'] = 'Імпортувати контакти';
 $labels['importfromfile'] = 'Імпортувати з файлу:';
+$labels['importtarget'] = 'Додати нові контакти до адресної книги';
 $labels['importreplace'] = 'Замінити всю адресну книгу';
 $labels['importtext'] = 'Ви можете завантажити контакти з існуючої адресної книги. На даний момент підтримується імпортування контактів з формату vCard';
 $labels['done'] = 'Готово';
@@ -284,6 +300,8 @@ $labels['edititem'] = 'Редагувати';
 $labels['preferhtml'] = 'Показувати в HTML';
 $labels['defaultcharset'] = 'Кодування за замовчуваннням';
 $labels['htmlmessage'] = 'Лист у HTML';
+$labels['dateformat'] = 'Формат дати';
+$labels['timeformat'] = 'Формат часу';
 $labels['prettydate'] = 'Дати у зручному форматі';
 $labels['setdefault'] = 'Встановити за замовчуванням';
 $labels['autodetect'] = 'Визначати автоматично';
@@ -354,6 +372,13 @@ $labels['afternseconds'] = 'через $n секунд';
 $labels['reqmdn'] = 'Завжди вимагати повідомлення про доставку';
 $labels['reqdsn'] = 'Завжди вимагати повідомлення про статус доставки';
 $labels['replysamefolder'] = 'Розмістити відповіді у тій же папці, де знаходиться оригінал';
+$labels['defaultaddressbook'] = 'Додати нові контакти до обраної адресної книги';
+$labels['spellcheckbeforesend'] = 'Перевірити орфографію перед відправкою повідомлення';
+$labels['spellcheckoptions'] = 'Налаштування перевірки орфографії';
+$labels['spellcheckignoresyms'] = 'Ігнорувати слова із символами';
+$labels['spellcheckignorenums'] = 'Ігнорувати слова із числами';
+$labels['spellcheckignorecaps'] = 'Ігнорувати слова із великими буквами';
+$labels['addtodict'] = 'Додати до словника';
 $labels['folder'] = 'Папка';
 $labels['folders'] = 'Папки';
 $labels['foldername'] = 'Назва папки';
@@ -377,6 +402,7 @@ $labels['sharedfolder'] = 'Публічний каталог';
 $labels['sortby'] = 'Відсортувати за';
 $labels['sortasc'] = 'Сортувати за зростанням';
 $labels['sortdesc'] = 'Сортувати за спаданням';
+$labels['undo'] = 'Відмінити';
 $labels['B'] = 'б';
 $labels['KB'] = 'Кб';
 $labels['MB'] = 'Мб';
index 341f0ba8e089f982c88dde0bb84e40328b574d33..e302710097d965d14f968820b470135dbbf66ff9 100644 (file)
@@ -13,7 +13,7 @@
 | Author: Volodymyr M. Kononenko aka kvm <vmkononenko@gmail.com>        |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 4671 2011-04-20 08:47:44Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -24,6 +24,7 @@ $messages['sessionerror'] = 'Ваша сесія застаріла';
 $messages['imaperror'] = 'Невдале з`єднання з IMAP сервером';
 $messages['servererror'] = 'Помилка сервера!';
 $messages['servererrormsg'] = 'Помилка сервера: $msg';
+$messages['dberror'] = 'Помилка бази даних!';
 $messages['errorreadonly'] = 'Неможливо виконати операцію. Папка доступна тільки для читання.';
 $messages['errornoperm'] = 'Неможливо виконати операцію. Доступ заборонено';
 $messages['invalidrequest'] = 'Невірний запит! Дані не збережено.';
@@ -32,6 +33,7 @@ $messages['loggedout'] = 'Вашу сесію завершено. Всього 
 $messages['mailboxempty'] = 'Поштова скринька порожня';
 $messages['loading'] = 'Завантаження...';
 $messages['uploading'] = 'Файл відправляється...';
+$messages['uploadingmany'] = 'Завантаження файлів...';
 $messages['loadingdata'] = 'Завантаження даних...';
 $messages['checkingmail'] = 'Перевірка нових листів...';
 $messages['sendingmessage'] = 'Відправка листа...';
@@ -41,10 +43,12 @@ $messages['messagesaved'] = 'Збережено в Чернетках';
 $messages['successfullysaved'] = 'Збережено';
 $messages['addedsuccessfully'] = 'Контакт успішно доданий до списку контактів';
 $messages['contactexists'] = 'Контакт з такою електронною адресою вже існує';
+$messages['contactnameexists'] = 'Контакт з таким самим іменем вже існує.';
 $messages['blockedimages'] = 'З метою безпеки зображення з зовнішніх джерел заблоковано у цьому листі.';
 $messages['encryptedmessage'] = 'Лист зашифровано і не може бути відображено.';
 $messages['nocontactsfound'] = 'Контакти не знайдені';
 $messages['contactnotfound'] = 'Запитаний контакт не знайдений';
+$messages['contactsearchonly'] = 'Введіть деякі критерії пошуку, щоб знайти контакти';
 $messages['sendingfailed'] = 'Не вдалося відправити листа';
 $messages['senttooquickly'] = 'Будь ласка, зачекайте $sec секунд для відправки листа';
 $messages['errorsavingsent'] = 'Помилка при збереженні відправленого листа';
@@ -54,9 +58,12 @@ $messages['errorcopying'] = 'Не вдалося зкопіювати листи
 $messages['errordeleting'] = 'Не вдалося видалити листи';
 $messages['errormarking'] = 'Не вдалося позначити листи';
 $messages['deletecontactconfirm'] = 'Ви дійсно бажаєте видалити вибрані контакти?';
+$messages['deletegroupconfirm'] = 'Ви дійсно хочете видалити обрану групу?';
 $messages['deletemessagesconfirm'] = 'Ви дійсно бажаєте видалити вибрані листи?';
 $messages['deletefolderconfirm'] = 'Ви дійсно бажаєте видалити цю папку?';
 $messages['purgefolderconfirm'] = 'Ви дійсно бажаєте видалити всі листи у цій папці?';
+$messages['contactdeleting'] = 'Видалення контакту(ів)...';
+$messages['groupdeleting'] = 'Видалення групи...';
 $messages['folderdeleting'] = 'Видалення папки...';
 $messages['foldermoving'] = 'Переміщення папки...';
 $messages['foldersubscribing'] = 'Підписати папку...';
@@ -71,10 +78,10 @@ $messages['nosubjectwarning'] = 'Не вказано тему листа. Баж
 $messages['nobodywarning'] = 'Відправити листа без тексту?';
 $messages['notsentwarning'] = 'Лист не було відправлено. Ви бажаєте відхилити відправку?';
 $messages['noldapserver'] = 'Будь ласка, виберіть LDAP сервер для пошуку';
-$messages['nocontactsreturned'] = 'Контакти не знайдені';
 $messages['nosearchname'] = 'Будь ласка, введіть ім`я чи електронну адресу';
 $messages['notuploadedwarning'] = 'Деякі вкладення не було завантажено. Будь ласка, почекайте або відмініть завантаження.';
 $messages['searchsuccessful'] = 'Зайдено $nr листів';
+$messages['contactsearchsuccessful'] = '$nr контактів знайдено.';
 $messages['searchnomatch'] = 'Листів не знайдено';
 $messages['searching'] = 'Пошук...';
 $messages['checking'] = 'Перевірка...';
@@ -95,8 +102,11 @@ $messages['sourceisreadonly'] = 'Дане джерело адрес доступ
 $messages['errorsavingcontact'] = 'Неможливо зберегти адресу контакту';
 $messages['movingmessage'] = 'Переміщення листа...';
 $messages['copyingmessage'] = 'Копіювання листа...';
+$messages['copyingcontact'] = 'Копіювання контакту(ів)...';
 $messages['deletingmessage'] = 'Видалення листа (ів)';
 $messages['markingmessage'] = 'Позначення листа (ів)';
+$messages['addingmember'] = 'Додання контакту(ів) до групи...';
+$messages['removingmember'] = 'Видалення контакту(ів) з групи...';
 $messages['receiptsent'] = 'Повідомлення про прочитання відправлено';
 $messages['errorsendingreceipt'] = 'Не вдалося відправити повідомлення про прочитання';
 $messages['nodeletelastidentity'] = 'Ви не можете видалити цей профіль, він у Вас останній.';
@@ -126,18 +136,27 @@ $messages['maxgroupmembersreached'] = 'Число адрес у групі пе
 $messages['internalerror'] = 'Виникла внутрішня помилка. Будь ласка, спробуйте ще раз';
 $messages['contactdelerror'] = 'Неможливо видалити контакт(и)';
 $messages['contactdeleted'] = 'Контакт(и) видалено успішно';
+$messages['contactrestoreerror'] = 'Неможливо відновити видалений(і) контакт(и).';
+$messages['contactrestored'] = 'Контакт(и) вдало відновлено.';
 $messages['groupdeleted'] = 'Група видалена успішно';
 $messages['grouprenamed'] = 'Група перейменована успішно';
 $messages['groupcreated'] = 'Група створена успішно';
+$messages['savedsearchdeleted'] = 'Збережений пошук вдало видалено.';
+$messages['savedsearchdeleteerror'] = 'Неможливо видалити збережений пошук.';
+$messages['savedsearchcreated'] = 'Збережений пошук вдало створено.';
+$messages['savedsearchcreateerror'] = 'Не вдалось створити збережений пошук. ';
 $messages['messagedeleted'] = 'Лист(и) видалено успішно';
 $messages['messagemoved'] = 'Лист(и) переміщено успішно';
 $messages['messagecopied'] = 'Лист(и) скопійовано успішно';
 $messages['messagemarked'] = 'Лист(и) помічено успішно';
 $messages['autocompletechars'] = 'Введіть щонайменьше $min символів для автозаповнення';
+$messages['autocompletemore'] = 'Знайдено багато відповідних записів. Будь ласка введіть більше символів.';
 $messages['namecannotbeempty'] = 'Ім’я не може бути пустим';
 $messages['nametoolong'] = 'Занадто довге ім’я';
 $messages['folderupdated'] = 'Папка відновлена';
 $messages['foldercreated'] = 'Папка створена';
 $messages['invalidimageformat'] = 'Невірний формат зображення';
+$messages['mispellingsfound'] = 'Знайдено орфографічні помилки у повідомленні.';
+$messages['parentnotwritable'] = 'Неможливо створити/перемістити папку до обраної батьківської папки. Нема прав доступу.';
 
 ?>
index 68fd505bb520b216a8fb44f4c6ef64c12b1a33e2..442f4a6032b4927c5dc61d1f86bb9c61307ad568 100644 (file)
 +-----------------------------------------------------------------------+
 | Author: Xue zhong sheng <xue.zhongsheng@gmail.com>                    |
 |         Zhang Huang bin <michaelbibby@gmail.com>                      |
+|         Kelphon Tang <kelphon@kingstor.com>                          |
 +-----------------------------------------------------------------------+
 
-@version $Id: labels.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: labels.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
 $labels = array();
+
+// login page
 $labels['welcome'] = '欢迎使用 $product';
 $labels['username'] = '用户名';
 $labels['password'] = '密码';
@@ -27,8 +30,8 @@ $labels['login'] = '登录';
 
 // taskbar
 $labels['logout'] = '注销';
-$labels['mail'] = '电子邮件';
-$labels['settings'] = '邮箱设置';
+$labels['mail'] = '邮件';
+$labels['settings'] = '设置';
 $labels['addressbook'] = '通讯录';
 
 // mailbox names
@@ -122,18 +125,16 @@ $labels['replytomessage'] = '回复发件人';
 $labels['replytoallmessage'] = '回复所有';
 $labels['replyall']         = '回复全部';
 $labels['replylist']        = '回复列表';
+$labels['forwardinline']    = '直接转发';
+$labels['forwardattachment'] = '以附件转发';
 $labels['forwardmessage'] = '转发邮件';
 $labels['deletemessage'] = '删除邮件';
 $labels['movemessagetotrash'] = '移动邮件到回收站';
 $labels['printmessage'] = '打印邮件';
 $labels['previousmessage'] = '显示上一封邮件';
-$labels['previousmessages'] = '上一页';
 $labels['firstmessage'] = '显示第一封邮件';
-$labels['firstmessages'] = '第一页';
 $labels['nextmessage'] = '下一封';
-$labels['nextmessages'] = '下一页';
 $labels['lastmessage'] = '最后一封';
-$labels['lastmessages'] = '最后一页';
 $labels['backtolist'] = '返回邮件列表';
 $labels['viewsource'] = '显示邮件源文件';
 $labels['markmessages'] = '标记邮件';
@@ -160,6 +161,7 @@ $labels['expand-all'] = '展开全部';
 $labels['expand-unread'] = '展开未读';
 $labels['collapse-all'] = '收合全部';
 $labels['threaded'] = '线索';
+
 $labels['autoexpand_threads'] = '展开消息主题';
 $labels['do_expand'] = '所有主题';
 $labels['expand_only_unread'] = '仅未读消息';
@@ -179,16 +181,19 @@ $labels['listmode'] = '列表视图样式';
 $labels['folderactions'] = '目录操作...';
 $labels['compact'] = '压缩';
 $labels['empty'] = '清空';
-$labels['purge'] = '清除';
+
 $labels['quota'] = '邮箱容量';
 $labels['unknown'] = '未知';
 $labels['unlimited'] = '无限制';
+
 $labels['quicksearch'] = '快速查找';
 $labels['resetsearch'] = '清空查找内容';
 $labels['searchmod'] = '搜寻模式';
 $labels['msgtext'] = '整个邮件';
+
 $labels['openinextwin'] = '在新窗口中打开';
 $labels['emlsave'] = '下载(.eml)';
+
 // message compose
 $labels['compose']        = '写新邮件';
 $labels['editasnew'] = '编辑为新邮件';
@@ -199,6 +204,7 @@ $labels['charset'] = '字符集';
 $labels['editortype'] = '编辑器类型';
 $labels['returnreceipt'] = '邮件回执';
 $labels['dsn']            = '投递状态提示';
+$labels['mailreplyintro'] = '于 $date, $sender 回复:';
 
 $labels['editidents']    = '编辑身份';
 $labels['checkspelling'] = '拼写检查';
@@ -207,6 +213,7 @@ $labels['revertto'] = '恢复至';
 
 $labels['attachments'] = '附件';
 $labels['upload'] = '上传';
+$labels['uploadprogress'] = '$percent ($current / $total)';
 $labels['close'] = '关闭';
 $labels['messageoptions']  = '邮件选项...';
 
@@ -241,18 +248,67 @@ $labels['receiptnote'] = '注意:回执只表示邮件在收件人的电脑上
 $labels['name'] = '显示名称';
 $labels['firstname'] = '名字';
 $labels['surname'] = '姓氏';
-$labels['email'] = '邮件地址';
-
+$labels['middlename']   = '中名';
+$labels['nameprefix']   = '前缀';
+$labels['namesuffix']   = '后缀';
+$labels['nickname']     = '昵称';
+$labels['jobtitle']     = '职位';
+$labels['organization'] = '公司';
+$labels['department']   = '部门';
+$labels['gender']       = 'Gender';
+$labels['maidenname']   = '娘家姓';
+$labels['email']        = 'Email';
+$labels['phone']        = '电话';
+$labels['address']      = '地址';
+$labels['street']       = '街道';
+$labels['locality']     = '城市';
+$labels['zipcode']      = '邮编';
+$labels['region']       = '省份';
+$labels['country']      = '国家';
+$labels['birthday']     = '生日';
+$labels['anniversary']  = '纪念日';
+$labels['website']      = '主页';
+$labels['instantmessenger'] = 'QQ';
+$labels['notes'] = '备注';
+$labels['male']   = '男';
+$labels['female'] = '女';
+$labels['manager'] = '经理';
+$labels['assistant'] = '助理';
+$labels['spouse'] = '配偶';
+$labels['allfields'] = '所有项目';
+$labels['search'] = '查询';
+$labels['advsearch'] = '高级查询';
+$labels['other'] = '其他';
+
+$labels['typehome']   = '家庭';
+$labels['typework']   = '工作';
+$labels['typeother']  = '其他';
+$labels['typemobile']  = '手机';
+$labels['typemain']  = '主';
+$labels['typehomefax']  = '家庭传真';
+$labels['typeworkfax']  = '工作传真';
+$labels['typecar']  = '车牌';
+$labels['typepager']  = '呼机';
+$labels['typevideo']  = '视频';
+$labels['typeassistant']  = '助理';
+$labels['typehomepage']  = '主页';
+$labels['typeblog'] = '博客';
+$labels['typeprofile'] = '资历';
+
+$labels['addfield'] = '增加项..';
 $labels['addcontact'] = '添加联系人';
 $labels['editcontact'] = '编辑联系人';
 $labels['contacts'] = '联系人';
 $labels['contactproperties'] = '联系人信息';
+$labels['personalinfo'] = '个人信息';
 
 $labels['edit'] = '编辑';
 $labels['cancel'] = '取消';
 $labels['save'] = '保存';
 $labels['delete'] = '删除';
 $labels['rename'] = '更名';
+$labels['addphoto'] = '上传';
+$labels['replacephoto'] = '覆盖';
 
 $labels['newcontact'] = '新建联系人';
 $labels['deletecontact'] = '删除选中的联系人';
@@ -261,8 +317,10 @@ $labels['contactsfromto'] = '第 $from 到 $to 个联系人,共 $count 个';
 $labels['print'] = '打印';
 $labels['export'] = '导出';
 $labels['exportvcards'] = '以 vCard 格式导出联系人';
-$labels['newcontactgroup'] = '创建新的联系人组';
-$labels['groupactions'] = '联系人组操作...';
+$labels['newcontactgroup'] = '创建新的组';
+$labels['groupactions'] = '组操作...';
+$labels['grouprename']    = '重命名组';
+$labels['groupdelete']    = '删除组';
 
 $labels['previouspage'] = '上一页';
 $labels['firstpage'] = '第一页';
@@ -271,11 +329,12 @@ $labels['lastpage'] = '最后一页';
 
 $labels['group'] = '分组';
 $labels['groups'] = '分组';
-$labels['personaladrbook'] = '个人地址';
+$labels['personaladrbook'] = '个人通讯录';
 
 $labels['import'] = '导入';
 $labels['importcontacts'] = '导入联系人';
 $labels['importfromfile'] = '从文件导入';
+$labels['importtarget'] = '增加新联系人到通讯录:';
 $labels['importreplace'] = '替换整个地址簿';
 $labels['importtext'] = '你可以从已有的地址簿导入联系人。目前支持从 vCard 数据格式导入地址簿。';
 $labels['done'] = '完成';
@@ -287,7 +346,7 @@ $labels['preferences'] = '偏好设置';
 $labels['userpreferences'] = '个人偏好';
 $labels['editpreferences'] = '修改个人偏好';
 
-$labels['identities'] = '发邮件的身份';
+$labels['identities'] = '发身份';
 $labels['manageidentities'] = '管理此账号的身份';
 $labels['newidentity'] = '添加身份';
 
@@ -314,7 +373,7 @@ $labels['logoutclear'] = '退出时清空回收站';
 $labels['logoutcompact'] = '退出时压缩收件箱';
 $labels['uisettings'] = '用户界面';
 $labels['serversettings'] = '服务器设置';
-$labels['mailboxview'] = '邮箱查看方式';
+$labels['mailboxview'] = '显示邮件';
 $labels['mdnrequests'] = '发件人请求回执';
 $labels['askuser'] = '询问用户';
 $labels['autosend'] = '自动发送';
@@ -334,8 +393,8 @@ $labels['everynminutes'] = '每隔 $n 分钟';
 $labels['keepalive'] = '检查新邮件';
 $labels['never'] = '从不';
 $labels['immediately'] = '立即';
-$labels['messagesdisplaying'] = '邮件显示设置';
-$labels['messagescomposition'] = '写邮件';
+$labels['messagesdisplaying'] = '显示参数';
+$labels['messagescomposition'] = '写邮件';
 $labels['mimeparamfolding'] = '附件名';
 $labels['2231folding'] = '完整的 RFC 2231(Thunderbird)';
 $labels['miscfolding'] = 'RFC 2047/2232(MS Outlook)';
@@ -367,16 +426,18 @@ $labels['afternseconds'] = '$n 秒之后';
 $labels['reqmdn'] = '总是要求回执';
 $labels['reqdsn'] = '总是要求投递状态通知';
 $labels['replysamefolder'] = '保存回复邮件到邮件相同的目录';
+$labels['defaultaddressbook'] = '增加联系人到所选通讯录';
+$labels['spellcheckbeforesend'] = '发邮件前进行拼写检查';
 
-$labels['folder'] = '邮件夹';
-$labels['folders'] = '邮件夹';
+$labels['folder'] = '目录管理';
+$labels['folders'] = '目录管理';
 $labels['foldername'] = '邮件夹名称';
 $labels['subscribed'] = '已订阅';
 $labels['messagecount'] = '邮件数量';
 $labels['create'] = '建立';
 $labels['createfolder'] = '建立新邮件夹';
 $labels['managefolders'] = '管理邮件夹';
-$labels['specialfolders'] = '指定邮件夹';
+$labels['specialfolders'] = '定义邮件夹';
 $labels['properties'] = '属性';
 $labels['folderproperties'] = '文件夹属性';
 $labels['parentfolder'] = '父文件夹';
@@ -384,10 +445,15 @@ $labels['location'] = '位置';
 $labels['info'] = '信息';
 $labels['getfoldersize'] = '获得文件夹容量';
 $labels['changesubscription'] = '更改订阅';
+$labels['foldertype'] = '目录类型';
+$labels['personalfolder']  = '私有目录';
+$labels['otherfolder']  = '他人目录';
+$labels['sharedfolder']  = '共享目录';
 
 $labels['sortby'] = '排序';
 $labels['sortasc'] = '递增排序';
 $labels['sortdesc'] = '递减排序';
+$labels['undo'] = '撤销';
 
 // units
 $labels['B'] = 'B';
@@ -415,4 +481,4 @@ $labels['japanese'] = '日文';
 $labels['korean'] = '韩语';
 $labels['chinese'] = '中文';
 
-?>
+?>
\ No newline at end of file
index f14d1b5a68afacc2e28f6ba741e4a26ee8db9c2b..28df0e54c6ce2870b669f598e9b3ba3cdec162b1 100644 (file)
 +-----------------------------------------------------------------------+
 | Author: Xue zhong sheng <xue.zhongsheng@gmail.com>                    |
 |         Zhang Huang bin <michaelbibby@gmail.com>                      |
+|         Kelphon Tang <kelphon@kingstor.com>                          |
 +-----------------------------------------------------------------------+
 
-@version $Id: messages.inc 5067 2011-08-14 18:47:58Z thomasb $
+@version $Id: messages.inc 5569 2011-12-07 14:47:32Z thomasb $
 
 */
 
@@ -25,6 +26,7 @@ $messages['sessionerror'] = '会话已过期';
 $messages['imaperror'] = '连接到邮件服务器失败';
 $messages['servererror'] = '服务器错误!';
 $messages['servererrormsg'] = '服务器错误: $msg';
+$messages['databaserror'] = '数据库错误!';
 $messages['errorreadonly'] = '不可对只读文件夹进行操作';
 $messages['errornoperm'] = '无权限操作';
 $messages['invalidrequest'] = '无效的请求!数据保存失败。';
@@ -33,6 +35,7 @@ $messages['loggedout'] = '您已成功注销';
 $messages['mailboxempty'] = '邮件夹为空';
 $messages['loading'] = '正在加载...';
 $messages['uploading'] = '正在上传文件...';
+$messages['uploadingmany'] = '正在上传多个文件...';
 $messages['loadingdata'] = '正在加载数据...';
 $messages['checkingmail'] = '检查新邮件...';
 $messages['sendingmessage'] = '正在发送邮件...';
@@ -42,6 +45,7 @@ $messages['messagesaved'] = '邮件已暂存到草稿箱';
 $messages['successfullysaved'] = '保存成功';
 $messages['addedsuccessfully'] = '成功添加联系人';
 $messages['contactexists'] = '当前联系人的电子邮件地址已存在';
+$messages['contactnameexists'] = '已存在同名的联系人.';
 $messages['blockedimages'] = '为保护隐私,此邮件中的远程图片未显示';
 $messages['encryptedmessage'] = '因此邮件已加密,无法正常显示';
 $messages['nocontactsfound'] = '未找到联系人';
@@ -55,9 +59,11 @@ $messages['errorcopying'] = '无法复制邮件';
 $messages['errordeleting'] = '无法删除邮件';
 $messages['errormarking'] = '无法标记邮件';
 $messages['deletecontactconfirm'] = '确定要删除已选中的联系人?';
+$messages['deletegroupconfirm']  = '确定要删除以选中的组?';
 $messages['deletemessagesconfirm'] = '确定要删除已选中的邮件?';
 $messages['deletefolderconfirm'] = '确定要删除已选中的邮件夹?';
 $messages['purgefolderconfirm'] = '是否确认要删除当前邮件夹中的所有邮件?';
+$messages['groupdeleting'] = '正在删除组...';
 $messages['folderdeleting'] = '正在删除邮件夹';
 $messages['foldermoving'] = '正在移动目录...';
 $messages['foldersubscribing'] = '注册文件夹中...';
@@ -96,8 +102,11 @@ $messages['sourceisreadonly'] = '源地址为只读';
 $messages['errorsavingcontact'] = '无法保存联系人的地址';
 $messages['movingmessage'] = '移动邮件到...';
 $messages['copyingmessage'] = '复制邮件到...';
+$messages['copyingcontact'] = '复制联系人...';
 $messages['deletingmessage'] = '正在删除邮件...';
 $messages['markingmessage'] = '正在标记邮件...';
+$messages['addingmember'] = '正在添加联系人到组...';
+$messages['removingmember'] = '正在从组中删除联系人...';
 $messages['receiptsent'] = '成功发送了一个已读回执';
 $messages['errorsendingreceipt'] = '无法发送回执';
 $messages['nodeletelastidentity'] = '无法删除这个身份,这是最后一个。';
@@ -109,6 +118,7 @@ $messages['contactremovedfromgroup'] = '成功从这个分组移除联系人';
 $messages['importwait'] = '正在导入,请稍后...';
 $messages['importerror'] = '导入失败!所上传的文件不是有效的 vCard 文件。';
 $messages['importconfirm'] = '<b>成功导入 $inserted 联系人,$skipped 乎略已存在的联系人</b>:<p><em>$names</em></p>';
+$messages['importconfirmskipped'] = '<b>跳过已存在 $skipped 项目</b>';
 $messages['opnotpermitted'] = '不允许的操作';
 $messages['nofromaddress'] = '选种的身份中没有邮件地址';
 $messages['editorwarning'] = '切换到纯文本编辑器将导致邮件正文中的所有文本格式失效,您确定要这样做吗?';
@@ -126,9 +136,11 @@ $messages['maxgroupmembersreached'] = '组员数量超过最大值 $max';
 $messages['internalerror'] = '遇到一个内部错误,请重试';
 $messages['contactdelerror'] = '无法删除联系人';
 $messages['contactdeleted'] = '删除联系人成功';
-$messages['groupdeleted'] = '成功删除联系人组';
-$messages['grouprenamed'] = '联系人组改名成功';
-$messages['groupcreated'] = '成功创建联系人组';
+$messages['contactrestoreerror'] = '无法恢复已删除的联系人.';
+$messages['contactrestored'] = '联系人恢复成功.';
+$messages['groupdeleted'] = '成功删除组';
+$messages['grouprenamed'] = '组改名成功';
+$messages['groupcreated'] = '成功创建组';
 $messages['messagedeleted'] = '删除邮件成功';
 $messages['messagemoved'] = '转移邮件成功';
 $messages['messagecopied'] = '复制邮件成功';
@@ -138,4 +150,7 @@ $messages['namecannotbeempty'] = '必须输入名字';
 $messages['nametoolong'] = '名字太长';
 $messages['folderupdated'] = '成功更新文件夹';
 $messages['foldercreated'] = '成功创建文件夹';
+$messages['invalidimageformat'] = '非法的图像类型.';
+$messages['mispellingsfound'] = '检查到拼写错误.';
+$messages['parentnotwritable'] = '无法创建和转移到所选的目录,权限不足.';
 ?>
index e07d62af973c51d8441ddcc9f568597ffd048eb9..5e526e1aa1ffddc89fd7b0000fa95689b59b75bf 100644 (file)
@@ -60,9 +60,9 @@ foreach ($cids as $source => $cid)
         // Check if contact exists, if so, we'll need it's ID
         // Note: Some addressbooks allows empty email address field
         if (!empty($a_record['email']))
-            $result = $TARGET->search('email', $a_record['email'], true, true, true);
+            $result = $TARGET->search('email', $a_record['email'], 1, true, true);
         else if (!empty($a_record['name']))
-            $result = $TARGET->search('name', $a_record['name'], true, true, true);
+            $result = $TARGET->search('name', $a_record['name'], 1, true, true);
         else
             $result = new rcube_result_set();
 
index a710aa20437478b7dd40e860c9e1a4e39cdc3ffa..8ba1c08b617a9b1a6b7413d7060d31fc1199b6fd 100644 (file)
@@ -66,7 +66,6 @@ else {
 }
 
 // send downlaod headers
-send_nocacheing_headers();
 header('Content-Type: text/x-vcard; charset='.RCMAIL_CHARSET);
 header('Content-Disposition: attachment; filename="rcube_contacts.vcf"');
 
index c03f3e35990cc54245e3df666d40ffc8797487fc..40a23d746d0d015fff5851f82bbb6819795b1335 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: func.inc 5165 2011-09-05 08:49:04Z thomasb $
+ $Id: func.inc 5499 2011-11-28 09:03:27Z alec $
 
 */
 
@@ -227,7 +227,32 @@ function rcmail_directory_list($attrib)
         $out = $groupdata['out'];
     }
 
-    $OUTPUT->set_env('contactgroups', $jsdata); 
+    $line_templ = html::tag('li', array(
+        'id' => 'rcmliS%s', 'class' => '%s'),
+        html::a(array('href' => '#', 'rel' => 'S%s',
+            'onclick' => "return ".JS_OBJECT_NAME.".command('listsearch', '%s', this)"), '%s'));
+
+    // Saved searches
+    $sources = $RCMAIL->user->list_searches(rcube_user::SEARCH_ADDRESSBOOK);
+    foreach ($sources as $j => $source) {
+        $id = $source['id'];
+        $js_id = JQ($id);
+
+        // set class name(s)
+        $class_name = 'contactsearch';
+        if ($current === $id)
+            $class_name .= ' selected';
+        if ($source['class_name'])
+            $class_name .= ' ' . $source['class_name'];
+
+        $out .= sprintf($line_templ,
+            html_identifier($id),
+            $class_name,
+            $id,
+            $js_id, (!empty($source['name']) ? Q($source['name']) : Q($id)));
+    }
+
+    $OUTPUT->set_env('contactgroups', $jsdata);
     $OUTPUT->add_gui_object('folderlist', $attrib['id']);
     // add some labels to client
     $OUTPUT->add_label('deletegroupconfirm', 'groupdeleting', 'addingmember', 'removingmember');
@@ -287,7 +312,7 @@ function rcmail_contacts_list($attrib)
     $OUTPUT->include_script('list.js');
 
     // add some labels to client
-    $OUTPUT->add_label('deletecontactconfirm', 'copyingcontact');
+    $OUTPUT->add_label('deletecontactconfirm', 'copyingcontact', 'contactdeleting');
 
     return $out;
 }
@@ -383,12 +408,12 @@ function rcmail_get_rowcount_text($result=null)
 function rcmail_get_type_label($type)
 {
     $label = 'type'.$type;
-    if (rcube_label_exists($label))
-        return rcube_label($label);
+    if (rcube_label_exists($label, '*', $domain))
+        return rcube_label($label, $domain);
     else if (preg_match('/\w+(\d+)$/', $label, $m)
             && ($label = preg_replace('/(\d+)$/', '', $label))
-            && rcube_label_exists($label))
-        return rcube_label($label) . ' ' . $m[1];
+            && rcube_label_exists($label, '*', $domain))
+        return rcube_label($label, $domain) . ' ' . $m[1];
 
     return ucfirst($type);
 }
@@ -397,6 +422,7 @@ function rcmail_get_type_label($type)
 function rcmail_contact_form($form, $record, $attrib = null)
 {
     global $RCMAIL, $CONFIG;
+    static $jqueryui_loaded = 0;
 
     // Allow plugins to modify contact form content
     $plugin = $RCMAIL->plugins->exec_hook('contact_form', array(
@@ -583,6 +609,19 @@ function rcmail_contact_form($form, $record, $attrib = null)
                         if ($colprop['subtypes'] || $colprop['limit'] != 1)
                             $colprop['array'] = true;
 
+                        // load jquery UI datepickert for date fields
+                        if ($colprop['type'] == 'date') {
+                            if (!$jqueryui_loaded++) {
+                                $RCMAIL->plugins->load_plugin('jqueryui');
+                                $RCMAIL->output->set_env('date_format', strtr($RCMAIL->config->get('date_format', 'Y-m-d'), array('y'=>'y', 'Y'=>'yy', 'm'=>'mm', 'n'=>'m', 'd'=>'dd', 'j'=>'d')));
+                                foreach (array('jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec') as $month)
+                                    $month_names[] = rcube_label($month);
+                                $RCMAIL->output->set_env('month_names', $month_names);
+                            }
+                            $colprop['class'] .= ($colprop['class'] ? ' ' : '') . 'datepicker';
+                            $val = format_date($val, $RCMAIL->config->get('date_format', 'Y-m-d'), false);
+                        }
+
                         $val = rcmail_get_edit_field($col, $val, $colprop, $colprop['type']);
                         $coltypes[$field]['count']++;
                     }
@@ -618,11 +657,16 @@ function rcmail_contact_form($form, $record, $attrib = null)
                 }
 
                 // wrap rows in fieldgroup container
-                $content .= html::tag('fieldset', array('class' => 'contactfieldgroup ' . ($colprop['subtypes'] ? 'contactfieldgroupmulti ' : '') . 'contactcontroller' . $col, 'style' => ($rows ? null : 'display:none')),
-                  ($colprop['subtypes'] ? html::tag('legend', null, Q($colprop['label'])) : ' ') .
-                  $rows);
+                if ($rows) {
+                    $content .= html::tag('fieldset', array('class' => 'contactfieldgroup ' . ($colprop['subtypes'] ? 'contactfieldgroupmulti ' : '') . 'contactcontroller' . $col, 'style' => ($rows ? null : 'display:none')),
+                      ($colprop['subtypes'] ? html::tag('legend', null, Q($colprop['label'])) : ' ') .
+                      $rows);
+                }
             }
 
+            if (!$content)
+                continue;
+
             // also render add-field selector
             if ($edit_mode)
                 $content .= html::p('addfield', $select_add->show(null, array('style' => $select_add->_count ? null : 'display:none')));
@@ -633,7 +677,8 @@ function rcmail_contact_form($form, $record, $attrib = null)
             $content = $fieldset['content'];
         }
 
-        $out .= html::tag('fieldset', null, html::tag('legend', null, Q($fieldset['name'])) . $content) ."\n";
+        if ($content)
+            $out .= html::tag('fieldset', null, html::tag('legend', null, Q($fieldset['name'])) . $content) ."\n";
     }
 
     if ($edit_mode) {
@@ -660,7 +705,7 @@ function rcmail_contact_photo($attrib)
     $RCMAIL->output->set_env('photo_placeholder', $photo_img);
     unset($attrib['placeholder']);
 
-    if (strpos($record['photo'], 'http:') === 0)
+    if (preg_match('!^https?://!i', $record['photo']))
         $photo_img = $record['photo'];
     else if ($record['photo'])
         $photo_img = $RCMAIL->url(array('_action' => 'photo', '_cid' => $record['ID'], '_source' => $SOURCE_ID));
@@ -683,7 +728,7 @@ function rcmail_contact_photo($attrib)
 function rcmail_format_date_col($val)
 {
     global $RCMAIL;
-    return format_date($val, $RCMAIL->config->get('date_format', 'Y-m-d'));
+    return format_date($val, $RCMAIL->config->get('date_format', 'Y-m-d'), false);
 }
 
 
@@ -713,9 +758,12 @@ function rcmail_get_cids()
     foreach ($cid as $id) {
         // if _source is not specified we'll find it from decoded ID
         if (!$got_source) {
-            list ($c, $s) = explode('-', $id, 2);
-            if (strlen($s)) {
-                $result[(string)$s][] = $c;
+            if ($sep = strrpos($id, '-')) {
+                $contact_id = substr($id, 0, $sep);
+                $source_id  = substr($id, $sep+1);
+                if (strlen($source_id)) {
+                    $result[(string)$source_id][] = $contact_id;
+                }
             }
         }
         else {
@@ -745,4 +793,6 @@ $RCMAIL->register_action_map(array(
     'group-delete' => 'groups.inc',
     'group-addmembers' => 'groups.inc',
     'group-delmembers' => 'groups.inc',
+    'search-create' => 'search.inc',
+    'search-delete' => 'search.inc',
 ));
index 1b9aea18a15c2689858500a8223a7af7d8e38200..63a6dae30928f48be02c41c7467af8a19e20953c 100644 (file)
@@ -174,9 +174,9 @@ if ($_FILES['_file']['tmp_name'] && is_uploaded_file($_FILES['_file']['tmp_name'
 
       if (!$replace && $email) {
         // compare e-mail address
-        $existing = $CONTACTS->search('email', $email, false, false);
+        $existing = $CONTACTS->search('email', $email, 1, false);
         if (!$existing->count && $vcard->displayname) {  // compare display name
-          $existing = $CONTACTS->search('name', $vcard->displayname, false, false);
+          $existing = $CONTACTS->search('name', $vcard->displayname, 1, false);
         }
         if ($existing->count) {
           $IMPORT_STATS->skipped++;
index ab82faf66e24702938f47860c3007b72841b4793..6b00a824f1efcf688cbfaacd3a025b02d79c9182 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: list.inc 4850 2011-06-14 13:45:26Z alec $
+ $Id: list.inc 5315 2011-10-06 12:31:38Z thomasb $
 
 */
 
@@ -73,6 +73,11 @@ else {
 
     // get contacts for this user
     $result = $CONTACTS->list_records(array('name'));
+    
+    if (!$result->count && $result->searchonly) {
+        $OUTPUT->show_message('contactsearchonly', 'notice');
+        $OUTPUT->command('command', 'advanced-search');
+    }
 }
 
 // update message count display
index 5996b9da7645e5d7c1ef55416ca59a9894d543d0..c40ecdf72192a59b2c512725533faceb27bb9dda 100644 (file)
@@ -21,6 +21,7 @@
 
 $cids   = rcmail_get_cids();
 $mailto = array();
+$recipients = null;
 
 foreach ($cids as $source => $cid)
 {
@@ -30,12 +31,35 @@ foreach ($cids as $source => $cid)
     {
         $CONTACTS->set_page(1);
         $CONTACTS->set_pagesize(count($cid) + 2); // +2 to skip counting query
-        $recipients = $CONTACTS->search($CONTACTS->primary_key, $cid, false, true, true, 'email');
+        $recipients = $CONTACTS->search($CONTACTS->primary_key, $cid, 0, true, true, 'email');
+    }
+}
+
+if (!empty($_REQUEST['_gid']) && isset($_REQUEST['_source']))
+{
+    $source = get_input_value('_source', RCUBE_INPUT_GPC);
+    $CONTACTS = $RCMAIL->get_address_book($source);
+    
+    $group_id = get_input_value('_gid', RCUBE_INPUT_GPC);
+    $group_data = $CONTACTS->get_group($group_id);
+    
+    // group has an email address assigned: use that
+    if ($group_data['email']) {
+        $mailto[] = format_email_recipient($group_data['email'][0], $group_data['name']);
+    }
+    else if ($CONTACTS->ready) {
+        $CONTACTS->set_group($group_id);
+        $CONTACTS->set_page(1);
+        $CONTACTS->set_pagesize(200); // limit somehow
+        $recipients = $CONTACTS->list_records();
+    }
+}
 
-        while (is_object($recipients) && ($rec = $recipients->iterate())) {
-            $emails = $CONTACTS->get_col_values('email', $rec, true);
-            $mailto[] = format_email_recipient($emails[0], $rec['name']);
-        }
+if ($recipients)
+{
+    while (is_object($recipients) && ($rec = $recipients->iterate())) {
+        $emails = $CONTACTS->get_col_values('email', $rec, true);
+        $mailto[] = format_email_recipient($emails[0], $rec['name']);
     }
 }
 
index ef4387afd282684984ceb7d64b335f349a7a037a..d573b0ea484b8617e5914d8dda27d055a3b47f3a 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: save.inc 5130 2011-08-25 08:30:01Z alec $
+ $Id: save.inc 5541 2011-12-04 17:05:42Z thomasb $
 
 */
 
@@ -50,10 +50,12 @@ foreach ($GLOBALS['CONTACT_COLTYPES'] as $col => $colprop) {
       foreach ((array)$vals as $i => $val)
         $values[$i][$childcol] = $val;
     }
-    $subtypes = get_input_value('_subtype_' . $col, RCUBE_INPUT_POST);
-    foreach ($subtypes as $i => $subtype)
+    $subtypes = isset($_REQUEST['_subtype_' . $col]) ? (array)get_input_value('_subtype_' . $col, RCUBE_INPUT_POST) : array('');
+    foreach ($subtypes as $i => $subtype) {
+      $suffix = $subtype ? ':'.$subtype : '';
       if ($values[$i])
-        $a_record[$col.':'.$subtype][] = $values[$i];
+        $a_record[$col.$suffix][] = $values[$i];
+     }
   }
   // assign values and subtypes
   else if (is_array($_POST[$fname])) {
@@ -162,7 +164,7 @@ else {
   // show notice if existing contacts with same e-mail are found
   $existing = false;
   foreach ($CONTACTS->get_col_values('email', $a_record, true) as $email) {
-      if ($email && ($res = $CONTACTS->search('email', $email, false, false, true)) && $res->count) {
+      if ($email && ($res = $CONTACTS->search('email', $email, 1, false, true)) && $res->count) {
           $OUTPUT->show_message('contactexists', 'notice', null, false);
           break;
       }
index 352556de0e4d9ee5c8dea96a9555a5a6e33b6950..643cc60a834b022fa5f2ed2e0dd9591845cf0977 100644 (file)
 
 */
 
+if ($RCMAIL->action == 'search-create') {
+    $id   = get_input_value('_search', RCUBE_INPUT_POST);
+    $name = get_input_value('_name', RCUBE_INPUT_POST, true);
+
+    if (($params = $_SESSION['search_params']) && $params['id'] == $id) {
+
+        $data = array(
+            'type' => rcube_user::SEARCH_ADDRESSBOOK,
+            'name' => $name,
+            'data' => array(
+                'fields' => $params['data'][0],
+                'search' => $params['data'][1],
+            ),
+        );
+
+        $plugin = $RCMAIL->plugins->exec_hook('saved_search_create', array('data' => $data));
+
+        if (!$plugin['abort'])
+            $result = $RCMAIL->user->insert_search($plugin['data']);
+        else
+            $result = $plugin['result'];
+    }
+
+    if ($result) {
+        $OUTPUT->show_message('savedsearchcreated', 'confirmation');
+        $OUTPUT->command('insert_saved_search', Q($name), Q($result));
+    }
+    else
+        $OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'savedsearchcreateerror', 'error');
+
+    $OUTPUT->send();
+}
+
+if ($RCMAIL->action == 'search-delete') {
+    $id = get_input_value('_sid', RCUBE_INPUT_POST);
+
+    $plugin = $RCMAIL->plugins->exec_hook('saved_search_delete', array('id' => $id));
+
+    if (!$plugin['abort'])
+        $result = $RCMAIL->user->delete_search($id);
+    else
+        $result = $plugin['result'];
+
+    if ($result) {
+        $OUTPUT->show_message('savedsearchdeleted', 'confirmation');
+        $OUTPUT->command('remove_search_item', Q($id));
+        // contact list will be cleared, clear also page counter
+        $OUTPUT->command('set_rowcount', rcube_label('nocontactsfound'));
+        $OUTPUT->set_env('pagecount', 0);
+    }
+    else
+        $OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'savedsearchdeleteerror', 'error');
+
+    $OUTPUT->send();
+}
+
+
 if (!isset($_GET['_form'])) {
     rcmail_contact_search();
 }
@@ -34,9 +91,15 @@ function rcmail_contact_search()
     global $RCMAIL, $OUTPUT, $CONFIG, $SEARCH_MODS_DEFAULT;
 
     $adv = isset($_POST['_adv']);
+    $sid = get_input_value('_sid', RCUBE_INPUT_GET);
 
+    // get search criteria from saved search
+    if ($sid && ($search = $RCMAIL->user->get_search($sid))) {
+        $fields = $search['data']['fields'];
+        $search = $search['data']['search'];
+    }
     // get fields/values from advanced search form
-    if ($adv) {
+    else if ($adv) {
         foreach (array_keys($_POST) as $key) {
             $s = trim(get_input_value($key, RCUBE_INPUT_POST, true));
             if (strlen($s) && preg_match('/^_search_([a-zA-Z0-9_-]+)$/', $key, $m)) {
@@ -74,6 +137,9 @@ function rcmail_contact_search()
         }
     }
 
+    // Values matching mode
+    $mode = (int) $RCMAIL->config->get('addressbook_search_mode');
+
     // get sources list
     $sources    = $RCMAIL->get_address_sources();
     $search_set = array();
@@ -105,7 +171,7 @@ function rcmail_contact_search()
         $source->set_pagesize(9999);
 
         // get contacts count
-        $result = $source->search($fields, $search, false, false);
+        $result = $source->search($fields, $search, $mode, false);
 
         if (!$result->count) {
             continue;
@@ -145,6 +211,7 @@ function rcmail_contact_search()
 
     // save search settings in session
     $_SESSION['search'][$search_request] = $search_set;
+    $_SESSION['search_params'] = array('id' => $search_request, 'data' => array($fields, $search));
     $_SESSION['page'] = 1;
 
     if ($adv)
@@ -153,6 +220,7 @@ function rcmail_contact_search()
     if ($result->count > 0) {
         // create javascript list
         rcmail_js_contacts_list($result);
+        $OUTPUT->show_message('contactsearchsuccessful', 'confirmation', array('nr' => $result->count));
     }
     else {
         $OUTPUT->show_message('nocontactsfound', 'notice');
@@ -162,9 +230,14 @@ function rcmail_contact_search()
     $OUTPUT->command('set_env', 'search_request', $search_request);
     $OUTPUT->command('set_env', 'pagecount', ceil($result->count / $CONFIG['pagesize']));
     $OUTPUT->command('set_rowcount', rcmail_get_rowcount_text($result));
+    // Re-set current source
+    $OUTPUT->command('set_env', 'search_id', $sid);
+    $OUTPUT->command('set_env', 'source', '');
+    $OUTPUT->command('set_env', 'group', '');
 
     // unselect currently selected directory/group
-    $OUTPUT->command('unselect_directory');
+    if (!$sid)
+        $OUTPUT->command('unselect_directory');
     $OUTPUT->command('update_group_commands');
 
     // send response
index bd9b8399328c7a8bb7f64849771410373a6d9944..6aeaea6c86ad2b6404ae8f85d204d19ad47bf7d1 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: upload_photo.inc 5130 2011-08-25 08:30:01Z alec $
+ $Id: upload_photo.inc 5110 2011-08-22 14:33:02Z alec $
 
 */
 
index 4df3a78979cce41c873a045274d686753d8572f5..5b77b56e81ec68cea6c693cb20aaa751c91fb8e6 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: addcontact.inc 4933 2011-07-18 16:57:15Z thomasb $
+ $Id: addcontact.inc 5415 2011-11-11 15:04:45Z alec $
 
 */
 
@@ -50,7 +50,7 @@ if (!empty($_POST['_address']) && is_object($CONTACTS))
       $OUTPUT->show_message('errorsavingcontact', 'error');
       $OUTPUT->send();
     }
-    
+
     $email = rcube_idn_to_ascii($contact['email']);
     if (!check_email($email, false)) {
       $OUTPUT->show_message('emailformaterror', 'error', array('email' => $contact['email']));
@@ -60,8 +60,18 @@ if (!empty($_POST['_address']) && is_object($CONTACTS))
     $contact['email'] = rcube_idn_to_utf8($contact['email']);
     $contact['name'] = rcube_addressbook::compose_display_name($contact);
 
+    // validate contact record
+    if (!$CONTACTS->validate($contact, true)) {
+      $error = $CONTACTS->get_error();
+      // TODO: show dialog to complete record
+      // if ($error['type'] == rcube_addressbook::ERROR_VALIDATE) { }
+
+      $OUTPUT->show_message($error['message'] ? $error['message'] : 'errorsavingcontact', 'error');
+      $OUTPUT->send();
+    }
+
     // check for existing contacts
-    $existing = $CONTACTS->search('email', $contact['email'], true, false);
+    $existing = $CONTACTS->search('email', $contact['email'], 1, false);
 
     if ($done = $existing->count)
       $OUTPUT->show_message('contactexists', 'warning');
index a5bc21f3c8b751e429dc40d2ecfacb1a015e65b6..3a85fa5a02acd32b26e8470054e5ce8b1b0e7268 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: attachments.inc 5229 2011-09-16 19:13:27Z thomasb $
+ $Id: attachments.inc 5543 2011-12-05 07:24:36Z alec $
 
 */
 
@@ -25,9 +25,12 @@ if (!empty($_GET['_progress'])) {
 }
 
 $COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GPC);
-$_SESSION['compose'] = $_SESSION['compose_data_'.$COMPOSE_ID];
+$COMPOSE    = null;
 
-if (!$_SESSION['compose']) {
+if ($COMPOSE_ID && $_SESSION['compose_data_'.$COMPOSE_ID])
+  $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
+
+if (!$COMPOSE) {
   die("Invalid session var!");
 }
 
@@ -38,15 +41,15 @@ if ($RCMAIL->action=='remove-attachment')
   $id = 'undefined';
   if (preg_match('/^rcmfile(\w+)$/', $_POST['_file'], $regs))
     $id = $regs[1];
-  if ($attachment = $_SESSION['compose']['attachments'][$id])
+  if ($attachment = $COMPOSE['attachments'][$id])
     $attachment = $RCMAIL->plugins->exec_hook('attachment_delete', $attachment);
   if ($attachment['status']) {
-    if (is_array($_SESSION['compose']['attachments'][$id])) {
-      unset($_SESSION['compose']['attachments'][$id]);
+    if (is_array($COMPOSE['attachments'][$id])) {
+      unset($COMPOSE['attachments'][$id]);
       $OUTPUT->command('remove_from_attachment_list', "rcmfile$id");
     }
   }
-  
+
   $OUTPUT->send();
   exit;
 }
@@ -56,16 +59,16 @@ if ($RCMAIL->action=='display-attachment')
   $id = 'undefined';
   if (preg_match('/^rcmfile(\w+)$/', $_GET['_file'], $regs))
     $id = $regs[1];
-  if ($attachment = $_SESSION['compose']['attachments'][$id])
+  if ($attachment = $COMPOSE['attachments'][$id])
     $attachment = $RCMAIL->plugins->exec_hook('attachment_display', $attachment);
-    
+
   if ($attachment['status']) {
     if (empty($attachment['size']))
       $attachment['size'] = $attachment['data'] ? strlen($attachment['data']) : @filesize($attachment['path']);
 
     header('Content-Type: ' . $attachment['mimetype']);
     header('Content-Length: ' . $attachment['size']);
-    
+
     if ($attachment['data'])
       echo $attachment['data'];
     else if ($attachment['path'])
@@ -76,8 +79,8 @@ if ($RCMAIL->action=='display-attachment')
 
 // attachment upload action
 
-if (!is_array($_SESSION['compose']['attachments'])) {
-  $_SESSION['compose']['attachments'] = array();
+if (!is_array($COMPOSE['attachments'])) {
+  $COMPOSE['attachments'] = array();
 }
 
 // clear all stored output properties (like scripts and env vars)
@@ -107,9 +110,9 @@ if (is_array($_FILES['_attachments']['tmp_name'])) {
 
       // store new attachment in session
       unset($attachment['status'], $attachment['abort']);
-      $_SESSION['compose']['attachments'][$id] = $attachment;
+      $COMPOSE['attachments'][$id] = $attachment;
 
-      if (($icon = $_SESSION['compose']['deleteicon']) && is_file($icon)) {
+      if (($icon = $COMPOSE['deleteicon']) && is_file($icon)) {
         $button = html::img(array(
           'src' => $icon,
           'alt' => rcube_label('delete')
index d92f10858172011b12d1847a926a2b476cbe7611..7769ec87db2ce8d349fdbbfec2c9de03083e611c 100644 (file)
@@ -5,7 +5,8 @@
  | program/steps/mail/autocomplete.inc                                   |
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2008-2010, Roundcube Dev Team                           |
+ | Copyright (C) 2008-2011, Roundcube Dev Team                           |
+ | Copyright (C) 2011, Kolab Systems AG                                  |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -15,7 +16,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: autocomplete.inc 4963 2011-07-25 10:49:39Z alec $
+ $Id: autocomplete.inc 5426 2011-11-15 10:50:30Z alec $
 
 */
 
@@ -31,14 +32,17 @@ if ($RCMAIL->action == 'group-expand') {
         $members[] = format_email_recipient($email, $sql_arr['name']);
     }
 
-    $OUTPUT->command('replace_group_recipients', $gid, join(', ', $members));
+    $separator = trim($RCMAIL->config->get('recipients_separator', ',')) . ' ';
+    $OUTPUT->command('replace_group_recipients', $gid, join($separator, array_unique($members)));
   }
 
   $OUTPUT->send();
 }
 
 
-$MAXNUM = (int)$RCMAIL->config->get('autocomplete_max', 15);
+$MAXNUM = (int) $RCMAIL->config->get('autocomplete_max', 15);
+$mode   = (int) $RCMAIL->config->get('addressbook_search_mode');
+$single = (bool) $RCMAIL->config->get('autocomplete_single');
 $search = get_input_value('_search', RCUBE_INPUT_GPC, true);
 $source = get_input_value('_source', RCUBE_INPUT_GPC);
 $sid    = get_input_value('_id', RCUBE_INPUT_GPC);
@@ -49,45 +53,73 @@ else
   $book_types = (array) $RCMAIL->config->get('autocomplete_addressbooks', 'sql');
 
 if (!empty($book_types) && strlen($search)) {
-  $contacts = array();
+  $contacts  = array();
+  $sort_keys = array();
   $books_num = count($book_types);
+  $search_lc = mb_strtolower($search);
 
   foreach ($book_types as $id) {
     $abook = $RCMAIL->get_address_book($id);
     $abook->set_pagesize($MAXNUM);
 
-    if ($result = $abook->search(array('email','name'), $search, false, true, true, 'email')) {
+    if ($result = $abook->search(array('email','name'), $search, $mode, true, true, 'email')) {
       while ($sql_arr = $result->iterate()) {
         // Contact can have more than one e-mail address
         $email_arr = (array)$abook->get_col_values('email', $sql_arr, true);
         $email_cnt = count($email_arr);
+        $idx = 0;
         foreach ($email_arr as $email) {
-          if (empty($email))
+          if (empty($email)) {
             continue;
+          }
+
           $contact = format_email_recipient($email, $sql_arr['name']);
+
           // skip entries that don't match
-          if ($email_cnt > 1 && stripos($contact, $search) === false) {
+          if ($email_cnt > 1 && strpos(mb_strtolower($contact), $search_lc) === false) {
             continue;
           }
-          // when we've got more than one book, we need to skip duplicates
-          if ($books_num == 1 || !in_array($contact, $contacts)) {
-            $contacts[] = $contact;
+
+          // skip duplicates
+          if (!in_array($contact, $contacts)) {
+            $contacts[]  = $contact;
+            $sort_keys[] = sprintf('%s %03d', $sql_arr['name'] , $idx++);
+
             if (count($contacts) >= $MAXNUM)
               break 2;
           }
+
+          // skip redundant entries (show only first email address)
+          if ($single) {
+            break;
+          }
         }
       }
     }
 
     // also list matching contact groups
-    if ($abook->groups) {
+    if ($abook->groups && count($contacts) < $MAXNUM) {
       foreach ($abook->list_groups($search) as $group) {
         $abook->reset();
         $abook->set_group($group['ID']);
-        $result = $abook->count();
+        $group_prop = $abook->get_group($group['ID']);
+
+        // group (distribution list) with email address(es)
+        if ($group_prop['email']) {
+            $idx = 0;
+            foreach ((array)$group_prop['email'] as $email) {
+                $contacts[]  = format_email_recipient($email, $group['name']);
+                $sort_keys[] = sprintf('%s %03d', $group['name'] , $idx++);
+
+                if (count($contacts) >= $MAXNUM)
+                  break 2;
+            }
+        }
+        // show group with count
+        else if (($result = $abook->count()) && $result->count) {
+          $contacts[]  = array('name' => $group['name'] . ' (' . intval($result->count) . ')', 'id' => $group['ID'], 'source' => $id);
+          $sort_keys[] = $group['name'];
 
-        if ($result->count) {
-          $contacts[] = array('name' => $group['name'] . ' (' . intval($result->count) . ')', 'id' => $group['ID'], 'source' => $id);
           if (count($contacts) >= $MAXNUM)
             break;
         }
@@ -95,17 +127,16 @@ if (!empty($book_types) && strlen($search)) {
     }
   }
 
-  usort($contacts, 'contact_results_sort');
+  if (count($contacts)) {
+    // sort contacts index
+    asort($sort_keys, SORT_LOCALE_STRING);
+    // re-sort contacts according to index
+    foreach ($sort_keys as $idx => $val) {
+      $sort_keys[$idx] = $contacts[$idx];
+    }
+    $contacts = array_values($sort_keys);
+  }
 }
 
 $OUTPUT->command('ksearch_query_results', $contacts, $search, $sid);
 $OUTPUT->send();
-
-
-function contact_results_sort($a, $b)
-{
-  $name_a = is_array($a) ? $a['name'] : $a;
-  $name_b = is_array($b) ? $b['name'] : $b;
-  return strcoll(trim($name_a, '" '), trim($name_b, '" '));
-}
-
index f3c9be0805d3f1c6a4babda60ed57ea8b6f4be0e..3b6b4ecad17ef79877a345b94f717a6b625a2f36 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: check_recent.inc 4872 2011-06-22 05:52:48Z thomasb $
+ $Id: check_recent.inc 5266 2011-09-22 07:49:33Z alec $
 
 */
 
@@ -34,10 +34,24 @@ else {
 
 // check recent/unseen counts
 foreach ($a_mailboxes as $mbox_name) {
-    if ($mbox_name == $current && ($status = $IMAP->mailbox_status($mbox_name))) {
+    $is_current = $mbox_name == $current;
+    if ($is_current) {
+        // Synchronize mailbox cache, handle flag changes
+        $IMAP->mailbox_sync($mbox_name);
+    }
+
+    // Get mailbox status
+    $status = $IMAP->mailbox_status($mbox_name);
+
+    if ($status & 1) {
+        // trigger plugin hook
+        $RCMAIL->plugins->exec_hook('new_messages',
+            array('mailbox' => $mbox_name, 'is_current' => $is_current));
+    }
 
-        rcmail_send_unread_count($mbox_name, true);
+    rcmail_send_unread_count($mbox_name, true);
 
+    if ($status && $is_current) {
         // refresh saved search set
         $search_request = get_input_value('_search', RCUBE_INPUT_GPC);
         if ($search_request && isset($_SESSION['search'])
@@ -67,14 +81,9 @@ foreach ($a_mailboxes as $mbox_name) {
 
         $OUTPUT->set_env('messagecount', $all_count);
         $OUTPUT->set_env('pagecount', ceil($all_count/$IMAP->page_size));
-        $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($all_count));
+        $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($all_count), $mbox_name);
         $OUTPUT->set_env('current_page', $all_count ? $IMAP->list_page : 1);
 
-        if ($status & 1) {
-            // trigger plugin hook
-            $RCMAIL->plugins->exec_hook('new_messages', array('mailbox' => $mbox_name));
-        }
-
         // remove old rows (and clear selection if new list is empty)
         $OUTPUT->command('message_list.clear', $all_count ? false : true);
 
@@ -86,9 +95,6 @@ foreach ($a_mailboxes as $mbox_name) {
             $OUTPUT->command('update_selection');
         }
     }
-    else {
-        rcmail_send_unread_count($mbox_name, true);
-    }
 }
 
 $RCMAIL->plugins->exec_hook('keep_alive', array());
index 8c60a7ea24822fb3c77e9f61ef5fec10004b5683..ca10898015720cc2e57be4c90e210a8fdf441294 100644 (file)
@@ -5,7 +5,7 @@
  | program/steps/mail/compose.inc                                        |
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2005-2009, The Roundcube Dev Team                       |
+ | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: compose.inc 5281 2011-09-27 07:29:49Z alec $
+ $Id: compose.inc 5545 2011-12-05 12:59:21Z thomasb $
 
 */
 
@@ -25,15 +25,26 @@ define('RCUBE_COMPOSE_FORWARD', 0x0107);
 define('RCUBE_COMPOSE_DRAFT', 0x0108);
 define('RCUBE_COMPOSE_EDIT', 0x0109);
 
-$MESSAGE_FORM = NULL;
-$MESSAGE = NULL;
-
-$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GET);
-$_SESSION['compose'] = $_SESSION['compose_data_'.$COMPOSE_ID];
+$MESSAGE_FORM = null;
+$MESSAGE      = null;
+$COMPOSE_ID   = get_input_value('_id', RCUBE_INPUT_GET);
+$COMPOSE      = null;
+
+if ($COMPOSE_ID && $_SESSION['compose_data_'.$COMPOSE_ID])
+  $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
+
+// give replicated session storage some time to synchronize
+$retries = 0;
+while ($COMPOSE_ID && !is_array($COMPOSE) && $RCMAIL->db->is_replicated() && $retries++ < 5) {
+  usleep(500000);
+  $RCMAIL->session->reload();
+  if ($_SESSION['compose_data_'.$COMPOSE_ID])
+    $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
+}
 
 // Nothing below is called during message composition, only at "new/forward/reply/draft" initialization or
 // if a compose-ID is given (i.e. when the compose step is opened in a new window/tab).
-if (!is_array($_SESSION['compose']))
+if (!is_array($COMPOSE))
 {
   // Infinite redirect prevention in case of broken session (#1487028)
   if ($COMPOSE_ID)
@@ -41,31 +52,33 @@ if (!is_array($_SESSION['compose']))
       'file' => __FILE__, 'line' => __LINE__,
       'message' => "Invalid compose ID"), true, true);
 
-  $_SESSION['compose'] = array(
-    'id' => uniqid(mt_rand()),
-    'param' => request2param(RCUBE_INPUT_GET),
+  $COMPOSE_ID = uniqid(mt_rand());
+  $_SESSION['compose_data_'.$COMPOSE_ID] = array(
+    'id'      => $COMPOSE_ID,
+    'param'   => request2param(RCUBE_INPUT_GET),
     'mailbox' => $IMAP->get_mailbox_name(),
   );
+  $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
 
   // process values like "mailto:foo@bar.com?subject=new+message&cc=another"
-  if ($_SESSION['compose']['param']['to']) {
+  if ($COMPOSE['param']['to']) {
     // #1486037: remove "mailto:" prefix
-    $_SESSION['compose']['param']['to'] = preg_replace('/^mailto:/i', '', $_SESSION['compose']['param']['to']);
-    $mailto = explode('?', $_SESSION['compose']['param']['to']);
+    $COMPOSE['param']['to'] = preg_replace('/^mailto:/i', '', $COMPOSE['param']['to']);
+    $mailto = explode('?', $COMPOSE['param']['to']);
     if (count($mailto) > 1) {
-      $_SESSION['compose']['param']['to'] = $mailto[0];
+      $COMPOSE['param']['to'] = $mailto[0];
       parse_str($mailto[1], $query);
       foreach ($query as $f => $val)
-        $_SESSION['compose']['param'][$f] = $val;
+        $COMPOSE['param'][$f] = $val;
     }
   }
 
   // select folder where to save the sent message
-  $_SESSION['compose']['param']['sent_mbox'] = $RCMAIL->config->get('sent_mbox');
+  $COMPOSE['param']['sent_mbox'] = $RCMAIL->config->get('sent_mbox');
 
   // pipe compose parameters thru plugins
-  $plugin = $RCMAIL->plugins->exec_hook('message_compose', $_SESSION['compose']);
-  $_SESSION['compose']['param'] = array_merge($_SESSION['compose']['param'], $plugin['param']);
+  $plugin = $RCMAIL->plugins->exec_hook('message_compose', $COMPOSE);
+  $COMPOSE['param'] = array_merge($COMPOSE['param'], $plugin['param']);
 
   // add attachments listed by message_compose hook
   if (is_array($plugin['attachments'])) {
@@ -92,18 +105,18 @@ if (!is_array($_SESSION['compose']))
 
       if ($attachment['status'] && !$attachment['abort']) {
         unset($attachment['data'], $attachment['status'], $attachment['abort']);
-        $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
+        $COMPOSE['attachments'][$attachment['id']] = $attachment;
       }
     }
   }
 
   // check if folder for saving sent messages exists and is subscribed (#1486802)
-  if ($sent_folder = $_SESSION['compose']['param']['sent_mbox']) {
+  if ($sent_folder = $COMPOSE['param']['sent_mbox']) {
     rcmail_check_sent_folder($sent_folder, true);
   }
 
   // redirect to a unique URL with all parameters stored in session
-  $OUTPUT->redirect(array('_action' => 'compose', '_id' => $_SESSION['compose']['id']));
+  $OUTPUT->redirect(array('_action' => 'compose', '_id' => $COMPOSE['id']));
 }
 
 
@@ -111,9 +124,9 @@ if (!is_array($_SESSION['compose']))
 $OUTPUT->add_label('nosubject', 'nosenderwarning', 'norecipientwarning', 'nosubjectwarning', 'cancel',
     'nobodywarning', 'notsentwarning', 'notuploadedwarning', 'savingmessage', 'sendingmessage', 
     'messagesaved', 'converting', 'editorwarning', 'searching', 'uploading', 'uploadingmany',
-    'fileuploaderror');
+    'fileuploaderror', 'sendmessage');
 
-$OUTPUT->set_env('compose_id', $COMPOSE_ID);
+$OUTPUT->set_env('compose_id', $COMPOSE['id']);
 
 // add config parameters to client script
 if (!empty($CONFIG['drafts_mbox'])) {
@@ -122,19 +135,23 @@ if (!empty($CONFIG['drafts_mbox'])) {
 }
 // set current mailbox in client environment
 $OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name());
-$OUTPUT->set_env('sig_above', $CONFIG['sig_above']);
-$OUTPUT->set_env('top_posting', $CONFIG['top_posting']);
+$OUTPUT->set_env('sig_above', $RCMAIL->config->get('sig_above', false));
+$OUTPUT->set_env('top_posting', $RCMAIL->config->get('top_posting', false));
+$OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ',')));
+
+// use jquery UI for showing prompt() dialogs
+$RCMAIL->plugins->load_plugin('jqueryui');
 
 // get reference message and set compose mode
-if ($msg_uid = $_SESSION['compose']['param']['draft_uid']) {
+if ($msg_uid = $COMPOSE['param']['draft_uid']) {
   $RCMAIL->imap->set_mailbox($CONFIG['drafts_mbox']);
   $compose_mode = RCUBE_COMPOSE_DRAFT;
 }
-else if ($msg_uid = $_SESSION['compose']['param']['reply_uid'])
+else if ($msg_uid = $COMPOSE['param']['reply_uid'])
   $compose_mode = RCUBE_COMPOSE_REPLY;
-else if ($msg_uid = $_SESSION['compose']['param']['forward_uid'])
+else if ($msg_uid = $COMPOSE['param']['forward_uid'])
   $compose_mode = RCUBE_COMPOSE_FORWARD;
-else if ($msg_uid = $_SESSION['compose']['param']['uid'])
+else if ($msg_uid = $COMPOSE['param']['uid'])
   $compose_mode = RCUBE_COMPOSE_EDIT;
 
 $config_show_sig = $RCMAIL->config->get('show_sig', 1);
@@ -156,30 +173,30 @@ if (!empty($msg_uid))
   // re-set 'prefer_html' to have possibility to use html part for compose
   $CONFIG['prefer_html'] = $CONFIG['prefer_html'] || $CONFIG['htmleditor'] || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT;
   $MESSAGE = new rcube_message($msg_uid);
-  
+
   // make sure message is marked as read
-  if ($MESSAGE && $MESSAGE->headers && !$MESSAGE->headers->seen)
+  if ($MESSAGE && $MESSAGE->headers && empty($MESSAGE->headers->flags['SEEN']))
     $IMAP->set_flag($msg_uid, 'SEEN');
 
   if (!empty($MESSAGE->headers->charset))
     $IMAP->set_charset($MESSAGE->headers->charset);
-    
+
   if ($compose_mode == RCUBE_COMPOSE_REPLY)
   {
-    $_SESSION['compose']['reply_uid'] = $msg_uid;
-    $_SESSION['compose']['reply_msgid'] = $MESSAGE->headers->messageID;
-    $_SESSION['compose']['references']  = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID);
+    $COMPOSE['reply_uid'] = $msg_uid;
+    $COMPOSE['reply_msgid'] = $MESSAGE->headers->messageID;
+    $COMPOSE['references']  = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID);
 
-    if (!empty($_SESSION['compose']['param']['all']))
-      $MESSAGE->reply_all = $_SESSION['compose']['param']['all'];
+    if (!empty($COMPOSE['param']['all']))
+      $MESSAGE->reply_all = $COMPOSE['param']['all'];
 
     $OUTPUT->set_env('compose_mode', 'reply');
 
     // Save the sent message in the same folder of the message being replied to
-    if ($RCMAIL->config->get('reply_same_folder') && ($sent_folder = $_SESSION['compose']['mailbox'])
+    if ($RCMAIL->config->get('reply_same_folder') && ($sent_folder = $COMPOSE['mailbox'])
       && rcmail_check_sent_folder($sent_folder, false)
     ) {
-      $_SESSION['compose']['param']['sent_mbox'] = $sent_folder;
+      $COMPOSE['param']['sent_mbox'] = $sent_folder;
     }
   }
   else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
@@ -190,31 +207,31 @@ if (!empty($msg_uid))
       $info = rcmail_draftinfo_decode($MESSAGE->headers->others['x-draft-info']);
 
       if ($info['type'] == 'reply')
-        $_SESSION['compose']['reply_uid'] = $info['uid'];
+        $COMPOSE['reply_uid'] = $info['uid'];
       else if ($info['type'] == 'forward')
-        $_SESSION['compose']['forward_uid'] = $info['uid'];
+        $COMPOSE['forward_uid'] = $info['uid'];
 
-      $_SESSION['compose']['mailbox'] = $info['folder'];
+      $COMPOSE['mailbox'] = $info['folder'];
 
       // Save the sent message in the same folder of the message being replied to
       if ($RCMAIL->config->get('reply_same_folder') && ($sent_folder = $info['folder'])
         && rcmail_check_sent_folder($sent_folder, false)
       ) {
-        $_SESSION['compose']['param']['sent_mbox'] = $sent_folder;
+        $COMPOSE['param']['sent_mbox'] = $sent_folder;
       }
     }
 
     if ($MESSAGE->headers->in_reply_to)
-      $_SESSION['compose']['reply_msgid'] = '<'.$MESSAGE->headers->in_reply_to.'>';
+      $COMPOSE['reply_msgid'] = '<'.$MESSAGE->headers->in_reply_to.'>';
 
-    $_SESSION['compose']['references']  = $MESSAGE->headers->references;
+    $COMPOSE['references']  = $MESSAGE->headers->references;
   }
   else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
   {
-    $_SESSION['compose']['forward_uid'] = $msg_uid;
+    $COMPOSE['forward_uid'] = $msg_uid;
     $OUTPUT->set_env('compose_mode', 'forward');
 
-    if (!empty($_SESSION['compose']['param']['attachment']))
+    if (!empty($COMPOSE['param']['attachment']))
       $MESSAGE->forward_attachment = true;
   }
 }
@@ -238,8 +255,8 @@ if (count($MESSAGE->identities))
 if (!empty($_POST['_from'])) {
   $MESSAGE->compose['from'] = get_input_value('_from', RCUBE_INPUT_POST);
 }
-else if (!empty($_SESSION['compose']['param']['from'])) {
-  $MESSAGE->compose['from'] = $_SESSION['compose']['param']['from'];
+else if (!empty($COMPOSE['param']['from'])) {
+  $MESSAGE->compose['from'] = $COMPOSE['param']['from'];
 }
 else if (count($MESSAGE->identities)) {
   $a_recipients = array();
@@ -324,23 +341,28 @@ else if (count($MESSAGE->identities)) {
 // Set other headers
 $a_recipients = array();
 $parts        = array('to', 'cc', 'bcc', 'replyto', 'followupto');
+$separator    = trim($RCMAIL->config->get('recipients_separator', ',')) . ' ';
 
 foreach ($parts as $header) {
   $fvalue = '';
   $decode_header = true;
 
   // we have a set of recipients stored is session
-  if ($header == 'to' && ($mailto_id = $_SESSION['compose']['param']['mailto'])
+  if ($header == 'to' && ($mailto_id = $COMPOSE['param']['mailto'])
       && $_SESSION['mailto'][$mailto_id]
   ) {
     $fvalue = urldecode($_SESSION['mailto'][$mailto_id]);
     $decode_header = false;
+
+    // make session to not grow up too much
+    unset($_SESSION['mailto'][$mailto_id]);
+    $COMPOSE['param']['to'] = $fvalue;
   }
   else if (!empty($_POST['_'.$header])) {
     $fvalue = get_input_value('_'.$header, RCUBE_INPUT_POST, TRUE);
   }
-  else if (!empty($_SESSION['compose']['param'][$header])) {
-    $fvalue = $_SESSION['compose']['param'][$header];
+  else if (!empty($COMPOSE['param'][$header])) {
+    $fvalue = $COMPOSE['param'][$header];
   }
   else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
     // get recipent address(es) out of the message headers
@@ -348,11 +370,13 @@ foreach ($parts as $header) {
       $mailfollowup = $MESSAGE->headers->others['mail-followup-to'];
       $mailreplyto  = $MESSAGE->headers->others['mail-reply-to'];
 
+      // Reply to mailing list...
       if ($MESSAGE->reply_all == 'list' && $mailfollowup)
         $fvalue = $mailfollowup;
       else if ($MESSAGE->reply_all == 'list'
         && preg_match('/<mailto:([^>]+)>/i', $MESSAGE->headers->others['list-post'], $m))
         $fvalue = $m[1];
+      // Reply to...
       else if ($MESSAGE->reply_all && $mailfollowup)
         $fvalue = $mailfollowup;
       else if ($mailreplyto)
@@ -361,13 +385,18 @@ foreach ($parts as $header) {
         $fvalue = $MESSAGE->headers->replyto;
       else if (!empty($MESSAGE->headers->from))
         $fvalue = $MESSAGE->headers->from;
+
+      // Reply to message sent by yourself (#1487074)
+      if (!empty($ident) && $fvalue == $ident['ident']) {
+        $fvalue = $MESSAGE->headers->to;
+      }
     }
     // add recipient of original message if reply to all
     else if ($header == 'cc' && !empty($MESSAGE->reply_all) && $MESSAGE->reply_all != 'list') {
       if ($v = $MESSAGE->headers->to)
         $fvalue .= $v;
       if ($v = $MESSAGE->headers->cc)
-        $fvalue .= (!empty($fvalue) ? ', ' : '') . $v;
+        $fvalue .= (!empty($fvalue) ? $separator : '') . $v;
     }
   }
   else if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) {
@@ -410,7 +439,7 @@ foreach ($parts as $header) {
       }
     }
 
-    $fvalue = implode(', ', $fvalue);
+    $fvalue = implode($separator, $fvalue);
   }
 
   $MESSAGE->compose[$header] = $fvalue;
@@ -513,7 +542,7 @@ function rcmail_compose_header_from($attrib)
       $select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $identity_id);
 
       // add signature to array
-      if (!empty($sql_arr['signature']) && empty($_SESSION['compose']['param']['nosig']))
+      if (!empty($sql_arr['signature']) && empty($COMPOSE['param']['nosig']))
       {
         $a_signatures[$identity_id]['text'] = $sql_arr['signature'];
         $a_signatures[$identity_id]['is_html'] = ($sql_arr['html_signature'] == 1) ? true : false;
@@ -567,22 +596,22 @@ function rcmail_compose_editor_mode()
 
 function rcmail_prepare_message_body()
 {
-  global $RCMAIL, $MESSAGE, $compose_mode, $LINE_LENGTH, $HTML_MODE;
+  global $RCMAIL, $MESSAGE, $COMPOSE, $compose_mode, $LINE_LENGTH, $HTML_MODE;
 
   // use posted message body
   if (!empty($_POST['_message'])) {
     $body = get_input_value('_message', RCUBE_INPUT_POST, true);
     $isHtml = (bool) get_input_value('_is_html', RCUBE_INPUT_POST);
   }
-  else if ($_SESSION['compose']['param']['body']) {
-    $body = $_SESSION['compose']['param']['body'];
+  else if ($COMPOSE['param']['body']) {
+    $body = $COMPOSE['param']['body'];
     $isHtml = false;
   }
   // forward as attachment
   else if ($compose_mode == RCUBE_COMPOSE_FORWARD && $MESSAGE->forward_attachment) {
     $isHtml = rcmail_compose_editor_mode();
     $body = '';
-    if (empty($_SESSION['compose']['attachments']))
+    if (empty($COMPOSE['attachments']))
       rcmail_write_forward_attachment($MESSAGE);
   }
   // reply/edit/draft/forward
@@ -645,9 +674,9 @@ function rcmail_prepare_message_body()
   // add blocked.gif attachment (#1486516)
   if ($isHtml && preg_match('#<img src="\./program/blocked\.gif"#', $body)) {
     if ($attachment = rcmail_save_image('program/blocked.gif', 'image/gif')) {
-      $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
+      $COMPOSE['attachments'][$attachment['id']] = $attachment;
       $body = preg_replace('#\./program/blocked\.gif#',
-        $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$_SESSION['compose']['id'],
+        $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$COMPOSE['id'],
         $body);
     }
   }
@@ -706,8 +735,8 @@ function rcmail_compose_body($attrib)
 
   // include GoogieSpell
   if (!empty($CONFIG['enable_spellcheck'])) {
-
-    $engine = $RCMAIL->config->get('spellcheck_engine','googie');
+    $engine           = $RCMAIL->config->get('spellcheck_engine','googie');
+    $dictionary       = (bool) $RCMAIL->config->get('spellcheck_dictionary');
     $spellcheck_langs = (array) $RCMAIL->config->get('spellcheck_languages',
       array('da'=>'Dansk', 'de'=>'Deutsch', 'en' => 'English', 'es'=>'Español',
             'fr'=>'Français', 'it'=>'Italiano', 'nl'=>'Nederlands', 'pl'=>'Polski',
@@ -737,25 +766,28 @@ function rcmail_compose_body($attrib)
     foreach ($spellcheck_langs as $key => $name) {
       $editor_lang_set[] = ($key == $lang ? '+' : '') . JQ($name).'='.JQ($key);
     }
-    
+
     $OUTPUT->include_script('googiespell.js');
     $OUTPUT->add_script(sprintf(
-      "var googie = new GoogieSpell('\$__skin_path/images/googiespell/','?_task=utils&_action=spell&lang=');\n".
+      "var googie = new GoogieSpell('\$__skin_path/images/googiespell/','?_task=utils&_action=spell&lang=', %s);\n".
       "googie.lang_chck_spell = \"%s\";\n".
       "googie.lang_rsm_edt = \"%s\";\n".
       "googie.lang_close = \"%s\";\n".
       "googie.lang_revert = \"%s\";\n".
       "googie.lang_no_error_found = \"%s\";\n".
+      "googie.lang_learn_word = \"%s\";\n".
       "googie.setLanguages(%s);\n".
       "googie.setCurrentLanguage('%s');\n".
       "googie.setSpellContainer('spellcheck-control');\n".
       "googie.decorateTextarea('%s');\n".
       "%s.set_env('spellcheck', googie);",
+      !empty($dictionary) ? 'true' : 'false',
       JQ(Q(rcube_label('checkspelling'))),
       JQ(Q(rcube_label('resumeediting'))),
       JQ(Q(rcube_label('close'))),
       JQ(Q(rcube_label('revertto'))),
       JQ(Q(rcube_label('nospellerrors'))),
+      JQ(Q(rcube_label('addtodict'))),
       json_serialize($spellcheck_langs),
       $lang,
       $attrib['id'],
@@ -828,24 +860,27 @@ function rcmail_create_reply_body($body, $bodyIsHtml)
 
 function rcmail_create_forward_body($body, $bodyIsHtml)
 {
-  global $IMAP, $MESSAGE, $OUTPUT;
+  global $RCMAIL, $MESSAGE, $COMPOSE;
 
   // add attachments
-  if (!isset($_SESSION['compose']['forward_attachments']) && is_array($MESSAGE->mime_parts))
+  if (!isset($COMPOSE['forward_attachments']) && is_array($MESSAGE->mime_parts))
     $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
 
+  $date    = format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long'));
+  $charset = $RCMAIL->output->get_charset();
+
   if (!$bodyIsHtml)
   {
-    $prefix = "\n\n\n-------- Original Message --------\n";
-    $prefix .= 'Subject: ' . $MESSAGE->subject . "\n";
-    $prefix .= 'Date: ' . $MESSAGE->headers->date . "\n";
-    $prefix .= 'From: ' . $MESSAGE->get_header('from') . "\n";
-    $prefix .= 'To: ' . $MESSAGE->get_header('to') . "\n";
+    $prefix = "\n\n\n-------- " . rcube_label('originalmessage') . " --------\n";
+    $prefix .= rcube_label('subject') . ': ' . $MESSAGE->subject . "\n";
+    $prefix .= rcube_label('date')    . ': ' . $date . "\n";
+    $prefix .= rcube_label('from')    . ': ' . $MESSAGE->get_header('from') . "\n";
+    $prefix .= rcube_label('to')      . ': ' . $MESSAGE->get_header('to') . "\n";
 
     if ($MESSAGE->headers->cc)
-      $prefix .= 'Cc: ' . $MESSAGE->get_header('cc') . "\n";
+      $prefix .= rcube_label('cc') . ': ' . $MESSAGE->get_header('cc') . "\n";
     if ($MESSAGE->headers->replyto && $MESSAGE->headers->replyto != $MESSAGE->headers->from)
-      $prefix .= 'Reply-To: ' . $MESSAGE->get_header('replyto') . "\n";
+      $prefix .= rcube_label('replyto') . ': ' . $MESSAGE->get_header('replyto') . "\n";
 
     $prefix .= "\n";
   }
@@ -857,24 +892,26 @@ function rcmail_create_forward_body($body, $bodyIsHtml)
     $body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
 
     $prefix = sprintf(
-      "<br /><p>-------- Original Message --------</p>" .
+      "<br /><p>-------- " . rcube_label('originalmessage') . " --------</p>" .
         "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" .
-        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Subject: </th><td>%s</td></tr>" .
-        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Date: </th><td>%s</td></tr>" .
-        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">From: </th><td>%s</td></tr>" .
-        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">To: </th><td>%s</td></tr>",
-      Q($MESSAGE->subject),
-      Q($MESSAGE->headers->date),
-      htmlspecialchars(Q($MESSAGE->get_header('from'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset()),
-      htmlspecialchars(Q($MESSAGE->get_header('to'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset()));
+        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
+        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
+        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
+        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
+      rcube_label('subject'), Q($MESSAGE->subject),
+      rcube_label('date'), Q($date),
+      rcube_label('from'), htmlspecialchars(Q($MESSAGE->get_header('from'), 'replace'), ENT_COMPAT, $charset),
+      rcube_label('to'), htmlspecialchars(Q($MESSAGE->get_header('to'), 'replace'), ENT_COMPAT, $charset));
 
     if ($MESSAGE->headers->cc)
-      $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Cc: </th><td>%s</td></tr>",
-        htmlspecialchars(Q($MESSAGE->get_header('cc'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset()));
+      $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
+        rcube_label('cc'),
+        htmlspecialchars(Q($MESSAGE->get_header('cc'), 'replace'), ENT_COMPAT, $charset));
 
     if ($MESSAGE->headers->replyto && $MESSAGE->headers->replyto != $MESSAGE->headers->from)
-      $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Reply-To: </th><td>%s</td></tr>",
-        htmlspecialchars(Q($MESSAGE->get_header('replyto'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset()));
+      $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
+        rcube_label('replyto'),
+        htmlspecialchars(Q($MESSAGE->get_header('replyto'), 'replace'), ENT_COMPAT, $charset));
 
     $prefix .= "</tbody></table><br>";
   }
@@ -885,13 +922,13 @@ function rcmail_create_forward_body($body, $bodyIsHtml)
 
 function rcmail_create_draft_body($body, $bodyIsHtml)
 {
-  global $MESSAGE, $OUTPUT;
+  global $MESSAGE, $OUTPUT, $COMPOSE;
 
   /**
    * add attachments
    * sizeof($MESSAGE->mime_parts can be 1 - e.g. attachment, but no text!
    */
-  if (empty($_SESSION['compose']['forward_attachments'])
+  if (empty($COMPOSE['forward_attachments'])
       && is_array($MESSAGE->mime_parts)
       && count($MESSAGE->mime_parts) > 0)
   {
@@ -929,7 +966,7 @@ function rcmail_remove_signature($body)
 
 function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
 {
-  global $RCMAIL;
+  global $RCMAIL, $COMPOSE;
 
   $cid_map = $messages = array();
   foreach ((array)$message->mime_parts as $pid => $part)
@@ -951,9 +988,9 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
       }
 
       if (!$skip && ($attachment = rcmail_save_attachment($message, $pid))) {
-        $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
+        $COMPOSE['attachments'][$attachment['id']] = $attachment;
         if ($bodyIsHtml && ($part->content_id || $part->content_location)) {
-          $url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$_SESSION['compose']['id'];
+          $url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$COMPOSE['id'];
           if ($part->content_id)
             $cid_map['cid:'.$part->content_id] = $url;
           else
@@ -963,7 +1000,7 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
     }
   }
 
-  $_SESSION['compose']['forward_attachments'] = true;
+  $COMPOSE['forward_attachments'] = true;
 
   return $cid_map;
 }
@@ -971,14 +1008,14 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
 
 function rcmail_write_inline_attachments(&$message)
 {
-  global $RCMAIL;
+  global $RCMAIL, $COMPOSE;
 
   $cid_map = array();
   foreach ((array)$message->mime_parts as $pid => $part) {
     if (($part->content_id || $part->content_location) && $part->filename) {
       if ($attachment = rcmail_save_attachment($message, $pid)) {
-        $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
-        $url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$_SESSION['compose']['id'];
+        $COMPOSE['attachments'][$attachment['id']] = $attachment;
+        $url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$COMPOSE['id'];
         if ($part->content_id)
           $cid_map['cid:'.$part->content_id] = $url;
         else
@@ -993,7 +1030,7 @@ function rcmail_write_inline_attachments(&$message)
 // Creates an attachment from the forwarded message
 function rcmail_write_forward_attachment(&$message)
 {
-  global $RCMAIL;
+  global $RCMAIL, $COMPOSE;
 
   if (strlen($message->subject)) {
     $name = mb_substr($message->subject, 0, 64) . '.eml';
@@ -1020,7 +1057,7 @@ function rcmail_write_forward_attachment(&$message)
   }
 
   $attachment = array(
-    'group' => $_SESSION['compose']['id'],
+    'group' => $COMPOSE['id'],
     'name' => $name,
     'mimetype' => 'message/rfc822',
     'data' => $data,
@@ -1032,7 +1069,7 @@ function rcmail_write_forward_attachment(&$message)
 
   if ($attachment['status']) {
     unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
-    $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
+    $COMPOSE['attachments'][$attachment['id']] = $attachment;
     return true;
   } else if ($path) {
     @unlink($path);
@@ -1044,6 +1081,8 @@ function rcmail_write_forward_attachment(&$message)
 
 function rcmail_save_attachment(&$message, $pid)
 {
+  global $COMPOSE;
+
   $rcmail = rcmail::get_instance();
   $part = $message->mime_parts[$pid];
   $mem_limit = parse_bytes(ini_get('memory_limit'));
@@ -1064,7 +1103,7 @@ function rcmail_save_attachment(&$message, $pid)
   }
 
   $attachment = array(
-    'group' => $_SESSION['compose']['id'],
+    'group' => $COMPOSE['id'],
     'name' => $part->filename ? $part->filename : 'Part_'.$pid.'.'.$part->ctype_secondary,
     'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
     'content_id' => $part->content_id,
@@ -1087,11 +1126,13 @@ function rcmail_save_attachment(&$message, $pid)
 
 function rcmail_save_image($path, $mimetype='')
 {
+  global $COMPOSE;
+
   // handle attachments in memory
   $data = file_get_contents($path);
 
   $attachment = array(
-    'group' => $_SESSION['compose']['id'],
+    'group' => $COMPOSE['id'],
     'name' => rcmail_basename($path),
     'mimetype' => $mimetype ? $mimetype : rc_mime_content_type($path, $name),
     'data' => $data,
@@ -1120,11 +1161,11 @@ function rcmail_basename($filename)
 
 function rcmail_compose_subject($attrib)
 {
-  global $MESSAGE, $compose_mode;
-  
+  global $MESSAGE, $COMPOSE, $compose_mode;
+
   list($form_start, $form_end) = get_form_tags($attrib);
   unset($attrib['form']);
-  
+
   $attrib['name'] = '_subject';
   $attrib['spellcheck'] = 'true';
   $textfield = new html_inputfield($attrib);
@@ -1153,10 +1194,10 @@ function rcmail_compose_subject($attrib)
   else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) {
     $subject = $MESSAGE->subject;
   }
-  else if (!empty($_SESSION['compose']['param']['subject'])) {
-    $subject = $_SESSION['compose']['param']['subject'];
+  else if (!empty($COMPOSE['param']['subject'])) {
+    $subject = $COMPOSE['param']['subject'];
   }
-  
+
   $out = $form_start ? "$form_start\n" : '';
   $out .= $textfield->show($subject);
   $out .= $form_end ? "\n$form_end" : '';
@@ -1167,17 +1208,16 @@ function rcmail_compose_subject($attrib)
 
 function rcmail_compose_attachment_list($attrib)
 {
-  global $OUTPUT, $CONFIG;
-  
+  global $OUTPUT, $CONFIG, $COMPOSE;
+
   // add ID if not given
   if (!$attrib['id'])
     $attrib['id'] = 'rcmAttachmentList';
-  
+
   $out = "\n";
   $jslist = array();
 
-  if (is_array($_SESSION['compose']['attachments']))
-  {
+  if (is_array($COMPOSE['attachments'])) {
     if ($attrib['deleteicon']) {
       $button = html::img(array(
         'src' => $CONFIG['skin_path'] . $attrib['deleteicon'],
@@ -1187,11 +1227,11 @@ function rcmail_compose_attachment_list($attrib)
     else
       $button = Q(rcube_label('delete'));
 
-    foreach ($_SESSION['compose']['attachments'] as $id => $a_prop)
+    foreach ($COMPOSE['attachments'] as $id => $a_prop)
     {
       if (empty($a_prop))
         continue;
-      
+
       $out .= html::tag('li', array('id' => 'rcmfile'.$id),
         html::a(array(
             'href' => "#delete",
@@ -1204,7 +1244,7 @@ function rcmail_compose_attachment_list($attrib)
   }
 
   if ($attrib['deleteicon'])
-    $_SESSION['compose']['deleteicon'] = $CONFIG['skin_path'] . $attrib['deleteicon'];
+    $COMPOSE['deleteicon'] = $CONFIG['skin_path'] . $attrib['deleteicon'];
   if ($attrib['cancelicon'])
     $OUTPUT->set_env('cancelicon', $CONFIG['skin_path'] . $attrib['cancelicon']);
   if ($attrib['loadingicon'])
@@ -1372,12 +1412,15 @@ function rcmail_editor_selector($attrib)
 
 function rcmail_store_target_selection($attrib)
 {
+  global $COMPOSE;
+
   $attrib['name'] = '_store_target';
   $select = rcmail_mailbox_select(array_merge($attrib, array(
     'noselection' => '- '.rcube_label('dontsave').' -',
-    'folder_filter' => 'mail'
+    'folder_filter' => 'mail',
+    'folder_rights' => 'w',
   )));
-  return $select->show($_SESSION['compose']['param']['sent_mbox'], $attrib);
+  return $select->show($COMPOSE['param']['sent_mbox'], $attrib);
 }
 
 
@@ -1403,14 +1446,14 @@ function rcmail_check_sent_folder($folder, $create=false)
 
 function get_form_tags($attrib)
 {
-  global $RCMAIL, $MESSAGE_FORM;
+  global $RCMAIL, $MESSAGE_FORM, $COMPOSE;
 
   $form_start = '';
   if (!$MESSAGE_FORM)
   {
     $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $RCMAIL->task));
     $hiddenfields->add(array('name' => '_action', 'value' => 'send'));
-    $hiddenfields->add(array('name' => '_id', 'value' => $_SESSION['compose']['id']));
+    $hiddenfields->add(array('name' => '_id', 'value' => $COMPOSE['id']));
 
     $form_start = empty($attrib['form']) ? $RCMAIL->output->form_tag(array('name' => "form", 'method' => "post")) : '';
     $form_start .= $hiddenfields->show();
index 7e0399e47fb569709da73ce1d96c44fac50df40e..80936a0049c2b2880de439c7f6bc3fe2d700343f 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: folders.inc 4410 2011-01-12 18:25:02Z thomasb $
+ $Id: folders.inc 5266 2011-09-22 07:49:33Z alec $
 */
 
 // only process ajax requests
@@ -65,7 +65,7 @@ else if ($RCMAIL->action == 'purge')
                 $OUTPUT->set_env('messagecount', 0);
                 $OUTPUT->set_env('pagecount', 0);
                 $OUTPUT->command('message_list.clear');
-                $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text());
+                $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text(), $mbox);
                 $OUTPUT->command('set_unread_count', $mbox, 0);
                 $OUTPUT->command('set_quota', rcmail_quota_content());
                 rcmail_set_unseen_count($mbox, 0);
index 8b0e58943d45ae6afe8cc8c21df21af14fb681dc..3c93cda1d993acdf1e6c2a9e88726b8223281e98 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: func.inc 5235 2011-09-19 06:43:57Z alec $
+ $Id: func.inc 5601 2011-12-14 09:08:54Z alec $
 
 */
 
@@ -253,7 +253,7 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null
     return;
 
   // remove 'threads', 'attachment', 'flag', 'status' columns, we don't need them here
-  foreach (array('threads', 'attachment', 'flag', 'status') as $col) {
+  foreach (array('threads', 'attachment', 'flag', 'status', 'priority') as $col) {
     if (($key = array_search($col, $a_show_cols)) !== FALSE)
       unset($a_show_cols[$key]);
   }
@@ -287,6 +287,7 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null
       $a_msg_cols[$col] = $cont;
     }
 
+    $a_msg_flags = array_change_key_case(array_map('intval', (array) $header->flags));
     if ($header->depth)
       $a_msg_flags['depth'] = $header->depth;
     else if ($header->has_children)
@@ -297,23 +298,15 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null
       $a_msg_flags['has_children'] = $header->has_children;
     if ($header->unread_children)
       $a_msg_flags['unread_children'] = $header->unread_children;
-    if ($header->deleted)
-      $a_msg_flags['deleted'] = 1;
-    if (!$header->seen)
-      $a_msg_flags['unread'] = 1;
-    if ($header->answered)
-      $a_msg_flags['replied'] = 1;
-    if ($header->forwarded)
-      $a_msg_flags['forwarded'] = 1;
-    if ($header->flagged)
-      $a_msg_flags['flagged'] = 1;
     if ($header->others['list-post'])
       $a_msg_flags['ml'] = 1;
+    if ($header->priority)
+      $a_msg_flags['prio'] = (int) $header->priority;
 
     $a_msg_flags['ctype'] = Q($header->ctype);
     $a_msg_flags['mbox'] = $mbox;
 
-    // merge with plugin result
+    // merge with plugin result (Deprecated, use $header->flags)
     if (!empty($header->list_flags) && is_array($header->list_flags))
       $a_msg_flags = array_merge($a_msg_flags, $header->list_flags);
     if (!empty($header->list_cols) && is_array($header->list_cols))
@@ -327,7 +320,7 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null
   }
 
   if ($IMAP->threading) {
-    $OUTPUT->command('init_threads', (array) $roots);
+    $OUTPUT->command('init_threads', (array) $roots, $mbox);
   }
 }
 
@@ -372,6 +365,7 @@ function rcmail_message_list_head($attrib, $a_show_cols)
         $col_name = '<span class="flagged">&nbsp;</span>';
         break;
       case 'attachment':
+      case 'priority':
       case 'status':
         $col_name = '<span class="' . $col .'">&nbsp;</span>';
         break;
@@ -556,7 +550,7 @@ function rcmail_check_safe(&$message)
  * @param array  CID map replaces (inline images)
  * @return string Clean HTML
  */
-function rcmail_wash_html($html, $p = array(), $cid_replaces)
+function rcmail_wash_html($html, $p, $cid_replaces)
 {
   global $REMOTE_OBJECTS;
 
@@ -565,7 +559,7 @@ function rcmail_wash_html($html, $p = array(), $cid_replaces)
   // special replacements (not properly handled by washtml class)
   $html_search = array(
     '/(<\/nobr>)(\s+)(<nobr>)/i',      // space(s) between <NOBR>
-    '/<title[^>]*>.*<\/title>/i',      // PHP bug #32547 workaround: remove title tag
+    '/<title[^>]*>[^<]*<\/title>/i',   // PHP bug #32547 workaround: remove title tag
     '/^(\0\0\xFE\xFF|\xFF\xFE\0\0|\xFE\xFF|\xFF\xFE|\xEF\xBB\xBF)/',   // byte-order mark (only outlook?)
     '/<html\s[^>]+>/i',                        // washtml/DOMDocument cannot handle xml namespaces
   );
@@ -596,16 +590,16 @@ function rcmail_wash_html($html, $p = array(), $cid_replaces)
   $html = preg_replace_callback('/(<[\/]*)([^\s>]+)/', 'rcmail_html_tag_callback', $html);
 
   // charset was converted to UTF-8 in rcube_imap::get_message_part(),
-  // -> change charset specification in HTML accordingly
-  $charset_pattern = '(<meta\s+[^>]*content=)[\'"]?(\w+\/\w+;\s*charset=)([a-z0-9-_]+[\'"]?)';
-  if (preg_match("/$charset_pattern/Ui", $html)) {
-    $html = preg_replace("/$charset_pattern/i", '\\1"\\2'.RCMAIL_CHARSET.'"', $html);
-  }
-  else {
-    // add meta content-type to malformed messages, washtml cannot work without that
-    if (!preg_match('/<head[^>]*>(.*)<\/head>/Uims', $html))
-      $html = '<head></head>'. $html;
-    $html = substr_replace($html, '<meta http-equiv="Content-Type" content="text/html; charset='.RCMAIL_CHARSET.'" />', intval(stripos($html, '<head>')+6), 0);
+  // change/add charset specification in HTML accordingly,
+  // washtml cannot work without that
+  $meta = '<meta http-equiv="Content-Type" content="text/html; charset='.RCMAIL_CHARSET.'" />';
+
+  // remove old meta tag and add the new one, making sure
+  // that it is placed in the head (#1488093)
+  $html = preg_replace('/<meta[^>]+charset=[a-z0-9-_]+[^>]*>/Ui', '', $html);
+  $html = preg_replace('/(<head[^>]*>)/Ui', '\\1'.$meta, $html, -1, $rcount);
+  if (!$rcount) {
+    $html = '<head>' . $meta . '</head>' . $html;
   }
 
   // turn relative into absolute urls
@@ -645,6 +639,9 @@ function rcmail_wash_html($html, $p = array(), $cid_replaces)
   if (!$p['skip_washer_style_callback'])
     $washer->add_callback('style', 'rcmail_washtml_callback');
 
+  // Remove non-UTF8 characters (#1487813)
+  $html = rc_utf8_clean($html);
+
   $html = $washer->wash($html);
   $REMOTE_OBJECTS = $washer->extlinks;
 
@@ -772,7 +769,7 @@ function rcmail_plain_body($body, $flowed=false)
 
           // previous line is flowed?
           if (isset($body[$last]) && $body[$n]
-            && $last != $last_sig
+            && $last !== $last_sig
             && $body[$last][strlen($body[$last])-1] == ' '
           ) {
             $body[$last] .= $body[$n];
@@ -824,7 +821,7 @@ function rcmail_plain_body($body, $flowed=false)
 /**
  * Callback function for washtml cleaning class
  */
-function rcmail_washtml_callback($tagname, $attrib, $content)
+function rcmail_washtml_callback($tagname, $attrib, $content, $washtml)
 {
   switch ($tagname) {
     case 'form':
@@ -836,8 +833,11 @@ function rcmail_washtml_callback($tagname, $attrib, $content)
       $stripped = preg_replace('/[^a-zA-Z\(:;]/', '', rcmail_xss_entity_decode($content));
 
       // now check for evil strings like expression, behavior or url()
-      if (!preg_match('/expression|behavior|url\(|import[^a]/', $stripped)) {
-        $out = html::tag('style', array('type' => 'text/css'), $content);
+      if (!preg_match('/expression|behavior|javascript:|import[^a]/i', $stripped)) {
+        if (!$washtml->get_config('allow_remote') && stripos($stripped, 'url('))
+          $washtml->extlinks = true;
+        else
+          $out = html::tag('style', array('type' => 'text/css'), $content);
         break;
       }
 
@@ -1017,7 +1017,7 @@ function rcmail_message_body($attrib)
         $body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$CONFIG['prefer_html']));
 
         if ($part->ctype_secondary == 'html') {
-          $body = rcmail_html4inline($body, $attrib['id'], 'rcmBody', $attrs);
+          $body = rcmail_html4inline($body, $attrib['id'], 'rcmBody', $attrs, $safe_mode);
           $div_attr = array('class' => 'message-htmlpart');
           $style = array();
 
@@ -1043,15 +1043,14 @@ function rcmail_message_body($attrib)
       rcmail_plain_body(Q($MESSAGE->body, 'strict', false))));
     }
 
-  $ctype_primary = strtolower($MESSAGE->structure->ctype_primary);
-  $ctype_secondary = strtolower($MESSAGE->structure->ctype_secondary);
-
   // list images after mail body
-  if ($CONFIG['inline_images']
-      && $ctype_primary == 'multipart'
-      && !empty($MESSAGE->attachments))
-    {
+  if ($CONFIG['inline_images'] && !empty($MESSAGE->attachments)) {
     foreach ($MESSAGE->attachments as $attach_prop) {
+      // skip inline images
+      if ($attach_prop->content_id && $attach_prop->disposition == 'inline') {
+        continue;
+      }
+
       // Content-Type: image/*...
       if (preg_match('/^image\//i', $attach_prop->mimetype) ||
         // ...or known file extension: many clients are using application/octet-stream
@@ -1061,7 +1060,7 @@ function rcmail_message_body($attrib)
       ) {
         $out .= html::tag('hr') . html::p(array('align' => "center"),
           html::img(array(
-            'src' => $MESSAGE->get_part_url($attach_prop->mime_id),
+            'src' => $MESSAGE->get_part_url($attach_prop->mime_id, true),
             'title' => $attach_prop->filename,
             'alt' => $attach_prop->filename,
           )));
@@ -1097,7 +1096,7 @@ function rcmail_resolve_base($body)
 /**
  * modify a HTML message that it can be displayed inside a HTML page
  */
-function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null)
+function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null, $allow_remote=false)
 {
   $last_style_pos = 0;
   $body_lc = strtolower($body);
@@ -1110,7 +1109,7 @@ function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null
 
     // replace all css definitions with #container [def]
     $styles = rcmail_mod_css_styles(
-      substr($body, $pos, $pos2-$pos), $cont_id);
+      substr($body, $pos, $pos2-$pos), $cont_id, $allow_remote);
 
     $body = substr($body, 0, $pos) . $styles . substr($body, $pos2);
     $body_lc = strtolower($body);
@@ -1288,6 +1287,7 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null)
             'href' => "#add",
             'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, urlencode($string)),
             'title' => rcube_label('addtoaddressbook'),
+            'class' => 'rcmaddcontact',
           ),
           html::img(array(
             'src' => $CONFIG['skin_path'] . $addicon,
@@ -1451,7 +1451,7 @@ function rcmail_send_mdn($message, &$smtp_error)
   if (!is_object($message) || !is_a($message, 'rcube_message'))
     $message = new rcube_message($message);
 
-  if ($message->headers->mdn_to && !$message->headers->mdn_sent &&
+  if ($message->headers->mdn_to && empty($message->headers->flags['MDNSENT']) &&
     ($IMAP->check_permflag('MDNSENT') || $IMAP->check_permflag('*')))
   {
     $identity = $RCMAIL->user->get_identity();
@@ -1551,6 +1551,11 @@ function rcmail_search_filter($attrib)
   $select_filter->add(rcube_label('unanswered'), 'UNANSWERED');
   if (!$CONFIG['skip_deleted'])
     $select_filter->add(rcube_label('deleted'), 'DELETED');
+  $select_filter->add(rcube_label('priority').': '.rcube_label('highest'), 'HEADER X-PRIORITY 1');
+  $select_filter->add(rcube_label('priority').': '.rcube_label('high'), 'HEADER X-PRIORITY 2');
+  $select_filter->add(rcube_label('priority').': '.rcube_label('normal'), 'NOT HEADER X-PRIORITY 1 NOT HEADER X-PRIORITY 2 NOT HEADER X-PRIORITY 4 NOT HEADER X-PRIORITY 5');
+  $select_filter->add(rcube_label('priority').': '.rcube_label('low'), 'HEADER X-PRIORITY 4');
+  $select_filter->add(rcube_label('priority').': '.rcube_label('lowest'), 'HEADER X-PRIORITY 5');
 
   $out = $select_filter->show($_SESSION['search_filter']);
 
index bf35d0178202fde1ac2f77c0e96bf3729b821c0e..16f7acf63b67aa6aab2a8bf70ce4b79131dd4c80 100644 (file)
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: get.inc 5151 2011-08-31 12:49:44Z alec $
+ $Id: get.inc 5514 2011-11-30 11:35:43Z alec $
 
 */
 
 
 // show loading page
 if (!empty($_GET['_preload'])) {
-  $url = str_replace('&_preload=1', '', $_SERVER['REQUEST_URI']);
+  $url = preg_replace('/[&?]+_preload=1/', '', $_SERVER['REQUEST_URI']);
   $message = rcube_label('loadingdata');
 
   header('Content-Type: text/html; charset=' . RCMAIL_CHARSET);
@@ -64,8 +64,6 @@ if (!empty($_GET['_uid'])) {
   $MESSAGE = new rcube_message(get_input_value('_uid', RCUBE_INPUT_GET));
 }
 
-send_nocacheing_headers();
-
 // show part page
 if (!empty($_GET['_frame'])) {
   $OUTPUT->send('messagepart');
@@ -136,11 +134,24 @@ else if ($pid = get_input_value('_part', RCUBE_INPUT_GET)) {
 
       header("Content-Disposition: $disposition; filename=\"$filename\"");
 
-      // turn off output buffering and print part content
-      if ($part->body)
-        echo $part->body;
-      else if ($part->size)
-        $IMAP->get_message_part($MESSAGE->uid, $part->mime_id, $part, true);
+      // do content filtering to avoid XSS through fake images
+      if (!empty($_REQUEST['_embed']) && $browser->ie && $browser->ver <= 8) {
+        if ($part->body)
+          echo preg_match('/<(script|iframe|object)/i', $part->body) ? '' : $part->body;
+        else if ($part->size) {
+          $stdout = fopen('php://output', 'w');
+          stream_filter_register('rcube_content', 'rcube_content_filter') or die('Failed to register content filter');
+          stream_filter_append($stdout, 'rcube_content');
+          $IMAP->get_message_part($MESSAGE->uid, $part->mime_id, $part, false, $stdout);
+        }
+      }
+      else {
+        // turn off output buffering and print part content
+        if ($part->body)
+          echo $part->body;
+        else if ($part->size)
+          $IMAP->get_message_part($MESSAGE->uid, $part->mime_id, $part, true);
+      }
     }
 
     exit;
@@ -168,3 +179,39 @@ header('HTTP/1.1 404 Not Found');
 exit;
 
 
+
+/**
+ * PHP stream filter to detect html/javascript code in attachments
+ */
+class rcube_content_filter extends php_user_filter
+{
+  private $buffer = '';
+  private $cutoff = 2048;
+
+  function onCreate()
+  {
+    $this->cutoff = rand(2048, 3027);
+    return true;
+  }
+
+  function filter($in, $out, &$consumed, $closing)
+  {
+    while ($bucket = stream_bucket_make_writeable($in)) {
+      $this->buffer .= $bucket->data;
+
+      // check for evil content and abort
+      if (preg_match('/<(script|iframe|object)/i', $this->buffer))
+        return PSFS_ERR_FATAL;
+
+      // keep buffer small enough
+      if (strlen($this->buffer) > 4096)
+        $this->buffer = substr($this->buffer, $this->cutoff);
+
+      $consumed += $bucket->datalen;
+      stream_bucket_append($out, $bucket);
+    }
+
+    return PSFS_PASS_ON;
+  }
+}
+
index 70d7508a56accb67aacd5977976f8059627e1375..3505d481ac1d98db4f30aea1aa27838597efb6f2 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: list.inc 4410 2011-01-12 18:25:02Z thomasb $
+ $Id: list.inc 5266 2011-09-22 07:49:33Z alec $
 
 */
 
@@ -53,6 +53,9 @@ if ($save_arr)
 
 $mbox_name = $IMAP->get_mailbox_name();
 
+// Synchronize mailbox cache, handle flag changes
+$IMAP->mailbox_sync($mbox_name);
+
 // initialize searching result if search_filter is used
 if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL')
 {
@@ -93,7 +96,7 @@ $OUTPUT->set_env('messagecount', $count);
 $OUTPUT->set_env('pagecount', $pages);
 $OUTPUT->set_env('threading', (bool) $IMAP->threading);
 $OUTPUT->set_env('current_page', $count ? $IMAP->list_page : 1);
-$OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count));
+$OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count), $mbox_name);
 $OUTPUT->command('set_mailboxname', rcmail_get_mailbox_name_text());
 
 // add message rows
@@ -116,5 +119,3 @@ else {
 
 // send response
 $OUTPUT->send();
-
-
index 94009fbc7612cf003a066db2934f742d4a510fa1..2d50391a2fdcf55c9017c74044258c66f16a0e53 100644 (file)
@@ -14,7 +14,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: mark.inc 4410 2011-01-12 18:25:02Z thomasb $
+ $Id: mark.inc 5266 2011-09-22 07:49:33Z alec $
 
 */
 
@@ -104,7 +104,7 @@ if (($uids = get_input_value('_uid', RCUBE_INPUT_POST)) && ($flag = get_input_va
         $OUTPUT->command('set_unread_count', $mbox, $unseen_count, ($mbox == 'INBOX'));
         rcmail_set_unseen_count($mbox, $unseen_count);
       }
-      $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($msg_count));
+      $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($msg_count), $mbox);
 
       if ($IMAP->threading)
            $count = get_input_value('_count', RCUBE_INPUT_POST);
index b575e58a150cac9ae6a5d917cd63fb7cf1a0a533..30928d499aa0d0f3e8ebd09b7ec1852a5af6d920 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: move_del.inc 4410 2011-01-12 18:25:02Z thomasb $
+ $Id: move_del.inc 5266 2011-09-22 07:49:33Z alec $
 
 */
 
@@ -116,12 +116,12 @@ else
     rcmail_set_unseen_count($mbox, $unseen_count);
   }
 
-  if ($RCMAIL->action=='moveto' && strlen($target)) {
+  if ($RCMAIL->action == 'moveto' && strlen($target)) {
     rcmail_send_unread_count($target, true);
   }
 
   $OUTPUT->command('set_quota', rcmail_quota_content());
-  $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($msg_count));
+  $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($msg_count), $mbox);
 
   if ($IMAP->threading)
     $count = get_input_value('_count', RCUBE_INPUT_POST);
index 27bc531b0925a3fb8c7df1b074e2d029159978a7..e6fb629ab8c02133c398a18a5ed41bf14819326c 100644 (file)
@@ -11,7 +11,7 @@
  |         Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: search.inc 4812 2011-05-27 13:01:05Z alec $
+ $Id: search.inc 5527 2011-12-02 09:58:03Z alec $
 
 */
 
@@ -31,6 +31,7 @@ $str     = get_input_value('_q', RCUBE_INPUT_GET, true);
 $mbox    = get_input_value('_mbox', RCUBE_INPUT_GET, true);
 $filter  = get_input_value('_filter', RCUBE_INPUT_GET);
 $headers = get_input_value('_headers', RCUBE_INPUT_GET);
+$subject = array();
 
 $search_request = md5($mbox.$filter.$str);
 
@@ -70,15 +71,20 @@ else if (preg_match("/^body:.*/i", $str))
   list(,$srch) = explode(":", $str);
   $subject['text'] = "TEXT";
 }
-else if(trim($str))
+else if (strlen(trim($str)))
 {
   if ($headers) {
-    foreach(explode(',', $headers) as $header)
-      switch ($header) {
-        case 'text': $subject['text'] = 'TEXT'; break;
-        default:     $subject[$header] = 'HEADER '.strtoupper($header);
+    foreach (explode(',', $headers) as $header) {
+      if ($header == 'text') {
+        // #1488208: get rid of other headers when searching by "TEXT"
+        $subject = array('text' => 'TEXT');
+        break;
       }
-    
+      else {
+        $subject[$header] = 'HEADER '.strtoupper($header);
+      }
+    }
+
     // save search modifiers for the current folder to user prefs
     $search_mods = $RCMAIL->config->get('search_mods', $SEARCH_MODS_DEFAULT);
     $search_mods[$mbox] = array_fill_keys(array_keys($subject), 1);
@@ -89,9 +95,9 @@ else if(trim($str))
   }
 }
 
-$search = $srch ? trim($srch) : trim($str);
+$search = isset($srch) ? trim($srch) : trim($str);
 
-if ($subject) {
+if (!empty($subject)) {
   $search_str .= str_repeat(' OR', count($subject)-1);
   foreach ($subject as $sub)
     $search_str .= sprintf(" %s {%d}\r\n%s", $sub, strlen($search), $search);
@@ -101,7 +107,7 @@ $search_str = trim($search_str);
 
 // execute IMAP search
 if ($search_str)
-  $result = $IMAP->search($mbox, $search_str, $imap_charset, $_SESSION['sort_col']);
+  $IMAP->search($mbox, $search_str, $imap_charset, $_SESSION['sort_col']);
 
 // Get the headers
 $result_h = $IMAP->list_headers($mbox, 1, $_SESSION['sort_col'], $_SESSION['sort_order']);
@@ -135,7 +141,7 @@ else {
 $OUTPUT->set_env('search_request', $search_str ? $search_request : '');
 $OUTPUT->set_env('messagecount', $count);
 $OUTPUT->set_env('pagecount', ceil($count/$IMAP->page_size));
-$OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count, 1));
+$OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count, 1), $mbox);
 $OUTPUT->send();
 
 
index 4c4f0d1bf60e56b63a705653603e860edb79ed72..8ea9f55dde748383f55e31ffb9bb41f92de0dba8 100644 (file)
@@ -5,7 +5,7 @@
  | program/steps/mail/sendmail.inc                                       |
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2005-2010, The Roundcube Dev Team                       |
+ | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -16,7 +16,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: sendmail.inc 5229 2011-09-16 19:13:27Z thomasb $
+ $Id: sendmail.inc 5527 2011-12-02 09:58:03Z alec $
 
 */
 
@@ -27,11 +27,11 @@ $OUTPUT->framed = TRUE;
 $savedraft = !empty($_POST['_draft']) ? true : false;
 
 $COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GPC);
-$_SESSION['compose'] = $_SESSION['compose_data_'.$COMPOSE_ID];
+$COMPOSE    =& $_SESSION['compose_data_'.$COMPOSE_ID];
 
 /****** checks ********/
 
-if (!isset($_SESSION['compose']['id'])) {
+if (!isset($COMPOSE['id'])) {
   raise_error(array('code' => 500, 'type' => 'php',
     'file' => __FILE__, 'line' => __LINE__,
     'message' => "Invalid compose ID"), true, false);
@@ -138,47 +138,47 @@ function rcmail_fix_emoticon_paths(&$mime_message)
   return $body;
 }
 
-// parse email address input (and count addresses)
+/**
+ * Parse and cleanup email address input (and count addresses)
+ *
+ * @param string  Address input
+ * @param boolean Do count recipients (saved in global $RECIPIENT_COUNT)
+ * @param boolean Validate addresses (errors saved in global $EMAIL_FORMAT_ERROR)
+ * @return string Canonical recipients string separated by comma
+ */
 function rcmail_email_input_format($mailto, $count=false, $check=true)
 {
-  global $EMAIL_FORMAT_ERROR, $RECIPIENT_COUNT;
+  global $RCMAIL, $EMAIL_FORMAT_ERROR, $RECIPIENT_COUNT;
 
   // simplified email regexp, supporting quoted local part
   $email_regexp = '(\S+|("[^"]+"))@\S+';
 
-  $regexp  = array('/[,;]\s*[\r\n]+/', '/[\r\n]+/', '/[,;]\s*$/m', '/;/', '/(\S{1})(<'.$email_regexp.'>)/U');
-  $replace = array(', ', ', ', '', ',', '\\1 \\2');
+  $delim = trim($RCMAIL->config->get('recipients_separator', ','));
+  $regexp  = array("/[,;$delim]\s*[\r\n]+/", '/[\r\n]+/', "/[,;$delim]\s*\$/m", '/;/', '/(\S{1})(<'.$email_regexp.'>)/U');
+  $replace = array($delim.' ', ', ', '', $delim, '\\1 \\2');
 
   // replace new lines and strip ending ', ', make address input more valid
   $mailto = trim(preg_replace($regexp, $replace, $mailto));
 
   $result = array();
-  $items = rcube_explode_quoted_string(',', $mailto);
+  $items = rcube_explode_quoted_string($delim, $mailto);
 
   foreach($items as $item) {
     $item = trim($item);
     // address in brackets without name (do nothing)
     if (preg_match('/^<'.$email_regexp.'>$/', $item)) {
-      $item = rcube_idn_to_ascii($item);
-      $result[] = $item;
+      $item = rcube_idn_to_ascii(trim($item, '<>'));
+      $result[] = '<' . $item . '>';
     // address without brackets and without name (add brackets)
     } else if (preg_match('/^'.$email_regexp.'$/', $item)) {
       $item = rcube_idn_to_ascii($item);
-      $result[] = '<'.$item.'>';
+      $result[] = '<' . $item . '>';
     // address with name (handle name)
-    } else if (preg_match('/'.$email_regexp.'>*$/', $item, $matches)) {
+    } else if (preg_match('/<*'.$email_regexp.'>*$/', $item, $matches)) {
       $address = $matches[0];
-      $name = str_replace($address, '', $item);
-      $name = trim($name);
-      if ($name && ($name[0] != '"' || $name[strlen($name)-1] != '"')
-          && preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name)) {
-            $name = '"'.addcslashes($name, '"').'"';
-      }
-      $address = rcube_idn_to_ascii($address);
-      if (!preg_match('/^<'.$email_regexp.'>$/', $address))
-        $address = '<'.$address.'>';
-
-      $result[] = $name.' '.$address;
+      $name = trim(str_replace($address, '', $item), '" ');
+      $address = rcube_idn_to_ascii(trim($address, '<>'));
+      $result[] = format_email_recipient($address, $name);
       $item = $address;
     } else if (trim($item)) {
       continue;
@@ -340,20 +340,20 @@ if (!empty($headers['Reply-To'])) {
 if (!empty($_POST['_followupto'])) {
   $headers['Mail-Followup-To'] = rcmail_email_input_format(get_input_value('_followupto', RCUBE_INPUT_POST, TRUE, $message_charset));
 }
-if (!empty($_SESSION['compose']['reply_msgid'])) {
-  $headers['In-Reply-To'] = $_SESSION['compose']['reply_msgid'];
+if (!empty($COMPOSE['reply_msgid'])) {
+  $headers['In-Reply-To'] = $COMPOSE['reply_msgid'];
 }
 
 // remember reply/forward UIDs in special headers
-if (!empty($_SESSION['compose']['reply_uid']) && $savedraft) {
-  $headers['X-Draft-Info'] = array('type' => 'reply', 'uid' => $_SESSION['compose']['reply_uid']);
+if (!empty($COMPOSE['reply_uid']) && $savedraft) {
+  $headers['X-Draft-Info'] = array('type' => 'reply', 'uid' => $COMPOSE['reply_uid']);
 }
-else if (!empty($_SESSION['compose']['forward_uid']) && $savedraft) {
-  $headers['X-Draft-Info'] = array('type' => 'forward', 'uid' => $_SESSION['compose']['forward_uid']);
+else if (!empty($COMPOSE['forward_uid']) && $savedraft) {
+  $headers['X-Draft-Info'] = array('type' => 'forward', 'uid' => $COMPOSE['forward_uid']);
 }
 
-if (!empty($_SESSION['compose']['references'])) {
-  $headers['References'] = $_SESSION['compose']['references'];
+if (!empty($COMPOSE['references'])) {
+  $headers['References'] = $COMPOSE['references'];
 }
 
 if (!empty($_POST['_priority'])) {
@@ -374,7 +374,7 @@ $headers['Message-ID'] = $message_id;
 $headers['X-Sender'] = $from;
 
 if (is_array($headers['X-Draft-Info'])) {
-  $headers['X-Draft-Info'] = rcmail_draftinfo_encode($headers['X-Draft-Info'] + array('folder' => $_SESSION['compose']['mailbox']));
+  $headers['X-Draft-Info'] = rcmail_draftinfo_encode($headers['X-Draft-Info'] + array('folder' => $COMPOSE['mailbox']));
 }
 if (!empty($CONFIG['useragent'])) {
   $headers['User-Agent'] = $CONFIG['useragent'];
@@ -414,12 +414,12 @@ if (!$savedraft) {
 
   // Check spelling before send
   if ($CONFIG['spellcheck_before_send'] && $CONFIG['enable_spellcheck']
-    && empty($_SESSION['compose']['spell_checked']) && !empty($message_body)
+    && empty($COMPOSE['spell_checked']) && !empty($message_body)
   ) {
     $spellchecker = new rcube_spellchecker(get_input_value('_lang', RCUBE_INPUT_GPC));
     $spell_result = $spellchecker->check($message_body, $isHtml);
 
-    $_SESSION['compose']['spell_checked'] = true;
+    $COMPOSE['spell_checked'] = true;
 
     if (!$spell_result) {
       $result = $isHtml ? $spellchecker->get_words() : $spellchecker->get_xml();
@@ -458,12 +458,12 @@ $MAIL_MIME = new Mail_mime("\r\n");
 
 // Check if we have enough memory to handle the message in it
 // It's faster than using files, so we'll do this if we only can
-if (is_array($_SESSION['compose']['attachments']) && $CONFIG['smtp_server']
+if (is_array($COMPOSE['attachments']) && $CONFIG['smtp_server']
   && ($mem_limit = parse_bytes(ini_get('memory_limit'))))
 {
   $memory = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
 
-  foreach ($_SESSION['compose']['attachments'] as $id => $attachment)
+  foreach ($COMPOSE['attachments'] as $id => $attachment)
     $memory += $attachment['size'];
 
   // Yeah, Net_SMTP needs up to 12x more memory, 1.33 is for base64
@@ -527,9 +527,9 @@ else {
 }
 
 // add stored attachments, if any
-if (is_array($_SESSION['compose']['attachments']))
+if (is_array($COMPOSE['attachments']))
 {
-  foreach ($_SESSION['compose']['attachments'] as $id => $attachment) {
+  foreach ($COMPOSE['attachments'] as $id => $attachment) {
     // This hook retrieves the attachment contents from the file storage backend
     $attachment = $RCMAIL->plugins->exec_hook('attachment_get', $attachment);
 
@@ -624,12 +624,12 @@ if (!$savedraft)
   // save message sent time
   if (!empty($CONFIG['sendmail_delay']))
     $RCMAIL->user->save_prefs(array('last_message_time' => time()));
-  
+
   // set replied/forwarded flag
-  if ($_SESSION['compose']['reply_uid'])
-    $IMAP->set_flag($_SESSION['compose']['reply_uid'], 'ANSWERED', $_SESSION['compose']['mailbox']);
-  else if ($_SESSION['compose']['forward_uid'])
-    $IMAP->set_flag($_SESSION['compose']['forward_uid'], 'FORWARDED', $_SESSION['compose']['mailbox']);
+  if ($COMPOSE['reply_uid'])
+    $IMAP->set_flag($COMPOSE['reply_uid'], 'ANSWERED', $COMPOSE['mailbox']);
+  else if ($COMPOSE['forward_uid'])
+    $IMAP->set_flag($COMPOSE['forward_uid'], 'FORWARDED', $COMPOSE['mailbox']);
 
 } // End of SMTP Delivery Block
 
@@ -637,11 +637,10 @@ if (!$savedraft)
 // Determine which folder to save message
 if ($savedraft)
   $store_target = $CONFIG['drafts_mbox'];
-else   
+else
   $store_target = isset($_POST['_store_target']) ? get_input_value('_store_target', RCUBE_INPUT_POST) : $CONFIG['sent_mbox'];
 
-if ($store_target)
-  {
+if ($store_target) {
   // check if folder is subscribed
   if ($IMAP->mailbox_exists($store_target, true))
     $store_folder = true;
@@ -653,11 +652,10 @@ if ($store_target)
 
   // append message to sent box
   if ($store_folder) {
-
     // message body in file
     if ($mailbody_file || $MAIL_MIME->getParam('delay_file_io')) {
       $headers = $MAIL_MIME->txtHeaders();
-      
+
       // file already created
       if ($mailbody_file)
         $msg = $mailbody_file;
@@ -666,12 +664,12 @@ if ($store_target)
         $mailbody_file = tempnam($temp_dir, 'rcmMsg');
         if (!PEAR::isError($msg = $MAIL_MIME->saveMessageBody($mailbody_file)))
           $msg = $mailbody_file;
-        }
       }
+    }
     else {
       $msg = $MAIL_MIME->getMessage();
       $headers = '';
-      }
+    }
 
     if (PEAR::isError($msg))
       raise_error(array('code' => 650, 'type' => 'php',
@@ -680,53 +678,58 @@ if ($store_target)
             TRUE, FALSE);
     else {
       $saved = $IMAP->save_message($store_target, $msg, $headers, $mailbody_file ? true : false);
-      }
+    }
 
     if ($mailbody_file) {
       unlink($mailbody_file);
       $mailbody_file = null;
-      }
+    }
 
     // raise error if saving failed
     if (!$saved) {
       raise_error(array('code' => 800, 'type' => 'imap',
            'file' => __FILE__, 'line' => __LINE__,
             'message' => "Could not save message in $store_target"), TRUE, FALSE);
-    
+
       if ($savedraft) {
         $OUTPUT->show_message('errorsaving', 'error');
         $OUTPUT->send('iframe');
-        }
       }
     }
+  }
 
-  if ($olddraftmessageid)
-    {
+  if ($olddraftmessageid) {
     // delete previous saved draft
+    // @TODO: use message UID (remember to check UIDVALIDITY) to skip this SEARCH
     $a_deleteid = $IMAP->search_once($CONFIG['drafts_mbox'],
         'HEADER Message-ID '.$olddraftmessageid, true);
-    $deleted = $IMAP->delete_message($a_deleteid, $CONFIG['drafts_mbox']);
 
-    // raise error if deletion of old draft failed
-    if (!$deleted)
-      raise_error(array('code' => 800, 'type' => 'imap',
-               'file' => __FILE__, 'line' => __LINE__,
-                'message' => "Could not delete message from ".$CONFIG['drafts_mbox']), TRUE, FALSE);
+    if (!empty($a_deleteid)) {
+      $deleted = $IMAP->delete_message($a_deleteid, $CONFIG['drafts_mbox']);
+
+      // raise error if deletion of old draft failed
+      if (!$deleted)
+        raise_error(array('code' => 800, 'type' => 'imap',
+          'file' => __FILE__, 'line' => __LINE__,
+          'message' => "Could not delete message from ".$CONFIG['drafts_mbox']), TRUE, FALSE);
     }
   }
+}
 // remove temp file
 else if ($mailbody_file) {
   unlink($mailbody_file);
-  }
+}
 
 
-if ($savedraft)
-  {
+if ($savedraft) {
   $msgid = strtr($message_id, array('>' => '', '<' => ''));
-  
-  // remember new draft-uid
-  $draftuids = $IMAP->search_once($CONFIG['drafts_mbox'], 'HEADER Message-ID '.$msgid, true);
-  $_SESSION['compose']['param']['draft_uid'] = $draftuids[0];
+
+  // remember new draft-uid ($saved could be an UID or TRUE here)
+  if (is_bool($saved)) {
+    $draftuids = $IMAP->search_once($CONFIG['drafts_mbox'], 'HEADER Message-ID '.$msgid, true);
+    $saved     = $draftuids[0];
+  }
+  $COMPOSE['param']['draft_uid'] = $saved;
 
   // display success
   $OUTPUT->show_message('messagesaved', 'confirmation');
@@ -739,9 +742,8 @@ if ($savedraft)
   $OUTPUT->command('auto_save_start');
 
   $OUTPUT->send('iframe');
-  }
-else
-  {
+}
+else {
   rcmail_compose_cleanup($COMPOSE_ID);
 
   if ($store_folder && !$saved)
@@ -749,6 +751,4 @@ else
   else
     $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'));
   $OUTPUT->send('iframe');
-  }
-
-
+}
index a1f9977e1e3cac6dd565a5b7ab2b6e5b246398d4..2f28adeb089249f3cd2363aaa0b0d087b24c7e61 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: show.inc 5151 2011-08-31 12:49:44Z alec $
+ $Id: show.inc 5514 2011-11-30 11:35:43Z alec $
 
 */
 
@@ -30,8 +30,6 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) {
     rcmail_message_error($uid);
   }
 
-  send_nocacheing_headers();
-
   $mbox_name = $IMAP->get_mailbox_name();
 
   // show images?
@@ -76,12 +74,13 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) {
       'movingmessage', 'deletingmessage');
 
   // check for unset disposition notification
-  if ($MESSAGE->headers->mdn_to &&
-      !$MESSAGE->headers->mdn_sent && !$MESSAGE->headers->seen &&
-      ($IMAP->check_permflag('MDNSENT') || $IMAP->check_permflag('*')) &&
-      $mbox_name != $CONFIG['drafts_mbox'] &&
-      $mbox_name != $CONFIG['sent_mbox'])
-  {
+  if ($MESSAGE->headers->mdn_to
+      && empty($MESSAGE->headers->flags['MDNSENT'])
+      && empty($MESSAGE->headers->flags['SEEN'])
+      && ($IMAP->check_permflag('MDNSENT') || $IMAP->check_permflag('*'))
+      && $mbox_name != $CONFIG['drafts_mbox']
+      && $mbox_name != $CONFIG['sent_mbox']
+  ) {
     $mdn_cfg = intval($CONFIG['mdn_requests']);
 
     if ($mdn_cfg == 1 || (($mdn_cfg == 3 || $mdn_cfg ==  4) && rcmail_contact_exists($MESSAGE->sender['mailto']))) {
@@ -100,9 +99,12 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) {
     }
   }
 
-  if (!$MESSAGE->headers->seen && ($RCMAIL->action == 'show' || ($RCMAIL->action == 'preview' && intval($CONFIG['preview_pane_mark_read']) == 0)))
+  if (empty($MESSAGE->headers->flags['SEEN'])
+    && ($RCMAIL->action == 'show' || ($RCMAIL->action == 'preview' && intval($CONFIG['preview_pane_mark_read']) == 0))
+  ) {
     $RCMAIL->plugins->exec_hook('message_read', array('uid' => $MESSAGE->uid,
       'mailbox' => $mbox_name, 'message' => $MESSAGE));
+  }
 }
 
 
@@ -130,7 +132,7 @@ function rcmail_message_attachments($attrib)
 
         $ol .= html::tag('li', null,
           html::a(array(
-            'href' => $MESSAGE->get_part_url($attach_prop->mime_id),
+            'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false),
             'onclick' => sprintf(
               'return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)',
               JS_OBJECT_NAME,
@@ -148,12 +150,13 @@ function rcmail_message_attachments($attrib)
   return $out;
 }
 
-function rcmail_remote_objects_msg($attrib)
+function rcmail_remote_objects_msg()
 {
   global $MESSAGE, $RCMAIL;
 
-  if (!$attrib['id'])
-    $attrib['id'] = 'rcmremoteobjmsg';
+  $attrib['id']    = 'remote-objects-message';
+  $attrib['class'] = 'notice';
+  $attrib['style'] = 'display: none';
 
   $msg = Q(rcube_label('blockedimages')) . '&nbsp;';
   $msg .= html::a(array('href' => "#loadimages", 'onclick' => JS_OBJECT_NAME.".command('load-images')"), Q(rcube_label('showimages')));
@@ -168,6 +171,48 @@ function rcmail_remote_objects_msg($attrib)
   return html::div($attrib, $msg);
 }
 
+function rcmail_message_buttons()
+{
+  global $MESSAGE, $RCMAIL, $CONFIG;
+
+  $mbox  = $RCMAIL->imap->get_mailbox_name();
+  $delim = $RCMAIL->imap->get_hierarchy_delimiter();
+  $dbox  = $CONFIG['drafts_mbox'];
+
+  // the message is not a draft
+  if ($mbox != $dbox && strpos($mbox, $dbox.$delim) !== 0) {
+    return '';
+  }
+
+  $attrib['id']    = 'message-buttons';
+  $attrib['class'] = 'notice';
+
+  $msg = Q(rcube_label('isdraft')) . '&nbsp;';
+  $msg .= html::a(array('href' => "#edit", 'onclick' => JS_OBJECT_NAME.".command('edit')"), Q(rcube_label('edit')));
+
+  return html::div($attrib, $msg);
+}
+
+function rcmail_message_objects($attrib)
+{
+  global $RCMAIL, $MESSAGE;
+
+  if (!$attrib['id'])
+    $attrib['id'] = 'message-objects';
+
+  $content = array(
+    rcmail_message_buttons(),
+    rcmail_remote_objects_msg(),
+  );
+
+  $plugin = $RCMAIL->plugins->exec_hook('message_objects',
+    array('content' => $content, 'message' => $MESSAGE));
+
+  $content = implode("\n", $plugin['content']);
+
+  return html::div($attrib, $content);
+}
+
 function rcmail_contact_exists($email)
 {
   global $RCMAIL;
@@ -187,7 +232,8 @@ function rcmail_contact_exists($email)
 $OUTPUT->add_handlers(array(
   'messageattachments' => 'rcmail_message_attachments',
   'mailboxname' => 'rcmail_mailbox_name_display',
-  'blockedobjects' => 'rcmail_remote_objects_msg'));
+  'messageobjects' => 'rcmail_message_objects',
+));
 
 
 if ($RCMAIL->action=='print' && $OUTPUT->template_exists('messageprint'))
@@ -199,7 +245,7 @@ else
 
 
 // mark message as read
-if ($MESSAGE && $MESSAGE->headers && !$MESSAGE->headers->seen &&
+if ($MESSAGE && $MESSAGE->headers && empty($MESSAGE->headers->flags['SEEN']) &&
   ($RCMAIL->action == 'show' || ($RCMAIL->action == 'preview' && intval($CONFIG['preview_pane_mark_read']) == 0)))
 {
   if ($IMAP->set_flag($MESSAGE->uid, 'SEEN')) {
index 46ccb40e1075684458ae7397580afd5d1b3dff49..e7db49c02b155199e1a853f0af28a33ab9a75577 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Aleksander Machniak <alec@alec.pl>                            |
  +-----------------------------------------------------------------------+
 
- $Id: edit_folder.inc 5022 2011-08-04 09:01:36Z alec $
+ $Id: edit_folder.inc 5402 2011-11-09 10:03:54Z alec $
 
 */
 
@@ -119,7 +119,8 @@ function rcmail_folder_form($attrib)
             'realnames'   => false,
             'maxlength'   => 150,
             'unsubscribed' => true,
-            'exceptions' => array($mbox_imap),
+            'skip_noinferiors' => true,
+            'exceptions'  => array($mbox_imap),
         ));
 
         $form['props']['fieldsets']['location']['content']['path'] = array(
@@ -134,7 +135,7 @@ function rcmail_folder_form($attrib)
     );
 
     // Settings: threading
-    if ($threading_supported && !$options['noselect'] && !$options['is_root']) {
+    if ($threading_supported && ($mbox_imap == 'INBOX' || (!$options['noselect'] && !$options['is_root']))) {
         $select = new html_select(array('name' => '_viewmode', 'id' => '_listmode'));
         $select->add(rcube_label('list'), 0);
         $select->add(rcube_label('threads'), 1);
@@ -191,7 +192,7 @@ function rcmail_folder_form($attrib)
             'content' => array()
         );
 
-        if (!$options['noselect'] && !$options['is_root']) {
+        if ((!$options['noselect'] && !$options['is_root']) || $mbox_imap == 'INBOX') {
             $msgcount = $RCMAIL->imap->messagecount($mbox_imap, 'ALL', true, false);
 
             // Size
index 5cc6e0c60c14c4b2bb61b08f98dec2b12314aa75..599fddb7f02d727ba8413f6b26667d1d19796d57 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: edit_identity.inc 4668 2011-04-19 20:41:24Z thomasb $
+ $Id: edit_identity.inc 5139 2011-08-28 09:47:15Z alec $
 
 */
 
@@ -28,7 +28,7 @@ if (($_GET['_iid'] || $_POST['_iid']) && $RCMAIL->action=='edit-identity') {
   if (is_array($IDENTITY_RECORD))
     $OUTPUT->set_env('iid', $IDENTITY_RECORD['identity_id']);
   else {
-    $OUTPUT->show_message('databaserror', 'error');
+    $OUTPUT->show_message('dberror', 'error');
     // go to identities page
     rcmail_overwrite_action('identities');
     return;
index 69339e29a3a09ee5f9fb2f1f275070402bcfc9fe..2cc863ab109b689a95ef6228771ca56bead115c3 100644 (file)
@@ -16,7 +16,7 @@
  | Author: Aleksander Machniak <alec@alec.pl>                            |
  +-----------------------------------------------------------------------+
 
- $Id: folders.inc 5091 2011-08-18 18:34:56Z thomasb $
+ $Id: folders.inc 5402 2011-11-09 10:03:54Z alec $
 
 */
 
@@ -203,7 +203,7 @@ function rcube_subscription_form($attrib)
     $IMAP->clear_cache('mailboxes', true);
 
     $a_unsubscribed = $IMAP->list_unsubscribed();
-    $a_subscribed   = $IMAP->list_mailboxes();
+    $a_subscribed   = $IMAP->list_mailboxes('', '*', null, null, true); // unsorted
     $delimiter      = $IMAP->get_hierarchy_delimiter();
     $namespace      = $IMAP->get_namespace();
     $a_js_folders   = array();
@@ -283,8 +283,8 @@ function rcube_subscription_form($attrib)
         }
 
         if (!$protected) {
-            $opts = $IMAP->mailbox_options($folder['id']);
-            $noselect = in_array('\\Noselect', $opts);
+            $attrs = $IMAP->mailbox_attributes($folder['id']);
+            $noselect = in_array('\\Noselect', $attrs);
         }
 
         $disabled = (($protected && $subscribed) || $noselect);
index 9df9cab54597d2f8b7c87f00f6bfc91e9140e3d2..8df3ce8d3300ad9c5d83f6beab18d6791cd150e9 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: func.inc 4875 2011-06-23 17:43:44Z alec $
+ $Id: func.inc 5481 2011-11-24 07:53:00Z alec $
 
 */
 
@@ -178,7 +178,7 @@ function rcmail_user_prefs($current=null)
     // show page size selection
     if (!isset($no_override['timezone'])) {
       $field_id = 'rcmfd_timezone';
-      $select_timezone = new html_select(array('name' => '_timezone', 'id' => $field_id, 'onchange' => "document.getElementById('rcmfd_dst').disabled=this.selectedIndex==0"));
+      $select_timezone = new html_select(array('name' => '_timezone', 'id' => $field_id, 'onchange' => "$('#rcmfd_dst').attr('disabled', this.selectedIndex==0)"));
       $select_timezone->add(rcube_label('autodetect'), 'auto');
       $select_timezone->add('(GMT -11:00) Midway Island, Samoa', '-11');
       $select_timezone->add('(GMT -10:00) Hawaii', '-10');
@@ -237,6 +237,33 @@ function rcmail_user_prefs($current=null)
       );
     }
 
+    // date/time formatting
+    if (!isset($no_override['time_format'])) {
+      $reftime = mktime(7,30,0);
+      $field_id = 'rcmfd_time_format';
+      $select_time = new html_select(array('name' => '_time_format', 'id' => $field_id));
+      foreach ((array)$RCMAIL->config->get('time_formats', array('G:i', 'H:i', 'g:i a', 'h:i A')) as $choice)
+        $select_time->add(date($choice, $reftime), $choice);
+
+      $blocks['main']['options']['time_format'] = array(
+        'title' => html::label($field_id, Q(rcube_label('timeformat'))),
+        'content' => $select_time->show($RCMAIL->config->get('time_format')),
+      );
+    }
+
+    if (!isset($no_override['date_format'])) {
+      $refdate = mktime(12,30,0,7,24);
+      $field_id = 'rcmfd_date_format';
+      $select_date = new html_select(array('name' => '_date_format', 'id' => $field_id));
+      foreach ((array)$RCMAIL->config->get('date_formats', array('Y-m-d','d-m-Y','Y/m/d','m/d/Y','d/m/Y','d.m.Y','j.n.Y')) as $choice)
+        $select_date->add(date($choice, $refdate), $choice);
+
+      $blocks['main']['options']['date_format'] = array(
+        'title' => html::label($field_id, Q(rcube_label('dateformat'))),
+        'content' => $select_date->show($config['date_format']),
+      );
+    }
+
     // MM: Show checkbox for toggling 'pretty dates' 
     if (!isset($no_override['prettydate'])) {
       $field_id = 'rcmfd_prettydate';
@@ -410,14 +437,15 @@ function rcmail_user_prefs($current=null)
 
     if (!isset($no_override['show_images'])) {
       $field_id = 'rcmfd_show_images';
-      $input_show_images = new html_select(array('name' => '_show_images', 'id' => $field_id));
+      $input_show_images = new html_select(array('name' => '_show_images', 'id' => $field_id,
+        'disabled' => !$config['prefer_html']));
       $input_show_images->add(rcube_label('never'), 0);
       $input_show_images->add(rcube_label('fromknownsenders'), 1);
       $input_show_images->add(rcube_label('always'), 2);
 
       $blocks['main']['options']['show_images'] = array(
         'title' => html::label($field_id, Q(rcube_label('showremoteimages'))),
-        'content' => $input_show_images->show($config['show_images']),
+        'content' => $input_show_images->show($config['prefer_html'] ? $config['show_images'] : 0),
       );
     }
 
@@ -448,8 +476,9 @@ function rcmail_user_prefs($current=null)
     case 'compose':
 
     $blocks = array(
-      'main' => array('name' => Q(rcube_label('mainoptions'))),
-      'sig' => array('name' => Q(rcube_label('signatureoptions'))),
+      'main'       => array('name' => Q(rcube_label('mainoptions'))),
+      'spellcheck' => array('name' => Q(rcube_label('spellcheckoptions'))),
+      'sig'        => array('name' => Q(rcube_label('signatureoptions'))),
     );
 
     // Show checkbox for HTML Editor
@@ -549,12 +578,26 @@ function rcmail_user_prefs($current=null)
       $field_id = 'rcmfd_spellcheck_before_send';
       $input_spellcheck = new html_checkbox(array('name' => '_spellcheck_before_send', 'id' => $field_id, 'value' => 1));
 
-      $blocks['main']['options']['spellcheck_before_send'] = array(
+      $blocks['spellcheck']['options']['spellcheck_before_send'] = array(
         'title' => html::label($field_id, Q(rcube_label('spellcheckbeforesend'))),
         'content' => $input_spellcheck->show($config['spellcheck_before_send']?1:0),
       );
     }
 
+    if ($config['enable_spellcheck']) {
+      foreach (array('syms', 'nums', 'caps') as $key) {
+        $key = 'spellcheck_ignore_'.$key;
+        if (!isset($no_override[$key])) {
+          $input_spellcheck = new html_checkbox(array('name' => '_'.$key, 'id' => 'rcmfd_'.$key, 'value' => 1));
+
+          $blocks['spellcheck']['options'][$key] = array(
+            'title' => html::label($field_id, Q(rcube_label(str_replace('_', '', $key)))),
+            'content' => $input_spellcheck->show($config[$key]?1:0),
+          );
+        }
+      }
+    }
+
     if (!isset($no_override['show_sig'])) {
       $field_id = 'rcmfd_show_sig';
       $select_show_sig = new html_select(array('name' => '_show_sig', 'id' => $field_id));
@@ -617,6 +660,16 @@ function rcmail_user_prefs($current=null)
       );
     }
 
+    if (!isset($no_override['autocomplete_single'])) {
+      $field_id = 'rcmfd_autocomplete_single';
+      $checkbox = new html_checkbox(array('name' => '_autocomplete_single', 'id' => $field_id, 'value' => 1));
+
+      $blocks['main']['options']['autocomplete_single'] = array(
+        'title' => html::label($field_id, Q(rcube_label('autocompletesingle'))),
+        'content' => $checkbox->show($config['autocomplete_single']?1:0),
+      );
+    }
+
     break;
 
     // Special IMAP folders
@@ -634,7 +687,7 @@ function rcmail_user_prefs($current=null)
       // load folders list only when needed
       if ($current) {
         $select = rcmail_mailbox_select(array('noselection' => '---', 'realnames' => true,
-          'maxlength' => 30, 'exceptions' => array('INBOX')));
+          'maxlength' => 30, 'exceptions' => array('INBOX'), 'folder_filter' => 'mail', 'folder_rights' => 'w'));
       }
       else // dummy select
         $select = new html_select();
index 27d94b3289b67b02eb512e8fb9e4e66b1037bba5..7af2c316e5355747394309816c0a7dd6d4d91d1b 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Aleksander Machniak <alec@alec.pl>                            |
  +-----------------------------------------------------------------------+
 
- $Id: save_folder.inc 5102 2011-08-19 16:32:38Z thomasb $
+ $Id: save_folder.inc 5096 2011-08-19 08:07:05Z alec $
 
 */
 
index e5dfdd9fc7022f1a5531ab8f7634b040a6439956..2fefef193e1dec081f8c273a812e218f1e0c85ea 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
  +-----------------------------------------------------------------------+
 
- $Id: save_prefs.inc 5151 2011-08-31 12:49:44Z alec $
+ $Id: save_prefs.inc 5543 2011-12-05 07:24:36Z alec $
 
 */
 
@@ -32,9 +32,17 @@ switch ($CURR_SECTION)
       'timezone'     => isset($_POST['_timezone']) ? (is_numeric($_POST['_timezone']) ? floatval($_POST['_timezone']) : get_input_value('_timezone', RCUBE_INPUT_POST)) : $CONFIG['timezone'],
       'dst_active'   => isset($_POST['_dst_active']) ? TRUE : FALSE,
       'pagesize'     => is_numeric($_POST['_pagesize']) ? max(2, intval($_POST['_pagesize'])) : $CONFIG['pagesize'],
+      'date_format'  => isset($_POST['_date_format']) ? get_input_value('_date_format', RCUBE_INPUT_POST) : $CONFIG['date_format'],
+      'time_format'  => isset($_POST['_time_format']) ? get_input_value('_time_format', RCUBE_INPUT_POST) : ($CONFIG['time_format'] ? $CONFIG['time_format'] : 'H:i'),
       'prettydate'   => isset($_POST['_pretty_date']) ? TRUE : FALSE,
       'skin'        => isset($_POST['_skin']) ? get_input_value('_skin', RCUBE_INPUT_POST) : $CONFIG['skin'],
     );
+    
+    // compose derived date/time format strings
+    if ((isset($_POST['_date_format']) || isset($_POST['_time_format'])) && $a_user_prefs['date_format'] && $a_user_prefs['time_format']) {
+      $a_user_prefs['date_short'] = 'D ' . $a_user_prefs['time_format'];
+      $a_user_prefs['date_long'] = $a_user_prefs['date_format'] . ' ' . $a_user_prefs['time_format'];
+    }
 
   break;
 
@@ -71,6 +79,9 @@ switch ($CURR_SECTION)
       'dsn_default'        => isset($_POST['_dsn_default']) ? TRUE : FALSE,
       'reply_same_folder'  => isset($_POST['_reply_same_folder']) ? TRUE : FALSE,
       'spellcheck_before_send' => isset($_POST['_spellcheck_before_send']) ? TRUE : FALSE,
+      'spellcheck_ignore_syms' => isset($_POST['_spellcheck_ignore_syms']) ? TRUE : FALSE,
+      'spellcheck_ignore_nums' => isset($_POST['_spellcheck_ignore_nums']) ? TRUE : FALSE,
+      'spellcheck_ignore_caps' => isset($_POST['_spellcheck_ignore_caps']) ? TRUE : FALSE,
       'show_sig'           => isset($_POST['_show_sig']) ? intval($_POST['_show_sig']) : 1,
       'top_posting'        => !empty($_POST['_top_posting']),
       'strip_existing_sig' => isset($_POST['_strip_existing_sig']),
@@ -82,6 +93,7 @@ switch ($CURR_SECTION)
   case 'addressbook':
     $a_user_prefs = array(
       'default_addressbook' => get_input_value('_default_addressbook', RCUBE_INPUT_POST, true),
+      'autocomplete_single' => isset($_POST['_autocomplete_single']) ? TRUE : FALSE,
     );
 
   break;
@@ -133,6 +145,8 @@ switch ($CURR_SECTION)
     // switch skin (if valid, otherwise unset the pref and fall back to default)
     if (!$OUTPUT->set_skin($a_user_prefs['skin']))
       unset($a_user_prefs['skin']);
+    else if ($RCMAIL->config->get('skin') != $a_user_prefs['skin'])
+      $OUTPUT->command('reload', 500);
 
     // force min size
     if ($a_user_prefs['pagesize'] < 1)
@@ -167,7 +181,7 @@ switch ($CURR_SECTION)
           $a_user_prefs['default_imap_folders'][] = $a_user_prefs[$p];
       }
     }
-  
+
   break;
 }
 
index 73c6c979c8b951f03c08306024b93e89b0081fc0..fd366a3aa2abf892726a0228da7093ef90c51042 100644 (file)
@@ -9,13 +9,13 @@
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
- |   Delete rows from cache and messages tables                          |
+ |   Delete rows from cache tables                                       |
  |                                                                       |
  +-----------------------------------------------------------------------+
  | Author: Dennis P. Nikolaenko <dennis@nikolaenko.ru>                   |
  +-----------------------------------------------------------------------+
 
- $Id: killcache.inc 4410 2011-01-12 18:25:02Z thomasb $
+ $Id: killcache.inc 5578 2011-12-09 07:23:04Z alec $
 
 */
 
@@ -41,12 +41,20 @@ if (PEAR::isError($res)) {
     exit($res->getMessage());
 }
 
-$res = $RCMAIL->db->query("DELETE FROM messages");
+$res = $RCMAIL->db->query("DELETE FROM cache_messages");
 if (PEAR::isError($res)) {
     exit($res->getMessage());
 }
 
-echo "Cache cleared\n";
-exit;
+$res = $RCMAIL->db->query("DELETE FROM cache_index");
+if (PEAR::isError($res)) {
+    exit($res->getMessage());
+}
 
+$res = $RCMAIL->db->query("DELETE FROM cache_thread");
+if (PEAR::isError($res)) {
+    exit($res->getMessage());
+}
 
+echo "Cache cleared\n";
+exit;
index b45ff39d051768b23c35d41ac18973be4ab666cd..65623ecb558cf266ac91309b441cefe8a26c9836 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Kris Steinhoff <steinhof@umich.edu>                           |
  +-----------------------------------------------------------------------+
 
- $Id: spell.inc 4815 2011-05-30 15:08:26Z alec $
+ $Id: spell.inc 5181 2011-09-06 13:39:45Z alec $
 
 */
 
@@ -23,6 +23,8 @@
 $lang = get_input_value('lang', RCUBE_INPUT_GET);
 $data = file_get_contents('php://input');
 
+$learn_word = strpos($data, '<learnword>');
+
 // Get data string
 $left = strpos($data, '<text>');
 $right = strrpos($data, '</text>');
@@ -30,8 +32,15 @@ $data = substr($data, $left+6, $right-($left+6));
 $data = html_entity_decode($data, ENT_QUOTES, RCMAIL_CHARSET);
 
 $spellchecker = new rcube_spellchecker($lang);
-$spellchecker->check($data);
-$result = $spellchecker->get_xml();
+
+if ($learn_word) {
+    $spellchecker->add_word($data);
+    $result = '<?xml version="1.0" encoding="'.RCMAIL_CHARSET.'"?><learnwordresult></learnwordresult>';
+}
+else {
+    $spellchecker->check($data);
+    $result = $spellchecker->get_xml();
+}
 
 // set response length
 header("Content-Length: " . strlen($result));
index d0324c6176174f36aa6be59ef4341e44f09e3b47..4567e13ba83a227205067c5d52d39dcb36d56c13 100644 (file)
@@ -15,7 +15,7 @@
  | Author: Aleksander Machniak <alec@alec.pl>                            |
  +-----------------------------------------------------------------------+
 
- $Id: spell_html.inc 4815 2011-05-30 15:08:26Z alec $
+ $Id: spell_html.inc 5181 2011-09-06 13:39:45Z alec $
 
 */
 
@@ -40,6 +40,10 @@ if ($request['method'] == 'checkWords') {
 else if ($request['method'] == 'getSuggestions') {
     $result['result'] = $spellchecker->get_suggestions($data);
 }
+else if ($request['method'] == 'learnWord') {
+    $spellchecker->add_word($data);
+    $result['result'] = true;
+}
 
 if ($error = $spellchecker->error()) {
     echo '{"error":{"errstr":"' . addslashes($error) . '","errfile":"","errline":null,"errcontext":"","level":"FATAL"}}';
index f3b52c806f729e8e5f1827ab24c1a508f41d5b56..c604c7549412866b1dbcb98430a5c2715c3917d1 100644 (file)
 
 #directorylistbox input
 {
-  margin: 2px;
+  margin: 0px;
+  font-size: 11px;
   width: 90%;
 }
 
 #directorylist li.contactgroup
 {
   padding-left: 15px;
-  background-position: 20px -144px;
+  background-position: 20px -143px;
+}
+
+#directorylist li.contactsearch
+{
+  background-position: 6px -162px;
 }
 
 #directorylist li.selected
index f5b1d3de92693908e9e95fb02b54a1845b95a589..bf00dc35177a25772f0f1e5c1004f08e71e407c8 100644 (file)
@@ -2,9 +2,11 @@
 
 body
 {
+  font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
   margin: 8px;
   background-color: #F6F6F6;
-  color: #000000;
+  color: #000;
+  font-size: 12px;
 }
 
 body.iframe
@@ -18,10 +20,10 @@ body.extwin
   margin: 10px;
 }
 
-body, td, th, div, p, h3, select, input, textarea
+td, th, div, p, select, input, textarea
 {
-  font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
   font-size: 12px;
+  font-family: inherit;
 }
 
 th
@@ -36,13 +38,13 @@ h3
 
 a, a:active, a:visited
 {
-  color: #000000;
+  color: #000;
   outline: none;
 }
 
 a.button, a.button:visited, a.tab, a.tab:visited, a.axislist
 {
-  color: #000000;
+  color: #000;
   text-decoration: none;
 }
 
@@ -56,7 +58,7 @@ a.tab
 hr
 {
   height: 1px;
-  background-color: #666666;
+  background-color: #666;
   border-style: none;
 }
 
@@ -65,9 +67,9 @@ input[type="button"],
 input[type="password"],
 textarea
 {
-  border: 1px solid #666666;
-  color: #333333;
-  background-color: #ffffff;
+  border: 1px solid #666;
+  color: #333;
+  background-color: #FFF;
 }
 
 input, textarea
@@ -230,7 +232,7 @@ img
 }
 
 #message div.notice,
-#remote-objects-message
+#message-objects div.notice
 {
   background: url(images/display/icons.png) 6px 3px no-repeat;
   background-color: #F7FDCB;
@@ -238,21 +240,25 @@ img
 }
 
 #message div.error,
-#message div.warning
+#message div.warning,
+#message-objects div.warning,
+#message-objects div.error
 {
   background: url(images/display/icons.png) 6px -97px no-repeat;
   background-color: #EF9398;
   border: 1px solid #DC5757;
 }
 
-#message div.confirmation
+#message div.confirmation,
+#message-objects div.confirmation
 {
   background: url(images/display/icons.png) 6px -47px no-repeat;
   background-color: #A6EF7B;
   border: 1px solid #76C83F;
 }
 
-#message div.loading
+#message div.loading,
+#message-objects div.loading
 {
   background: url(images/display/loading.gif) 6px 3px no-repeat;
   background-color: #EBEBEB;
@@ -279,6 +285,9 @@ img
   font-size: 11px;
   font-weight: bold;
   overflow: hidden;
+  text-overflow: ellipsis;
+  -o-text-overflow: ellipsis;
+  white-space: nowrap;
   background: url(images/listheader.gif) top left repeat-x #CCC;
 }
 
@@ -738,7 +747,7 @@ a.rcmContactAddress:hover
   margin: auto;
 }
 
-#rcmloginuser, #rcmloginpwd, #rcmloginhost
+#login-form table td.input input
 {
   width: 200px;
 }
index 2ddc87fe97352f968db7ca0dce3722fd4b5b12a7..aabed07b5798d894c6728abf7a9b1aea5295a13f 100644 (file)
@@ -1,5 +1,4 @@
 /* This file contains the CSS data for the editable area(iframe) of TinyMCE */
-/* You can extend this CSS by adding your own CSS file with the the content_css option */
 
 body, td, pre {
        font-family: "Lucida Grande", Verdana,  Arial, Helvetica, sans-serif;
@@ -25,8 +24,8 @@ pre
 
 blockquote
 {
-       padding-left:5px;
-       border-left:#1010ff 2px solid;
-       margin-left:5px;
-       width:100%;
+       padding-left: 5px;
+       border-left: #1010ff 2px solid;
+       margin-left: 5px;
+       width: 100%;
 }
index 32c0a73688d6466f0f9f5d803ac96cc0ecc3ce7a..8482e375a8af082b7dd39f02738c53e4613fa927 100644 (file)
@@ -108,8 +108,13 @@ function rcube_mail_ui()
 
 rcube_mail_ui.prototype = {
 
-show_popup: function(popup, show)
+show_popup: function(popup, show, config)
 {
+  var obj;
+  // auto-register menu object
+  if (!this.popups[popup] && (obj = $('#'+popup)) && obj.length)
+    this.popups[popup] = $.extend(config, {id: popup, obj: obj});
+
   if (typeof this[popup] == 'function')
     return this[popup](show);
   else
@@ -141,7 +146,7 @@ show_popupmenu: function(popup, show)
   }
 
   obj[show?'show':'hide']();
-  
+
   if (bw.ie6 && this.popups[popup].overlap) {
     $('select').css('visibility', show?'hidden':'inherit');
     $('select', obj).css('visibility', 'inherit');
@@ -187,29 +192,32 @@ searchmenu: function(show)
 
   if (show && ref) {
     var pos = $(ref).offset();
-    obj.css({ left:pos.left, top:(pos.top + ref.offsetHeight + 2)})
-        .find(':checked').prop('checked', false);
+    obj.css({left:pos.left, top:(pos.top + ref.offsetHeight + 2)});
 
     if (rcmail.env.search_mods) {
-      var n, mbox = rcmail.env.mailbox, mods = rcmail.env.search_mods;
+      var n, all,
+        list = $('input:checkbox[name="s_mods[]"]', obj),
+        mbox = rcmail.env.mailbox,
+        mods = rcmail.env.search_mods;
 
-      if (rcmail.env.task != 'addressbook') {
+      if (rcmail.env.task == 'mail') {
         mods = mods[mbox] ? mods[mbox] : mods['*'];
+        all = 'text';
+      }
+      else {
+        all = '*';
+      }
 
+      if (mods[all])
+        list.map(function() {
+          this.checked = true;
+          this.disabled = this.value != all;
+        });
+      else {
+        list.prop('disabled', false).prop('checked', false);
         for (n in mods)
           $('#s_mod_' + n).prop('checked', true);
       }
-      else {
-        if (mods['*'])
-          $('input:checkbox[name="s_mods[]"]').map(function() {
-            this.checked = true;
-            this.disabled = this.value != '*';
-          });
-        else {
-          for (n in mods)
-            $('#s_mod_' + n).prop('checked', true);
-        }
-      }
     }
   }
   obj[show?'show':'hide']();
@@ -217,7 +225,7 @@ searchmenu: function(show)
 
 set_searchmod: function(elem)
 {
-  var task = rcmail.env.task,
+  var all, m, task = rcmail.env.task,
     mods = rcmail.env.search_mods,
     mbox = rcmail.env.mailbox;
 
@@ -227,36 +235,37 @@ set_searchmod: function(elem)
   if (task == 'mail') {
     if (!mods[mbox])
       mods[mbox] = rcube_clone_object(mods['*']);
-    if (!elem.checked)
-      delete(mods[mbox][elem.value]);
-    else
-      mods[mbox][elem.value] = 1;
+    m = mods[mbox];
+    all = 'text';
   }
   else { //addressbook
-    if (!elem.checked)
-      delete(mods[elem.value]);
-    else
-      mods[elem.value] = 1;
+    m = mods;
+    all = '*';
+  }
 
-    // mark all fields
-    if (elem.value == '*') {
-      $('input:checkbox[name="s_mods[]"]').map(function() {
-        if (this == elem)
-          return;
+  if (!elem.checked)
+    delete(m[elem.value]);
+  else
+    m[elem.value] = 1;
 
-        if (elem.checked) {
-          mods[this.value] = 1;
-          this.checked = true;
-          this.disabled = true;
-        }
-        else {
-          this.disabled = false;
-        }
-      });
-    }
-  }
+  // mark all fields
+  if (elem.value != all)
+    return;
+
+  $('input:checkbox[name="s_mods[]"]').map(function() {
+    if (this == elem)
+      return;
 
-  rcmail.env.search_mods = mods;
+    this.checked = true;
+    if (elem.checked) {
+      this.disabled = true;
+      delete m[this.value];
+    }
+    else {
+      this.disabled = false;
+      m[this.value] = 1;
+    }
+  });
 },
 
 listmenu: function(show)
@@ -561,7 +570,6 @@ function rcube_init_mail_ui()
       rcmail.addEventListener('responseaftergetunread', rcube_render_mailboxlist);
       rcmail.addEventListener('responseaftercheck-recent', rcube_render_mailboxlist);
       rcmail.addEventListener('aftercollapse-folder', rcube_render_mailboxlist);
-      rcube_render_mailboxlist();
     }
 
     if (rcmail.env.action == 'compose')
@@ -583,12 +591,16 @@ function iframe_events()
 // Abbreviate mailbox names to fit width of the container
 function rcube_render_mailboxlist()
 {
-  if (bw.ie6)  // doesn't work well on IE6
+  var list = $('#mailboxlist > li a, #mailboxlist ul:visible > li a');
+
+  // it's too slow with really big number of folders, especially on IE
+  if (list.length > 500 * (bw.ie ? 0.2 : 1))
     return;
 
-  $('#mailboxlist > li a, #mailboxlist ul:visible > li a').each(function(){
-    var elem = $(this);
-    var text = elem.data('text');
+  list.each(function(){
+    var elem = $(this),
+      text = elem.data('text');
+
     if (!text) {
       text = elem.text().replace(/\s+\(.+$/, '');
       elem.data('text', text);
@@ -606,34 +618,45 @@ function rcube_render_mailboxlist()
 // inspired by https://gist.github.com/24261/7fdb113f1e26111bd78c0c6fe515f6c0bf418af5
 function fit_string_to_size(str, elem, len)
 {
-    var result = str;
-    var ellip = '...';
-    var span = $('<b>').css({ visibility:'hidden', padding:'0px' }).appendTo(elem).get(0);
-
-    // on first run, check if string fits into the length already.
-    span.innerHTML = result;
-    if (span.offsetWidth > len) {
-        var cut = Math.max(1, Math.floor(str.length * ((span.offsetWidth - len) / span.offsetWidth) / 2)),
-          mid = Math.floor(str.length / 2);
-        var offLeft = mid, offRight = mid;
-        while (true) {
-            offLeft = mid - cut;
-            offRight = mid + cut;
-            span.innerHTML = str.substring(0,offLeft) + ellip + str.substring(offRight);
-
-            // break loop if string fits size
-            if (span.offsetWidth <= len || offLeft < 3)
-              break;
-
-            cut++;
-        }
-
-        // build resulting string
-        result = str.substring(0,offLeft) + ellip + str.substring(offRight);
+  var w, span, result = str, ellip = '...';
+
+  if (!rcmail.env.tmp_span) {
+    // it should be appended to elem to use the same css style
+    // but for performance reasons we'll append it to body (once)
+    span = $('<b>').css({visibility: 'hidden', padding: '0px'})
+      .appendTo($('body', document)).get(0);
+    rcmail.env.tmp_span = span;
+  }
+  else {
+    span = rcmail.env.tmp_span;
+  }
+  span.innerHTML = result;
+
+  // on first run, check if string fits into the length already.
+  w = span.offsetWidth;
+  if (w > len) {
+    var cut = Math.max(1, Math.floor(str.length * ((w - len) / w) / 2)),
+      mid = Math.floor(str.length / 2),
+      offLeft = mid,
+      offRight = mid;
+
+    while (true) {
+      offLeft = mid - cut;
+      offRight = mid + cut;
+      span.innerHTML = str.substring(0,offLeft) + ellip + str.substring(offRight);
+
+      // break loop if string fits size
+      if (offLeft < 3 || span.offsetWidth)
+        break;
+
+      cut++;
     }
-    
-    span.parentNode.removeChild(span);
-    return result;
+
+    // build resulting string
+    result = str.substring(0,offLeft) + ellip + str.substring(offRight);
+  }
+
+  return result;
 }
 
 // Optional parameters used by TinyMCE
index bfdb6f08733c042e8c45fd29905fbc0b8cd2be49..dc7f24a2eec496ee7b6c7e1fb58421b97bb9ba5a 100644 (file)
@@ -20,7 +20,10 @@ img
 #message div.error,
 #message div.warning,
 #message div.confirmation,
-#remote-objects-message
+#message-objects div.notice,
+#message-objects div.error,
+#message-objects div.warning,
+#message-objects div.confirmation
 {
   background-image: url(images/display/icons.gif);
 }
@@ -131,6 +134,12 @@ ul.toolbarmenu li.separator_below
 #messagelist tr td.threads div.listmenu,
 #messagelist tr td.attachment span.attachment,
 #messagelist tr td.attachment span.report,
+#messagelist tr td.priority span.priority,
+#messagelist tr td.priority span.prio1,
+#messagelist tr td.priority span.prio2,
+#messagelist tr td.priority span.prio3,
+#messagelist tr td.priority span.prio4,
+#messagelist tr td.priority span.prio5,
 #messagelist tr td.flag span.flagged,
 #messagelist tr td.flag span.unflagged:hover,
 #messagelist tr td.status span.status,
index 0fccb2c18a1d19c7d537552a7c3713123c3726d3..eb06bd8c10d560461a89af7eabe9214f1f93e897 100644 (file)
Binary files a/skins/default/images/icons/folders.gif and b/skins/default/images/icons/folders.gif differ
index 5013318f8584a6c7d9661d2d3d7ffa245e0b4475..2580fd1be79a1ed4dc127a343ba7c7f487ed082b 100644 (file)
Binary files a/skins/default/images/icons/folders.png and b/skins/default/images/icons/folders.png differ
index 60526f1f32df297cb9b4297e0c6d713b75d1705a..80423dd1e50f2fe04b9fc64aa7262c851e83af8f 100644 (file)
Binary files a/skins/default/images/messageicons.gif and b/skins/default/images/messageicons.gif differ
index 3dd37606ef47683abc6924942580314a981dee47..d45f065ea2b62f50594339a85e1372593e671122 100644 (file)
Binary files a/skins/default/images/messageicons.png and b/skins/default/images/messageicons.png differ
index f670182a02d3f8d1c2898d4688963002c5522850..57bed8a34af86c7de33f7ff6f6770b262f19a91c 100644 (file)
@@ -19,7 +19,7 @@
 <roundcube:if condition="template:name == 'mail'" />
 <roundcube:button name="markmenulink" id="markmenulink" type="link" class="button markmessage" title="markmessages" onclick="rcmail_ui.show_popup('markmenu');return false" content=" " />                                                                   
 <roundcube:endif />
-<roundcube:button name="messagemenulink" id="messagemenulink" type="link" class="button messagemenu" title="messageactions" onclick="rcmail_ui.show_popup('messagemenu');return false" content=" " />
+<roundcube:button name="messagemenulink" id="messagemenulink" type="link" class="button messagemenu" title="moreactions" onclick="rcmail_ui.show_popup('messagemenu');return false" content=" " />
 <roundcube:if condition="template:name == 'message'" />
 <roundcube:object name="mailboxlist" type="select" noSelection="moveto" maxlength="25" onchange="rcmail.command('moveto', this.options[this.selectedIndex].value)" class="mboxlist" folder_filter="mail" />
 <roundcube:endif />
index a4ae57d2ef13bca626be7d86efe132a5fd9397d0..4dfc206741146bdb1508c52cf051e94b841ec22a 100644 (file)
   padding-top: 2px;
   padding-bottom: 2px;
   text-decoration: none;
+  height: 15px;
 }
 
 #mailboxlist li.unread
@@ -701,7 +702,8 @@ body.messagelist
 
 #messagelist tr td.flag span,
 #messagelist tr td.status span,
-#messagelist tr td.attachment span
+#messagelist tr td.attachment span,
+#messagelist tr td.priority span
 {
   display: block;
   width: 15px;
@@ -712,6 +714,12 @@ body.messagelist
 #messagelist tr td.threads div.listmenu,
 #messagelist tr td.attachment span.attachment,
 #messagelist tr td.attachment span.report,
+#messagelist tr td.priority span.priority,
+#messagelist tr td.priority span.prio1,
+#messagelist tr td.priority span.prio2,
+#messagelist tr td.priority span.prio3,
+#messagelist tr td.priority span.prio4,
+#messagelist tr td.priority span.prio5,
 #messagelist tr td.flag span.flagged,
 #messagelist tr td.flag span.unflagged,
 #messagelist tr td.flag span.unflagged:hover,
@@ -744,6 +752,36 @@ body.messagelist
   background-position: 0 -255px;
 }
 
+#messagelist tr td.priority span.priority
+{
+  background-position: 0 -309px;
+}
+
+#messagelist tr td.priority span.prio5
+{
+  background-position: 0 -358px;
+}
+
+#messagelist tr td.priority span.prio4
+{
+  background-position: 0 -340px;
+}
+
+#messagelist tr td.priority span.prio3
+{
+  background-position: 0 -324px;
+}
+
+#messagelist tr td.priority span.prio2
+{
+  background-position: 0 -309px;
+}
+
+#messagelist tr td.priority span.prio1
+{
+  background-position: 0 -290px;
+}
+
 #messagelist tr td.flag span.flagged
 {
   background-position: 0 -153px;
@@ -839,7 +877,8 @@ body.messagelist
 #messagelist tr td.attachment,
 #messagelist tr td.threads,
 #messagelist tr td.status,
-#messagelist tr td.flag
+#messagelist tr td.flag,
+#messagelist tr td.priority
 {
   width: 17px;
   padding: 0 0 0 2px;
@@ -872,13 +911,6 @@ body.messagelist
   background-color: #FFF;
 }
 
-/*
-#messagelist tr.odd
-{
-  background-color: #F9F9F9;
-}
-*/
-
 #messagelist tr.unread
 {
   font-weight: bold;
@@ -1166,21 +1198,20 @@ div.message-htmlpart div.rcmBody
   margin: 8px;
 }
 
-#remote-objects-message
+#message-objects div
 {
-  display: none;
   margin: 8px;
   min-height: 20px;
   padding: 10px 10px 6px 46px;
 }
 
-#remote-objects-message a
+#message-objects div a
 {
   color: #666666;
   padding-left: 10px;
 }
 
-#remote-objects-message a:hover
+#message-objects div a:hover
 {
   color: #333333;
 }
index 76c3e0c9159a5dbc42272a8fbedfc1979b0e1f48..afdf6741800bcde45a14df61991cfa2a801e366a 100644 (file)
@@ -2,14 +2,14 @@
 
 body
 {
+  font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
   background-color: #ffffff;
   color: #000000;
   margin: 2mm;
 }
 
-body, td, th, span, div, p, h3
+body, td, th, span, div, p
 {
-  font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;
   font-size: 9pt;
   color: #000000;
 }
index a85c889c0cf3438cb61904ccf33e9cc60aeb976a..1930debf1c9c434f4a6f2800b1394614c7458124 100644 (file)
@@ -58,7 +58,7 @@
 </div>
 <div class="boxfooter">
   <roundcube:button command="group-create" type="link" title="newcontactgroup" class="buttonPas addgroup" classAct="button addgroup" content=" " />
-  <roundcube:button name="groupmenulink" id="groupmenulink" type="link" title="groupactions" class="button groupactions" onclick="rcmail_ui.show_popup('groupmenu');return false" content=" " />
+  <roundcube:button name="groupmenulink" id="groupmenulink" type="link" title="moreactions" class="button groupactions" onclick="rcmail_ui.show_popup('groupmenu');return false" content=" " />
 </div>
 </div>
 
@@ -99,6 +99,8 @@
   <ul>
     <li><roundcube:button command="group-rename" label="grouprename" classAct="active" /></li>
     <li><roundcube:button command="group-delete" label="groupdelete" classAct="active" /></li>
+    <li class="separator_above"><roundcube:button command="search-create" label="searchsave" classAct="active" /></li>
+    <li><roundcube:button command="search-delete" label="searchdelete" classAct="active" /></li>
     <roundcube:container name="groupoptions" id="groupoptionsmenu" />
   </ul>
 </div>
index ea6a2f71c767949694d17d15f8c07e2833c534a2..30b56aa4135f57b8f2b3dff58c7d3ce44d86b8d2 100644 (file)
     <li><input type="checkbox" name="list_col[]" value="status" id="cols_status" /><label for="cols_status"><roundcube:label name="readstatus" /></label></li>
     <li><input type="checkbox" name="list_col[]" value="attachment" id="cols_attachment" /><label for="cols_attachment"><roundcube:label name="attachment" /></label></li>
     <li><input type="checkbox" name="list_col[]" value="flag" id="cols_flag" /><label for="cols_flag"><roundcube:label name="flag" /></label></li>
+    <li><input type="checkbox" name="list_col[]" value="priority" id="cols_priority" /><label for="cols_priority"><roundcube:label name="priority" /></label></li>
   </ul>
 </fieldset>
 <roundcube:endif />
index 8e2bb2cb3af8b821beef3bb2e69bb05c055452cd..714540b7859b2bac9d328b3f921cdfe2c67fc90f 100644 (file)
@@ -36,8 +36,7 @@
 <roundcube:object name="messageHeaders" class="headers-table" cellspacing="0" cellpadding="2" addicon="/images/icons/silhouette.png" summary="Message headers" />
 <roundcube:object name="messageFullHeaders" id="full-headers" />
 <roundcube:object name="messageAttachments" id="attachment-list" />
-
-<roundcube:object name="blockedObjects" id="remote-objects-message" />
+<roundcube:object name="messageObjects" id="message-objects" />
 <roundcube:object name="messageBody" id="messagebody" />
 </div>
 <div class="boxfooter">
index bfd7d7d9206918ec41d24d7aaa94e35c698db302..a606311e1f8c70dca3d149a593e3b011f900e540 100644 (file)
@@ -13,7 +13,7 @@
 <roundcube:object name="messageAttachments" id="attachment-list" />
 </div>
 
-<roundcube:object name="blockedObjects" id="remote-objects-message" />
+<roundcube:object name="messageObjects" id="message-objects" />
 <roundcube:object name="messageBody" id="messagebody" />
 
 </body>