diff options
Diffstat (limited to 'program/steps/mail')
-rw-r--r-- | program/steps/mail/autocomplete.inc | 9 | ||||
-rw-r--r-- | program/steps/mail/check_recent.inc | 34 | ||||
-rw-r--r-- | program/steps/mail/compose.inc | 122 | ||||
-rw-r--r-- | program/steps/mail/copy.inc | 11 | ||||
-rw-r--r-- | program/steps/mail/func.inc | 336 | ||||
-rw-r--r-- | program/steps/mail/get.inc | 6 | ||||
-rw-r--r-- | program/steps/mail/import.inc | 115 | ||||
-rw-r--r-- | program/steps/mail/list.inc | 16 | ||||
-rw-r--r-- | program/steps/mail/list_contacts.inc | 48 | ||||
-rw-r--r-- | program/steps/mail/mark.inc | 20 | ||||
-rw-r--r-- | program/steps/mail/move_del.inc | 44 | ||||
-rw-r--r-- | program/steps/mail/search.inc | 61 | ||||
-rw-r--r-- | program/steps/mail/search_contacts.inc | 12 | ||||
-rw-r--r-- | program/steps/mail/sendmail.inc | 45 | ||||
-rw-r--r-- | program/steps/mail/show.inc | 2 | ||||
-rw-r--r-- | program/steps/mail/viewsource.inc | 4 |
16 files changed, 549 insertions, 336 deletions
diff --git a/program/steps/mail/autocomplete.inc b/program/steps/mail/autocomplete.inc index c15de92cf..71b337a53 100644 --- a/program/steps/mail/autocomplete.inc +++ b/program/steps/mail/autocomplete.inc @@ -49,7 +49,7 @@ $mode = (int) $RCMAIL->config->get('addressbook_search_mode'); $single = (bool) $RCMAIL->config->get('autocomplete_single'); $search = rcube_utils::get_input_value('_search', rcube_utils::INPUT_GPC, true); $source = rcube_utils::get_input_value('_source', rcube_utils::INPUT_GPC); -$sid = rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC); +$reqid = rcube_utils::get_input_value('_reqid', rcube_utils::INPUT_GPC); if (strlen($source)) { $book_types = array($source); @@ -90,7 +90,7 @@ if (!empty($book_types) && strlen($search)) { // skip duplicates if (!in_array($contact, $contacts)) { - $contacts[] = $contact; + $contacts[] = array('name' => $contact, 'type' => $sql_arr['_type']); $sort_keys[] = sprintf('%s %03d', $sql_arr['name'] , $idx++); if (count($contacts) >= $MAXNUM) { @@ -118,7 +118,7 @@ if (!empty($book_types) && strlen($search)) { if ($group_prop['email']) { $idx = 0; foreach ((array)$group_prop['email'] as $email) { - $contacts[] = format_email_recipient($email, $group['name']); + $contacts[] = array('name' => format_email_recipient($email, $group['name']), 'type' => 'group'); $sort_keys[] = sprintf('%s %03d', $group['name'] , $idx++); if (count($contacts) >= $MAXNUM) { @@ -131,6 +131,7 @@ if (!empty($book_types) && strlen($search)) { $sort_keys[] = $group['name']; $contacts[] = array( 'name' => $group['name'] . ' (' . intval($result->count) . ')', + 'type' => 'group', 'id' => $group['ID'], 'source' => $id ); @@ -154,5 +155,5 @@ if (!empty($book_types) && strlen($search)) { } } -$OUTPUT->command('ksearch_query_results', $contacts, $search, $sid); +$OUTPUT->command('ksearch_query_results', $contacts, $search, $reqid); $OUTPUT->send(); diff --git a/program/steps/mail/check_recent.inc b/program/steps/mail/check_recent.inc index d2d27a2ca..70f4c03a6 100644 --- a/program/steps/mail/check_recent.inc +++ b/program/steps/mail/check_recent.inc @@ -5,7 +5,7 @@ | program/steps/mail/check_recent.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2010, The Roundcube Dev Team | + | Copyright (C) 2005-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -21,7 +21,7 @@ // If there's no folder or messages list, there's nothing to update // This can happen on 'refresh' request -if (empty($_REQUEST['_folderlist']) && empty($_REQUEST['_list'])) { +if (empty($_POST['_folderlist']) && empty($_POST['_list'])) { return; } @@ -29,10 +29,18 @@ $trash = $RCMAIL->config->get('trash_mbox'); $current = $RCMAIL->storage->get_folder(); $check_all = $RCMAIL->action != 'refresh' || (bool)$RCMAIL->config->get('check_all_folders'); +$search_request = rcube_utils::get_input_value('_search', rcube_utils::INPUT_GPC); +if ($search_request && $_SESSION['search_request'] != $search_request) { + $search_request = null; +} + // list of folders to check if ($check_all) { $a_mailboxes = $RCMAIL->storage->list_folders_subscribed('', '*', 'mail'); } +else if ($search_request && is_object($_SESSION['search'][1])) { + $a_mailboxes = (array) $_SESSION['search'][1]->get_parameters('MAILBOX'); +} else { $a_mailboxes = (array) $current; if ($current != 'INBOX') { @@ -46,7 +54,7 @@ $a_mailboxes = $plugin['folders']; // check recent/unseen counts foreach ($a_mailboxes as $mbox_name) { - $is_current = $mbox_name == $current; + $is_current = $mbox_name == $current || ($search_request && is_object($_SESSION['search'][1]) && in_array($mbox_name, (array)$_SESSION['search'][1]->get_parameters('MAILBOX'))); if ($is_current) { // Synchronize mailbox cache, handle flag changes $RCMAIL->storage->folder_sync($mbox_name); @@ -66,21 +74,23 @@ foreach ($a_mailboxes as $mbox_name) { if ($status && $is_current) { // refresh saved search set - $search_request = rcube_utils::get_input_value('_search', rcube_utils::INPUT_GPC); - if ($search_request && isset($_SESSION['search']) - && $_SESSION['search_request'] == $search_request - ) { + if ($search_request && isset($_SESSION['search'])) { + unset($search_request); // only do this once $_SESSION['search'] = $RCMAIL->storage->refresh_search(); + if ($_SESSION['search'][1]->multi) + $mbox_name = ''; } - if (!empty($_GET['_quota'])) + if (!empty($_POST['_quota'])) { $OUTPUT->command('set_quota', $RCMAIL->quota_content()); + } - $OUTPUT->set_env('exists', $RCMAIL->storage->count($mbox_name, 'EXISTS')); + $OUTPUT->set_env('exists', $RCMAIL->storage->count($mbox_name, 'EXISTS', true)); // "No-list" mode, don't get messages - if (empty($_GET['_list'])) + if (empty($_POST['_list'])) { continue; + } // get overall message count; allow caching because rcube_storage::folder_status() // did a refresh but only in list mode @@ -116,7 +126,7 @@ foreach ($a_mailboxes as $mbox_name) { } } // handle flag updates - else if ($is_current && ($uids = rcube_utils::get_input_value('_uids', rcube_utils::INPUT_GPC))) { + else if ($is_current && ($uids = rcube_utils::get_input_value('_uids', rcube_utils::INPUT_GPC)) && empty($search_request)) { $data = $RCMAIL->storage->folder_data($mbox_name); if (empty($_SESSION['list_mod_seq']) || $_SESSION['list_mod_seq'] != $data['HIGHESTMODSEQ']) { @@ -136,7 +146,7 @@ foreach ($a_mailboxes as $mbox_name) { // set trash folder state if ($mbox_name === $trash) { - $OUTPUT->command('set_trash_count', $RCMAIL->storage->count($mbox_name, 'EXISTS')); + $OUTPUT->command('set_trash_count', $RCMAIL->storage->count($mbox_name, 'EXISTS', true)); } } diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index db485fda8..6a0139d72 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -62,36 +62,6 @@ if (!is_array($COMPOSE)) { rcmail_process_compose_params($COMPOSE); - // add attachments listed by message_compose hook - if (is_array($plugin['attachments'])) { - foreach ($plugin['attachments'] as $attach) { - // we have structured data - if (is_array($attach)) { - $attachment = $attach; - } - // only a file path is given - else { - $filename = basename($attach); - $attachment = array( - 'group' => $COMPOSE_ID, - 'name' => $filename, - 'mimetype' => rcube_mime::file_content_type($attach, $filename), - 'path' => $attach, - ); - } - - // save attachment if valid - if (($attachment['data'] && $attachment['name']) || ($attachment['path'] && file_exists($attachment['path']))) { - $attachment = rcmail::get_instance()->plugins->exec_hook('attachment_save', $attachment); - } - - if ($attachment['status'] && !$attachment['abort']) { - unset($attachment['data'], $attachment['status'], $attachment['abort']); - $COMPOSE['attachments'][$attachment['id']] = $attachment; - } - } - } - // check if folder for saving sent messages exists and is subscribed (#1486802) if ($sent_folder = $COMPOSE['param']['sent_mbox']) { rcmail_check_sent_folder($sent_folder, true); @@ -111,7 +81,8 @@ $OUTPUT->add_label('nosubject', 'nosenderwarning', 'norecipientwarning', 'nosubj 'nobodywarning', 'notsentwarning', 'notuploadedwarning', 'savingmessage', 'sendingmessage', 'messagesaved', 'converting', 'editorwarning', 'searching', 'uploading', 'uploadingmany', 'fileuploaderror', 'sendmessage', 'savenewresponse', 'responsename', 'responsetext', 'save', - 'savingresponse', 'restoresavedcomposedata', 'restoremessage', 'delete', 'restore', 'ignore'); + 'savingresponse', 'restoresavedcomposedata', 'restoremessage', 'delete', 'restore', 'ignore', + 'selectimportfile'); $OUTPUT->set_pagetitle($RCMAIL->gettext('compose')); @@ -208,14 +179,20 @@ if (!empty($msg_uid) && empty($COMPOSE['as_attachment'])) { if (!$MESSAGE->headers) { // error } - else if ($compose_mode == RCUBE_COMPOSE_REPLY) { - $COMPOSE['reply_uid'] = $msg_uid; - $COMPOSE['reply_msgid'] = $MESSAGE->headers->messageID; - $COMPOSE['references'] = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID); + else if ($compose_mode == RCUBE_COMPOSE_FORWARD || $compose_mode == RCUBE_COMPOSE_REPLY) { + if ($compose_mode == RCUBE_COMPOSE_REPLY) { + $COMPOSE['reply_uid'] = $msg_uid; - if (!empty($COMPOSE['param']['all'])) { - $MESSAGE->reply_all = $COMPOSE['param']['all']; + if (!empty($COMPOSE['param']['all'])) { + $MESSAGE->reply_all = $COMPOSE['param']['all']; + } } + else { + $COMPOSE['forward_uid'] = $msg_uid; + } + + $COMPOSE['reply_msgid'] = $MESSAGE->headers->messageID; + $COMPOSE['references'] = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID); // Save the sent message in the same folder of the message being replied to if ($RCMAIL->config->get('reply_same_folder') && ($sent_folder = $COMPOSE['mailbox']) @@ -271,6 +248,10 @@ else { } } +if (!empty($COMPOSE['reply_msgid'])) { + $OUTPUT->set_env('reply_msgid', $COMPOSE['reply_msgid']); +} + $MESSAGE->compose = array(); // get user's identities @@ -482,6 +463,11 @@ function rcmail_process_compose_params(&$COMPOSE) } } + // resolve _forward_uid=* to an absolute list of messages from a search result + if ($COMPOSE['param']['forward_uid'] == '*' && is_object($_SESSION['search'][1])) { + $COMPOSE['param']['forward_uid'] = $_SESSION['search'][1]->get(); + } + // clean HTML message body which can be submitted by URL if (!empty($COMPOSE['param']['body'])) { $COMPOSE['param']['body'] = rcmail_wash_html($COMPOSE['param']['body'], array('safe' => false, 'inline_html' => true), array()); @@ -495,6 +481,36 @@ function rcmail_process_compose_params(&$COMPOSE) // pipe compose parameters thru plugins $plugin = $RCMAIL->plugins->exec_hook('message_compose', $COMPOSE); $COMPOSE['param'] = array_merge($COMPOSE['param'], $plugin['param']); + + // add attachments listed by message_compose hook + if (is_array($plugin['attachments'])) { + foreach ($plugin['attachments'] as $attach) { + // we have structured data + if (is_array($attach)) { + $attachment = $attach + array('group' => $COMPOSE_ID); + } + // only a file path is given + else { + $filename = basename($attach); + $attachment = array( + 'group' => $COMPOSE_ID, + 'name' => $filename, + 'mimetype' => rcube_mime::file_content_type($attach, $filename), + 'path' => $attach, + ); + } + + // save attachment if valid + if (($attachment['data'] && $attachment['name']) || ($attachment['path'] && file_exists($attachment['path']))) { + $attachment = rcmail::get_instance()->plugins->exec_hook('attachment_save', $attachment); + } + + if ($attachment['status'] && !$attachment['abort']) { + unset($attachment['data'], $attachment['status'], $attachment['abort']); + $COMPOSE['attachments'][$attachment['id']] = $attachment; + } + } + } } function rcmail_compose_headers($attrib) @@ -595,7 +611,7 @@ function rcmail_compose_header_from($attrib) $text = $html = $sql_arr['signature']; if ($sql_arr['html_signature']) { - $h2t = new rcube_html2text($sql_arr['signature'], false, false); + $h2t = new rcube_html2text($sql_arr['signature'], false, true); $text = trim($h2t->get_text()); } else { @@ -608,7 +624,8 @@ function rcmail_compose_header_from($attrib) } if (!$sql_arr['html_signature']) { - $html = "<pre>" . $html . "</pre>"; + $t2h = new rcube_text2html($sql_arr['signature'], false); + $html = $t2h->get_html(); } $a_signatures[$identity_id]['text'] = $text; @@ -810,15 +827,8 @@ function rcmail_compose_part_body($part, $isHtml = false) } } - if ($part->ctype_parameters['format'] == 'flowed') { - $body = rcube_mime::unfold_flowed($body); - } - // add HTML formatting - $body = rcmail_plain_body($body); - if ($body) { - $body = '<pre>' . $body . '</pre>'; - } + $body = rcmail_plain_body($body, $part->ctype_parameters['format'] == 'flowed'); } } else { @@ -1237,6 +1247,7 @@ function rcmail_write_forward_attachments() $storage = $RCMAIL->get_storage(); $names = array(); + $refs = array(); $loaded_attachments = array(); foreach ((array)$COMPOSE['attachments'] as $attachment) { @@ -1247,7 +1258,10 @@ function rcmail_write_forward_attachments() $index = $storage->index(null, rcmail_sort_column(), rcmail_sort_order()); $COMPOSE['forward_uid'] = $index->get(); } - else { + else if (!is_array($COMPOSE['forward_uid']) && strpos($COMPOSE['forward_uid'], ':')) { + $COMPOSE['forward_uid'] = rcube_imap_generic::uncompressMessageSet($COMPOSE['forward_uid']); + } + else if (is_string($COMPOSE['forward_uid'])) { $COMPOSE['forward_uid'] = explode(',', $COMPOSE['forward_uid']); } @@ -1316,6 +1330,18 @@ function rcmail_write_forward_attachments() else if ($path) { @unlink($path); } + + if ($message->headers->messageID) { + $refs[] = $message->headers->messageID; + } + } + + // set In-Reply-To and References headers + if (count($refs) == 1) { + $COMPOSE['reply_msgid'] = $refs[0]; + } + if (!empty($refs)) { + $COMPOSE['references'] = implode(' ', $refs); } } diff --git a/program/steps/mail/copy.inc b/program/steps/mail/copy.inc index a392f309f..86586d34d 100644 --- a/program/steps/mail/copy.inc +++ b/program/steps/mail/copy.inc @@ -5,7 +5,7 @@ | program/steps/mail/copy.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2013, The Roundcube Dev Team | + | Copyright (C) 2005-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -26,11 +26,14 @@ if (!$OUTPUT->ajax_call) { // move messages if (!empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) { - $uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST); $target = rcube_utils::get_input_value('_target_mbox', rcube_utils::INPUT_POST, true); - $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true); - $copied = $RCMAIL->storage->copy_message($uids, $target, $mbox); + foreach (rcmail::get_uids() as $mbox => $uids) { + if ($mbox === $target) + $copied++; + else + $copied += (int)$RCMAIL->storage->copy_message($uids, $target, $mbox); + } if (!$copied) { // send error message diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 0211fabc4..50b1e8292 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -5,7 +5,7 @@ | program/steps/mail/func.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2013, The Roundcube Dev Team | + | Copyright (C) 2005-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -23,40 +23,8 @@ // always instantiate storage object (but not connect to server yet) $RCMAIL->storage_init(); -// set imap properties and session vars -if (strlen(trim($mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC, true)))) { - $RCMAIL->storage->set_folder(($_SESSION['mbox'] = $mbox)); -} -else if ($RCMAIL->storage) { - $_SESSION['mbox'] = $RCMAIL->storage->get_folder(); -} - -if (!empty($_GET['_page'])) { - $RCMAIL->storage->set_page(($_SESSION['page'] = intval($_GET['_page']))); -} - -$a_threading = $RCMAIL->config->get('message_threading', array()); -$message_sort_col = $RCMAIL->config->get('message_sort_col'); -$message_sort_order = $RCMAIL->config->get('message_sort_col'); - -// set default sort col/order to session -if (!isset($_SESSION['sort_col'])) { - $_SESSION['sort_col'] = $message_sort_col ? $message_sort_col : ''; -} -if (!isset($_SESSION['sort_order'])) { - $_SESSION['sort_order'] = strtoupper($message_sort_order) == 'ASC' ? 'ASC' : 'DESC'; -} - -// set threads mode -if (isset($_GET['_threads'])) { - if ($_GET['_threads']) - $a_threading[$_SESSION['mbox']] = true; - else - unset($a_threading[$_SESSION['mbox']]); - - $RCMAIL->user->save_prefs(array('message_threading' => $a_threading)); -} -$RCMAIL->storage->set_threading($a_threading[$_SESSION['mbox']]); +// init environment - set current folder, page, list mode +rcmail_init_env(); // set message set for search result if (!empty($_REQUEST['_search']) && isset($_SESSION['search']) @@ -68,6 +36,26 @@ if (!empty($_REQUEST['_search']) && isset($_SESSION['search']) $OUTPUT->set_env('search_text', $_SESSION['last_text_search']); } +// remove mbox part from _uid +if (($_uid = rcube_utils::get_input_value('_uid', RCUBE_INPUT_GPC)) && !is_array($_uid) && preg_match('/^\d+-.+/', $_uid)) { + list($_uid, $mbox) = explode('-', $_uid, 2); + if (isset($_GET['_uid'])) $_GET['_uid'] = $_uid; + if (isset($_POST['_uid'])) $_POST['_uid'] = $_uid; + $_REQUEST['_uid'] = $_uid; + unset($_uid); + + // override mbox + if (!empty($mbox)) { + $_GET['_mbox'] = $mbox; + $_POST['_mbox'] = $mbox; + $RCMAIL->storage->set_folder(($_SESSION['mbox'] = $mbox)); + } +} + +if (!empty($_SESSION['browser_caps']) && !$OUTPUT->ajax_call) { + $OUTPUT->set_env('browser_capabilities', $_SESSION['browser_caps']); +} + // set main env variables, labels and page title if (empty($RCMAIL->action) || $RCMAIL->action == 'list') { // connect to storage server and trigger error on failure @@ -88,6 +76,9 @@ if (empty($RCMAIL->action) || $RCMAIL->action == 'list') { } $OUTPUT->set_env('search_mods', rcmail_search_mods()); + + if (!empty($_SESSION['search_scope'])) + $OUTPUT->set_env('search_scope', $_SESSION['search_scope']); } $threading = (bool) $RCMAIL->storage->get_threading(); @@ -117,17 +108,13 @@ if (empty($RCMAIL->action) || $RCMAIL->action == 'list') { $RCMAIL->set_env_config(array('delete_junk', 'flag_for_deletion', 'read_when_deleted', 'skip_deleted', 'display_next', 'message_extwin', 'compose_extwin', 'forward_attachment')); - if (!empty($_SESSION['browser_caps'])) { - $OUTPUT->set_env('browser_capabilities', $_SESSION['browser_caps']); - } - if (!$OUTPUT->ajax_call) { $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash', 'movingmessage', 'copyingmessage', 'deletingmessage', 'markingmessage', - 'copy', 'move', 'quota', 'replyall', 'replylist', 'importwait'); + 'copy', 'move', 'quota', 'replyall', 'replylist', 'stillsearching'); } - $pagetitle = $RCMAIL->localize_foldername($RCMAIL->storage->mod_folder($mbox_name), true); + $pagetitle = $RCMAIL->localize_foldername($mbox_name, true); $pagetitle = str_replace($delimiter, " \xC2\xBB ", $pagetitle); $OUTPUT->set_pagetitle($pagetitle); @@ -166,6 +153,60 @@ $RCMAIL->register_action_map(array( )); +/** + * Sets storage properties and session + */ +function rcmail_init_env() +{ + global $RCMAIL; + + $a_threading = $RCMAIL->config->get('message_threading', array()); + $message_sort_col = $RCMAIL->config->get('message_sort_col'); + $message_sort_order = $RCMAIL->config->get('message_sort_order'); + + // set imap properties and session vars + if (!strlen($mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC, true))) { + $mbox = strlen($_SESSION['mbox']) ? $_SESSION['mbox'] : 'INBOX'; + } + if (!($page = intval($_GET['_page']))) { + $page = $_SESSION['page'] ? $_SESSION['page'] : 1; + } + + $RCMAIL->storage->set_folder($_SESSION['mbox'] = $mbox); + $RCMAIL->storage->set_page($_SESSION['page'] = $page); + + // set default sort col/order to session + if (!isset($_SESSION['sort_col'])) { + $_SESSION['sort_col'] = $message_sort_col ? $message_sort_col : ''; + } + if (!isset($_SESSION['sort_order'])) { + $_SESSION['sort_order'] = strtoupper($message_sort_order) == 'ASC' ? 'ASC' : 'DESC'; + } + + // set threads mode + if (isset($_GET['_threads'])) { + if ($_GET['_threads']) { + // re-set current page number when listing mode changes + if (!$a_threading[$_SESSION['mbox']]) { + $RCMAIL->storage->set_page($_SESSION['page'] = 1); + } + + $a_threading[$_SESSION['mbox']] = true; + } + else { + // re-set current page number when listing mode changes + if ($a_threading[$_SESSION['mbox']]) { + $RCMAIL->storage->set_page($_SESSION['page'] = 1); + } + + unset($a_threading[$_SESSION['mbox']]); + } + + $RCMAIL->user->save_prefs(array('message_threading' => $a_threading)); + } + + $RCMAIL->storage->set_threading($a_threading[$_SESSION['mbox']]); +} /** * Returns default search mods @@ -298,7 +339,7 @@ function rcmail_message_list($attrib) $OUTPUT->set_env('sort_col', $_SESSION['sort_col']); $OUTPUT->set_env('sort_order', $_SESSION['sort_order']); $OUTPUT->set_env('messages', array()); - $OUTPUT->set_env('coltypes', $a_show_cols); + $OUTPUT->set_env('listcols', $a_show_cols); $OUTPUT->include_script('list.js'); @@ -315,7 +356,7 @@ function rcmail_message_list($attrib) /** * return javascript commands to add rows to the message list */ -function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null) +function rcmail_js_message_list($a_headers, $insert_top=false, $a_show_cols=null) { global $RCMAIL, $OUTPUT; @@ -334,6 +375,14 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null $head_replace = true; } + // add 'folder' column to list on multi-folder searches + $search_set = $RCMAIL->storage->get_search_set(); + $multifolder = $search_set && $search_set[1]->multi; + if ($multifolder && !in_array('folder', $a_show_cols)) { + $a_show_cols[] = 'folder'; + $head_replace = true; + } + $mbox = $RCMAIL->storage->get_folder(); // make sure 'threads' and 'subject' columns are present @@ -342,8 +391,6 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null if (!in_array('threads', $a_show_cols)) array_unshift($a_show_cols, 'threads'); - $_SESSION['list_attrib']['columns'] = $a_show_cols; - // Make sure there are no duplicated columns (#1486999) $a_show_cols = array_unique($a_show_cols); @@ -364,6 +411,12 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null $OUTPUT->command('set_message_coltypes', $a_show_cols, $thead, $smart_col); + if ($multifolder && $_SESSION['search_scope'] == 'all') { + $OUTPUT->command('select_folder', ''); + } + + $OUTPUT->set_env('multifolder_listing', $multifolder); + if (empty($a_headers)) { return; } @@ -380,6 +433,14 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null if (empty($header)) continue; + // make message UIDs unique by appending the folder name + if ($multifolder) { + $header->uid .= '-'.$header->folder; + $header->flags['skip_mbox_check'] = true; + if ($header->parent_uid) + $header->parent_uid .= '-'.$header->folder; + } + $a_msg_cols = array(); $a_msg_flags = array(); @@ -398,6 +459,8 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null $cont = show_bytes($header->$col); else if ($col == 'date') $cont = $RCMAIL->format_date($header->date); + else if ($col == 'folder') + $cont = rcube::Q(rcube_charset::convert($header->folder, 'UTF7-IMAP')); else $cont = rcube::Q($header->$col); @@ -421,7 +484,7 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null $a_msg_flags['prio'] = (int) $header->priority; $a_msg_flags['ctype'] = rcube::Q($header->ctype); - $a_msg_flags['mbox'] = $mbox; + $a_msg_flags['mbox'] = $header->folder; // merge with plugin result (Deprecated, use $header->flags) if (!empty($header->list_flags) && is_array($header->list_flags)) @@ -481,7 +544,7 @@ function rcmail_message_list_head($attrib, $a_show_cols) $list_menu = ''; } - $cells = array(); + $cells = $coltypes = array(); // get name of smart From/To column in folder context if (array_search('fromto', $a_show_cols) !== false) { @@ -489,32 +552,39 @@ function rcmail_message_list_head($attrib, $a_show_cols) } foreach ($a_show_cols as $col) { + $label = ''; + $sortable = false; + // get column name switch ($col) { case 'flag': - $col_name = '<span class="flagged"> </span>'; + $col_name = html::span('flagged', ' '); break; case 'attachment': case 'priority': case 'status': - $col_name = '<span class="' . $col .'"> </span>'; + $col_name = html::span($col, ' '); break; case 'threads': $col_name = $list_menu; break; case 'fromto': - $col_name = rcube::Q($RCMAIL->gettext($smart_col)); + $label = $RCMAIL->gettext($smart_col); + $col_name = rcube::Q($label); break; default: - $col_name = rcube::Q($RCMAIL->gettext($col)); + $label = $RCMAIL->gettext($col); + $col_name = rcube::Q($label); } // make sort links if (in_array($col, $a_sort_cols)) { + $sortable = true; $col_name = html::a(array( - 'href' => "./#sort", - 'onclick' => 'return '.rcmail_output::JS_OBJECT_NAME.".command('sort','".$col."',this)", - 'title' => $RCMAIL->gettext('sortby') + 'href' => "./#sort", + 'class' => 'sortcol', + 'rel' => $col, + 'title' => $RCMAIL->gettext('sortby') ), $col_name); } else if ($col_name[0] != '<') { @@ -526,8 +596,10 @@ function rcmail_message_list_head($attrib, $a_show_cols) // put it all together $cells[] = array('className' => $class_name, 'id' => "rcm$col", 'html' => $col_name); + $coltypes[$col] = array('className' => $class_name, 'id' => "rcm$col", 'label' => $label, 'sortable' => $sortable); } + $RCMAIL->output->set_env('coltypes', $coltypes); return $cells; } @@ -583,7 +655,7 @@ function rcmail_get_messagecount_text($count = null, $page = null) $max = $RCMAIL->storage->count(NULL, $RCMAIL->storage->get_threading() ? 'THREADS' : 'ALL'); if ($max == 0) - $out = $RCMAIL->gettext('mailboxempty'); + $out = $RCMAIL->storage->get_search_set() ? $RCMAIL->gettext('nomessages') : $RCMAIL->gettext('mailboxempty'); else $out = $RCMAIL->gettext(array('name' => $RCMAIL->storage->get_threading() ? 'threadsfromto' : 'messagesfromto', 'vars' => array('from' => $start_msg, @@ -806,95 +878,29 @@ function rcmail_print_body($part, $p = array()) // plaintext postprocessing 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); + $body = rcmail_plain_body($body, $part->ctype_parameters['format'] == 'flowed'); } // allow post-processing of the message body $data = $RCMAIL->plugins->exec_hook('message_part_after', array('type' => $part->ctype_secondary, 'body' => $body, 'id' => $part->mime_id) + $data); - return $data['type'] == 'html' ? $data['body'] : html::tag('pre', array(), $data['body']); + return $data['body']; } /** * Handle links and citation marks in plain text message * * @param string Plain text string + * @param boolean Set to True if the source text is in format=flowed * * @return string Formatted HTML string */ -function rcmail_plain_body($body) +function rcmail_plain_body($body, $flowed = false) { - global $RCMAIL; - - // make links and email-addresses clickable - $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); - - // split body into single lines - $body = preg_split('/\r?\n/', $body); - $quote_level = 0; - $last = -1; - - // find/mark quoted lines... - for ($n=0, $cnt=count($body); $n < $cnt; $n++) { - if ($body[$n][0] == '>' && preg_match('/^(>+ {0,1})+/', $body[$n], $regs)) { - $q = substr_count($regs[0], '>'); - $body[$n] = substr($body[$n], strlen($regs[0])); - - 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]; - $last = $n; - } - } - else { - $q = 0; - if ($quote_level > 0) - $body[$n] = $replacer->get_replacement($replacer->add( - str_repeat('</blockquote>', $quote_level))) . $body[$n]; - } - - $quote_level = $q; - } - - $body = join("\n", $body); - - // quote plain text (don't use rcube::Q() here, to display entities "as is") - $table = get_html_translation_table(HTML_SPECIALCHARS); - unset($table['?']); - $body = strtr($body, $table); - - // colorize signature (up to <sig_max_lines> lines) - $len = strlen($body); - $sig_max_lines = $RCMAIL->config->get('sig_max_lines', 15); - - while (($sp = strrpos($body, "-- \n", $sp ? -$len+$sp-1 : 0)) !== false) { - if ($sp == 0 || $body[$sp-1] == "\n") { - // do not touch blocks with more that X lines - if (substr_count($body, "\n", $sp) < $sig_max_lines) { - $body = substr($body, 0, max(0, $sp)) - . '<span class="sig">'.substr($body, $sp).'</span>'; - } - - break; - } - } - - // insert url/mailto links and citation tags - $body = $replacer->resolve($body); + $options = array('flowed' => $flowed, 'wrap' => !$flowed); + $text2html = new rcube_text2html($body, false, $options); + $body = $text2html->get_html(); return $body; } @@ -1049,7 +1055,9 @@ function rcmail_message_headers($attrib, $headers=null) $plugin = $RCMAIL->plugins->exec_hook('message_headers_output', array( 'output' => $output_headers, 'headers' => $headers_obj, - 'exclude' => $exclude_headers + 'exclude' => $exclude_headers, // readonly + 'folder' => $MESSAGE->folder, // readonly + 'uid' => $MESSAGE->uid, // readonly )); // single header value is requested @@ -1224,8 +1232,8 @@ function rcmail_message_body($attrib) $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array('part' => $MESSAGE, 'prefix' => '')); - $out .= html::div('message-part', $plugin['prefix'] . html::tag('pre', array(), - rcmail_plain_body(rcube::Q($MESSAGE->body, 'strict', false)))); + $out .= html::div('message-part', + $plugin['prefix'] . rcmail_plain_body($MESSAGE->body)); } } @@ -1295,12 +1303,10 @@ function rcmail_message_body($attrib) function rcmail_part_image_type($part) { - $rcmail = rcmail::get_instance(); - // Skip TIFF images if browser doesn't support this format... $tiff_support = !empty($_SESSION['browser_caps']) && !empty($_SESSION['browser_caps']['tif']); // until we can convert them to JPEG - $tiff_support = $tiff_support || $rcmail->config->get('im_convert_path'); + $tiff_support = $tiff_support || rcube_image::is_convertable('image/tiff'); // Content-type regexp $mime_regex = $tiff_support ? '/^image\//i' : '/^image\/(?!tif)/i'; @@ -1693,7 +1699,8 @@ function rcmail_draftinfo_encode($p) { $parts = array(); foreach ($p as $key => $val) { - $parts[] = $key . '=' . ($key == 'folder' ? base64_encode($val) : $val); + $encode = $key == 'folder' || strpos($val, ';') !== false; + $parts[] = $key . '=' . ($encode ? 'B::' . base64_encode($val) : $val); } return join('; ', $parts); @@ -1705,7 +1712,10 @@ function rcmail_draftinfo_decode($str) foreach (preg_split('/;\s+/', $str) as $part) { list($key, $val) = explode('=', $part, 2); - if ($key == 'folder') { + if (strpos($val, 'B::') === 0) { + $val = base64_decode(substr($val, 3)); + } + else if ($key == 'folder') { $val = base64_decode($val); } @@ -1984,7 +1994,7 @@ function rcmail_search_filter($attrib) $ctypes = array('application/', 'multipart/m', 'multipart/signed', 'multipart/report'); // Build search string of "with attachment" filter - $attachment = str_repeat(' OR', count($ctypes)-1); + $attachment = trim(str_repeat(' OR', count($ctypes)-1)); foreach ($ctypes as $type) { $attachment .= ' HEADER Content-Type ' . rcube_imap_generic::escape($type); } @@ -2070,6 +2080,58 @@ function rcmail_message_import_form($attrib = array()) $content); $RCMAIL->output->add_gui_object('importform', $attrib['id'].'Frm'); + $RCMAIL->output->add_label('selectimportfile','importwait'); return html::div($attrib, $out); } + +/** + * Add groups from the given address source to the address book widget + */ +function rcmail_compose_contact_groups($abook, $source_id, $search = null, $search_mode = 0) +{ + global $RCMAIL, $OUTPUT; + + $jsresult = array(); + foreach ($abook->list_groups($search, $search_mode) as $group) { + $abook->reset(); + $abook->set_group($group['ID']); + $group_prop = $abook->get_group($group['ID']); + + // group (distribution list) with email address(es) + if ($group_prop['email']) { + foreach ((array)$group_prop['email'] as $email) { + $row_id = 'G'.$group['ID']; + $jsresult[$row_id] = format_email_recipient($email, $group['name']); + $OUTPUT->command('add_contact_row', $row_id, array( + 'contactgroup' => html::span(array('title' => $email), rcube::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' => $group['ID'], + 'title' => $RCMAIL->gettext('listgroup'), + 'onclick' => sprintf("return %s.command('pushgroup',{'source':'%s','id':'%s'},this,event)", + rcmail_output::JS_OBJECT_NAME, $source_id, $group['ID']), + ), rcube::Q($group['name']) . ' ' . html::span('action', '»'))), + 'group', + array('ID' => $group['ID'], 'name' => $group['name'], 'virtual' => true)); + } + // show group with count + else if (($result = $abook->count()) && $result->count) { + $row_id = 'E'.$group['ID']; + $jsresult[$row_id] = $group['name']; + $OUTPUT->command('add_contact_row', $row_id, array( + 'contactgroup' => rcube::Q($group['name'] . ' (' . intval($result->count) . ')')), 'group'); + } + } + + $abook->reset(); + $abook->set_group(0); + + return $jsresult; +} diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index 8f869c67c..02d57c7dc 100644 --- a/program/steps/mail/get.inc +++ b/program/steps/mail/get.inc @@ -221,7 +221,7 @@ else if (strlen($part_id)) { // TIFF to JPEG conversion, if needed $tiff_support = !empty($_SESSION['browser_caps']) && !empty($_SESSION['browser_caps']['tif']); if (!empty($_REQUEST['_embed']) && !$tiff_support - && $RCMAIL->config->get('im_convert_path') + && rcube_image::is_convertable('image/tiff') && rcmail_part_image_type($part) == 'image/tiff' ) { $tiff2jpeg = true; @@ -293,9 +293,7 @@ else if (strlen($part_id)) { $filename = rcmail_attachment_name($part); - if ($browser->ie && $browser->ver < 7) - $filename = rawurlencode(abbreviate_string($filename, 55)); - else if ($browser->ie) + if ($browser->ie) $filename = rawurlencode($filename); else $filename = addcslashes($filename, '"'); diff --git a/program/steps/mail/import.inc b/program/steps/mail/import.inc index 4f822e0e4..ef78ad945 100644 --- a/program/steps/mail/import.inc +++ b/program/steps/mail/import.inc @@ -5,7 +5,7 @@ | program/steps/mail/import.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2013, The Roundcube Dev Team | + | Copyright (C) 2005-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -16,6 +16,7 @@ | | +-----------------------------------------------------------------------+ | Author: Thomas Bruederli <roundcube@gmail.com> | + | Author: Aleksander Machniak <alec@alec.pl> | +-----------------------------------------------------------------------+ */ @@ -31,48 +32,62 @@ if (is_array($_FILES['_file'])) { if (!$err) { // check file content type first - list($mtype_primary,) = explode('/', rcube_mime::file_content_type($filepath, $_FILES['_file']['name'][$i], $_FILES['_file']['type'][$i])); + $ctype = rcube_mime::file_content_type($filepath, $_FILES['_file']['name'][$i], $_FILES['_file']['type'][$i]); + list($mtype_primary, $mtype_secondary) = explode('/', $ctype); - if (!in_array($mtype_primary, array('text','message'))) { - $OUTPUT->show_message('importmessageerror', 'error'); + if (in_array($ctype, array('application/zip', 'application/x-zip'))) { + $filepath = rcmail_zip_extract($filepath); + if (empty($filepath)) { + continue; + } + } + else if (!in_array($mtype_primary, array('text', 'message'))) { continue; } - // read the first few lines to detect header-like structure - $fp = fopen($filepath, 'r'); - do { - $line = fgets($fp); - } - while ($line !== false && trim($line) == ''); + foreach ((array) $filepath as $file) { + // read the first few lines to detect header-like structure + $fp = fopen($file, '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; - } + if (!preg_match('/^From .+/', $line) && !preg_match('/^[a-z-_]+:\s+.+/i', $line)) { + 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 = $lastline = ''; + fseek($fp, 0); + while (($line = fgets($fp)) !== false) { + // importing mbox file, split by From - lines + if ($lastline === '' && strncmp($line, 'From ', 5) === 0 && strlen($line) > 5) { + if (!empty($message)) { + // unquote ">From " lines in message body + $message = preg_replace('/\n>([>]*)From /', "\n\\1From ", $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 = ''; } - $message = ''; + continue; } - continue; + + $message .= $line; + $lastline = rtrim($line); } - $message .= $line; - $lastline = rtrim($line); - } + if (!empty($message) && $RCMAIL->storage->save_message(null, rtrim($message))) { + $imported++; + } - if (!empty($message) && $RCMAIL->storage->save_message(null, rtrim($message))) { - $imported++; + // remove temp files extracted from zip + if (is_array($filepath)) { + unlink($file); + } } } @@ -106,3 +121,39 @@ else if ($_SERVER['REQUEST_METHOD'] == 'POST') { // send html page with JS calls as response $OUTPUT->send('iframe'); + + +function rcmail_zip_extract($path) +{ + if (!class_exists('ZipArchive', false)) { + return; + } + + $rcmail = rcmail::get_instance(); + $temp_dir = $rcmail->config->get('temp_dir'); + $zip = new ZipArchive; + $files = array(); + + if ($zip->open($path)) { + for ($i = 0; $i < $zip->numFiles; $i++) { + $entry = $zip->getNameIndex($i); + $tmpfname = tempnam($temp_dir, 'zipimport'); + + if (copy("zip://$path#$entry", $tmpfname)) { + $ctype = rcube_mime::file_content_type($tmpfname, $entry); + list($mtype_primary, $mtype_secondary) = explode('/', $ctype); + + if (in_array($mtype_primary, array('text', 'message'))) { + $files[] = $tmpfname; + } + else { + unlink($tmpfname); + } + } + } + + $zip->close(); + } + + return $files; +} diff --git a/program/steps/mail/list.inc b/program/steps/mail/list.inc index 277564c38..929dda299 100644 --- a/program/steps/mail/list.inc +++ b/program/steps/mail/list.inc @@ -5,7 +5,7 @@ | program/steps/mail/list.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2007, The Roundcube Dev Team | + | Copyright (C) 2005-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -42,6 +42,7 @@ if ($sort = rcube_utils::get_input_value('_sort', rcube_utils::INPUT_GET)) { // is there a set of columns for this request? if ($cols = rcube_utils::get_input_value('_cols', rcube_utils::INPUT_GET)) { + $_SESSION['list_attrib']['columns'] = explode(',', $cols); if (!in_array('list_cols', $dont_override)) { $save_arr['list_cols'] = explode(',', $cols); } @@ -59,11 +60,12 @@ $RCMAIL->storage->folder_sync($mbox_name); // initialize searching result if search_filter is used if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL') { - $search_request = md5($mbox_name.$_SESSION['search_filter']); + $search_request = md5($mbox_name.$_SESSION['search_scope'].$_SESSION['search_filter']); $RCMAIL->storage->search($mbox_name, $_SESSION['search_filter'], RCUBE_CHARSET, rcmail_sort_column()); $_SESSION['search'] = $RCMAIL->storage->get_search_set(); $_SESSION['search_request'] = $search_request; $OUTPUT->set_env('search_request', $search_request); + $OUTPUT->set_env('search_filter', $_SESSION['search_filter']); } // fetch message headers @@ -91,7 +93,7 @@ rcmail_send_unread_count($mbox_name, !empty($_REQUEST['_refresh']), $unseen); // update message count display $pages = ceil($count/$RCMAIL->storage->get_pagesize()); -$exists = $RCMAIL->storage->count($mbox_name, 'EXISTS'); +$exists = $RCMAIL->storage->count($mbox_name, 'EXISTS', true); $OUTPUT->set_env('messagecount', $count); $OUTPUT->set_env('pagecount', $pages); @@ -100,8 +102,14 @@ $OUTPUT->set_env('current_page', $count ? $RCMAIL->storage->get_page() : 1); $OUTPUT->set_env('exists', $exists); $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count), $mbox_name); +// remove old message rows if commanded by the client +if (!empty($_REQUEST['_clear'])) { + $OUTPUT->command('clear_message_list'); +} + // add message rows -rcmail_js_message_list($a_headers, FALSE, $cols); +rcmail_js_message_list($a_headers, false, $cols); + if (isset($a_headers) && count($a_headers)) { if ($search_request) { $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $count)); diff --git a/program/steps/mail/list_contacts.inc b/program/steps/mail/list_contacts.inc index 46f81353a..0ee81135b 100644 --- a/program/steps/mail/list_contacts.inc +++ b/program/steps/mail/list_contacts.inc @@ -5,7 +5,7 @@ | program/steps/mail/list_contacts.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2012-2013, The Roundcube Dev Team | + | Copyright (C) 2012-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -23,15 +23,22 @@ $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'])); +$jsresult = array(); // Use search result if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']])) { $search = (array)$_SESSION['search'][$_REQUEST['_search']]; + $sparam = $_SESSION['search_params']['id'] == $_REQUEST['_search'] ? $_SESSION['search_params']['data'] : array(); // get records from all sources foreach ($search as $s => $set) { $CONTACTS = $RCMAIL->get_address_book($s); + // list matching groups of this source (on page one) + if ($sparam[1] && $CONTACTS->groups && $list_page == 1) { + $jsresult += rcmail_compose_contact_groups($CONTACTS, $s, $sparam[1], (int)$RCMAIL->config->get('addressbook_search_mode')); + } + // reset page $CONTACTS->set_page(1); $CONTACTS->set_pagesize(9999); @@ -78,44 +85,7 @@ else { } // list groups of this source (on page one) else if ($CONTACTS->groups && $CONTACTS->list_page == 1) { - foreach ($CONTACTS->list_groups() as $group) { - $CONTACTS->reset(); - $CONTACTS->set_group($group['ID']); - $group_prop = $CONTACTS->get_group($group['ID']); - - // group (distribution list) with email address(es) - if ($group_prop['email']) { - foreach ((array)$group_prop['email'] as $email) { - $row_id = 'G'.$group['ID']; - $jsresult[$row_id] = format_email_recipient($email, $group['name']); - $OUTPUT->command('add_contact_row', $row_id, array( - 'contactgroup' => html::span(array('title' => $email), rcube::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' => $RCMAIL->gettext('listgroup'), - 'onclick' => sprintf("return %s.command('pushgroup',{'source':'%s','id':'%s'},this,event)", rcmail_output::JS_OBJECT_NAME, $source, $group['ID']), - ), rcube::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']; - $jsresult[$row_id] = $group['name']; - $OUTPUT->command('add_contact_row', $row_id, array( - 'contactgroup' => rcube::Q($group['name'] . ' (' . intval($result->count) . ')')), 'group'); - } - } - - $CONTACTS->reset(); - $CONTACTS->set_group(0); + $jsresult = rcmail_compose_contact_groups($CONTACTS, $source); } // get contacts for this user diff --git a/program/steps/mail/mark.inc b/program/steps/mail/mark.inc index daa8c7e54..4e83f975c 100644 --- a/program/steps/mail/mark.inc +++ b/program/steps/mail/mark.inc @@ -4,7 +4,7 @@ | program/steps/mail/mark.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2013, The Roundcube Dev Team | + | Copyright (C) 2005-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -36,7 +36,7 @@ $a_flags_map = array( 'unflagged' => 'UNFLAGGED', ); -if (($uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST)) +if (($_uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST)) && ($flag = rcube_utils::get_input_value('_flag', rcube_utils::INPUT_POST)) ) { $flag = $a_flags_map[$flag] ? $a_flags_map[$flag] : strtoupper($flag); @@ -45,10 +45,12 @@ if (($uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST)) // count messages before changing anything $old_count = $RCMAIL->storage->count(NULL, $threading ? 'THREADS' : 'ALL'); $old_pages = ceil($old_count / $RCMAIL->storage->get_pagesize()); - $count = sizeof(explode(',', $uids)); } - $marked = $RCMAIL->storage->set_flag($uids, $flag); + foreach (rcmail::get_uids() as $mbox => $uids) { + $marked += (int)$RCMAIL->storage->set_flag($uids, $flag, $mbox); + $count += count($uids); + } if (!$marked) { // send error message @@ -66,7 +68,9 @@ if (($uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST)) if ($flag == 'DELETED' && $read_deleted && !empty($_POST['_ruid'])) { $ruids = rcube_utils::get_input_value('_ruid', rcube_utils::INPUT_POST); - $read = $RCMAIL->storage->set_flag($ruids, 'SEEN'); + foreach (rcmail::get_uids($ruids) as $mbox => $uids) { + $read += (int)$RCMAIL->storage->set_flag($uids, 'SEEN', $mbox); + } if ($read && !$skip_deleted) { $OUTPUT->command('flag_deleted_as_read', $ruids); @@ -74,7 +78,9 @@ if (($uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST)) } if ($flag == 'SEEN' || $flag == 'UNSEEN' || ($flag == 'DELETED' && !$skip_deleted)) { - rcmail_send_unread_count($RCMAIL->storage->get_folder()); + foreach (rcmail::get_uids() as $mbox => $uids) { + rcmail_send_unread_count($mbox); + } } else if ($flag == 'DELETED' && $skip_deleted) { if ($_POST['_from'] == 'show') { @@ -128,7 +134,7 @@ if (($uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST)) } // add new rows from next page (if any) - if ($count && $uids != '*' && ($jump_back || $nextpage_count > 0)) { + if ($old_count && $_uids != '*' && ($jump_back || $nextpage_count > 0)) { $a_headers = $RCMAIL->storage->list_messages($mbox, NULL, rcmail_sort_column(), rcmail_sort_order(), $jump_back ? NULL : $count); diff --git a/program/steps/mail/move_del.inc b/program/steps/mail/move_del.inc index 7564bb89d..d98d49d1f 100644 --- a/program/steps/mail/move_del.inc +++ b/program/steps/mail/move_del.inc @@ -5,7 +5,7 @@ | program/steps/mail/move_del.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2009, The Roundcube Dev Team | + | Copyright (C) 2005-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -32,13 +32,23 @@ $trash = $RCMAIL->config->get('trash_mbox'); // move messages if ($RCMAIL->action == 'move' && !empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) { - $count = sizeof(explode(',', ($uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST)))); $target = rcube_utils::get_input_value('_target_mbox', rcube_utils::INPUT_POST, true); - $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true); - - $moved = $RCMAIL->storage->move_message($uids, $target, $mbox); + $trash = $RCMAIL->config->get('trash_mbox'); + + $success = true; + foreach (rcmail::get_uids() as $mbox => $uids) { + if ($mbox === $target) { + $count += count($uids); + } + else if ($RCMAIL->storage->move_message($uids, $target, $mbox)) { + $count += count($uids); + } + else { + $success = false; + } + } - if (!$moved) { + if (!$success) { // send error message if ($_POST['_from'] != 'show') $OUTPUT->command('list_mailbox'); @@ -47,17 +57,23 @@ if ($RCMAIL->action == 'move' && !empty($_POST['_uid']) && strlen($_POST['_targe exit; } else { - $OUTPUT->show_message('messagemoved', 'confirmation'); + $OUTPUT->show_message('messagemoved', 'confirmation'); } - $addrows = true; + if (!empty($_POST['_refresh'])) { + // FIXME: send updated message rows instead of releading the entire list + $OUTPUT->command('refresh_list'); + } + else { + $addrows = true; + } } // delete messages else if ($RCMAIL->action=='delete' && !empty($_POST['_uid'])) { - $count = sizeof(explode(',', ($uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST)))); - $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true); - - $del = $RCMAIL->storage->delete_message($uids, $mbox); + foreach (rcmail::get_uids() as $mbox => $uids) { + $del += (int)$RCMAIL->storage->delete_message($uids, $mbox); + $count += count($uids); + } if (!$del) { // send error message @@ -68,7 +84,7 @@ else if ($RCMAIL->action=='delete' && !empty($_POST['_uid'])) { exit; } else { - $OUTPUT->show_message('messagedeleted', 'confirmation'); + $OUTPUT->show_message('messagedeleted', 'confirmation'); } $addrows = true; @@ -150,7 +166,7 @@ else { $OUTPUT->command('set_trash_count', $exists); } else if ($target !== null && $target === $trash) { - $OUTPUT->command('set_trash_count', $RCMAIL->storage->count($trash, 'EXISTS')); + $OUTPUT->command('set_trash_count', $RCMAIL->storage->count($trash, 'EXISTS', true)); } } diff --git a/program/steps/mail/search.inc b/program/steps/mail/search.inc index a80887254..4aa22e14b 100644 --- a/program/steps/mail/search.inc +++ b/program/steps/mail/search.inc @@ -21,6 +21,8 @@ $REMOTE_REQUEST = TRUE; +@set_time_limit(170); // extend default max_execution_time to ~3 minutes + // reset list_page and old search results $RCMAIL->storage->set_page(1); $RCMAIL->storage->set_search_set(NULL); @@ -35,9 +37,12 @@ $str = rcube_utils::get_input_value('_q', rcube_utils::INPUT_GET, true); $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GET, true); $filter = rcube_utils::get_input_value('_filter', rcube_utils::INPUT_GET); $headers = rcube_utils::get_input_value('_headers', rcube_utils::INPUT_GET); +$scope = rcube_utils::get_input_value('_scope', rcube_utils::INPUT_GET); +$continue = rcube_utils::get_input_value('_continue', rcube_utils::INPUT_GET); $subject = array(); -$search_request = md5($mbox.$filter.$str); +$filter = trim($filter); +$search_request = md5($mbox.$scope.$filter.$str); // add list filter string $search_str = $filter && $filter != 'ALL' ? $filter : ''; @@ -83,8 +88,9 @@ else if (strlen(trim($str))) { } // save search modifiers for the current folder to user prefs + $mkey = $scope == 'all' ? '*' : $mbox; $search_mods = rcmail_search_mods(); - $search_mods[$mbox] = array_fill_keys(array_keys($subject), 1); + $search_mods[$mkey] = array_fill_keys(array_keys($subject), 1); $RCMAIL->user->save_prefs(array('search_mods' => $search_mods)); } @@ -106,9 +112,26 @@ if (!empty($subject)) { $search_str = trim($search_str); $sort_column = rcmail_sort_column(); +// set message set for already stored (but incomplete) search request +if (!empty($continue) && isset($_SESSION['search']) && $_SESSION['search_request'] == $continue) { + $RCMAIL->storage->set_search_set($_SESSION['search']); + $search_str = $_SESSION['search'][0]; +} + // execute IMAP search if ($search_str) { - $RCMAIL->storage->search($mbox, $search_str, $imap_charset, $sort_column); + // search all, current or subfolders folders + if ($scope == 'all') { + $mboxes = $RCMAIL->storage->list_folders_subscribed('', '*', 'mail', null, true); + natcasesort($mboxes); // we want natural alphabetic sorting of folders in the result set + } + else if ($scope == 'sub') { + $mboxes = $RCMAIL->storage->list_folders_subscribed($mbox, '*', 'mail'); + if ($mbox != 'INBOX' && $mboxes[0] == 'INBOX') + array_shift($mboxes); + } + + $result = $RCMAIL->storage->search($mboxes, $search_str, $imap_charset, $sort_column); } // save search results in session @@ -121,38 +144,54 @@ if ($search_str) { $_SESSION['last_text_search'] = $str; } $_SESSION['search_request'] = $search_request; +$_SESSION['search_scope'] = $scope; // Get the headers -$result_h = $RCMAIL->storage->list_messages($mbox, 1, $sort_column, rcmail_sort_order()); -$count = $RCMAIL->storage->count($mbox, $RCMAIL->storage->get_threading() ? 'THREADS' : 'ALL'); +if (!$result->incomplete) { + $result_h = $RCMAIL->storage->list_messages($mbox, 1, $sort_column, rcmail_sort_order()); + $count = $RCMAIL->storage->count($mbox, $RCMAIL->storage->get_threading() ? 'THREADS' : 'ALL'); +} // Make sure we got the headers if (!empty($result_h)) { - rcmail_js_message_list($result_h); + rcmail_js_message_list($result_h, false); if ($search_str) { $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $RCMAIL->storage->count(NULL, 'ALL'))); } // remember last HIGHESTMODSEQ value (if supported) // we need it for flag updates in check-recent - $data = $RCMAIL->storage->folder_data($mbox_name); - if (!empty($data['HIGHESTMODSEQ'])) { - $_SESSION['list_mod_seq'] = $data['HIGHESTMODSEQ']; + if ($mbox !== null) { + $data = $RCMAIL->storage->folder_data($mbox); + if (!empty($data['HIGHESTMODSEQ'])) { + $_SESSION['list_mod_seq'] = $data['HIGHESTMODSEQ']; + } } } // handle IMAP errors (e.g. #1486905) -else if ($err_code = $RCMAIL->storage->get_error_code()) { +else if ($err_code = $RCMAIL->storage->get_error_code()) { $RCMAIL->display_server_error(); } +// advice the client to re-send the (cross-folder) search request +else if ($result->incomplete) { + $count = 0; // keep UI locked + $OUTPUT->command('continue_search', $search_request); +} else { $OUTPUT->show_message('searchnomatch', 'notice'); + $OUTPUT->set_env('multifolder_listing', (bool)$result->multi); + if ($result->multi && $scope == 'all') + $OUTPUT->command('select_folder', ''); } // update message count display $OUTPUT->set_env('search_request', $search_str ? $search_request : ''); +$OUTPUT->set_env('search_filter', $_SESSION['search_filter']); +$OUTPUT->set_env('threading', $RCMAIL->storage->get_threading()); $OUTPUT->set_env('messagecount', $count); $OUTPUT->set_env('pagecount', ceil($count/$RCMAIL->storage->get_pagesize())); -$OUTPUT->set_env('exists', $RCMAIL->storage->count($mbox_name, 'EXISTS')); +$OUTPUT->set_env('exists', $mbox === null ? 0 : $RCMAIL->storage->count($mbox, 'EXISTS')); $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count, 1), $mbox); +$OUTPUT->set_pagetitle($RCMAIL->gettext(array('name' => 'searchfor', 'vars' => array('q' => $str)))); $OUTPUT->send(); diff --git a/program/steps/mail/search_contacts.inc b/program/steps/mail/search_contacts.inc index 4d5abf9ef..d56581695 100644 --- a/program/steps/mail/search_contacts.inc +++ b/program/steps/mail/search_contacts.inc @@ -5,7 +5,7 @@ | program/steps/mail/search_contacts.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2013, The Roundcube Dev Team | + | Copyright (C) 2013-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -27,12 +27,18 @@ $afields = $RCMAIL->config->get('contactlist_fields'); $page_size = $RCMAIL->config->get('addressbook_pagesize', $RCMAIL->config->get('pagesize', 50)); $records = array(); $search_set = array(); +$jsresult = array(); foreach ($sources as $s) { $source = $RCMAIL->get_address_book($s['id']); $source->set_page(1); $source->set_pagesize(9999); + // list matching groups of this source + if ($source->groups) { + $jsresult += rcmail_compose_contact_groups($source, $s['id'], $search, $search_mode); + } + // get contacts count $result = $source->search($afields, $search, $search_mode, true, true, 'email'); @@ -53,6 +59,8 @@ foreach ($sources as $s) { unset($result); } +$group_count = count($jsresult); + // sort the records ksort($records, SORT_LOCALE_STRING); @@ -98,7 +106,7 @@ if (!empty($result) && $result->count > 0) { $OUTPUT->command('set_env', 'source', ''); $OUTPUT->command('unselect_directory'); } -else { +else if (!$group_count) { $OUTPUT->show_message('nocontactsfound', 'notice'); } diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index 2255acc13..7ae03e522 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -201,7 +201,7 @@ if (!empty($headers['Reply-To'])) { $headers['Mail-Reply-To'] = $headers['Reply-To']; } if ($hdr = rcube_utils::get_input_value('_followupto', rcube_utils::INPUT_POST, TRUE, $message_charset)) { - $headers['Mail-Followup-To'] = rcmail_email_input_format(); + $headers['Mail-Followup-To'] = rcmail_email_input_format($hdr); } // remember reply/forward UIDs in special headers @@ -209,7 +209,7 @@ if (!empty($COMPOSE['reply_uid']) && $savedraft) { $headers['X-Draft-Info'] = array('type' => 'reply', 'uid' => $COMPOSE['reply_uid']); } else if (!empty($COMPOSE['forward_uid']) && $savedraft) { - $headers['X-Draft-Info'] = array('type' => 'forward', 'uid' => $COMPOSE['forward_uid']); + $headers['X-Draft-Info'] = array('type' => 'forward', 'uid' => rcube_imap_generic::compressMessageSet($COMPOSE['forward_uid'])); } if (!empty($COMPOSE['reply_msgid'])) { @@ -281,13 +281,23 @@ if ($isHtml) { if (!$savedraft) { if ($isHtml) { - // remove signature's div ID - $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'; - $message_body = preg_replace('/<blockquote>/', - '<blockquote type="cite" style="'.$bstyle.'">', $message_body); + $b_style = 'padding: 0 0.4em; border-left: #1010ff 2px solid; margin: 0'; + $pre_style = 'margin: 0; padding: 0; font-family: monospace'; + + $message_body = preg_replace( + array( + // remove signature's div ID + '/\s*id="_rc_sig"/', + // add inline css for blockquotes and container + '/<blockquote>/', + '/<div class="pre">/' + ), + array( + '', + '<blockquote type="cite" style="'.$b_style.'">', + '<div class="pre" style="'.$pre_style.'">' + ), + $message_body); } // Check spelling before send @@ -541,10 +551,16 @@ if (!$savedraft) { } // set replied/forwarded flag - if ($COMPOSE['reply_uid']) - $RCMAIL->storage->set_flag($COMPOSE['reply_uid'], 'ANSWERED', $COMPOSE['mailbox']); - else if ($COMPOSE['forward_uid']) - $RCMAIL->storage->set_flag($COMPOSE['forward_uid'], 'FORWARDED', $COMPOSE['mailbox']); + if ($COMPOSE['reply_uid']) { + foreach (rcmail::get_uids($COMPOSE['reply_uid'], $COMPOSE['mailbox']) as $mbox => $uids) { + $RCMAIL->storage->set_flag($uids, 'ANSWERED', $mbox); + } + } + else if ($COMPOSE['forward_uid']) { + foreach (rcmail::get_uids($COMPOSE['forward_uid'], $COMPOSE['mailbox']) as $mbox => $uids) { + $RCMAIL->storage->set_flag($uids, 'FORWARDED', $mbox); + } + } } // Determine which folder to save message @@ -913,7 +929,8 @@ function rcmail_generic_message_footer($isHtml) if (!preg_match('/\.(php|ini|conf)$/', $file) && strpos($file, '/etc/') === false) { $footer = file_get_contents($file); if ($isHtml && !$html_footer) { - $footer = '<pre>' . $footer . '</pre>'; + $t2h = new rcube_text2html($footer, false); + $footer = $t2h->get_html(); } return $footer; } diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc index 9498d1dc5..beb2cc6e9 100644 --- a/program/steps/mail/show.inc +++ b/program/steps/mail/show.inc @@ -102,7 +102,7 @@ if ($uid) { } if (empty($_SESSION['browser_caps']['tif']) && ($key = array_search('image/tiff', $mimetypes)) !== false) { // we can convert tiff to jpeg - if (!$RCMAIL->config->get('im_convert_path')) { + if (!rcube_image::is_convertable('image/tiff')) { unset($mimetypes[$key]); } } diff --git a/program/steps/mail/viewsource.inc b/program/steps/mail/viewsource.inc index 0328d9600..f988f679a 100644 --- a/program/steps/mail/viewsource.inc +++ b/program/steps/mail/viewsource.inc @@ -33,9 +33,7 @@ if ($uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_GET)) { $filename = ($subject ? $subject : $RCMAIL->config->get('product_name', 'email')) . '.eml'; $browser = $RCMAIL->output->browser; - if ($browser->ie && $browser->ver < 7) - $filename = rawurlencode(abbreviate_string($filename, 55)); - else if ($browser->ie) + if ($browser->ie) $filename = rawurlencode($filename); else $filename = addcslashes($filename, '"'); |