diff options
Diffstat (limited to 'program/steps')
-rw-r--r-- | program/steps/addressbook/export.inc | 106 | ||||
-rw-r--r-- | program/steps/addressbook/import.inc | 71 | ||||
-rw-r--r-- | program/steps/addressbook/save.inc | 25 | ||||
-rw-r--r-- | program/steps/mail/attachments.inc | 5 | ||||
-rw-r--r-- | program/steps/mail/compose.inc | 142 | ||||
-rw-r--r-- | program/steps/mail/func.inc | 71 | ||||
-rw-r--r-- | program/steps/mail/sendmail.inc | 44 | ||||
-rw-r--r-- | program/steps/mail/show.inc | 5 | ||||
-rw-r--r-- | program/steps/settings/edit_prefs.inc | 9 | ||||
-rw-r--r-- | program/steps/settings/func.inc | 104 | ||||
-rw-r--r-- | program/steps/settings/save_prefs.inc | 1 | ||||
-rw-r--r-- | program/steps/utils/save_pref.inc | 22 | ||||
-rw-r--r-- | program/steps/utils/spell.inc | 3 |
13 files changed, 427 insertions, 181 deletions
diff --git a/program/steps/addressbook/export.inc b/program/steps/addressbook/export.inc index 761f26b75..1e988feab 100644 --- a/program/steps/addressbook/export.inc +++ b/program/steps/addressbook/export.inc @@ -5,7 +5,7 @@ | program/steps/addressbook/export.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2008-2011, The Roundcube Dev Team | + | Copyright (C) 2008-2013, The Roundcube Dev Team | | Copyright (C) 2011, Kolab Systems AG | | | | Licensed under the GNU General Public License version 3 or | @@ -21,6 +21,46 @@ +-----------------------------------------------------------------------+ */ + +/** + * Copy contact record properties into a vcard object + */ +function prepare_for_export(&$record, $source = null) +{ + $groups = $source && $source->groups && $source->export_groups ? $source->get_record_groups($record['ID']) : null; + + if (empty($record['vcard'])) { + $vcard = new rcube_vcard(); + if ($source) { + $vcard->extend_fieldmap($source->vcard_map); + } + $vcard->load($record['vcard']); + $vcard->reset(); + + foreach ($record as $key => $values) { + list($field, $section) = explode(':', $key); + foreach ((array)$values as $value) { + if (is_array($value) || @strlen($value)) { + $vcard->set($field, $value, strtoupper($section)); + } + } + } + + // append group names + if ($groups) { + $vcard->set('groups', join(',', $groups), null); + } + + $record['vcard'] = $vcard->export(true); + } + // patch categories to alread existing vcard block + else if ($record['vcard'] && !empty($groups) && !strpos($record['vcard'], 'CATEGORIES:')) { + $vgroups = 'CATEGORIES:' . rcube_vcard::vcard_quote(join(',', $groups)); + $record['vcard'] = str_replace('END:VCARD', $vgroups . rcube_vcard::$eol . 'END:VCARD', $record['vcard']); + } +} + + // Use search result if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']])) { @@ -42,23 +82,7 @@ if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search while ($record = $result->next()) { // because vcard_map is per-source we need to create vcard here - if (empty($record['vcard']) || empty($record['name'])) { - $vcard = new rcube_vcard(); - $vcard->extend_fieldmap($source->vcard_map); - $vcard->load($record['vcard']); - $vcard->reset(); - - foreach ($record as $key => $values) { - list($field, $section) = explode(':', $key); - foreach ((array)$values as $value) { - if (is_array($value) || @strlen($value)) { - $vcard->set($field, $value, strtoupper($section)); - } - } - } - - $record['vcard'] = $vcard->export(true); - } + prepare_for_export($record, $source); $record['sourceid'] = $s; $key = rcube_addressbook::compose_contact_key($record, $sort_col); @@ -90,23 +114,7 @@ else if (!empty($_REQUEST['_cid'])) { while ($record = $result->next()) { // because vcard_map is per-source we need to create vcard here - if (empty($record['vcard']) || empty($record['name'])) { - $vcard = new rcube_vcard(); - $vcard->extend_fieldmap($source->vcard_map); - $vcard->load($record['vcard']); - $vcard->reset(); - - foreach ($record as $key => $values) { - list($field, $section) = explode(':', $key); - foreach ((array)$values as $value) { - if (is_array($value) || @strlen($value)) { - $vcard->set($field, $value, strtoupper($section)); - } - } - } - - $record['vcard'] = $vcard->export(true); - } + prepare_for_export($record, $source); $record['sourceid'] = $s; $key = rcube_addressbook::compose_contact_key($record, $sort_col); @@ -136,30 +144,12 @@ header('Content-Type: text/x-vcard; charset='.RCMAIL_CHARSET); header('Content-Disposition: attachment; filename="contacts.vcf"'); while ($result && ($row = $result->next())) { - // we already have a vcard record - if ($row['vcard'] && $row['name']) { - // fix folding and end-of-line chars - $row['vcard'] = preg_replace('/\r|\n\s+/', '', $row['vcard']); - $row['vcard'] = preg_replace('/\n/', rcube_vcard::$eol, $row['vcard']); - echo rcube_vcard::rfc2425_fold($row['vcard']) . rcube_vcard::$eol; - } - // copy values into vcard object - else { - $vcard = new rcube_vcard(); - $vcard->extend_fieldmap($CONTACTS->vcard_map); - $vcard->load($row['vcard']); - $vcard->reset(); + prepare_for_export($row, $CONTACTS); - foreach ($row as $key => $values) { - list($field, $section) = explode(':', $key); - foreach ((array)$values as $value) { - if (is_array($value) || @strlen($value)) - $vcard->set($field, $value, strtoupper($section)); - } - } - - echo $vcard->export(true) . rcube_vcard::$eol; - } + // fix folding and end-of-line chars + $row['vcard'] = preg_replace('/\r|\n\s+/', '', $row['vcard']); + $row['vcard'] = preg_replace('/\n/', rcube_vcard::$eol, $row['vcard']); + echo rcube_vcard::rfc2425_fold($row['vcard']) . rcube_vcard::$eol; } exit; diff --git a/program/steps/addressbook/import.inc b/program/steps/addressbook/import.inc index 915aac884..60f5d7b61 100644 --- a/program/steps/addressbook/import.inc +++ b/program/steps/addressbook/import.inc @@ -40,6 +40,7 @@ function rcmail_import_form($attrib) 'multiple' => 'multiple', )); $form = html::p(null, html::label('rcmimportfile', rcube_label('importfromfile')) . $upload->show()); + $table = new html_table(array('cols' => 2)); // addressbook selector if (count($writable_books) > 1) { @@ -48,17 +49,31 @@ function rcmail_import_form($attrib) foreach ($writable_books as $book) $select->add($book['name'], $book['id']); - $form .= html::p(null, html::label('rcmimporttarget', rcube_label('importtarget')) - . $select->show($target)); + $table->add('title', html::label('rcmimporttarget', rcube_label('importtarget'))); + $table->add(null, $select->show($target)); } else { $abook = new html_hiddenfield(array('name' => '_target', 'value' => key($writable_books))); $form .= $abook->show(); } + // selector for group import options + if (count($writable_books) >= 1 || $writable_books[0]->groups) { + $select = new html_select(array('name' => '_groups', 'id' => 'rcmimportgroups', 'is_escaped' => true)); + $select->add(rcube_label('none'), '0'); + $select->add(rcube_label('importgroupsall'), '1'); + $select->add(rcube_label('importgroupsexisting'), '2'); + + $table->add('title', html::label('rcmimportgroups', rcube_label('importgroups'))); + $table->add(null, $select->show(get_input_value('_groups', RCUBE_INPUT_GPC))); + } + + // checkbox to replace the entire address book $check_replace = new html_checkbox(array('name' => '_replace', 'value' => 1, 'id' => 'rcmimportreplace')); - $form .= html::p(null, $check_replace->show(get_input_value('_replace', RCUBE_INPUT_GPC)) . - html::label('rcmimportreplace', rcube_label('importreplace'))); + $table->add('title', html::label('rcmimportreplace', rcube_label('importreplace'))); + $table->add(null, $check_replace->show(get_input_value('_replace', RCUBE_INPUT_GPC))); + + $form .= $table->show(array('id' => null) + $attrib); $OUTPUT->set_env('writable_source', !empty($writable_books)); $OUTPUT->add_label('selectimportfile','importwait'); @@ -134,19 +149,50 @@ function rcmail_import_buttons($attrib) } +/** + * Returns the matching group id. If group doesn't exist, it'll be created if allowed. + */ +function rcmail_import_group_id($group_name, $CONTACTS, $create, &$import_groups) +{ + $group_id = 0; + foreach ($import_groups as $key => $group) { + if (strtolower($group['name']) == strtolower($group_name)) { + $group_id = $group['ID']; + break; + } + } + + // create a new group + if (!$group_id && $create) { + $new_group = $CONTACTS->create_group($group_name); + if (!$new_group['ID']) + $new_group['ID'] = $new_group['id']; + $import_groups[] = $new_group; + $group_id = $new_group['ID']; + } + + return $group_id; +} + + /** The import process **/ $importstep = 'rcmail_import_form'; if (is_array($_FILES['_file'])) { - $replace = (bool)get_input_value('_replace', RCUBE_INPUT_GPC); - $target = get_input_value('_target', RCUBE_INPUT_GPC); + $replace = (bool)get_input_value('_replace', RCUBE_INPUT_GPC); + $target = get_input_value('_target', RCUBE_INPUT_GPC); + $with_groups = intval(get_input_value('_groups', RCUBE_INPUT_GPC)); $vcards = array(); $upload_error = null; $CONTACTS = $RCMAIL->get_address_book($target, true); + if (!$CONTACTS->groups) { + $with_groups = false; + } + if ($CONTACTS->readonly) { $OUTPUT->show_message('addresswriterror', 'error'); } @@ -206,6 +252,10 @@ if (is_array($_FILES['_file'])) { $CONTACTS->delete_all(); } + if ($with_groups) { + $import_groups = $CONTACTS->list_groups(); + } + foreach ($vcards as $vcard) { $a_record = $vcard->get_assoc(); @@ -258,6 +308,15 @@ if (is_array($_FILES['_file'])) { $success = $plugin['result']; if ($success) { + // assign groups for this contact (if enabled) + if ($with_groups && !empty($a_record['groups'])) { + foreach (explode(',', $a_record['groups'][0]) as $group_name) { + if ($group_id = rcmail_import_group_id($group_name, $CONTACTS, $with_groups == 1, $import_groups)) { + $CONTACTS->add_to_group($group_id, $success); + } + } + } + $IMPORT_STATS->inserted++; $IMPORT_STATS->names[] = $a_record['name'] ? $a_record['name'] : $email; } diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc index e7e5efc63..2adc53bcf 100644 --- a/program/steps/addressbook/save.inc +++ b/program/steps/addressbook/save.inc @@ -59,15 +59,34 @@ foreach ($GLOBALS['CONTACT_COLTYPES'] as $col => $colprop) { } // assign values and subtypes else if (is_array($_POST[$fname])) { - $values = get_input_value($fname, RCUBE_INPUT_POST, true); + $values = get_input_value($fname, RCUBE_INPUT_POST, true); $subtypes = get_input_value('_subtype_' . $col, RCUBE_INPUT_POST); + foreach ($values as $i => $val) { + if ($col == 'email') { + // extract email from full address specification, e.g. "Name" <addr@domain.tld> + $addr = rcube_mime::decode_address_list($val, 1, false); + if (!empty($addr) && ($addr = array_pop($addr)) && $addr['mailto']) { + $val = $addr['mailto']; + } + } + $subtype = $subtypes[$i] ? ':'.$subtypes[$i] : ''; $a_record[$col.$subtype][] = $val; } } else if (isset($_POST[$fname])) { $a_record[$col] = get_input_value($fname, RCUBE_INPUT_POST, true); + + // normalize the submitted date strings + if ($colprop['type'] == 'date') { + if ($timestamp = rcube_utils::strtotime($a_record[$col])) { + $a_record[$col] = date('Y-m-d', $timestamp); + } + else { + unset($a_record[$col]); + } + } } } @@ -75,8 +94,10 @@ foreach ($GLOBALS['CONTACT_COLTYPES'] as $col => $colprop) { if (empty($a_record['name'])) { $a_record['name'] = rcube_addressbook::compose_display_name($a_record, true); // Reset it if equals to email address (from compose_display_name()) - if ($a_record['name'] == $a_record['email'][0]) + $email = rcube_addressbook::get_col_values('email', $a_record, true); + if ($a_record['name'] == $email[0]) { $a_record['name'] = ''; + } } // do input checks (delegated to $CONTACTS instance) diff --git a/program/steps/mail/attachments.inc b/program/steps/mail/attachments.inc index f83f6892e..85aa9542b 100644 --- a/program/steps/mail/attachments.inc +++ b/program/steps/mail/attachments.inc @@ -118,9 +118,12 @@ if (is_array($_FILES['_attachments']['tmp_name'])) { 'alt' => rcube_label('delete') )); } - else { + else if ($COMPOSE['textbuttons']) { $button = Q(rcube_label('delete')); } + else { + $button = ''; + } $content = html::a(array( 'href' => "#delete", diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index bce4fd313..646d2bcd1 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -54,30 +54,12 @@ if (!is_array($COMPOSE)) $COMPOSE_ID = uniqid(mt_rand()); $_SESSION['compose_data_'.$COMPOSE_ID] = array( 'id' => $COMPOSE_ID, - 'param' => request2param(RCUBE_INPUT_GET), + 'param' => rcube_utils::request2param(RCUBE_INPUT_GET, 'task|action', true), 'mailbox' => $RCMAIL->storage->get_folder(), ); $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID]; - // process values like "mailto:foo@bar.com?subject=new+message&cc=another" - if ($COMPOSE['param']['to']) { - // #1486037: remove "mailto:" prefix - $COMPOSE['param']['to'] = preg_replace('/^mailto:/i', '', $COMPOSE['param']['to']); - $mailto = explode('?', $COMPOSE['param']['to']); - if (count($mailto) > 1) { - $COMPOSE['param']['to'] = $mailto[0]; - parse_str($mailto[1], $query); - foreach ($query as $f => $val) - $COMPOSE['param'][$f] = $val; - } - } - - // select folder where to save the sent message - $COMPOSE['param']['sent_mbox'] = $RCMAIL->config->get('sent_mbox'); - - // pipe compose parameters thru plugins - $plugin = $RCMAIL->plugins->exec_hook('message_compose', $COMPOSE); - $COMPOSE['param'] = array_merge($COMPOSE['param'], $plugin['param']); + rcmail_process_compose_params($COMPOSE); // add attachments listed by message_compose hook if (is_array($plugin['attachments'])) { @@ -149,6 +131,11 @@ if ($font && !is_array($font)) { $OUTPUT->set_env('default_font', $font); } +// default font size for HTML editor +if ($font_size = $RCMAIL->config->get('default_font_size')) { + $OUTPUT->set_env('default_font_size', $font_size); +} + // get reference message and set compose mode if ($msg_uid = $COMPOSE['param']['draft_uid']) { $compose_mode = RCUBE_COMPOSE_DRAFT; @@ -166,6 +153,8 @@ else if ($msg_uid = $COMPOSE['param']['forward_uid']) { else if ($msg_uid = $COMPOSE['param']['uid']) { $compose_mode = RCUBE_COMPOSE_EDIT; } + +$COMPOSE['mode'] = $compose_mode; $OUTPUT->set_env('compose_mode', $compose_mode); $config_show_sig = $RCMAIL->config->get('show_sig', 1); @@ -254,6 +243,14 @@ if (!empty($msg_uid) && empty($COMPOSE['as_attachment'])) } else { $MESSAGE = new stdClass(); + + // apply mailto: URL parameters + if (!empty($COMPOSE['param']['in-reply-to'])) { + $COMPOSE['reply_msgid'] = '<' . $COMPOSE['param']['in-reply-to'] . '>'; + } + if (!empty($COMPOSE['param']['references'])) { + $COMPOSE['references'] = $COMPOSE['param']['references']; + } } $MESSAGE->compose = array(); @@ -378,7 +375,12 @@ foreach ($parts as $header) { $mailto = format_email(rcube_idn_to_utf8($addr_part['mailto'])); if (!in_array($mailto, $a_recipients) - && ($header == 'to' || empty($MESSAGE->compose['from_email']) || $mailto != $MESSAGE->compose['from_email']) + && ( + $header == 'to' + || $compose_mode != RCUBE_COMPOSE_REPLY + || empty($MESSAGE->compose['from_email']) + || $mailto != $MESSAGE->compose['from_email'] + ) ) { if ($addr_part['name'] && $addr_part['mailto'] != $addr_part['name']) $string = format_email_recipient($mailto, $addr_part['name']); @@ -403,6 +405,53 @@ $MESSAGE_BODY = rcmail_prepare_message_body(); /****** compose mode functions ********/ +// process compose request parameters +function rcmail_process_compose_params(&$COMPOSE) +{ + if ($COMPOSE['param']['to']) { + $mailto = explode('?', $COMPOSE['param']['to'], 2); + + // #1486037: remove "mailto:" prefix + $COMPOSE['param']['to'] = preg_replace('/^mailto:/i', '', $mailto[0]); + + // Supported case-insensitive tokens in mailto URL + $url_tokens = array('to', 'cc', 'bcc', 'reply-to', 'in-reply-to', 'references', 'subject', 'body'); + + if (!empty($mailto[1])) { + parse_str($mailto[1], $query); + foreach ($query as $f => $val) { + if (($key = array_search(strtolower($f), $url_tokens)) !== false) { + $f = $url_tokens[$key]; + } + + // merge mailto: addresses with addresses from 'to' parameter + if ($f == 'to' && !empty($COMPOSE['param']['to'])) { + $to_addresses = rcube_mime::decode_address_list($COMPOSE['param']['to'], null, true, null, true); + $add_addresses = rcube_mime::decode_address_list($val, null, true); + foreach ($add_addresses as $addr) { + if (!in_array($addr['mailto'], $to_addresses)) { + $to_addresses[] = $addr['mailto']; + $COMPOSE['param']['to'] = (!empty($to_addresses) ? ', ' : '') . $addr['string']; + } + } + } + else { + $COMPOSE['param'][$f] = $val; + } + } + } + } + + $RCMAIL = rcmail::get_instance(); + + // select folder where to save the sent message + $COMPOSE['param']['sent_mbox'] = $RCMAIL->config->get('sent_mbox'); + + // pipe compose parameters thru plugins + $plugin = $RCMAIL->plugins->exec_hook('message_compose', $COMPOSE); + $COMPOSE['param'] = array_merge($COMPOSE['param'], $plugin['param']); +} + function rcmail_compose_headers($attrib) { global $MESSAGE; @@ -804,29 +853,14 @@ function rcmail_compose_body($attrib) // Set language list if (!empty($CONFIG['enable_spellcheck'])) { - $engine = $RCMAIL->config->get('spellcheck_engine','googie'); + $engine = new rcube_spellchecker(); $dictionary = (bool) $RCMAIL->config->get('spellcheck_dictionary'); - $spellcheck_langs = (array) $RCMAIL->config->get('spellcheck_languages', - array('da'=>'Dansk', 'de'=>'Deutsch', 'en' => 'English', 'es'=>'Español', - 'fr'=>'Français', 'it'=>'Italiano', 'nl'=>'Nederlands', 'pl'=>'Polski', - 'pt'=>'Português', 'ru'=>'Русский', 'fi'=>'Suomi', 'sv'=>'Svenska')); - - // googie works only with two-letter codes - if ($engine == 'googie') { - $lang = strtolower(substr($_SESSION['language'], 0, 2)); - - $spellcheck_langs_googie = array(); - foreach ($spellcheck_langs as $key => $name) - $spellcheck_langs_googie[strtolower(substr($key,0,2))] = $name; - $spellcheck_langs = $spellcheck_langs_googie; - } - else { - $lang = $_SESSION['language']; + $spellcheck_langs = $engine->languages(); + $lang = $_SESSION['language']; - // if not found in the list, try with two-letter code - if (!$spellcheck_langs[$lang]) - $lang = strtolower(substr($lang, 0, 2)); - } + // if not found in the list, try with two-letter code + if (!$spellcheck_langs[$lang]) + $lang = strtolower(substr($lang, 0, 2)); if (!$spellcheck_langs[$lang]) $lang = 'en'; @@ -1364,8 +1398,9 @@ function rcmail_compose_attachment_list($attrib) if (!$attrib['id']) $attrib['id'] = 'rcmAttachmentList'; - $out = "\n"; + $out = "\n"; $jslist = array(); + $button = ''; if (is_array($COMPOSE['attachments'])) { if ($attrib['deleteicon']) { @@ -1374,27 +1409,38 @@ function rcmail_compose_attachment_list($attrib) 'alt' => rcube_label('delete') )); } - else + else if (rcube_utils::get_boolean($attrib['textbuttons'])) { $button = Q(rcube_label('delete')); + } foreach ($COMPOSE['attachments'] as $id => $a_prop) { if (empty($a_prop)) continue; - $out .= html::tag('li', array('id' => 'rcmfile'.$id, 'class' => rcmail_filetype2classname($a_prop['mimetype'], $a_prop['name'])), + $out .= html::tag('li', + array( + 'id' => 'rcmfile'.$id, + 'class' => rcmail_filetype2classname($a_prop['mimetype'], $a_prop['name']), + 'onmouseover' => "rcube_webmail.long_subject_title_ex(this, 0)", + ), html::a(array( 'href' => "#delete", 'title' => rcube_label('delete'), 'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", JS_OBJECT_NAME, $id), - 'class' => 'delete'), - $button) . Q($a_prop['name'])); + 'class' => 'delete' + ), + $button + ) . Q($a_prop['name']) + ); - $jslist['rcmfile'.$id] = array('name' => $a_prop['name'], 'complete' => true, 'mimetype' => $a_prop['mimetype']); + $jslist['rcmfile'.$id] = array('name' => $a_prop['name'], 'complete' => true, 'mimetype' => $a_prop['mimetype']); } } if ($attrib['deleteicon']) $COMPOSE['deleteicon'] = $CONFIG['skin_path'] . $attrib['deleteicon']; + else if (rcube_utils::get_boolean($attrib['textbuttons'])) + $COMPOSE['textbuttons'] = true; if ($attrib['cancelicon']) $OUTPUT->set_env('cancelicon', $CONFIG['skin_path'] . $attrib['cancelicon']); if ($attrib['loadingicon']) diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 018a31b84..70441e0d7 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1383,9 +1383,6 @@ function rcmail_alter_html_link($matches) { global $RCMAIL; - // Support unicode/punycode in top-level domain part - $EMAIL_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))'; - $tag = strtolower($matches[1]); $attrib = parse_attrib_string($matches[2]); $end = '>'; @@ -1400,12 +1397,36 @@ function rcmail_alter_html_link($matches) $attrib['href'] = $RCMAIL->url(array('task' => 'utils', 'action' => 'modcss', 'u' => $tempurl, 'c' => $GLOBALS['rcmail_html_container_id'])); $end = ' />'; } - else if (preg_match('/^mailto:'.$EMAIL_PATTERN.'(\?[^"\'>]+)?/i', $attrib['href'], $mailto)) { - $attrib['href'] = $mailto[0]; - $attrib['onclick'] = sprintf( - "return %s.command('compose','%s',this)", - JS_OBJECT_NAME, - JQ($mailto[1].$mailto[3])); + else if (preg_match('/^mailto:(.+)/i', $attrib['href'], $mailto)) { + list($mailto, $url) = explode('?', html_entity_decode($mailto[1], ENT_QUOTES, 'UTF-8'), 2); + + $url = urldecode($url); + $mailto = urldecode($mailto); + $addresses = rcube_mime::decode_address_list($mailto, null, true); + $mailto = array(); + + // do sanity checks on recipients + foreach ($addresses as $idx => $addr) { + if (rcube_utils::check_email($addr['mailto'], false)) { + $addresses[$idx] = $addr['mailto']; + $mailto[] = $addr['string']; + } + else { + unset($addresses[$idx]); + } + } + + if (!empty($addresses)) { + $attrib['href'] = 'mailto:' . implode(',', $addresses); + $attrib['onclick'] = sprintf( + "return %s.command('compose','%s',this)", + JS_OBJECT_NAME, + JQ(implode(',', $mailto) . ($url ? "?$url" : ''))); + } + else { + $attrib['href'] = '#NOP'; + $attrib['onclick'] = ''; + } } else if (empty($attrib['href']) && !$attrib['name']) { $attrib['href'] = './#NOP'; @@ -1470,15 +1491,13 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, $mailto = rcube_idn_to_utf8($mailto); if ($PRINT_MODE) { - $out .= ($out ? ', ' : '') . sprintf('%s <%s>', Q($name), $mailto); - // for printing we display all addresses - continue; + $address = sprintf('%s <%s>', Q($name), Q($mailto)); } else if ($valid) { if ($linked) { $attrs = array( 'href' => 'mailto:' . $mailto, - 'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($mailto)), + 'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ(format_email_recipient($mailto, $name))), 'class' => "rcmContactAddress", ); @@ -1536,6 +1555,15 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, } if ($moreadrs) { + if ($PRINT_MODE) { + $out .= ' ' . html::a(array( + 'href' => '#more', + 'class' => 'morelink', + 'onclick' => '$(this).hide().next().show()', + ), Q(rcube_label(array('name' => 'andnmore', 'vars' => array('nr' => $moreadrs))))) . + html::span(array('style' => 'display:none'), join(', ', $allvalues)); + } + else { $out .= ' ' . html::a(array( 'href' => '#more', 'class' => 'morelink', @@ -1545,6 +1573,7 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, JQ($title)) ), Q(rcube_label(array('name' => 'andnmore', 'vars' => array('nr' => $moreadrs))))); + } } return $out; @@ -1730,7 +1759,7 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset); foreach ($a_to as $addr) { if (!empty($addr['mailto'])) { - $a_recipients[] = format_email($addr['mailto']); + $a_recipients[] = strtolower($addr['mailto']); $a_names[] = $addr['name']; } } @@ -1739,7 +1768,7 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset); foreach ($a_cc as $addr) { if (!empty($addr['mailto'])) { - $a_recipients[] = format_email($addr['mailto']); + $a_recipients[] = strtolower($addr['mailto']); $a_names[] = $addr['name']; } } @@ -1765,7 +1794,7 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r break; } // use replied message recipients - else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) { + else if (($found = array_search(strtolower($ident['email_ascii']), $a_recipients)) !== false) { if ($found_idx === null) { $found_idx = $idx; } @@ -1784,15 +1813,19 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r // Try Return-Path if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) { + $return_path = array_map('strtolower', (array) $return_path); + foreach ($identities as $idx => $ident) { // Return-Path header contains an email address, but on some mailing list // it can be e.g. <pear-dev-return-55250-local=domain.tld@lists.php.net> // where local@domain.tld is the address we're looking for (#1489241) - $ident1 = $ident['email_ascii']; + $ident1 = strtolower($ident['email_ascii']); $ident2 = str_replace('@', '=', $ident1); + $ident1 = '<' . $ident1 . '>'; + $ident2 = '-' . $ident2 . '@'; - foreach ((array)$return_path as $path) { - if (stripos($path, $ident1) !== false || stripos($path, $ident2)) { + foreach ($return_path as $path) { + if ($path == $ident1 || stripos($path, $ident2)) { $from_idx = $idx; break 2; } diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index 1a92844c0..ccb8978be 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -414,9 +414,6 @@ if (!empty($headers['Reply-To'])) { if (!empty($_POST['_followupto'])) { $headers['Mail-Followup-To'] = rcmail_email_input_format(get_input_value('_followupto', RCUBE_INPUT_POST, TRUE, $message_charset)); } -if (!empty($COMPOSE['reply_msgid'])) { - $headers['In-Reply-To'] = $COMPOSE['reply_msgid']; -} // remember reply/forward UIDs in special headers if (!empty($COMPOSE['reply_uid']) && $savedraft) { @@ -426,6 +423,9 @@ else if (!empty($COMPOSE['forward_uid']) && $savedraft) { $headers['X-Draft-Info'] = array('type' => 'forward', 'uid' => $COMPOSE['forward_uid']); } +if (!empty($COMPOSE['reply_msgid'])) { + $headers['In-Reply-To'] = $COMPOSE['reply_msgid']; +} if (!empty($COMPOSE['references'])) { $headers['References'] = $COMPOSE['references']; } @@ -473,12 +473,19 @@ $isHtml = (bool) get_input_value('_is_html', RCUBE_INPUT_POST); $message_body = get_input_value('_message', RCUBE_INPUT_POST, TRUE, $message_charset); if ($isHtml) { - $font = rcube_fontdefs($RCMAIL->config->get('default_font')); - $bstyle = $font && is_string($font) ? " style='font-family: $font'" : ''; + $bstyle = array(); + + if ($font_size = $RCMAIL->config->get('default_font_size')) { + $bstyle[] = 'font-size: ' . $font_size; + } + if ($font_family = $RCMAIL->config->get('default_font')) { + $bstyle[] = 'font-family: ' . rcmail::font_defs($font_family); + } // append doctype and html/body wrappers - $message_body = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">' . - "\r\n<html><body$bstyle>\r\n" . $message_body; + $message_body = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">' + . "\r\n<html><body" . (!empty($bstyle) ? " style='" . implode($bstyle, '; ') . "'" : '') . ">\r\n" + . $message_body; } if (!$savedraft) { @@ -752,7 +759,7 @@ if ($store_target) { if (PEAR::isError($msg)) raise_error(array('code' => 650, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, + 'file' => __FILE__, 'line' => __LINE__, 'message' => "Could not create message: ".$msg->getMessage()), TRUE, FALSE); else { @@ -823,15 +830,24 @@ if ($savedraft) { // start the auto-save timer again $OUTPUT->command('auto_save_start'); - - $OUTPUT->send('iframe'); } else { + $folders = array(); + + if ($COMPOSE['mode'] == 'reply' || $COMPOSE['mode'] == 'forward') + $folders[] = $COMPOSE['mailbox']; + rcmail_compose_cleanup($COMPOSE_ID); if ($store_folder && !$saved) - $OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent')); - else - $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'), $store_target); - $OUTPUT->send('iframe'); + $OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent'), $folders); + else { + if ($store_folder) { + $folders[] = $store_target; + } + + $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'), $folders); + } } + +$OUTPUT->send('iframe'); diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc index 59f4d55e1..9d85f9c8f 100644 --- a/program/steps/mail/show.inc +++ b/program/steps/mail/show.inc @@ -175,9 +175,9 @@ function rcmail_message_attachments($attrib) $ol .= html::tag('li', null, Q(sprintf("%s (%s)", $filename, $size))); } else { - if (mb_strlen($filename) > 50) { + if ($attrib['maxlength'] && mb_strlen($filename) > $attrib['maxlength']) { $title = $filename; - $filename = abbreviate_string($filename, 50); + $filename = abbreviate_string($filename, $attrib['maxlength']); } else { $title = ''; @@ -190,6 +190,7 @@ function rcmail_message_attachments($attrib) 'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false), 'onclick' => sprintf('return %s.command(\'load-attachment\',\'%s\',this)', JS_OBJECT_NAME, $attach_prop->mime_id), + 'onmouseover' => $title ? '' : 'rcube_webmail.long_subject_title_ex(this, 0)', 'title' => Q($title), ), Q($filename)); $ol .= html::tag('li', array('class' => $class, 'id' => $id), $link); diff --git a/program/steps/settings/edit_prefs.inc b/program/steps/settings/edit_prefs.inc index 468e4994d..adf6b1623 100644 --- a/program/steps/settings/edit_prefs.inc +++ b/program/steps/settings/edit_prefs.inc @@ -40,24 +40,21 @@ function rcmail_user_prefs_form($attrib) $out = $form_start; - foreach ($SECTIONS[$CURR_SECTION]['blocks'] as $block) { + foreach ($SECTIONS[$CURR_SECTION]['blocks'] as $class => $block) { if (!empty($block['options'])) { $table = new html_table(array('cols' => 2)); foreach ($block['options'] as $option) { - if ($option['advanced']) - $table->set_row_attribs('advanced'); - if (isset($option['title'])) { $table->add('title', $option['title']); - $table->add(null, $option['content']); + $table->add(null, $option['content']); } else { $table->add(array('colspan' => 2), $option['content']); } } - $out .= html::tag('fieldset', null, html::tag('legend', null, $block['name']) . $table->show($attrib)); + $out .= html::tag('fieldset', $class, html::tag('legend', null, $block['name']) . $table->show($attrib)); } else if (!empty($block['content'])) { $out .= html::tag('fieldset', null, html::tag('legend', null, $block['name']) . $block['content']); diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index 39a925e0e..3b599c0e5 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -158,6 +158,7 @@ function rcmail_user_prefs($current = null) 'main' => array('name' => Q(rcube_label('mainoptions'))), 'skin' => array('name' => Q(rcube_label('skin'))), 'browser' => array('name' => Q(rcube_label('browseroptions'))), + 'advanced'=> array('name' => Q(rcube_label('advancedoptions'))), ); // language selection @@ -367,6 +368,7 @@ function rcmail_user_prefs($current = null) $blocks = array( 'main' => array('name' => Q(rcube_label('mainoptions'))), 'new_message' => array('name' => Q(rcube_label('newmessage'))), + 'advanced' => array('name' => Q(rcube_label('advancedoptions'))), ); // show config parameter for preview pane @@ -488,6 +490,7 @@ function rcmail_user_prefs($current = null) case 'mailview': $blocks = array( 'main' => array('name' => Q(rcube_label('mainoptions'))), + 'advanced' => array('name' => Q(rcube_label('advancedoptions'))), ); // show checkbox to open message view in new window @@ -543,7 +546,7 @@ function rcmail_user_prefs($current = null) $field_id = 'rcmfd_default_charset'; - $blocks['main']['options']['default_charset'] = array( + $blocks['advanced']['options']['default_charset'] = array( 'title' => html::label($field_id, Q(rcube_label('defaultcharset'))), 'content' => $RCMAIL->output->charset_selector(array( 'id' => $field_id, 'name' => '_default_charset', 'selected' => $config['default_charset'] @@ -605,6 +608,7 @@ function rcmail_user_prefs($current = null) 'main' => array('name' => Q(rcube_label('mainoptions'))), 'sig' => array('name' => Q(rcube_label('signatureoptions'))), 'spellcheck' => array('name' => Q(rcube_label('spellcheckoptions'))), + 'advanced' => array('name' => Q(rcube_label('advancedoptions'))), ); // show checkbox to compose messages in a new window @@ -673,8 +677,7 @@ function rcmail_user_prefs($current = null) $select->add(rcube_label('miscfolding'), 1); $select->add(rcube_label('2047folding'), 2); - $blocks['main']['options']['mime_param_folding'] = array( - 'advanced' => true, + $blocks['advanced']['options']['mime_param_folding'] = array( 'title' => html::label($field_id, Q(rcube_label('mimeparamfolding'))), 'content' => $select->show($config['mime_param_folding']), ); @@ -688,8 +691,7 @@ function rcmail_user_prefs($current = null) $field_id = 'rcmfd_force_7bit'; $input = new html_checkbox(array('name' => '_force_7bit', 'id' => $field_id, 'value' => 1)); - $blocks['main']['options']['force_7bit'] = array( - 'advanced' => true, + $blocks['advanced']['options']['force_7bit'] = array( 'title' => html::label($field_id, Q(rcube_label('force7bit'))), 'content' => $input->show($config['force_7bit']?1:0), ); @@ -837,28 +839,34 @@ function rcmail_user_prefs($current = null) ); } - if (!isset($no_override['default_font'])) { + if (!isset($no_override['default_font']) || !isset($no_override['default_font_size'])) { if (!$current) { continue 2; } + // Default font size + $field_id = 'rcmfd_default_font_size'; + $select_default_font_size = new html_select(array('name' => '_default_font_size', 'id' => $field_id)); + + $fontsizes = array('', '8pt', '10pt', '12pt', '14pt', '18pt', '24pt', '36pt'); + foreach ($fontsizes as $size) { + $select_default_font_size->add($size, $size); + } + + // Default font $field_id = 'rcmfd_default_font'; - $fonts = rcube_fontdefs(); - $selected = $config['default_font']; + $select_default_font = new html_select(array('name' => '_default_font', 'id' => $field_id)); + $select_default_font->add('', ''); - $select = '<select name="_default_font" id="'.$field_id.'">'; - $select .= '<option value=""' . (!$selected ? ' selected="selected"' : '') . '>---</option>'; + $fonts = rcube_fontdefs(); foreach ($fonts as $fname => $font) { - $select .= '<option value="'.$fname.'"' - . ($fname == $selected ? ' selected="selected"' : '') - . ' style=\'font-family: ' . $font . '\'>' - . Q($fname) . '</option>'; + $select_default_font->add($fname, $fname); } - $select .= '</select>'; $blocks['main']['options']['default_font'] = array( 'title' => html::label($field_id, Q(rcube_label('defaultfont'))), - 'content' => $select + 'content' => $select_default_font->show($RCMAIL->config->get('default_font', 1)) . + $select_default_font_size->show($RCMAIL->config->get('default_font_size', 1)) ); } break; @@ -866,7 +874,8 @@ function rcmail_user_prefs($current = null) // Addressbook config case 'addressbook': $blocks = array( - 'main' => array('name' => Q(rcube_label('mainoptions'))), + 'main' => array('name' => Q(rcube_label('mainoptions'))), + 'advanced' => array('name' => Q(rcube_label('advancedoptions'))), ); if (!isset($no_override['default_addressbook']) @@ -962,7 +971,8 @@ function rcmail_user_prefs($current = null) // Special IMAP folders case 'folders': $blocks = array( - 'main' => array('name' => Q(rcube_label('mainoptions'))), + 'main' => array('name' => Q(rcube_label('mainoptions'))), + 'advanced' => array('name' => Q(rcube_label('advancedoptions'))), ); if (!isset($no_override['show_real_foldernames'])) { @@ -1043,6 +1053,7 @@ function rcmail_user_prefs($current = null) $blocks = array( 'main' => array('name' => Q(rcube_label('mainoptions'))), 'maintenance' => array('name' => Q(rcube_label('maintenance'))), + 'advanced' => array('name' => Q(rcube_label('advancedoptions'))), ); if (!isset($no_override['read_when_deleted'])) { @@ -1235,9 +1246,66 @@ function rcmail_update_folder_row($name, $oldname=null, $subscribe=false, $class $name_utf8, $display_name, $protected, $class_name); } +/** + * Render the list of settings sections (AKA tabs) + */ +function rcmail_settings_tabs($attrib) +{ + global $RCMAIL, $OUTPUT; + + // add default attributes + $attrib += array('tagname' => 'span', 'idprefix' => 'settingstab', 'selclass' => 'selected'); + + $default_actions = array( + array('command' => 'preferences', 'type' => 'link', 'label' => 'preferences', 'title' => 'editpreferences'), + array('command' => 'folders', 'type' => 'link', 'label' => 'folders', 'title' => 'managefolders'), + array('command' => 'identities', 'type' => 'link', 'label' => 'identities', 'title' => 'manageidentities'), + ); + + // get all identites from DB and define list of cols to be displayed + $plugin = $RCMAIL->plugins->exec_hook('settings_actions', array( + 'actions' => $default_actions, + 'attrib' => $attrib, + )); + + $attrib = $plugin['attrib']; + $tagname = $attrib['tagname']; + $tabs = array(); + + foreach ($plugin['actions'] as $k => $action) { + if (!$action['command'] && !$action['href'] && $action['action']) { + $action['href'] = $RCMAIL->url(array('_action' => $action['action'])); + } + + $button = $OUTPUT->button($action); + $attr = $attrib; + + $cmd = $action['action'] ? $action['action'] : $action['command']; + $id = $action['id'] ? $action['id'] : $cmd; + if (!empty($id)) { + $attr['id'] = preg_replace('/[^a-z0-9]/i', '', $attrib['idprefix'] . $id); + } + $classnames = array($attrib['class']); + if (!empty($action['class'])) { + $classnames[] = $action['class']; + } + else if (!empty($cmd)) { + $classnames[] = $cmd; + } + if ($RCMAIL->action == $cmd) { + $classnames[] = $attrib['selclass']; + } + $attr['class'] = join(' ', $classnames); + $tabs[] = html::tag($tagname, $attr, $button, html::$common_attrib); + } + + return join('', $tabs); +} + // register UI objects $OUTPUT->add_handlers(array( + 'settingstabs' => 'rcmail_settings_tabs', 'prefsframe' => 'rcmail_preferences_frame', 'sectionslist' => 'rcmail_sections_list', 'identitieslist' => 'rcmail_identities_list', diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc index 3e8b1d17e..717c7ad8c 100644 --- a/program/steps/settings/save_prefs.inc +++ b/program/steps/settings/save_prefs.inc @@ -89,6 +89,7 @@ switch ($CURR_SECTION) 'reply_mode' => isset($_POST['_reply_mode']) ? intval($_POST['_reply_mode']) : 0, 'strip_existing_sig' => isset($_POST['_strip_existing_sig']), 'default_font' => get_input_value('_default_font', RCUBE_INPUT_POST), + 'default_font_size' => get_input_value('_default_font_size', RCUBE_INPUT_POST), 'forward_attachment' => !empty($_POST['_forward_attachment']), ); diff --git a/program/steps/utils/save_pref.inc b/program/steps/utils/save_pref.inc index 7def8733d..7c30be71b 100644 --- a/program/steps/utils/save_pref.inc +++ b/program/steps/utils/save_pref.inc @@ -5,7 +5,7 @@ | program/steps/utils/save_pref.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2010, The Roundcube Dev Team | + | 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. | @@ -19,16 +19,26 @@ +-----------------------------------------------------------------------+ */ -$name = get_input_value('_name', RCUBE_INPUT_POST); -$value = get_input_value('_value', RCUBE_INPUT_POST); +$name = get_input_value('_name', RCUBE_INPUT_POST); +$value = get_input_value('_value', RCUBE_INPUT_POST); +$sessname = get_input_value('_session', RCUBE_INPUT_POST); + +// Whitelisted preferences and session variables, others +// can be added by plugins $whitelist = array( 'preview_pane', 'list_cols', 'collapsed_folders', 'collapsed_abooks', ); +$whitelist_sess = array( + 'list_attrib/columns', +); + +$whitelist = array_merge($whitelist, $RCMAIL->plugins->allowed_prefs); +$whitelist_sess = array_merge($whitelist_sess, $RCMAIL->plugins->allowed_session_prefs); -if (!in_array($name, array_merge($whitelist, $RCMAIL->plugins->allowed_prefs))) { +if (!in_array($name, $whitelist) || ($sessname && !in_array($sessname, $whitelist_sess))) { raise_error(array('code' => 500, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, 'message' => sprintf("Hack attempt detected (user: %s)", $RCMAIL->get_user_name())), @@ -42,7 +52,7 @@ if (!in_array($name, array_merge($whitelist, $RCMAIL->plugins->allowed_prefs))) $RCMAIL->user->save_prefs(array($name => $value)); // update also session if requested -if ($sessname = get_input_value('_session', RCUBE_INPUT_POST)) { +if ($sessname) { // Support multidimensional arrays... $vars = explode('/', $sessname); @@ -57,5 +67,3 @@ if ($sessname = get_input_value('_session', RCUBE_INPUT_POST)) { $OUTPUT->reset(); $OUTPUT->send(); - - diff --git a/program/steps/utils/spell.inc b/program/steps/utils/spell.inc index 1c68e8328..595cfd6f2 100644 --- a/program/steps/utils/spell.inc +++ b/program/steps/utils/spell.inc @@ -47,6 +47,9 @@ if ($err = $spellchecker->error()) { 'file' => __FILE__, 'line' => __LINE__, 'message' => "Spell check engine error: " . trim($err)), true, false); + + header("HTTP/1.0 500 Internal Server Error"); + exit; } // set response length |