summaryrefslogtreecommitdiff
path: root/program/steps
diff options
context:
space:
mode:
Diffstat (limited to 'program/steps')
-rw-r--r--program/steps/addressbook/delete.inc2
-rw-r--r--program/steps/addressbook/export.inc79
-rw-r--r--program/steps/addressbook/func.inc67
-rw-r--r--program/steps/addressbook/groups.inc13
-rw-r--r--program/steps/addressbook/import.inc11
-rw-r--r--program/steps/addressbook/list.inc2
-rw-r--r--program/steps/addressbook/save.inc2
-rw-r--r--program/steps/addressbook/search.inc8
-rw-r--r--program/steps/mail/attachments.inc16
-rw-r--r--program/steps/mail/check_recent.inc20
-rw-r--r--program/steps/mail/compose.inc348
-rw-r--r--program/steps/mail/folders.inc1
-rw-r--r--program/steps/mail/func.inc316
-rw-r--r--program/steps/mail/get.inc93
-rw-r--r--program/steps/mail/list.inc2
-rw-r--r--program/steps/mail/list_contacts.inc153
-rw-r--r--program/steps/mail/move_del.inc16
-rw-r--r--program/steps/mail/search.inc8
-rw-r--r--program/steps/mail/search_contacts.inc112
-rw-r--r--program/steps/mail/sendmail.inc53
-rw-r--r--program/steps/mail/show.inc59
-rw-r--r--program/steps/settings/edit_identity.inc7
-rw-r--r--program/steps/settings/func.inc17
-rw-r--r--program/steps/settings/save_identity.inc41
-rw-r--r--program/steps/settings/save_prefs.inc1
-rw-r--r--program/steps/utils/html2text.inc4
-rw-r--r--program/steps/utils/save_pref.inc16
27 files changed, 933 insertions, 534 deletions
diff --git a/program/steps/addressbook/delete.inc b/program/steps/addressbook/delete.inc
index 81b8a0970..56118583c 100644
--- a/program/steps/addressbook/delete.inc
+++ b/program/steps/addressbook/delete.inc
@@ -93,7 +93,7 @@ if (($search_request = $_REQUEST['_search']) && isset($_SESSION['search'][$searc
while ($row = $result->next()) {
$row['sourceid'] = $s;
- $key = rcmail_contact_key($row, $sort_col);
+ $key = rcube_addressbook::compose_contact_key($row, $sort_col);
$records[$key] = $row;
}
unset($result);
diff --git a/program/steps/addressbook/export.inc b/program/steps/addressbook/export.inc
index 850795c85..761f26b75 100644
--- a/program/steps/addressbook/export.inc
+++ b/program/steps/addressbook/export.inc
@@ -40,11 +40,31 @@ if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search
// get records
$result = $source->list_records();
- while ($row = $result->next()) {
- $row['sourceid'] = $s;
- $key = rcmail_contact_key($row, $sort_col);
- $records[$key] = $row;
+ while ($record = $result->next()) {
+ // because vcard_map is per-source we need to create vcard here
+ if (empty($record['vcard']) || empty($record['name'])) {
+ $vcard = new rcube_vcard();
+ $vcard->extend_fieldmap($source->vcard_map);
+ $vcard->load($record['vcard']);
+ $vcard->reset();
+
+ foreach ($record as $key => $values) {
+ list($field, $section) = explode(':', $key);
+ foreach ((array)$values as $value) {
+ if (is_array($value) || @strlen($value)) {
+ $vcard->set($field, $value, strtoupper($section));
+ }
+ }
+ }
+
+ $record['vcard'] = $vcard->export(true);
+ }
+
+ $record['sourceid'] = $s;
+ $key = rcube_addressbook::compose_contact_key($record, $sort_col);
+ $records[$key] = $record;
}
+
unset($result);
}
@@ -56,6 +76,51 @@ if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search
$result = new rcube_result_set($count);
$result->records = array_values($records);
}
+// selected contacts
+else if (!empty($_REQUEST['_cid'])) {
+ $sort_col = $RCMAIL->config->get('addressbook_sort_col', 'name');
+ $records = array();
+
+ // Selected contact IDs (with multi-source support)
+ $cids = rcmail_get_cids();
+
+ foreach ($cids as $s => $ids) {
+ $source = $RCMAIL->get_address_book($s);
+ $result = $source->search('ID', $ids, 1, true, true);
+
+ while ($record = $result->next()) {
+ // because vcard_map is per-source we need to create vcard here
+ if (empty($record['vcard']) || empty($record['name'])) {
+ $vcard = new rcube_vcard();
+ $vcard->extend_fieldmap($source->vcard_map);
+ $vcard->load($record['vcard']);
+ $vcard->reset();
+
+ foreach ($record as $key => $values) {
+ list($field, $section) = explode(':', $key);
+ foreach ((array)$values as $value) {
+ if (is_array($value) || @strlen($value)) {
+ $vcard->set($field, $value, strtoupper($section));
+ }
+ }
+ }
+
+ $record['vcard'] = $vcard->export(true);
+ }
+
+ $record['sourceid'] = $s;
+ $key = rcube_addressbook::compose_contact_key($record, $sort_col);
+ $records[$key] = $record;
+ }
+ }
+
+ ksort($records, SORT_LOCALE_STRING);
+
+ // create resultset object
+ $count = count($records);
+ $result = new rcube_result_set($count);
+ $result->records = array_values($records);
+}
// selected directory/group
else {
$CONTACTS = rcmail_contact_source(null, true);
@@ -68,12 +133,14 @@ else {
// send downlaod headers
header('Content-Type: text/x-vcard; charset='.RCMAIL_CHARSET);
-header('Content-Disposition: attachment; filename="rcube_contacts.vcf"');
+header('Content-Disposition: attachment; filename="contacts.vcf"');
while ($result && ($row = $result->next())) {
// we already have a vcard record
if ($row['vcard'] && $row['name']) {
- $row['vcard'] = preg_replace('/\r?\n/', rcube_vcard::$eol, $row['vcard']);
+ // fix folding and end-of-line chars
+ $row['vcard'] = preg_replace('/\r|\n\s+/', '', $row['vcard']);
+ $row['vcard'] = preg_replace('/\n/', rcube_vcard::$eol, $row['vcard']);
echo rcube_vcard::rfc2425_fold($row['vcard']) . rcube_vcard::$eol;
}
// copy values into vcard object
diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc
index fded9a819..ffc0b3b92 100644
--- a/program/steps/addressbook/func.inc
+++ b/program/steps/addressbook/func.inc
@@ -26,7 +26,7 @@ $CONTACT_COLTYPES = array(
'name' => array('type' => 'text', 'size' => 40, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('name'), 'category' => 'main'),
'firstname' => array('type' => 'text', 'size' => 19, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('firstname'), 'category' => 'main'),
'surname' => array('type' => 'text', 'size' => 19, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('surname'), 'category' => 'main'),
- 'email' => array('type' => 'text', 'size' => 40, 'maxlength' => 50, 'label' => rcube_label('email'), 'subtypes' => array('home','work','other'), 'category' => 'main'),
+ 'email' => array('type' => 'text', 'size' => 40, 'maxlength' => 254, 'label' => rcube_label('email'), 'subtypes' => array('home','work','other'), 'category' => 'main'),
'middlename' => array('type' => 'text', 'size' => 19, 'maxlength' => 50, 'limit' => 1, 'label' => rcube_label('middlename'), 'category' => 'main'),
'prefix' => array('type' => 'text', 'size' => 8, 'maxlength' => 20, 'limit' => 1, 'label' => rcube_label('nameprefix'), 'category' => 'main'),
'suffix' => array('type' => 'text', 'size' => 8, 'maxlength' => 20, 'limit' => 1, 'label' => rcube_label('namesuffix'), 'category' => 'main'),
@@ -187,7 +187,7 @@ function rcmail_directory_list($attrib)
$jsdata = array();
$line_templ = html::tag('li', array(
- 'id' => 'rcmli%s', 'class' => '%s'),
+ 'id' => 'rcmli%s', 'class' => '%s', 'noclose' => true),
html::a(array('href' => '%s',
'rel' => '%s',
'onclick' => "return ".JS_OBJECT_NAME.".command('list','%s',this)"), '%s'));
@@ -213,7 +213,7 @@ function rcmail_directory_list($attrib)
$name = !empty($source['name']) ? $source['name'] : $id;
$out .= sprintf($line_templ,
- html_identifier($id),
+ rcube_utils::html_identifier($id, true),
$class_name,
Q(rcmail_url(null, array('_source' => $id))),
$source['id'],
@@ -224,10 +224,11 @@ function rcmail_directory_list($attrib)
$groupdata = rcmail_contact_groups($groupdata);
$jsdata = $groupdata['jsdata'];
$out = $groupdata['out'];
+ $out .= '</li>';
}
$line_templ = html::tag('li', array(
- 'id' => 'rcmliS%s', 'class' => '%s'),
+ 'id' => 'rcmli%s', 'class' => '%s'),
html::a(array('href' => '#', 'rel' => 'S%s',
'onclick' => "return ".JS_OBJECT_NAME.".command('listsearch', '%s', this)"), '%s'));
@@ -245,14 +246,17 @@ function rcmail_directory_list($attrib)
$class_name .= ' ' . $source['class_name'];
$out .= sprintf($line_templ,
- html_identifier($id),
+ rcube_utils::html_identifier('S'.$id, true),
$class_name,
$id,
$js_id, (!empty($source['name']) ? Q($source['name']) : Q($id)));
}
$OUTPUT->set_env('contactgroups', $jsdata);
+ $OUTPUT->set_env('collapsed_abooks', (string)$RCMAIL->config->get('collapsed_abooks',''));
$OUTPUT->add_gui_object('folderlist', $attrib['id']);
+ $OUTPUT->include_script('treelist.js');
+
// add some labels to client
$OUTPUT->add_label('deletegroupconfirm', 'groupdeleting', 'addingmember', 'removingmember');
@@ -264,19 +268,25 @@ function rcmail_contact_groups($args)
{
global $RCMAIL;
+ $groups_html = '';
$groups = $RCMAIL->get_address_book($args['source'])->list_groups();
+ $js_id = $RCMAIL->JQ($args['source']);
if (!empty($groups)) {
$line_templ = html::tag('li', array(
- 'id' => 'rcmliG%s', 'class' => 'contactgroup'),
+ 'id' => 'rcmli%s', 'class' => 'contactgroup'),
html::a(array('href' => '#',
'rel' => '%s:%s',
'onclick' => "return ".JS_OBJECT_NAME.".command('listgroup',{'source':'%s','id':'%s'},this)"), '%s'));
+ // append collapse/expand toggle and open a new <ul>
+ $is_collapsed = strpos($RCMAIL->config->get('collapsed_abooks',''), '&'.rawurlencode($args['source']).'&') !== false;
+ $args['out'] .= html::div('treetoggle ' . ($is_collapsed ? 'collapsed' : 'expanded'), '&nbsp;');
+
$jsdata = array();
foreach ($groups as $group) {
- $args['out'] .= sprintf($line_templ,
- html_identifier($args['source'] . $group['ID']),
+ $groups_html .= sprintf($line_templ,
+ rcube_utils::html_identifier('G' . $args['source'] . $group['ID'], true),
$args['source'], $group['ID'],
$args['source'], $group['ID'], Q($group['name'])
);
@@ -286,6 +296,10 @@ function rcmail_contact_groups($args)
}
}
+ $args['out'] .= html::tag('ul',
+ array('class' => 'groups', 'style' => ($is_collapsed ? "display:none;" : null)),
+ $groups_html);
+
return $args;
}
@@ -733,30 +747,12 @@ function rcmail_format_date_col($val)
}
-function rcmail_contact_key($row, $sort_col)
-{
- $key = $row[$sort_col] . ':' . $row['sourceid'];
-
- // add email to a key to not skip contacts with the same name (#1488375)
- if (!empty($row['email'])) {
- if (is_array($row['email'])) {
- $key .= ':' . implode(':', $row['email']);
- }
- else {
- $key .= ':' . $row['email'];
- }
- }
-
- return $key;
-}
-
-
/**
* Returns contact ID(s) and source(s) from GET/POST data
*
* @return array List of contact IDs per-source
*/
-function rcmail_get_cids()
+function rcmail_get_cids($filter = null)
{
// contact ID (or comma-separated list of IDs) is provided in two
// forms. If _source is an empty string then the ID is a string
@@ -765,6 +761,10 @@ function rcmail_get_cids()
$cid = get_input_value('_cid', RCUBE_INPUT_GPC);
$source = (string) get_input_value('_source', RCUBE_INPUT_GPC);
+ if (is_array($cid)) {
+ return $cid;
+ }
+
if (!preg_match('/^[a-zA-Z0-9\+\/=_-]+(,[a-zA-Z0-9\+\/=_-]+)*$/', $cid)) {
return array();
}
@@ -775,24 +775,29 @@ function rcmail_get_cids()
// create per-source contact IDs array
foreach ($cid as $id) {
- // if _source is not specified we'll find it from decoded ID
+ // extract source ID from contact ID (it's there in search mode)
+ // see #1488959 and #1488862 for reference
if (!$got_source) {
if ($sep = strrpos($id, '-')) {
$contact_id = substr($id, 0, $sep);
- $source_id = substr($id, $sep+1);
+ $source_id = (string) substr($id, $sep+1);
if (strlen($source_id)) {
- $result[(string)$source_id][] = $contact_id;
+ $result[$source_id][] = $contact_id;
}
}
}
else {
+ if (substr($id, -($got_source+1)) == "-$source") {
+ $id = substr($id, 0, -($got_source+1));
+ }
$result[$source][] = $id;
}
}
- return $result;
+ return $filter !== null ? $result[$filter] : $result;
}
+
// register UI objects
$OUTPUT->add_handlers(array(
'directorylist' => 'rcmail_directory_list',
diff --git a/program/steps/addressbook/groups.inc b/program/steps/addressbook/groups.inc
index b70453889..3b9288a2b 100644
--- a/program/steps/addressbook/groups.inc
+++ b/program/steps/addressbook/groups.inc
@@ -20,7 +20,7 @@
*/
$source = get_input_value('_source', RCUBE_INPUT_GPC);
-$CONTACTS = rcmail_contact_source($source, true);
+$CONTACTS = rcmail_contact_source($source);
if ($CONTACTS->readonly || !$CONTACTS->groups) {
$OUTPUT->show_message('sourceisreadonly', 'warning');
@@ -28,11 +28,11 @@ if ($CONTACTS->readonly || !$CONTACTS->groups) {
}
if ($RCMAIL->action == 'group-addmembers') {
- if (($gid = get_input_value('_gid', RCUBE_INPUT_POST)) && ($ids = get_input_value('_cid', RCUBE_INPUT_POST))) {
+ if (($gid = get_input_value('_gid', RCUBE_INPUT_POST)) && ($ids = rcmail_get_cids($source))) {
$plugin = $RCMAIL->plugins->exec_hook('group_addmembers', array('group_id' => $gid, 'ids' => $ids, 'source' => $source));
$CONTACTS->set_group($gid);
- $num2add = count(explode(',', $plugin['ids']));
+ $num2add = count($plugin['ids']);
if (!$plugin['abort']) {
if (($maxnum = $RCMAIL->config->get('max_group_members', 0)) && ($CONTACTS->count()->count + $num2add > $maxnum)) {
@@ -55,7 +55,7 @@ if ($RCMAIL->action == 'group-addmembers') {
}
else if ($RCMAIL->action == 'group-delmembers') {
- if (($gid = get_input_value('_gid', RCUBE_INPUT_POST)) && ($ids = get_input_value('_cid', RCUBE_INPUT_POST))) {
+ if (($gid = get_input_value('_gid', RCUBE_INPUT_POST)) && ($ids = rcmail_get_cids($source))) {
$plugin = $RCMAIL->plugins->exec_hook('group_delmembers', array('group_id' => $gid, 'ids' => $ids, 'source' => $source));
if (!$plugin['abort'])
@@ -63,10 +63,11 @@ else if ($RCMAIL->action == 'group-delmembers') {
else
$result = $plugin['result'];
- if ($result){
+ if ($result) {
$OUTPUT->show_message('contactremovedfromgroup');
$OUTPUT->command('remove_group_contacts',array('source' => $source, 'gid' => $gid));
- }else{
+ }
+ else {
$OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'errorsaving', 'error');
}
}
diff --git a/program/steps/addressbook/import.inc b/program/steps/addressbook/import.inc
index df07d64bc..72da15078 100644
--- a/program/steps/addressbook/import.inc
+++ b/program/steps/addressbook/import.inc
@@ -209,6 +209,15 @@ if (is_array($_FILES['_file'])) {
foreach ($vcards as $vcard) {
$a_record = $vcard->get_assoc();
+ // Generate contact's display name (must be before validation), the same we do in save.inc
+ if (empty($a_record['name'])) {
+ $a_record['name'] = rcube_addressbook::compose_display_name($a_record, true);
+ // Reset it if equals to email address (from compose_display_name())
+ if ($a_record['name'] == $a_record['email'][0]) {
+ $a_record['name'] = '';
+ }
+ }
+
// skip invalid (incomplete) entries
if (!$CONTACTS->validate($a_record, true)) {
$IMPORT_STATS->invalid++;
@@ -250,7 +259,7 @@ if (is_array($_FILES['_file'])) {
if ($success) {
$IMPORT_STATS->inserted++;
- $IMPORT_STATS->names[] = $vcard->displayname ? $vcard->displayname : $email;
+ $IMPORT_STATS->names[] = $a_record['name'] ? $a_record['name'] : $email;
}
else {
$IMPORT_STATS->errors++;
diff --git a/program/steps/addressbook/list.inc b/program/steps/addressbook/list.inc
index 06a1e10a3..1bb28658b 100644
--- a/program/steps/addressbook/list.inc
+++ b/program/steps/addressbook/list.inc
@@ -49,7 +49,7 @@ if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search
while ($row = $result->next()) {
$row['sourceid'] = $s;
- $key = rcmail_contact_key($row, $sort_col);
+ $key = rcube_addressbook::compose_contact_key($row, $sort_col);
$records[$key] = $row;
}
unset($result);
diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc
index 901ea0190..8cab6e817 100644
--- a/program/steps/addressbook/save.inc
+++ b/program/steps/addressbook/save.inc
@@ -82,7 +82,7 @@ if (empty($a_record['name'])) {
// do input checks (delegated to $CONTACTS instance)
if (!$CONTACTS->validate($a_record)) {
$err = (array)$CONTACTS->get_error();
- $OUTPUT->show_message($err['message'] ? $err['message'] : 'formincomplete', 'warning');
+ $OUTPUT->show_message($err['message'] ? Q($err['message']) : 'formincomplete', 'warning');
$GLOBALS['EDIT_RECORD'] = $a_record; // store submitted data to be used in edit form
rcmail_overwrite_action($return_action);
return;
diff --git a/program/steps/addressbook/search.inc b/program/steps/addressbook/search.inc
index 851325070..d153c255a 100644
--- a/program/steps/addressbook/search.inc
+++ b/program/steps/addressbook/search.inc
@@ -184,7 +184,7 @@ function rcmail_contact_search()
while ($row = $result->next()) {
$row['sourceid'] = $s['id'];
- $key = rcmail_contact_key($row, $sort_col);
+ $key = rcube_addressbook::compose_contact_key($row, $sort_col);
$records[$key] = $row;
}
@@ -300,9 +300,13 @@ function rcmail_contact_search_form($attrib)
$label = isset($colprop['label']) ? $colprop['label'] : rcube_label($col);
$category = $colprop['category'] ? $colprop['category'] : 'other';
- if ($ftype == 'text')
+ // load jquery UI datepicker for date fields
+ if ($colprop['type'] == 'date')
+ $colprop['class'] .= ($colprop['class'] ? ' ' : '') . 'datepicker';
+ else if ($ftype == 'text')
$colprop['size'] = $i_size;
+
$content = html::div('row', html::div('contactfieldlabel label', Q($label))
. html::div('contactfieldcontent', rcmail_get_edit_field('search_'.$col, '', $colprop, $ftype)));
diff --git a/program/steps/mail/attachments.inc b/program/steps/mail/attachments.inc
index 180fc0bb9..f83f6892e 100644
--- a/program/steps/mail/attachments.inc
+++ b/program/steps/mail/attachments.inc
@@ -27,8 +27,10 @@ if (!empty($_GET['_progress'])) {
$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GPC);
$COMPOSE = null;
-if ($COMPOSE_ID && $_SESSION['compose_data_'.$COMPOSE_ID])
- $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
+if ($COMPOSE_ID && $_SESSION['compose_data_' . $COMPOSE_ID]) {
+ $SESSION_KEY = 'compose_data_' . $COMPOSE_ID;
+ $COMPOSE =& $_SESSION[$SESSION_KEY];
+}
if (!$COMPOSE) {
die("Invalid session var!");
@@ -45,7 +47,7 @@ if ($RCMAIL->action=='remove-attachment')
$attachment = $RCMAIL->plugins->exec_hook('attachment_delete', $attachment);
if ($attachment['status']) {
if (is_array($COMPOSE['attachments'][$id])) {
- unset($COMPOSE['attachments'][$id]);
+ $RCMAIL->session->remove($SESSION_KEY.'.attachments.'.$id);
$OUTPUT->command('remove_from_attachment_list', "rcmfile$id");
}
}
@@ -77,11 +79,7 @@ if ($RCMAIL->action=='display-attachment')
exit;
}
-// attachment upload action
-
-if (!is_array($COMPOSE['attachments'])) {
- $COMPOSE['attachments'] = array();
-}
+/***** attachment upload action *****/
// clear all stored output properties (like scripts and env vars)
$OUTPUT->reset();
@@ -112,7 +110,7 @@ if (is_array($_FILES['_attachments']['tmp_name'])) {
// store new attachment in session
unset($attachment['status'], $attachment['abort']);
- $COMPOSE['attachments'][$id] = $attachment;
+ $RCMAIL->session->append($SESSION_KEY.'.attachments', $id, $attachment);
if (($icon = $COMPOSE['deleteicon']) && is_file($icon)) {
$button = html::img(array(
diff --git a/program/steps/mail/check_recent.inc b/program/steps/mail/check_recent.inc
index 90d17c15b..3649d148c 100644
--- a/program/steps/mail/check_recent.inc
+++ b/program/steps/mail/check_recent.inc
@@ -25,7 +25,7 @@ if (empty($_REQUEST['_folderlist']) && empty($_REQUEST['_list'])) {
return;
}
-$current = $RCMAIL->storage->get_folder();
+$current = $RCMAIL->storage->get_folder();
$check_all = $RCMAIL->action != 'refresh' || (bool)$RCMAIL->config->get('check_all_folders');
// list of folders to check
@@ -34,10 +34,15 @@ if ($check_all) {
}
else {
$a_mailboxes = (array) $current;
- if ($a_mailboxes[0] != 'INBOX')
+ if ($current != 'INBOX') {
$a_mailboxes[] = 'INBOX';
+ }
}
+// Control folders list from a plugin
+$plugin = $RCMAIL->plugins->exec_hook('check_recent', array('folders' => $a_mailboxes, 'all' => $check_all));
+$a_mailboxes = $plugin['folders'];
+
// check recent/unseen counts
foreach ($a_mailboxes as $mbox_name) {
$is_current = $mbox_name == $current;
@@ -47,12 +52,12 @@ foreach ($a_mailboxes as $mbox_name) {
}
// Get mailbox status
- $status = $RCMAIL->storage->folder_status($mbox_name);
+ $status = $RCMAIL->storage->folder_status($mbox_name, $diff);
if ($status & 1) {
// trigger plugin hook
$RCMAIL->plugins->exec_hook('new_messages',
- array('mailbox' => $mbox_name, 'is_current' => $is_current));
+ array('mailbox' => $mbox_name, 'is_current' => $is_current, 'diff' => $diff));
}
rcmail_send_unread_count($mbox_name, true, null,
@@ -70,13 +75,15 @@ foreach ($a_mailboxes as $mbox_name) {
if (!empty($_GET['_quota']))
$OUTPUT->command('set_quota', rcmail_quota_content());
+ $OUTPUT->set_env('exists', $RCMAIL->storage->count($mbox_name, 'EXISTS'));
+
// "No-list" mode, don't get messages
if (empty($_GET['_list']))
continue;
// get overall message count; allow caching because rcube_storage::folder_status() did a refresh
$list_mode = $RCMAIL->storage->get_threading() ? 'THREADS' : 'ALL';
- $all_count = $RCMAIL->storage->count(null, $list_mode, false, false);
+ $all_count = $RCMAIL->storage->count($mbox_name, $list_mode, false, false);
$page = $RCMAIL->storage->get_page();
$page_size = $RCMAIL->storage->get_pagesize();
@@ -108,4 +115,7 @@ foreach ($a_mailboxes as $mbox_name) {
}
}
+// trigger refresh hook
+$RCMAIL->plugins->exec_hook('refresh', array());
+
$OUTPUT->send();
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 92ec88f1b..36c6d9622 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -20,10 +20,10 @@
*/
// define constants for message compose mode
-define('RCUBE_COMPOSE_REPLY', 0x0106);
-define('RCUBE_COMPOSE_FORWARD', 0x0107);
-define('RCUBE_COMPOSE_DRAFT', 0x0108);
-define('RCUBE_COMPOSE_EDIT', 0x0109);
+define('RCUBE_COMPOSE_REPLY', 'reply');
+define('RCUBE_COMPOSE_FORWARD', 'forward');
+define('RCUBE_COMPOSE_DRAFT', 'draft');
+define('RCUBE_COMPOSE_EDIT', 'edit');
$MESSAGE_FORM = null;
$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GET);
@@ -139,7 +139,6 @@ if (!empty($CONFIG['drafts_mbox'])) {
}
// set current mailbox in client environment
$OUTPUT->set_env('mailbox', $RCMAIL->storage->get_folder());
-$OUTPUT->set_env('sig_above', $RCMAIL->config->get('sig_above', false));
$OUTPUT->set_env('top_posting', intval($RCMAIL->config->get('reply_mode')) > 0);
$OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ',')));
@@ -151,34 +150,51 @@ if ($font && !is_array($font)) {
// get reference message and set compose mode
if ($msg_uid = $COMPOSE['param']['draft_uid']) {
- $RCMAIL->storage->set_folder($CONFIG['drafts_mbox']);
$compose_mode = RCUBE_COMPOSE_DRAFT;
+ $OUTPUT->set_env('draft_id', $msg_uid);
+ $RCMAIL->storage->set_folder($CONFIG['drafts_mbox']);
}
-else if ($msg_uid = $COMPOSE['param']['reply_uid'])
+else if ($msg_uid = $COMPOSE['param']['reply_uid']) {
$compose_mode = RCUBE_COMPOSE_REPLY;
-else if ($msg_uid = $COMPOSE['param']['forward_uid'])
+}
+else if ($msg_uid = $COMPOSE['param']['forward_uid']) {
$compose_mode = RCUBE_COMPOSE_FORWARD;
-else if ($msg_uid = $COMPOSE['param']['uid'])
+ $COMPOSE['forward_uid'] = $msg_uid;
+ $COMPOSE['as_attachment'] = !empty($COMPOSE['param']['attachment']);
+}
+else if ($msg_uid = $COMPOSE['param']['uid']) {
$compose_mode = RCUBE_COMPOSE_EDIT;
+}
+$OUTPUT->set_env('compose_mode', $compose_mode);
$config_show_sig = $RCMAIL->config->get('show_sig', 1);
-if ($config_show_sig == 1)
+if ($compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT) {
+ // don't add signature in draft/edit mode, we'll also not remove the old-one
+}
+else if ($config_show_sig == 1)
$OUTPUT->set_env('show_sig', true);
-else if ($config_show_sig == 2 && (empty($compose_mode) || $compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT))
+else if ($config_show_sig == 2 && empty($compose_mode))
$OUTPUT->set_env('show_sig', true);
else if ($config_show_sig == 3 && ($compose_mode == RCUBE_COMPOSE_REPLY || $compose_mode == RCUBE_COMPOSE_FORWARD))
$OUTPUT->set_env('show_sig', true);
-else
- $OUTPUT->set_env('show_sig', false);
// set line length for body wrapping
$LINE_LENGTH = $RCMAIL->config->get('line_length', 72);
-if (!empty($msg_uid))
+if (!empty($msg_uid) && empty($COMPOSE['as_attachment']))
{
- // similar as in program/steps/mail/show.inc
- // re-set 'prefer_html' to have possibility to use html part for compose
- $CONFIG['prefer_html'] = $CONFIG['prefer_html'] || $CONFIG['htmleditor'] || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT;
+ $mbox_name = $RCMAIL->storage->get_folder();
+
+ // set format before rcube_message construction
+ // use the same format as for the message view
+ if (isset($_SESSION['msg_formats'][$mbox_name.':'.$msg_uid])) {
+ $RCMAIL->config->set('prefer_html', $_SESSION['msg_formats'][$mbox_name.':'.$msg_uid]);
+ }
+ else {
+ $prefer_html = $CONFIG['prefer_html'] || $CONFIG['htmleditor'] || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT;
+ $RCMAIL->config->set('prefer_html', $prefer_html);
+ }
+
$MESSAGE = new rcube_message($msg_uid);
// make sure message is marked as read
@@ -188,8 +204,7 @@ if (!empty($msg_uid))
if (!empty($MESSAGE->headers->charset))
$RCMAIL->storage->set_charset($MESSAGE->headers->charset);
- if ($compose_mode == RCUBE_COMPOSE_REPLY)
- {
+ 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);
@@ -197,8 +212,6 @@ if (!empty($msg_uid))
if (!empty($COMPOSE['param']['all']))
$MESSAGE->reply_all = $COMPOSE['param']['all'];
- $OUTPUT->set_env('compose_mode', 'reply');
-
// 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'])
&& rcmail_check_sent_folder($sent_folder, false)
@@ -206,10 +219,8 @@ if (!empty($msg_uid))
$COMPOSE['param']['sent_mbox'] = $sent_folder;
}
}
- else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
- {
- if ($MESSAGE->headers->others['x-draft-info'])
- {
+ else if ($compose_mode == RCUBE_COMPOSE_DRAFT) {
+ if ($MESSAGE->headers->others['x-draft-info']) {
// get reply_uid/forward_uid to flag the original message when sending
$info = rcmail_draftinfo_decode($MESSAGE->headers->others['x-draft-info']);
@@ -233,14 +244,6 @@ if (!empty($msg_uid))
$COMPOSE['references'] = $MESSAGE->headers->references;
}
- else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
- {
- $COMPOSE['forward_uid'] = $msg_uid;
- $OUTPUT->set_env('compose_mode', 'forward');
-
- if (!empty($COMPOSE['param']['attachment']))
- $MESSAGE->forward_attachment = true;
- }
}
else {
$MESSAGE = new stdClass();
@@ -249,18 +252,7 @@ else {
$MESSAGE->compose = array();
// get user's identities
-$MESSAGE->identities = $RCMAIL->user->list_identities();
-if (count($MESSAGE->identities))
-{
- foreach ($MESSAGE->identities as $idx => $ident) {
- $ident['email'] = format_email($ident['email']);
- $email = format_email(rcube_idn_to_utf8($ident['email']));
-
- $MESSAGE->identities[$idx]['email_ascii'] = $ident['email'];
- $MESSAGE->identities[$idx]['ident'] = format_email_recipient($ident['email'], $ident['name']);
- $MESSAGE->identities[$idx]['email'] = $email;
- }
-}
+$MESSAGE->identities = $RCMAIL->user->list_identities(null, true);
// Set From field value
if (!empty($_POST['_from'])) {
@@ -270,83 +262,10 @@ else if (!empty($COMPOSE['param']['from'])) {
$MESSAGE->compose['from'] = $COMPOSE['param']['from'];
}
else if (count($MESSAGE->identities)) {
- $a_recipients = array();
- $a_names = array();
-
- // extract all recipients of the reply-message
- if (is_object($MESSAGE->headers) && in_array($compose_mode, array(RCUBE_COMPOSE_REPLY, RCUBE_COMPOSE_FORWARD)))
- {
- $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset);
- foreach ($a_to as $addr) {
- if (!empty($addr['mailto'])) {
- $a_recipients[] = format_email($addr['mailto']);
- $a_names[] = $addr['name'];
- }
- }
-
- if (!empty($MESSAGE->headers->cc)) {
- $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset);
- foreach ($a_cc as $addr) {
- if (!empty($addr['mailto'])) {
- $a_recipients[] = format_email($addr['mailto']);
- $a_names[] = $addr['name'];
- }
- }
- }
- }
-
- $from_idx = null;
- $found_idx = null;
- $default_identity = 0; // default identity is always first on the list
- $return_path = $MESSAGE->headers->others['return-path'];
-
- // Select identity
- foreach ($MESSAGE->identities as $idx => $ident) {
- // use From header
- if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) {
- if ($MESSAGE->headers->from == $ident['ident']) {
- $from_idx = $idx;
- break;
- }
- }
- // reply to yourself
- else if ($compose_mode == RCUBE_COMPOSE_REPLY && $MESSAGE->headers->from == $ident['ident']) {
- $from_idx = $idx;
- break;
- }
- // use replied message recipients
- else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) {
- if ($found_idx === null) {
- $found_idx = $idx;
- }
- // match identity name
- if ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name']) {
- $from_idx = $idx;
- break;
- }
- }
- }
-
- // If matching by name+address doesn't found any amtches, get first found address (identity)
- if ($from_idx === null) {
- $from_idx = $found_idx;
- }
-
- // Fallback using Return-Path
- if ($from_idx === null && $return_path) {
- foreach ($MESSAGE->identities as $idx => $ident) {
- if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) {
- $from_idx = $idx;
- break;
- }
- }
- }
-
- $ident = $MESSAGE->identities[$from_idx !== null ? $from_idx : $default_identity];
- $from_id = $ident['identity_id'];
+ $ident = rcmail_identity_select($MESSAGE, $MESSAGE->identities, $compose_mode);
$MESSAGE->compose['from_email'] = $ident['email'];
- $MESSAGE->compose['from'] = $from_id;
+ $MESSAGE->compose['from'] = $ident['identity_id'];
}
// Set other headers
@@ -542,7 +461,7 @@ function rcmail_compose_header_from($attrib)
if (count($MESSAGE->identities))
{
$a_signatures = array();
- $separator = $RCMAIL->config->get('sig_above')
+ $separator = intval($RCMAIL->config->get('reply_mode')) > 0
&& ($compose_mode == RCUBE_COMPOSE_REPLY || $compose_mode == RCUBE_COMPOSE_FORWARD) ? '---' : '-- ';
$field_attrib['onchange'] = JS_OBJECT_NAME.".change_identity(this)";
@@ -560,7 +479,7 @@ function rcmail_compose_header_from($attrib)
$text = $html = $sql_arr['signature'];
if ($sql_arr['html_signature']) {
- $h2t = new html2text($sql_arr['signature'], false, false);
+ $h2t = new rcube_html2text($sql_arr['signature'], false, false);
$text = trim($h2t->get_text());
}
else {
@@ -599,7 +518,7 @@ function rcmail_compose_header_from($attrib)
function rcmail_compose_editor_mode()
{
- global $RCMAIL, $MESSAGE, $compose_mode;
+ global $RCMAIL, $compose_mode;
static $useHtml;
if ($useHtml !== null)
@@ -611,13 +530,13 @@ function rcmail_compose_editor_mode()
$useHtml = !empty($_POST['_is_html']);
}
else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) {
- $useHtml = $MESSAGE->has_html_part(false, true);
+ $useHtml = rcmail_message_is_html();
}
else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
- $useHtml = ($html_editor == 1 || ($html_editor >= 2 && $MESSAGE->has_html_part(false, true)));
+ $useHtml = ($html_editor == 1 || ($html_editor >= 2 && rcmail_message_is_html()));
}
else if ($compose_mode == RCUBE_COMPOSE_FORWARD) {
- $useHtml = ($html_editor == 1 || ($html_editor == 3 && $MESSAGE->has_html_part(false, true)));
+ $useHtml = ($html_editor == 1 || ($html_editor == 3 && rcmail_message_is_html()));
}
else {
$useHtml = ($html_editor == 1);
@@ -626,6 +545,11 @@ function rcmail_compose_editor_mode()
return $useHtml;
}
+function rcmail_message_is_html()
+{
+ global $RCMAIL, $MESSAGE;
+ return $RCMAIL->config->get('prefer_html') && ($MESSAGE instanceof rcube_message) && $MESSAGE->has_html_part(true);
+}
function rcmail_prepare_message_body()
{
@@ -641,11 +565,10 @@ function rcmail_prepare_message_body()
$isHtml = false;
}
// forward as attachment
- else if ($compose_mode == RCUBE_COMPOSE_FORWARD && $MESSAGE->forward_attachment) {
+ else if ($compose_mode == RCUBE_COMPOSE_FORWARD && $COMPOSE['as_attachment']) {
$isHtml = rcmail_compose_editor_mode();
$body = '';
- if (empty($COMPOSE['attachments']))
- rcmail_write_forward_attachment($MESSAGE);
+ rcmail_write_forward_attachments();
}
// reply/edit/draft/forward
else if ($compose_mode && ($compose_mode != RCUBE_COMPOSE_REPLY || $RCMAIL->config->get('reply_mode') != -1)) {
@@ -731,13 +654,14 @@ function rcmail_compose_part_body($part, $isHtml = false)
if ($part->ctype_secondary == 'html') {
}
else if ($part->ctype_secondary == 'enriched') {
- require_once(INSTALL_PATH . 'program/lib/enriched.inc');
- $body = enriched_to_html($body);
+ $body = rcube_enriched::to_html($body);
}
else {
// try to remove the signature
- if ($RCMAIL->config->get('strip_existing_sig', true)) {
- $body = rcmail_remove_signature($body);
+ if ($compose_mode != RCUBE_COMPOSE_DRAFT && $compose_mode != RCUBE_COMPOSE_EDIT) {
+ if ($RCMAIL->config->get('strip_existing_sig', true)) {
+ $body = rcmail_remove_signature($body);
+ }
}
// add HTML formatting
$body = rcmail_plain_body($body);
@@ -748,8 +672,7 @@ function rcmail_compose_part_body($part, $isHtml = false)
}
else {
if ($part->ctype_secondary == 'enriched') {
- require_once(INSTALL_PATH . 'program/lib/enriched.inc');
- $body = enriched_to_html($body);
+ $body = rcube_enriched::to_html($body);
$part->ctype_secondary = 'html';
}
@@ -757,21 +680,19 @@ function rcmail_compose_part_body($part, $isHtml = false)
// use html part if it has been used for message (pre)viewing
// decrease line length for quoting
$len = $compose_mode == RCUBE_COMPOSE_REPLY ? $LINE_LENGTH-2 : $LINE_LENGTH;
- $txt = new html2text($body, false, true, $len);
+ $txt = new rcube_html2text($body, false, true, $len);
$body = $txt->get_text();
}
- else if ($part->ctype_secondary == 'enriched') {
- require_once(INSTALL_PATH . 'program/lib/enriched.inc');
- $body = enriched_to_html($body);
- }
else {
if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed') {
$body = rcube_mime::unfold_flowed($body);
}
// try to remove the signature
- if ($RCMAIL->config->get('strip_existing_sig', true)) {
- $body = rcmail_remove_signature($body);
+ if ($compose_mode != RCUBE_COMPOSE_DRAFT && $compose_mode != RCUBE_COMPOSE_EDIT) {
+ if ($RCMAIL->config->get('strip_existing_sig', true)) {
+ $body = rcmail_remove_signature($body);
+ }
}
}
}
@@ -1067,14 +988,21 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
{
global $RCMAIL, $COMPOSE, $compose_mode;
+ $loaded_attachments = array();
+ foreach ((array)$COMPOSE['attachments'] as $id => $attachment) {
+ $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment;
+ }
+
$cid_map = $messages = array();
foreach ((array)$message->mime_parts as $pid => $part)
{
if ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename) {
- if ($part->ctype_primary == 'message' || $part->ctype_primary == 'multipart') {
+ // skip parts that aren't valid attachments
+ if ($part->ctype_primary == 'multipart' || $part->mimetype == 'application/ms-tnef') {
continue;
}
- if ($part->mimetype == 'application/ms-tnef') {
+ // skip message attachments in reply mode
+ if ($part->ctype_primary == 'message' && $compose_mode == RCUBE_COMPOSE_REPLY) {
continue;
}
// skip inline images when forwarding in plain text
@@ -1094,7 +1022,8 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
}
}
- if (!$skip && ($attachment = rcmail_save_attachment($message, $pid))) {
+ if (!$skip && (($attachment = $loaded_attachments[rcmail_attachment_name($part) . $part->mimetype])
+ || ($attachment = rcmail_save_attachment($message, $pid)))) {
$COMPOSE['attachments'][$attachment['id']] = $attachment;
if ($bodyIsHtml && ($part->content_id || $part->content_location)) {
$url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s',
@@ -1136,55 +1065,95 @@ function rcmail_write_inline_attachments(&$message)
return $cid_map;
}
-// Creates an attachment from the forwarded message
-function rcmail_write_forward_attachment(&$message)
+// Creates attachment(s) from the forwarded message(s)
+function rcmail_write_forward_attachments()
{
- global $RCMAIL, $COMPOSE;
+ global $RCMAIL, $COMPOSE, $MESSAGE;
+
+ $storage = $RCMAIL->get_storage();
+ $mem_limit = parse_bytes(ini_get('memory_limit'));
+ $curr_mem = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
+ $names = array();
- if (strlen($message->subject)) {
- $name = mb_substr($message->subject, 0, 64) . '.eml';
+ $loaded_attachments = array();
+ foreach ((array)$COMPOSE['attachments'] as $id => $attachment) {
+ $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment;
+ }
+
+ if ($COMPOSE['forward_uid'] == '*') {
+ $index = $storage->index(null, rcmail_sort_column(), rcmail_sort_order());
+ $COMPOSE['forward_uid'] = $index->get();
}
else {
- $name = 'message_rfc822.eml';
+ $COMPOSE['forward_uid'] = explode(',', $COMPOSE['forward_uid']);
}
- $mem_limit = parse_bytes(ini_get('memory_limit'));
- $curr_mem = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
- $data = $path = null;
+ foreach ((array)$COMPOSE['forward_uid'] as $uid) {
+ $message = new rcube_message($uid);
- // don't load too big attachments into memory
- if ($mem_limit > 0 && $message->size > $mem_limit - $curr_mem) {
- $temp_dir = unslashify($RCMAIL->config->get('temp_dir'));
- $path = tempnam($temp_dir, 'rcmAttmnt');
- if ($fp = fopen($path, 'w')) {
- $RCMAIL->storage->get_raw_body($message->uid, $fp);
- fclose($fp);
- } else
- return false;
- } else {
- $data = $RCMAIL->storage->get_raw_body($message->uid);
- }
+ if (empty($message->headers)) {
+ continue;
+ }
- $attachment = array(
- 'group' => $COMPOSE['id'],
- 'name' => $name,
- 'mimetype' => 'message/rfc822',
- 'data' => $data,
- 'path' => $path,
- 'size' => $path ? filesize($path) : strlen($data),
- );
+ if (!empty($message->headers->charset)) {
+ $storage->set_charset($message->headers->charset);
+ }
- $attachment = $RCMAIL->plugins->exec_hook('attachment_save', $attachment);
+ if (empty($MESSAGE->subject)) {
+ $MESSAGE->subject = $message->subject;
+ }
- if ($attachment['status']) {
- unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
- $COMPOSE['attachments'][$attachment['id']] = $attachment;
- return true;
- } else if ($path) {
- @unlink($path);
- }
+ // generate (unique) attachment name
+ $name = strlen($message->subject) ? mb_substr($message->subject, 0, 64) : 'message_rfc822';
+ if (!empty($names[$name])) {
+ $names[$name]++;
+ $name .= '_' . $names[$name];
+ }
+ $names[$name] = 1;
+ $name .= '.eml';
- return false;
+ $data = $path = null;
+
+ if (!empty($loaded_attachments[$name . 'message/rfc822'])) {
+ continue;
+ }
+
+ // don't load too big attachments into memory
+ if ($mem_limit > 0 && $message->size > $mem_limit - $curr_mem) {
+ $temp_dir = unslashify($RCMAIL->config->get('temp_dir'));
+ $path = tempnam($temp_dir, 'rcmAttmnt');
+ if ($fp = fopen($path, 'w')) {
+ $storage->get_raw_body($message->uid, $fp);
+ fclose($fp);
+ }
+ else {
+ return false;
+ }
+ }
+ else {
+ $data = $storage->get_raw_body($message->uid);
+ $curr_mem += $message->size;
+ }
+
+ $attachment = array(
+ 'group' => $COMPOSE['id'],
+ 'name' => $name,
+ 'mimetype' => 'message/rfc822',
+ 'data' => $data,
+ 'path' => $path,
+ 'size' => $path ? filesize($path) : strlen($data),
+ );
+
+ $attachment = $RCMAIL->plugins->exec_hook('attachment_save', $attachment);
+
+ if ($attachment['status']) {
+ unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
+ $COMPOSE['attachments'][$attachment['id']] = $attachment;
+ }
+ else if ($path) {
+ @unlink($path);
+ }
+ }
}
@@ -1212,16 +1181,7 @@ function rcmail_save_attachment(&$message, $pid)
}
$mimetype = $part->ctype_primary . '/' . $part->ctype_secondary;
- $filename = $part->filename;
- if (!strlen($filename)) {
- if ($mimetype == 'text/html') {
- $filename = rcube_label('htmlmessage');
- }
- else {
- $filename = 'Part_'.$pid;
- }
- $filename .= '.' . $part->ctype_secondary;
- }
+ $filename = rcmail_attachment_name($part);
$attachment = array(
'group' => $COMPOSE['id'],
@@ -1625,7 +1585,7 @@ function rcmail_addressbook_list($attrib = array())
$class_name .= ' ' . $source['class_name'];
$out .= sprintf($line_templ,
- html_identifier($id),
+ html_identifier($id,true),
$class_name,
$source['id'],
$js_id, (!empty($source['name']) ? $source['name'] : $id));
diff --git a/program/steps/mail/folders.inc b/program/steps/mail/folders.inc
index c56c914cd..574d6e975 100644
--- a/program/steps/mail/folders.inc
+++ b/program/steps/mail/folders.inc
@@ -65,6 +65,7 @@ else if ($RCMAIL->action == 'purge')
if (!empty($_REQUEST['_reload'])) {
$OUTPUT->set_env('messagecount', 0);
$OUTPUT->set_env('pagecount', 0);
+ $OUTPUT->set_env('exists', 0);
$OUTPUT->command('message_list.clear');
$OUTPUT->command('set_rowcount', rcmail_get_messagecount_text(), $mbox);
$OUTPUT->command('set_unread_count', $mbox, 0);
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index cb1a5ddae..274c40b5c 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-2010, The Roundcube Dev Team |
+ | Copyright (C) 2005-2012, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
@@ -89,11 +89,12 @@ if (empty($RCMAIL->action) || $RCMAIL->action == 'list') {
}
$threading = (bool) $RCMAIL->storage->get_threading();
+ $delimiter = $RCMAIL->storage->get_hierarchy_delimiter();
// set current mailbox and some other vars in client environment
$OUTPUT->set_env('mailbox', $mbox_name);
$OUTPUT->set_env('pagesize', $RCMAIL->storage->get_pagesize());
- $OUTPUT->set_env('delimiter', $RCMAIL->storage->get_hierarchy_delimiter());
+ $OUTPUT->set_env('delimiter', $delimiter);
$OUTPUT->set_env('threading', $threading);
$OUTPUT->set_env('threads', $threading || $RCMAIL->storage->get_capability('THREAD'));
$OUTPUT->set_env('preview_pane_mark_read', $RCMAIL->config->get('preview_pane_mark_read', 0));
@@ -121,7 +122,10 @@ if (empty($RCMAIL->action) || $RCMAIL->action == 'list') {
'movingmessage', 'copyingmessage', 'deletingmessage', 'markingmessage',
'copy', 'move', 'quota');
- $OUTPUT->set_pagetitle(rcmail_localize_foldername($RCMAIL->storage->mod_folder($mbox_name)));
+ $pagetitle = $RCMAIL->localize_foldername($RCMAIL->storage->mod_folder($mbox_name), true);
+ $pagetitle = str_replace($delimiter, " \xC2\xBB ", $pagetitle);
+
+ $OUTPUT->set_pagetitle($pagetitle);
}
/**
@@ -628,39 +632,6 @@ function rcmail_wash_html($html, $p, $cid_replaces)
$p += array('safe' => false, 'inline_html' => true);
- // special replacements (not properly handled by washtml class)
- $html_search = array(
- '/(<\/nobr>)(\s+)(<nobr>)/i', // space(s) between <NOBR>
- '/<title[^>]*>[^<]*<\/title>/i', // PHP bug #32547 workaround: remove title tag
- '/^(\0\0\xFE\xFF|\xFF\xFE\0\0|\xFE\xFF|\xFF\xFE|\xEF\xBB\xBF)/', // byte-order mark (only outlook?)
- '/<html\s[^>]+>/i', // washtml/DOMDocument cannot handle xml namespaces
- );
- $html_replace = array(
- '\\1'.' &nbsp; '.'\\3',
- '',
- '',
- '<html>',
- );
- $html = preg_replace($html_search, $html_replace, trim($html));
-
- // PCRE errors handling (#1486856), should we use something like for every preg_* use?
- if ($html === null && ($preg_error = preg_last_error()) != PREG_NO_ERROR) {
- $errstr = "Could not clean up HTML message! PCRE Error: $preg_error.";
-
- if ($preg_error == PREG_BACKTRACK_LIMIT_ERROR)
- $errstr .= " Consider raising pcre.backtrack_limit!";
- if ($preg_error == PREG_RECURSION_LIMIT_ERROR)
- $errstr .= " Consider raising pcre.recursion_limit!";
-
- raise_error(array('code' => 620, 'type' => 'php',
- 'line' => __LINE__, 'file' => __FILE__,
- 'message' => $errstr), true, false);
- return '';
- }
-
- // fix (unknown/malformed) HTML tags before "wash"
- $html = preg_replace_callback('/(<[\/]*)([^\s>]+)/', 'rcmail_html_tag_callback', $html);
-
// charset was converted to UTF-8 in rcube_storage::get_message_part(),
// change/add charset specification in HTML accordingly,
// washtml cannot work without that
@@ -674,9 +645,6 @@ function rcmail_wash_html($html, $p, $cid_replaces)
$html = '<head>' . $meta . '</head>' . $html;
}
- // turn relative into absolute urls
- $html = rcmail_resolve_base($html);
-
// clean HTML with washhtml by Frederic Motte
$wash_opts = array(
'show_washed' => false,
@@ -702,7 +670,7 @@ function rcmail_wash_html($html, $p, $cid_replaces)
$wash_opts['html_attribs'] = $p['html_attribs'];
// initialize HTML washer
- $washer = new washtml($wash_opts);
+ $washer = new rcube_washtml($wash_opts);
if (!$p['skip_washer_form_callback'])
$washer->add_callback('form', 'rcmail_washtml_callback');
@@ -739,8 +707,11 @@ function rcmail_print_body($part, $p = array())
+ $p + array('safe' => false, 'plain' => false, 'inline_html' => true));
// convert html to text/plain
- if ($data['type'] == 'html' && $data['plain']) {
- $txt = new html2text($data['body'], false, true);
+ if ($data['plain'] && ($data['type'] == 'html' || $data['type'] == 'enriched')) {
+ if ($data['type'] == 'enriched') {
+ $data['body'] = rcube_enriched::to_html($data['body']);
+ }
+ $txt = new rcube_html2text($data['body'], false, true);
$body = $txt->get_text();
$part->ctype_secondary = 'plain';
}
@@ -751,8 +722,7 @@ function rcmail_print_body($part, $p = array())
}
// text/enriched
else if ($data['type'] == 'enriched') {
- require_once(INSTALL_PATH . 'program/lib/enriched.inc');
- $body = enriched_to_html($data['body']);
+ $body = rcube_enriched::to_html($data['body']);
$body = rcmail_wash_html($body, $data, $part->replaces);
$part->ctype_secondary = 'html';
}
@@ -790,7 +760,8 @@ function rcmail_plain_body($body, $flowed=false)
global $RCMAIL;
// make links and email-addresses clickable
- $replacer = new rcmail_string_replacer;
+ $attribs = array('link_attribs' => array('rel' => 'noreferrer', 'target' => '_blank'));
+ $replacer = new rcmail_string_replacer($attribs);
// search for patterns like links and e-mail addresses and replace with tokens
$body = $replacer->replace($body);
@@ -922,31 +893,15 @@ function rcmail_washtml_callback($tagname, $attrib, $content, $washtml)
/**
- * Callback function for HTML tags fixing
- */
-function rcmail_html_tag_callback($matches)
-{
- $tagname = $matches[2];
-
- $tagname = preg_replace(array(
- '/:.*$/', // Microsoft's Smart Tags <st1:xxxx>
- '/[^a-z0-9_\[\]\!-]/i', // forbidden characters
- ), '', $tagname);
-
- return $matches[1].$tagname;
-}
-
-
-/**
* return table with message headers
*/
-function rcmail_message_headers($attrib, $headers=NULL)
+function rcmail_message_headers($attrib, $headers=null)
{
global $OUTPUT, $MESSAGE, $PRINT_MODE, $RCMAIL;
static $sa_attrib;
// keep header table attrib
- if (is_array($attrib) && !$sa_attrib)
+ if (is_array($attrib) && !$sa_attrib && !$attrib['valueof'])
$sa_attrib = $attrib;
else if (!is_array($attrib) && is_array($sa_attrib))
$attrib = $sa_attrib;
@@ -955,11 +910,20 @@ function rcmail_message_headers($attrib, $headers=NULL)
return FALSE;
// get associative array of headers object
- if (!$headers)
- $headers = is_object($MESSAGE->headers) ? get_object_vars($MESSAGE->headers) : $MESSAGE->headers;
+ if (!$headers) {
+ $headers_obj = $MESSAGE->headers;
+ $headers = get_object_vars($MESSAGE->headers);
+ }
+ else if (is_object($headers)) {
+ $headers_obj = $headers;
+ $headers = get_object_vars($headers_obj);
+ }
+ else {
+ $headers_obj = rcube_message_header::from_array($headers);
+ }
// show these headers
- $standard_headers = array('subject', 'from', 'to', 'cc', 'bcc', 'replyto',
+ $standard_headers = array('subject', 'from', 'sender', 'to', 'cc', 'bcc', 'replyto',
'mail-reply-to', 'mail-followup-to', 'date', 'priority');
$exclude_headers = $attrib['exclude'] ? explode(',', $attrib['exclude']) : array();
$output_headers = array();
@@ -1010,6 +974,14 @@ function rcmail_message_headers($attrib, $headers=NULL)
else
continue;
}
+ else if ($hkey == 'sender') {
+ if ($headers['sender'] != $headers['from']) {
+ $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
+ $ishtml = true;
+ }
+ else
+ continue;
+ }
else if ($hkey == 'mail-followup-to') {
$header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title);
$ishtml = true;
@@ -1032,7 +1004,7 @@ function rcmail_message_headers($attrib, $headers=NULL)
}
$plugin = $RCMAIL->plugins->exec_hook('message_headers_output',
- array('output' => $output_headers, 'headers' => $MESSAGE->headers, 'exclude' => $exclude_headers));
+ array('output' => $output_headers, 'headers' => $headers_obj, 'exclude' => $exclude_headers));
// single header value is requested
if (!empty($attrib['valueof']))
@@ -1111,12 +1083,13 @@ function rcmail_message_body($attrib)
if (!empty($MESSAGE->parts)) {
foreach ($MESSAGE->parts as $i => $part) {
- if ($part->type == 'headers')
- $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
+ if ($part->type == 'headers') {
+ $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers));
+ }
else if ($part->type == 'content') {
- // unsapported
+ // unsupported (e.g. encrypted)
if ($part->realtype) {
- if ($part->realtype == 'multipart/encrypted') {
+ if ($part->realtype == 'multipart/encrypted' || $part->realtype == 'application/pkcs7-mime') {
$out .= html::span('part-notice', rcube_label('encryptedmessage'));
}
continue;
@@ -1140,6 +1113,15 @@ function rcmail_message_body($attrib)
if (!isset($part->body))
$part->body = $MESSAGE->get_part_content($part->mime_id);
+ // extract headers from message/rfc822 parts
+ if ($part->mimetype == 'message/rfc822') {
+ $msgpart = rcube_mime::parse_message($part->body);
+ if (!empty($msgpart->headers)) {
+ $part = $msgpart;
+ $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers));
+ }
+ }
+
// message is cached but not exists (#1485443), or other error
if ($part->body === false) {
rcmail_message_error($MESSAGE->uid);
@@ -1204,10 +1186,9 @@ function rcmail_message_body($attrib)
$show_link = array(
'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false),
'onclick' => sprintf(
- 'return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)',
+ 'return %s.command(\'load-attachment\',\'%s\',this)',
JS_OBJECT_NAME,
- $attach_prop->mime_id,
- $mimetype)
+ $attach_prop->mime_id)
);
$out .= html::p('image-attachment',
html::a($show_link + array('class' => 'image-link', 'style' => sprintf('width:%dpx', $thumbnail_size)),
@@ -1264,7 +1245,7 @@ function rcmail_part_image_type($part)
// Content-Type: image/*...
if (preg_match($mime_regex, $part->mimetype)) {
- return $part->mimetype;
+ return rcmail_fix_mimetype($part->mimetype);
}
// Many clients use application/octet-stream, we'll detect mimetype
@@ -1293,20 +1274,6 @@ function rcmail_part_image_type($part)
}
}
-/**
- * Convert all relative URLs according to a <base> in HTML
- */
-function rcmail_resolve_base($body)
-{
- // check for <base href=...>
- if (preg_match('!(<base.*href=["\']?)([hftps]{3,5}://[a-z0-9/.%-]+)!i', $body, $regs)) {
- $replacer = new rcube_base_replacer($regs[2]);
- $body = $replacer->replace($body);
- }
-
- return $body;
-}
-
/**
* modify a HTML message that it can be displayed inside a HTML page
@@ -1407,7 +1374,7 @@ function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null
/**
- * parse link attributes and set correct target
+ * parse link (a, link, area) attributes and set correct target
*/
function rcmail_alter_html_link($matches)
{
@@ -1416,9 +1383,9 @@ function rcmail_alter_html_link($matches)
// Support unicode/punycode in top-level domain part
$EMAIL_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))';
- $tag = $matches[1];
+ $tag = strtolower($matches[1]);
$attrib = parse_attrib_string($matches[2]);
- $end = '>';
+ $end = '>';
// Remove non-printable characters in URL (#1487805)
if ($attrib['href'])
@@ -1445,6 +1412,11 @@ function rcmail_alter_html_link($matches)
$attrib['target'] = '_blank';
}
+ // Better security by adding rel="noreferrer" (#1484686)
+ if (($tag == 'a' || $tag == 'area') && $attrib['href'] && $attrib['href'][0] != '#') {
+ $attrib['rel'] = 'noreferrer';
+ }
+
// allowed attributes for a|link|area tags
$allow = array('href','name','target','onclick','id','class','style','title',
'rel','type','media','alt','coords','nohref','hreflang','shape');
@@ -1480,6 +1452,11 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null,
$mailto = $part['mailto'];
$string = $part['string'];
+ // phishing email prevention (#1488981), e.g. "valid@email.addr <phishing@email.addr>"
+ if ($name && $name != $mailto && strpos($name, '@')) {
+ $name = '';
+ }
+
// IDNA ASCII to Unicode
if ($name == $mailto)
$name = rcube_idn_to_utf8($name);
@@ -1510,7 +1487,7 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null,
if ($addicon && $_SESSION['writeable_abook']) {
$address .= html::a(array(
'href' => "#add",
- 'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, $string),
+ 'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, JQ($string)),
'title' => rcube_label('addtoaddressbook'),
'class' => 'rcmaddcontact',
),
@@ -1638,10 +1615,7 @@ function rcmail_message_part_controls($attrib)
$part = $MESSAGE->mime_parts[$part];
$table = new html_table(array('cols' => 3));
- $filename = $part->filename;
- if (empty($filename) && $attach_prop->mimetype == 'text/html') {
- $filename = rcube_label('htmlmessage');
- }
+ $filename = rcmail_attachment_name($part);
if (!empty($filename)) {
$table->add('title', Q(rcube_label('filename')));
@@ -1656,7 +1630,6 @@ function rcmail_message_part_controls($attrib)
}
-
function rcmail_message_part_frame($attrib)
{
global $MESSAGE;
@@ -1702,11 +1675,11 @@ function rcmail_send_mdn($message, &$smtp_error)
if ($message->headers->mdn_to && empty($message->headers->flags['MDNSENT']) &&
($RCMAIL->storage->check_permflag('MDNSENT') || $RCMAIL->storage->check_permflag('*')))
{
- $identity = $RCMAIL->user->get_identity();
- $sender = format_email_recipient($identity['email'], $identity['name']);
+ $identity = rcmail_identity_select($message);
+ $sender = format_email_recipient($identity['email'], $identity['name']);
$recipient = array_shift(rcube_mime::decode_address_list(
$message->headers->mdn_to, 1, true, $message->headers->charset));
- $mailto = $recipient['mailto'];
+ $mailto = $recipient['mailto'];
$compose = new Mail_mime("\r\n");
@@ -1731,6 +1704,9 @@ function rcmail_send_mdn($message, &$smtp_error)
if ($agent = $RCMAIL->config->get('useragent'))
$headers['User-Agent'] = $agent;
+ if ($RCMAIL->config->get('mdn_use_from'))
+ $options['mdn_use_from'] = true;
+
$body = rcube_label("yourmessage") . "\r\n\r\n" .
"\t" . rcube_label("to") . ': ' . rcube_mime::decode_mime_string($message->headers->to, $message->headers->charset) . "\r\n" .
"\t" . rcube_label("subject") . ': ' . $message->subject . "\r\n" .
@@ -1752,7 +1728,7 @@ function rcmail_send_mdn($message, &$smtp_error)
$compose->setTXTBody(rc_wordwrap($body, 75, "\r\n"));
$compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
- $sent = rcmail_deliver_message($compose, $identity['email'], $mailto, $smtp_error, $body_file);
+ $sent = rcmail_deliver_message($compose, $identity['email'], $mailto, $smtp_error, $body_file, $options);
if ($sent)
{
@@ -1764,6 +1740,107 @@ function rcmail_send_mdn($message, &$smtp_error)
return false;
}
+/**
+ * Detect recipient identity from specified message
+ */
+function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'reply')
+{
+ $a_recipients = array();
+ $a_names = array();
+
+ if ($identities === null) {
+ $identities = rcmail::get_instance()->user->list_identities(null, true);
+ }
+
+ // extract all recipients of the reply-message
+ if (is_object($MESSAGE->headers) && in_array($compose_mode, array('reply', 'forward'))) {
+ $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset);
+ foreach ($a_to as $addr) {
+ if (!empty($addr['mailto'])) {
+ $a_recipients[] = format_email($addr['mailto']);
+ $a_names[] = $addr['name'];
+ }
+ }
+
+ if (!empty($MESSAGE->headers->cc)) {
+ $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset);
+ foreach ($a_cc as $addr) {
+ if (!empty($addr['mailto'])) {
+ $a_recipients[] = format_email($addr['mailto']);
+ $a_names[] = $addr['name'];
+ }
+ }
+ }
+ }
+
+ $from_idx = null;
+ $found_idx = null;
+ $default_identity = 0; // default identity is always first on the list
+
+ // Select identity
+ foreach ($identities as $idx => $ident) {
+ // use From header
+ if (in_array($compose_mode, array('draft', 'edit'))) {
+ if ($MESSAGE->headers->from == $ident['ident']) {
+ $from_idx = $idx;
+ break;
+ }
+ }
+ // reply to yourself
+ else if ($compose_mode == 'reply' && $MESSAGE->headers->from == $ident['ident']) {
+ $from_idx = $idx;
+ break;
+ }
+ // use replied message recipients
+ else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) {
+ if ($found_idx === null) {
+ $found_idx = $idx;
+ }
+ // match identity name
+ if ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name']) {
+ $from_idx = $idx;
+ break;
+ }
+ }
+ }
+
+ // If matching by name+address doesn't found any matches, get first found address (identity)
+ if ($from_idx === null) {
+ $from_idx = $found_idx;
+ }
+
+ // Try Return-Path
+ if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) {
+ foreach ($identities as $idx => $ident) {
+ if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) {
+ $from_idx = $idx;
+ break;
+ }
+ }
+ }
+
+ // Fallback using Delivered-To
+ if ($from_idx === null && ($delivered_to = $MESSAGE->headers->others['delivered-to'])) {
+ foreach ($identities as $idx => $ident) {
+ if (in_array($ident['email_ascii'], (array)$delivered_to)) {
+ $from_idx = $idx;
+ break;
+ }
+ }
+ }
+
+ // Fallback using Envelope-To
+ if ($from_idx === null && ($envelope_to = $MESSAGE->headers->others['envelope-to'])) {
+ foreach ($identities as $idx => $ident) {
+ if (in_array($ident['email_ascii'], (array)$envelope_to)) {
+ $from_idx = $idx;
+ break;
+ }
+ }
+ }
+
+ return $identities[$from_idx !== null ? $from_idx : $default_identity];
+}
// Fixes some content-type names
function rcmail_fix_mimetype($name)
@@ -1773,9 +1850,44 @@ function rcmail_fix_mimetype($name)
if (preg_match('/^application\/pdf.+/', $name))
$name = 'application/pdf';
+ // treat image/pjpeg as image/jpeg
+ else if (preg_match('/^image\/p?jpe?g$/', $name))
+ $name = 'image/jpeg';
+
return $name;
}
+// return attachment filename, handle empty filename case
+function rcmail_attachment_name($attachment, $display = false)
+{
+ $filename = $attachment->filename;
+
+ if ($filename === null || $filename === '') {
+ if ($attachment->mimetype == 'text/html') {
+ $filename = rcube_label('htmlmessage');
+ }
+ else {
+ $ext = (array) rcube_mime::get_mime_extensions($attachment->mimetype);
+ $ext = array_shift($ext);
+ $filename = rcube_label('messagepart') . ' ' . $attachment->mime_id;
+ if ($ext) {
+ $filename .= '.' . $ext;
+ }
+ }
+ }
+
+ $filename = preg_replace('[\r\n]', '', $filename);
+
+ // Display smart names for some known mimetypes
+ if ($display) {
+ if (preg_match('/application\/(pgp|pkcs7)-signature/i', $attachment->mimetype)) {
+ $filename = rcube_label('digitalsig');
+ }
+ }
+
+ return $filename;
+}
+
function rcmail_search_filter($attrib)
{
global $OUTPUT, $CONFIG;
diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc
index 71a5e1b02..23dc22b7c 100644
--- a/program/steps/mail/get.inc
+++ b/program/steps/mail/get.inc
@@ -35,6 +35,7 @@ if (!empty($_GET['_preload'])) {
ob_end_clean();
+
// similar code as in program/steps/mail/show.inc
if (!empty($_GET['_uid'])) {
$RCMAIL->config->set('prefer_html', true);
@@ -47,13 +48,7 @@ check_storage_status();
// show part page
if (!empty($_GET['_frame'])) {
if (($part_id = get_input_value('_part', RCUBE_INPUT_GPC)) && ($part = $MESSAGE->mime_parts[$part_id])) {
- $filename = $part->filename;
- if (empty($filename) && $part->mimetype == 'text/html') {
- $filename = rcube_label('htmlmessage');
- }
- if (!empty($filename)) {
- $OUTPUT->set_pagetitle($filename);
- }
+ $OUTPUT->set_pagetitle(rcmail_attachment_name($part));
}
$OUTPUT->send('messagepart');
@@ -65,11 +60,11 @@ else if ($_GET['_thumb']) {
$pid = get_input_value('_part', RCUBE_INPUT_GET);
if ($part = $MESSAGE->mime_parts[$pid]) {
$thumbnail_size = $RCMAIL->config->get('image_thumbnail_size', 240);
- $temp_dir = $RCMAIL->config->get('temp_dir');
- list(,$ext) = explode('/', $part->mimetype);
+ $temp_dir = $RCMAIL->config->get('temp_dir');
+ list(,$ext) = explode('/', $part->mimetype);
$cache_basename = $temp_dir . '/' . md5($MESSAGE->headers->messageID . $part->mime_id . ':' . $RCMAIL->user->ID . ':' . $thumbnail_size);
- $cache_file = $cache_basename . '.' . $ext;
- $mimetype = $part->mimetype;
+ $cache_file = $cache_basename . '.' . $ext;
+ $mimetype = $part->mimetype;
// render thumbnail image if not done yet
if (!is_file($cache_file)) {
@@ -78,7 +73,7 @@ else if ($_GET['_thumb']) {
fclose($fp);
$image = new rcube_image($orig_name);
- if ($imgtype = $image->resize($RCMAIL->config->get('image_thumbnail_size', 240), $cache_file, true)) {
+ if ($imgtype = $image->resize($thumbnail_size, $cache_file, true)) {
$mimetype = 'image/' . $imgtype;
unlink($orig_name);
}
@@ -126,7 +121,7 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) {
$valid = $file_extension && in_array($file_extension, (array)$extensions);
// 2. detect the real mimetype of the attachment part and compare it with the stated mimetype and filename extension
- if ($valid || !$file_extension || $mimetype == 'application/octet-stream') {
+ if ($valid || !$file_extension || $mimetype == 'application/octet-stream' || $mimetype == 'text/plain') {
if ($part->body) // part body is already loaded
$body = $part->body;
else if ($part->size && $part->size < 1024*1024) // load the entire part if it's small enough
@@ -138,6 +133,10 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) {
$real_mimetype = rcube_mime::file_content_type($body, $part->filename, $mimetype, true, true);
list($real_ctype_primary, $real_ctype_secondary) = explode('/', $real_mimetype);
+ // accept text/plain with any extension
+ if ($real_mimetype == 'text/plain' && $real_mimetype == $mimetype)
+ $file_extension = 'txt';
+
// ignore differences in text/* mimetypes. Filetype detection isn't very reliable here
if ($real_ctype_primary == 'text' && strpos($mimetype, $real_ctype_primary) === 0)
$real_mimetype = $mimetype;
@@ -159,19 +158,20 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) {
// show warning if validity checks failed
if (!$valid) {
$OUTPUT = new rcmail_html_page();
- $OUTPUT->write(html::tag('html', null, html::tag('body', array('style' => 'font-family:sans-serif; margin:1em'),
- html::div(array('class' => 'warning', 'style' => 'border:2px solid #ffdf0e; background:#fef893; padding:1em 1em 0 1em;'),
+ $OUTPUT->write(html::tag('html', null, html::tag('body', 'embed',
+ html::div(array('class' => 'rcmail-inline-message rcmail-inline-warning'),
rcube_label(array(
'name' => 'attachmentvalidationerror',
- 'vars' => array('expected' => "$mimetype (.$file_extension)", 'detected' => "$real_mimetype (.$extensions[0])")
+ 'vars' => array(
+ 'expected' => $mimetype . ($file_extension ? "(.$file_extension)" : ''),
+ 'detected' => $real_mimetype . ($extensions[0] ? "(.$extensions[0])" : ''),
+ )
)) .
- html::p('buttons',
- html::tag('button', null,
- html::a(array(
- 'href' => $RCMAIL->url(array_merge($_GET, array('_nocheck' => 1))),
- 'style' => 'text-decoration:none;color:#000',
- ), rcube_label('showanyway')))
- ))
+ html::p(array('class' => 'rcmail-inline-buttons'),
+ html::tag('button',
+ array('onclick' => "location.href='" . $RCMAIL->url(array_merge($_GET, array('_nocheck' => 1))) . "'"),
+ rcube_label('showanyway')))
+ )
)));
exit;
}
@@ -222,7 +222,27 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) {
if (!$part->body)
$part->body = $MESSAGE->get_part_content($part->mime_id);
+ // show images?
+ rcmail_check_safe($MESSAGE);
+
+ // render HTML body
$out = rcmail_print_body($part, array('safe' => $MESSAGE->is_safe, 'inline_html' => false));
+
+ // insert remote objects warning into HTML body
+ if ($REMOTE_OBJECTS) {
+ $body_start = 0;
+ if ($body_pos = strpos($out, '<body')) {
+ $body_start = strpos($out, '>', $body_pos) + 1;
+ }
+ $out = substr($out, 0, $body_start) .
+ html::div(array('class' => 'rcmail-inline-message rcmail-inline-warning'),
+ Q(rcube_label('blockedimages')) . '&nbsp;' .
+ html::tag('button',
+ array('onclick' => "location.href='" . $RCMAIL->url(array_merge($_GET, array('_safe' => 1))) . "'"),
+ Q(rcube_label('showimages')))
+ ) .
+ substr($out, $body_start);
+ }
}
// check connection status
@@ -237,18 +257,7 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) {
// don't kill the connection if download takes more than 30 sec.
@set_time_limit(0);
- if ($part->filename) {
- $filename = $part->filename;
- }
- else if ($part->mimetype == 'text/html') {
- $filename = rcube_label('htmlmessage');
- }
- else {
- $ext = '.' . ($mimetype == 'text/plain' ? 'txt' : $ctype_secondary);
- $filename = ($MESSAGE->subject ? $MESSAGE->subject : 'roundcube') . $ext;
- }
-
- $filename = preg_replace('[\r\n]', '', $filename);
+ $filename = rcmail_attachment_name($part);
if ($browser->ie && $browser->ver < 7)
$filename = rawurlencode(abbreviate_string($filename, 55));
@@ -259,6 +268,18 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) {
$disposition = !empty($plugin['download']) ? 'attachment' : 'inline';
+ // Workaround for nasty IE bug (#1488844)
+ // If Content-Disposition header contains string "attachment" e.g. in filename
+ // IE handles data as attachment not inline
+ if ($disposition == 'inline' && $browser->ie && $browser->ver < 9) {
+ $filename = str_ireplace('attachment', 'attach', $filename);
+ }
+
+ // add filename extension if missing
+ if (!pathinfo($filename, PATHINFO_EXTENSION) && ($extensions = rcube_mime::get_mime_extensions($mimetype))) {
+ $filename .= '.' . $extensions[0];
+ }
+
header("Content-Disposition: $disposition; filename=\"$filename\"");
// handle tiff to jpeg conversion
@@ -302,7 +323,7 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) {
// send part as-it-is
else {
if ($part->body) {
- header("Content-Length: " . sizeof($part->body));
+ header("Content-Length: " . strlen($part->body));
echo $part->body;
$sent = true;
}
diff --git a/program/steps/mail/list.inc b/program/steps/mail/list.inc
index b433f81fc..a2380131a 100644
--- a/program/steps/mail/list.inc
+++ b/program/steps/mail/list.inc
@@ -95,8 +95,8 @@ $OUTPUT->set_env('messagecount', $count);
$OUTPUT->set_env('pagecount', $pages);
$OUTPUT->set_env('threading', $threading);
$OUTPUT->set_env('current_page', $count ? $RCMAIL->storage->get_page() : 1);
+$OUTPUT->set_env('exists', $RCMAIL->storage->count($mbox_name, 'EXISTS'));
$OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count), $mbox_name);
-$OUTPUT->command('set_mailboxname', rcmail_get_mailbox_name_text());
// add message rows
rcmail_js_message_list($a_headers, FALSE, $cols);
diff --git a/program/steps/mail/list_contacts.inc b/program/steps/mail/list_contacts.inc
index 9347190da..7e3b349cd 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, The Roundcube Dev Team |
+ | Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
@@ -19,72 +19,117 @@
+-----------------------------------------------------------------------+
*/
-$jsenv = array();
-$source = get_input_value('_source', RCUBE_INPUT_GPC);
-$CONTACTS = $RCMAIL->get_address_book($source);
-$PAGE_SIZE = $RCMAIL->config->get('addressbook_pagesize', $RCMAIL->config->get('pagesize', 50));
-
-if ($CONTACTS && $CONTACTS->ready) {
- // set list properties
- $CONTACTS->set_pagesize($PAGE_SIZE);
- $CONTACTS->set_page(max(1, intval($_GET['_page'])));
-
- // list groups of this source (on page one)
- 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']);
+$afields = $RCMAIL->config->get('contactlist_fields');
+$sort_col = $RCMAIL->config->get('addressbook_sort_col', 'name');
+$page_size = $RCMAIL->config->get('addressbook_pagesize', $RCMAIL->config->get('pagesize', 50));
+$page = max(1, intval($_GET['_page']));
+
+// Use search result
+if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']])) {
+ $search = (array)$_SESSION['search'][$_REQUEST['_search']];
+
+ // get records from all sources
+ foreach ($search as $s => $set) {
+ $CONTACTS = $RCMAIL->get_address_book($s);
+
+ // reset page
+ $CONTACTS->set_page(1);
+ $CONTACTS->set_pagesize(9999);
+ $CONTACTS->set_search_set($set);
+
+ // get records
+ $result = $CONTACTS->list_records($afields);
+
+ while ($row = $result->next()) {
+ $row['sourceid'] = $s;
+ $key = rcube_addressbook::compose_contact_key($row, $sort_col);
+ $records[$key] = $row;
+ }
+ unset($result);
+ }
+
+ // sort the records
+ ksort($records, SORT_LOCALE_STRING);
+
+ // create resultset object
+ $count = count($records);
+ $first = ($page-1) * $page_size;
+ $result = new rcube_result_set($count, $first);
+
+ // we need only records for current page
+ if ($page_size < $count) {
+ $records = array_slice($records, $first, $page_size);
+ }
+
+ $result->records = array_values($records);
+}
+// list contacts from selected source
+else {
+ $source = get_input_value('_source', RCUBE_INPUT_GPC);
+ $CONTACTS = $RCMAIL->get_address_book($source);
+
+ if ($CONTACTS && $CONTACTS->ready) {
+ // set list properties
+ $CONTACTS->set_pagesize($page_size);
+ $CONTACTS->set_page($page);
+
+ // list groups of this source (on page one)
+ 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), Q($group['name']))), 'group');
+ }
+ }
+ // 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' => html::span(array('title' => $email), Q($group['name']))), 'group');
+ 'contactgroup' => Q($group['name'] . ' (' . intval($result->count) . ')')), 'group');
}
}
- // 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' => Q($group['name'] . ' (' . intval($result->count) . ')')), 'group');
- }
}
+
+ // get contacts for this user
+ $CONTACTS->set_group(0);
+ $result = $CONTACTS->list_records($afields);
}
+}
- // get contacts for this user
- $CONTACTS->set_group(0);
- $afields = $RCMAIL->config->get('contactlist_fields');
- $result = $CONTACTS->list_records($afields);
+if (!empty($result) && !$result->count && $result->searchonly) {
+ $OUTPUT->show_message('contactsearchonly', 'notice');
+}
+else if (!empty($result) && $result->count > 0) {
+ // create javascript list
+ while ($row = $result->next()) {
+ $name = rcube_addressbook::compose_list_name($row);
- if (!$result->count && $result->searchonly) {
- $OUTPUT->show_message('contactsearchonly', 'notice');
- }
- else if (!empty($result) && $result->count > 0) {
- // create javascript list
- while ($row = $result->next()) {
- $name = rcube_addressbook::compose_list_name($row);
-
- // add record for every email address of the contact
- $emails = $CONTACTS->get_col_values('email', $row, true);
- foreach ($emails as $i => $email) {
- $row_id = $row['ID'].$i;
- $jsresult[$row_id] = format_email_recipient($email, $name);
- $OUTPUT->command('add_contact_row', $row_id, array(
- 'contact' => html::span(array('title' => $email), Q($name ? $name : $email) .
- ($name && count($emails) > 1 ? '&nbsp;' . html::span('email', Q($email)) : '')
- )), 'person');
- }
+ // add record for every email address of the contact
+ $emails = $CONTACTS->get_col_values('email', $row, true);
+ foreach ($emails as $i => $email) {
+ $row_id = $row['ID'].$i;
+ $jsresult[$row_id] = format_email_recipient($email, $name);
+ $OUTPUT->command('add_contact_row', $row_id, array(
+ 'contact' => html::span(array('title' => $email), Q($name ? $name : $email) .
+ ($name && count($emails) > 1 ? '&nbsp;' . html::span('email', Q($email)) : '')
+ )), 'person');
}
}
}
+
// update env
$OUTPUT->set_env('contactdata', $jsresult);
-$OUTPUT->set_env('pagecount', ceil($result->count / $PAGE_SIZE));
+$OUTPUT->set_env('pagecount', ceil($result->count / $page_size));
$OUTPUT->command('set_page_buttons');
// send response
diff --git a/program/steps/mail/move_del.inc b/program/steps/mail/move_del.inc
index da43b4000..3fc6ac5a7 100644
--- a/program/steps/mail/move_del.inc
+++ b/program/steps/mail/move_del.inc
@@ -29,18 +29,19 @@ $old_count = $RCMAIL->storage->count(NULL, $threading ? 'THREADS' : 'ALL');
$old_pages = ceil($old_count / $RCMAIL->storage->get_pagesize());
// move messages
-if ($RCMAIL->action=='moveto' && !empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) {
- $count = sizeof(explode(',', ($uids = get_input_value('_uid', RCUBE_INPUT_POST))));
+if ($RCMAIL->action == 'moveto' && !empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) {
+ $count = sizeof(explode(',', ($uids = get_input_value('_uid', RCUBE_INPUT_POST))));
$target = get_input_value('_target_mbox', RCUBE_INPUT_POST, true);
- $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true);
+ $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true);
+ $trash = $RCMAIL->config->get('trash_mbox');
$moved = $RCMAIL->storage->move_message($uids, $target, $mbox);
if (!$moved) {
// send error message
- if ($_POST['_from'] != 'show')
+ if ($_POST['_from'] != 'show')
$OUTPUT->command('list_mailbox');
- rcmail_display_server_error('errormoving');
+ rcmail_display_server_error('errormoving', null, $target == $trash ? 'delete' : '');
$OUTPUT->send();
exit;
}
@@ -59,7 +60,7 @@ else if ($RCMAIL->action=='delete' && !empty($_POST['_uid'])) {
if (!$del) {
// send error message
- if ($_POST['_from'] != 'show')
+ if ($_POST['_from'] != 'show')
$OUTPUT->command('list_mailbox');
rcmail_display_server_error('errordeleting');
$OUTPUT->send();
@@ -111,6 +112,7 @@ else
$OUTPUT->set_env('messagecount', $msg_count);
$OUTPUT->set_env('current_page', $page);
$OUTPUT->set_env('pagecount', $pages);
+ $OUTPUT->set_env('exists', $RCMAIL->storage->count($mbox, 'EXISTS', true));
// update mailboxlist
$mbox = $RCMAIL->storage->get_folder();
@@ -144,5 +146,3 @@ else
// send response
$OUTPUT->send();
-
-
diff --git a/program/steps/mail/search.inc b/program/steps/mail/search.inc
index db5424b3b..fb1b48797 100644
--- a/program/steps/mail/search.inc
+++ b/program/steps/mail/search.inc
@@ -69,7 +69,7 @@ else if (preg_match("/^subject:.*/i", $str))
else if (preg_match("/^body:.*/i", $str))
{
list(,$srch) = explode(":", $str);
- $subject['text'] = "TEXT";
+ $subject['body'] = "BODY";
}
else if (strlen(trim($str)))
{
@@ -81,7 +81,7 @@ else if (strlen(trim($str)))
break;
}
else {
- $subject[$header] = 'HEADER '.strtoupper($header);
+ $subject[$header] = ($header != 'body' ? 'HEADER ' : '') . strtoupper($header);
}
}
@@ -89,7 +89,8 @@ else if (strlen(trim($str)))
$search_mods = $RCMAIL->config->get('search_mods', $SEARCH_MODS_DEFAULT);
$search_mods[$mbox] = array_fill_keys(array_keys($subject), 1);
$RCMAIL->user->save_prefs(array('search_mods' => $search_mods));
- } else {
+ }
+ else {
// search in subject by default
$subject['subject'] = 'HEADER SUBJECT';
}
@@ -143,5 +144,6 @@ else {
$OUTPUT->set_env('search_request', $search_str ? $search_request : '');
$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->command('set_rowcount', rcmail_get_messagecount_text($count, 1), $mbox);
$OUTPUT->send();
diff --git a/program/steps/mail/search_contacts.inc b/program/steps/mail/search_contacts.inc
new file mode 100644
index 000000000..2e6bb12f8
--- /dev/null
+++ b/program/steps/mail/search_contacts.inc
@@ -0,0 +1,112 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/search_contacts.inc |
+ | |
+ | This file is part of the Roundcube Webmail client |
+ | Copyright (C) 2013, The Roundcube Dev Team |
+ | |
+ | Licensed under the GNU General Public License version 3 or |
+ | any later version with exceptions for skins & plugins. |
+ | See the README file for a full license statement. |
+ | |
+ | PURPOSE: |
+ | Search contacts from the adress book widget |
+ | |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ +-----------------------------------------------------------------------+
+*/
+
+$search = get_input_value('_q', RCUBE_INPUT_GPC, true);
+$sources = $RCMAIL->get_address_sources();
+$search_mode = (int) $RCMAIL->config->get('addressbook_search_mode');
+$sort_col = $RCMAIL->config->get('addressbook_sort_col', 'name');
+$afields = $RCMAIL->config->get('contactlist_fields');
+
+$page = 1;
+$page_size = $RCMAIL->config->get('addressbook_pagesize', $RCMAIL->config->get('pagesize', 50));
+
+$records = $search_set = array();
+foreach ($sources as $s) {
+ $source = $RCMAIL->get_address_book($s['id']);
+ $source->set_page(1);
+ $source->set_pagesize(9999);
+
+ // get contacts count
+ $result = $source->search($afields, $search, $search_mode, true, true, 'email');
+
+ if (!$result->count) {
+ continue;
+ }
+
+ // get records
+ $result = $source->list_records($afields);
+
+ while ($row = $result->next()) {
+ $row['sourceid'] = $s['id'];
+ $key = rcube_addressbook::compose_contact_key($row, $sort_col);
+ $records[$key] = $row;
+ }
+
+ $search_set[$s['id']] = $source->get_search_set();
+ unset($result);
+}
+
+// sort the records
+ksort($records, SORT_LOCALE_STRING);
+
+// create resultset object
+$count = count($records);
+$result = new rcube_result_set($count);
+
+// select the requested page
+if ($page_size < $count) {
+ $records = array_slice($records, $result->first, $page_size);
+}
+
+$result->records = array_values($records);
+
+if (!empty($result) && $result->count > 0) {
+ // create javascript list
+ while ($row = $result->next()) {
+ $name = rcube_addressbook::compose_list_name($row);
+
+ // add record for every email address of the contact
+ // (same as in list_contacts.inc)
+ $emails = $source->get_col_values('email', $row, true);
+ foreach ($emails as $i => $email) {
+ $row_id = $row['ID'].$i;
+ $jsresult[$row_id] = format_email_recipient($email, $name);
+ $OUTPUT->command('add_contact_row', $row_id, array(
+ 'contact' => html::span(array('title' => $email), Q($name ? $name : $email) .
+ ($name && count($emails) > 1 ? '&nbsp;' . html::span('email', Q($email)) : '')
+ )), 'person');
+ }
+ }
+
+ // search request ID
+ $search_request = md5('composeaddr' . $search);
+
+ // save search settings in session
+ $_SESSION['search'][$search_request] = $search_set;
+ $_SESSION['search_params'] = array('id' => $search_request, 'data' => array($afields, $search));
+
+ $OUTPUT->show_message('contactsearchsuccessful', 'confirmation', array('nr' => $result->count));
+
+ $OUTPUT->command('set_env', 'search_request', $search_request);
+ $OUTPUT->command('set_env', 'source', '');
+ $OUTPUT->command('unselect_directory');
+}
+else {
+ $OUTPUT->show_message('nocontactsfound', 'notice');
+}
+
+// update env
+$OUTPUT->set_env('contactdata', $jsresult);
+$OUTPUT->set_env('pagecount', ceil($result->count / $page_size));
+$OUTPUT->command('set_page_buttons');
+
+// send response
+$OUTPUT->send();
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index c26d774a2..2f96e930f 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -219,11 +219,11 @@ function rcmail_email_input_format($mailto, $count=false, $check=true)
// address in brackets without name (do nothing)
if (preg_match('/^<'.$email_regexp.'>$/', $item)) {
$item = rcube_idn_to_ascii(trim($item, '<>'));
- $result[] = '<' . $item . '>';
+ $result[] = $item;
// address without brackets and without name (add brackets)
} else if (preg_match('/^'.$email_regexp.'$/', $item)) {
$item = rcube_idn_to_ascii($item);
- $result[] = '<' . $item . '>';
+ $result[] = $item;
// address with name (handle name)
} else if (preg_match('/<*'.$email_regexp.'>*$/', $item, $matches)) {
$address = $matches[0];
@@ -255,6 +255,33 @@ function rcmail_email_input_format($mailto, $count=false, $check=true)
}
+function rcmail_generic_message_footer($isHtml)
+{
+ global $CONFIG;
+
+ if ($isHtml && !empty($CONFIG['generic_message_footer_html'])) {
+ $file = $CONFIG['generic_message_footer_html'];
+ $html_footer = true;
+ }
+ else {
+ $file = $CONFIG['generic_message_footer'];
+ $html_footer = false;
+ }
+
+ if ($file && realpath($file)) {
+ // sanity check
+ if (!preg_match('/\.(php|ini|conf)$/', $file) && strpos($file, '/etc/') === false) {
+ $footer = file_get_contents($file);
+ if ($isHtml && !$html_footer)
+ $footer = '<pre>' . $footer . '</pre>';
+ return $footer;
+ }
+ }
+
+ return false;
+}
+
+
/****** compose message ********/
if (strlen($_POST['_draft_saveid']) > 3)
@@ -466,7 +493,7 @@ if (!$savedraft) {
$message_body = preg_replace('/\s*id="_rc_sig"/', '', $message_body);
// add inline css for blockquotes
- $bstyle = 'padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px; width:100%';
+ $bstyle = 'padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px';
$message_body = preg_replace('/<blockquote>/',
'<blockquote type="cite" style="'.$bstyle.'">', $message_body);
}
@@ -490,19 +517,10 @@ if (!$savedraft) {
}
// generic footer for all messages
- if ($isHtml && !empty($CONFIG['generic_message_footer_html'])) {
- $footer = file_get_contents(realpath($CONFIG['generic_message_footer_html']));
- $footer = rcube_charset_convert($footer, RCMAIL_CHARSET, $message_charset);
- }
- else if (!empty($CONFIG['generic_message_footer'])) {
- $footer = file_get_contents(realpath($CONFIG['generic_message_footer']));
+ if ($footer = rcmail_generic_message_footer($isHtml)) {
$footer = rcube_charset_convert($footer, RCMAIL_CHARSET, $message_charset);
- if ($isHtml)
- $footer = '<pre>'.$footer.'</pre>';
- }
-
- if ($footer)
$message_body .= "\r\n" . $footer;
+ }
}
if ($isHtml) {
@@ -559,7 +577,7 @@ if ($isHtml) {
$plugin['body'] = rcmail_replace_emoticons($plugin['body']);
// add a plain text version of the e-mail as an alternative part.
- $h2t = new html2text($plugin['body'], false, true, 0, $message_charset);
+ $h2t = new rcube_html2text($plugin['body'], false, true, 0, $message_charset);
$plainTextPart = rc_wordwrap($h2t->get_text(), $LINE_LENGTH, "\r\n", false, $message_charset);
$plainTextPart = wordwrap($plainTextPart, 998, "\r\n", true);
@@ -617,13 +635,12 @@ if (is_array($COMPOSE['attachments']))
$ctype = str_replace('image/pjpeg', 'image/jpeg', $attachment['mimetype']); // #1484914
$file = $attachment['data'] ? $attachment['data'] : $attachment['path'];
- // .eml attachments send inline
$MAIL_MIME->addAttachment($file,
$ctype,
$attachment['name'],
($attachment['data'] ? false : true),
($ctype == 'message/rfc822' ? '8bit' : 'base64'),
- ($ctype == 'message/rfc822' ? 'inline' : 'attachment'),
+ 'attachment',
'', '', '',
$CONFIG['mime_param_folding'] ? 'quoted-printable' : NULL,
$CONFIG['mime_param_folding'] == 2 ? 'quoted-printable' : NULL,
@@ -821,6 +838,6 @@ else {
if ($store_folder && !$saved)
$OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent'));
else
- $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'));
+ $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'), $store_target);
$OUTPUT->send('iframe');
}
diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc
index 82594f3e4..552c180f5 100644
--- a/program/steps/mail/show.inc
+++ b/program/steps/mail/show.inc
@@ -19,7 +19,7 @@
+-----------------------------------------------------------------------+
*/
-$PRINT_MODE = $RCMAIL->action=='print' ? TRUE : FALSE;
+$PRINT_MODE = $RCMAIL->action == 'print' ? TRUE : FALSE;
// Read browser capabilities and store them in session
if ($caps = get_input_value('_caps', RCUBE_INPUT_GET)) {
@@ -31,8 +31,21 @@ if ($caps = get_input_value('_caps', RCUBE_INPUT_GET)) {
$_SESSION['browser_caps'] = $browser_caps;
}
+$uid = get_input_value('_uid', RCUBE_INPUT_GET);
+$mbox_name = $RCMAIL->storage->get_folder();
+
// similar code as in program/steps/mail/get.inc
-if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) {
+if ($uid) {
+ // set message format (need to be done before rcube_message construction)
+ if (!empty($_GET['_format'])) {
+ $prefer_html = $_GET['_format'] == 'html';
+ $RCMAIL->config->set('prefer_html', $prefer_html);
+ $_SESSION['msg_formats'][$mbox_name.':'.$uid] = $prefer_html;
+ }
+ else if (isset($_SESSION['msg_formats'][$mbox_name.':'.$uid])) {
+ $RCMAIL->config->set('prefer_html', $_SESSION['msg_formats'][$mbox_name.':'.$uid]);
+ }
+
$MESSAGE = new rcube_message($uid);
// if message not found (wrong UID)...
@@ -40,7 +53,6 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) {
rcmail_message_error($uid);
}
- $mbox_name = $RCMAIL->storage->get_folder();
// show images?
rcmail_check_safe($MESSAGE);
@@ -79,7 +91,7 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) {
}
}
- $OUTPUT->set_env('mimetypes', $mimetypes);
+ $OUTPUT->set_env('mimetypes', array_values($mimetypes));
if ($CONFIG['drafts_mbox'])
$OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
@@ -106,6 +118,11 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) {
$OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash',
'movingmessage', 'deletingmessage', 'markingmessage');
+ $prefer_html = $RCMAIL->config->get('prefer_html');
+ if ($MESSAGE->has_html_part()) {
+ $OUTPUT->set_env('optional_format', $prefer_html ? 'text' : 'html');
+ }
+
// check for unset disposition notification
if ($MESSAGE->headers->mdn_to
&& empty($MESSAGE->headers->flags['MDNSENT'])
@@ -147,13 +164,11 @@ function rcmail_message_attachments($attrib)
global $PRINT_MODE, $MESSAGE, $RCMAIL;
$out = $ol = '';
+ $attachments = array();
if (sizeof($MESSAGE->attachments)) {
foreach ($MESSAGE->attachments as $attach_prop) {
- $filename = $attach_prop->filename;
- if (empty($filename) && $attach_prop->mimetype == 'text/html') {
- $filename = rcube_label('htmlmessage');
- }
+ $filename = rcmail_attachment_name($attach_prop, true);
if ($PRINT_MODE) {
$size = $RCMAIL->message_part_size($attach_prop);
@@ -161,28 +176,30 @@ function rcmail_message_attachments($attrib)
}
else {
if (mb_strlen($filename) > 50) {
+ $title = $filename;
$filename = abbreviate_string($filename, 50);
- $title = $filename;
}
else {
$title = '';
}
- $ol .= html::tag('li', rcmail_filetype2classname($attach_prop->mimetype, $filename),
- html::a(array(
+ $mimetype = rcmail_fix_mimetype($attach_prop->mimetype);
+ $class = rcmail_filetype2classname($mimetype, $filename);
+ $id = 'attach' . $attach_prop->mime_id;
+ $link = html::a(array(
'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false),
- 'onclick' => sprintf(
- 'return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)',
- JS_OBJECT_NAME,
- $attach_prop->mime_id,
- rcmail_fix_mimetype($attach_prop->mimetype)),
- 'title' => Q($title),
- ),
- Q($filename)));
+ 'onclick' => sprintf('return %s.command(\'load-attachment\',\'%s\',this)',
+ JS_OBJECT_NAME, $attach_prop->mime_id),
+ 'title' => Q($title),
+ ), Q($filename));
+ $ol .= html::tag('li', array('class' => $class, 'id' => $id), $link);
+
+ $attachments[$attach_prop->mime_id] = $mimetype;
}
}
$out = html::tag('ul', $attrib, $ol, html::$common_attrib);
+ $RCMAIL->output->set_env('attachments', $attachments);
}
return $out;
@@ -288,9 +305,9 @@ $OUTPUT->add_handlers(array(
));
-if ($RCMAIL->action=='print' && $OUTPUT->template_exists('messageprint'))
+if ($RCMAIL->action == 'print' && $OUTPUT->template_exists('messageprint'))
$OUTPUT->send('messageprint', false);
-else if ($RCMAIL->action=='preview' && $OUTPUT->template_exists('messagepreview'))
+else if ($RCMAIL->action == 'preview' && $OUTPUT->template_exists('messagepreview'))
$OUTPUT->send('messagepreview', false);
else
$OUTPUT->send('message', false);
diff --git a/program/steps/settings/edit_identity.inc b/program/steps/settings/edit_identity.inc
index 39076f408..d70a7aef7 100644
--- a/program/steps/settings/edit_identity.inc
+++ b/program/steps/settings/edit_identity.inc
@@ -99,6 +99,13 @@ function rcube_identity_form($attrib)
$form['addressing']['content']['email']['class'] = 'disabled';
}
+ if (IDENTITIES_LEVEL == 4) {
+ foreach($form['addressing']['content'] as $formfield => $value){
+ $form['addressing']['content'][$formfield]['disabled'] = true;
+ $form['addressing']['content'][$formfield]['class'] = 'disabled';
+ }
+ }
+
$IDENTITY_RECORD['email'] = rcube_idn_to_utf8($IDENTITY_RECORD['email']);
// Allow plugins to modify identity form content
diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc
index 3bcca21bf..319c58db9 100644
--- a/program/steps/settings/func.inc
+++ b/program/steps/settings/func.inc
@@ -483,8 +483,8 @@ function rcmail_user_prefs($current=null)
$blocks = array(
'main' => array('name' => Q(rcube_label('mainoptions'))),
- 'spellcheck' => array('name' => Q(rcube_label('spellcheckoptions'))),
'sig' => array('name' => Q(rcube_label('signatureoptions'))),
+ 'spellcheck' => array('name' => Q(rcube_label('spellcheckoptions'))),
);
// show checkbox to compose messages in a new window
@@ -581,8 +581,7 @@ function rcmail_user_prefs($current=null)
if (!isset($no_override['reply_mode'])) {
$field_id = 'rcmfd_reply_mode';
- $select_replymode = new html_select(array('name' => '_reply_mode', 'id' => $field_id,
- 'onchange' => "\$('#rcmfd_sig_above').attr('disabled',this.selectedIndex<2)"));
+ $select_replymode = new html_select(array('name' => '_reply_mode', 'id' => $field_id));
$select_replymode->add(rcube_label('replyempty'), -1);
$select_replymode->add(rcube_label('replybottomposting'), 0);
$select_replymode->add(rcube_label('replytopposting'), 1);
@@ -631,18 +630,6 @@ function rcmail_user_prefs($current=null)
);
}
- if (!isset($no_override['sig_above'])) {
- $field_id = 'rcmfd_sig_above';
- $select_sigabove = new html_select(array('name' => '_sig_above', 'id' => $field_id, 'disabled' => $config['reply_mode'] < 1));
- $select_sigabove->add(rcube_label('belowquote'), 0);
- $select_sigabove->add(rcube_label('abovequote'), 1);
-
- $blocks['sig']['options']['sig_above'] = array(
- 'title' => html::label($field_id, Q(rcube_label('replysignaturepos'))),
- 'content' => $select_sigabove->show($config['sig_above']?1:0),
- );
- }
-
if (!isset($no_override['strip_existing_sig'])) {
$field_id = 'rcmfd_strip_existing_sig';
$input_stripexistingsig = new html_checkbox(array('name' => '_strip_existing_sig', 'id' => $field_id, 'value' => 1));
diff --git a/program/steps/settings/save_identity.inc b/program/steps/settings/save_identity.inc
index d579ee61f..34d8be268 100644
--- a/program/steps/settings/save_identity.inc
+++ b/program/steps/settings/save_identity.inc
@@ -26,17 +26,14 @@ $a_boolean_cols = array('standard', 'html_signature');
$updated = $default_id = false;
// check input
-if (empty($_POST['_name']) || (empty($_POST['_email']) && IDENTITIES_LEVEL != 1 && IDENTITIES_LEVEL != 3))
-{
+if (IDENTITIES_LEVEL != 4 && (empty($_POST['_name']) || (empty($_POST['_email']) && IDENTITIES_LEVEL != 1 && IDENTITIES_LEVEL != 3))) {
$OUTPUT->show_message('formincomplete', 'warning');
rcmail_overwrite_action('edit-identity');
return;
}
-
$save_data = array();
-foreach ($a_save_cols as $col)
-{
+foreach ($a_save_cols as $col) {
$fname = '_'.$col;
if (isset($_POST[$fname]))
$save_data[$col] = get_input_value($fname, RCUBE_INPUT_POST, true);
@@ -44,16 +41,24 @@ foreach ($a_save_cols as $col)
// set "off" values for checkboxes that were not checked, and therefore
// not included in the POST body.
-foreach ($a_boolean_cols as $col)
-{
+foreach ($a_boolean_cols as $col) {
$fname = '_' . $col;
if (!isset($_POST[$fname]))
$save_data[$col] = 0;
}
// unset email address if user has no rights to change it
-if (IDENTITIES_LEVEL == 1 || IDENTITIES_LEVEL == 3)
+if (IDENTITIES_LEVEL == 1 || IDENTITIES_LEVEL == 3) {
unset($save_data['email']);
+}
+// unset all fields except signature
+else if (IDENTITIES_LEVEL == 4) {
+ foreach ($save_data as $idx => $value) {
+ if ($idx != 'signature' && $idx != 'html_signature') {
+ unset($save_data[$idx]);
+ }
+ }
+}
// Validate e-mail addresses
$email_checks = array(rcube_idn_to_ascii($save_data['email']));
@@ -72,9 +77,16 @@ foreach ($email_checks as $email) {
}
// update an existing contact
-if ($_POST['_iid'])
-{
+if ($_POST['_iid']) {
$iid = get_input_value('_iid', RCUBE_INPUT_POST);
+
+ if (in_array(IDENTITIES_LEVEL, array(1,3,4))) {
+ // merge with old identity data, fixes #1488834
+ $identity = $RCMAIL->user->get_identity($iid);
+ $save_data = array_merge($identity, $save_data);
+ unset($save_data['changed'], $save_data['del'], $save_data['user_id'], $save_data['identity_id']);
+ }
+
$plugin = $RCMAIL->plugins->exec_hook('identity_update', array('id' => $iid, 'record' => $save_data));
$save_data = $plugin['record'];
@@ -88,8 +100,8 @@ if ($_POST['_iid'])
if ($updated) {
$OUTPUT->show_message('successfullysaved', 'confirmation');
- if (!empty($_POST['_standard']))
- $default_id = get_input_value('_iid', RCUBE_INPUT_POST);
+ if (!empty($save_data['standard']))
+ $default_id = $iid;
if ($_POST['_framed']) {
// update the changed col in list
@@ -105,8 +117,7 @@ if ($_POST['_iid'])
}
// insert a new identity record
-else if (IDENTITIES_LEVEL < 2)
-{
+else if (IDENTITIES_LEVEL < 2) {
if (IDENTITIES_LEVEL == 1) {
$save_data['email'] = $RCMAIL->get_user_email();
}
@@ -127,7 +138,7 @@ else if (IDENTITIES_LEVEL < 2)
$_GET['_iid'] = $insert_id;
- if (!empty($_POST['_standard']))
+ if (!empty($save_data['standard']))
$default_id = $insert_id;
if ($_POST['_framed']) {
diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc
index 5daab0d24..140f173c6 100644
--- a/program/steps/settings/save_prefs.inc
+++ b/program/steps/settings/save_prefs.inc
@@ -86,7 +86,6 @@ switch ($CURR_SECTION)
'show_sig' => isset($_POST['_show_sig']) ? intval($_POST['_show_sig']) : 1,
'reply_mode' => isset($_POST['_reply_mode']) ? intval($_POST['_reply_mode']) : 0,
'strip_existing_sig' => isset($_POST['_strip_existing_sig']),
- 'sig_above' => !empty($_POST['_sig_above']) && $_POST['_reply_mode'] > 0,
'default_font' => get_input_value('_default_font', RCUBE_INPUT_POST),
'forward_attachment' => !empty($_POST['_forward_attachment']),
);
diff --git a/program/steps/utils/html2text.inc b/program/steps/utils/html2text.inc
index e17665fec..c6481b197 100644
--- a/program/steps/utils/html2text.inc
+++ b/program/steps/utils/html2text.inc
@@ -24,10 +24,8 @@ $html = $HTTP_RAW_POST_DATA;
// Replace emoticon images with its text representation
$html = rcmail_replace_emoticons($html);
-$converter = new html2text($html, false, true, 0);
+$converter = new rcube_html2text($html, false, true, 0);
header('Content-Type: text/plain; charset=UTF-8');
print rtrim($converter->get_text());
exit;
-
-
diff --git a/program/steps/utils/save_pref.inc b/program/steps/utils/save_pref.inc
index b550ad7ef..7def8733d 100644
--- a/program/steps/utils/save_pref.inc
+++ b/program/steps/utils/save_pref.inc
@@ -21,6 +21,22 @@
$name = get_input_value('_name', RCUBE_INPUT_POST);
$value = get_input_value('_value', RCUBE_INPUT_POST);
+$whitelist = array(
+ 'preview_pane',
+ 'list_cols',
+ 'collapsed_folders',
+ 'collapsed_abooks',
+);
+
+if (!in_array($name, array_merge($whitelist, $RCMAIL->plugins->allowed_prefs))) {
+ raise_error(array('code' => 500, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => sprintf("Hack attempt detected (user: %s)", $RCMAIL->get_user_name())),
+ true, false);
+
+ $OUTPUT->reset();
+ $OUTPUT->send();
+}
// save preference value
$RCMAIL->user->save_prefs(array($name => $value));