diff options
Diffstat (limited to 'program/steps/mail')
-rw-r--r-- | program/steps/mail/autocomplete.inc | 2 | ||||
-rw-r--r-- | program/steps/mail/check_recent.inc | 5 | ||||
-rw-r--r-- | program/steps/mail/compose.inc | 133 | ||||
-rw-r--r-- | program/steps/mail/copy.inc | 8 | ||||
-rw-r--r-- | program/steps/mail/func.inc | 298 | ||||
-rw-r--r-- | program/steps/mail/get.inc | 136 | ||||
-rw-r--r-- | program/steps/mail/import.inc | 105 | ||||
-rw-r--r-- | program/steps/mail/list_contacts.inc | 43 | ||||
-rw-r--r-- | program/steps/mail/mark.inc | 10 | ||||
-rw-r--r-- | program/steps/mail/move_del.inc | 6 | ||||
-rw-r--r-- | program/steps/mail/search_contacts.inc | 19 | ||||
-rw-r--r-- | program/steps/mail/sendmail.inc | 53 | ||||
-rw-r--r-- | program/steps/mail/show.inc | 18 |
13 files changed, 545 insertions, 291 deletions
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 3649d148c..8c0b1ffc0 100644 --- a/program/steps/mail/check_recent.inc +++ b/program/steps/mail/check_recent.inc @@ -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(); diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index c166eb74e..9ffde8a57 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']) { @@ -170,6 +170,9 @@ $OUTPUT->set_env('compose_mode', $compose_mode); $config_show_sig = $RCMAIL->config->get('show_sig', 1); if ($compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT) { // don't add signature in draft/edit mode, we'll also not remove the old-one + // but only on page display, later we should be able to change identity/sig (#1489229) + if ($config_show_sig == 1 || $config_show_sig == 2) + $OUTPUT->set_env('show_sig_later', true); } else if ($config_show_sig == 1) $OUTPUT->set_env('show_sig', true); @@ -219,10 +222,10 @@ if (!empty($msg_uid) && empty($COMPOSE['as_attachment'])) $COMPOSE['param']['sent_mbox'] = $sent_folder; } } - else if ($compose_mode == RCUBE_COMPOSE_DRAFT) { - if ($MESSAGE->headers->others['x-draft-info']) { + else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) { + if ($compose_mode == RCUBE_COMPOSE_DRAFT && ($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']; @@ -239,10 +242,10 @@ 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; + $COMPOSE['references'] = $MESSAGE->headers->references; } } else { @@ -327,6 +330,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))) { @@ -386,7 +403,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']); @@ -450,7 +467,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'); @@ -461,7 +478,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)"; @@ -498,12 +516,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']); + $out = $select_from->show((int)$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 { @@ -553,7 +580,7 @@ function rcmail_message_is_html() 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'])) { @@ -571,16 +598,33 @@ function rcmail_prepare_message_body() rcmail_write_forward_attachments(); } // reply/edit/draft/forward - else if ($compose_mode && ($compose_mode != RCUBE_COMPOSE_REPLY || $RCMAIL->config->get('reply_mode') != -1)) { - $isHtml = rcmail_compose_editor_mode(); + else if ($compose_mode && ($compose_mode != RCUBE_COMPOSE_REPLY || intval($RCMAIL->config->get('reply_mode')) != -1)) { + $isHtml = rcmail_compose_editor_mode(); + $messages = array(); if (!empty($MESSAGE->parts)) { + // collect IDs of message/rfc822 parts + if ($compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT) { + foreach ($MESSAGE->attachments as $part) { + if ($part->mimetype == 'message/rfc822') { + $messages[] = $part->mime_id; + } + } + } + foreach ($MESSAGE->parts as $part) { // skip no-content and attachment parts (#1488557) if ($part->type != 'content' || !$part->size || $MESSAGE->is_attachment($part)) { continue; } + // skip all content parts inside the message/rfc822 part in DRAFT/EDIT mode + foreach ($messages as $mimeid) { + if (strpos($part->mime_id, $mimeid . '.') === 0) { + continue 2; + } + } + if ($part_body = rcmail_compose_part_body($part, $isHtml)) { $body .= ($body ? ($isHtml ? '<br/>' : "\n") : '') . $part_body; } @@ -626,7 +670,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 @@ -663,6 +707,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) { @@ -702,7 +751,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']); @@ -886,8 +935,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"; @@ -941,7 +989,7 @@ function rcmail_create_forward_body($body, $bodyIsHtml) function rcmail_create_draft_body($body, $bodyIsHtml) { - global $MESSAGE, $OUTPUT, $COMPOSE; + global $MESSAGE, $COMPOSE; /** * add attachments @@ -989,39 +1037,50 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml) global $RCMAIL, $COMPOSE, $compose_mode; $loaded_attachments = array(); - foreach ((array)$COMPOSE['attachments'] as $id => $attachment) { + foreach ((array)$COMPOSE['attachments'] as $attachment) { $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment; } - $cid_map = $messages = array(); + $cid_map = array(); + $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' && $bodyIsHtml) || $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 in text mode + if ($part->content_id && $part->disposition == 'inline' && !$bodyIsHtml && $compose_mode == RCUBE_COMPOSE_FORWARD) { continue; } - $skip = false; + // skip message/rfc822 attachments on forwards (#1489214) + // Thunderbird when forwarding in inline mode displays such attachments + // and skips any attachments from inside of such part, this however + // skipped e.g. images used in HTML body or other attachments. So, + // better to skip .eml attachments but not their content (included files). if ($part->mimetype == 'message/rfc822') { + if ($compose_mode == RCUBE_COMPOSE_FORWARD) { + continue; + } $messages[] = $part->mime_id; - } else if ($messages) { + } + else if ($compose_mode != RCUBE_COMPOSE_FORWARD) { // skip attachments included in message/rfc822 attachment (#1486487) foreach ($messages as $mimeid) - if (strpos($part->mime_id, $mimeid.'.') === 0) { - $skip = true; - break; + if (strpos($part->mime_id, $mimeid . '.') === 0) { + continue 2; } } - if (!$skip && (($attachment = $loaded_attachments[rcmail_attachment_name($part) . $part->mimetype]) - || ($attachment = rcmail_save_attachment($message, $pid)))) { + if (($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', @@ -1074,7 +1133,7 @@ function rcmail_write_forward_attachments() $names = array(); $loaded_attachments = array(); - foreach ((array)$COMPOSE['attachments'] as $id => $attachment) { + foreach ((array)$COMPOSE['attachments'] as $attachment) { $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment; } @@ -1209,10 +1268,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), @@ -1482,7 +1542,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; @@ -1490,6 +1550,7 @@ function rcmail_editor_selector($attrib) } return $selector; +*/ } 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 da01e6cbf..ae23d4a6d 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); @@ -128,6 +128,40 @@ if (empty($RCMAIL->action) || $RCMAIL->action == 'list') { $OUTPUT->set_pagetitle($pagetitle); } +// register UI objects +$OUTPUT->add_handlers(array( + 'mailboxlist' => 'rcmail_mailbox_list', + 'messages' => 'rcmail_message_list', + 'messagecountdisplay' => 'rcmail_messagecount_display', + 'quotadisplay' => 'rcmail_quota_display', + 'mailboxname' => 'rcmail_mailbox_name_display', + 'messageheaders' => 'rcmail_message_headers', + 'messagefullheaders' => 'rcmail_message_full_headers', + 'messagebody' => 'rcmail_message_body', + 'messagecontentframe' => 'rcmail_messagecontent_frame', + 'messageimportform' => 'rcmail_message_import_form', + 'searchfilter' => 'rcmail_search_filter', + 'searchform' => array($OUTPUT, 'search_form'), +)); + +// register action aliases +$RCMAIL->register_action_map(array( + 'refresh' => 'check_recent.inc', + 'preview' => 'show.inc', + 'print' => 'show.inc', + 'move' => 'move_del.inc', + 'delete' => 'move_del.inc', + 'send' => 'sendmail.inc', + 'expunge' => 'folders.inc', + 'purge' => 'folders.inc', + 'remove-attachment' => 'attachments.inc', + 'display-attachment' => 'attachments.inc', + 'upload' => 'attachments.inc', + 'group-expand' => 'autocomplete.inc', +)); + + + /** * Returns 'to' if current folder is configured Sent or Drafts * or their subfolders, otherwise returns 'from'. @@ -224,7 +258,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 +270,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 +323,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 +339,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 +413,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 +448,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(); } @@ -738,8 +769,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', @@ -753,11 +789,10 @@ 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; @@ -782,53 +817,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]; } @@ -898,8 +897,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 @@ -937,7 +936,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)) @@ -1084,7 +1083,7 @@ 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)); } @@ -1196,7 +1195,7 @@ function rcmail_message_body($attrib) 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), @@ -1216,7 +1215,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, ))) @@ -1442,7 +1441,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; @@ -1453,9 +1453,10 @@ 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 ($name && $name != $mailto && strpos($name, '@')) { + if (!$show_email && $valid && $name && $name != $mailto && strpos($name, '@')) { $name = ''; } @@ -1471,15 +1472,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"), @@ -1504,7 +1513,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); @@ -1543,11 +1552,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) @@ -1563,7 +1572,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 @@ -1606,45 +1615,6 @@ function rcmail_draftinfo_decode($str) } -function rcmail_message_part_controls($attrib) -{ - global $MESSAGE, $RCMAIL; - - $part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC)); - if (!is_object($MESSAGE) || !is_array($MESSAGE->parts) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE->mime_parts[$part]) - return ''; - - $part = $MESSAGE->mime_parts[$part]; - $table = new html_table(array('cols' => 3)); - - $filename = rcmail_attachment_name($part); - - if (!empty($filename)) { - $table->add('title', Q(rcube_label('filename'))); - $table->add('header', Q($filename)); - $table->add('download-link', html::a(array('href' => './?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING'])), Q(rcube_label('download')))); - } - - $table->add('title', Q(rcube_label('filesize'))); - $table->add('header', Q($RCMAIL->message_part_size($part))); - - return $table->show($attrib); -} - - -function rcmail_message_part_frame($attrib) -{ - global $MESSAGE; - - $part = $MESSAGE->mime_parts[asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))]; - $ctype_primary = strtolower($part->ctype_primary); - - $attrib['src'] = './?' . str_replace('_frame=', ($ctype_primary=='text' ? '_embed=' : '_preload='), $_SERVER['QUERY_STRING']); - - return html::iframe($attrib); -} - - /** * clear message composing settings */ @@ -1732,8 +1702,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; } @@ -1814,9 +1783,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; + } } } } @@ -1851,8 +1823,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'; @@ -1899,13 +1870,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'); @@ -1916,6 +1889,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'); @@ -1945,35 +1919,37 @@ function rcmail_message_error($uid=null) $RCMAIL->output->send('messageerror'); } -// register UI objects -$OUTPUT->add_handlers(array( - 'mailboxlist' => 'rcmail_mailbox_list', - 'messages' => 'rcmail_message_list', - 'messagecountdisplay' => 'rcmail_messagecount_display', - 'quotadisplay' => 'rcmail_quota_display', - 'mailboxname' => 'rcmail_mailbox_name_display', - 'messageheaders' => 'rcmail_message_headers', - 'messagefullheaders' => 'rcmail_message_full_headers', - 'messagebody' => 'rcmail_message_body', - 'messagecontentframe' => 'rcmail_messagecontent_frame', - 'messagepartframe' => 'rcmail_message_part_frame', - 'messagepartcontrols' => 'rcmail_message_part_controls', - 'searchfilter' => 'rcmail_search_filter', - 'searchform' => array($OUTPUT, 'search_form'), -)); +function rcmail_message_import_form($attrib = array()) +{ + global $OUTPUT; -// register action aliases -$RCMAIL->register_action_map(array( - 'refresh' => 'check_recent.inc', - 'preview' => 'show.inc', - 'print' => 'show.inc', - 'moveto' => 'move_del.inc', - 'delete' => 'move_del.inc', - 'send' => 'sendmail.inc', - 'expunge' => 'folders.inc', - 'purge' => 'folders.inc', - 'remove-attachment' => 'attachments.inc', - 'display-attachment' => 'attachments.inc', - 'upload' => 'attachments.inc', - 'group-expand' => 'autocomplete.inc', -)); + // 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; +} diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index 23dc22b7c..a27e788a3 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); @@ -38,19 +38,34 @@ ob_end_clean(); // similar code as in program/steps/mail/show.inc if (!empty($_GET['_uid'])) { + $uid = get_input_value('_uid', RCUBE_INPUT_GET); $RCMAIL->config->set('prefer_html', true); - $MESSAGE = new rcube_message(get_input_value('_uid', RCUBE_INPUT_GET)); + $MESSAGE = new rcube_message($uid); } // check connection status check_storage_status(); +$part_id = get_input_value('_part', RCUBE_INPUT_GPC); + // show part page if (!empty($_GET['_frame'])) { - if (($part_id = get_input_value('_part', RCUBE_INPUT_GPC)) && ($part = $MESSAGE->mime_parts[$part_id])) { - $OUTPUT->set_pagetitle(rcmail_attachment_name($part)); + if ($part_id && ($part = $MESSAGE->mime_parts[$part_id])) { + $filename = rcmail_attachment_name($part); + $OUTPUT->set_pagetitle($filename); } + // register UI objects + $OUTPUT->add_handlers(array( + 'messagepartframe' => 'rcmail_message_part_frame', + 'messagepartcontrols' => 'rcmail_message_part_controls', + )); + + $OUTPUT->set_env('mailbox', $RCMAIL->storage->get_folder()); + $OUTPUT->set_env('uid', $uid); + $OUTPUT->set_env('part', $part_id); + $OUTPUT->set_env('filename', $filename); + $OUTPUT->send('messagepart'); exit; } @@ -62,9 +77,10 @@ else if ($_GET['_thumb']) { $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; + $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)) { @@ -91,12 +107,9 @@ else if ($_GET['_thumb']) { exit; } -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); +else if (strlen($part_id)) { + if ($part = $MESSAGE->mime_parts[$part_id]) { + $mimetype = rcmail_fix_mimetype($part->mimetype); // allow post-processing of the message body $plugin = $RCMAIL->plugins->exec_hook('message_part_get', @@ -106,7 +119,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,7 +131,7 @@ 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' || $mimetype == 'text/plain') { @@ -145,6 +158,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; @@ -157,22 +174,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 ? "(.$file_extension)" : ''), - 'detected' => $real_mimetype . ($extensions[0] ? "(.$extensions[0])" : ''), + // 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'))) ) - )) . - 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; } } @@ -202,7 +229,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"); } @@ -366,7 +392,9 @@ else { header('HTTP/1.1 404 Not Found'); exit; - +/** + * Handles nicely storage connection errors + */ function check_storage_status() { $error = rcmail::get_instance()->storage->get_error_code(); @@ -398,3 +426,49 @@ function check_storage_status() exit; } } + +/** + * Attachment properties table + */ +function rcmail_message_part_controls($attrib) +{ + global $MESSAGE, $RCMAIL; + + $part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC)); + if (!is_object($MESSAGE) || !is_array($MESSAGE->parts) + || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE->mime_parts[$part] + ) { + return ''; + } + + $part = $MESSAGE->mime_parts[$part]; + $table = new html_table(array('cols' => 2)); + + $table->add('title', Q(rcube_label('namex')).':'); + $table->add('header', Q(rcmail_attachment_name($part))); + + $table->add('title', Q(rcube_label('type')).':'); + $table->add('header', Q($part->mimetype)); + + $table->add('title', Q(rcube_label('size')).':'); + $table->add('header', Q($RCMAIL->message_part_size($part))); + + return $table->show($attrib); +} + +/** + * Attachment preview frame + */ +function rcmail_message_part_frame($attrib) +{ + global $MESSAGE, $RCMAIL; + + $part = $MESSAGE->mime_parts[asciiwords(get_input_value('_part', RCUBE_INPUT_GPC))]; + $ctype_primary = strtolower($part->ctype_primary); + + $attrib['src'] = './?' . str_replace('_frame=', ($ctype_primary=='text' ? '_embed=' : '_preload='), $_SERVER['QUERY_STRING']); + + $RCMAIL->output->add_gui_object('messagepartframe', $attrib['id']); + + return html::iframe($attrib); +} 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/list_contacts.inc b/program/steps/mail/list_contacts.inc index 7e3b349cd..dab146431 100644 --- a/program/steps/mail/list_contacts.inc +++ b/program/steps/mail/list_contacts.inc @@ -19,10 +19,10 @@ +-----------------------------------------------------------------------+ */ -$afields = $RCMAIL->config->get('contactlist_fields'); -$sort_col = $RCMAIL->config->get('addressbook_sort_col', 'name'); -$page_size = $RCMAIL->config->get('addressbook_pagesize', $RCMAIL->config->get('pagesize', 50)); -$page = max(1, intval($_GET['_page'])); +$afields = $RCMAIL->config->get('contactlist_fields'); +$addr_sort_col = $RCMAIL->config->get('addressbook_sort_col', 'name'); +$page_size = $RCMAIL->config->get('addressbook_pagesize', $RCMAIL->config->get('pagesize', 50)); +$list_page = max(1, intval($_GET['_page'])); // Use search result if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']])) { @@ -42,7 +42,7 @@ if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search while ($row = $result->next()) { $row['sourceid'] = $s; - $key = rcube_addressbook::compose_contact_key($row, $sort_col); + $key = rcube_addressbook::compose_contact_key($row, $addr_sort_col); $records[$key] = $row; } unset($result); @@ -53,7 +53,7 @@ if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search // create resultset object $count = count($records); - $first = ($page-1) * $page_size; + $first = ($list_page-1) * $page_size; $result = new rcube_result_set($count, $first); // we need only records for current page @@ -71,10 +71,13 @@ else { if ($CONTACTS && $CONTACTS->ready) { // set list properties $CONTACTS->set_pagesize($page_size); - $CONTACTS->set_page($page); + $CONTACTS->set_page($list_page); + if ($group_id = get_input_value('_gid', RCUBE_INPUT_GPC)) { + $CONTACTS->set_group($group_id); + } // list groups of this source (on page one) - if ($CONTACTS->groups && $CONTACTS->list_page == 1) { + else if ($CONTACTS->groups && $CONTACTS->list_page == 1) { foreach ($CONTACTS->list_groups() as $group) { $CONTACTS->reset(); $CONTACTS->set_group($group['ID']); @@ -89,6 +92,19 @@ else { 'contactgroup' => html::span(array('title' => $email), Q($group['name']))), 'group'); } } + // make virtual groups clickable to list their members + else if ($group_prop['virtual']) { + $row_id = 'G'.$group['ID']; + $OUTPUT->command('add_contact_row', $row_id, array( + 'contactgroup' => html::a(array( + 'href' => '#list', + 'rel' => $row['ID'], + 'title' => rcube_label('listgroup'), + 'onclick' => sprintf("return %s.command('pushgroup',{'source':'%s','id':'%s'},this,event)", JS_OBJECT_NAME, $source, $group['ID']), + ), Q($group['name']) . ' ' . html::span('action', '»'))), + 'group', + array('ID' => $group['ID'], 'name' => $group['name'], 'virtual' => true)); + } // show group with count else if (($result = $CONTACTS->count()) && $result->count) { $row_id = 'E'.$group['ID']; @@ -97,10 +113,12 @@ else { 'contactgroup' => Q($group['name'] . ' (' . intval($result->count) . ')')), 'group'); } } + + $CONTACTS->reset(); + $CONTACTS->set_group(0); } // get contacts for this user - $CONTACTS->set_group(0); $result = $CONTACTS->list_records($afields); } } @@ -118,10 +136,13 @@ else if (!empty($result) && $result->count > 0) { foreach ($emails as $i => $email) { $row_id = $row['ID'].$i; $jsresult[$row_id] = format_email_recipient($email, $name); + $classname = $row['_type'] == 'group' ? 'group' : 'person'; + $keyname = $row['_type'] == 'group' ? 'contactgroup' : 'contact'; + $OUTPUT->command('add_contact_row', $row_id, array( - 'contact' => html::span(array('title' => $email), Q($name ? $name : $email) . + $keyname => html::span(array('title' => $email), Q($name ? $name : $email) . ($name && count($emails) > 1 ? ' ' . html::span('email', Q($email)) : '') - )), 'person'); + )), $classname); } } } 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 3fc6ac5a7..f15cd2460 100644 --- a/program/steps/mail/move_del.inc +++ b/program/steps/mail/move_del.inc @@ -29,7 +29,7 @@ $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'])) { +if ($RCMAIL->action == 'move' && !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); @@ -74,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; } @@ -124,7 +126,7 @@ else rcmail_set_unseen_count($mbox, $unseen_count); } - if ($RCMAIL->action == 'moveto' && strlen($target)) { + if ($RCMAIL->action == 'move' && strlen($target)) { rcmail_send_unread_count($target, true); } diff --git a/program/steps/mail/search_contacts.inc b/program/steps/mail/search_contacts.inc index 2e6bb12f8..6a30ad1f5 100644 --- a/program/steps/mail/search_contacts.inc +++ b/program/steps/mail/search_contacts.inc @@ -19,16 +19,15 @@ +-----------------------------------------------------------------------+ */ -$search = get_input_value('_q', RCUBE_INPUT_GPC, true); -$sources = $RCMAIL->get_address_sources(); -$search_mode = (int) $RCMAIL->config->get('addressbook_search_mode'); -$sort_col = $RCMAIL->config->get('addressbook_sort_col', 'name'); -$afields = $RCMAIL->config->get('contactlist_fields'); +$search = get_input_value('_q', RCUBE_INPUT_GPC, true); +$sources = $RCMAIL->get_address_sources(); +$search_mode = (int) $RCMAIL->config->get('addressbook_search_mode'); +$addr_sort_col = $RCMAIL->config->get('addressbook_sort_col', 'name'); +$afields = $RCMAIL->config->get('contactlist_fields'); +$page_size = $RCMAIL->config->get('addressbook_pagesize', $RCMAIL->config->get('pagesize', 50)); +$records = array(); +$search_set = array(); -$page = 1; -$page_size = $RCMAIL->config->get('addressbook_pagesize', $RCMAIL->config->get('pagesize', 50)); - -$records = $search_set = array(); foreach ($sources as $s) { $source = $RCMAIL->get_address_book($s['id']); $source->set_page(1); @@ -46,7 +45,7 @@ foreach ($sources as $s) { while ($row = $result->next()) { $row['sourceid'] = $s['id']; - $key = rcube_addressbook::compose_contact_key($row, $sort_col); + $key = rcube_addressbook::compose_contact_key($row, $addr_sort_col); $records[$key] = $row; } 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 552c180f5..59f4d55e1 100644 --- a/program/steps/mail/show.inc +++ b/program/steps/mail/show.inc @@ -109,14 +109,14 @@ if ($uid) { $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()) { @@ -228,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) { @@ -275,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; |