From a522971cf853b2f0ccd1b569491a06218ebbaee9 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Wed, 08 May 2013 08:28:36 -0400 Subject: [PATCH] Merge branch 'master' of github.com:roundcube/roundcubemail --- tests/src/format-flowed-unfolded.txt | 2 plugins/attachment_reminder/localization/en_US.inc | 6 program/lib/Roundcube/rcube_db_mysql.php | 4 program/steps/mail/compose.inc | 38 + plugins/acl/acl.php | 6 program/lib/Roundcube/rcube_imap.php | 9 program/steps/mail/show.inc | 4 program/steps/utils/spell_html.inc | 5 plugins/attachment_reminder/localization/de_DE.inc | 5 program/lib/Roundcube/html.php | 34 + plugins/managesieve/managesieve.php | 8 program/lib/Roundcube/rcube_spellchecker.php | 17 installer/rcube_install.php | 9 plugins/password/drivers/xmail.php | 4 plugins/help/help.php | 2 plugins/attachment_reminder/localization/pl_PL.inc | 6 plugins/attachment_reminder/localization/de_CH.inc | 5 program/steps/settings/func.inc | 11 program/steps/addressbook/show.inc | 8 plugins/squirrelmail_usercopy/squirrelmail_usercopy.php | 2 program/lib/Roundcube/rcube_output.php | 2 CHANGELOG | 15 program/include/rcmail_output_html.php | 42 + program/lib/utf8.class.php | 4 plugins/password/drivers/pam.php | 3 program/lib/Roundcube/rcube_message.php | 2 tests/Framework/Mime.php | 4 program/lib/Roundcube/rcube_csv2vcard.php | 53 + config/main.inc.php.dist | 3 plugins/attachment_reminder/localization/zh_TW.inc | 5 program/lib/Roundcube/rcube_mime.php | 48 + plugins/new_user_identity/new_user_identity.php | 2 program/steps/utils/spell.inc | 7 program/localization/fr_FR/csv2vcard.inc | 96 +++ program/steps/settings/save_prefs.inc | 1 program/lib/Roundcube/rcube_enriched.php | 2 program/lib/Roundcube/rcube_utils.php | 2 plugins/attachment_reminder/attachment_reminder.php | 81 +++ plugins/password/drivers/directadmin.php | 1 plugins/attachment_reminder/localization/zh_CN.inc | 5 program/steps/addressbook/import.inc | 4 program/localization/en_US/csv2vcard.inc | 17 plugins/attachment_reminder/localization/es_ES.inc | 5 program/js/app.js | 101 +-- plugins/enigma/enigma.php | 9 tests/src/Csv2vcard/tb_plain.vcf | 2 tests/src/format-flowed.txt | 2 plugins/debug_logger/runlog/runlog.php | 2 program/localization/en_GB/labels.inc | 1 program/lib/Roundcube/rcube_smtp.php | 6 plugins/vcard_attachments/vcard_attachments.php | 3 program/include/bc.php | 2 plugins/enigma/lib/enigma_ui.php | 5 bin/updatecss.sh | 122 ++++ plugins/zipdownload/zipdownload.php | 4 program/steps/mail/func.inc | 57 +- program/lib/Roundcube/rcube_plugin_api.php | 1 program/lib/Roundcube/rcube_imap_cache.php | 4 program/js/list.js | 177 +++++- plugins/attachment_reminder/localization/it_IT.inc | 6 program/lib/Roundcube/rcube_addressbook.php | 26 program/lib/Roundcube/rcube_vcard.php | 27 plugins/password/drivers/sql.php | 7 plugins/attachment_reminder/localization/nl_NL.inc | 5 program/steps/settings/folders.inc | 3 plugins/attachment_reminder/package.xml | 66 ++ program/steps/settings/edit_prefs.inc | 2 program/steps/addressbook/func.inc | 7 program/lib/Roundcube/rcube_contacts.php | 36 + plugins/enigma/lib/enigma_engine.php | 34 tests/src/Csv2vcard/tb_plain.csv | 2 plugins/attachment_reminder/attachment_reminder.js | 51 + tests/Framework/VCard.php | 14 program/steps/mail/autocomplete.inc | 2 plugins/attachment_reminder/localization/fr_FR.inc | 5 program/include/rcmail.php | 30 - program/lib/Roundcube/rcube_ldap.php | 74 + program/localization/en_US/labels.inc | 1 program/lib/Roundcube/rcube_db.php | 13 program/lib/Roundcube/rcube_imap_generic.php | 18 plugins/managesieve/lib/Roundcube/rcube_sieve_script.php | 6 plugins/autologon/autologon.php | 4 82 files changed, 1,139 insertions(+), 387 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 5ac7e59..4393515 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,12 +1,25 @@ CHANGELOG Roundcube Webmail =========================== +- Added attachment_reminder plugin +- Fix IMAP connection issue with default_socket_timeout < 0 and imap_timeout < 0 (#1489090) +- Fix various PHP code bugs found using static analysis (#1489086) +- Fix backslash character handling on vCard import (#1489085) +- Fix csv import from Thunderbird with French localization (#1489059) +- Fix messages list focus issue in Opera and Webkit (#1489058) +- Make PHP code eval() free, use create_function() +- Add option to display email address together with a name in mail preview (#1488732) +- Fix Reply-To header handling in Reply-All action (#1489037) +- Fix so Sender: address is added to Cc: field on reply to all (#1489011) +- Fix so addressbook_search_mode works also for group search (#1489079) +- Fix removal of a contact from a group in LDAP addressbook (#1489081) +- Support CSV import from Atmail (#1489045) +- Inlcude SQL query in the log on SQL error (#1489064) - Fix handling untagged responses in IMAP FETCH - "could not load message" error (#1489074) - Fix very small window size in Chrome (#1488931) - Fix list page reset when viewing a message in Larry skin (#1489076) - Fix min_refresh_interval handling on preferences save (#1489073) - Fix PDF support detection for Firefox PDF.js (#1488972) -- Fix messages list focus issue in Internet Explorer (#1489058) - Add db_prefix configuration option in place of db_table_*/db_sequence_* options - Make possible to use db_prefix for schema initialization in Installer (#1489067) - Fix updatedb.sh script so it recognizes also table prefix for external DDL files diff --git a/bin/updatecss.sh b/bin/updatecss.sh new file mode 100755 index 0000000..c477171 --- /dev/null +++ b/bin/updatecss.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env php +<?php +/* + +-----------------------------------------------------------------------+ + | bin/updatecss.sh | + | | + | This file is part of the Roundcube Webmail client | + | Copyright (C) 2010-2013, The Roundcube Dev Team | + | | + | Licensed under the GNU General Public License version 3 or | + | any later version with exceptions for skins & plugins. | + | See the README file for a full license statement. | + | | + | PURPOSE: | + | Update cache-baster marks for css background images | + +-----------------------------------------------------------------------+ + | Author: Aleksander Machniak <alec@alec.pl> | + +-----------------------------------------------------------------------+ +*/ + +define('INSTALL_PATH', realpath(dirname(__FILE__) . '/..') . '/' ); + +require_once INSTALL_PATH . 'program/include/clisetup.php'; + +// get arguments +$opts = rcube_utils::get_opt(array( + 'd' => 'dir', +)); + +if (empty($opts['dir'])) { + print "Skin directory not specified (--dir). Using skins/ and plugins/skins/.\n"; + + $dir = INSTALL_PATH . 'skins'; + $dir_p = INSTALL_PATH . 'plugins'; + $skins = glob("$dir/*", GLOB_ONLYDIR); + $skins_p = glob("$dir_p/*/skins/*", GLOB_ONLYDIR); + + $dirs = array_merge($skins, $skins_p); +} +// Check if directory exists +else if (!file_exists($opts['dir'])) { + rcube::raise_error("Specified directory doesn't exist.", false, true); +} +else { + $dirs = array($opts['dir']); +} + +foreach ($dirs as $dir) { + $img_dir = $dir . '/images'; + if (!file_exists($img_dir)) { + continue; + } + + $files = get_files($dir); + $images = get_images($img_dir); + $find = array(); + $replace = array(); + + // build regexps array + foreach ($images as $path => $sum) { + $path_ex = str_replace('.', '\\.', $path); + $find[] = "#url\(['\"]?images/$path_ex(\?v=[a-f0-9-\.]+)?['\"]?\)#"; + $replace[] = "url(images/$path?v=$sum)"; + } + + foreach ($files as $file) { + $file = $dir . '/' . $file; + print "File: $file\n"; + $content = file_get_contents($file); + $content = preg_replace($find, $replace, $content, -1, $count); + if ($count) { + file_put_contents($file, $content); + } + } +} + + +function get_images($dir) +{ + $images = array(); + $dh = opendir($dir); + + while ($file = readdir($dh)) { + if (preg_match('/^(.+)\.(gif|ico|png|jpg|jpeg)$/', $file, $m)) { + $filepath = "$dir/$file"; + $images[$file] = substr(md5_file($filepath), 0, 4) . '.' . filesize($filepath); + print "Image: $filepath ({$images[$file]})\n"; + } + else if ($file != '.' && $file != '..' && is_dir($dir . '/' . $file)) { + foreach (get_images($dir . '/' . $file) as $img => $sum) { + $images[$file . '/' . $img] = $sum; + } + } + } + + closedir($dh); + + return $images; +} + +function get_files($dir) +{ + $files = array(); + $dh = opendir($dir); + + while ($file = readdir($dh)) { + if (preg_match('/^(.+)\.(css|html)$/', $file, $m)) { + $files[] = $file; + } + else if ($file != '.' && $file != '..' && is_dir($dir . '/' . $file)) { + foreach (get_files($dir . '/' . $file) as $f) { + $files[] = $file . '/' . $f; + } + } + } + + closedir($dh); + + return $files; +} + +?> diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 5a652a5..05afed9 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -890,4 +890,7 @@ // Georgia, Helvetica, Impact, Tahoma, Terminal, Times New Roman, Trebuchet MS, Verdana $rcmail_config['default_font'] = ''; +// Enables display of email address with name instead of a name (and address in title) +$rcmail_config['message_show_email'] = false; + // end of config file diff --git a/installer/rcube_install.php b/installer/rcube_install.php index 32b6a5d..e7da54c 100644 --- a/installer/rcube_install.php +++ b/installer/rcube_install.php @@ -347,7 +347,7 @@ $this->config = array_merge($this->config, $current); - foreach ((array)$current['ldap_public'] as $key => $values) { + foreach (array_keys((array)$current['ldap_public']) as $key) { $this->config['ldap_public'][$key] = $current['ldap_public'][$key]; } } @@ -356,10 +356,11 @@ * Compare the local database schema with the reference schema * required for this version of Roundcube * - * @param boolean True if the schema schould be updated + * @param rcube_db Database object + * * @return boolean True if the schema is up-to-date, false if not or an error occured */ - function db_schema_check($DB, $update = false) + function db_schema_check($DB) { if (!$this->configured) return false; @@ -583,7 +584,7 @@ } else { // check if all keys are numeric $isnum = true; - foreach ($var as $key => $value) { + foreach (array_keys($var) as $key) { if (!is_numeric($key)) { $isnum = false; break; diff --git a/plugins/acl/acl.php b/plugins/acl/acl.php index 938287b..28139e9 100644 --- a/plugins/acl/acl.php +++ b/plugins/acl/acl.php @@ -384,7 +384,6 @@ $table->add_header(array('class' => 'acl'.$key, 'title' => $label), $label); } - $i = 1; $js_table = array(); foreach ($acl as $user => $rights) { if ($this->rc->storage->conn->user == $user) { @@ -433,8 +432,9 @@ $acl = trim(rcube_utils::get_input_value('_acl', rcube_utils::INPUT_GPC)); $oldid = trim(rcube_utils::get_input_value('_old', rcube_utils::INPUT_GPC)); - $acl = array_intersect(str_split($acl), $this->rights_supported()); - $users = $oldid ? array($user) : explode(',', $user); + $acl = array_intersect(str_split($acl), $this->rights_supported()); + $users = $oldid ? array($user) : explode(',', $user); + $result = 0; foreach ($users as $user) { $user = trim($user); diff --git a/plugins/attachment_reminder/attachment_reminder.js b/plugins/attachment_reminder/attachment_reminder.js new file mode 100755 index 0000000..a4f3e63 --- /dev/null +++ b/plugins/attachment_reminder/attachment_reminder.js @@ -0,0 +1,51 @@ +/* Attachment Reminder plugin script */ + +function rcmail_get_compose_message() +{ + var msg; + + if (window.tinyMCE && (ed = tinyMCE.get(rcmail.env.composebody))) { + msg = ed.getContent(); + msg = msg.replace(/<blockquote[^>]*>(.|[\r\n])*<\/blockquote>/gmi, ''); + } + else { + msg = $('#' + rcmail.env.composebody).val(); + msg = msg.replace(/^>.*$/gmi, ''); + } + + return msg; +} + +function rcmail_check_message(msg) +{ + var i, rx, keywords = rcmail.gettext('keywords', 'attachment_reminder').split(",").concat([".doc", ".pdf"]); + + $.each(keywords, function(n) { return RegExp.escape(n); }); + rx = new RegExp('(' + keywords.join('|') + ')', 'i'); + + return msg.search(rx) != -1; +} + +function rcmail_have_attachments() +{ + return rcmail.env.attachments && $('li', rcmail.gui_objects.attachmentlist).length; +} + + +if (window.rcmail) { + rcmail.addEventListener('beforesend', function(evt) { + var msg = rcmail_get_compose_message(), + subject = $('#compose-subject').val(); + + if (!rcmail_have_attachments() && (rcmail_check_message(msg) || rcmail_check_message(subject))) { + if (confirm(rcmail.gettext('forgotattachment', 'attachment_reminder'))) { + if (window.UI && UI.show_uploadform) // Larry skin + UI.show_uploadform(); + else if (window.rcmail_ui && rcmail_ui.show_popup) // classic skin + rcmail_ui.show_popup('uploadmenu', true); + + return false; + } + } + }); +} diff --git a/plugins/attachment_reminder/attachment_reminder.php b/plugins/attachment_reminder/attachment_reminder.php new file mode 100755 index 0000000..0a25973 --- /dev/null +++ b/plugins/attachment_reminder/attachment_reminder.php @@ -0,0 +1,81 @@ +<?php +/** + * Attachement Reminder + * + * A plugin that reminds a user to attach the files + * + * @version @package_version@ + * @author Thomas Yu - Sian, Liu + * @author Aleksander Machniak <machniak@kolabsys.com> + * + * Copyright (C) 2013 Thomas Yu - Sian, Liu + * Copyright (C) 2013, 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 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see <http://www.gnu.org/licenses/> + */ + +class attachment_reminder extends rcube_plugin +{ + public $task = 'mail|settings'; + public $noajax = true; + + + function init() + { + $rcmail = rcube::get_instance(); + + if ($rcmail->task == 'mail' && $rcmail->action == 'compose') { + $this->include_script('attachment_reminder.js'); + $this->add_texts('localization/', array('keywords', 'forgotattachment')); + } + + if ($rcmail->task == 'settings') { + $dont_override = $rcmail->config->get('dont_override', array()); + + if (!in_array('attachment_reminder', $dont_override)) { + $this->add_hook('preferences_list', array($this, 'prefs_list')); + $this->add_hook('preferences_save', array($this, 'prefs_save')); + } + } + } + + function prefs_list($args) + { + if ($args['section'] == 'compose') { + $this->add_texts('localization/'); + $reminder = rcube::get_instance()->config->get('attachment_reminder'); + $field_id = 'rcmfd_attachment_reminder'; + $checkbox = new html_checkbox(array('name' => '_attachment_reminder', 'id' => $field_id, 'value' => 1)); + + $args['blocks']['main']['options']['attachment_reminder'] = array( + 'title' => html::label($field_id, rcube::Q($this->gettext('reminderoption'))), + 'content' => $checkbox->show($reminder ? 1 : 0), + ); + } + + return $args; + } + + function prefs_save($args) + { + if ($args['section'] == 'compose') { + $dont_override = rcube::get_instance()->config->get('dont_override', array()); + if (!in_array('attachment_reminder', $dont_override)) { + $args['prefs']['attachment_reminder'] = !empty($_POST['_attachment_reminder']); + } + } + return $args; + } + +} diff --git a/plugins/attachment_reminder/localization/de_CH.inc b/plugins/attachment_reminder/localization/de_CH.inc new file mode 100644 index 0000000..bf6eef7 --- /dev/null +++ b/plugins/attachment_reminder/localization/de_CH.inc @@ -0,0 +1,5 @@ +<?php + +$messages = array(); +$messages['forgotattachment'] = "Haben Sie möglicherweise vergessen eine Datei anzuhängen?"; +$messages['keywords'] = "anbei,im anhang,angehängt,angefügt,beigefügt,beliegend"; diff --git a/plugins/attachment_reminder/localization/de_DE.inc b/plugins/attachment_reminder/localization/de_DE.inc new file mode 100644 index 0000000..bf6eef7 --- /dev/null +++ b/plugins/attachment_reminder/localization/de_DE.inc @@ -0,0 +1,5 @@ +<?php + +$messages = array(); +$messages['forgotattachment'] = "Haben Sie möglicherweise vergessen eine Datei anzuhängen?"; +$messages['keywords'] = "anbei,im anhang,angehängt,angefügt,beigefügt,beliegend"; diff --git a/plugins/attachment_reminder/localization/en_US.inc b/plugins/attachment_reminder/localization/en_US.inc new file mode 100644 index 0000000..a736682 --- /dev/null +++ b/plugins/attachment_reminder/localization/en_US.inc @@ -0,0 +1,6 @@ +<?php + +$messages = array(); +$messages['forgotattachment'] = "Did you forget to attach a file?"; +$messages['reminderoption'] = "Remind about forgotten attachments"; +$messages['keywords'] = "attachment,file,attach,attached,attaching,enclosed,CV,cover letter"; diff --git a/plugins/attachment_reminder/localization/es_ES.inc b/plugins/attachment_reminder/localization/es_ES.inc new file mode 100644 index 0000000..8e35121 --- /dev/null +++ b/plugins/attachment_reminder/localization/es_ES.inc @@ -0,0 +1,5 @@ +<?php + +$messages = array(); +$messages['forgotattachment'] = "¿Olvidó adjuntar un fichero al mensaje?"; +$messages['keywords'] = "adjunto"; diff --git a/plugins/attachment_reminder/localization/fr_FR.inc b/plugins/attachment_reminder/localization/fr_FR.inc new file mode 100644 index 0000000..ab48cc5 --- /dev/null +++ b/plugins/attachment_reminder/localization/fr_FR.inc @@ -0,0 +1,5 @@ +<?php + +$messages = array(); +$messages['forgotattachment'] = "Avez vous oublié d'attacher un fichier ?"; +$messages['keywords'] = "joins,joint,attaché,CV"; \ No newline at end of file diff --git a/plugins/attachment_reminder/localization/it_IT.inc b/plugins/attachment_reminder/localization/it_IT.inc new file mode 100644 index 0000000..2807bc1 --- /dev/null +++ b/plugins/attachment_reminder/localization/it_IT.inc @@ -0,0 +1,6 @@ +<?php + +$messages = array(); +$messages['forgotattachment'] = "Sembra che tu abbia dimenticato di allegare un file!\nPremere Annulla per inviare lo stesso.\nOK per tornare al messaggio senza inviare."; +$messages['keywords'] = "allegato,allegati,allegata,allegate,allega,allego,alleghi,attaccato,file,attachment,attach"; + diff --git a/plugins/attachment_reminder/localization/nl_NL.inc b/plugins/attachment_reminder/localization/nl_NL.inc new file mode 100644 index 0000000..62564f1 --- /dev/null +++ b/plugins/attachment_reminder/localization/nl_NL.inc @@ -0,0 +1,5 @@ +<?php + +$messages = array(); +$messages['forgotattachment'] = "Ben je vergeten het bestand bij te voegen?"; +$messages['keywords'] = "attachment,bestand,bijgaand,bijgaande,brief,bijgevoegd,bijgesloten,CV"; diff --git a/plugins/attachment_reminder/localization/pl_PL.inc b/plugins/attachment_reminder/localization/pl_PL.inc new file mode 100644 index 0000000..96f4f49 --- /dev/null +++ b/plugins/attachment_reminder/localization/pl_PL.inc @@ -0,0 +1,6 @@ +<?php + +$messages = array(); +$messages['forgotattachment'] = "Czy nie zapomniałeś załączyć pliku?"; +$messages['reminderoption'] = "Włącz przypominanie o brakującym załączniku"; +$messages['keywords'] = "załącznik,plik,załącz,CV"; diff --git a/plugins/attachment_reminder/localization/zh_CN.inc b/plugins/attachment_reminder/localization/zh_CN.inc new file mode 100644 index 0000000..27cf04b --- /dev/null +++ b/plugins/attachment_reminder/localization/zh_CN.inc @@ -0,0 +1,5 @@ +<?php + +$messages = array(); +$messages['forgotattachment'] = "您似乎忘記加入附件了,你確定要寄出?"; +$messages['keywords'] = "附件,附加,附檔,附上,附加檔案"; diff --git a/plugins/attachment_reminder/localization/zh_TW.inc b/plugins/attachment_reminder/localization/zh_TW.inc new file mode 100644 index 0000000..27cf04b --- /dev/null +++ b/plugins/attachment_reminder/localization/zh_TW.inc @@ -0,0 +1,5 @@ +<?php + +$messages = array(); +$messages['forgotattachment'] = "您似乎忘記加入附件了,你確定要寄出?"; +$messages['keywords'] = "附件,附加,附檔,附上,附加檔案"; diff --git a/plugins/attachment_reminder/package.xml b/plugins/attachment_reminder/package.xml new file mode 100644 index 0000000..4386118 --- /dev/null +++ b/plugins/attachment_reminder/package.xml @@ -0,0 +1,66 @@ +<?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>Attachment Reminder</name> + <summary>Roundcube plugin that prompts you if it looks like you wanted to attach a file but you didn't.</summary> + <description> + This Roundcube plugin reminds the user to attach a file if the composed message text indicates that there should be any. + </description> + <lead> + <name>Aleksander Machniak</name> + <user>alec</user> + <email>alec@alec.pl</email> + <active>yes</active> + </lead> + <lead> + <name>Thomas Yu - Sian , Liu</name> + <active>yes</active> + </lead> + <version> + <release>1.1</release> + <api>1.0</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <license uri="http://www.gnu.org/licenses/gpl.html">GNU GPLv3+</license> + <notes>-</notes> + <contents> + <dir baseinstalldir="/" name="/"> + <file name="attachment_reminder.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="attachment_reminder.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/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_ES.inc" role="data"></file> + <file name="localization/fr_FR.inc" role="data"></file> + <file name="localization/it_IT.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/zh_CN.inc" role="data"></file> + <file name="localization/zh_TW.inc" 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/autologon/autologon.php b/plugins/autologon/autologon.php index 63ffb94..9c7d5b6 100644 --- a/plugins/autologon/autologon.php +++ b/plugins/autologon/autologon.php @@ -19,8 +19,6 @@ function startup($args) { - $rcmail = rcmail::get_instance(); - // change action to login if (empty($_SESSION['user_id']) && !empty($_GET['_autologin']) && $this->is_localhost()) $args['action'] = 'login'; @@ -37,7 +35,7 @@ $args['cookiecheck'] = false; $args['valid'] = true; } - + return $args; } diff --git a/plugins/debug_logger/runlog/runlog.php b/plugins/debug_logger/runlog/runlog.php index c9f6726..0c766a1 100644 --- a/plugins/debug_logger/runlog/runlog.php +++ b/plugins/debug_logger/runlog/runlog.php @@ -194,7 +194,7 @@ public function print_totals(){ $totals = array(); - foreach ( $this->run_log as $k => $entry ) { + foreach ($this->run_log as $entry) { if ( $entry['type'] == 'start' && $entry['ended'] == true) { $totals[$entry['value']]['duration'] += $entry['duration']; $totals[$entry['value']]['count'] += 1; diff --git a/plugins/enigma/enigma.php b/plugins/enigma/enigma.php index c96b946..25520a2 100644 --- a/plugins/enigma/enigma.php +++ b/plugins/enigma/enigma.php @@ -47,6 +47,8 @@ $rcmail = rcmail::get_instance(); $this->rc = $rcmail; + $section = rcube_utils::get_input_value('_section', rcube_utils::INPUT_GET); + if ($this->rc->task == 'mail') { // message parse/display hooks $this->add_hook('message_part_structure', array($this, 'parse_structure')); @@ -79,7 +81,6 @@ $this->register_action('plugin.enigma', array($this, 'preferences_ui')); // grab keys/certs management iframe requests - $section = rcube_utils::get_input_value('_section', rcube_utils::INPUT_GET); if ($this->rc->action == 'edit-prefs' && preg_match('/^enigma(certs|keys)/', $section)) { $this->load_ui(); $this->ui->init($section); @@ -148,7 +149,7 @@ */ function parse_structure($p) { - $struct = $p['structure']; +// $struct = $p['structure']; if ($p['mimetype'] == 'text/plain' || $p['mimetype'] == 'application/pgp') { $this->parse_plain($p); @@ -390,7 +391,7 @@ function message_load($p) { $this->message = $p['object']; - + // handle attachments vcard attachments foreach ((array)$this->message->attachments as $attachment) { if ($this->is_keys_part($attachment)) { @@ -398,7 +399,7 @@ } } // the same with message bodies - foreach ((array)$this->message->parts as $idx => $part) { + foreach ((array)$this->message->parts as $part) { if ($this->is_keys_part($part)) { $this->keys_parts[] = $part->mime_id; $this->keys_bodies[] = $part->mime_id; diff --git a/plugins/enigma/lib/enigma_engine.php b/plugins/enigma/lib/enigma_engine.php index 220d6c0..8a64c07 100644 --- a/plugins/enigma/lib/enigma_engine.php +++ b/plugins/enigma/lib/enigma_engine.php @@ -374,17 +374,15 @@ { // @TODO: Handle big bodies using (temp) files // @TODO: caching of verification result - - $sig = $this->pgp_driver->verify($msg_body, $sig_body); + $sig = $this->pgp_driver->verify($msg_body, $sig_body); - if (($sig instanceof enigma_error) && $sig->getCode() != enigma_error::E_KEYNOTFOUND) - rcube::raise_error(array( + if (($sig instanceof enigma_error) && $sig->getCode() != enigma_error::E_KEYNOTFOUND) + rcube::raise_error(array( 'code' => 600, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Enigma plugin: " . $error->getMessage() + 'message' => "Enigma plugin: " . $sig->getMessage() ), true, false); -//print_r($sig); return $sig; } @@ -399,10 +397,8 @@ { // @TODO: Handle big bodies using (temp) files // @TODO: caching of verification result - + $key = ''; $pass = ''; // @TODO $result = $this->pgp_driver->decrypt($msg_body, $key, $pass); - -//print_r($result); if ($result instanceof enigma_error) { $err_code = $result->getCode(); @@ -430,7 +426,7 @@ { $this->load_pgp_driver(); $result = $this->pgp_driver->list_keys($pattern); - + if ($result instanceof enigma_error) { rcube::raise_error(array( 'code' => 600, 'type' => 'php', @@ -438,7 +434,7 @@ 'message' => "Enigma plugin: " . $result->getMessage() ), true, false); } - + return $result; } @@ -501,9 +497,11 @@ $uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST); $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST); $mime_id = rcube_utils::get_input_value('_part', rcube_utils::INPUT_POST); + $storage = $this->rc->get_storage(); if ($uid && $mime_id) { - $part = $this->rc->storage->get_message_part($uid, $mime_id); + $storage->set_folder($mbox); + $part = $storage->get_message_part($uid, $mime_id); } if ($part && is_array($result = $this->import_key($part))) { @@ -531,17 +529,5 @@ $part->body = $this->rc->storage->get_message_part( $uid, $part->mime_id, $part); } - } - - /** - * Adds CSS style file to the page header. - */ - private function add_css() - { - $skin = $this->rc->config->get('skin'); - if (!file_exists($this->home . "/skins/$skin/enigma.css")) - $skin = 'default'; - - $this->include_stylesheet("skins/$skin/enigma.css"); } } diff --git a/plugins/enigma/lib/enigma_ui.php b/plugins/enigma/lib/enigma_ui.php index 47366b7..adb619d 100644 --- a/plugins/enigma/lib/enigma_ui.php +++ b/plugins/enigma/lib/enigma_ui.php @@ -176,8 +176,7 @@ $search = rcube_utils::get_input_value('_q', rcube_utils::INPUT_GPC); // define list of cols to be displayed - $a_show_cols = array('name'); - $result = array(); +// $a_show_cols = array('name'); // Get the list $list = $this->enigma->engine->list_keys($search); @@ -200,7 +199,7 @@ $size = count($list); // Add rows - foreach($list as $idx => $key) { + foreach ($list as $key) { $this->rc->output->command('enigma_add_list_row', array('name' => rcube::Q($key->name), 'id' => $key->id)); } diff --git a/plugins/help/help.php b/plugins/help/help.php index 4b11dce..69da682 100644 --- a/plugins/help/help.php +++ b/plugins/help/help.php @@ -21,8 +21,6 @@ function init() { - $rcmail = rcmail::get_instance(); - $this->add_texts('localization/', false); // register task diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php index 80f590f..0e95b0f 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php @@ -206,7 +206,6 @@ // rules foreach ($this->content as $rule) { - $extension = ''; $script = ''; $tests = array(); $i = 0; @@ -1015,11 +1014,10 @@ * @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 call recursively inside a list * * @return mixed Tokens array or string if $num=1 */ - static function tokenize(&$str, $num=0, $in_list=false) + static function tokenize(&$str, $num=0) { $result = array(); @@ -1054,7 +1052,7 @@ // Parenthesized list case '[': $str = substr($str, 1); - $result[] = self::tokenize($str, 0, true); + $result[] = self::tokenize($str, 0); break; case ']': $str = substr($str, 1); diff --git a/plugins/managesieve/managesieve.php b/plugins/managesieve/managesieve.php index 817fa86..fc2a79d 100644 --- a/plugins/managesieve/managesieve.php +++ b/plugins/managesieve/managesieve.php @@ -1640,7 +1640,7 @@ .'value="' . rcube::Q($action['value']) . '" size="35" ' . $this->error_class($id, 'action', 'value', 'action_varvalue') .' />'; $out .= '<br /><span class="label">' .rcube::Q($this->gettext('setvarmodifiers')) . '</span><br />'; - foreach ($set_modifiers as $j => $s_m) { + foreach ($set_modifiers as $s_m) { $s_m_id = 'action_varmods' . $id . $s_m; $out .= sprintf('<input type="checkbox" name="_action_varmods[%s][]" value="%s" id="%s"%s />%s<br>', $id, $s_m, $s_m_id, @@ -1902,7 +1902,7 @@ $user_script = $_SESSION['managesieve_user_script']; // if the script is not active... - if ($user_script && ($key = array_search($name, $this->active)) === false) { + if ($user_script && 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(); @@ -1920,7 +1920,7 @@ // get all active scripts for sorting foreach ($script as $rid => $rules) { - foreach ($rules['actions'] as $aid => $action) { + foreach ($rules['actions'] as $action) { if ($action['type'] == 'include' && empty($action['global'])) { $target = $extension ? preg_replace($regexp, '', $action['target']) : $action['target']; $list[] = $target; @@ -1988,7 +1988,7 @@ $name = $name.$extension; foreach ($script as $rid => $rules) { - foreach ($rules['actions'] as $aid => $action) { + foreach ($rules['actions'] as $action) { if ($action['type'] == 'include' && empty($action['global']) && $action['target'] == $name ) { diff --git a/plugins/new_user_identity/new_user_identity.php b/plugins/new_user_identity/new_user_identity.php index f98145b..d32051e 100644 --- a/plugins/new_user_identity/new_user_identity.php +++ b/plugins/new_user_identity/new_user_identity.php @@ -33,8 +33,6 @@ function lookup_user_name($args) { - $rcmail = rcmail::get_instance(); - if ($this->init_ldap($args['host'])) { $results = $this->ldap->search('*', $args['user'], true); if (count($results->records) == 1) { diff --git a/plugins/password/drivers/directadmin.php b/plugins/password/drivers/directadmin.php index 8bf0dc6..44ecea4 100644 --- a/plugins/password/drivers/directadmin.php +++ b/plugins/password/drivers/directadmin.php @@ -297,7 +297,6 @@ $status = socket_get_status($socket); $startTime = time(); $length = 0; - $prevSecond = 0; while ( !feof($socket) && !$status['timed_out'] ) { $chunk = fgets($socket,1024); diff --git a/plugins/password/drivers/pam.php b/plugins/password/drivers/pam.php index 8cd94c7..4d0ba16 100644 --- a/plugins/password/drivers/pam.php +++ b/plugins/password/drivers/pam.php @@ -11,7 +11,8 @@ { function save($currpass, $newpass) { - $user = $_SESSION['username']; + $user = $_SESSION['username']; + $error = ''; if (extension_loaded('pam') || extension_loaded('pam_auth')) { if (pam_auth($user, $currpass, $error, false)) { diff --git a/plugins/password/drivers/sql.php b/plugins/password/drivers/sql.php index e02bff1..7a51dfe 100644 --- a/plugins/password/drivers/sql.php +++ b/plugins/password/drivers/sql.php @@ -34,8 +34,9 @@ $db = $rcmail->get_dbh(); } - if ($err = $db->is_error()) + if ($db->is_error()) { return PASSWORD_ERROR; + } // crypted password if (strpos($sql, '%c') !== FALSE) { @@ -183,8 +184,8 @@ $res = $db->query($sql, $sql_vars); if (!$db->is_error()) { - if (strtolower(substr(trim($query),0,6))=='select') { - if ($result = $db->fetch_array($res)) + if (strtolower(substr(trim($sql),0,6)) == 'select') { + if ($db->fetch_array($res)) return PASSWORD_SUCCESS; } else { // This is the good case: 1 row updated diff --git a/plugins/password/drivers/xmail.php b/plugins/password/drivers/xmail.php index 37abc30..59e467c 100644 --- a/plugins/password/drivers/xmail.php +++ b/plugins/password/drivers/xmail.php @@ -67,7 +67,7 @@ function send($msg) { socket_write($this->socket,$msg); - if (substr($in = socket_read($this->socket, 512, PHP_BINARY_READ),0,1) != "+") { + if (substr(socket_read($this->socket, 512, PHP_BINARY_READ),0,1) != "+") { return false; } return true; @@ -85,7 +85,7 @@ return false; } - if (substr($in = socket_read($this->socket, 512, PHP_BINARY_READ),0,1) != "+") { + if (substr(socket_read($this->socket, 512, PHP_BINARY_READ),0,1) != "+") { socket_close($this->socket); return false; } diff --git a/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php b/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php index d5d0d47..e882a2f 100644 --- a/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php +++ b/plugins/squirrelmail_usercopy/squirrelmail_usercopy.php @@ -63,7 +63,7 @@ if ($this->prefs['___sig'.$i.'___']) $ident_data['signature'] = $this->prefs['___sig'.$i.'___']; // insert identity - $identid = $rcmail->user->insert_identity($ident_data); + $rcmail->user->insert_identity($ident_data); } } diff --git a/plugins/vcard_attachments/vcard_attachments.php b/plugins/vcard_attachments/vcard_attachments.php index 4905b37..cf7e22d 100644 --- a/plugins/vcard_attachments/vcard_attachments.php +++ b/plugins/vcard_attachments/vcard_attachments.php @@ -45,7 +45,7 @@ } } // the same with message bodies - foreach ((array)$this->message->parts as $idx => $part) { + foreach ((array)$this->message->parts as $part) { if ($this->is_vcard($part)) { $this->vcard_parts[] = $part->mime_id; $this->vcard_bodies[] = $part->mime_id; @@ -63,7 +63,6 @@ function html_output($p) { $attach_script = false; - $icon = 'plugins/vcard_attachments/' .$this->local_skin_path(). '/vcard_add_contact.png'; foreach ($this->vcard_parts as $part) { $vcards = rcube_vcard::import($this->message->get_part_content($part, null, true)); diff --git a/plugins/zipdownload/zipdownload.php b/plugins/zipdownload/zipdownload.php index 7e132bf..fbf1d23 100644 --- a/plugins/zipdownload/zipdownload.php +++ b/plugins/zipdownload/zipdownload.php @@ -169,7 +169,7 @@ for ($i = 0; ($i * $imap->get_pagesize()) <= $count; $i++) { $a_headers = $imap->list_messages($mbox_name, ($i + 1)); - foreach ($a_headers as $n => $header) { + foreach ($a_headers as $header) { if (empty($header)) continue; @@ -199,7 +199,7 @@ $zip = new ZipArchive(); $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE); - foreach ($uids as $key => $uid){ + foreach ($uids as $uid){ $headers = $imap->get_message_headers($uid); $subject = rcube_mime::decode_mime_string((string)$headers->subject); $subject = $this->_convert_filename($subject); diff --git a/program/include/bc.php b/program/include/bc.php index df01832..0ddfb32 100644 --- a/program/include/bc.php +++ b/program/include/bc.php @@ -62,7 +62,7 @@ function rcmail_temp_gc() { - $rcmail = rcmail::get_instance()->temp_gc(); + rcmail::get_instance()->temp_gc(); } function rcube_charset_convert($str, $from, $to=NULL) diff --git a/program/include/rcmail.php b/program/include/rcmail.php index 7acb349..c16257d 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -596,7 +596,7 @@ $post_host = rcube_utils::get_input_value('_host', rcube_utils::INPUT_POST); $post_user = rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST); - list($user, $domain) = explode('@', $post_user); + list(, $domain) = explode('@', $post_user); // direct match in default_host array if ($default_host[$post_host] || in_array($post_host, array_values($default_host))) { @@ -696,28 +696,6 @@ $token = rcube_utils::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); } @@ -1167,7 +1145,7 @@ */ public function table_output($attrib, $table_data, $a_show_cols, $id_col) { - $table = new html_table(/*array('cols' => count($a_show_cols))*/); + $table = new html_table($attrib); // add table header if (!$attrib['noheader']) { @@ -1532,7 +1510,7 @@ $collapsed = $this->config->get('collapsed_folders'); $out = ''; - foreach ($arrFolders as $key => $folder) { + foreach ($arrFolders as $folder) { $title = null; $folder_class = $this->folder_classname($folder['id']); $is_collapsed = strpos($collapsed, '&'.rawurlencode($folder['id']).'&') !== false; @@ -1618,7 +1596,7 @@ { $out = ''; - foreach ($arrFolders as $key => $folder) { + foreach ($arrFolders as $folder) { // skip exceptions (and its subfolders) if (!empty($opts['exceptions']) && in_array($folder['id'], $opts['exceptions'])) { continue; diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index d8996ed..02eef2f 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -731,14 +731,13 @@ /** * Determines if a given condition is met * - * @todo Get rid off eval() once I understand what this does. * @todo Extend this to allow real conditions, not just "set" * @param string Condition statement * @return boolean True if condition is met, False if not */ protected function check_condition($condition) { - return eval("return (".$this->parse_expression($condition).");"); + return $this->eval_expression($condition); } @@ -760,14 +759,15 @@ /** - * Parses expression and replaces variables + * Parse & evaluate a given expression and return its result. * - * @param string Expression statement - * @return string Expression value + * @param string Expression statement + * + * @return mixed Expression result */ - protected function parse_expression($expression) + protected function eval_expression ($expression) { - return preg_replace( + $expression = preg_replace( array( '/session:([a-z0-9_]+)/i', '/config:([a-z0-9_]+)(:([a-z0-9_]+))?/i', @@ -779,14 +779,29 @@ ), array( "\$_SESSION['\\1']", - "\$this->app->config->get('\\1',rcube_utils::get_boolean('\\3'))", - "\$this->env['\\1']", + "\$app->config->get('\\1',rcube_utils::get_boolean('\\3'))", + "\$env['\\1']", "rcube_utils::get_input_value('\\1', rcube_utils::INPUT_GPC)", "\$_COOKIE['\\1']", - "\$this->browser->{'\\1'}", + "\$browser->{'\\1'}", $this->template_name, ), - $expression); + $expression + ); + + $fn = create_function('$app,$browser,$env', "return ($expression);"); + if (!$fn) { + rcube::raise_error(array( + 'code' => 505, + 'type' => 'php', + 'file' => __FILE__, + 'line' => __LINE__, + 'message' => "Expression parse error on: ($expression)"), true, false); + + return null; + } + + return $fn($this->app, $this->browser, $this->env); } @@ -839,7 +854,7 @@ // show a label case 'label': if ($attrib['expression']) - $attrib['name'] = eval("return " . $this->parse_expression($attrib['expression']) .";"); + $attrib['name'] = $this->eval_expression($attrib['expression']); if ($attrib['name'] || $attrib['command']) { // @FIXME: 'noshow' is useless, remove? @@ -971,8 +986,7 @@ // return code for a specified eval expression case 'exp': - $value = $this->parse_expression($attrib['expression']); - return eval("return html::quote($value);"); + return html::quote($this->eval_expression($attrib['expression'])); // return variable case 'var': diff --git a/program/js/app.js b/program/js/app.js index 474a1b8..af09572 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -936,16 +936,13 @@ url._to = props; } else { - // use contact_id passed as command parameter - var n, len, a_cids = []; + var a_cids = []; + // use contact id passed as command parameter if (props) a_cids.push(props); // get selected contacts - else if (this.contact_list) { - var selection = this.contact_list.get_selection(); - for (n=0, len=selection.length; n<len; n++) - a_cids.push(selection[n]); - } + else if (this.contact_list) + a_cids = this.contact_list.get_selection(); if (a_cids.length) this.http_post('mailto', { _cid: a_cids.join(','), _source: this.env.source }, true); @@ -1582,7 +1579,7 @@ this.msglist_set_coltypes = function(list) { - var i, found, name, cols = list.list.tHead.rows[0].cells; + var i, found, name, cols = list.thead.rows[0].cells; this.env.coltypes = []; @@ -1632,13 +1629,17 @@ this.open_window = function(url, width, height) { - var w = Math.min(width, screen.width - 10), - h = Math.min(height, screen.height - 100), - l = (screen.width - w) / 2 + (screen.left || 0), - t = Math.max(0, (screen.height - h) / 2 + (screen.top || 0) - 20), + var dh = (window.outerHeight || 0) - (window.innerHeight || 0), + dw = (window.outerWidth || 0) - (window.innerWidth || 0), + sh = screen.availHeight || screen.height, + sw = screen.availWidth || screen.width, + w = Math.min(width, sw), + h = Math.min(height, sh), + l = Math.max(0, (sw - w) / 2 + (screen.left || 0)), + t = Math.max(0, (sh - h) / 2 + (screen.top || 0)), wname = 'rcmextwin' + new Date().getTime(), extwin = window.open(url + (url.match(/\?/) ? '&' : '?') + '_extwin=1', wname, - 'width='+w+',height='+h+',top='+t+',left='+l+',resizable=yes,toolbar=no,status=no,location=no'); + 'width='+(w-dw)+',height='+(h-dh)+',top='+t+',left='+l+',resizable=yes,toolbar=no,status=no,location=no'); // write loading... message to empty windows if (!url && extwin.document) { @@ -1732,10 +1733,7 @@ + (flags.flagged ? ' flagged' : '') + (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'); - - row.id = 'rcmrow'+uid; + row = { cols:[], style:{}, id:'rcmrow'+uid }; // message status icons css_class = 'msgicon'; @@ -1799,8 +1797,7 @@ // add each submitted col for (n in this.env.coltypes) { c = this.env.coltypes[n]; - col = document.createElement('td'); - col.className = String(c).toLowerCase(); + col = { className: String(c).toLowerCase() }; if (c == 'flag') { css_class = (flags.flagged ? 'flagged' : 'unflagged'); @@ -1845,8 +1842,7 @@ html = cols[c]; col.innerHTML = html; - - row.appendChild(col); + row.cols.push(col); } list.insert_row(row, attop); @@ -2209,7 +2205,7 @@ if (root) row = rows[root] ? rows[root].obj : null; else - row = this.message_list.list.tBodies[0].firstChild; + row = this.message_list.tbody.firstChild; while (row) { if (row.nodeType == 1 && (r = rows[row.uid])) { @@ -2385,7 +2381,7 @@ this.delete_excessive_thread_rows = function() { var rows = this.message_list.rows, - tbody = this.message_list.list.tBodies[0], + tbody = this.message_list.tbody, row = tbody.firstChild, cnt = this.env.pagesize + 1; @@ -4328,21 +4324,7 @@ newcid = newcid+'-'+source; } - if (list.rows[cid] && (row = list.rows[cid].obj)) { - for (c=0; c<cols_arr.length; c++) - if (row.cells[c]) - $(row.cells[c]).html(cols_arr[c]); - - // cid change - if (newcid) { - newcid = this.html_identifier(newcid); - row.id = 'rcmrow' + newcid; - list.remove_row(cid); - list.init_row(row); - list.selection[0] = newcid; - row.style.display = ''; - } - } + list.update_row(cid, cols_arr, newcid, true); }; // add row to contacts list @@ -4352,7 +4334,7 @@ return false; var c, col, list = this.contact_list, - row = document.createElement('tr'); + row = { cols:[] }; row.id = 'rcmrow'+this.html_identifier(cid); row.className = 'contact ' + (classes || ''); @@ -4362,10 +4344,10 @@ // add each submitted col for (c in cols) { - col = document.createElement('td'); + col = {}; col.className = String(c).toLowerCase(); col.innerHTML = cols[c]; - row.appendChild(col); + row.cols.push(col); } list.insert_row(row); @@ -4471,11 +4453,22 @@ 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) : $('ul.groups li:last', this.get_folder_li(this.env.source,'',true)); + var ul, li; + + // find list (UL) element + if (type == 'contactsearch') + ul = this.gui_objects.folderlist; + else + ul = $('ul.groups', this.get_folder_li(this.env.source,'',true)); + + // append to the list + li = $('li:last', ul); if (li.length) this.name_input_li.insertAfter(li); - else - this.name_input_li.appendTo(type == 'contactsearch' ? this.gui_objects.folderlist : $('ul.groups', this.get_folder_li(this.env.source,'',true))); + else { + this.name_input_li.appendTo(ul); + ul.show(); // make sure the list is visible + } } this.name_input.select().focus(); @@ -4532,11 +4525,13 @@ this.reset_add_input = function() { if (this.name_input) { + var li = this.name_input.parent(); if (this.env.group_renaming) { - var li = this.name_input.parent(); li.children().last().show(); this.env.group_renaming = false; } + else if ($('li', li.parent()).length == 1) + li.parent().hide(); this.name_input.remove(); @@ -4964,17 +4959,15 @@ this.update_identity_row = function(id, name, add) { - var row, col, list = this.identity_list, + var list = this.identity_list, rid = this.html_identifier(id); - if (list.rows[rid] && (row = list.rows[rid].obj)) { - $(row.cells[0]).html(name); - } - else if (add) { - row = $('<tr>').attr('id', 'rcmrow'+rid).get(0); - col = $('<td>').addClass('mail').html(name).appendTo(row); - list.insert_row(row); + if (add) { + list.insert_row({ id:'rcmrow'+rid, cols:[ { className:'mail', innerHTML:name } ] }); list.select(rid); + } + else { + list.update_row(rid, [ name ]); } }; @@ -5750,7 +5743,7 @@ this.set_message_coltypes = function(coltypes, repl, smart_col) { var list = this.message_list, - thead = list ? list.list.tHead : null, + thead = list ? list.thead : null, cell, col, n, len, th, tr; this.env.coltypes = coltypes; diff --git a/program/js/list.js b/program/js/list.js index e689c67..fbebc8a 100644 --- a/program/js/list.js +++ b/program/js/list.js @@ -30,6 +30,9 @@ this.BACKSPACE_KEY = 8; this.list = list ? list : null; + this.tagname = this.list ? this.list.nodeName.toLowerCase() : 'table'; + this.thead; + this.tbody; this.frame = null; this.rows = []; this.selection = []; @@ -56,7 +59,7 @@ this.focused = false; this.drag_mouse_start = null; this.dblclick_time = 600; - this.row_init = function(){}; + this.row_init = function(){}; // @deprecated; use list.addEventListener('initrow') instead // overwrite default paramaters if (p && typeof p === 'object') @@ -73,11 +76,19 @@ */ init: function() { - if (this.list && this.list.tBodies[0]) { + if (this.tagname == 'table' && this.list && this.list.tBodies[0]) { + this.thead = this.list.tHead; + this.tbody = this.list.tBodies[0]; + } + else if (this.tagname != 'table' && this.list) { + this.tbody = this.list; + } + + if (this.tbody) { this.rows = []; this.rowcount = 0; - var r, len, rows = this.list.tBodies[0].rows; + var r, len, rows = this.tbody.childNodes; for (r=0, len=rows.length; r<len; r++) { this.init_row(rows[r]); @@ -127,7 +138,8 @@ if (document.all) row.onselectstart = function() { return false; }; - this.row_init(this.rows[uid]); + this.row_init(this.rows[uid]); // legacy support + this.triggerEvent('initrow', this.rows[uid]); } }, @@ -137,16 +149,16 @@ */ init_header: function() { - if (this.list && this.list.tHead) { + if (this.thead) { this.colcount = 0; var col, r, p = this; // add events for list columns moving - if (this.column_movable && this.list.tHead && this.list.tHead.rows) { - for (r=0; r<this.list.tHead.rows[0].cells.length; r++) { + if (this.column_movable && this.thead && this.thead.rows) { + for (r=0; r<this.thead.rows[0].cells.length; r++) { if (this.column_fixed == r) continue; - col = this.list.tHead.rows[0].cells[r]; + col = this.thead.rows[0].cells[r]; col.onmousedown = function(e){ return p.drag_column(e, this); }; this.colcount++; } @@ -160,10 +172,16 @@ */ clear: function(sel) { - var tbody = document.createElement('tbody'); + if (this.tagname == 'table') { + var tbody = document.createElement('tbody'); + this.list.insertBefore(tbody, this.tbody); + this.list.removeChild(this.list.tBodies[1]); + this.tbody = tbody; + } + else { + $(this.row_tagname() + ':not(.thead)', this.tbody).remove(); + } - this.list.insertBefore(tbody, this.list.tBodies[0]); - this.list.removeChild(this.list.tBodies[1]); this.rows = []; this.rowcount = 0; @@ -181,12 +199,12 @@ */ remove_row: function(uid, sel_next) { - var obj = this.rows[uid] ? this.rows[uid].obj : null; + var node = this.rows[uid] ? this.rows[uid].obj : null; - if (!obj) + if (!node) return; - obj.style.display = 'none'; + node.style.display = 'none'; if (sel_next) this.select_next(); @@ -201,9 +219,28 @@ */ insert_row: function(row, before) { - var tbody = this.list.tBodies[0]; + var tbody = this.tbody; - if (before && tbody.rows.length) + // create a real dom node first + if (row.nodeName === undefined) { + // for performance reasons use DOM instead of jQuery here + var domrow = document.createElement(this.row_tagname()); + if (row.id) domrow.id = row.id; + if (row.className) domrow.className = row.className; + if (row.style) $.extend(domrow.style, row.style); + + for (var domcell, col, i=0; row.cols && i < row.cols.length; i++) { + col = row.cols[i]; + domcell = document.createElement(this.col_tagname()); + if (col.className) domcell.className = col.className; + if (col.innerHTML) domcell.innerHTML = col.innerHTML; + domrow.appendChild(domcell); + } + + row = domrow; + } + + if (before && tbody.childNodes.length) tbody.insertBefore(row, (typeof before == 'object' && before.parentNode == tbody) ? before : tbody.firstChild); else tbody.appendChild(row); @@ -212,6 +249,28 @@ this.rowcount++; }, +/** + * + */ +update_row: function(id, cols, newid, select) +{ + var row = this.rows[id]; + if (!row) return false; + + var domrow = row.obj; + for (var domcell, col, i=0; cols && i < cols.length; i++) { + this.get_cell(domrow, i).html(cols[i]); + } + + if (newid) { + delete this.rows[id]; + domrow.id = 'rcmrow' + newid; + this.init_row(domrow); + + if (select) + this.selection[0] = newid; + } +}, /** @@ -230,9 +289,9 @@ } // Un-focus already focused elements (#1487123, #1487316, #1488600, #1488620) + // It looks that window.focus() does the job for all browsers, but not Firefox (#1489058) $(':focus:not(body)').blur(); - // un-focus iframe bodies (#1489058), this doesn't work in Opera and Chrome - $('iframe').contents().find('body').blur(); + window.focus(); if (e || (e = window.event)) rcube_event.cancel(e); @@ -271,8 +330,8 @@ this.add_dragfix(); // find selected column number - for (var i=0; i<this.list.tHead.rows[0].cells.length; i++) { - if (col == this.list.tHead.rows[0].cells[i]) { + for (var i=0; i<this.thead.rows[0].cells.length; i++) { + if (col == this.thead.rows[0].cells[i]) { this.selected_column = i; break; } @@ -451,7 +510,7 @@ this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded, obj:row.obj }); } else { - var tbody = this.list.tBodies[0]; + var tbody = this.tbody; new_row = tbody.firstChild; depth = 0; last_expanded_parent_depth = 0; @@ -504,7 +563,7 @@ return false; } else { - new_row = this.list.tBodies[0].firstChild; + new_row = this.tbody.firstChild; depth = 0; } @@ -543,7 +602,7 @@ this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded, obj:row.obj }); } else { - new_row = this.list.tBodies[0].firstChild; + new_row = this.tbody.firstChild; depth = 0; } @@ -611,7 +670,7 @@ get_first_row: function() { if (this.rowcount) { - var i, len, rows = this.list.tBodies[0].rows; + var i, len, rows = this.tbody.childNodes; for (i=0, len=rows.length-1; i<len; i++) if (rows[i].id && String(rows[i].id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i) && this.rows[RegExp.$1] != null) @@ -624,7 +683,7 @@ get_last_row: function() { if (this.rowcount) { - var i, rows = this.list.tBodies[0].rows; + var i, rows = this.tbody.childNodes; for (i=rows.length-1; i>=0; i--) if (rows[i].id && String(rows[i].id).match(/^rcmrow([a-z0-9\-_=\+\/]+)/i) && this.rows[RegExp.$1] != null) @@ -634,6 +693,22 @@ return null; }, +row_tagname: function() +{ + var row_tagnames = { table:'tr', ul:'li', '*':'div' }; + return row_tagnames[this.tagname] || row_tagnames['*']; +}, + +col_tagname: function() +{ + var col_tagnames = { table:'td', '*':'span' }; + return col_tagnames[this.tagname] || col_tagnames['*']; +}, + +get_cell: function(row, index) +{ + return $(this.col_tagname(), row).eq(index); +}, /** * selects or unselects the proper row depending on the modifier key pressed @@ -781,19 +856,19 @@ this.shift_start = id; var n, i, j, to_row = this.rows[id], - from_rowIndex = this.rows[this.shift_start].obj.rowIndex, - to_rowIndex = to_row.obj.rowIndex; + from_rowIndex = this._rowIndex(this.rows[this.shift_start].obj), + to_rowIndex = this._rowIndex(to_row.obj); if (!to_row.expanded && to_row.has_children) if (to_row = this.rows[(this.row_children(id)).pop()]) - to_rowIndex = to_row.obj.rowIndex; + to_rowIndex = this._rowIndex(to_row.obj); i = ((from_rowIndex < to_rowIndex) ? from_rowIndex : to_rowIndex), j = ((from_rowIndex > to_rowIndex) ? from_rowIndex : to_rowIndex); // iterate through the entire message list for (n in this.rows) { - if (this.rows[n].obj.rowIndex >= i && this.rows[n].obj.rowIndex <= j) { + if (this._rowIndex(this.rows[n].obj) >= i && this._rowIndex(this.rows[n].obj) <= j) { if (!this.in_selection(n)) { this.highlight_row(n, true); } @@ -806,6 +881,13 @@ } }, +/** + * Helper method to emulate the rowIndex property of non-tr elements + */ +_rowIndex: function(obj) +{ + return (obj.rowIndex !== undefined) ? obj.rowIndex : $(obj).prevAll().length; +}, /** * Check if given id is part of the current selection @@ -1150,7 +1232,7 @@ this.draglayer.html(''); // get subjects of selected messages - var i, n, obj; + var i, n, obj, me; for (n=0; n<this.selection.length; n++) { // only show 12 lines if (n>12) { @@ -1158,29 +1240,28 @@ break; } + me = this; if (obj = this.rows[this.selection[n]].obj) { - for (i=0; i<obj.childNodes.length; i++) { - if (obj.childNodes[i].nodeName == 'TD') { - if (n == 0) - this.drag_start_pos = $(obj.childNodes[i]).offset(); + $('> '+this.col_tagname(), obj).each(function(i,elem){ + if (n == 0) + me.drag_start_pos = $(elem).offset(); - if (this.subject_col < 0 || (this.subject_col >= 0 && this.subject_col == i)) { - var subject = $(obj.childNodes[i]).text(); + if (me.subject_col < 0 || (me.subject_col >= 0 && me.subject_col == i)) { + var subject = $(elem).text(); - if (!subject) - break; - + if (subject) { // remove leading spaces subject = $.trim(subject); // truncate line to 50 characters subject = (subject.length > 50 ? subject.substring(0, 50) + '...' : subject); var entry = $('<div>').text(subject); - this.draglayer.append(entry); - break; + me.draglayer.append(entry); } + + return false; // break } - } + }); } } @@ -1255,7 +1336,7 @@ if (!this.col_draglayer) { var lpos = $(this.list).offset(), - cells = this.list.tHead.rows[0].cells; + cells = this.thead.rows[0].cells; // create dragging layer this.col_draglayer = $('<div>').attr('id', 'rcmcoldraglayer') @@ -1411,7 +1492,11 @@ */ column_replace: function(from, to) { - var len, cells = this.list.tHead.rows[0].cells, + // only supported for <table> lists + if (!this.thead || !this.thead.rows) + return; + + var len, cells = this.thead.rows[0].cells, elem = cells[from], before = cells[to], td = document.createElement('td'); @@ -1424,8 +1509,8 @@ cells[0].parentNode.replaceChild(elem, td); // replace list cells - for (r=0, len=this.list.tBodies[0].rows.length; r<len; r++) { - row = this.list.tBodies[0].rows[r]; + for (r=0, len=this.tbody.rows.length; r<len; r++) { + row = this.tbody.rows[r]; elem = row.cells[from]; before = row.cells[to]; diff --git a/program/lib/Roundcube/html.php b/program/lib/Roundcube/html.php index dbc9ca5..830ada9 100644 --- a/program/lib/Roundcube/html.php +++ b/program/lib/Roundcube/html.php @@ -678,6 +678,11 @@ { $default_attrib = self::$doctype == 'xhtml' ? array('summary' => '', 'border' => 0) : array(); $this->attrib = array_merge($attrib, $default_attrib); + + if (!empty($attrib['tagname']) && $attrib['tagname'] != 'table') { + $this->tagname = $attrib['tagname']; + $this->allowed = self::$common_attrib; + } } /** @@ -816,19 +821,20 @@ if (!empty($this->header)) { $rowcontent = ''; foreach ($this->header as $c => $col) { - $rowcontent .= self::tag('td', $col->attrib, $col->content); + $rowcontent .= self::tag($this->_col_tagname(), $col->attrib, $col->content); } - $thead = self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib)); + $thead = $this->tagname == 'table' ? self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib)) : + self::tag($this->_row_tagname(), array('class' => 'thead'), $rowcontent, parent::$common_attrib); } foreach ($this->rows as $r => $row) { $rowcontent = ''; foreach ($row->cells as $c => $col) { - $rowcontent .= self::tag('td', $col->attrib, $col->content); + $rowcontent .= self::tag($this->_col_tagname(), $col->attrib, $col->content); } if ($r < $this->rowindex || count($row->cells)) { - $tbody .= self::tag('tr', $row->attrib, $rowcontent, parent::$common_attrib); + $tbody .= self::tag($this->_row_tagname(), $row->attrib, $rowcontent, parent::$common_attrib); } } @@ -837,7 +843,7 @@ } // add <tbody> - $this->content = $thead . self::tag('tbody', null, $tbody); + $this->content = $thead . ($this->tagname == 'table' ? self::tag('tbody', null, $tbody) : $tbody); unset($this->attrib['cols'], $this->attrib['rowsonly']); return parent::show(); @@ -862,4 +868,22 @@ $this->rowindex = 0; } + /** + * Getter for the corresponding tag name for table row elements + */ + private function _row_tagname() + { + static $row_tagnames = array('table' => 'tr', 'ul' => 'li', '*' => 'div'); + return $row_tagnames[$this->tagname] ?: $row_tagnames['*']; + } + + /** + * Getter for the corresponding tag name for table cell elements + */ + private function _col_tagname() + { + static $col_tagnames = array('table' => 'td', '*' => 'span'); + return $col_tagnames[$this->tagname] ?: $col_tagnames['*']; + } + } diff --git a/program/lib/Roundcube/rcube_addressbook.php b/program/lib/Roundcube/rcube_addressbook.php index cbc3c67..d23ad36 100644 --- a/program/lib/Roundcube/rcube_addressbook.php +++ b/program/lib/Roundcube/rcube_addressbook.php @@ -309,9 +309,14 @@ * List all active contact groups of this source * * @param string Optional search string to match group name + * @param int Matching mode: + * 0 - partial (*abc*), + * 1 - strict (=), + * 2 - prefix (abc*) + * * @return array Indexed list of contact groups, each a hash array */ - function list_groups($search = null) + function list_groups($search = null, $mode = 0) { /* empty for address books don't supporting groups */ return array(); @@ -370,9 +375,10 @@ /** * Add the given contact records the a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be added - * @return int Number of contacts added + * @param string Group identifier + * @param array|string List of contact identifiers to be added + * + * @return int Number of contacts added */ function add_to_group($group_id, $ids) { @@ -383,9 +389,10 @@ /** * Remove the given contact records from a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be removed - * @return int Number of deleted group members + * @param string Group identifier + * @param array|string List of contact identifiers to be removed + * + * @return int Number of deleted group members */ function remove_from_group($group_id, $ids) { @@ -425,7 +432,7 @@ $out = array_merge($out, (array)$values); } else { - list($f, $type) = explode(':', $c); + list(, $type) = explode(':', $c); $out[$type] = array_merge((array)$out[$type], (array)$values); } } @@ -528,7 +535,7 @@ */ public static function compose_contact_key($contact, $sort_col) { - $key = $contact[$sort_col] . ':' . $row['sourceid']; + $key = $contact[$sort_col] . ':' . $contact['sourceid']; // add email to a key to not skip contacts with the same name (#1488375) if (!empty($contact['email'])) { @@ -537,7 +544,6 @@ return $key; } - /** * Compare search value with contact data diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index e4fd7dc..3919cdc 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -137,16 +137,34 @@ * List all active contact groups of this source * * @param string Search string to match group name + * @param int Matching mode: + * 0 - partial (*abc*), + * 1 - strict (=), + * 2 - prefix (abc*) + * * @return array Indexed list of contact groups, each a hash array */ - function list_groups($search = null) + function list_groups($search = null, $mode = 0) { $results = array(); if (!$this->groups) return $results; - $sql_filter = $search ? " AND " . $this->db->ilike('name', '%'.$search.'%') : ''; + if ($search) { + switch (intval($mode)) { + case 1: + $sql_filter = $this->db->ilike('name', $search); + break; + case 2: + $sql_filter = $this->db->ilike('name', $search . '%'); + break; + default: + $sql_filter = $this->db->ilike('name', '%' . $search . '%'); + } + + $sql_filter = " AND $sql_filter"; + } $sql_result = $this->db->query( "SELECT * FROM ".$this->db->table_name($this->db_groups). @@ -879,9 +897,10 @@ /** * Add the given contact records the a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be added - * @return int Number of contacts added + * @param string Group identifier + * @param array|string List of contact identifiers to be added + * + * @return int Number of contacts added */ function add_to_group($group_id, $ids) { @@ -926,9 +945,10 @@ /** * Remove the given contact records from a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be removed - * @return int Number of deleted group members + * @param string Group identifier + * @param array|string List of contact identifiers to be removed + * + * @return int Number of deleted group members */ function remove_from_group($group_id, $ids) { diff --git a/program/lib/Roundcube/rcube_csv2vcard.php b/program/lib/Roundcube/rcube_csv2vcard.php index 0d3276b..fb8d8f1 100644 --- a/program/lib/Roundcube/rcube_csv2vcard.php +++ b/program/lib/Roundcube/rcube_csv2vcard.php @@ -130,6 +130,21 @@ 'work_state' => 'region:work', 'home_city_short' => 'locality:home', 'home_state_short' => 'region:home', + + // Atmail + 'date_of_birth' => 'birthday', + 'email' => 'email:pref', + 'home_mobile' => 'phone:cell', + 'home_zip' => 'zipcode:home', + 'info' => 'notes', + 'user_photo' => 'photo', + 'url' => 'website:homepage', + 'work_company' => 'organization', + 'work_dept' => 'departament', + 'work_fax' => 'phone:work,fax', + 'work_mobile' => 'phone:work,cell', + 'work_title' => 'jobtitle', + 'work_zip' => 'zipcode:work', ); /** @@ -230,8 +245,29 @@ 'work_phone' => "Work Phone", 'work_address' => "Work Address", //'work_address_2' => "Work Address 2", + 'work_city' => "Work City", 'work_country' => "Work Country", + 'work_state' => "Work State", 'work_zipcode' => "Work ZipCode", + + // Atmail + 'date_of_birth' => "Date of Birth", + 'email' => "Email", + //'email_2' => "Email2", + //'email_3' => "Email3", + //'email_4' => "Email4", + //'email_5' => "Email5", + 'home_mobile' => "Home Mobile", + 'home_zip' => "Home Zip", + 'info' => "Info", + 'user_photo' => "User Photo", + 'url' => "URL", + 'work_company' => "Work Company", + 'work_dept' => "Work Dept", + 'work_fax' => "Work Fax", + 'work_mobile' => "Work Mobile", + 'work_title' => "Work Title", + 'work_zip' => "Work Zip", ); protected $local_label_map = array(); @@ -268,7 +304,6 @@ { // convert to UTF-8 $head = substr($csv, 0, 4096); - $fallback = rcube::get_instance()->config->get('default_charset', 'ISO-8859-1'); // fallback to Latin-1? $charset = rcube_charset::detect($head, RCUBE_CHARSET); $csv = rcube_charset::convert($csv, $charset); $head = ''; @@ -276,7 +311,7 @@ $this->map = array(); // Parse file - foreach (preg_split("/[\r\n]+/", $csv) as $i => $line) { + foreach (preg_split("/[\r\n]+/", $csv) as $line) { $elements = $this->parse_line($line); if (empty($elements)) { continue; @@ -353,6 +388,12 @@ if (!empty($this->local_label_map)) { for ($i = 0; $i < $size; $i++) { $label = $this->local_label_map[$elements[$i]]; + + // special localization label + if ($label && $label[0] == '_') { + $label = substr($label, 1); + } + if ($label && !empty($this->csv2vcard_map[$label])) { $map2[$i] = $this->csv2vcard_map[$label]; } @@ -384,9 +425,13 @@ $contact['birthday'] = $contact['birthday-y'] .'-' .$contact['birthday-m'] . '-' . $contact['birthday-d']; } + // Empty dates, e.g. "0/0/00", "0000-00-00 00:00:00" foreach (array('birthday', 'anniversary') as $key) { - if (!empty($contact[$key]) && $contact[$key] == '0/0/00') { // @TODO: localization? - unset($contact[$key]); + if (!empty($contact[$key])) { + $date = preg_replace('/[0[:^word:]]/', '', $contact[$key]); + if (empty($date)) { + unset($contact[$key]); + } } } diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php index d86e3dd..4b9ab13 100644 --- a/program/lib/Roundcube/rcube_db.php +++ b/program/lib/Roundcube/rcube_db.php @@ -128,7 +128,7 @@ $dsn_string = $this->dsn_string($dsn); $dsn_options = $this->dsn_options($dsn); - if ($db_pconn) { + if ($this->db_pconn) { $dsn_options[PDO::ATTR_PERSISTENT] = true; } @@ -405,21 +405,22 @@ $this->db_error_msg = null; // send query - $query = $this->dbh->query($query); + $result = $this->dbh->query($query); - if ($query === false) { + if ($result === false) { $error = $this->dbh->errorInfo(); $this->db_error = true; $this->db_error_msg = sprintf('[%s] %s', $error[1], $error[2]); rcube::raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, - 'message' => $this->db_error_msg), true, false); + 'message' => $this->db_error_msg . " (SQL Query: $query)" + ), true, false); } - $this->last_result = $query; + $this->last_result = $result; - return $query; + return $result; } /** diff --git a/program/lib/Roundcube/rcube_db_mysql.php b/program/lib/Roundcube/rcube_db_mysql.php index 8ab6403..b2cbab2 100644 --- a/program/lib/Roundcube/rcube_db_mysql.php +++ b/program/lib/Roundcube/rcube_db_mysql.php @@ -127,7 +127,7 @@ $result[PDO::MYSQL_ATTR_FOUND_ROWS] = true; // Enable AUTOCOMMIT mode (#1488902) - $dsn_options[PDO::ATTR_AUTOCOMMIT] = true; + $result[PDO::ATTR_AUTOCOMMIT] = true; return $result; } @@ -147,7 +147,7 @@ $result = $this->query('SHOW VARIABLES'); - while ($sql_arr = $this->fetch_array($result)) { + while ($row = $this->fetch_array($result)) { $this->variables[$row[0]] = $row[1]; } } diff --git a/program/lib/Roundcube/rcube_enriched.php b/program/lib/Roundcube/rcube_enriched.php index 8c628c9..12deb33 100644 --- a/program/lib/Roundcube/rcube_enriched.php +++ b/program/lib/Roundcube/rcube_enriched.php @@ -118,7 +118,7 @@ $quoted = ''; $lines = explode('<br>', $a[2]); - foreach ($lines as $n => $line) + foreach ($lines as $line) $quoted .= '>'.$line.'<br>'; $body = $a[1].'<span class="quotes">'.$quoted.'</span>'.$a[3]; diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index c679851..43c61fd 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -981,7 +981,7 @@ // use memory less expensive (and quick) method for big result set $index = clone $this->index('', $this->sort_field, $this->sort_order); // get messages uids for one page... - $index->slice($start_msg, min($cnt-$from, $this->page_size)); + $index->slice($from, min($cnt-$from, $this->page_size)); if ($slice) { $index->slice(-$slice, $slice); @@ -1423,8 +1423,6 @@ */ protected function search_index($folder, $criteria='ALL', $charset=NULL, $sort_field=NULL) { - $orig_criteria = $criteria; - if (!$this->check_connection()) { if ($this->threading) { return new rcube_result_thread(); @@ -2727,7 +2725,7 @@ // filter folders list according to rights requirements if ($rights && $this->get_capability('ACL')) { - $a_folders = $this->filter_rights($a_folders, $rights); + $a_mboxes = $this->filter_rights($a_mboxes, $rights); } // filter folders and sort them @@ -2783,7 +2781,6 @@ */ private function list_folders_update(&$result, $type = null) { - $delim = $this->get_hierarchy_delimiter(); $namespace = $this->get_namespace(); $search = array(); @@ -3846,7 +3843,7 @@ $delimiter = $this->get_hierarchy_delimiter(); // find default folders and skip folders starting with '.' - foreach ($a_folders as $i => $folder) { + foreach ($a_folders as $folder) { if ($folder[0] == '.') { continue; } diff --git a/program/lib/Roundcube/rcube_imap_cache.php b/program/lib/Roundcube/rcube_imap_cache.php index 748474a..47d9aaf 100644 --- a/program/lib/Roundcube/rcube_imap_cache.php +++ b/program/lib/Roundcube/rcube_imap_cache.php @@ -430,7 +430,7 @@ ." AND uid = ?", $flags, $msg, $this->userid, $mailbox, (int) $message->uid); - if ($this->db->affected_rows()) { + if ($this->db->affected_rows($res)) { return; } } @@ -983,7 +983,7 @@ $uids, true, array('FLAGS'), $index['modseq'], $qresync); if (!empty($result)) { - foreach ($result as $id => $msg) { + foreach ($result as $msg) { $uid = $msg->uid; // Remove deleted message if ($this->skip_deleted && !empty($msg->flags['DELETED'])) { diff --git a/program/lib/Roundcube/rcube_imap_generic.php b/program/lib/Roundcube/rcube_imap_generic.php index db50ffb..6c1b855 100644 --- a/program/lib/Roundcube/rcube_imap_generic.php +++ b/program/lib/Roundcube/rcube_imap_generic.php @@ -746,7 +746,7 @@ } if ($this->prefs['timeout'] <= 0) { - $this->prefs['timeout'] = ini_get('default_socket_timeout'); + $this->prefs['timeout'] = max(0, intval(ini_get('default_socket_timeout'))); } // Connect @@ -1077,7 +1077,7 @@ } if (!$this->data['READ-WRITE']) { - $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'EXPUNGE'); + $this->setError(self::ERROR_READONLY, "Mailbox is read-only"); return false; } @@ -1652,7 +1652,6 @@ } if (!empty($criteria)) { - $modseq = stripos($criteria, 'MODSEQ') !== false; $params .= ($params ? ' ' : '') . $criteria; } else { @@ -1791,7 +1790,6 @@ if ($skip_deleted && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) { $flags = explode(' ', strtoupper($matches[1])); if (in_array('\\DELETED', $flags)) { - $deleted[$id] = $id; continue; } } @@ -1936,7 +1934,7 @@ } if (!$this->data['READ-WRITE']) { - $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE'); + $this->setError(self::ERROR_READONLY, "Mailbox is read-only"); return false; } @@ -1997,7 +1995,7 @@ } if (!$this->data['READ-WRITE']) { - $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE'); + $this->setError(self::ERROR_READONLY, "Mailbox is read-only"); return false; } @@ -2172,7 +2170,7 @@ // create array with header field:data if (!empty($headers)) { $headers = explode("\n", trim($headers)); - foreach ($headers as $hid => $resln) { + foreach ($headers as $resln) { if (ord($resln[0]) <= 32) { $lines[$ln] .= (empty($lines[$ln]) ? '' : "\n") . trim($resln); } else { @@ -2180,7 +2178,7 @@ } } - while (list($lines_key, $str) = each($lines)) { + foreach ($lines as $str) { list($field, $string) = explode(':', $str, 2); $field = strtolower($field); @@ -2508,7 +2506,7 @@ $tokens = $this->tokenizeResponse(preg_replace('/(^\(|\)$)/', '', $line)); for ($i=0; $i<count($tokens); $i+=2) { - if (preg_match('/^(BODY|BINARY)/i', $token)) { + if (preg_match('/^(BODY|BINARY)/i', $tokens[$i])) { $result = $tokens[$i+1]; $found = true; break; @@ -3540,7 +3538,7 @@ if (is_array($element)) { reset($element); - while (list($key, $value) = each($element)) { + foreach ($element as $value) { $string .= ' ' . self::r_implode($value); } } diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php index a2dd163..70163b2 100644 --- a/program/lib/Roundcube/rcube_ldap.php +++ b/program/lib/Roundcube/rcube_ldap.php @@ -169,7 +169,7 @@ // Build sub_fields filter if (!empty($this->prop['sub_fields']) && is_array($this->prop['sub_fields'])) { $this->sub_filter = ''; - foreach ($this->prop['sub_fields'] as $attr => $class) { + foreach ($this->prop['sub_fields'] as $class) { if (!empty($class)) { $class = is_array($class) ? array_pop($class) : $class; $this->sub_filter .= '(objectClass=' . $class . ')'; @@ -1035,7 +1035,6 @@ $mail_field = $this->fieldmap['email']; // try to extract surname and firstname from displayname - $reverse_map = array_flip($this->fieldmap); $name_parts = preg_split('/[\s,.]+/', $save_data['name']); if ($sn_field && $missing[$sn_field]) { @@ -1107,7 +1106,7 @@ // Remove attributes that need to be added separately (child objects) $xfields = array(); if (!empty($this->prop['sub_fields']) && is_array($this->prop['sub_fields'])) { - foreach ($this->prop['sub_fields'] as $xf => $xclass) { + foreach (array_keys($this->prop['sub_fields']) as $xf) { if (!empty($newentry[$xf])) { $xfields[$xf] = $newentry[$xf]; unset($newentry[$xf]); @@ -1170,7 +1169,7 @@ } } - foreach ($this->fieldmap as $col => $fld) { + foreach ($this->fieldmap as $fld) { if ($fld) { $val = $ldap_data[$fld]; $old = $old_data[$fld]; @@ -1396,6 +1395,10 @@ */ protected function add_autovalues(&$attrs) { + if (empty($this->prop['autovalues'])) { + return; + } + $attrvals = array(); foreach ($attrs as $k => $v) { $attrvals['{'.$k.'}'] = is_array($v) ? $v[0] : $v; @@ -1403,13 +1406,24 @@ foreach ((array)$this->prop['autovalues'] as $lf => $templ) { if (empty($attrs[$lf])) { - // replace {attr} placeholders with concrete attribute values - $templ = preg_replace('/\{\w+\}/', '', strtr($templ, $attrvals)); + if (strpos($templ, '(') !== false) { + // replace {attr} placeholders with (escaped!) attribute values to be safely eval'd + $code = preg_replace('/\{\w+\}/', '', strtr($templ, array_map('addslashes', $attrvals))); + $fn = create_function('', "return ($code);"); + if (!$fn) { + rcube::raise_error(array( + 'code' => 505, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Expression parse error on: ($code)"), true, false); + continue; + } - if (strpos($templ, '(') !== false) - $attrs[$lf] = eval("return ($templ);"); - else - $attrs[$lf] = $templ; + $attrs[$lf] = $fn(); + } + else { + // replace {attr} placeholders with concrete attribute values + $attrs[$lf] = preg_replace('/\{\w+\}/', '', strtr($templ, $attrvals)); + } } } } @@ -1715,9 +1729,14 @@ * List all active contact groups of this source * * @param string Optional search string to match group name + * @param int Matching mode: + * 0 - partial (*abc*), + * 1 - strict (=), + * 2 - prefix (abc*) + * * @return array Indexed list of contact groups, each a hash array */ - function list_groups($search = null) + function list_groups($search = null, $mode = 0) { if (!$this->groups) return array(); @@ -1729,10 +1748,10 @@ $groups = array(); if ($search) { - $search = mb_strtolower($search); foreach ($group_cache as $group) { - if (strpos(mb_strtolower($group['name']), $search) !== false) + if ($this->compare_search_value('name', $group['name'], $search, $mode)) { $groups[] = $group; + } } } else @@ -1763,7 +1782,7 @@ $vlv_active = $this->_vlv_set_controls($this->prop['groups'], $vlv_page+1, $page_size); } - $function = $this->_scope2func($this->prop['groups']['scope'], $ns_function); + $function = $this->_scope2func($this->prop['groups']['scope']); $res = @$function($this->conn, $base_dn, $filter, array_unique(array('dn', 'objectClass', $name_attr, $email_attr, $sort_attr))); if ($res === false) { @@ -1921,9 +1940,10 @@ /** * Add the given contact records the a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be added - * @return int Number of contacts added + * @param string Group identifier + * @param array|string List of contact identifiers to be added + * + * @return int Number of contacts added */ function add_to_group($group_id, $contact_ids) { @@ -1937,8 +1957,8 @@ $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(); - $new_attrs = array(); foreach ($contact_ids as $id) $new_attrs[$member_attr][] = self::dn_decode($id); @@ -1949,28 +1969,32 @@ $this->cache->remove('groups'); - return count($new_attrs['member']); + return count($new_attrs[$member_attr]); } /** * Remove the given contact records from a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be removed - * @return int Number of deleted group members + * @param string Group identifier + * @param array|string List of contact identifiers to be removed + * + * @return int Number of deleted group members */ function remove_from_group($group_id, $contact_ids) { 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 = $group_cache[$group_id]['name']; $member_attr = $group_cache[$group_id]['member_attr']; $group_dn = "cn=$group_name,$base_dn"; + $del_attrs = array(); - $del_attrs = array(); - foreach (explode(",", $contact_ids) as $id) + foreach ($contact_ids as $id) $del_attrs[$member_attr][] = self::dn_decode($id); if (!$this->ldap_mod_del($group_dn, $del_attrs)) { @@ -1980,7 +2004,7 @@ $this->cache->remove('groups'); - return count($del_attrs['member']); + return count($del_attrs[$member_attr]); } /** diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php index 9db1fa3..a42d3fb 100644 --- a/program/lib/Roundcube/rcube_message.php +++ b/program/lib/Roundcube/rcube_message.php @@ -362,7 +362,7 @@ // parse headers from message/rfc822 part if (!isset($structure->headers['subject']) && !isset($structure->headers['from'])) { - list($headers, $dump) = explode("\r\n\r\n", $this->get_part_content($structure->mime_id, null, true, 32768)); + list($headers, ) = explode("\r\n\r\n", $this->get_part_content($structure->mime_id, null, true, 32768)); $structure->headers = rcube_mime::parse_headers($headers); } } diff --git a/program/lib/Roundcube/rcube_mime.php b/program/lib/Roundcube/rcube_mime.php index 96296a5..63549fb 100644 --- a/program/lib/Roundcube/rcube_mime.php +++ b/program/lib/Roundcube/rcube_mime.php @@ -127,10 +127,11 @@ * @param int $max List only this number of addresses * @param boolean $decode Decode address strings * @param string $fallback Fallback charset if none specified + * @param boolean $addronly Return flat array with e-mail addresses only * - * @return array Indexed list of addresses + * @return array Indexed list of addresses */ - static function decode_address_list($input, $max = null, $decode = true, $fallback = null) + static function decode_address_list($input, $max = null, $decode = true, $fallback = null, $addronly = false) { $a = self::parse_address_list($input, $decode, $fallback); $out = array(); @@ -145,20 +146,21 @@ foreach ($a as $val) { $j++; $address = trim($val['address']); - $name = trim($val['name']); - if ($name && $address && $name != $address) - $string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address); - else if ($address) - $string = $address; - else if ($name) - $string = $name; + if ($addronly) { + $out[$j] = $address; + } + else { + $name = trim($val['name']); + if ($name && $address && $name != $address) + $string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address); + else if ($address) + $string = $address; + else if ($name) + $string = $name; - $out[$j] = array( - 'name' => $name, - 'mailto' => $address, - 'string' => $string - ); + $out[$j] = array('name' => $name, 'mailto' => $address, 'string' => $string); + } if ($max && $j==$max) break; @@ -476,9 +478,10 @@ $q_level = 0; foreach ($text as $idx => $line) { - if ($line[0] == '>') { - // remove quote chars, store level in $q - $line = preg_replace('/^>+/', '', $line, -1, $q); + if (preg_match('/^(>+)/', $line, $m)) { + // remove quote chars + $q = strlen($m[1]); + $line = preg_replace('/^>+/', '', $line); // remove (optional) space-staffing $line = preg_replace('/^ /', '', $line); @@ -541,9 +544,10 @@ foreach ($text as $idx => $line) { if ($line != '-- ') { - if ($line[0] == '>') { - // remove quote chars, store level in $level - $line = preg_replace('/^>+/', '', $line, -1, $level); + if (preg_match('/^(>+)/', $line, $m)) { + // remove quote chars + $level = strlen($m[1]); + $line = preg_replace('/^>+/', '', $line); // remove (optional) space-staffing and spaces before the line end $line = preg_replace('/(^ | +$)/', '', $line); $prefix = str_repeat('>', $level) . ' '; @@ -657,8 +661,8 @@ $cutLength = $spacePos + 1; } else { - $subString = $string; - $cutLength = null; + $subString = $substr_func($string, 0, $breakPos, $charset); + $cutLength = $breakPos + 1; } } else { diff --git a/program/lib/Roundcube/rcube_output.php b/program/lib/Roundcube/rcube_output.php index b8ae86c..7ccf9a0 100644 --- a/program/lib/Roundcube/rcube_output.php +++ b/program/lib/Roundcube/rcube_output.php @@ -162,7 +162,7 @@ header("Cache-Control: private, must-revalidate"); } else { - header("Cache-Control: private, no-cache, must-revalidate, post-check=0, pre-check=0"); + header("Cache-Control: private, no-cache, no-store, must-revalidate, post-check=0, pre-check=0"); header("Pragma: no-cache"); } } diff --git a/program/lib/Roundcube/rcube_plugin_api.php b/program/lib/Roundcube/rcube_plugin_api.php index 4bb6c66..33f04ea 100644 --- a/program/lib/Roundcube/rcube_plugin_api.php +++ b/program/lib/Roundcube/rcube_plugin_api.php @@ -313,7 +313,6 @@ $doc->loadXML($file); $xpath = new DOMXPath($doc); $xpath->registerNamespace('rc', "http://pear.php.net/dtd/package-2.0"); - $data = array(); // XPaths of plugin metadata elements $metadata = array( diff --git a/program/lib/Roundcube/rcube_smtp.php b/program/lib/Roundcube/rcube_smtp.php index 5c7d220..0f3ac04 100644 --- a/program/lib/Roundcube/rcube_smtp.php +++ b/program/lib/Roundcube/rcube_smtp.php @@ -119,7 +119,7 @@ } // try to connect to server and exit on failure - $result = $this->conn->connect($smtp_timeout); + $result = $this->conn->connect($CONFIG['smtp_timeout']); if (PEAR::isError($result)) { $this->response[] = "Connection failed: ".$result->getMessage(); @@ -433,9 +433,9 @@ $recipients = rcube_utils::explode_quoted_string(',', $recipients); reset($recipients); - while (list($k, $recipient) = each($recipients)) { + foreach ($recipients as $recipient) { $a = rcube_utils::explode_quoted_string(' ', $recipient); - while (list($k2, $word) = each($a)) { + foreach ($a as $word) { if (strpos($word, "@") > 0 && $word[strlen($word)-1] != '"') { $word = preg_replace('/^<|>$/', '', trim($word)); if (in_array($word, $addresses) === false) { diff --git a/program/lib/Roundcube/rcube_spellchecker.php b/program/lib/Roundcube/rcube_spellchecker.php index 816bcad..60aec50 100644 --- a/program/lib/Roundcube/rcube_spellchecker.php +++ b/program/lib/Roundcube/rcube_spellchecker.php @@ -314,11 +314,6 @@ if (!$this->plink) { if (!extension_loaded('pspell')) { $this->error = "Pspell extension not available"; - rcube::raise_error(array( - 'code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => $this->error), true, false); - return; } @@ -372,8 +367,18 @@ fclose($fp); } + // parse HTTP response + if (preg_match('!^HTTP/1.\d (\d+)(.+)!', $store, $m)) { + $http_status = $m[1]; + if ($http_status != '200') + $this->error = 'HTTP ' . $m[1] . $m[2]; + } + if (!$store) { $this->error = "Empty result from spelling engine"; + } + else if (preg_match('/<spellresult error="([^"]+)"/', $store, $m)) { + $this->error = "Error code $m[1] returned"; } preg_match_all('/<c o="([^"]*)" l="([^"]*)" s="([^"]*)">([^<]*)<\/c>/', $store, $matches, PREG_SET_ORDER); @@ -588,7 +593,7 @@ if (empty($plugin['abort'])) { $dict = array(); - $this->rc->db->query( + $sql_result = $this->rc->db->query( "SELECT data FROM ".$this->rc->db->table_name('dictionary') ." WHERE user_id ". ($plugin['userid'] ? "= ".$this->rc->db->quote($plugin['userid']) : "IS NULL") ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?", diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php index fabe0f0..8467107 100644 --- a/program/lib/Roundcube/rcube_utils.php +++ b/program/lib/Roundcube/rcube_utils.php @@ -404,7 +404,7 @@ $out = array(); $src = $mode == self::INPUT_GET ? $_GET : ($mode == self::INPUT_POST ? $_POST : $_REQUEST); - foreach ($src as $key => $value) { + foreach (array_keys($src) as $key) { $fname = $key[0] == '_' ? substr($key, 1) : $key; if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname)) { $out[$fname] = self::get_input_value($key, $mode); diff --git a/program/lib/Roundcube/rcube_vcard.php b/program/lib/Roundcube/rcube_vcard.php index 54bb952..90acb21 100644 --- a/program/lib/Roundcube/rcube_vcard.php +++ b/program/lib/Roundcube/rcube_vcard.php @@ -90,7 +90,7 @@ */ public function __construct($vcard = null, $charset = RCUBE_CHARSET, $detect = false, $fieldmap = array()) { - if (!empty($fielmap)) { + if (!empty($fieldmap)) { $this->extend_fieldmap($fieldmap); } @@ -481,7 +481,7 @@ $vcard_block = ''; $in_vcard_block = false; - foreach (preg_split("/[\r\n]+/", $data) as $i => $line) { + foreach (preg_split("/[\r\n]+/", $data) as $line) { if ($in_vcard_block && !empty($line)) { $vcard_block .= $line . "\n"; } @@ -784,9 +784,30 @@ } return $result; } + + $s = strtr($s, $rep2); } - return strtr($s, array("\r" => '', '\\\\' => '\\', '\n' => "\n", '\N' => "\n", '\,' => ',', '\;' => ';')); + // some implementations (GMail) use non-standard backslash before colon (#1489085) + // we will handle properly any backslashed character - removing dummy backslahes + // return strtr($s, array("\r" => '', '\\\\' => '\\', '\n' => "\n", '\N' => "\n", '\,' => ',', '\;' => ';')); + + $s = str_replace("\r", '', $s); + $pos = 0; + + while (($pos = strpos($s, '\\', $pos)) !== false) { + $next = substr($s, $pos + 1, 1); + if ($next == 'n' || $next == 'N') { + $s = substr_replace($s, "\n", $pos, 2); + } + else { + $s = substr_replace($s, '', $pos, 1); + } + + $pos += 1; + } + + return $s; } /** diff --git a/program/lib/utf8.class.php b/program/lib/utf8.class.php index e0dc9e2..0446159 100644 --- a/program/lib/utf8.class.php +++ b/program/lib/utf8.class.php @@ -60,8 +60,8 @@ function loadCharset($charset) { $charset = preg_replace(array('/^WINDOWS-*125([0-8])$/', '/^CP-/'), array('CP125\\1', 'CP'), $charset); - if (isset($aliases[$charset])) - $charset = $aliases[$charset]; + if (isset($this->aliases[$charset])) + $charset = $this->aliases[$charset]; $this->charset = $charset; diff --git a/program/localization/en_GB/labels.inc b/program/localization/en_GB/labels.inc index 8d5509b..581a717 100644 --- a/program/localization/en_GB/labels.inc +++ b/program/localization/en_GB/labels.inc @@ -397,6 +397,7 @@ $labels['signature'] = 'Signature'; $labels['dstactive'] = 'Summer time'; $labels['showinextwin'] = 'Open message in a new window'; +$labels['showemail'] = 'Show email address with display name'; $labels['composeextwin'] = 'Compose in a new window'; $labels['htmleditor'] = 'Compose HTML messages'; $labels['htmlonreply'] = 'on reply to HTML message only'; diff --git a/program/localization/en_US/csv2vcard.inc b/program/localization/en_US/csv2vcard.inc index 5412f7e..e7b8679 100644 --- a/program/localization/en_US/csv2vcard.inc +++ b/program/localization/en_US/csv2vcard.inc @@ -91,3 +91,20 @@ $map['work_address'] = "Work Address"; $map['work_country'] = "Work Country"; $map['work_zipcode'] = "Work ZipCode"; + +// Atmail +$map['date_of_birth'] = "Date of Birth"; +$map['email'] = "Email"; +$map['home_mobile'] = "Home Mobile"; +$map['home_zip'] = "Home Zip"; +$map['info'] = "Info"; +$map['user_photo'] = "User Photo"; +$map['url'] = "URL"; +$map['work_city'] = "Work City"; +$map['work_company'] = "Work Company"; +$map['work_dept'] = "Work Dept"; +$map['work_fax'] = "Work Fax"; +$map['work_mobile'] = "Work Mobile"; +$map['work_state'] = "Work State"; +$map['work_title'] = "Work Title"; +$map['work_zip'] = "Work Zip"; diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index 3e1bde0..ab57007 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -402,6 +402,7 @@ $labels['htmlonreply'] = 'on reply to HTML message'; $labels['htmlonreplyandforward'] = 'on forward or reply to HTML message'; $labels['htmlsignature'] = 'HTML signature'; +$labels['showemail'] = 'Show email address with display name'; $labels['previewpane'] = 'Show preview pane'; $labels['skin'] = 'Interface skin'; $labels['logoutclear'] = 'Clear Trash on logout'; diff --git a/program/localization/fr_FR/csv2vcard.inc b/program/localization/fr_FR/csv2vcard.inc new file mode 100644 index 0000000..bb77001 --- /dev/null +++ b/program/localization/fr_FR/csv2vcard.inc @@ -0,0 +1,96 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | localization/<lang>/csv2vcard.inc | + | | + | Localization file of the Roundcube Webmail client | + | Copyright (C) 2005-2013, The Roundcube Dev Team | + | | + | Licensed under the GNU General Public License version 3 or | + | any later version with exceptions for skins & plugins. | + | See the README file for a full license statement. | + | | + +-----------------------------------------------------------------------+ + | Author: Aleksander Machniak <alec@alec.pl> | + +-----------------------------------------------------------------------+ +*/ + +// This is a list of CSV column names specified in CSV file header +// These must be original texts used in Outlook/Thunderbird exported csv files +// Encoding UTF-8 + +$map = array(); + +// MS Outlook 2010 +$map['anniversary'] = "Anniversaire de mariage ou fête"; +$map['assistants_name'] = "Nom de l''assistant(e)"; +$map['assistants_phone'] = "Téléphone de l''assistant(e)"; +$map['birthday'] = "Anniversaire"; +$map['business_city'] = "Ville (bureau)"; +$map['business_countryregion'] = "Pays/Région (bureau)"; +$map['business_fax'] = "Télécopie (bureau)"; +$map['business_phone'] = "Téléphone (bureau)"; +$map['business_phone_2'] = "Téléphone 2 (bureau)"; +$map['business_postal_code'] = "Code postal (bureau)"; +$map['business_state'] = "Dép/Région (bureau)"; +$map['business_street'] = "Rue (bureau)"; +$map['car_phone'] = "Téléphone (voiture)"; +$map['categories'] = "Catégories"; +$map['company'] = "Société"; +$map['department'] = "Service"; +$map['email_address'] = "Adresse de messagerie"; +$map['first_name'] = "Prénom"; +$map['gender'] = "Sexe"; +$map['home_city'] = "Ville (domicile)"; +$map['home_countryregion'] = "Pays/Région (domicile)"; +$map['home_fax'] = "Télécopie (domicile)"; +$map['home_phone'] = "Téléphone (domicile)"; +$map['home_phone_2'] = "Téléphone 2 (domicile)"; +$map['home_postal_code'] = "Code postal (domicile)"; +$map['home_state'] = "Dép/Région (domicile)"; +$map['home_street'] = "Rue (domicile)"; +$map['job_title'] = "Profession"; +$map['last_name'] = "Nom"; +$map['managers_name'] = "Manager's Name"; +$map['middle_name'] = "Deuxième prénom"; +$map['mobile_phone'] = "Tél. mobile"; +$map['notes'] = "Notes"; +$map['other_city'] = "Ville (autre)"; +$map['other_countryregion'] = "Pays/Région (autre)"; +$map['other_fax'] = "Télécopie (autre)"; +$map['other_phone'] = "Téléphone (autre)"; +$map['other_postal_code'] = "Code postal (autre)"; +$map['other_state'] = "Dép/Région (autre)"; +$map['other_street'] = "Rue (autre)"; +$map['pager'] = "Récepteur de radiomessagerie"; +$map['primary_phone'] = "Téléphone principal"; +$map['spouse'] = "Conjoint(e)"; +$map['suffix'] = "Suffixe"; +$map['title'] = "Titre"; +$map['web_page'] = "Page Web"; + +// Thunderbird +$map['birth_day'] = "Jour"; +$map['birth_month'] = "Mois"; +$map['birth_year'] = "Année de naissance"; +$map['display_name'] = "Nom à afficher"; +$map['fax_number'] = "Fax"; +$map['home_address'] = "Adresse privée"; +$map['home_country'] = "Région"; +$map['home_zipcode'] = "Code postal"; +$map['mobile_number'] = "Portable"; +$map['nickname'] = "Surnom"; +$map['organization'] = "Société"; +$map['pager_number'] = "Pager"; +$map['primary_email'] = "Adresse électronique principale"; +$map['secondary_email'] = "Adresse électronique secondaire"; +$map['web_page_1'] = "Site Web 1"; +$map['web_page_2'] = "Site Web 2"; +$map['work_phone'] = "Tél. professionnel"; +$map['work_address'] = "Adresse professionnelle"; +$map['work_country'] = "Région"; +$map['work_zipcode'] = "Code postal"; + +// Other +$map['_home_city'] = "Ville"; diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index ffc0b3b..8faf76f 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -167,7 +167,7 @@ // get address book name (for display) if ($abook && $_SESSION['addressbooks_count'] > 1) { $name = $abook->get_name(); - if (!$name && $source == 0) { + if (!$name) { $name = rcube_label('personaladrbook'); } $OUTPUT->set_env('sourcename', html_entity_decode($name, ENT_COMPAT, 'UTF-8')); @@ -183,7 +183,6 @@ $attrib['id'] = 'rcmdirectorylist'; $out = ''; - $local_id = '0'; $jsdata = array(); $line_templ = html::tag('li', array( @@ -270,7 +269,6 @@ $groups_html = ''; $groups = $RCMAIL->get_address_book($args['source'])->list_groups(); - $js_id = $RCMAIL->JQ($args['source']); if (!empty($groups)) { $line_templ = html::tag('li', array( @@ -283,7 +281,6 @@ $is_collapsed = strpos($RCMAIL->config->get('collapsed_abooks',''), '&'.rawurlencode($args['source']).'&') !== false; $args['out'] .= html::div('treetoggle ' . ($is_collapsed ? 'collapsed' : 'expanded'), ' '); - $jsdata = array(); foreach ($groups as $group) { $groups_html .= sprintf($line_templ, rcube_utils::html_identifier('G' . $args['source'] . $group['ID'], true), @@ -297,7 +294,7 @@ } $args['out'] .= html::tag('ul', - array('class' => 'groups', 'style' => ($is_collapsed ? "display:none;" : null)), + array('class' => 'groups', 'style' => ($is_collapsed || empty($groups) ? "display:none;" : null)), $groups_html); return $args; diff --git a/program/steps/addressbook/import.inc b/program/steps/addressbook/import.inc index 72da150..915aac8 100644 --- a/program/steps/addressbook/import.inc +++ b/program/steps/addressbook/import.inc @@ -88,7 +88,7 @@ $content = html::p(null, rcube_label(array( 'name' => 'importconfirm', - 'nr' => $IMORT_STATS->inserted, + 'nr' => $IMPORT_STATS->inserted, 'vars' => $vars, )) . ($IMPORT_STATS->names ? ':' : '.')); @@ -98,7 +98,7 @@ if ($IMPORT_STATS->skipped) { $content .= html::p(null, rcube_label(array( 'name' => 'importconfirmskipped', - 'nr' => $IMORT_STATS->skipped, + 'nr' => $IMPORT_STATS->skipped, 'vars' => $vars, )) . ':'); $content .= html::p('em', join(', ', array_map('Q', $IMPORT_STATS->skipped_names))); diff --git a/program/steps/addressbook/show.inc b/program/steps/addressbook/show.inc index 16be89f..1a97c65 100644 --- a/program/steps/addressbook/show.inc +++ b/program/steps/addressbook/show.inc @@ -101,8 +101,6 @@ return false; } - $microformats = array('name' => 'fn', 'email' => 'email'); - $form = array( 'head' => array( // section 'head' is magic! 'content' => array( @@ -177,7 +175,7 @@ } -function rcmail_render_email_value($email, $col) +function rcmail_render_email_value($email) { return html::a(array( 'href' => 'mailto:' . $email, @@ -188,7 +186,7 @@ } -function rcmail_render_url_value($url, $col) +function rcmail_render_url_value($url) { $prefix = preg_match('!^(http|ftp)s?://!', $url) ? '' : 'http://'; return html::a(array( @@ -223,7 +221,7 @@ } $hiddenfields = new html_hiddenfield(array('name' => '_source', 'value' => get_input_value('_source', RCUBE_INPUT_GPC))); - $hiddenfields->add(array('name' => '_cid', 'value' => $record['ID'])); + $hiddenfields->add(array('name' => '_cid', 'value' => $contact_id)); $form_start = $RCMAIL->output->request_form(array( 'name' => "form", 'method' => "post", diff --git a/program/steps/mail/autocomplete.inc b/program/steps/mail/autocomplete.inc index 5557981..f9e8d71 100644 --- a/program/steps/mail/autocomplete.inc +++ b/program/steps/mail/autocomplete.inc @@ -102,7 +102,7 @@ // also list matching contact groups if ($abook->groups && count($contacts) < $MAXNUM) { - foreach ($abook->list_groups($search) as $group) { + foreach ($abook->list_groups($search, $mode) as $group) { $abook->reset(); $abook->set_group($group['ID']); $group_prop = $abook->get_group($group['ID']); diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 81b5983..c3fc715 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -327,6 +327,19 @@ $fvalue .= $v; if ($v = $MESSAGE->headers->cc) $fvalue .= (!empty($fvalue) ? $separator : '') . $v; + if ($v = $MESSAGE->headers->get('Sender', false)) + $fvalue .= (!empty($fvalue) ? $separator : '') . $v; + + // When To: and Reply-To: are the same we add From: address to the list (#1489037) + if ($v = $MESSAGE->headers->from) { + $from = rcube_mime::decode_address_list($v, null, false, $MESSAGE->headers->charset, true); + $to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, false, $MESSAGE->headers->charset, true); + $replyto = rcube_mime::decode_address_list($MESSAGE->headers->replyto, null, false, $MESSAGE->headers->charset, true); + + if (count($replyto) && !count(array_diff($to, $replyto)) && count(array_diff($from, $to))) { + $fvalue .= (!empty($fvalue) ? $separator : '') . $v; + } + } } } else if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) { @@ -386,7 +399,7 @@ { global $MESSAGE; - list($form_start, $form_end) = get_form_tags($attrib); + list($form_start,) = get_form_tags($attrib); $out = ''; $part = strtolower($attrib['part']); @@ -450,7 +463,7 @@ function rcmail_compose_header_from($attrib) { - global $MESSAGE, $OUTPUT, $RCMAIL, $compose_mode; + global $MESSAGE, $OUTPUT, $RCMAIL, $COMPOSE, $compose_mode; // pass the following attributes to the form class $field_attrib = array('name' => '_from'); @@ -553,7 +566,7 @@ function rcmail_prepare_message_body() { - global $RCMAIL, $MESSAGE, $COMPOSE, $compose_mode, $LINE_LENGTH, $HTML_MODE; + global $RCMAIL, $MESSAGE, $COMPOSE, $compose_mode, $HTML_MODE; // use posted message body if (!empty($_POST['_message'])) { @@ -626,7 +639,7 @@ function rcmail_compose_part_body($part, $isHtml = false) { - global $RCMAIL, $MESSAGE, $compose_mode; + global $RCMAIL, $MESSAGE, $LINE_LENGTH, $compose_mode; // Check if we have enough memory to handle the message in it // #1487424: we need up to 10x more memory than the body @@ -702,7 +715,7 @@ function rcmail_compose_body($attrib) { - global $RCMAIL, $CONFIG, $OUTPUT, $MESSAGE, $compose_mode, $LINE_LENGTH, $HTML_MODE, $MESSAGE_BODY; + global $RCMAIL, $CONFIG, $OUTPUT, $MESSAGE, $compose_mode, $HTML_MODE, $MESSAGE_BODY; list($form_start, $form_end) = get_form_tags($attrib); unset($attrib['form']); @@ -886,8 +899,7 @@ 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(); + $date = format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long')); if (!$bodyIsHtml) { $prefix = "\n\n\n-------- " . rcube_label('originalmessage') . " --------\n"; @@ -941,7 +953,7 @@ function rcmail_create_draft_body($body, $bodyIsHtml) { - global $MESSAGE, $OUTPUT, $COMPOSE; + global $MESSAGE, $COMPOSE; /** * add attachments @@ -989,7 +1001,7 @@ global $RCMAIL, $COMPOSE, $compose_mode; $loaded_attachments = array(); - foreach ((array)$COMPOSE['attachments'] as $id => $attachment) { + foreach ((array)$COMPOSE['attachments'] as $attachment) { $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment; } @@ -1076,7 +1088,7 @@ $names = array(); $loaded_attachments = array(); - foreach ((array)$COMPOSE['attachments'] as $id => $attachment) { + foreach ((array)$COMPOSE['attachments'] as $attachment) { $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment; } @@ -1211,10 +1223,11 @@ // handle attachments in memory $data = file_get_contents($path); + $name = rcmail_basename($path); $attachment = array( 'group' => $COMPOSE['id'], - 'name' => rcmail_basename($path), + 'name' => $name, 'mimetype' => $mimetype ? $mimetype : rc_mime_content_type($path, $name), 'data' => $data, 'size' => strlen($data), @@ -1484,7 +1497,7 @@ $select->add(Q(rcube_label('plaintoggle')), 'plain'); return $select->show($useHtml ? 'html' : 'plain'); - +/* foreach ($choices as $value => $text) { $attrib['id'] = '_' . $value; $attrib['value'] = $value; @@ -1492,6 +1505,7 @@ } return $selector; +*/ } diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 60db3f3..17ab6f9 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -224,7 +224,7 @@ if (!in_array('threads', $a_show_cols)) array_unshift($a_show_cols, 'threads'); - $skin_path = $_SESSION['skin_path'] = $CONFIG['skin_path']; + $_SESSION['skin_path'] = $CONFIG['skin_path']; // set client env $OUTPUT->add_gui_object('messagelist', $attrib['id']); @@ -236,15 +236,13 @@ $OUTPUT->include_script('list.js'); - $thead = ''; - foreach (rcmail_message_list_head($attrib, $a_show_cols) as $cell) - $thead .= html::tag('td', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']); + $table = new html_table($attrib); + if (!$attrib['noheader']) { + foreach (rcmail_message_list_head($attrib, $a_show_cols) as $cell) + $table->add_header(array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']); + } - return html::tag('table', - $attrib, - html::tag('thead', null, html::tag('tr', null, $thead)) . - html::tag('tbody', null, ''), - array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary')); + return $table->show(); } @@ -291,7 +289,7 @@ $thead = $head_replace ? rcmail_message_list_head($_SESSION['list_attrib'], $a_show_cols) : NULL; // get name of smart From/To column in folder context - if (($f = array_search('fromto', $a_show_cols)) !== false) { + if (array_search('fromto', $a_show_cols) !== false) { $smart_col = rcmail_message_list_smart_column_name(); } @@ -307,7 +305,7 @@ } // loop through message headers - foreach ($a_headers as $n => $header) { + foreach ($a_headers as $header) { if (empty($header)) continue; @@ -381,7 +379,6 @@ global $RCMAIL; $skin_path = $_SESSION['skin_path']; - $image_tag = html::img(array('src' => "%s%s", 'alt' => "%s")); // check to see if we have some settings for sorting $sort_col = $_SESSION['sort_col']; @@ -417,7 +414,7 @@ $cells = array(); // get name of smart From/To column in folder context - if (($f = array_search('fromto', $a_show_cols)) !== false) { + if (array_search('fromto', $a_show_cols) !== false) { $smart_col = rcmail_message_list_smart_column_name(); } @@ -897,7 +894,7 @@ */ function rcmail_message_headers($attrib, $headers=null) { - global $OUTPUT, $MESSAGE, $PRINT_MODE, $RCMAIL; + global $MESSAGE, $PRINT_MODE, $RCMAIL; static $sa_attrib; // keep header table attrib @@ -1082,7 +1079,7 @@ $header_attrib[$regs[1]] = $value; if (!empty($MESSAGE->parts)) { - foreach ($MESSAGE->parts as $i => $part) { + foreach ($MESSAGE->parts as $part) { if ($part->type == 'headers') { $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers)); } @@ -1440,7 +1437,8 @@ $c = count($a_parts); $j = 0; $out = ''; - $allvalues = array(); + $allvalues = array(); + $show_email = $RCMAIL->config->get('message_show_email'); if ($addicon && !isset($_SESSION['writeable_abook'])) { $_SESSION['writeable_abook'] = $RCMAIL->get_address_sources(true) ? true : false; @@ -1453,7 +1451,7 @@ $string = $part['string']; // phishing email prevention (#1488981), e.g. "valid@email.addr <phishing@email.addr>" - if ($name && $name != $mailto && strpos($name, '@')) { + if (!$show_email && $name && $name != $mailto && strpos($name, '@')) { $name = ''; } @@ -1471,13 +1469,21 @@ } else if (check_email($part['mailto'], false)) { if ($linked) { - $address = html::a(array( - 'href' => 'mailto:'.$mailto, - 'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($mailto)), - 'title' => $mailto, - 'class' => "rcmContactAddress", - ), - Q($name ? $name : $mailto)); + $attrs = array( + 'href' => 'mailto:' . $mailto, + 'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($mailto)), + 'class' => "rcmContactAddress", + ); + + if ($show_email && $name && $mailto) { + $content = Q($name ? sprintf('%s <%s>', $name, $mailto) : $mailto); + } + else { + $content = Q($name ? $name : $mailto); + $attrs['title'] = $mailto; + } + + $address = html::a($attrs, $content); } else { $address = html::span(array('title' => $mailto, 'class' => "rcmContactAddress"), @@ -1730,8 +1736,7 @@ $sent = rcmail_deliver_message($compose, $identity['email'], $mailto, $smtp_error, $body_file, $options); - if ($sent) - { + if ($sent) { $RCMAIL->storage->set_flag($message->uid, 'MDNSENT'); return true; } diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc index 1947c0f..2ad1ba9 100644 --- a/program/steps/mail/show.inc +++ b/program/steps/mail/show.inc @@ -228,11 +228,11 @@ function rcmail_message_buttons() { - global $MESSAGE, $RCMAIL, $CONFIG; + global $RCMAIL; $mbox = $RCMAIL->storage->get_folder(); $delim = $RCMAIL->storage->get_hierarchy_delimiter(); - $dbox = $CONFIG['drafts_mbox']; + $dbox = $RCMAIL->config->get('drafts_mbox'); // the message is not a draft if ($mbox != $dbox && strpos($mbox, $dbox.$delim) !== 0) { diff --git a/program/steps/settings/edit_prefs.inc b/program/steps/settings/edit_prefs.inc index 971ed60..468e499 100644 --- a/program/steps/settings/edit_prefs.inc +++ b/program/steps/settings/edit_prefs.inc @@ -40,7 +40,7 @@ $out = $form_start; - foreach ($SECTIONS[$CURR_SECTION]['blocks'] as $idx => $block) { + foreach ($SECTIONS[$CURR_SECTION]['blocks'] as $block) { if (!empty($block['options'])) { $table = new html_table(array('cols' => 2)); diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc index 0c7d906..34a72dd 100644 --- a/program/steps/settings/folders.inc +++ b/program/steps/settings/folders.inc @@ -283,7 +283,6 @@ $noselect = false; $classes = array($i%2 ? 'even' : 'odd'); - $folder_js = Q($folder['id']); $folder_utf8 = rcube_charset_convert($folder['id'], 'UTF7-IMAP'); $display_folder = str_repeat(' ', $folder['level']) . Q($protected ? rcmail_localize_foldername($folder['id']) : $folder['name']); @@ -394,7 +393,7 @@ $a_threaded = (array) $RCMAIL->config->get('message_threading', array()); $oldprefix = '/^' . preg_quote($oldname . $delimiter, '/') . '/'; - foreach ($a_threaded as $key => $val) { + foreach (array_keys($a_threaded) as $key) { if ($key == $oldname) { unset($a_threaded[$key]); $a_threaded[$newname] = true; diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index 319c58d..860f36c 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -418,6 +418,17 @@ ); } + // show checkbox to show email instead of name + if (!isset($no_override['message_show_email'])) { + $field_id = 'rcmfd_message_show_email'; + $input_msgshowemail = new html_checkbox(array('name' => '_message_show_email', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['message_show_email'] = array( + 'title' => html::label($field_id, Q(rcube_label('showemail'))), + 'content' => $input_msgshowemail->show($config['message_show_email']?1:0), + ); + } + // show checkbox for HTML/plaintext messages if (!isset($no_override['prefer_html'])) { $field_id = 'rcmfd_htmlmsg'; diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc index dfb2b13..3bb82aa 100644 --- a/program/steps/settings/save_prefs.inc +++ b/program/steps/settings/save_prefs.inc @@ -60,6 +60,7 @@ case 'mailview': $a_user_prefs = array( 'message_extwin' => intval($_POST['_message_extwin']), + 'message_show_email' => isset($_POST['_message_show_email']) ? TRUE : FALSE, 'prefer_html' => isset($_POST['_prefer_html']) ? TRUE : FALSE, 'inline_images' => isset($_POST['_inline_images']) ? TRUE : FALSE, 'show_images' => isset($_POST['_show_images']) ? intval($_POST['_show_images']) : 0, diff --git a/program/steps/utils/spell.inc b/program/steps/utils/spell.inc index a0dd35d..38e4ca2 100644 --- a/program/steps/utils/spell.inc +++ b/program/steps/utils/spell.inc @@ -42,6 +42,13 @@ $result = $spellchecker->get_xml(); } +if ($err = $spellchecker->error()) { + rcube::raise_error(array('code' => 500, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => sprintf("Spell check engine error: " . $err)), + true, false); +} + // set response length header("Content-Length: " . strlen($result)); diff --git a/program/steps/utils/spell_html.inc b/program/steps/utils/spell_html.inc index 861e4ba..96b41e2 100644 --- a/program/steps/utils/spell_html.inc +++ b/program/steps/utils/spell_html.inc @@ -46,6 +46,11 @@ } if ($error = $spellchecker->error()) { + rcube::raise_error(array('code' => 500, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => sprintf("Spell check engine error: " . $error)), + true, false); + echo '{"error":{"errstr":"' . addslashes($error) . '","errfile":"","errline":null,"errcontext":"","level":"FATAL"}}'; exit; } diff --git a/tests/Framework/Mime.php b/tests/Framework/Mime.php index 61123dd..3035ba0 100644 --- a/tests/Framework/Mime.php +++ b/tests/Framework/Mime.php @@ -185,6 +185,10 @@ array("----------------------------------------------------------------------------------------\nabc def123456789012345", 76), "----------------------------------------------------------------------------------------\nabc def123456789012345", ), + array( + array("-------\nabc def", 5), + "-------\nabc\ndef", + ), ); foreach ($samples as $sample) { diff --git a/tests/Framework/VCard.php b/tests/Framework/VCard.php index 15aa5d8..3353b5b 100644 --- a/tests/Framework/VCard.php +++ b/tests/Framework/VCard.php @@ -65,6 +65,20 @@ $this->assertEquals("prefix", $vcard['prefix'], "Decode backslash character"); } + /** + * Backslash parsing test (#1489085) + */ + function test_parse_five() + { + $vcard = "BEGIN:VCARD\nVERSION:3.0\nN:last\\\\\\a;fir\\nst\nURL:http\\://domain.tld\nEND:VCARD"; + $vcard = new rcube_vcard($vcard, null); + $vcard = $vcard->get_assoc(); + + $this->assertEquals("last\\a", $vcard['surname'], "Decode dummy backslash character"); + $this->assertEquals("fir\nst", $vcard['firstname'], "Decode backslash character"); + $this->assertEquals("http://domain.tld", $vcard['website:other'][0], "Decode dummy backslash character"); + } + function test_import() { $input = file_get_contents($this->_srcpath('apple.vcf')); diff --git a/tests/src/Csv2vcard/tb_plain.csv b/tests/src/Csv2vcard/tb_plain.csv index 94ea766..4c4af14 100644 --- a/tests/src/Csv2vcard/tb_plain.csv +++ b/tests/src/Csv2vcard/tb_plain.csv @@ -1,2 +1,2 @@ First Name,Last Name,Display Name,Nickname,Primary Email,Secondary Email,Screen Name,Work Phone,Home Phone,Fax Number,Pager Number,Mobile Number,Home Address,Home Address 2,Home City,Home State,Home ZipCode,Home Country,Work Address,Work Address 2,Work City,Work State,Work ZipCode,Work Country,Job Title,Department,Organization,Web Page 1,Web Page 2,Birth Year,Birth Month,Birth Day,Custom 1,Custom 2,Custom 3,Custom 4,Notes, -Firstname,Lastname,Displayname,Nick,test@domain.tld,next@domain.tld,,phone work,phone home,fax,pager,mobile,Priv address,,City,region,xx-xxx,USA,Addr work,,city,region,33-333,Poland,title,department,Organization,http://page.com,http://webpage.tld,1970,11,15,,,,,, +Firstname,Lastname,Displayname,Nick,test@domain.tld,next@domain.tld,,phone work,phone home,fax,pager,mobile,Priv address,,City,region,xx-xxx,USA,Addr work,,Wcity,Wstate,33-333,Poland,title,department,Organization,http://page.com,http://webpage.tld,1970,11,15,,,,,, diff --git a/tests/src/Csv2vcard/tb_plain.vcf b/tests/src/Csv2vcard/tb_plain.vcf index b001c39..2aa91ad 100644 --- a/tests/src/Csv2vcard/tb_plain.vcf +++ b/tests/src/Csv2vcard/tb_plain.vcf @@ -16,5 +16,5 @@ URL;TYPE=other:http://webpage.tld BDAY;VALUE=date:1970-11-15 ADR;TYPE=home:;;Priv address;City;region;xx-xxx;USA -ADR;TYPE=work:;;Addr work;;;33-333;Poland +ADR;TYPE=work:;;Addr work;Wcity;Wstate;33-333;Poland END:VCARD diff --git a/tests/src/format-flowed-unfolded.txt b/tests/src/format-flowed-unfolded.txt index 14e526b..0af9b71 100644 --- a/tests/src/format-flowed-unfolded.txt +++ b/tests/src/format-flowed-unfolded.txt @@ -5,7 +5,7 @@ On XX.YY.YYYY Y:YY, Somebody wrote: > This part is a reply wihtout any flowing lines. rcube_mime::unfold_flowed() -> has to be careful with empty quoted lines because they might end with a +>> has to be careful with empty quoted lines because they might end with a > space but still shouldn't be considered as flowed! > > The above empty line should persist after unfolding. diff --git a/tests/src/format-flowed.txt b/tests/src/format-flowed.txt index 359a41a..da36064 100644 --- a/tests/src/format-flowed.txt +++ b/tests/src/format-flowed.txt @@ -7,7 +7,7 @@ On XX.YY.YYYY Y:YY, Somebody wrote: > This part is a reply wihtout any flowing lines. rcube_mime::unfold_flowed() -> has to be careful with empty quoted lines because they might end with a +>> has to be careful with empty quoted lines because they might end with a > space but still shouldn't be considered as flowed! > > The above empty line should persist after unfolding. -- Gitblit v1.9.1