diff options
Diffstat (limited to 'program/steps')
34 files changed, 699 insertions, 411 deletions
diff --git a/program/steps/addressbook/copy.inc b/program/steps/addressbook/copy.inc index 9114cb1fd..e4e276591 100644 --- a/program/steps/addressbook/copy.inc +++ b/program/steps/addressbook/copy.inc @@ -88,9 +88,9 @@ foreach ($cids as $source => $cid) { } } else { - $record = $result->first(); - $ids[] = $record['ID']; - $errormsg = empty($a_record['email']) ? 'contactnameexists' : 'contactexists'; + $record = $result->first(); + $ids[] = $record['ID']; + $errormsg = empty($email) ? 'contactnameexists' : 'contactexists'; } } diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index b33396baf..be0dd2a33 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -160,7 +160,7 @@ function rcmail_contact_source($source=null, $init_env=false, $writable=false) return $CONTACTS; $OUTPUT->set_env('readonly', $CONTACTS->readonly); - $OUTPUT->set_env('source', $source); + $OUTPUT->set_env('source', (string) $source); // reduce/extend $CONTACT_COLTYPES with specification from the current $CONTACT object if (is_array($CONTACTS->coltypes)) { diff --git a/program/steps/addressbook/import.inc b/program/steps/addressbook/import.inc index 33e473242..5dee5c06a 100644 --- a/program/steps/addressbook/import.inc +++ b/program/steps/addressbook/import.inc @@ -308,10 +308,11 @@ function rcmail_import_buttons($attrib) $out = $OUTPUT->button(array('command' => 'list', 'prop' => $target, 'label' => 'done') + $attrib); } else { - $out = $OUTPUT->button(array('command' => 'list', 'label' => 'cancel') + $attrib); - $out .= ' '; + $cancel = $OUTPUT->button(array('command' => 'list', 'label' => 'cancel') + $attrib); $attrib['class'] = trim($attrib['class'] . ' mainaction'); - $out .= $OUTPUT->button(array('command' => 'import', 'label' => 'import') + $attrib); + $out = $OUTPUT->button(array('command' => 'import', 'label' => 'import') + $attrib); + $out .= ' '; + $out .= $cancel; } return $out; diff --git a/program/steps/addressbook/move.inc b/program/steps/addressbook/move.inc index 6a70e7bda..7a730af77 100644 --- a/program/steps/addressbook/move.inc +++ b/program/steps/addressbook/move.inc @@ -97,9 +97,9 @@ foreach ($cids as $source => $source_cids) { } } else { - $record = $result->first(); - $ids[] = $record['ID']; - $errormsg = empty($a_record['email']) ? 'contactnameexists' : 'contactexists'; + $record = $result->first(); + $ids[] = $record['ID']; + $errormsg = empty($email) ? 'contactnameexists' : 'contactexists'; } } diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc index 94556f96b..7451f433b 100644 --- a/program/steps/addressbook/save.inc +++ b/program/steps/addressbook/save.inc @@ -165,6 +165,10 @@ if (!empty($cid)) { $a_js_cols[] = rcube::Q((string)$record[$col]); } + // performance: unset some big data items we don't need here + $record = array_intersect_key($record, array('ID' => 1,'email' => 1,'name' => 1)); + $record['_type'] = 'person'; + // update the changed col in list $OUTPUT->command('parent.update_contact_row', $cid, $a_js_cols, $newcid, $source, $record); 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, '"'); diff --git a/program/steps/settings/edit_folder.inc b/program/steps/settings/edit_folder.inc index fc6b2cd16..6b7bd08d2 100644 --- a/program/steps/settings/edit_folder.inc +++ b/program/steps/settings/edit_folder.inc @@ -139,6 +139,7 @@ function rcmail_folder_form($attrib) 'unsubscribed' => true, 'skip_noinferiors' => true, 'exceptions' => $exceptions, + 'additional' => strlen($selected) ? array($selected) : null, )); $form['props']['fieldsets']['location']['content']['path'] = array( diff --git a/program/steps/settings/edit_identity.inc b/program/steps/settings/edit_identity.inc index f208c8a05..3f7b6a58a 100644 --- a/program/steps/settings/edit_identity.inc +++ b/program/steps/settings/edit_identity.inc @@ -71,7 +71,7 @@ function rcube_identity_form($attrib) $RCMAIL->html_editor('identity'); // add some labels to client - $OUTPUT->add_label('noemailwarning', 'nonamewarning', 'converting', 'editorwarning'); + $OUTPUT->add_label('noemailwarning', 'converting', 'editorwarning'); $i_size = !empty($attrib['size']) ? $attrib['size'] : 40; $t_rows = !empty($attrib['textarearows']) ? $attrib['textarearows'] : 6; diff --git a/program/steps/settings/edit_prefs.inc b/program/steps/settings/edit_prefs.inc index 05f4db6a6..b72c3e73f 100644 --- a/program/steps/settings/edit_prefs.inc +++ b/program/steps/settings/edit_prefs.inc @@ -51,6 +51,10 @@ function rcmail_user_prefs_form($attrib) $out = $form_start; + if(!empty($SECTIONS[$CURR_SECTION]['header'])) { + $out .= html::div(array('id' => 'preferences-header', 'class' =>'boxcontent'), $SECTIONS[$CURR_SECTION]['header']); + } + foreach ($SECTIONS[$CURR_SECTION]['blocks'] as $class => $block) { if (!empty($block['options'])) { $table = new html_table(array('cols' => 2)); diff --git a/program/steps/settings/edit_response.inc b/program/steps/settings/edit_response.inc index 760f28290..6d3c3dc41 100644 --- a/program/steps/settings/edit_response.inc +++ b/program/steps/settings/edit_response.inc @@ -35,7 +35,7 @@ if (($key = rcube_utils::get_input_value('_key', rcube_utils::INPUT_GPC))) { // save response if ($RCMAIL->action == 'save-response' && isset($_POST['_name']) && !$RESPONSE_RECORD['static']) { $name = trim(rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST)); - $text = trim(rcube_utils::get_input_value('_text', rcube_utils::INPUT_POST)); + $text = trim(rcube_utils::get_input_value('_text', rcube_utils::INPUT_POST, true)); if (!empty($name) && !empty($text)) { $dupes = 0; diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc index b09ea03ce..1bcfb4cfc 100644 --- a/program/steps/settings/folders.inc +++ b/program/steps/settings/folders.inc @@ -45,7 +45,7 @@ if ($RCMAIL->action == 'subscribe') { if ($result) { // Handle subscription of protected folder (#1487656) if ($RCMAIL->config->get('protect_default_folders') - && in_array($mbox, (array)$RCMAIL->config->get('default_folders')) + && $STORAGE->is_special_folder($mbox) ) { $OUTPUT->command('disable_subscription', $mbox); } @@ -221,16 +221,15 @@ function rcube_subscription_form($attrib) // get folders from server $STORAGE->clear_cache('mailboxes', true); - $a_unsubscribed = $STORAGE->list_folders(); - $a_subscribed = $STORAGE->list_folders_subscribed('', '*', null, null, true); // unsorted - $delimiter = $STORAGE->get_hierarchy_delimiter(); - $namespace = $STORAGE->get_namespace(); - $a_js_folders = array(); - $seen = array(); - $list_folders = array(); - - $default_folders = (array) $RCMAIL->config->get('default_folders'); + $a_unsubscribed = $STORAGE->list_folders(); + $a_subscribed = $STORAGE->list_folders_subscribed('', '*', null, null, true); // unsorted + $delimiter = $STORAGE->get_hierarchy_delimiter(); + $namespace = $STORAGE->get_namespace(); + $special_folders = array_flip(array_merge(array('inbox' => 'INBOX'), $STORAGE->get_special_folders())); $protect_default = $RCMAIL->config->get('protect_default_folders'); + $a_js_folders = array(); + $seen = array(); + $list_folders = array(); // pre-process folders list foreach ($a_unsubscribed as $i => $folder) { @@ -291,7 +290,7 @@ function rcube_subscription_form($attrib) $idx = $i + 1; $sub_key = array_search($folder['id'], $a_subscribed); $subscribed = $sub_key !== false; - $protected = $protect_default && in_array($folder['id'], $default_folders); + $protected = $protect_default && isset($special_folders[$folder['id']]); $noselect = false; $classes = array($i%2 ? 'even' : 'odd'); @@ -368,7 +367,7 @@ function rcube_subscription_form($attrib) $OUTPUT->add_gui_object('subscriptionlist', $attrib['id']); $OUTPUT->set_env('subscriptionrows', $a_js_folders); - $OUTPUT->set_env('defaultfolders', $default_folders); + $OUTPUT->set_env('defaultfolders', array_keys($special_folders)); $OUTPUT->set_env('delimiter', $delimiter); return $form_start . $table->show($attrib) . $form_end; diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index 7c36df3b1..4b4575f10 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -346,6 +346,7 @@ function rcmail_user_prefs($current = null) $license_link = $meta['license-url'] ? html::a(array('href' => $meta['license-url'], 'target' => '_blank'), rcube::Q($meta['license'])) : rcube::Q($meta['license']); } + $skinnames[] = mb_strtolower($skinname); $blocks['skin']['options'][$skin]['content'] = html::label(array('class' => 'skinselection'), html::span('skinitem', $input->show($config['skin'], array('value' => $skin, 'id' => $field_id.$skin))) . html::span('skinitem', html::img(array('src' => $thumbnail, 'class' => 'skinthumbnail', 'alt' => $skin, 'width' => 64, 'height' => 64))) . @@ -354,6 +355,7 @@ function rcmail_user_prefs($current = null) html::span('skinlicense', $license_link ? $RCMAIL->gettext('license').': ' . $license_link : '')) ); } + array_multisort($blocks['skin']['options'], SORT_ASC, SORT_STRING, $skinnames); } } @@ -376,13 +378,16 @@ function rcmail_user_prefs($current = null) if ($current) { $product_name = $RCMAIL->config->get('product_name', 'Roundcube Webmail'); $RCMAIL->output->add_script(sprintf("%s.check_protocol_handler('%s', '#mailtoprotohandler');", - rcmail_output::JS_OBJECT_NAME, rcube::JQ($product_name)), 'foot'); + rcmail_output::JS_OBJECT_NAME, rcube::JQ($product_name)), 'docready'); } $blocks['browser']['options']['mailtoprotohandler'] = array( 'content' => html::a(array( - 'href' => '#', - 'id' => 'mailtoprotohandler'), rcube::Q($RCMAIL->gettext('mailtoprotohandler'))), + 'href' => '#', + 'id' => 'mailtoprotohandler' + ), + rcube::Q($RCMAIL->gettext('mailtoprotohandler'))) . + html::span('mailtoprotohandler-status', ''), ); break; @@ -1032,7 +1037,8 @@ function rcmail_user_prefs($current = null) } // Configure special folders - if (!isset($no_override['default_folders']) && $current) { + $set = array('drafts_mbox', 'sent_mbox', 'junk_mbox', 'trash_mbox'); + if ($current && count(array_intersect($no_override, $set)) < 4) { $select = $RCMAIL->folder_selector(array( 'noselection' => '---', 'realnames' => true, @@ -1040,10 +1046,10 @@ function rcmail_user_prefs($current = null) 'folder_filter' => 'mail', 'folder_rights' => 'w', )); - } - // #1486114, #1488279, #1489219 - $onchange = "if ($(this).val() == 'INBOX') $(this).val('')"; + // #1486114, #1488279, #1489219 + $onchange = "if ($(this).val() == 'INBOX') $(this).val('')"; + } if (!isset($no_override['drafts_mbox'])) { if (!$current) { @@ -1235,6 +1241,13 @@ function rcmail_user_prefs($current = null) $sections[$idx]['blocks'] = $data['blocks']; } + $data = $RCMAIL->plugins->exec_hook('preferences_section_header', + array('section' => $sect['id'], 'header' => '', 'current' => $current)); + + if(!empty($data['header'])) { + $sections[$idx]['header'] = $data['header']; + } + return array($sections, $plugin['cols']); } @@ -1284,13 +1297,11 @@ function rcmail_update_folder_row($name, $oldname=null, $subscribe=false, $class { global $RCMAIL, $OUTPUT; - $default_folders = (array) $RCMAIL->config->get('default_folders'); $protect_folders = $RCMAIL->config->get('protect_default_folders'); - - $storage = $RCMAIL->get_storage(); - $delimiter = $storage->get_hierarchy_delimiter(); - $name_utf8 = rcube_charset::convert($name, 'UTF7-IMAP'); - $protected = $protect_folders && in_array($name, $default_folders); + $storage = $RCMAIL->get_storage(); + $delimiter = $storage->get_hierarchy_delimiter(); + $name_utf8 = rcube_charset::convert($name, 'UTF7-IMAP'); + $protected = $protect_folders && $storage->is_special_folder($name); $foldersplit = explode($delimiter, $storage->mod_folder($name)); $level = count($foldersplit) - 1; diff --git a/program/steps/settings/responses.inc b/program/steps/settings/responses.inc index 35a2a1b64..06093b3b8 100644 --- a/program/steps/settings/responses.inc +++ b/program/steps/settings/responses.inc @@ -22,7 +22,7 @@ if (!empty($_POST['_insert'])) { $name = trim(rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST)); - $text = trim(rcube_utils::get_input_value('_text', rcube_utils::INPUT_POST)); + $text = trim(rcube_utils::get_input_value('_text', rcube_utils::INPUT_POST, true)); if (!empty($name) && !empty($text)) { $dupes = 0; diff --git a/program/steps/settings/save_identity.inc b/program/steps/settings/save_identity.inc index 1584c5f00..77245b988 100644 --- a/program/steps/settings/save_identity.inc +++ b/program/steps/settings/save_identity.inc @@ -26,8 +26,8 @@ $a_boolean_cols = array('standard', 'html_signature'); $updated = $default_id = false; // check input -if (IDENTITIES_LEVEL != 4 && (empty($_POST['_name']) || (empty($_POST['_email']) && IDENTITIES_LEVEL != 1 && IDENTITIES_LEVEL != 3))) { - $OUTPUT->show_message('formincomplete', 'warning'); +if (empty($_POST['_email']) && (IDENTITIES_LEVEL == 0 || IDENTITIES_LEVEL == 2)) { + $OUTPUT->show_message('noemailwarning', 'warning'); $RCMAIL->overwrite_action('edit-identity'); return; } diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc index f71eee39a..7a17f21f4 100644 --- a/program/steps/settings/save_prefs.inc +++ b/program/steps/settings/save_prefs.inc @@ -121,12 +121,12 @@ case 'server': case 'folders': $a_user_prefs = array( 'show_real_foldernames' => isset($_POST['_show_real_foldernames']) ? true : false, - 'drafts_mbox' => rcube_utils::get_input_value('_drafts_mbox', rcube_utils::INPUT_POST, true), - 'sent_mbox' => rcube_utils::get_input_value('_sent_mbox', rcube_utils::INPUT_POST, true), - 'junk_mbox' => rcube_utils::get_input_value('_junk_mbox', rcube_utils::INPUT_POST, true), - 'trash_mbox' => rcube_utils::get_input_value('_trash_mbox', rcube_utils::INPUT_POST, true), ); + foreach (rcube_storage::$folder_types as $type) { + $a_user_prefs[$type . '_mbox'] = rcube_utils::get_input_value('_' . $type . '_mbox', rcube_utils::INPUT_POST, true); + }; + break; } @@ -191,21 +191,15 @@ case 'addressbook': break; case 'folders': - // special handling for 'default_folders' - if (in_array('default_folders', (array)$CONFIG['dont_override'])) { - foreach (array('drafts_mbox','sent_mbox','junk_mbox','trash_mbox') as $p) { - $a_user_prefs[$p] = $CONFIG[$p]; - } - } - else { - $a_user_prefs['default_folders'] = array('INBOX'); - foreach (array('drafts_mbox','sent_mbox','junk_mbox','trash_mbox') as $p) { - if ($a_user_prefs[$p]) { - $a_user_prefs['default_folders'][] = $a_user_prefs[$p]; - } - } + $storage = $RCMAIL->get_storage(); + $specials = array(); + + foreach (rcube_storage::$folder_types as $type) { + $specials[$type] = $a_user_prefs[$type . '_mbox']; } + $storage->set_special_folders($specials); + break; } diff --git a/program/steps/utils/html2text.inc b/program/steps/utils/html2text.inc index c01443b22..f6e2bec4d 100644 --- a/program/steps/utils/html2text.inc +++ b/program/steps/utils/html2text.inc @@ -19,7 +19,12 @@ +-----------------------------------------------------------------------+ */ -$html = $HTTP_RAW_POST_DATA; +$html = stream_get_contents(fopen('php://input', 'r')); + +// strip slashes if magic_quotes enabled +if (get_magic_quotes_gpc() || get_magic_quotes_runtime()) { + $html = stripslashes($html); +} // Replace emoticon images with its text representation $html = $RCMAIL->replace_emoticons($html); diff --git a/program/steps/utils/modcss.inc b/program/steps/utils/modcss.inc index c8a7cb524..f3d8d897a 100644 --- a/program/steps/utils/modcss.inc +++ b/program/steps/utils/modcss.inc @@ -5,7 +5,7 @@ | program/steps/utils/modcss.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2007-2012, The Roundcube Dev Team | + | Copyright (C) 2007-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -33,27 +33,47 @@ if (!preg_match('~^(https?)://~i', $realurl, $matches)) { exit("Invalid URL"); } -if (!ini_get('allow_url_fopen')) { +if (ini_get('allow_url_fopen')) { + $scheme = strtolower($matches[1]); + $options = array( + $scheme => array( + 'method' => 'GET', + 'timeout' => 15, + ) + ); + + $context = stream_context_create($options); + $source = @file_get_contents($realurl, false, $context); + + // php.net/manual/en/reserved.variables.httpresponseheader.php + $headers = implode("\n", (array) $http_response_header); +} +else if (function_exists('curl_init')) { + $curl = curl_init($realurl); + curl_setopt($curl, CURLOPT_TIMEOUT, 15); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 15); + curl_setopt($curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + curl_setopt($curl, CURLOPT_ENCODING, ''); + curl_setopt($curl, CURLOPT_HEADER, true); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + $data = curl_exec($curl); + + if ($data !== false) { + list($headers, $source) = explode("\r\n\r\n", $data, 2); + } + else { + $headers = false; + $source = false; + } +} +else { header('HTTP/1.1 403 Forbidden'); exit("HTTP connections disabled"); } -$scheme = strtolower($matches[1]); -$options = array( - $scheme => array( - 'method' => 'GET', - 'timeout' => 15, - ) -); - -$context = stream_context_create($options); -$source = @file_get_contents($realurl, false, $context); - -// php.net/manual/en/reserved.variables.httpresponseheader.php -$headers = implode("\n", (array)$http_response_header); -$ctype = '~Content-Type:\s+text/(css|plain)~i'; +$ctype_regexp = '~Content-Type:\s+text/(css|plain)~i'; -if ($source !== false && preg_match($ctype, $headers)) { +if ($source !== false && preg_match($ctype_regexp, $headers)) { header('Content-Type: text/css'); echo rcube_utils::mod_css_styles($source, preg_replace('/[^a-z0-9]/i', '', $_GET['_c'])); exit; diff --git a/program/steps/utils/spell.inc b/program/steps/utils/spell.inc index c8807e32f..696fa6005 100644 --- a/program/steps/utils/spell.inc +++ b/program/steps/utils/spell.inc @@ -37,6 +37,9 @@ if ($learn_word) { $spellchecker->add_word($data); $result = '<?xml version="1.0" encoding="'.RCUBE_CHARSET.'"?><learnwordresult></learnwordresult>'; } +else if (empty($data)) { + $result = '<?xml version="1.0" encoding="'.RCUBE_CHARSET.'"?><spellresult charschecked="0"></spellresult>'; +} else { $spellchecker->check($data); $result = $spellchecker->get_xml(); diff --git a/program/steps/utils/text2html.inc b/program/steps/utils/text2html.inc new file mode 100644 index 000000000..56d15fa19 --- /dev/null +++ b/program/steps/utils/text2html.inc @@ -0,0 +1,33 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | program/steps/utils/text2html.inc | + | | + | This file is part of the Roundcube Webmail client | + | 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. | + | See the README file for a full license statement. | + | | + | PURPOSE: | + | Convert plain text to HTML | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ +*/ + +$text = stream_get_contents(fopen('php://input', 'r')); + +// strip slashes if magic_quotes enabled +if (get_magic_quotes_gpc() || get_magic_quotes_runtime()) { + $html = stripslashes($html); +} + +$converter = new rcube_text2html($text, false, array('wrap' => true)); + +header('Content-Type: text/html; charset=' . RCUBE_CHARSET); +print $converter->get_html(); +exit; |