diff options
Diffstat (limited to 'program/steps')
26 files changed, 1694 insertions, 1193 deletions
diff --git a/program/steps/addressbook/export.inc b/program/steps/addressbook/export.inc index 15bf8b0d4..761f26b75 100644 --- a/program/steps/addressbook/export.inc +++ b/program/steps/addressbook/export.inc @@ -138,7 +138,9 @@ header('Content-Disposition: attachment; filename="contacts.vcf"'); while ($result && ($row = $result->next())) { // we already have a vcard record if ($row['vcard'] && $row['name']) { - $row['vcard'] = preg_replace('/\r?\n/', rcube_vcard::$eol, $row['vcard']); + // 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 diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index 60a19845e..c7f7fb479 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -26,7 +26,7 @@ $CONTACT_COLTYPES = array( 'name' => array('type' => 'text', 'size' => 40, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('name'), 'category' => 'main'), 'firstname' => array('type' => 'text', 'size' => 19, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('firstname'), 'category' => 'main'), 'surname' => array('type' => 'text', 'size' => 19, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('surname'), 'category' => 'main'), - 'email' => array('type' => 'text', 'size' => 40, 'maxlength' => 50, 'label' => rcube_label('email'), 'subtypes' => array('home','work','other'), 'category' => 'main'), + 'email' => array('type' => 'text', 'size' => 40, 'maxlength' => 254, 'label' => rcube_label('email'), 'subtypes' => array('home','work','other'), 'category' => 'main'), 'middlename' => array('type' => 'text', 'size' => 19, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('middlename'), 'category' => 'main'), 'prefix' => array('type' => 'text', 'size' => 8, 'maxlength' => 20, 'limit' => 1, 'label' => rcube_label('nameprefix'), 'category' => 'main'), 'suffix' => array('type' => 'text', 'size' => 8, 'maxlength' => 20, 'limit' => 1, 'label' => rcube_label('namesuffix'), 'category' => 'main'), @@ -167,7 +167,7 @@ function rcmail_set_sourcename($abook) // 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 @@ function rcmail_directory_list($attrib) $attrib['id'] = 'rcmdirectorylist'; $out = ''; - $local_id = '0'; $jsdata = array(); $line_templ = html::tag('li', array( @@ -268,8 +267,8 @@ function rcmail_contact_groups($args) { global $RCMAIL; + $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( @@ -282,8 +281,6 @@ function rcmail_contact_groups($args) $is_collapsed = strpos($RCMAIL->config->get('collapsed_abooks',''), '&'.rawurlencode($args['source']).'&') !== false; $args['out'] .= html::div('treetoggle ' . ($is_collapsed ? 'collapsed' : 'expanded'), ' '); - $jsdata = array(); - $groups_html = ''; foreach ($groups as $group) { $groups_html .= sprintf($line_templ, rcube_utils::html_identifier('G' . $args['source'] . $group['ID'], true), @@ -297,7 +294,7 @@ function rcmail_contact_groups($args) } $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; @@ -596,22 +593,13 @@ function rcmail_contact_form($form, $record, $attrib = null) // iterate over possible subtypes and collect values with their subtype if (is_array($colprop['subtypes'])) { $values = $subtypes = array(); - foreach ($colprop['subtypes'] as $i => $st) { - $newval = false; - if ($record[$field.':'.$st]) { - $subtypes[count($values)] = $st; - $newval = $record[$field.':'.$st]; - } - else if ($i == 0 && $record[$field]) { - $subtypes[count($values)] = $st; - $newval = $record[$field]; - } - if ($newval !== false) { - if (is_array($newval) && isset($newval[0])) - $values = array_merge($values, $newval); - else - $values[] = $newval; + foreach (rcube_addressbook::get_col_values($field, $record) as $st => $vals) { + foreach((array)$vals as $value) { + $i = count($values); + $subtypes[$i] = $st; + $values[$i] = $value; } + // TODO: add $st to $select_subtype if missing ? } } else { @@ -814,20 +802,27 @@ function rcmail_get_cids($filter = null) return array(); } - $cid = explode(',', $cid); - $result = array(); + $cid = explode(',', $cid); + $got_source = strlen($source); + $result = array(); // create per-source contact IDs array foreach ($cid as $id) { - // get source from decoded ID - if ($sep = strrpos($id, '-')) { - $contact_id = substr($id, 0, $sep); - $source_id = substr($id, $sep+1); - if (strlen($source_id)) { - $result[(string)$source_id][] = $contact_id; + // extract source ID from contact ID (it's there in search mode) + // see #1488959 and #1488862 for reference + if (!$got_source) { + if ($sep = strrpos($id, '-')) { + $contact_id = substr($id, 0, $sep); + $source_id = (string) substr($id, $sep+1); + if (strlen($source_id)) { + $result[$source_id][] = $contact_id; + } } } else { + if (substr($id, -($got_source+1)) === "-$source") { + $id = substr($id, 0, -($got_source+1)); + } $result[$source][] = $id; } } diff --git a/program/steps/addressbook/import.inc b/program/steps/addressbook/import.inc index df07d64bc..915aac884 100644 --- a/program/steps/addressbook/import.inc +++ b/program/steps/addressbook/import.inc @@ -88,7 +88,7 @@ function rcmail_import_confirm($attrib) $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 @@ function rcmail_import_confirm($attrib) 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))); @@ -209,6 +209,15 @@ if (is_array($_FILES['_file'])) { foreach ($vcards as $vcard) { $a_record = $vcard->get_assoc(); + // Generate contact's display name (must be before validation), the same we do in save.inc + 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]) { + $a_record['name'] = ''; + } + } + // skip invalid (incomplete) entries if (!$CONTACTS->validate($a_record, true)) { $IMPORT_STATS->invalid++; @@ -250,7 +259,7 @@ if (is_array($_FILES['_file'])) { if ($success) { $IMPORT_STATS->inserted++; - $IMPORT_STATS->names[] = $vcard->displayname ? $vcard->displayname : $email; + $IMPORT_STATS->names[] = $a_record['name'] ? $a_record['name'] : $email; } else { $IMPORT_STATS->errors++; diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc index 7bd7da302..e7e5efc63 100644 --- a/program/steps/addressbook/save.inc +++ b/program/steps/addressbook/save.inc @@ -82,7 +82,7 @@ if (empty($a_record['name'])) { // do input checks (delegated to $CONTACTS instance) if (!$CONTACTS->validate($a_record)) { $err = (array)$CONTACTS->get_error(); - $OUTPUT->show_message($err['message'] ? $err['message'] : 'formincomplete', 'warning'); + $OUTPUT->show_message($err['message'] ? Q($err['message']) : 'formincomplete', 'warning'); $GLOBALS['EDIT_RECORD'] = $a_record; // store submitted data to be used in edit form rcmail_overwrite_action($return_action); return; @@ -192,7 +192,7 @@ else { if (($maxnum = $RCMAIL->config->get('max_group_members', 0)) && ($counts->count + 1 > $maxnum)) $OUTPUT->show_message('maxgroupmembersreached', 'warning', array('max' => $maxnum)); - $CONTACTS->add_to_group($gid, $plugin['ids']); + $CONTACTS->add_to_group($plugin['group_id'], $plugin['ids']); } } else diff --git a/program/steps/addressbook/show.inc b/program/steps/addressbook/show.inc index b5870f463..63abc8c4b 100644 --- a/program/steps/addressbook/show.inc +++ b/program/steps/addressbook/show.inc @@ -101,8 +101,6 @@ function rcmail_contact_head($attrib) return false; } - $microformats = array('name' => 'fn', 'email' => 'email'); - $form = array( 'head' => array( // section 'head' is magic! 'content' => array( @@ -177,7 +175,7 @@ function rcmail_contact_details($attrib) } -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_email_value($email, $col) } -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( @@ -226,7 +224,7 @@ function rcmail_contact_record_groups($contact_id) } $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/attachments.inc b/program/steps/mail/attachments.inc index 180fc0bb9..f83f6892e 100644 --- a/program/steps/mail/attachments.inc +++ b/program/steps/mail/attachments.inc @@ -27,8 +27,10 @@ if (!empty($_GET['_progress'])) { $COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GPC); $COMPOSE = null; -if ($COMPOSE_ID && $_SESSION['compose_data_'.$COMPOSE_ID]) - $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID]; +if ($COMPOSE_ID && $_SESSION['compose_data_' . $COMPOSE_ID]) { + $SESSION_KEY = 'compose_data_' . $COMPOSE_ID; + $COMPOSE =& $_SESSION[$SESSION_KEY]; +} if (!$COMPOSE) { die("Invalid session var!"); @@ -45,7 +47,7 @@ if ($RCMAIL->action=='remove-attachment') $attachment = $RCMAIL->plugins->exec_hook('attachment_delete', $attachment); if ($attachment['status']) { if (is_array($COMPOSE['attachments'][$id])) { - unset($COMPOSE['attachments'][$id]); + $RCMAIL->session->remove($SESSION_KEY.'.attachments.'.$id); $OUTPUT->command('remove_from_attachment_list', "rcmfile$id"); } } @@ -77,11 +79,7 @@ if ($RCMAIL->action=='display-attachment') exit; } -// attachment upload action - -if (!is_array($COMPOSE['attachments'])) { - $COMPOSE['attachments'] = array(); -} +/***** attachment upload action *****/ // clear all stored output properties (like scripts and env vars) $OUTPUT->reset(); @@ -112,7 +110,7 @@ if (is_array($_FILES['_attachments']['tmp_name'])) { // store new attachment in session unset($attachment['status'], $attachment['abort']); - $COMPOSE['attachments'][$id] = $attachment; + $RCMAIL->session->append($SESSION_KEY.'.attachments', $id, $attachment); if (($icon = $COMPOSE['deleteicon']) && is_file($icon)) { $button = html::img(array( diff --git a/program/steps/mail/autocomplete.inc b/program/steps/mail/autocomplete.inc index 55579814c..f9e8d71a4 100644 --- a/program/steps/mail/autocomplete.inc +++ b/program/steps/mail/autocomplete.inc @@ -102,7 +102,7 @@ if (!empty($book_types) && strlen($search)) { // 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/check_recent.inc b/program/steps/mail/check_recent.inc index 4befbf275..8c0b1ffc0 100644 --- a/program/steps/mail/check_recent.inc +++ b/program/steps/mail/check_recent.inc @@ -52,12 +52,12 @@ foreach ($a_mailboxes as $mbox_name) { } // Get mailbox status - $status = $RCMAIL->storage->folder_status($mbox_name); + $status = $RCMAIL->storage->folder_status($mbox_name, $diff); if ($status & 1) { // trigger plugin hook $RCMAIL->plugins->exec_hook('new_messages', - array('mailbox' => $mbox_name, 'is_current' => $is_current)); + array('mailbox' => $mbox_name, 'is_current' => $is_current, 'diff' => $diff)); } rcmail_send_unread_count($mbox_name, true, null, @@ -81,9 +81,10 @@ foreach ($a_mailboxes as $mbox_name) { if (empty($_GET['_list'])) continue; - // get overall message count; allow caching because rcube_storage::folder_status() did a refresh + // get overall message count; allow caching because rcube_storage::folder_status() + // did a refresh but only in list mode $list_mode = $RCMAIL->storage->get_threading() ? 'THREADS' : 'ALL'; - $all_count = $RCMAIL->storage->count($mbox_name, $list_mode, false, false); + $all_count = $RCMAIL->storage->count($mbox_name, $list_mode, $list_mode == 'THREADS', false); $page = $RCMAIL->storage->get_page(); $page_size = $RCMAIL->storage->get_pagesize(); @@ -115,4 +116,7 @@ foreach ($a_mailboxes as $mbox_name) { } } +// trigger refresh hook +$RCMAIL->plugins->exec_hook('refresh', array()); + $OUTPUT->send(); diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 74c6d5f29..d3bc7fe72 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -139,12 +139,11 @@ if (!empty($CONFIG['drafts_mbox'])) { } // set current mailbox in client environment $OUTPUT->set_env('mailbox', $RCMAIL->storage->get_folder()); -$OUTPUT->set_env('sig_above', $RCMAIL->config->get('sig_above', false)); $OUTPUT->set_env('top_posting', intval($RCMAIL->config->get('reply_mode')) > 0); $OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ','))); // default font for HTML editor -$font = rcube_fontdefs($RCMAIL->config->get('default_font', 'Verdana')); +$font = rcube_fontdefs($RCMAIL->config->get('default_font')); if ($font && !is_array($font)) { $OUTPUT->set_env('default_font', $font); } @@ -152,6 +151,7 @@ if ($font && !is_array($font)) { // get reference message and set compose mode if ($msg_uid = $COMPOSE['param']['draft_uid']) { $compose_mode = RCUBE_COMPOSE_DRAFT; + $OUTPUT->set_env('draft_id', $msg_uid); $RCMAIL->storage->set_folder($CONFIG['drafts_mbox']); } else if ($msg_uid = $COMPOSE['param']['reply_uid']) { @@ -183,9 +183,18 @@ $LINE_LENGTH = $RCMAIL->config->get('line_length', 72); if (!empty($msg_uid) && empty($COMPOSE['as_attachment'])) { - // similar as in program/steps/mail/show.inc - // re-set 'prefer_html' to have possibility to use html part for compose - $CONFIG['prefer_html'] = $CONFIG['prefer_html'] || $CONFIG['htmleditor'] || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT; + $mbox_name = $RCMAIL->storage->get_folder(); + + // set format before rcube_message construction + // use the same format as for the message view + if (isset($_SESSION['msg_formats'][$mbox_name.':'.$msg_uid])) { + $RCMAIL->config->set('prefer_html', $_SESSION['msg_formats'][$mbox_name.':'.$msg_uid]); + } + else { + $prefer_html = $CONFIG['prefer_html'] || $CONFIG['htmleditor'] || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT; + $RCMAIL->config->set('prefer_html', $prefer_html); + } + $MESSAGE = new rcube_message($msg_uid); // make sure message is marked as read @@ -211,9 +220,9 @@ if (!empty($msg_uid) && empty($COMPOSE['as_attachment'])) } } else if ($compose_mode == RCUBE_COMPOSE_DRAFT) { - if ($MESSAGE->headers->others['x-draft-info']) { + if ($draft_info = $MESSAGE->headers->get('x-draft-info')) { // get reply_uid/forward_uid to flag the original message when sending - $info = rcmail_draftinfo_decode($MESSAGE->headers->others['x-draft-info']); + $info = rcmail_draftinfo_decode($draft_info); if ($info['type'] == 'reply') $COMPOSE['reply_uid'] = $info['uid']; @@ -230,8 +239,8 @@ if (!empty($msg_uid) && empty($COMPOSE['as_attachment'])) } } - if ($MESSAGE->headers->in_reply_to) - $COMPOSE['reply_msgid'] = '<'.$MESSAGE->headers->in_reply_to.'>'; + if ($in_reply_to = $MESSAGE->headers->get('in-reply-to')) + $COMPOSE['reply_msgid'] = '<' . $in_reply_to . '>'; $COMPOSE['references'] = $MESSAGE->headers->references; } @@ -318,6 +327,20 @@ foreach ($parts as $header) { $fvalue .= $v; if ($v = $MESSAGE->headers->cc) $fvalue .= (!empty($fvalue) ? $separator : '') . $v; + // Use Sender header (#1489011) + if (($v = $MESSAGE->headers->get('Sender', false)) && strpos($v, '-bounces@') === 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))) { @@ -377,7 +400,7 @@ function rcmail_compose_headers($attrib) { global $MESSAGE; - list($form_start, $form_end) = get_form_tags($attrib); + list($form_start,) = get_form_tags($attrib); $out = ''; $part = strtolower($attrib['part']); @@ -441,7 +464,7 @@ function rcmail_compose_headers($attrib) 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'); @@ -452,7 +475,8 @@ function rcmail_compose_header_from($attrib) if (count($MESSAGE->identities)) { $a_signatures = array(); - $separator = $RCMAIL->config->get('sig_above') + $identities = array(); + $separator = intval($RCMAIL->config->get('reply_mode')) > 0 && ($compose_mode == RCUBE_COMPOSE_REPLY || $compose_mode == RCUBE_COMPOSE_FORWARD) ? '---' : '-- '; $field_attrib['onchange'] = JS_OBJECT_NAME.".change_identity(this)"; @@ -489,12 +513,21 @@ function rcmail_compose_header_from($attrib) $a_signatures[$identity_id]['text'] = $text; $a_signatures[$identity_id]['html'] = $html; } + + // add bcc and reply-to + if (!empty($sql_arr['reply-to'])) { + $identities[$identity_id]['replyto'] = $sql_arr['reply-to']; + } + if (!empty($sql_arr['bcc'])) { + $identities[$identity_id]['bcc'] = $sql_arr['bcc']; + } } $out = $select_from->show($MESSAGE->compose['from']); // add signatures to client $OUTPUT->set_env('signatures', $a_signatures); + $OUTPUT->set_env('identities', $identities); } // no identities, display text input field else { @@ -509,7 +542,7 @@ function rcmail_compose_header_from($attrib) function rcmail_compose_editor_mode() { - global $RCMAIL, $MESSAGE, $compose_mode; + global $RCMAIL, $compose_mode; static $useHtml; if ($useHtml !== null) @@ -521,13 +554,13 @@ function rcmail_compose_editor_mode() $useHtml = !empty($_POST['_is_html']); } else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) { - $useHtml = $MESSAGE->has_html_part(false, true); + $useHtml = rcmail_message_is_html(); } else if ($compose_mode == RCUBE_COMPOSE_REPLY) { - $useHtml = ($html_editor == 1 || ($html_editor >= 2 && $MESSAGE->has_html_part(false, true))); + $useHtml = ($html_editor == 1 || ($html_editor >= 2 && rcmail_message_is_html())); } else if ($compose_mode == RCUBE_COMPOSE_FORWARD) { - $useHtml = ($html_editor == 1 || ($html_editor == 3 && $MESSAGE->has_html_part(false, true))); + $useHtml = ($html_editor == 1 || ($html_editor == 3 && rcmail_message_is_html())); } else { $useHtml = ($html_editor == 1); @@ -536,10 +569,15 @@ function rcmail_compose_editor_mode() return $useHtml; } +function rcmail_message_is_html() +{ + global $RCMAIL, $MESSAGE; + return $RCMAIL->config->get('prefer_html') && ($MESSAGE instanceof rcube_message) && $MESSAGE->has_html_part(true); +} 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'])) { @@ -554,11 +592,10 @@ function rcmail_prepare_message_body() else if ($compose_mode == RCUBE_COMPOSE_FORWARD && $COMPOSE['as_attachment']) { $isHtml = rcmail_compose_editor_mode(); $body = ''; - if (empty($COMPOSE['attachments'])) - rcmail_write_forward_attachments(); + rcmail_write_forward_attachments(); } // reply/edit/draft/forward - else if ($compose_mode && ($compose_mode != RCUBE_COMPOSE_REPLY || $RCMAIL->config->get('reply_mode') != -1)) { + else if ($compose_mode && ($compose_mode != RCUBE_COMPOSE_REPLY || intval($RCMAIL->config->get('reply_mode')) != -1)) { $isHtml = rcmail_compose_editor_mode(); if (!empty($MESSAGE->parts)) { @@ -613,7 +650,7 @@ function rcmail_prepare_message_body() 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 @@ -650,6 +687,11 @@ function rcmail_compose_part_body($part, $isHtml = false) $body = rcmail_remove_signature($body); } } + + if ($part->ctype_parameters['format'] == 'flowed') { + $body = rcube_mime::unfold_flowed($body); + } + // add HTML formatting $body = rcmail_plain_body($body); if ($body) { @@ -670,9 +712,6 @@ function rcmail_compose_part_body($part, $isHtml = false) $txt = new rcube_html2text($body, false, true, $len); $body = $txt->get_text(); } - else if ($part->ctype_secondary == 'enriched') { - $body = rcube_enriched::to_html($body); - } else { if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed') { $body = rcube_mime::unfold_flowed($body); @@ -692,7 +731,7 @@ function rcmail_compose_part_body($part, $isHtml = false) 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']); @@ -876,8 +915,7 @@ function rcmail_create_forward_body($body, $bodyIsHtml) 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"; @@ -931,7 +969,7 @@ function rcmail_create_forward_body($body, $bodyIsHtml) function rcmail_create_draft_body($body, $bodyIsHtml) { - global $MESSAGE, $OUTPUT, $COMPOSE; + global $MESSAGE, $COMPOSE; /** * add attachments @@ -978,18 +1016,25 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml) { global $RCMAIL, $COMPOSE, $compose_mode; + $loaded_attachments = array(); + foreach ((array)$COMPOSE['attachments'] as $attachment) { + $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment; + } + $cid_map = $messages = array(); foreach ((array)$message->mime_parts as $pid => $part) { if ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename) { - if ($part->ctype_primary == 'message' || $part->ctype_primary == 'multipart') { + // skip parts that aren't valid attachments + if ($part->ctype_primary == 'multipart' || $part->mimetype == 'application/ms-tnef') { continue; } - if ($part->mimetype == 'application/ms-tnef') { + // skip message attachments in reply mode + if ($part->ctype_primary == 'message' && $compose_mode == RCUBE_COMPOSE_REPLY) { continue; } - // skip inline images when forwarding in plain text - if ($part->content_id && !$bodyIsHtml && $compose_mode == RCUBE_COMPOSE_FORWARD) { + // skip inline images when forwarding + if ($part->content_id && $part->disposition == 'inline' && $compose_mode == RCUBE_COMPOSE_FORWARD) { continue; } @@ -1005,7 +1050,8 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml) } } - if (!$skip && ($attachment = rcmail_save_attachment($message, $pid))) { + if (!$skip && (($attachment = $loaded_attachments[rcmail_attachment_name($part) . $part->mimetype]) + || ($attachment = rcmail_save_attachment($message, $pid)))) { $COMPOSE['attachments'][$attachment['id']] = $attachment; if ($bodyIsHtml && ($part->content_id || $part->content_location)) { $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s', @@ -1057,6 +1103,11 @@ function rcmail_write_forward_attachments() $curr_mem = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB $names = array(); + $loaded_attachments = array(); + foreach ((array)$COMPOSE['attachments'] as $attachment) { + $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment; + } + if ($COMPOSE['forward_uid'] == '*') { $index = $storage->index(null, rcmail_sort_column(), rcmail_sort_order()); $COMPOSE['forward_uid'] = $index->get(); @@ -1091,6 +1142,10 @@ function rcmail_write_forward_attachments() $data = $path = null; + if (!empty($loaded_attachments[$name . 'message/rfc822'])) { + continue; + } + // don't load too big attachments into memory if ($mem_limit > 0 && $message->size > $mem_limit - $curr_mem) { $temp_dir = unslashify($RCMAIL->config->get('temp_dir')); @@ -1184,10 +1239,11 @@ function rcmail_save_image($path, $mimetype='') // 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), @@ -1457,7 +1513,7 @@ function rcmail_editor_selector($attrib) $select->add(Q(rcube_label('plaintoggle')), 'plain'); return $select->show($useHtml ? 'html' : 'plain'); - +/* foreach ($choices as $value => $text) { $attrib['id'] = '_' . $value; $attrib['value'] = $value; @@ -1465,6 +1521,7 @@ function rcmail_editor_selector($attrib) } return $selector; +*/ } @@ -1558,7 +1615,7 @@ function rcmail_addressbook_list($attrib = array()) $class_name .= ' ' . $source['class_name']; $out .= sprintf($line_templ, - html_identifier($id), + html_identifier($id,true), $class_name, $source['id'], $js_id, (!empty($source['name']) ? $source['name'] : $id)); diff --git a/program/steps/mail/copy.inc b/program/steps/mail/copy.inc index a72378b0e..876657485 100644 --- a/program/steps/mail/copy.inc +++ b/program/steps/mail/copy.inc @@ -24,10 +24,10 @@ if (!$OUTPUT->ajax_call) return; // move messages -if (!empty($_POST['_uid']) && !empty($_POST['_target_mbox'])) { - $uids = get_input_value('_uid', RCUBE_INPUT_POST); +if (!empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) { + $uids = get_input_value('_uid', RCUBE_INPUT_POST); $target = get_input_value('_target_mbox', RCUBE_INPUT_POST, true); - $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true); + $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true); $copied = $RCMAIL->storage->copy_message($uids, $target, $mbox); @@ -47,7 +47,7 @@ if (!empty($_POST['_uid']) && !empty($_POST['_target_mbox'])) { } // unknown action or missing query param else { - exit; + $OUTPUT->show_message('internalerror', 'error'); } // send response diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 6b8879dcf..4483ec160 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -120,7 +120,7 @@ if (empty($RCMAIL->action) || $RCMAIL->action == 'list') { if (!$OUTPUT->ajax_call) $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash', 'movingmessage', 'copyingmessage', 'deletingmessage', 'markingmessage', - 'copy', 'move', 'quota'); + 'copy', 'move', 'quota', 'replyall', 'replylist', 'importwait'); $pagetitle = $RCMAIL->localize_foldername($RCMAIL->storage->mod_folder($mbox_name), true); $pagetitle = str_replace($delimiter, " \xC2\xBB ", $pagetitle); @@ -224,7 +224,7 @@ function rcmail_message_list($attrib) 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 @@ function rcmail_message_list($attrib) $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 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null $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 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null } // loop through message headers - foreach ($a_headers as $n => $header) { + foreach ($a_headers as $header) { if (empty($header)) continue; @@ -381,7 +379,6 @@ function rcmail_message_list_head($attrib, $a_show_cols) 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 @@ function rcmail_message_list_head($attrib, $a_show_cols) $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(); } @@ -707,7 +704,10 @@ function rcmail_print_body($part, $p = array()) + $p + array('safe' => false, 'plain' => false, 'inline_html' => true)); // convert html to text/plain - if ($data['type'] == 'html' && $data['plain']) { + if ($data['plain'] && ($data['type'] == 'html' || $data['type'] == 'enriched')) { + if ($data['type'] == 'enriched') { + $data['body'] = rcube_enriched::to_html($data['body']); + } $txt = new rcube_html2text($data['body'], false, true); $body = $txt->get_text(); $part->ctype_secondary = 'plain'; @@ -733,8 +733,13 @@ function rcmail_print_body($part, $p = array()) unset($data['body']); // plaintext postprocessing - if ($part->ctype_secondary == 'plain') - $body = rcmail_plain_body($body, $part->ctype_parameters['format'] == 'flowed'); + if ($part->ctype_secondary == 'plain') { + if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed') { + $body = rcube_mime::unfold_flowed($body); + } + + $body = rcmail_plain_body($body); + } // allow post-processing of the message body $data = $RCMAIL->plugins->exec_hook('message_part_after', @@ -748,16 +753,16 @@ function rcmail_print_body($part, $p = array()) * Handle links and citation marks in plain text message * * @param string Plain text string - * @param boolean Text uses format=flowed * * @return string Formatted HTML string */ -function rcmail_plain_body($body, $flowed=false) +function rcmail_plain_body($body) { global $RCMAIL; // make links and email-addresses clickable - $replacer = new rcmail_string_replacer; + $attribs = array('link_attribs' => array('rel' => 'noreferrer', 'target' => '_blank')); + $replacer = new rcmail_string_replacer($attribs); // search for patterns like links and e-mail addresses and replace with tokens $body = $replacer->replace($body); @@ -776,53 +781,17 @@ function rcmail_plain_body($body, $flowed=false) if ($q > $quote_level) { $body[$n] = $replacer->get_replacement($replacer->add( str_repeat('<blockquote>', $q - $quote_level))) . $body[$n]; + $last = $n; } else if ($q < $quote_level) { $body[$n] = $replacer->get_replacement($replacer->add( str_repeat('</blockquote>', $quote_level - $q))) . $body[$n]; - } - else if ($flowed) { - // previous line is flowed - if (isset($body[$last]) && $body[$n] - && $body[$last][strlen($body[$last])-1] == ' ') { - // merge lines - $body[$last] .= $body[$n]; - unset($body[$n]); - } - else { - $last = $n; - } + $last = $n; } } else { $q = 0; - if ($flowed) { - // sig separator - line is fixed - if ($body[$n] == '-- ') { - $last = $last_sig = $n; - } - else { - // remove space-stuffing - if ($body[$n][0] == ' ') - $body[$n] = substr($body[$n], 1); - - // previous line is flowed? - if (isset($body[$last]) && $body[$n] - && $last !== $last_sig - && $body[$last][strlen($body[$last])-1] == ' ' - ) { - $body[$last] .= $body[$n]; - unset($body[$n]); - } - else { - $last = $n; - } - } - if ($quote_level > 0) - $body[$last] = $replacer->get_replacement($replacer->add( - str_repeat('</blockquote>', $quote_level))) . $body[$last]; - } - else if ($quote_level > 0) + if ($quote_level > 0) $body[$n] = $replacer->get_replacement($replacer->add( str_repeat('</blockquote>', $quote_level))) . $body[$n]; } @@ -892,8 +861,8 @@ function rcmail_washtml_callback($tagname, $attrib, $content, $washtml) * return table with message headers */ 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 @@ -931,7 +900,7 @@ function rcmail_message_headers($attrib, $headers=null) $value = $headers[$hkey]; else if ($headers['others'][$hkey]) $value = $headers['others'][$hkey]; - else + else if (!$attrib['valueof']) continue; if (in_array($hkey, $exclude_headers)) @@ -1078,14 +1047,14 @@ function rcmail_message_body($attrib) $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)); } else if ($part->type == 'content') { - // unsapported + // unsupported (e.g. encrypted) if ($part->realtype) { - if ($part->realtype == 'multipart/encrypted') { + if ($part->realtype == 'multipart/encrypted' || $part->realtype == 'application/pkcs7-mime') { $out .= html::span('part-notice', rcube_label('encryptedmessage')); } continue; @@ -1182,16 +1151,15 @@ function rcmail_message_body($attrib) $show_link = array( 'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false), 'onclick' => sprintf( - 'return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)', + 'return %s.command(\'load-attachment\',\'%s\',this)', JS_OBJECT_NAME, - $attach_prop->mime_id, - $mimetype) + $attach_prop->mime_id) ); $out .= html::p('image-attachment', html::a($show_link + array('class' => 'image-link', 'style' => sprintf('width:%dpx', $thumbnail_size)), html::img(array( 'class' => 'image-thumbnail', - 'src' => $MESSAGE->get_part_url($attach_prop->mime_id, true) . '&_thumb=1', + 'src' => $MESSAGE->get_part_url($attach_prop->mime_id, 'image') . '&_thumb=1', 'title' => $attach_prop->filename, 'alt' => $attach_prop->filename, 'style' => sprintf('max-width:%dpx; max-height:%dpx', $thumbnail_size, $thumbnail_size), @@ -1211,7 +1179,7 @@ function rcmail_message_body($attrib) html::tag('legend', 'image-filename', Q($attach_prop->filename)) . html::p(array('align' => "center"), html::img(array( - 'src' => $MESSAGE->get_part_url($attach_prop->mime_id, true), + 'src' => $MESSAGE->get_part_url($attach_prop->mime_id, 'image'), 'title' => $attach_prop->filename, 'alt' => $attach_prop->filename, ))) @@ -1371,7 +1339,7 @@ function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null /** - * parse link attributes and set correct target + * parse link (a, link, area) attributes and set correct target */ function rcmail_alter_html_link($matches) { @@ -1380,9 +1348,9 @@ function rcmail_alter_html_link($matches) // 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 = $matches[1]; + $tag = strtolower($matches[1]); $attrib = parse_attrib_string($matches[2]); - $end = '>'; + $end = '>'; // Remove non-printable characters in URL (#1487805) if ($attrib['href']) @@ -1409,6 +1377,11 @@ function rcmail_alter_html_link($matches) $attrib['target'] = '_blank'; } + // Better security by adding rel="noreferrer" (#1484686) + if (($tag == 'a' || $tag == 'area') && $attrib['href'] && $attrib['href'][0] != '#') { + $attrib['rel'] = 'noreferrer'; + } + // allowed attributes for a|link|area tags $allow = array('href','name','target','onclick','id','class','style','title', 'rel','type','media','alt','coords','nohref','hreflang','shape'); @@ -1432,7 +1405,8 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, $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; @@ -1443,6 +1417,12 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, $name = $part['name']; $mailto = $part['mailto']; $string = $part['string']; + $valid = check_email($mailto, false); + + // phishing email prevention (#1488981), e.g. "valid@email.addr <phishing@email.addr>" + if (!$show_email && $valid && $name && $name != $mailto && strpos($name, '@')) { + $name = ''; + } // IDNA ASCII to Unicode if ($name == $mailto) @@ -1456,15 +1436,23 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, // for printing we display all addresses continue; } - else if (check_email($part['mailto'], false)) { + else if ($valid) { 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"), @@ -1489,7 +1477,7 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, if ($name) $address .= Q($name); if ($mailto) - $address .= (strlen($address) ? ' ' : '') . sprintf('<%s>', Q($mailto)); + $address = trim($address . ' ' . Q($name ? sprintf('<%s>', $mailto) : $mailto)); } $address = html::span('adr', $address); @@ -1528,11 +1516,11 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, /** * Wrap text to a given number of characters per line * but respect the mail quotation of replies messages (>). - * Finally add another quotation level by prpending the lines + * Finally add another quotation level by prepending the lines * with > * * @param string Text to wrap - * @param int The line width + * @param int The line width * @return string The wrapped text */ function rcmail_wrap_and_quote($text, $length = 72) @@ -1548,7 +1536,7 @@ function rcmail_wrap_and_quote($text, $length = 72) $line = '>' . rtrim($line); else if (mb_strlen($line) > $max) { $newline = ''; - foreach(explode("\n", rc_wordwrap($line, $length - 2)) as $l) { + foreach (explode("\n", rc_wordwrap($line, $length - 2)) as $l) { if (strlen($l)) $newline .= '> ' . $l . "\n"; else @@ -1717,8 +1705,7 @@ function rcmail_send_mdn($message, &$smtp_error) $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; } @@ -1799,9 +1786,12 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r // Try Return-Path if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) { foreach ($identities as $idx => $ident) { - if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) { - $from_idx = $idx; - break; + $ident = str_replace('@', '=', $ident['email_ascii']) . '@'; + foreach ((array)$return_path as $path) { + if (strpos($path, $ident) !== false) { + $from_idx = $idx; + break 2; + } } } } @@ -1836,8 +1826,7 @@ function rcmail_fix_mimetype($name) // application/pdf.A520491B_3BF7_494D_8855_7FAC2C6C0608 if (preg_match('/^application\/pdf.+/', $name)) $name = 'application/pdf'; - - // treat image/pjpeg as image/jpeg + // treat image/pjpeg (image/pjpg, image/jpg) as image/jpeg (#1489097) else if (preg_match('/^image\/p?jpe?g$/', $name)) $name = 'image/jpeg'; @@ -1854,7 +1843,7 @@ function rcmail_attachment_name($attachment, $display = false) $filename = rcube_label('htmlmessage'); } else { - $ext = rcube_mime::get_mime_extensions($attachment->mimetype); + $ext = (array) rcube_mime::get_mime_extensions($attachment->mimetype); $ext = array_shift($ext); $filename = rcube_label('messagepart') . ' ' . $attachment->mime_id; if ($ext) { @@ -1884,13 +1873,15 @@ function rcmail_search_filter($attrib) $attrib['onchange'] = JS_OBJECT_NAME.'.filter_mailbox(this.value)'; - /* - RFC3501 (6.4.4): 'ALL', 'RECENT', - 'ANSWERED', 'DELETED', 'FLAGGED', 'SEEN', - 'UNANSWERED', 'UNDELETED', 'UNFLAGGED', 'UNSEEN', - 'NEW', // = (RECENT UNSEEN) - 'OLD' // = NOT RECENT - */ + // Content-Type values of messages with attachments + // the same as in app.js:add_message_row() + $ctypes = array('application/', 'multipart/m', 'multipart/signed', 'multipart/report'); + + // Build search string of "with attachment" filter + $attachment = str_repeat(' OR', count($ctypes)-1); + foreach ($ctypes as $type) { + $attachment .= ' HEADER Content-Type ' . rcube_imap_generic::escape($type); + } $select_filter = new html_select($attrib); $select_filter->add(rcube_label('all'), 'ALL'); @@ -1901,6 +1892,7 @@ function rcmail_search_filter($attrib) $select_filter->add(rcube_label('deleted'), 'DELETED'); $select_filter->add(rcube_label('undeleted'), 'UNDELETED'); } + $select_filter->add(rcube_label('withattachment'), $attachment); $select_filter->add(rcube_label('priority').': '.rcube_label('highest'), 'HEADER X-PRIORITY 1'); $select_filter->add(rcube_label('priority').': '.rcube_label('high'), 'HEADER X-PRIORITY 2'); $select_filter->add(rcube_label('priority').': '.rcube_label('normal'), 'NOT HEADER X-PRIORITY 1 NOT HEADER X-PRIORITY 2 NOT HEADER X-PRIORITY 4 NOT HEADER X-PRIORITY 5'); @@ -1930,6 +1922,42 @@ function rcmail_message_error($uid=null) $RCMAIL->output->send('messageerror'); } +function rcmail_message_import_form($attrib = array()) +{ + global $OUTPUT; + + // set defaults + $attrib += array('id' => 'rcmImportform', 'buttons' => 'yes'); + + // Get filesize, enable upload progress bar + $max_filesize = rcube_upload_init(); + + $button = new html_inputfield(array('type' => 'button')); + $fileinput = new html_inputfield(array( + 'type' => 'file', + 'name' => '_file[]', + 'size' => $attrib['attachmentfieldsize'], + 'multiple' => 'multiple', + 'accept' => ".eml, .mbox, message/rfc822, text/*", + )); + + $out = html::div($attrib, + $OUTPUT->form_tag(array('id' => $attrib['id'].'Frm', 'method' => 'post', 'enctype' => 'multipart/form-data'), + html::tag('input', array('type' => 'hidden', 'name' => '_unlock', 'value' => '')) . + html::div(null, $fileinput->show()) . + html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize)))) . + (get_boolean($attrib['buttons']) ? html::div('buttons', + $button->show(rcube_label('close'), array('class' => 'button', 'onclick' => "$('#$attrib[id]').hide()")) . ' ' . + $button->show(rcube_label('upload'), array('class' => 'button mainaction', 'onclick' => JS_OBJECT_NAME . ".command('import-messages', this.form)")) + ) : '') + ) + ); + + $OUTPUT->add_gui_object('importform', $attrib['id'].'Frm'); + return $out; +} + + // register UI objects $OUTPUT->add_handlers(array( 'mailboxlist' => 'rcmail_mailbox_list', @@ -1943,6 +1971,7 @@ $OUTPUT->add_handlers(array( 'messagecontentframe' => 'rcmail_messagecontent_frame', 'messagepartframe' => 'rcmail_message_part_frame', 'messagepartcontrols' => 'rcmail_message_part_controls', + 'messageimportform' => 'rcmail_message_import_form', 'searchfilter' => 'rcmail_search_filter', 'searchform' => array($OUTPUT, 'search_form'), )); diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index fddb40ae8..372757720 100644 --- a/program/steps/mail/get.inc +++ b/program/steps/mail/get.inc @@ -22,7 +22,7 @@ // show loading page if (!empty($_GET['_preload'])) { - $url = preg_replace('/([&?]+)_preload=/', '\\1_embed=', $_SERVER['REQUEST_URI']); + $url = preg_replace('/([&?]+)_preload=/', '\\1_mimewarning=1&_embed=', $_SERVER['REQUEST_URI']); $message = rcube_label('loadingdata'); header('Content-Type: text/html; charset=' . RCMAIL_CHARSET); @@ -60,11 +60,12 @@ else if ($_GET['_thumb']) { $pid = get_input_value('_part', RCUBE_INPUT_GET); if ($part = $MESSAGE->mime_parts[$pid]) { $thumbnail_size = $RCMAIL->config->get('image_thumbnail_size', 240); - $temp_dir = $RCMAIL->config->get('temp_dir'); - list(,$ext) = explode('/', $part->mimetype); - $cache_basename = $temp_dir . '/' . md5($MESSAGE->headers->messageID . $part->mime_id . ':' . $RCMAIL->user->ID . ':' . $thumbnail_size); - $cache_file = $cache_basename . '.' . $ext; - $mimetype = $part->mimetype; + $temp_dir = $RCMAIL->config->get('temp_dir'); + list(,$ext) = explode('/', $part->mimetype); + $mimetype = $part->mimetype; + $file_ident = $MESSAGE->headers->messageID . ':' . $part->mime_id . ':' . $part->size . ':' . $part->mimetype; + $cache_basename = $temp_dir . '/' . md5($file_ident . ':' . $RCMAIL->user->ID . ':' . $thumbnail_size); + $cache_file = $cache_basename . '.' . $ext; // render thumbnail image if not done yet if (!is_file($cache_file)) { @@ -73,7 +74,7 @@ else if ($_GET['_thumb']) { fclose($fp); $image = new rcube_image($orig_name); - if ($imgtype = $image->resize($RCMAIL->config->get('image_thumbnail_size', 240), $cache_file, true)) { + if ($imgtype = $image->resize($thumbnail_size, $cache_file, true)) { $mimetype = 'image/' . $imgtype; unlink($orig_name); } @@ -94,9 +95,7 @@ else if ($_GET['_thumb']) { else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { if ($part = $MESSAGE->mime_parts[$pid]) { - $ctype_primary = strtolower($part->ctype_primary); - $ctype_secondary = strtolower($part->ctype_secondary); - $mimetype = sprintf('%s/%s', $ctype_primary, $ctype_secondary); + $mimetype = rcmail_fix_mimetype($part->mimetype); // allow post-processing of the message body $plugin = $RCMAIL->plugins->exec_hook('message_part_get', @@ -106,7 +105,7 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { exit; // overwrite modified vars from plugin - $mimetype = $plugin['mimetype']; + $mimetype = $plugin['mimetype']; $extensions = rcube_mime::get_mime_extensions($mimetype); if ($plugin['body']) @@ -118,10 +117,10 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { $file_extension = strtolower(pathinfo($part->filename, PATHINFO_EXTENSION)); // 1. compare filename suffix with expected suffix derived from mimetype - $valid = $file_extension && in_array($file_extension, (array)$extensions); + $valid = $file_extension && in_array($file_extension, (array)$extensions) || !empty($_REQUEST['_mimeclass']); // 2. detect the real mimetype of the attachment part and compare it with the stated mimetype and filename extension - if ($valid || !$file_extension || $mimetype == 'application/octet-stream') { + if ($valid || !$file_extension || $mimetype == 'application/octet-stream' || $mimetype == 'text/plain') { if ($part->body) // part body is already loaded $body = $part->body; else if ($part->size && $part->size < 1024*1024) // load the entire part if it's small enough @@ -133,6 +132,10 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { $real_mimetype = rcube_mime::file_content_type($body, $part->filename, $mimetype, true, true); list($real_ctype_primary, $real_ctype_secondary) = explode('/', $real_mimetype); + // accept text/plain with any extension + if ($real_mimetype == 'text/plain' && $real_mimetype == $mimetype) + $file_extension = 'txt'; + // ignore differences in text/* mimetypes. Filetype detection isn't very reliable here if ($real_ctype_primary == 'text' && strpos($mimetype, $real_ctype_primary) === 0) $real_mimetype = $mimetype; @@ -141,6 +144,10 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { $extensions = rcube_mime::get_mime_extensions($real_mimetype); $valid_extension = (!$file_extension || in_array($file_extension, (array)$extensions)); + // ignore filename extension if mimeclass matches (#1489029) + if (!empty($_REQUEST['_mimeclass']) && $real_ctype_primary == $_REQUEST['_mimeclass']) + $valid_extension = true; + // fix mimetype for images wrongly declared as octet-stream if ($mimetype == 'application/octet-stream' && strpos($real_mimetype, 'image/') === 0 && $valid_extension) $mimetype = $real_mimetype; @@ -153,19 +160,32 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { // show warning if validity checks failed if (!$valid) { - $OUTPUT = new rcmail_html_page(); - $OUTPUT->write(html::tag('html', null, html::tag('body', 'embed', - html::div(array('class' => 'rcmail-inline-message rcmail-inline-warning'), - rcube_label(array( - 'name' => 'attachmentvalidationerror', - 'vars' => array('expected' => "$mimetype (.$file_extension)", 'detected' => "$real_mimetype (.$extensions[0])") - )) . - html::p(array('class' => 'rcmail-inline-buttons'), - html::tag('button', - array('onclick' => "location.href='" . $RCMAIL->url(array_merge($_GET, array('_nocheck' => 1))) . "'"), - rcube_label('showanyway'))) - ) - ))); + // send blocked.gif for expected images + if (empty($_REQUEST['_mimewarning']) && strpos($mimetype, 'image/') === 0) { + // Do not cache. Failure might be the result of a misconfiguration, thus real content should be returned once fixed. + $OUTPUT->nocacheing_headers(); + header("Content-Type: image/gif"); + header("Content-Transfer-Encoding: binary"); + readfile(INSTALL_PATH . 'program/resources/blocked.gif'); + } + else { // html warning with a button to load the file anyway + $OUTPUT = new rcmail_html_page(); + $OUTPUT->write(html::tag('html', null, html::tag('body', 'embed', + html::div(array('class' => 'rcmail-inline-message rcmail-inline-warning'), + rcube_label(array( + 'name' => 'attachmentvalidationerror', + 'vars' => array( + 'expected' => $mimetype . ($file_extension ? "(.$file_extension)" : ''), + 'detected' => $real_mimetype . ($extensions[0] ? "(.$extensions[0])" : ''), + ) + )) . + html::p(array('class' => 'rcmail-inline-buttons'), + html::tag('button', + array('onclick' => "location.href='" . $RCMAIL->url(array_merge($_GET, array('_nocheck' => 1))) . "'"), + rcube_label('showanyway'))) + ) + ))); + } exit; } } @@ -195,7 +215,6 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { header("Content-Type: text/$ctype_secondary; charset=" . ($part->charset ? $part->charset : RCMAIL_CHARSET)); } else { - $mimetype = rcmail_fix_mimetype($mimetype); header("Content-Type: $mimetype"); header("Content-Transfer-Encoding: binary"); } diff --git a/program/steps/mail/import.inc b/program/steps/mail/import.inc new file mode 100644 index 000000000..f7e7a3eb8 --- /dev/null +++ b/program/steps/mail/import.inc @@ -0,0 +1,105 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/mail/import.inc | + | | + | This file is part 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. | + | | + | PURPOSE: | + | Save the uploaded file(s) as messages to the current IMAP folder | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ +*/ + +// clear all stored output properties (like scripts and env vars) +$OUTPUT->reset(); + +if (is_array($_FILES['_file'])) { + $imported = 0; + + foreach ((array)$_FILES['_file']['tmp_name'] as $i => $filepath) { + // Process uploaded file if there is no error + $err = $_FILES['_file']['error'][$i]; + + if (!$err) { + // check file content type first + list($mtype_primary,) = explode('/', rc_mime_content_type($filepath, $_FILES['_file']['name'][$i], $_FILES['_file']['type'][$i])); + if (!in_array($mtype_primary, array('text','message'))) { + $OUTPUT->show_message('importmessageerror', 'error'); + continue; + } + + // read the first few lines to detect header-like structure + $fp = fopen($filepath, 'r'); + do { $line = fgets($fp); } + while ($line !== false && trim($line) == ''); + + if (!preg_match('/^From\s+-/', $line) && !preg_match('/^[a-z-_]+:\s+.+/i', $line)) { + $OUTPUT->show_message('importmessageerror', 'error'); + continue; + } + + $message = $lastline = ''; + fseek($fp, 0); + while (($line = fgets($fp)) !== false) { + // importing mbox file, split by From - lines + if (preg_match('/^From\s+-/', $line) && $lastline == '') { + if (!empty($message)) { + if ($RCMAIL->storage->save_message(null, rtrim($message))) { + $imported++; + } + else { + rcube::raise_error("Failed to import message to " . $RCMAIL->storage->get_folder(), false, true); + } + $message = ''; + } + continue; + } + + $message .= $line; + $lastline = rtrim($line); + } + + if (!empty($message) && $RCMAIL->storage->save_message(null, rtrim($message))) { + $imported++; + } + } + + if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { + $msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array('size' => show_bytes(parse_bytes(ini_get('upload_max_filesize')))))); + } + else if ($err) { + $OUTPUT->show_message('fileuploaderror', 'error'); + } + } // end foreach + + if ($imported) { + $OUTPUT->show_message(rcube_label(array('name' => 'importmessagesuccess', 'nr' => $imported, 'vars' => array('nr' => $imported))), 'confirmation'); + $OUTPUT->command('command', 'list'); + } + else { + $OUTPUT->show_message('importmessageerror', 'error'); + } +} +else if ($_SERVER['REQUEST_METHOD'] == 'POST') { + // if filesize exceeds post_max_size then $_FILES array is empty, + // show filesizeerror instead of fileuploaderror + if ($maxsize = ini_get('post_max_size')) + $msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array('size' => show_bytes(parse_bytes($maxsize))))); + else + $msg = rcube_label('fileuploaderror'); + + $OUTPUT->command('display_message', $msg, 'error'); +} + +// send html page with JS calls as response +$OUTPUT->send('iframe'); + diff --git a/program/steps/mail/mark.inc b/program/steps/mail/mark.inc index c220fc5c4..dfc892ea1 100644 --- a/program/steps/mail/mark.inc +++ b/program/steps/mail/mark.inc @@ -113,7 +113,7 @@ if (($uids = get_input_value('_uid', RCUBE_INPUT_POST)) && ($flag = get_input_va $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($msg_count), $mbox); if ($threading) { - $count = get_input_value('_count', RCUBE_INPUT_POST); + $count = get_input_value('_count', RCUBE_INPUT_POST); } // add new rows from next page (if any) @@ -125,9 +125,9 @@ if (($uids = get_input_value('_uid', RCUBE_INPUT_POST)) && ($flag = get_input_va } } } - - $OUTPUT->send(); +} +else { + $OUTPUT->show_message('internalerror', 'error'); } -exit; - +$OUTPUT->send(); diff --git a/program/steps/mail/move_del.inc b/program/steps/mail/move_del.inc index 3e2252683..e21ba2c6b 100644 --- a/program/steps/mail/move_del.inc +++ b/program/steps/mail/move_del.inc @@ -29,10 +29,11 @@ $old_count = $RCMAIL->storage->count(NULL, $threading ? 'THREADS' : 'ALL'); $old_pages = ceil($old_count / $RCMAIL->storage->get_pagesize()); // move messages -if ($RCMAIL->action=='moveto' && !empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) { - $count = sizeof(explode(',', ($uids = get_input_value('_uid', RCUBE_INPUT_POST)))); +if ($RCMAIL->action == 'moveto' && !empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) { + $count = sizeof(explode(',', ($uids = get_input_value('_uid', RCUBE_INPUT_POST)))); $target = get_input_value('_target_mbox', RCUBE_INPUT_POST, true); - $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true); + $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true); + $trash = $RCMAIL->config->get('trash_mbox'); $moved = $RCMAIL->storage->move_message($uids, $target, $mbox); @@ -40,7 +41,7 @@ if ($RCMAIL->action=='moveto' && !empty($_POST['_uid']) && strlen($_POST['_targe // send error message if ($_POST['_from'] != 'show') $OUTPUT->command('list_mailbox'); - rcmail_display_server_error('errormoving'); + rcmail_display_server_error('errormoving', null, $target == $trash ? 'delete' : ''); $OUTPUT->send(); exit; } @@ -73,6 +74,8 @@ else if ($RCMAIL->action=='delete' && !empty($_POST['_uid'])) { } // unknown action or missing query param else { + $OUTPUT->show_message('internalerror', 'error'); + $OUTPUT->send(); exit; } diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index eb0ba89c6..cf22a2af9 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -255,6 +255,33 @@ function rcmail_email_input_format($mailto, $count=false, $check=true) } +function rcmail_generic_message_footer($isHtml) +{ + global $CONFIG; + + if ($isHtml && !empty($CONFIG['generic_message_footer_html'])) { + $file = $CONFIG['generic_message_footer_html']; + $html_footer = true; + } + else { + $file = $CONFIG['generic_message_footer']; + $html_footer = false; + } + + if ($file && realpath($file)) { + // sanity check + if (!preg_match('/\.(php|ini|conf)$/', $file) && strpos($file, '/etc/') === false) { + $footer = file_get_contents($file); + if ($isHtml && !$html_footer) + $footer = '<pre>' . $footer . '</pre>'; + return $footer; + } + } + + return false; +} + + /****** compose message ********/ if (strlen($_POST['_draft_saveid']) > 3) @@ -364,10 +391,6 @@ if (!empty($mailcc)) { if (!empty($mailbcc)) { $headers['Bcc'] = $mailbcc; } -if (!empty($identity_arr['bcc']) && stripos($headers['Bcc'], $identity_arr['bcc']) === false) { - $headers['Bcc'] = ($headers['Bcc'] ? $headers['Bcc'].', ' : '') . $identity_arr['bcc']; - $RECIPIENT_COUNT ++; -} if (($max_recipients = (int) $RCMAIL->config->get('max_recipients')) > 0) { if ($RECIPIENT_COUNT > $max_recipients) { @@ -385,9 +408,6 @@ if (!empty($identity_arr['organization'])) { if (!empty($_POST['_replyto'])) { $headers['Reply-To'] = rcmail_email_input_format(get_input_value('_replyto', RCUBE_INPUT_POST, TRUE, $message_charset)); } -else if (!empty($identity_arr['reply-to'])) { - $headers['Reply-To'] = rcmail_email_input_format($identity_arr['reply-to'], false, true); -} if (!empty($headers['Reply-To'])) { $headers['Mail-Reply-To'] = $headers['Reply-To']; } @@ -452,7 +472,7 @@ $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', 'Verdana')); + $font = rcube_fontdefs($RCMAIL->config->get('default_font')); $bstyle = $font && is_string($font) ? " style='font-family: $font'" : ''; // append doctype and html/body wrappers @@ -466,7 +486,7 @@ if (!$savedraft) { $message_body = preg_replace('/\s*id="_rc_sig"/', '', $message_body); // add inline css for blockquotes - $bstyle = 'padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px; width:100%'; + $bstyle = 'padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px'; $message_body = preg_replace('/<blockquote>/', '<blockquote type="cite" style="'.$bstyle.'">', $message_body); } @@ -490,19 +510,10 @@ if (!$savedraft) { } // generic footer for all messages - if ($isHtml && !empty($CONFIG['generic_message_footer_html'])) { - $footer = file_get_contents(realpath($CONFIG['generic_message_footer_html'])); - $footer = rcube_charset_convert($footer, RCMAIL_CHARSET, $message_charset); - } - else if (!empty($CONFIG['generic_message_footer'])) { - $footer = file_get_contents(realpath($CONFIG['generic_message_footer'])); + if ($footer = rcmail_generic_message_footer($isHtml)) { $footer = rcube_charset_convert($footer, RCMAIL_CHARSET, $message_charset); - if ($isHtml) - $footer = '<pre>'.$footer.'</pre>'; - } - - if ($footer) $message_body .= "\r\n" . $footer; + } } if ($isHtml) { @@ -820,6 +831,6 @@ else { if ($store_folder && !$saved) $OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent')); else - $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent')); + $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'), $store_target); $OUTPUT->send('iframe'); } diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc index 3495df9c0..59f4d55e1 100644 --- a/program/steps/mail/show.inc +++ b/program/steps/mail/show.inc @@ -19,7 +19,7 @@ +-----------------------------------------------------------------------+ */ -$PRINT_MODE = $RCMAIL->action=='print' ? TRUE : FALSE; +$PRINT_MODE = $RCMAIL->action == 'print' ? TRUE : FALSE; // Read browser capabilities and store them in session if ($caps = get_input_value('_caps', RCUBE_INPUT_GET)) { @@ -31,8 +31,21 @@ if ($caps = get_input_value('_caps', RCUBE_INPUT_GET)) { $_SESSION['browser_caps'] = $browser_caps; } +$uid = get_input_value('_uid', RCUBE_INPUT_GET); +$mbox_name = $RCMAIL->storage->get_folder(); + // similar code as in program/steps/mail/get.inc -if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) { +if ($uid) { + // set message format (need to be done before rcube_message construction) + if (!empty($_GET['_format'])) { + $prefer_html = $_GET['_format'] == 'html'; + $RCMAIL->config->set('prefer_html', $prefer_html); + $_SESSION['msg_formats'][$mbox_name.':'.$uid] = $prefer_html; + } + else if (isset($_SESSION['msg_formats'][$mbox_name.':'.$uid])) { + $RCMAIL->config->set('prefer_html', $_SESSION['msg_formats'][$mbox_name.':'.$uid]); + } + $MESSAGE = new rcube_message($uid); // if message not found (wrong UID)... @@ -40,7 +53,6 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) { rcmail_message_error($uid); } - $mbox_name = $RCMAIL->storage->get_folder(); // show images? rcmail_check_safe($MESSAGE); @@ -97,14 +109,19 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) { $OUTPUT->set_env('skip_deleted', true); if ($CONFIG['display_next']) $OUTPUT->set_env('display_next', true); - if ($MESSAGE->headers->others['list-post']) + if ($MESSAGE->headers->get('list-post', false)) $OUTPUT->set_env('list_post', true); if ($CONFIG['forward_attachment']) $OUTPUT->set_env('forward_attachment', true); if (!$OUTPUT->ajax_call) $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash', - 'movingmessage', 'deletingmessage', 'markingmessage'); + 'movingmessage', 'deletingmessage', 'markingmessage', 'replyall', 'replylist'); + + $prefer_html = $RCMAIL->config->get('prefer_html'); + if ($MESSAGE->has_html_part()) { + $OUTPUT->set_env('optional_format', $prefer_html ? 'text' : 'html'); + } // check for unset disposition notification if ($MESSAGE->headers->mdn_to @@ -147,6 +164,7 @@ function rcmail_message_attachments($attrib) global $PRINT_MODE, $MESSAGE, $RCMAIL; $out = $ol = ''; + $attachments = array(); if (sizeof($MESSAGE->attachments)) { foreach ($MESSAGE->attachments as $attach_prop) { @@ -158,28 +176,30 @@ function rcmail_message_attachments($attrib) } else { if (mb_strlen($filename) > 50) { + $title = $filename; $filename = abbreviate_string($filename, 50); - $title = $filename; } else { $title = ''; } - $ol .= html::tag('li', rcmail_filetype2classname($attach_prop->mimetype, $filename), - html::a(array( + $mimetype = rcmail_fix_mimetype($attach_prop->mimetype); + $class = rcmail_filetype2classname($mimetype, $filename); + $id = 'attach' . $attach_prop->mime_id; + $link = html::a(array( 'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false), - 'onclick' => sprintf( - 'return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)', - JS_OBJECT_NAME, - $attach_prop->mime_id, - rcmail_fix_mimetype($attach_prop->mimetype)), - 'title' => Q($title), - ), - Q($filename))); + 'onclick' => sprintf('return %s.command(\'load-attachment\',\'%s\',this)', + JS_OBJECT_NAME, $attach_prop->mime_id), + 'title' => Q($title), + ), Q($filename)); + $ol .= html::tag('li', array('class' => $class, 'id' => $id), $link); + + $attachments[$attach_prop->mime_id] = $mimetype; } } $out = html::tag('ul', $attrib, $ol, html::$common_attrib); + $RCMAIL->output->set_env('attachments', $attachments); } return $out; @@ -208,11 +228,11 @@ function rcmail_remote_objects_msg() 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) { @@ -255,9 +275,13 @@ function rcmail_contact_exists($email) if ($email) { // @TODO: search in all address books? $CONTACTS = $RCMAIL->get_address_book(-1, true); - $existing = $CONTACTS->search('email', $email, true, false); - if ($existing->count) - return true; + + if (is_object($CONTACTS)) { + $existing = $CONTACTS->search('email', $email, true, false); + if ($existing->count) { + return true; + } + } } return false; @@ -285,9 +309,9 @@ $OUTPUT->add_handlers(array( )); -if ($RCMAIL->action=='print' && $OUTPUT->template_exists('messageprint')) +if ($RCMAIL->action == 'print' && $OUTPUT->template_exists('messageprint')) $OUTPUT->send('messageprint', false); -else if ($RCMAIL->action=='preview' && $OUTPUT->template_exists('messagepreview')) +else if ($RCMAIL->action == 'preview' && $OUTPUT->template_exists('messagepreview')) $OUTPUT->send('messagepreview', false); else $OUTPUT->send('message', false); diff --git a/program/steps/settings/about.inc b/program/steps/settings/about.inc index 9b13402f1..0fdefddda 100644 --- a/program/steps/settings/about.inc +++ b/program/steps/settings/about.inc @@ -40,17 +40,28 @@ function rcmail_plugins_list($attrib) $attrib['id'] = 'rcmpluginlist'; $plugins = array_filter((array) $RCMAIL->config->get('plugins')); - $plugins = array_flip($plugins); + $plugin_info = array(); - foreach ($plugins as $name => $plugin) { - rcube_plugin_data($name, $plugins); + foreach ($plugins as $name) { + if ($info = $RCMAIL->plugins->get_info($name)) + $plugin_info[$name] = $info; } - if (empty($plugins)) { + // load info from required plugins, too + foreach ($plugin_info as $name => $info) { + if (is_array($info['required']) && !empty($info['required'])) { + foreach ($info['required'] as $req_name) { + if (!isset($plugin_info[$req_name]) && ($req_info = $RCMAIL->plugins->get_info($req_name))) + $plugin_info[$req_name] = $req_info; + } + } + } + + if (empty($plugin_info)) { return ''; } - ksort($plugins, SORT_LOCALE_STRING); + ksort($plugin_info, SORT_LOCALE_STRING); $table = new html_table($attrib); @@ -60,8 +71,8 @@ function rcmail_plugins_list($attrib) $table->add_header('license', rcube_label('license')); $table->add_header('source', rcube_label('source')); - foreach ($plugins as $name => $data) { - $uri = $data['srcuri'] ? $data['srcuri'] : $data['uri']; + foreach ($plugin_info as $name => $data) { + $uri = $data['src_uri'] ? $data['src_uri'] : $data['uri']; if ($uri && stripos($uri, 'http') !== 0) { $uri = 'http://' . $uri; } @@ -78,48 +89,6 @@ function rcmail_plugins_list($attrib) return $table->show(); } -function rcube_plugin_data($name, &$plugins = array()) -{ - // XPaths of plugin metadata elements - $metadata = array( - 'name' => 'string(//rc:package/rc:name)', - 'version' => 'string(//rc:package/rc:version/rc:release)', - 'license' => 'string(//rc:package/rc:license)', - 'license_uri' => 'string(//rc:package/rc:license/@uri)', - 'srcuri' => 'string(//rc:package/rc:srcuri)', - 'uri' => 'string(//rc:package/rc:uri)', - ); - - $package = INSTALL_PATH . "/plugins/$name/package.xml"; - if (file_exists($package) && ($file = file_get_contents($package))) { - $doc = new DOMDocument(); - $doc->loadXML($file); - $xpath = new DOMXPath($doc); - $xpath->registerNamespace('rc', "http://pear.php.net/dtd/package-2.0"); - $data = array(); - - foreach ($metadata as $key => $path) { - $data[$key] = $xpath->evaluate($path); - } - - $plugins[$name] = $data; - - // dependent required plugins (can be used, but not included in config) - $deps = $xpath->evaluate('//rc:package/rc:dependencies/rc:required/rc:package/rc:name'); - $cnt = $deps->length; - - for ($i=0; $i<$cnt; $i++) { - $dn = $deps->item($i)->nodeValue; - if (!array_key_exists($dn, $plugins)) { - rcube_plugin_data($dn, $plugins); - } - } - } - else { - unset($plugins[$name]); - } -} - $OUTPUT->set_pagetitle(rcube_label('about')); diff --git a/program/steps/settings/edit_folder.inc b/program/steps/settings/edit_folder.inc index cd2372790..fdb38e602 100644 --- a/program/steps/settings/edit_folder.inc +++ b/program/steps/settings/edit_folder.inc @@ -78,7 +78,7 @@ function rcmail_folder_form($attrib) // Location (name) if ($options['protected']) { - $foldername = Q(str_replace($delimiter, ' » ', rcmail_localize_folderpath($mbox_imap))); + $foldername = str_replace($delimiter, ' » ', Q(rcmail_localize_folderpath($mbox_imap))); } else if ($options['norename']) { $foldername = Q($folder); diff --git a/program/steps/settings/edit_prefs.inc b/program/steps/settings/edit_prefs.inc index 971ed60b6..468e4994d 100644 --- a/program/steps/settings/edit_prefs.inc +++ b/program/steps/settings/edit_prefs.inc @@ -40,7 +40,7 @@ function rcmail_user_prefs_form($attrib) $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 0c7d9063f..34a72dd95 100644 --- a/program/steps/settings/folders.inc +++ b/program/steps/settings/folders.inc @@ -283,7 +283,6 @@ function rcube_subscription_form($attrib) $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 @@ function rcmail_rename_folder($oldname, $newname) $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 3bcca21bf..3ee098ca8 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -19,927 +19,1175 @@ +-----------------------------------------------------------------------+ */ -if (!$OUTPUT->ajax_call) - $OUTPUT->set_pagetitle(rcube_label('preferences')); - +if (!$OUTPUT->ajax_call) { + $OUTPUT->set_pagetitle(rcube_label('preferences')); +} // similar function as /steps/settings/identities.inc::rcmail_identity_frame() function rcmail_preferences_frame($attrib) { - global $OUTPUT; + global $OUTPUT; - if (!$attrib['id']) - $attrib['id'] = 'rcmprefsframe'; + if (!$attrib['id']) { + $attrib['id'] = 'rcmprefsframe'; + } - return $OUTPUT->frame($attrib, true); + return $OUTPUT->frame($attrib, true); } function rcmail_sections_list($attrib) { - global $RCMAIL; + global $RCMAIL; - // add id to message list table if not specified - if (!strlen($attrib['id'])) - $attrib['id'] = 'rcmsectionslist'; + // add id to message list table if not specified + if (!strlen($attrib['id'])) { + $attrib['id'] = 'rcmsectionslist'; + } - list($list, $cols) = rcmail_user_prefs(); + list($list, $cols) = rcmail_user_prefs(); - // create XHTML table - $out = rcube_table_output($attrib, $list, $cols, 'id'); + // create XHTML table + $out = rcube_table_output($attrib, $list, $cols, 'id'); - // set client env - $RCMAIL->output->add_gui_object('sectionslist', $attrib['id']); - $RCMAIL->output->include_script('list.js'); + // set client env + $RCMAIL->output->add_gui_object('sectionslist', $attrib['id']); + $RCMAIL->output->include_script('list.js'); - return $out; + return $out; } function rcmail_identities_list($attrib) { - global $OUTPUT, $RCMAIL; + global $OUTPUT, $RCMAIL; - // add id to message list table if not specified - if (!strlen($attrib['id'])) - $attrib['id'] = 'rcmIdentitiesList'; + // add id to message list table if not specified + if (!strlen($attrib['id'])) { + $attrib['id'] = 'rcmIdentitiesList'; + } - // get identities list and define 'mail' column - $list = $RCMAIL->user->list_identities(); - foreach ($list as $idx => $row) - $list[$idx]['mail'] = trim($row['name'] . ' <' . rcube_idn_to_utf8($row['email']) .'>'); + // get identities list and define 'mail' column + $list = $RCMAIL->user->list_identities(); + foreach ($list as $idx => $row) { + $list[$idx]['mail'] = trim($row['name'] . ' <' . rcube_idn_to_utf8($row['email']) .'>'); + } - // get all identites from DB and define list of cols to be displayed - $plugin = $RCMAIL->plugins->exec_hook('identities_list', array( - 'list' => $list, - 'cols' => array('mail'))); + // get all identites from DB and define list of cols to be displayed + $plugin = $RCMAIL->plugins->exec_hook('identities_list', array( + 'list' => $list, + 'cols' => array('mail') + )); - // @TODO: use <UL> instead of <TABLE> for identities list - // create XHTML table - $out = rcube_table_output($attrib, $plugin['list'], $plugin['cols'], 'identity_id'); + // @TODO: use <UL> instead of <TABLE> for identities list + // create XHTML table + $out = rcube_table_output($attrib, $plugin['list'], $plugin['cols'], 'identity_id'); - // set client env - $OUTPUT->add_gui_object('identitieslist', $attrib['id']); + // set client env + $OUTPUT->add_gui_object('identitieslist', $attrib['id']); - return $out; + return $out; } // similar function as in /steps/addressbook/edit.inc function get_form_tags($attrib, $action, $id = null, $hidden = null) { - global $EDIT_FORM, $RCMAIL; - - $form_start = $form_end = ''; - - if (empty($EDIT_FORM)) { - $request_key = $action . (isset($id) ? '.'.$id : ''); - $form_start = $RCMAIL->output->request_form(array( - 'name' => 'form', - 'method' => 'post', - 'task' => $RCMAIL->task, - 'action' => $action, - 'request' => $request_key, - 'noclose' => true - ) + $attrib); - - if (is_array($hidden)) { - $hiddenfields = new html_hiddenfield($hidden); - $form_start .= $hiddenfields->show(); - } + global $EDIT_FORM, $RCMAIL; + + $form_start = $form_end = ''; + + if (empty($EDIT_FORM)) { + $request_key = $action . (isset($id) ? '.'.$id : ''); + $form_start = $RCMAIL->output->request_form(array( + 'name' => 'form', + 'method' => 'post', + 'task' => $RCMAIL->task, + 'action' => $action, + 'request' => $request_key, + 'noclose' => true + ) + $attrib); + + if (is_array($hidden)) { + $hiddenfields = new html_hiddenfield($hidden); + $form_start .= $hiddenfields->show(); + } - $form_end = !strlen($attrib['form']) ? '</form>' : ''; + $form_end = !strlen($attrib['form']) ? '</form>' : ''; - $EDIT_FORM = !empty($attrib['form']) ? $attrib['form'] : 'form'; - $RCMAIL->output->add_gui_object('editform', $EDIT_FORM); - } + $EDIT_FORM = !empty($attrib['form']) ? $attrib['form'] : 'form'; + $RCMAIL->output->add_gui_object('editform', $EDIT_FORM); + } - return array($form_start, $form_end); + return array($form_start, $form_end); } -function rcmail_user_prefs($current=null) +function rcmail_user_prefs($current = null) { - global $RCMAIL; - - $sections['general'] = array('id' => 'general', 'section' => rcube_label('uisettings')); - $sections['mailbox'] = array('id' => 'mailbox', 'section' => rcube_label('mailboxview')); - $sections['mailview'] = array('id' => 'mailview','section' => rcube_label('messagesdisplaying')); - $sections['compose'] = array('id' => 'compose', 'section' => rcube_label('messagescomposition')); - $sections['addressbook'] = array('id' => 'addressbook','section' => rcube_label('addressbook')); - $sections['folders'] = array('id' => 'folders', 'section' => rcube_label('specialfolders')); - $sections['server'] = array('id' => 'server', 'section' => rcube_label('serversettings')); - - // hook + define list cols - $plugin = $RCMAIL->plugins->exec_hook('preferences_sections_list', - array('list' => $sections, 'cols' => array('section'))); - - $sections = $plugin['list']; - - $config = $RCMAIL->config->all(); - $no_override = array_flip($RCMAIL->config->get('dont_override', array())); - - foreach ($sections as $idx => $sect) { - - if ($current && $sect['id'] != $current) - continue; - - $blocks = array(); - - switch ($sect['id']) { - // general - case 'general': - - $blocks = array( - 'main' => array('name' => Q(rcube_label('mainoptions'))), - ); - - // language selection - if (!isset($no_override['language'])) { - $a_lang = $RCMAIL->list_languages(); - asort($a_lang); - - $field_id = 'rcmfd_lang'; - $select_lang = new html_select(array('name' => '_language', 'id' => $field_id)); - $select_lang->add(array_values($a_lang), array_keys($a_lang)); - - $blocks['main']['options']['language'] = array( - 'title' => html::label($field_id, Q(rcube_label('language'))), - 'content' => $select_lang->show($RCMAIL->user->language), - ); - } - - // timezone selection - if (!isset($no_override['timezone'])) { - $field_id = 'rcmfd_timezone'; - $select_timezone = new html_select(array('name' => '_timezone', 'id' => $field_id)); - $select_timezone->add(rcube_label('autodetect'), 'auto'); - - $zones = array(); - foreach (DateTimeZone::listIdentifiers() as $i => $tzs) { - try { - $tz = new DateTimeZone($tzs); - $date = new DateTime('2012-12-21', $tz); - $offset = $date->format('Z') + 45000; - $sortkey = sprintf('%06d.%s', $offset, $tzs); - $zones[$sortkey] = array($tzs, $date->format('P')); - } - catch (Exception $e) {} - } - - ksort($zones); - foreach ($zones as $zone) { - list($tzs, $offset) = $zone; - $select_timezone->add('(GMT ' . $offset . ') ' . strtr($tzs, '_', ' '), $tzs); - } - - $blocks['main']['options']['timezone'] = array( - 'title' => html::label($field_id, Q(rcube_label('timezone'))), - 'content' => $select_timezone->show((string)$config['timezone']), - ); - } - - // date/time formatting - if (!isset($no_override['time_format'])) { - $reftime = mktime(7,30,0); - $field_id = 'rcmfd_time_format'; - $select_time = new html_select(array('name' => '_time_format', 'id' => $field_id)); - foreach ((array)$RCMAIL->config->get('time_formats', array('G:i', 'H:i', 'g:i a', 'h:i A')) as $choice) - $select_time->add(date($choice, $reftime), $choice); - - $blocks['main']['options']['time_format'] = array( - 'title' => html::label($field_id, Q(rcube_label('timeformat'))), - 'content' => $select_time->show($RCMAIL->config->get('time_format')), - ); - } - - if (!isset($no_override['date_format'])) { - $refdate = mktime(12,30,0,7,24); - $field_id = 'rcmfd_date_format'; - $select_date = new html_select(array('name' => '_date_format', 'id' => $field_id)); - foreach ((array)$RCMAIL->config->get('date_formats', array('Y-m-d','d-m-Y','Y/m/d','m/d/Y','d/m/Y','d.m.Y','j.n.Y')) as $choice) - $select_date->add(date($choice, $refdate), $choice); - - $blocks['main']['options']['date_format'] = array( - 'title' => html::label($field_id, Q(rcube_label('dateformat'))), - 'content' => $select_date->show($config['date_format']), - ); - } + global $RCMAIL; - // MM: Show checkbox for toggling 'pretty dates' - if (!isset($no_override['prettydate'])) { - $field_id = 'rcmfd_prettydate'; - $input_prettydate = new html_checkbox(array('name' => '_pretty_date', 'id' => $field_id, 'value' => 1)); + $sections['general'] = array('id' => 'general', 'section' => rcube_label('uisettings')); + $sections['mailbox'] = array('id' => 'mailbox', 'section' => rcube_label('mailboxview')); + $sections['mailview'] = array('id' => 'mailview','section' => rcube_label('messagesdisplaying')); + $sections['compose'] = array('id' => 'compose', 'section' => rcube_label('messagescomposition')); + $sections['addressbook'] = array('id' => 'addressbook','section' => rcube_label('addressbook')); + $sections['folders'] = array('id' => 'folders', 'section' => rcube_label('specialfolders')); + $sections['server'] = array('id' => 'server', 'section' => rcube_label('serversettings')); - $blocks['main']['options']['prettydate'] = array( - 'title' => html::label($field_id, Q(rcube_label('prettydate'))), - 'content' => $input_prettydate->show($config['prettydate']?1:0), - ); - } + // hook + define list cols + $plugin = $RCMAIL->plugins->exec_hook('preferences_sections_list', + array('list' => $sections, 'cols' => array('section'))); - if (!isset($no_override['refresh_interval'])) { - $field_id = 'rcmfd_refresh_interval'; - $select_refresh_interval = new html_select(array('name' => '_refresh_interval', 'id' => $field_id)); + $sections = $plugin['list']; + $config = $RCMAIL->config->all(); + $no_override = array_flip((array)$RCMAIL->config->get('dont_override')); - $select_refresh_interval->add(rcube_label('never'), 0); - foreach (array(1, 3, 5, 10, 15, 30, 60) as $min) { - if (!$config['min_refresh_interval'] || $config['min_refresh_interval'] <= $min * 60) { - $label = rcube_label(array('name' => 'everynminutes', 'vars' => array('n' => $min))); - $select_refresh_interval->add($label, $min); + foreach ($sections as $idx => $sect) { + if ($current && $sect['id'] != $current) { + continue; } - } - $blocks['main']['options']['refresh_interval'] = array( - 'title' => html::label($field_id, Q(rcube_label('refreshinterval'))), - 'content' => $select_refresh_interval->show($config['refresh_interval']/60), - ); - } - - // show drop-down for available skins - if (!isset($no_override['skin'])) { - $skins = rcmail_get_skins(); - - if (count($skins) > 1) { - $field_id = 'rcmfd_skin'; - $input_skin = new html_radiobutton(array('name'=>'_skin')); - - $blocks['skin'] = array('name' => Q(rcube_label('skin')),); - - foreach($skins as $skin) { - $thumbnail = "./skins/$skin/thumbnail.png"; - if (!is_file($thumbnail)) - $thumbnail = './program/resources/blank.gif'; - - $skinname = ucfirst($skin); - $author_link = $license_link = ''; - $meta = @json_decode(@file_get_contents("./skins/$skin/meta.json"), true); - if (is_array($meta) && $meta['name']) { - $skinname = $meta['name']; - $author_link = $meta['url'] ? html::a(array('href' => $meta['url'], 'target' => '_blank'), Q($meta['author'])) : Q($meta['author']); - $license_link = $meta['license-url'] ? html::a(array('href' => $meta['license-url'], 'target' => '_blank'), Q($meta['license'])) : Q($meta['license']); - } - - $blocks['skin']['options'][$skin]['content'] = html::label(array('class' => 'skinselection'), - html::span('skinitem', $input_skin->show($config['skin'], array('value' => $skin, 'id' => $field_id.$skin))) . - html::span('skinitem', html::img(array('src' => $thumbnail, 'class' => 'skinthumbnail', 'alt' => $skin, 'width' => 64, 'height' => 64))) . - html::span('skinitem', html::span('skinname', Q($skinname)) . html::br() . - html::span('skinauthor', $author_link ? 'by ' . $author_link : '') . html::br() . - html::span('skinlicense', $license_link ? rcube_label('license').': ' . $license_link : '')) - ); + $blocks = array(); + + switch ($sect['id']) { + + // general + case 'general': + $blocks = array( + 'main' => array('name' => Q(rcube_label('mainoptions'))), + 'skin' => array('name' => Q(rcube_label('skin'))), + 'browser' => array('name' => Q(rcube_label('browseroptions'))), + ); + + // language selection + if (!isset($no_override['language'])) { + if (!$current) { + continue 2; + } + + $a_lang = $RCMAIL->list_languages(); + asort($a_lang); + + $field_id = 'rcmfd_lang'; + $select = new html_select(array('name' => '_language', 'id' => $field_id)); + $select->add(array_values($a_lang), array_keys($a_lang)); + + $blocks['main']['options']['language'] = array( + 'title' => html::label($field_id, Q(rcube_label('language'))), + 'content' => $select->show($RCMAIL->user->language), + ); + } + + // timezone selection + if (!isset($no_override['timezone'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_timezone'; + $select = new html_select(array('name' => '_timezone', 'id' => $field_id)); + $select->add(rcube_label('autodetect'), 'auto'); + + $zones = array(); + foreach (DateTimeZone::listIdentifiers() as $i => $tzs) { + try { + $tz = new DateTimeZone($tzs); + $date = new DateTime('2012-12-21', $tz); + $offset = $date->format('Z') + 45000; + $sortkey = sprintf('%06d.%s', $offset, $tzs); + $zones[$sortkey] = array($tzs, $date->format('P')); + } + catch (Exception $e) {} + } + + ksort($zones); + + foreach ($zones as $zone) { + list($tzs, $offset) = $zone; + $select->add('(GMT ' . $offset . ') ' . strtr($tzs, '_', ' '), $tzs); + } + + $blocks['main']['options']['timezone'] = array( + 'title' => html::label($field_id, Q(rcube_label('timezone'))), + 'content' => $select->show((string)$config['timezone']), + ); + } + + // date/time formatting + if (!isset($no_override['time_format'])) { + if (!$current) { + continue 2; + } + + $reftime = mktime(7,30,0); + $defaults = array('G:i', 'H:i', 'g:i a', 'h:i A'); + $formats = (array)$RCMAIL->config->get('time_formats', $defaults); + $field_id = 'rcmfd_time_format'; + $select = new html_select(array('name' => '_time_format', 'id' => $field_id)); + + foreach ($formats as $choice) { + $select->add(date($choice, $reftime), $choice); + } + + $blocks['main']['options']['time_format'] = array( + 'title' => html::label($field_id, Q(rcube_label('timeformat'))), + 'content' => $select->show($RCMAIL->config->get('time_format')), + ); + } + + if (!isset($no_override['date_format'])) { + if (!$current) { + continue 2; + } + + $refdate = mktime(12,30,0,7,24); + $defaults = array('Y-m-d','d-m-Y','Y/m/d','m/d/Y','d/m/Y','d.m.Y','j.n.Y'); + $formats = (array)$RCMAIL->config->get('date_formats', $defaults); + $field_id = 'rcmfd_date_format'; + $select = new html_select(array('name' => '_date_format', 'id' => $field_id)); + + foreach ($formats as $choice) { + $select->add(date($choice, $refdate), $choice); + } + + $blocks['main']['options']['date_format'] = array( + 'title' => html::label($field_id, Q(rcube_label('dateformat'))), + 'content' => $select->show($config['date_format']), + ); + } + + // Show checkbox for toggling 'pretty dates' + if (!isset($no_override['prettydate'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_prettydate'; + $input = new html_checkbox(array('name' => '_pretty_date', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['prettydate'] = array( + 'title' => html::label($field_id, Q(rcube_label('prettydate'))), + 'content' => $input->show($config['prettydate']?1:0), + ); + } + + if (!isset($no_override['refresh_interval'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_refresh_interval'; + $select = new html_select(array('name' => '_refresh_interval', 'id' => $field_id)); + + $select->add(rcube_label('never'), 0); + foreach (array(1, 3, 5, 10, 15, 30, 60) as $min) { + if (!$config['min_refresh_interval'] || $config['min_refresh_interval'] <= $min * 60) { + $label = rcube_label(array('name' => 'everynminutes', 'vars' => array('n' => $min))); + $select->add($label, $min); + } + } + + $blocks['main']['options']['refresh_interval'] = array( + 'title' => html::label($field_id, Q(rcube_label('refreshinterval'))), + 'content' => $select->show($config['refresh_interval']/60), + ); + } + + // show drop-down for available skins + if (!isset($no_override['skin'])) { + if (!$current) { + continue 2; + } + + $skins = rcmail_get_skins(); + + if (count($skins) > 1) { + $field_id = 'rcmfd_skin'; + $input = new html_radiobutton(array('name'=>'_skin')); + + foreach ($skins as $skin) { + $thumbnail = "./skins/$skin/thumbnail.png"; + if (!is_file($thumbnail)) + $thumbnail = './program/resources/blank.gif'; + + $skinname = ucfirst($skin); + $author_link = $license_link = ''; + $meta = @json_decode(@file_get_contents("./skins/$skin/meta.json"), true); + + if (is_array($meta) && $meta['name']) { + $skinname = $meta['name']; + $author_link = $meta['url'] ? html::a(array('href' => $meta['url'], 'target' => '_blank'), Q($meta['author'])) : Q($meta['author']); + $license_link = $meta['license-url'] ? html::a(array('href' => $meta['license-url'], 'target' => '_blank'), Q($meta['license'])) : Q($meta['license']); + } + + $blocks['skin']['options'][$skin]['content'] = html::label(array('class' => 'skinselection'), + html::span('skinitem', $input->show($config['skin'], array('value' => $skin, 'id' => $field_id.$skin))) . + html::span('skinitem', html::img(array('src' => $thumbnail, 'class' => 'skinthumbnail', 'alt' => $skin, 'width' => 64, 'height' => 64))) . + html::span('skinitem', html::span('skinname', Q($skinname)) . html::br() . + html::span('skinauthor', $author_link ? 'by ' . $author_link : '') . html::br() . + html::span('skinlicense', $license_link ? rcube_label('license').': ' . $license_link : '')) + ); + } + } + } + + // standard_windows option decides if new windows should be + // opened as popups or standard windows (which can be handled by browsers as tabs) + if (!isset($no_override['standard_windows'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_standard_windows'; + $checkbox = new html_checkbox(array('name' => '_standard_windows', 'id' => $field_id, 'value' => 1)); + + $blocks['browser']['options']['standard_windows'] = array( + 'title' => html::label($field_id, Q(rcube_label('standardwindows'))), + 'content' => $checkbox->show($config['standard_windows']?1:0), + ); + } + + if ($current) { + $product_name = $RCMAIL->config->get('product_name', 'Roundcube Webmail'); + $RCMAIL->output->add_script(sprintf("%s.check_protocol_handler('%s', '#mailtoprotohandler');", + JS_OBJECT_NAME, JQ($product_name)), 'foot'); + } + + $blocks['browser']['options']['mailtoprotohandler'] = array( + 'content' => html::a(array( + 'href' => '#', + 'id' => 'mailtoprotohandler'), Q(rcube_label('mailtoprotohandler'))), + ); + + break; + + // Mailbox view (mail screen) + case 'mailbox': + $blocks = array( + 'main' => array('name' => Q(rcube_label('mainoptions'))), + 'new_message' => array('name' => Q(rcube_label('newmessage'))), + ); + + // show config parameter for preview pane + if (!isset($no_override['preview_pane'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_preview'; + $input = new html_checkbox(array('name' => '_preview_pane', 'id' => $field_id, 'value' => 1, + 'onchange' => "$('#rcmfd_preview_pane_mark_read').prop('disabled', !this.checked)")); + + $blocks['main']['options']['preview_pane'] = array( + 'title' => html::label($field_id, Q(rcube_label('previewpane'))), + 'content' => $input->show($config['preview_pane']?1:0), + ); + } + + // show config parameter for preview pane auto mark as read delay + if (!isset($no_override['preview_pane_mark_read'])) { + if (!$current) { + continue 2; + } + + // apply default if config option is not set at all + $config['preview_pane_mark_read'] = $RCMAIL->config->get('preview_pane_mark_read', 0); + + $field_id = 'rcmfd_preview_pane_mark_read'; + $select = new html_select(array('name' => '_preview_pane_mark_read', 'id' => $field_id, + 'disabled' => $config['preview_pane']?0:1)); + + $select->add(rcube_label('never'), '-1'); + $select->add(rcube_label('immediately'), 0); + + foreach (array(5, 10, 20, 30) as $sec) { + $label = rcube_label(array('name' => 'afternseconds', 'vars' => array('n' => $sec))); + $select->add($label, $sec); + } + + $blocks['main']['options']['preview_pane_mark_read'] = array( + 'title' => html::label($field_id, Q(rcube_label('previewpanemarkread'))), + 'content' => $select->show(intval($config['preview_pane_mark_read'])), + ); + } + + if (!isset($no_override['mdn_requests'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_mdn_requests'; + $select = new html_select(array('name' => '_mdn_requests', 'id' => $field_id)); + $select->add(rcube_label('askuser'), 0); + $select->add(rcube_label('autosend'), 1); + $select->add(rcube_label('autosendknown'), 3); + $select->add(rcube_label('autosendknownignore'), 4); + $select->add(rcube_label('ignore'), 2); + + $blocks['main']['options']['mdn_requests'] = array( + 'title' => html::label($field_id, Q(rcube_label('mdnrequests'))), + 'content' => $select->show($config['mdn_requests']), + ); + } + + if (!isset($no_override['autoexpand_threads'])) { + if (!$current) { + continue 2; + } + + $storage = $RCMAIL->get_storage(); + $supported = $storage->get_capability('THREAD'); + + if ($supported) { + $field_id = 'rcmfd_autoexpand_threads'; + $select = new html_select(array('name' => '_autoexpand_threads', 'id' => $field_id)); + $select->add(rcube_label('never'), 0); + $select->add(rcube_label('do_expand'), 1); + $select->add(rcube_label('expand_only_unread'), 2); + + $blocks['main']['options']['autoexpand_threads'] = array( + 'title' => html::label($field_id, Q(rcube_label('autoexpand_threads'))), + 'content' => $select->show($config['autoexpand_threads']), + ); + } + } + + // show page size selection + if (!isset($no_override['mail_pagesize'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_mail_pagesize'; + $input = new html_inputfield(array('name' => '_mail_pagesize', 'id' => $field_id, 'size' => 5)); + $size = intval($config['mail_pagesize'] ? $config['mail_pagesize'] : $config['pagesize']); + + $blocks['main']['options']['pagesize'] = array( + 'title' => html::label($field_id, Q(rcube_label('pagesize'))), + 'content' => $input->show($size ? $size : 50), + ); + } + + if (!isset($no_override['check_all_folders'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_check_all_folders'; + $input = new html_checkbox(array('name' => '_check_all_folders', 'id' => $field_id, 'value' => 1)); + + $blocks['new_message']['options']['check_all_folders'] = array( + 'title' => html::label($field_id, Q(rcube_label('checkallfolders'))), + 'content' => $input->show($config['check_all_folders']?1:0), + ); + } + break; + + // Message viewing + case 'mailview': + $blocks = array( + 'main' => array('name' => Q(rcube_label('mainoptions'))), + ); + + // show checkbox to open message view in new window + if (!isset($no_override['message_extwin'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_message_extwin'; + $input = new html_checkbox(array('name' => '_message_extwin', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['message_extwin'] = array( + 'title' => html::label($field_id, Q(rcube_label('showinextwin'))), + 'content' => $input->show($config['message_extwin']?1:0), + ); + } + + // show checkbox to show email instead of name + if (!isset($no_override['message_show_email'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_message_show_email'; + $input = 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->show($config['message_show_email']?1:0), + ); + } + + // show checkbox for HTML/plaintext messages + if (!isset($no_override['prefer_html'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_htmlmsg'; + $input = new html_checkbox(array('name' => '_prefer_html', 'id' => $field_id, 'value' => 1, + 'onchange' => "$('#rcmfd_show_images').prop('disabled', !this.checked).val(0)")); + + $blocks['main']['options']['prefer_html'] = array( + 'title' => html::label($field_id, Q(rcube_label('preferhtml'))), + 'content' => $input->show($config['prefer_html']?1:0), + ); + } + + if (!isset($no_override['default_charset'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_default_charset'; + + $blocks['main']['options']['default_charset'] = array( + 'title' => html::label($field_id, Q(rcube_label('defaultcharset'))), + 'content' => $RCMAIL->output->charset_selector(array( + 'name' => '_default_charset', 'selected' => $config['default_charset'] + ))); + } + + if (!isset($no_override['show_images'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_show_images'; + $input = new html_select(array('name' => '_show_images', 'id' => $field_id, + 'disabled' => !$config['prefer_html'])); + + $input->add(rcube_label('never'), 0); + $input->add(rcube_label('fromknownsenders'), 1); + $input->add(rcube_label('always'), 2); + + $blocks['main']['options']['show_images'] = array( + 'title' => html::label($field_id, Q(rcube_label('showremoteimages'))), + 'content' => $input->show($config['prefer_html'] ? $config['show_images'] : 0), + ); + } + + if (!isset($no_override['inline_images'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_inline_images'; + $input = new html_checkbox(array('name' => '_inline_images', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['inline_images'] = array( + 'title' => html::label($field_id, Q(rcube_label('showinlineimages'))), + 'content' => $input->show($config['inline_images']?1:0), + ); + } + + // "display after delete" checkbox + if (!isset($no_override['display_next'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_displaynext'; + $input = new html_checkbox(array('name' => '_display_next', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['display_next'] = array( + 'title' => html::label($field_id, Q(rcube_label('displaynext'))), + 'content' => $input->show($config['display_next']?1:0), + ); + } + break; + + // Mail composition + case 'compose': + $blocks = array( + 'main' => array('name' => Q(rcube_label('mainoptions'))), + 'sig' => array('name' => Q(rcube_label('signatureoptions'))), + 'spellcheck' => array('name' => Q(rcube_label('spellcheckoptions'))), + ); + + // show checkbox to compose messages in a new window + if (!isset($no_override['compose_extwin'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfdcompose_extwin'; + $input = new html_checkbox(array('name' => '_compose_extwin', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['compose_extwin'] = array( + 'title' => html::label($field_id, Q(rcube_label('composeextwin'))), + 'content' => $input->show($config['compose_extwin']?1:0), + ); + } + + if (!isset($no_override['htmleditor'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_htmleditor'; + $select = new html_select(array('name' => '_htmleditor', 'id' => $field_id)); + + $select->add(rcube_label('never'), 0); + $select->add(rcube_label('always'), 1); + $select->add(rcube_label('htmlonreply'), 2); + $select->add(rcube_label('htmlonreplyandforward'), 3); + + $blocks['main']['options']['htmleditor'] = array( + 'title' => html::label($field_id, Q(rcube_label('htmleditor'))), + 'content' => $select->show(intval($config['htmleditor'])), + ); + } + + if (!isset($no_override['draft_autosave'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_autosave'; + $select = new html_select(array('name' => '_draft_autosave', 'id' => $field_id, 'disabled' => empty($config['drafts_mbox']))); + + $select->add(rcube_label('never'), 0); + foreach (array(1, 3, 5, 10) as $i => $min) { + $label = rcube_label(array('name' => 'everynminutes', 'vars' => array('n' => $min))); + $select->add($label, $min*60); + } + + $blocks['main']['options']['draft_autosave'] = array( + 'title' => html::label($field_id, Q(rcube_label('autosavedraft'))), + 'content' => $select->show($config['draft_autosave']), + ); + } + + if (!isset($no_override['mime_param_folding'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_param_folding'; + $select = new html_select(array('name' => '_mime_param_folding', 'id' => $field_id)); + + $select->add(rcube_label('2231folding'), 0); + $select->add(rcube_label('miscfolding'), 1); + $select->add(rcube_label('2047folding'), 2); + + $blocks['main']['options']['mime_param_folding'] = array( + 'advanced' => true, + 'title' => html::label($field_id, Q(rcube_label('mimeparamfolding'))), + 'content' => $select->show($config['mime_param_folding']), + ); + } + + if (!isset($no_override['force_7bit'])) { + if (!$current) { + continue 2; + } + + $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, + 'title' => html::label($field_id, Q(rcube_label('force7bit'))), + 'content' => $input->show($config['force_7bit']?1:0), + ); + } + + if (!isset($no_override['mdn_default'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_mdn_default'; + $input = new html_checkbox(array('name' => '_mdn_default', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['mdn_default'] = array( + 'title' => html::label($field_id, Q(rcube_label('reqmdn'))), + 'content' => $input->show($config['mdn_default']?1:0), + ); + } + + if (!isset($no_override['dsn_default'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_dsn_default'; + $input = new html_checkbox(array('name' => '_dsn_default', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['dsn_default'] = array( + 'title' => html::label($field_id, Q(rcube_label('reqdsn'))), + 'content' => $input->show($config['dsn_default']?1:0), + ); + } + + if (!isset($no_override['reply_same_folder'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_reply_same_folder'; + $input = new html_checkbox(array('name' => '_reply_same_folder', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['reply_same_folder'] = array( + 'title' => html::label($field_id, Q(rcube_label('replysamefolder'))), + 'content' => $input->show($config['reply_same_folder']?1:0), + ); + } + + if (!isset($no_override['reply_mode'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_reply_mode'; + $select = new html_select(array('name' => '_reply_mode', 'id' => $field_id)); + + $select->add(rcube_label('replyempty'), -1); + $select->add(rcube_label('replybottomposting'), 0); + $select->add(rcube_label('replytopposting'), 1); + + $blocks['main']['options']['reply_mode'] = array( + 'title' => html::label($field_id, Q(rcube_label('whenreplying'))), + 'content' => $select->show(intval($config['reply_mode'])), + ); + } + + if (!isset($no_override['spellcheck_before_send']) && $config['enable_spellcheck']) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_spellcheck_before_send'; + $input = new html_checkbox(array('name' => '_spellcheck_before_send', 'id' => $field_id, 'value' => 1)); + + $blocks['spellcheck']['options']['spellcheck_before_send'] = array( + 'title' => html::label($field_id, Q(rcube_label('spellcheckbeforesend'))), + 'content' => $input->show($config['spellcheck_before_send']?1:0), + ); + } + + if ($config['enable_spellcheck']) { + if (!$current) { + continue 2; + } + + foreach (array('syms', 'nums', 'caps') as $key) { + $key = 'spellcheck_ignore_'.$key; + if (!isset($no_override[$key])) { + $input = new html_checkbox(array('name' => '_'.$key, 'id' => 'rcmfd_'.$key, 'value' => 1)); + + $blocks['spellcheck']['options'][$key] = array( + 'title' => html::label($field_id, Q(rcube_label(str_replace('_', '', $key)))), + 'content' => $input->show($config[$key]?1:0), + ); + } + } + } + + if (!isset($no_override['show_sig'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_show_sig'; + $select = new html_select(array('name' => '_show_sig', 'id' => $field_id)); + + $select->add(rcube_label('never'), 0); + $select->add(rcube_label('always'), 1); + $select->add(rcube_label('newmessageonly'), 2); + $select->add(rcube_label('replyandforwardonly'), 3); + + $blocks['sig']['options']['show_sig'] = array( + 'title' => html::label($field_id, Q(rcube_label('autoaddsignature'))), + 'content' => $select->show($RCMAIL->config->get('show_sig', 1)), + ); + } + + if (!isset($no_override['strip_existing_sig'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_strip_existing_sig'; + $input = new html_checkbox(array('name' => '_strip_existing_sig', 'id' => $field_id, 'value' => 1)); + + $blocks['sig']['options']['strip_existing_sig'] = array( + 'title' => html::label($field_id, Q(rcube_label('replyremovesignature'))), + 'content' => $input->show($config['strip_existing_sig']?1:0), + ); + } + + if (!isset($no_override['forward_attachment'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_forward_attachment'; + $select = new html_select(array('name' => '_forward_attachment', 'id' => $field_id)); + + $select->add(rcube_label('inline'), 0); + $select->add(rcube_label('asattachment'), 1); + + $blocks['main']['options']['forward_attachment'] = array( + 'title' => html::label($field_id, Q(rcube_label('forwardmode'))), + 'content' => $select->show(intval($config['forward_attachment'])), + ); + } + + if (!isset($no_override['default_font'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_default_font'; + $fonts = rcube_fontdefs(); + $selected = $config['default_font']; + + $select = '<select name="_default_font" id="'.$field_id.'">'; + $select .= '<option value=""' . (!$selected ? ' selected="selected"' : '') . '>---</option>'; + foreach ($fonts as $fname => $font) { + $select .= '<option value="'.$fname.'"' + . ($fname == $selected ? ' selected="selected"' : '') + . ' style=\'font-family: ' . $font . '\'>' + . Q($fname) . '</option>'; + } + $select .= '</select>'; + + $blocks['main']['options']['default_font'] = array( + 'title' => html::label($field_id, Q(rcube_label('defaultfont'))), + 'content' => $select + ); + } + break; + + // Addressbook config + case 'addressbook': + $blocks = array( + 'main' => array('name' => Q(rcube_label('mainoptions'))), + ); + + if (!isset($no_override['default_addressbook']) + && (!$current || ($books = $RCMAIL->get_address_sources(true, true))) + ) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_default_addressbook'; + $select = new html_select(array('name' => '_default_addressbook', 'id' => $field_id)); + + foreach ($books as $book) { + $select->add(html_entity_decode($book['name'], ENT_COMPAT, 'UTF-8'), $book['id']); + } + + $blocks['main']['options']['default_addressbook'] = array( + 'title' => html::label($field_id, Q(rcube_label('defaultabook'))), + 'content' => $select->show($config['default_addressbook']), + ); + } + + // show addressbook listing mode selection + if (!isset($no_override['addressbook_name_listing'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_addressbook_name_listing'; + $select = new html_select(array('name' => '_addressbook_name_listing', 'id' => $field_id)); + + $select->add(rcube_label('name'), 0); + $select->add(rcube_label('firstname') . ' ' . rcube_label('surname'), 1); + $select->add(rcube_label('surname') . ' ' . rcube_label('firstname'), 2); + $select->add(rcube_label('surname') . ', ' . rcube_label('firstname'), 3); + + $blocks['main']['options']['list_name_listing'] = array( + 'title' => html::label($field_id, Q(rcube_label('listnamedisplay'))), + 'content' => $select->show($config['addressbook_name_listing']), + ); + } + + // show addressbook sort column + if (!isset($no_override['addressbook_sort_col'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_addressbook_sort_col'; + $select = new html_select(array('name' => '_addressbook_sort_col', 'id' => $field_id)); + + $select->add(rcube_label('name'), 'name'); + $select->add(rcube_label('firstname'), 'firstname'); + $select->add(rcube_label('surname'), 'surname'); + + $blocks['main']['options']['sort_col'] = array( + 'title' => html::label($field_id, Q(rcube_label('listsorting'))), + 'content' => $select->show($config['addressbook_sort_col']), + ); + } + + // show addressbook page size selection + if (!isset($no_override['addressbook_pagesize'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_addressbook_pagesize'; + $input = new html_inputfield(array('name' => '_addressbook_pagesize', 'id' => $field_id, 'size' => 5)); + $size = intval($config['addressbook_pagesize'] ? $config['addressbook_pagesize'] : $config['pagesize']); + + $blocks['main']['options']['pagesize'] = array( + 'title' => html::label($field_id, Q(rcube_label('pagesize'))), + 'content' => $input->show($size ? $size : 50), + ); + } + + if (!isset($no_override['autocomplete_single'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_autocomplete_single'; + $checkbox = new html_checkbox(array('name' => '_autocomplete_single', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['autocomplete_single'] = array( + 'title' => html::label($field_id, Q(rcube_label('autocompletesingle'))), + 'content' => $checkbox->show($config['autocomplete_single']?1:0), + ); + } + break; + + // Special IMAP folders + case 'folders': + $blocks = array( + 'main' => array('name' => Q(rcube_label('mainoptions'))), + ); + + if (!isset($no_override['show_real_foldernames'])) { + if (!$current) { + continue 2; + } + + $field_id = 'show_real_foldernames'; + $input = new html_checkbox(array('name' => '_show_real_foldernames', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['show_real_foldernames'] = array( + 'title' => html::label($field_id, Q(rcube_label('show_real_foldernames'))), + 'content' => $input->show($config['show_real_foldernames']?1:0), + ); + } + + // Configure special folders + if (!isset($no_override['default_folders']) && $current) { + $select = rcmail_mailbox_select(array( + 'noselection' => '---', + 'realnames' => true, + 'maxlength' => 30, + 'folder_filter' => 'mail', + 'folder_rights' => 'w', + // #1486114, #1488279 + 'onchange' => "if ($(this).val() == 'INBOX') $(this).val('')", + )); + } + + if (!isset($no_override['drafts_mbox'])) { + if (!$current) { + continue 2; + } + + $blocks['main']['options']['drafts_mbox'] = array( + 'title' => Q(rcube_label('drafts')), + 'content' => $select->show($config['drafts_mbox'], array('name' => "_drafts_mbox")), + ); + } + + if (!isset($no_override['sent_mbox'])) { + if (!$current) { + continue 2; + } + + $blocks['main']['options']['sent_mbox'] = array( + 'title' => Q(rcube_label('sent')), + 'content' => $select->show($config['sent_mbox'], array('name' => "_sent_mbox")), + ); + } + + if (!isset($no_override['junk_mbox'])) { + if (!$current) { + continue 2; + } + + $blocks['main']['options']['junk_mbox'] = array( + 'title' => Q(rcube_label('junk')), + 'content' => $select->show($config['junk_mbox'], array('name' => "_junk_mbox")), + ); + } + + if (!isset($no_override['trash_mbox'])) { + if (!$current) { + continue 2; + } + + $blocks['main']['options']['trash_mbox'] = array( + 'title' => Q(rcube_label('trash')), + 'content' => $select->show($config['trash_mbox'], array('name' => "_trash_mbox")), + ); + } + break; + + // Server settings + case 'server': + $blocks = array( + 'main' => array('name' => Q(rcube_label('mainoptions'))), + 'maintenance' => array('name' => Q(rcube_label('maintenance'))), + ); + + if (!isset($no_override['read_when_deleted'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_read_deleted'; + $input = new html_checkbox(array('name' => '_read_when_deleted', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['read_when_deleted'] = array( + 'title' => html::label($field_id, Q(rcube_label('readwhendeleted'))), + 'content' => $input->show($config['read_when_deleted']?1:0), + ); + } + + if (!isset($no_override['flag_for_deletion'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_flag_for_deletion'; + $input = new html_checkbox(array('name' => '_flag_for_deletion', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['flag_for_deletion'] = array( + 'title' => html::label($field_id, Q(rcube_label('flagfordeletion'))), + 'content' => $input->show($config['flag_for_deletion']?1:0), + ); + } + + // don't show deleted messages + if (!isset($no_override['skip_deleted'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_skip_deleted'; + $input = new html_checkbox(array('name' => '_skip_deleted', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['skip_deleted'] = array( + 'title' => html::label($field_id, Q(rcube_label('skipdeleted'))), + 'content' => $input->show($config['skip_deleted']?1:0), + ); + } + + if (!isset($no_override['delete_always'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_delete_always'; + $input = new html_checkbox(array('name' => '_delete_always', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['delete_always'] = array( + 'title' => html::label($field_id, Q(rcube_label('deletealways'))), + 'content' => $input->show($config['delete_always']?1:0), + ); + } + + if (!isset($no_override['delete_junk'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_delete_junk'; + $input = new html_checkbox(array('name' => '_delete_junk', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['delete_junk'] = array( + 'title' => html::label($field_id, Q(rcube_label('deletejunk'))), + 'content' => $input->show($config['delete_junk']?1:0), + ); + } + + // Trash purging on logout + if (!isset($no_override['logout_purge'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_logout_purge'; + $input = new html_checkbox(array('name' => '_logout_purge', 'id' => $field_id, 'value' => 1)); + + $blocks['maintenance']['options']['logout_purge'] = array( + 'title' => html::label($field_id, Q(rcube_label('logoutclear'))), + 'content' => $input->show($config['logout_purge']?1:0), + ); + } + + // INBOX compacting on logout + if (!isset($no_override['logout_expunge'])) { + if (!$current) { + continue 2; + } + + $field_id = 'rcmfd_logout_expunge'; + $input = new html_checkbox(array('name' => '_logout_expunge', 'id' => $field_id, 'value' => 1)); + + $blocks['maintenance']['options']['logout_expunge'] = array( + 'title' => html::label($field_id, Q(rcube_label('logoutcompact'))), + 'content' => $input->show($config['logout_expunge']?1:0), + ); + } } - } - } - - $product_name = $RCMAIL->config->get('product_name', 'Roundcube Webmail'); - $RCMAIL->output->add_script(sprintf("%s.check_protocol_handler('%s', '#mailtoprotohandler');", - JS_OBJECT_NAME, JQ($product_name)), 'foot'); - - $blocks['browser'] = array( - 'name' => Q(rcube_label('browseroptions')), - 'options' => array('mailtoprotohandler' => array( - 'content' => html::a(array( - 'href' => '#', - 'id' => 'mailtoprotohandler'), Q(rcube_label('mailtoprotohandler'))), - )), - ); - - break; - - // Mailbox view (mail screen) - case 'mailbox': - - $blocks = array( - 'main' => array('name' => Q(rcube_label('mainoptions'))), - 'new_message' => array('name' => Q(rcube_label('newmessage'))), - ); - - // show config parameter for preview pane - if (!isset($no_override['preview_pane'])) { - $field_id = 'rcmfd_preview'; - $input_preview = new html_checkbox(array('name' => '_preview_pane', 'id' => $field_id, 'value' => 1, - 'onchange' => "$('#rcmfd_preview_pane_mark_read').prop('disabled', !this.checked)")); - - $blocks['main']['options']['preview_pane'] = array( - 'title' => html::label($field_id, Q(rcube_label('previewpane'))), - 'content' => $input_preview->show($config['preview_pane']?1:0), - ); - } - - // show config parameter for preview pane auto mark as read delay - if (!isset($no_override['preview_pane_mark_read'])) { - // apply default if config option is not set at all - $config['preview_pane_mark_read'] = $RCMAIL->config->get('preview_pane_mark_read', 0); - - $field_id = 'rcmfd_preview_pane_mark_read'; - $select_delay = new html_select(array('name' => '_preview_pane_mark_read', 'id' => $field_id, - 'disabled' => $config['preview_pane']?0:1)); - - $select_delay->add(rcube_label('never'), '-1'); - $select_delay->add(rcube_label('immediately'), 0); - foreach(array(5, 10, 20, 30) as $sec) - $select_delay->add(rcube_label(array('name' => 'afternseconds', 'vars' => array('n' => $sec))), $sec); - - $blocks['main']['options']['preview_pane_mark_read'] = array( - 'title' => html::label($field_id, Q(rcube_label('previewpanemarkread'))), - 'content' => $select_delay->show(intval($config['preview_pane_mark_read'])), - ); - } - - if (!isset($no_override['mdn_requests'])) { - $field_id = 'rcmfd_mdn_requests'; - $select_mdn_requests = new html_select(array('name' => '_mdn_requests', 'id' => $field_id)); - $select_mdn_requests->add(rcube_label('askuser'), 0); - $select_mdn_requests->add(rcube_label('autosend'), 1); - $select_mdn_requests->add(rcube_label('autosendknown'), 3); - $select_mdn_requests->add(rcube_label('autosendknownignore'), 4); - $select_mdn_requests->add(rcube_label('ignore'), 2); - - $blocks['main']['options']['mdn_requests'] = array( - 'title' => html::label($field_id, Q(rcube_label('mdnrequests'))), - 'content' => $select_mdn_requests->show($config['mdn_requests']), - ); - } - - $storage = $RCMAIL->get_storage(); - $threading_supported = $storage->get_capability('THREAD'); - - if (!isset($no_override['autoexpand_threads']) && $threading_supported) { - $field_id = 'rcmfd_autoexpand_threads'; - $select_autoexpand_threads = new html_select(array('name' => '_autoexpand_threads', 'id' => $field_id)); - $select_autoexpand_threads->add(rcube_label('never'), 0); - $select_autoexpand_threads->add(rcube_label('do_expand'), 1); - $select_autoexpand_threads->add(rcube_label('expand_only_unread'), 2); - - $blocks['main']['options']['autoexpand_threads'] = array( - 'title' => html::label($field_id, Q(rcube_label('autoexpand_threads'))), - 'content' => $select_autoexpand_threads->show($config['autoexpand_threads']), - ); - } - - // show page size selection - if (!isset($no_override['mail_pagesize'])) { - $field_id = 'rcmfd_mail_pagesize'; - $input_pagesize = new html_inputfield(array('name' => '_mail_pagesize', 'id' => $field_id, 'size' => 5)); - - $size = intval($config['mail_pagesize'] ? $config['mail_pagesize'] : $config['pagesize']); - - $blocks['main']['options']['pagesize'] = array( - 'title' => html::label($field_id, Q(rcube_label('pagesize'))), - 'content' => $input_pagesize->show($size ? $size : 50), - ); - } - if (!isset($no_override['check_all_folders'])) { - $field_id = 'rcmfd_check_all_folders'; - $input_check_all = new html_checkbox(array('name' => '_check_all_folders', 'id' => $field_id, 'value' => 1)); - - $blocks['new_message']['options']['check_all_folders'] = array( - 'title' => html::label($field_id, Q(rcube_label('checkallfolders'))), - 'content' => $input_check_all->show($config['check_all_folders']?1:0), - ); - } - - break; - - // Message viewing - case 'mailview': - - $blocks = array( - 'main' => array('name' => Q(rcube_label('mainoptions'))), - ); - - // show checkbox to open message view in new window - if (!isset($no_override['message_extwin'])) { - $field_id = 'rcmfd_message_extwin'; - $input_msgextwin = new html_checkbox(array('name' => '_message_extwin', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['message_extwin'] = array( - 'title' => html::label($field_id, Q(rcube_label('showinextwin'))), - 'content' => $input_msgextwin->show($config['message_extwin']?1:0), - ); - } - - // show checkbox for HTML/plaintext messages - if (!isset($no_override['prefer_html'])) { - $field_id = 'rcmfd_htmlmsg'; - $input_preferhtml = new html_checkbox(array('name' => '_prefer_html', 'id' => $field_id, 'value' => 1, - 'onchange' => "$('#rcmfd_show_images').prop('disabled', !this.checked).val(0)")); - - $blocks['main']['options']['prefer_html'] = array( - 'title' => html::label($field_id, Q(rcube_label('preferhtml'))), - 'content' => $input_preferhtml->show($config['prefer_html']?1:0), - ); - } - - if (!isset($no_override['default_charset'])) { - $field_id = 'rcmfd_default_charset'; - - $blocks['main']['options']['default_charset'] = array( - 'title' => html::label($field_id, Q(rcube_label('defaultcharset'))), - 'content' => $RCMAIL->output->charset_selector(array( - 'name' => '_default_charset', 'selected' => $config['default_charset'] - )) - ); - } - - if (!isset($no_override['show_images'])) { - $field_id = 'rcmfd_show_images'; - $input_show_images = new html_select(array('name' => '_show_images', 'id' => $field_id, - 'disabled' => !$config['prefer_html'])); - $input_show_images->add(rcube_label('never'), 0); - $input_show_images->add(rcube_label('fromknownsenders'), 1); - $input_show_images->add(rcube_label('always'), 2); - - $blocks['main']['options']['show_images'] = array( - 'title' => html::label($field_id, Q(rcube_label('showremoteimages'))), - 'content' => $input_show_images->show($config['prefer_html'] ? $config['show_images'] : 0), - ); - } - - if (!isset($no_override['inline_images'])) { - $field_id = 'rcmfd_inline_images'; - $input_inline_images = new html_checkbox(array('name' => '_inline_images', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['inline_images'] = array( - 'title' => html::label($field_id, Q(rcube_label('showinlineimages'))), - 'content' => $input_inline_images->show($config['inline_images']?1:0), - ); - } - - // "display after delete" checkbox - if (!isset($no_override['display_next'])) { - $field_id = 'rcmfd_displaynext'; - $input_displaynext = new html_checkbox(array('name' => '_display_next', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['display_next'] = array( - 'title' => html::label($field_id, Q(rcube_label('displaynext'))), - 'content' => $input_displaynext->show($config['display_next']?1:0), - ); - } - - break; - - // Mail composition - case 'compose': - - $blocks = array( - 'main' => array('name' => Q(rcube_label('mainoptions'))), - 'spellcheck' => array('name' => Q(rcube_label('spellcheckoptions'))), - 'sig' => array('name' => Q(rcube_label('signatureoptions'))), - ); - - // show checkbox to compose messages in a new window - if (!isset($no_override['compose_extwin'])) { - $field_id = 'rcmfdcompose_extwin'; - $input_compextwin = new html_checkbox(array('name' => '_compose_extwin', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['compose_extwin'] = array( - 'title' => html::label($field_id, Q(rcube_label('composeextwin'))), - 'content' => $input_compextwin->show($config['compose_extwin']?1:0), - ); - } - - if (!isset($no_override['htmleditor'])) { - $field_id = 'rcmfd_htmleditor'; - $select_htmleditor = new html_select(array('name' => '_htmleditor', 'id' => $field_id)); - $select_htmleditor->add(rcube_label('never'), 0); - $select_htmleditor->add(rcube_label('always'), 1); - $select_htmleditor->add(rcube_label('htmlonreply'), 2); - $select_htmleditor->add(rcube_label('htmlonreplyandforward'), 3); - - $blocks['main']['options']['htmleditor'] = array( - 'title' => html::label($field_id, Q(rcube_label('htmleditor'))), - 'content' => $select_htmleditor->show(intval($config['htmleditor'])), - ); - } - - if (!isset($no_override['draft_autosave'])) { - $field_id = 'rcmfd_autosave'; - $select_autosave = new html_select(array('name' => '_draft_autosave', 'id' => $field_id, 'disabled' => empty($config['drafts_mbox']))); - $select_autosave->add(rcube_label('never'), 0); - foreach (array(1, 3, 5, 10) as $i => $min) - $select_autosave->add(rcube_label(array('name' => 'everynminutes', 'vars' => array('n' => $min))), $min*60); - - $blocks['main']['options']['draft_autosave'] = array( - 'title' => html::label($field_id, Q(rcube_label('autosavedraft'))), - 'content' => $select_autosave->show($config['draft_autosave']), - ); - } - - if (!isset($no_override['mime_param_folding'])) { - $field_id = 'rcmfd_param_folding'; - $select_param_folding = new html_select(array('name' => '_mime_param_folding', 'id' => $field_id)); - $select_param_folding->add(rcube_label('2231folding'), 0); - $select_param_folding->add(rcube_label('miscfolding'), 1); - $select_param_folding->add(rcube_label('2047folding'), 2); - - $blocks['main']['options']['mime_param_folding'] = array( - 'advanced' => true, - 'title' => html::label($field_id, Q(rcube_label('mimeparamfolding'))), - 'content' => $select_param_folding->show($config['mime_param_folding']), - ); - } - if (!isset($no_override['force_7bit'])) { - $field_id = 'rcmfd_force_7bit'; - $input_7bit = new html_checkbox(array('name' => '_force_7bit', 'id' => $field_id, 'value' => 1)); + $found = false; + $data = $RCMAIL->plugins->exec_hook('preferences_list', + array('section' => $sect['id'], 'blocks' => $blocks, 'current' => $current)); - $blocks['main']['options']['force_7bit'] = array( - 'title' => html::label($field_id, Q(rcube_label('force7bit'))), - 'content' => $input_7bit->show($config['force_7bit']?1:0), - ); - } - - if (!isset($no_override['mdn_default'])) { - $field_id = 'rcmfd_mdn_default'; - $input_mdn = new html_checkbox(array('name' => '_mdn_default', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['mdn_default'] = array( - 'title' => html::label($field_id, Q(rcube_label('reqmdn'))), - 'content' => $input_mdn->show($config['mdn_default']?1:0), - ); - } - - if (!isset($no_override['dsn_default'])) { - $field_id = 'rcmfd_dsn_default'; - $input_dsn = new html_checkbox(array('name' => '_dsn_default', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['dsn_default'] = array( - 'title' => html::label($field_id, Q(rcube_label('reqdsn'))), - 'content' => $input_dsn->show($config['dsn_default']?1:0), - ); - } - - if (!isset($no_override['reply_same_folder'])) { - $field_id = 'rcmfd_reply_same_folder'; - $input_reply_same_folder = new html_checkbox(array('name' => '_reply_same_folder', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['reply_same_folder'] = array( - 'title' => html::label($field_id, Q(rcube_label('replysamefolder'))), - 'content' => $input_reply_same_folder->show($config['reply_same_folder']?1:0), - ); - } - - if (!isset($no_override['reply_mode'])) { - $field_id = 'rcmfd_reply_mode'; - $select_replymode = new html_select(array('name' => '_reply_mode', 'id' => $field_id, - 'onchange' => "\$('#rcmfd_sig_above').attr('disabled',this.selectedIndex<2)")); - $select_replymode->add(rcube_label('replyempty'), -1); - $select_replymode->add(rcube_label('replybottomposting'), 0); - $select_replymode->add(rcube_label('replytopposting'), 1); - - $blocks['main']['options']['reply_mode'] = array( - 'title' => html::label($field_id, Q(rcube_label('whenreplying'))), - 'content' => $select_replymode->show(intval($config['reply_mode'])), - ); - } - - if (!isset($no_override['spellcheck_before_send']) && $config['enable_spellcheck']) { - $field_id = 'rcmfd_spellcheck_before_send'; - $input_spellcheck = new html_checkbox(array('name' => '_spellcheck_before_send', 'id' => $field_id, 'value' => 1)); - - $blocks['spellcheck']['options']['spellcheck_before_send'] = array( - 'title' => html::label($field_id, Q(rcube_label('spellcheckbeforesend'))), - 'content' => $input_spellcheck->show($config['spellcheck_before_send']?1:0), - ); - } - - if ($config['enable_spellcheck']) { - foreach (array('syms', 'nums', 'caps') as $key) { - $key = 'spellcheck_ignore_'.$key; - if (!isset($no_override[$key])) { - $input_spellcheck = new html_checkbox(array('name' => '_'.$key, 'id' => 'rcmfd_'.$key, 'value' => 1)); - - $blocks['spellcheck']['options'][$key] = array( - 'title' => html::label($field_id, Q(rcube_label(str_replace('_', '', $key)))), - 'content' => $input_spellcheck->show($config[$key]?1:0), - ); + // create output + foreach ($data['blocks'] as $block) { + if (!empty($block['content']) || !empty($block['options'])) { + $found = true; + break; + } } - } - } - if (!isset($no_override['show_sig'])) { - $field_id = 'rcmfd_show_sig'; - $select_show_sig = new html_select(array('name' => '_show_sig', 'id' => $field_id)); - $select_show_sig->add(rcube_label('never'), 0); - $select_show_sig->add(rcube_label('always'), 1); - $select_show_sig->add(rcube_label('newmessageonly'), 2); - $select_show_sig->add(rcube_label('replyandforwardonly'), 3); - - $blocks['sig']['options']['show_sig'] = array( - 'title' => html::label($field_id, Q(rcube_label('autoaddsignature'))), - 'content' => $select_show_sig->show($RCMAIL->config->get('show_sig', 1)), - ); + if (!$found) + unset($sections[$idx]); + else + $sections[$idx]['blocks'] = $data['blocks']; } - if (!isset($no_override['sig_above'])) { - $field_id = 'rcmfd_sig_above'; - $select_sigabove = new html_select(array('name' => '_sig_above', 'id' => $field_id, 'disabled' => $config['reply_mode'] < 1)); - $select_sigabove->add(rcube_label('belowquote'), 0); - $select_sigabove->add(rcube_label('abovequote'), 1); - - $blocks['sig']['options']['sig_above'] = array( - 'title' => html::label($field_id, Q(rcube_label('replysignaturepos'))), - 'content' => $select_sigabove->show($config['sig_above']?1:0), - ); - } - - if (!isset($no_override['strip_existing_sig'])) { - $field_id = 'rcmfd_strip_existing_sig'; - $input_stripexistingsig = new html_checkbox(array('name' => '_strip_existing_sig', 'id' => $field_id, 'value' => 1)); - - $blocks['sig']['options']['strip_existing_sig'] = array( - 'title' => html::label($field_id, Q(rcube_label('replyremovesignature'))), - 'content' => $input_stripexistingsig->show($config['strip_existing_sig']?1:0), - ); - } - - if (!isset($no_override['forward_attachment'])) { - $field_id = 'rcmfd_forward_attachment'; - $select = new html_select(array('name' => '_forward_attachment', 'id' => $field_id)); - $select->add(rcube_label('inline'), 0); - $select->add(rcube_label('asattachment'), 1); - - $blocks['main']['options']['forward_attachment'] = array( - 'title' => html::label($field_id, Q(rcube_label('forwardmode'))), - 'content' => $select->show(intval($config['forward_attachment'])), - ); - } - - if (!isset($no_override['default_font'])) { - $field_id = 'rcmfd_default_font'; - $fonts = rcube_fontdefs(); - $default_font = $config['default_font'] ? $config['default_font'] : 'Verdana'; - - $select = '<select name="_default_font" id="'.$field_id.'">'; - foreach ($fonts as $fname => $font) - $select .= '<option value="'.$fname.'"' - . ($fname == $default_font ? ' selected="selected"' : '') - . ' style=\'font-family: ' . $font . '\'>' - . Q($fname) . '</option>'; - $select .= '</select>'; - - $blocks['main']['options']['default_font'] = array( - 'title' => html::label($field_id, Q(rcube_label('defaultfont'))), - 'content' => $select - ); - } - - break; - - - // Addressbook config - case 'addressbook': - - $blocks = array( - 'main' => array('name' => Q(rcube_label('mainoptions'))), - ); - - if (!isset($no_override['default_addressbook']) - && ($books = $RCMAIL->get_address_sources(true, true)) - ) { - $field_id = 'rcmfd_default_addressbook'; - $select_abook = new html_select(array('name' => '_default_addressbook', 'id' => $field_id)); - - foreach ($books as $book) { - $select_abook->add(html_entity_decode($book['name'], ENT_COMPAT, 'UTF-8'), $book['id']); - } - - $blocks['main']['options']['default_addressbook'] = array( - 'title' => html::label($field_id, Q(rcube_label('defaultabook'))), - 'content' => $select_abook->show($config['default_addressbook']), - ); - } - - // show addressbook listing mode selection - if (!isset($no_override['addressbook_name_listing'])) { - $field_id = 'rcmfd_addressbook_name_listing'; - $select_listing = new html_select(array('name' => '_addressbook_name_listing', 'id' => $field_id)); - $select_listing->add(rcube_label('name'), 0); - $select_listing->add(rcube_label('firstname') . ' ' . rcube_label('surname'), 1); - $select_listing->add(rcube_label('surname') . ' ' . rcube_label('firstname'), 2); - $select_listing->add(rcube_label('surname') . ', ' . rcube_label('firstname'), 3); - - $blocks['main']['options']['list_name_listing'] = array( - 'title' => html::label($field_id, Q(rcube_label('listnamedisplay'))), - 'content' => $select_listing->show($config['addressbook_name_listing']), - ); - } - - // show addressbook sort column - if (!isset($no_override['addressbook_sort_col'])) { - $field_id = 'rcmfd_addressbook_sort_col'; - $select_sort = new html_select(array('name' => '_addressbook_sort_col', 'id' => $field_id)); - $select_sort->add(rcube_label('name'), 'name'); - $select_sort->add(rcube_label('firstname'), 'firstname'); - $select_sort->add(rcube_label('surname'), 'surname'); - - $blocks['main']['options']['sort_col'] = array( - 'title' => html::label($field_id, Q(rcube_label('listsorting'))), - 'content' => $select_sort->show($config['addressbook_sort_col']), - ); - } - - // show addressbook page size selection - if (!isset($no_override['addressbook_pagesize'])) { - $field_id = 'rcmfd_addressbook_pagesize'; - $input_pagesize = new html_inputfield(array('name' => '_addressbook_pagesize', 'id' => $field_id, 'size' => 5)); - - $size = intval($config['addressbook_pagesize'] ? $config['addressbook_pagesize'] : $config['pagesize']); - - $blocks['main']['options']['pagesize'] = array( - 'title' => html::label($field_id, Q(rcube_label('pagesize'))), - 'content' => $input_pagesize->show($size ? $size : 50), - ); - } - - if (!isset($no_override['autocomplete_single'])) { - $field_id = 'rcmfd_autocomplete_single'; - $checkbox = new html_checkbox(array('name' => '_autocomplete_single', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['autocomplete_single'] = array( - 'title' => html::label($field_id, Q(rcube_label('autocompletesingle'))), - 'content' => $checkbox->show($config['autocomplete_single']?1:0), - ); - } - - break; - - // Special IMAP folders - case 'folders': - - $blocks = array( - 'main' => array('name' => Q(rcube_label('mainoptions'))), - ); - - // Configure special folders - if (!isset($no_override['default_folders'])) { - // load folders list only when needed - if ($current) { - $select = rcmail_mailbox_select(array( - 'noselection' => '---', - 'realnames' => true, - 'maxlength' => 30, - 'folder_filter' => 'mail', - 'folder_rights' => 'w', - // #1486114, #1488279 - 'onchange' => "if ($(this).val() == 'INBOX') $(this).val('')", - )); - } - else // dummy select - $select = new html_select(); - - if (!isset($no_override['drafts_mbox'])) - $blocks['main']['options']['drafts_mbox'] = array( - 'title' => Q(rcube_label('drafts')), - 'content' => $select->show($config['drafts_mbox'], array('name' => "_drafts_mbox")), - ); - - if (!isset($no_override['sent_mbox'])) - $blocks['main']['options']['sent_mbox'] = array( - 'title' => Q(rcube_label('sent')), - 'content' => $select->show($config['sent_mbox'], array('name' => "_sent_mbox")), - ); - - if (!isset($no_override['junk_mbox'])) - $blocks['main']['options']['junk_mbox'] = array( - 'title' => Q(rcube_label('junk')), - 'content' => $select->show($config['junk_mbox'], array('name' => "_junk_mbox")), - ); - - if (!isset($no_override['trash_mbox'])) - $blocks['main']['options']['trash_mbox'] = array( - 'title' => Q(rcube_label('trash')), - 'content' => $select->show($config['trash_mbox'], array('name' => "_trash_mbox")), - ); - } - - break; - - // Server settings - case 'server': - - $blocks = array( - 'main' => array('name' => Q(rcube_label('mainoptions'))), - 'maintenance' => array('name' => Q(rcube_label('maintenance'))), - ); - - if (!isset($no_override['read_when_deleted'])) { - $field_id = 'rcmfd_read_deleted'; - $input_readdeleted = new html_checkbox(array('name' => '_read_when_deleted', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['read_when_deleted'] = array( - 'title' => html::label($field_id, Q(rcube_label('readwhendeleted'))), - 'content' => $input_readdeleted->show($config['read_when_deleted']?1:0), - ); - } - - if (!isset($no_override['flag_for_deletion'])) { - $field_id = 'rcmfd_flag_for_deletion'; - $input_flagfordeletion = new html_checkbox(array('name' => '_flag_for_deletion', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['flag_for_deletion'] = array( - 'title' => html::label($field_id, Q(rcube_label('flagfordeletion'))), - 'content' => $input_flagfordeletion->show($config['flag_for_deletion']?1:0), - ); - } - - // don't show deleted messages - if (!isset($no_override['skip_deleted'])) { - $field_id = 'rcmfd_skip_deleted'; - $input_purge = new html_checkbox(array('name' => '_skip_deleted', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['skip_deleted'] = array( - 'title' => html::label($field_id, Q(rcube_label('skipdeleted'))), - 'content' => $input_purge->show($config['skip_deleted']?1:0), - ); - } - - if (!isset($no_override['delete_always'])) { - $field_id = 'rcmfd_delete_always'; - $input_delete_always = new html_checkbox(array('name' => '_delete_always', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['delete_always'] = array( - 'title' => html::label($field_id, Q(rcube_label('deletealways'))), - 'content' => $input_delete_always->show($config['delete_always']?1:0), - ); - } - - if (!isset($no_override['delete_junk'])) { - $field_id = 'rcmfd_delete_junk'; - $input_delete_junk = new html_checkbox(array('name' => '_delete_junk', 'id' => $field_id, 'value' => 1)); - - $blocks['main']['options']['delete_junk'] = array( - 'title' => html::label($field_id, Q(rcube_label('deletejunk'))), - 'content' => $input_delete_junk->show($config['delete_junk']?1:0), - ); - } - - // Trash purging on logout - if (!isset($no_override['logout_purge'])) { - $field_id = 'rcmfd_logout_purge'; - $input_purge = new html_checkbox(array('name' => '_logout_purge', 'id' => $field_id, 'value' => 1)); - - $blocks['maintenance']['options']['logout_purge'] = array( - 'title' => html::label($field_id, Q(rcube_label('logoutclear'))), - 'content' => $input_purge->show($config['logout_purge']?1:0), - ); - } - - // INBOX compacting on logout - if (!isset($no_override['logout_expunge'])) { - $field_id = 'rcmfd_logout_expunge'; - $input_expunge = new html_checkbox(array('name' => '_logout_expunge', 'id' => $field_id, 'value' => 1)); - - $blocks['maintenance']['options']['logout_expunge'] = array( - 'title' => html::label($field_id, Q(rcube_label('logoutcompact'))), - 'content' => $input_expunge->show($config['logout_expunge']?1:0), - ); - } - - break; - } - - $data = $RCMAIL->plugins->exec_hook('preferences_list', array('section' => $sect['id'], 'blocks' => $blocks)); - $found = false; - - // create output - foreach ($data['blocks'] as $block) { - if (!empty($block['content']) || !empty($block['options'])) { - $found = true; - break; - } - } - - if (!$found) - unset($sections[$idx]); - else - $sections[$idx]['blocks'] = $data['blocks']; - } - - return array($sections, $plugin['cols']); + return array($sections, $plugin['cols']); } function rcmail_get_skins() { - $path = 'skins'; - $skins = array(); - - $dir = opendir($path); + $path = 'skins'; + $skins = array(); + $dir = opendir($path); - if (!$dir) - return false; + if (!$dir) { + return false; + } - while (($file = readdir($dir)) !== false) - { - $filename = $path.'/'.$file; - if (!preg_match('/^\./', $file) && is_dir($filename) && is_readable($filename)) - $skins[] = $file; - } + while (($file = readdir($dir)) !== false) { + $filename = $path.'/'.$file; + if (!preg_match('/^\./', $file) && is_dir($filename) && is_readable($filename)) { + $skins[] = $file; + } + } - closedir($dir); + closedir($dir); - return $skins; + return $skins; } @@ -989,9 +1237,9 @@ function rcmail_update_folder_row($name, $oldname=null, $subscribe=false, $class // register UI objects $OUTPUT->add_handlers(array( - 'prefsframe' => 'rcmail_preferences_frame', - 'sectionslist' => 'rcmail_sections_list', - 'identitieslist' => 'rcmail_identities_list', + 'prefsframe' => 'rcmail_preferences_frame', + 'sectionslist' => 'rcmail_sections_list', + 'identitieslist' => 'rcmail_identities_list', )); // register action aliases diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc index 5daab0d24..19edb41d4 100644 --- a/program/steps/settings/save_prefs.inc +++ b/program/steps/settings/save_prefs.inc @@ -34,6 +34,7 @@ switch ($CURR_SECTION) 'time_format' => isset($_POST['_time_format']) ? get_input_value('_time_format', RCUBE_INPUT_POST) : ($CONFIG['time_format'] ? $CONFIG['time_format'] : 'H:i'), 'prettydate' => isset($_POST['_pretty_date']) ? TRUE : FALSE, 'refresh_interval' => isset($_POST['_refresh_interval']) ? intval($_POST['_refresh_interval'])*60 : $CONFIG['refresh_interval'], + 'standard_windows' => isset($_POST['_standard_windows']) ? TRUE : FALSE, 'skin' => isset($_POST['_skin']) ? get_input_value('_skin', RCUBE_INPUT_POST) : $CONFIG['skin'], ); @@ -60,6 +61,7 @@ switch ($CURR_SECTION) 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, @@ -86,7 +88,6 @@ switch ($CURR_SECTION) 'show_sig' => isset($_POST['_show_sig']) ? intval($_POST['_show_sig']) : 1, 'reply_mode' => isset($_POST['_reply_mode']) ? intval($_POST['_reply_mode']) : 0, 'strip_existing_sig' => isset($_POST['_strip_existing_sig']), - 'sig_above' => !empty($_POST['_sig_above']) && $_POST['_reply_mode'] > 0, 'default_font' => get_input_value('_default_font', RCUBE_INPUT_POST), 'forward_attachment' => !empty($_POST['_forward_attachment']), ); @@ -119,6 +120,8 @@ switch ($CURR_SECTION) case 'folders': $a_user_prefs = array( + 'show_real_foldernames' => + isset($_POST['_show_real_foldernames']) ? TRUE : FALSE, 'drafts_mbox' => get_input_value('_drafts_mbox', RCUBE_INPUT_POST, true), 'sent_mbox' => get_input_value('_sent_mbox', RCUBE_INPUT_POST, true), 'junk_mbox' => get_input_value('_junk_mbox', RCUBE_INPUT_POST, true), @@ -158,7 +161,7 @@ switch ($CURR_SECTION) $a_user_prefs['timezone'] = (string) $a_user_prefs['timezone']; if (isset($a_user_prefs['refresh_interval']) && !empty($CONFIG['min_refresh_interval'])) { - if ($a_user_prefs['refresh_interval'] > $CONFIG['min_refresh_interval']) { + if ($a_user_prefs['refresh_interval'] < $CONFIG['min_refresh_interval']) { $a_user_prefs['refresh_interval'] = $CONFIG['min_refresh_interval']; } } diff --git a/program/steps/utils/save_pref.inc b/program/steps/utils/save_pref.inc index b550ad7ef..7def8733d 100644 --- a/program/steps/utils/save_pref.inc +++ b/program/steps/utils/save_pref.inc @@ -21,6 +21,22 @@ $name = get_input_value('_name', RCUBE_INPUT_POST); $value = get_input_value('_value', RCUBE_INPUT_POST); +$whitelist = array( + 'preview_pane', + 'list_cols', + 'collapsed_folders', + 'collapsed_abooks', +); + +if (!in_array($name, array_merge($whitelist, $RCMAIL->plugins->allowed_prefs))) { + raise_error(array('code' => 500, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => sprintf("Hack attempt detected (user: %s)", $RCMAIL->get_user_name())), + true, false); + + $OUTPUT->reset(); + $OUTPUT->send(); +} // save preference value $RCMAIL->user->save_prefs(array($name => $value)); diff --git a/program/steps/utils/spell.inc b/program/steps/utils/spell.inc index a0dd35d27..38e4ca285 100644 --- a/program/steps/utils/spell.inc +++ b/program/steps/utils/spell.inc @@ -42,6 +42,13 @@ else { $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 861e4ba48..96b41e230 100644 --- a/program/steps/utils/spell_html.inc +++ b/program/steps/utils/spell_html.inc @@ -46,6 +46,11 @@ else if ($request['method'] == 'learnWord') { } 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; } |