Aleksander Machniak
2016-02-24 b0a8e3693090bd96fc31c4c5f42b77f8fddf9b90
commit | author | age
4e17e6 1 <?php
T 2
a95874 3 /**
4e17e6 4  +-----------------------------------------------------------------------+
T 5  | program/steps/mail/compose.inc                                        |
6  |                                                                       |
e019f2 7  | This file is part of the Roundcube Webmail client                     |
5e3034 8  | Copyright (C) 2005-2013, The Roundcube Dev Team                       |
7fe381 9  |                                                                       |
T 10  | Licensed under the GNU General Public License version 3 or            |
11  | any later version with exceptions for skins & plugins.                |
12  | See the README file for a full license statement.                     |
4e17e6 13  |                                                                       |
T 14  | PURPOSE:                                                              |
15  |   Compose a new mail message with all headers and attachments         |
16  |                                                                       |
17  +-----------------------------------------------------------------------+
18  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
19  +-----------------------------------------------------------------------+
20 */
21
8d4bcd 22 // define constants for message compose mode
a0e3dc 23 define('RCUBE_COMPOSE_REPLY', 'reply');
AM 24 define('RCUBE_COMPOSE_FORWARD', 'forward');
25 define('RCUBE_COMPOSE_DRAFT', 'draft');
26 define('RCUBE_COMPOSE_EDIT', 'edit');
8d4bcd 27
72ff6a 28 $MESSAGE_FORM = null;
6b2b2e 29 $COMPOSE_ID   = rcube_utils::get_input_value('_id', rcube_utils::INPUT_GET);
72ff6a 30 $COMPOSE      = null;
f0f98f 31
72ff6a 32 if ($COMPOSE_ID && $_SESSION['compose_data_'.$COMPOSE_ID])
A 33   $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
4591de 34
81f5dd 35 // give replicated session storage some time to synchronize
T 36 $retries = 0;
72ff6a 37 while ($COMPOSE_ID && !is_array($COMPOSE) && $RCMAIL->db->is_replicated() && $retries++ < 5) {
5e3034 38     usleep(500000);
AM 39     $RCMAIL->session->reload();
40     if ($_SESSION['compose_data_'.$COMPOSE_ID]) {
41         $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
42     }
81f5dd 43 }
T 44
86df15 45 // Nothing below is called during message composition, only at "new/forward/reply/draft" initialization or
T 46 // if a compose-ID is given (i.e. when the compose step is opened in a new window/tab).
5e3034 47 if (!is_array($COMPOSE)) {
AM 48     // Infinite redirect prevention in case of broken session (#1487028)
49     if ($COMPOSE_ID) {
4b72a1 50         // if we know the message with specified ID was already sent
AM 51         // we can ignore the error and compose a new message (#1490009)
52         if ($COMPOSE_ID != $_SESSION['last_compose_session']) {
53             rcube::raise_error(array('code' => 450), false, true);
54         }
76791c 55     }
5f314d 56
5e3034 57     $COMPOSE_ID = uniqid(mt_rand());
5d42a9 58     $params     = rcube_utils::request2param(rcube_utils::INPUT_GET, 'task|action', true);
AM 59
5e3034 60     $_SESSION['compose_data_'.$COMPOSE_ID] = array(
AM 61         'id'      => $COMPOSE_ID,
5d42a9 62         'param'   => $params,
AM 63         'mailbox' => $params['mbox'] ?: $RCMAIL->storage->get_folder(),
5e3034 64     );
AM 65     $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
66     rcmail_process_compose_params($COMPOSE);
67
68     // check if folder for saving sent messages exists and is subscribed (#1486802)
69     if ($sent_folder = $COMPOSE['param']['sent_mbox']) {
70         rcmail_check_sent_folder($sent_folder, true);
71     }
72
73     // redirect to a unique URL with all parameters stored in session
74     $OUTPUT->redirect(array(
75         '_action' => 'compose',
76         '_id'     => $COMPOSE['id'],
77         '_search' => $_REQUEST['_search'],
78     ));
4315b0 79 }
76791c 80
4e17e6 81
10a699 82 // add some labels to client
3f9712 83 $OUTPUT->add_label('nosubject', 'nosenderwarning', 'norecipientwarning', 'nosubjectwarning', 'cancel',
V 84     'nobodywarning', 'notsentwarning', 'notuploadedwarning', 'savingmessage', 'sendingmessage', 
1abb97 85     'messagesaved', 'converting', 'editorwarning', 'searching', 'uploading', 'uploadingmany',
6eb08d 86     'fileuploaderror', 'sendmessage', 'newresponse', 'responsename', 'responsetext', 'save',
08da30 87     'savingresponse', 'restoresavedcomposedata', 'restoremessage', 'delete', 'restore', 'ignore',
3167e5 88     'selectimportfile', 'messageissent', 'loadingdata', 'nopubkeyfor', 'nopubkeyforsender',
2965a9 89     'encryptnoattachments','encryptedsendialog','searchpubkeyservers', 'importpubkeys',
TB 90     'encryptpubkeysfound',  'search', 'close', 'import', 'keyid', 'keylength', 'keyexpired',
d56091 91     'keyrevoked', 'keyimportsuccess', 'keyservererror', 'attaching');
10a699 92
6b2b2e 93 $OUTPUT->set_pagetitle($RCMAIL->gettext('compose'));
4591de 94
5e3034 95 $OUTPUT->set_env('compose_id', $COMPOSE['id']);
AM 96 $OUTPUT->set_env('session_id', session_id());
c321a9 97 $OUTPUT->set_env('mailbox', $RCMAIL->storage->get_folder());
651c7b 98 $OUTPUT->set_env('top_posting', intval($RCMAIL->config->get('reply_mode')) > 0);
09225a 99 $OUTPUT->set_env('sig_below', $RCMAIL->config->get('sig_below'));
62c861 100 $OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ',')));
44b47d 101 $OUTPUT->set_env('save_localstorage', (bool)$RCMAIL->config->get('compose_save_localstorage'));
c5c8e7 102 $OUTPUT->set_env('is_sent', false);
10a699 103
f5d2ee 104 $drafts_mbox     = $RCMAIL->config->get('drafts_mbox');
AM 105 $config_show_sig = $RCMAIL->config->get('show_sig', 1);
106
5e3034 107 // add config parameters to client script
f5d2ee 108 if (strlen($drafts_mbox)) {
AM 109     $OUTPUT->set_env('drafts_mailbox', $drafts_mbox);
110     $OUTPUT->set_env('draft_autosave', $RCMAIL->config->get('draft_autosave'));
5e3034 111 }
AM 112
7e263e 113 // default font for HTML editor
6b2b2e 114 $font = rcmail::font_defs($RCMAIL->config->get('default_font'));
7e263e 115 if ($font && !is_array($font)) {
5e3034 116     $OUTPUT->set_env('default_font', $font);
7e263e 117 }
A 118
965dea 119 // default font size for HTML editor
f7b2bf 120 if ($font_size = $RCMAIL->config->get('default_font_size')) {
5e3034 121     $OUTPUT->set_env('default_font_size', $font_size);
f7b2bf 122 }
965dea 123
8d4bcd 124 // get reference message and set compose mode
72ff6a 125 if ($msg_uid = $COMPOSE['param']['draft_uid']) {
5e3034 126     $compose_mode = RCUBE_COMPOSE_DRAFT;
AM 127     $OUTPUT->set_env('draft_id', $msg_uid);
f5d2ee 128     $RCMAIL->storage->set_folder($drafts_mbox);
4591de 129 }
d9f109 130 else if ($msg_uid = $COMPOSE['param']['reply_uid']) {
5e3034 131     $compose_mode = RCUBE_COMPOSE_REPLY;
d9f109 132 }
AM 133 else if ($msg_uid = $COMPOSE['param']['forward_uid']) {
5e3034 134     $compose_mode = RCUBE_COMPOSE_FORWARD;
AM 135     $COMPOSE['forward_uid']   = $msg_uid;
136     $COMPOSE['as_attachment'] = !empty($COMPOSE['param']['attachment']);
d9f109 137 }
AM 138 else if ($msg_uid = $COMPOSE['param']['uid']) {
5e3034 139     $compose_mode = RCUBE_COMPOSE_EDIT;
d9f109 140 }
66a549 141
fa424e 142 if ($compose_mode) {
AM 143     $COMPOSE['mode'] = $compose_mode;
144     $OUTPUT->set_env('compose_mode', $compose_mode);
145 }
50f56d 146
a9bb50 147 if ($compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT) {
5e3034 148     // don't add signature in draft/edit mode, we'll also not remove the old-one
AM 149     // but only on page display, later we should be able to change identity/sig (#1489229)
150     if ($config_show_sig == 1 || $config_show_sig == 2) {
151         $OUTPUT->set_env('show_sig_later', true);
152     }
a9bb50 153 }
AM 154 else if ($config_show_sig == 1)
5e3034 155     $OUTPUT->set_env('show_sig', true);
a9bb50 156 else if ($config_show_sig == 2 && empty($compose_mode))
5e3034 157     $OUTPUT->set_env('show_sig', true);
0207c4 158 else if ($config_show_sig == 3 && ($compose_mode == RCUBE_COMPOSE_REPLY || $compose_mode == RCUBE_COMPOSE_FORWARD))
5e3034 159     $OUTPUT->set_env('show_sig', true);
8d4bcd 160
b62049 161 // set line length for body wrapping
6b6f2e 162 $LINE_LENGTH = $RCMAIL->config->get('line_length', 72);
b62049 163
5e3034 164 if (!empty($msg_uid) && empty($COMPOSE['as_attachment'])) {
AM 165     $mbox_name = $RCMAIL->storage->get_folder();
a02c77 166
5e3034 167     // set format before rcube_message construction
AM 168     // use the same format as for the message view
169     if (isset($_SESSION['msg_formats'][$mbox_name.':'.$msg_uid])) {
170         $RCMAIL->config->set('prefer_html', $_SESSION['msg_formats'][$mbox_name.':'.$msg_uid]);
eeb85f 171     }
5e3034 172     else {
f5d2ee 173         $prefer_html = $RCMAIL->config->get('prefer_html') || $RCMAIL->config->get('htmleditor')
AM 174             || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT;
175
5e3034 176         $RCMAIL->config->set('prefer_html', $prefer_html);
AM 177     }
bc404f 178
5e3034 179     $MESSAGE = new rcube_message($msg_uid);
bc404f 180
5e3034 181     // make sure message is marked as read
AM 182     if ($MESSAGE->headers && empty($MESSAGE->headers->flags['SEEN'])) {
183         $RCMAIL->storage->set_flag($msg_uid, 'SEEN');
184     }
185
186     if (!empty($MESSAGE->headers->charset)) {
187         $RCMAIL->storage->set_charset($MESSAGE->headers->charset);
188     }
189
190     if (!$MESSAGE->headers) {
191         // error
192     }
73076d 193     else if ($compose_mode == RCUBE_COMPOSE_FORWARD || $compose_mode == RCUBE_COMPOSE_REPLY) {
AM 194         if ($compose_mode == RCUBE_COMPOSE_REPLY) {
d0cb32 195             $COMPOSE['reply_uid'] = $msg_uid;
73076d 196
AM 197             if (!empty($COMPOSE['param']['all'])) {
198                 $MESSAGE->reply_all = $COMPOSE['param']['all'];
199             }
200         }
201         else {
202             $COMPOSE['forward_uid'] = $msg_uid;
203         }
204
5e3034 205         $COMPOSE['reply_msgid'] = $MESSAGE->headers->messageID;
AM 206         $COMPOSE['references']  = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID);
eeb85f 207
10936f 208         // Save the sent message in the same folder of the message being replied to
5e3034 209         if ($RCMAIL->config->get('reply_same_folder') && ($sent_folder = $COMPOSE['mailbox'])
AM 210             && rcmail_check_sent_folder($sent_folder, false)
10936f 211         ) {
5e3034 212             $COMPOSE['param']['sent_mbox'] = $sent_folder;
10936f 213         }
95fd38 214     }
5e3034 215     else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) {
AM 216         if ($compose_mode == RCUBE_COMPOSE_DRAFT) {
217             if ($draft_info = $MESSAGE->headers->get('x-draft-info')) {
218                 // get reply_uid/forward_uid to flag the original message when sending
219                 $info = rcmail_draftinfo_decode($draft_info);
eeb85f 220
5e3034 221                 if ($info['type'] == 'reply')
AM 222                     $COMPOSE['reply_uid'] = $info['uid'];
223                 else if ($info['type'] == 'forward')
224                     $COMPOSE['forward_uid'] = $info['uid'];
bc404f 225
5e3034 226                 $COMPOSE['mailbox'] = $info['folder'];
AM 227
228                 // Save the sent message in the same folder of the message being replied to
229                 if ($RCMAIL->config->get('reply_same_folder') && ($sent_folder = $info['folder'])
230                     && rcmail_check_sent_folder($sent_folder, false)
231                 ) {
232                     $COMPOSE['param']['sent_mbox'] = $sent_folder;
233                 }
234             }
235
236             $COMPOSE['param']['message-id'] = $MESSAGE->headers->get('message-id');
237
238             // use message UID as draft_id
239             $OUTPUT->set_env('draft_id', $msg_uid);
240         }
241
242         if ($in_reply_to = $MESSAGE->headers->get('in-reply-to')) {
243             $COMPOSE['reply_msgid'] = '<' . $in_reply_to . '>';
244         }
245
246         $COMPOSE['references'] = $MESSAGE->headers->references;
247     }
252d27 248 }
A 249 else {
5e3034 250     $MESSAGE = new stdClass();
eafd5b 251
5e3034 252     // apply mailto: URL parameters
AM 253     if (!empty($COMPOSE['param']['in-reply-to'])) {
254         $COMPOSE['reply_msgid'] = '<' . $COMPOSE['param']['in-reply-to'] . '>';
255     }
256
257     if (!empty($COMPOSE['param']['references'])) {
258         $COMPOSE['references'] = $COMPOSE['param']['references'];
259     }
4315b0 260 }
4e17e6 261
73076d 262 if (!empty($COMPOSE['reply_msgid'])) {
AM 263     $OUTPUT->set_env('reply_msgid', $COMPOSE['reply_msgid']);
264 }
90dc9b 265
da142b 266 $MESSAGE->compose = array();
A 267
268 // get user's identities
0247b8 269 $MESSAGE->identities = $RCMAIL->user->list_identities(null, true);
da142b 270
A 271 // Set From field value
272 if (!empty($_POST['_from'])) {
5e3034 273     $MESSAGE->compose['from'] = rcube_utils::get_input_value('_from', rcube_utils::INPUT_POST);
da142b 274 }
72ff6a 275 else if (!empty($COMPOSE['param']['from'])) {
5e3034 276     $MESSAGE->compose['from'] = $COMPOSE['param']['from'];
da142b 277 }
A 278 else if (count($MESSAGE->identities)) {
5e3034 279     $ident = rcmail_identity_select($MESSAGE, $MESSAGE->identities, $compose_mode);
da142b 280
1414e3 281     $MESSAGE->compose['from']  = $ident['identity_id'];
AM 282     $MESSAGE->compose['ident'] = $ident;
da142b 283 }
A 284
087c7d 285 // process $MESSAGE body/attachments, set $MESSAGE_BODY/$HTML_MODE vars and some session data
A 286 $MESSAGE_BODY = rcmail_prepare_message_body();
4e17e6 287
087c7d 288
5e3034 289 // register UI objects
AM 290 $OUTPUT->add_handlers(array(
291     'composeheaders'        => 'rcmail_compose_headers',
292     'composesubject'        => 'rcmail_compose_subject',
293     'composebody'           => 'rcmail_compose_body',
294     'composeattachmentlist' => 'rcmail_compose_attachment_list',
295     'composeattachmentform' => 'rcmail_compose_attachment_form',
296     'composeattachment'     => 'rcmail_compose_attachment_field',
297     'filedroparea'          => 'compose_file_drop_area',
298     'priorityselector'      => 'rcmail_priority_selector',
299     'editorselector'        => 'rcmail_editor_selector',
f65021 300     'receiptcheckbox'       => 'rcmail_mdn_checkbox', // deprecated
AM 301     'mdncheckbox'           => 'rcmail_mdn_checkbox',
5e3034 302     'dsncheckbox'           => 'rcmail_dsn_checkbox',
AM 303     'storetarget'           => 'rcmail_store_target_selection',
304     'addressbooks'          => 'rcmail_addressbook_list',
305     'addresslist'           => 'rcmail_contacts_list',
306     'responseslist'         => 'rcmail_compose_responses_list',
307 ));
308
309 $OUTPUT->send('compose');
310
311
087c7d 312 /****** compose mode functions ********/
0247b8 313
eafd5b 314 // process compose request parameters
AM 315 function rcmail_process_compose_params(&$COMPOSE)
316 {
5e3034 317     if ($COMPOSE['param']['to']) {
AM 318         $mailto = explode('?', $COMPOSE['param']['to'], 2);
eafd5b 319
5e3034 320         // #1486037: remove "mailto:" prefix
AM 321         $COMPOSE['param']['to'] = preg_replace('/^mailto:/i', '', $mailto[0]);
1a7e66 322         // #1490346: decode the recipient address
8297ea 323         // #1490510: use raw encoding for correct "+" character handling as specified in RFC6068
AM 324         $COMPOSE['param']['to'] = rawurldecode($COMPOSE['param']['to']);
eafd5b 325
5e3034 326         // Supported case-insensitive tokens in mailto URL
AM 327         $url_tokens = array('to', 'cc', 'bcc', 'reply-to', 'in-reply-to', 'references', 'subject', 'body');
eafd5b 328
5e3034 329         if (!empty($mailto[1])) {
AM 330             parse_str($mailto[1], $query);
331             foreach ($query as $f => $val) {
332                 if (($key = array_search(strtolower($f), $url_tokens)) !== false) {
333                     $f = $url_tokens[$key];
334                 }
eafd5b 335
5e3034 336                 // merge mailto: addresses with addresses from 'to' parameter
AM 337                 if ($f == 'to' && !empty($COMPOSE['param']['to'])) {
338                     $to_addresses  = rcube_mime::decode_address_list($COMPOSE['param']['to'], null, true, null, true);
339                     $add_addresses = rcube_mime::decode_address_list($val, null, true);
340
341                     foreach ($add_addresses as $addr) {
342                         if (!in_array($addr['mailto'], $to_addresses)) {
343                             $to_addresses[]         = $addr['mailto'];
344                             $COMPOSE['param']['to'] = (!empty($to_addresses) ? ', ' : '') . $addr['string'];
345                         }
346                     }
347                 }
348                 else {
349                     $COMPOSE['param'][$f] = $val;
350                 }
eafd5b 351             }
AM 352         }
353     }
354
aafbe8 355     // resolve _forward_uid=* to an absolute list of messages from a search result
TB 356     if ($COMPOSE['param']['forward_uid'] == '*' && is_object($_SESSION['search'][1])) {
357         $COMPOSE['param']['forward_uid'] = $_SESSION['search'][1]->get();
358     }
359
5e3034 360     // clean HTML message body which can be submitted by URL
AM 361     if (!empty($COMPOSE['param']['body'])) {
b0a8e3 362         if ($COMPOSE['param']['html'] = strpos($COMPOSE['param']['body'], '<') !== false) {
AM 363             $wash_params = array('safe' => false, 'inline_html' => true);
364             $COMPOSE['param']['body'] = rcmail_wash_html($COMPOSE['param']['body'], $wash_params, array());
365             $COMPOSE['param']['body'] = preg_replace('/<!--[^>\n]+>/', '', $COMPOSE['param']['body']);
366             $COMPOSE['param']['body'] = preg_replace('/<\/?body>/', '', $COMPOSE['param']['body']);
367         }
5e3034 368     }
2af374 369
5e3034 370     $RCMAIL = rcmail::get_instance();
eafd5b 371
5e3034 372     // select folder where to save the sent message
AM 373     $COMPOSE['param']['sent_mbox'] = $RCMAIL->config->get('sent_mbox');
eafd5b 374
5e3034 375     // pipe compose parameters thru plugins
AM 376     $plugin = $RCMAIL->plugins->exec_hook('message_compose', $COMPOSE);
377     $COMPOSE['param'] = array_merge($COMPOSE['param'], $plugin['param']);
37b9e0 378
AM 379     // add attachments listed by message_compose hook
380     if (is_array($plugin['attachments'])) {
381         foreach ($plugin['attachments'] as $attach) {
382             // we have structured data
383             if (is_array($attach)) {
8ed382 384                 $attachment = $attach + array('group' => $COMPOSE_ID);
37b9e0 385             }
AM 386             // only a file path is given
387             else {
388                 $filename   = basename($attach);
389                 $attachment = array(
390                     'group'    => $COMPOSE_ID,
391                     'name'     => $filename,
392                     'mimetype' => rcube_mime::file_content_type($attach, $filename),
bb1ed2 393                     'size'     => filesize($attach),
37b9e0 394                     'path'     => $attach,
AM 395                 );
396             }
397
398             // save attachment if valid
399             if (($attachment['data'] && $attachment['name']) || ($attachment['path'] && file_exists($attachment['path']))) {
400                 $attachment = rcmail::get_instance()->plugins->exec_hook('attachment_save', $attachment);
401             }
402
403             if ($attachment['status'] && !$attachment['abort']) {
404                 unset($attachment['data'], $attachment['status'], $attachment['abort']);
405                 $COMPOSE['attachments'][$attachment['id']] = $attachment;
406             }
407         }
408     }
eafd5b 409 }
AM 410
4e17e6 411 function rcmail_compose_headers($attrib)
4315b0 412 {
5e3034 413     global $RCMAIL, $MESSAGE;
4e17e6 414
5e3034 415     list($form_start,) = get_form_tags($attrib);
8958d0 416
5e3034 417     $out  = '';
AM 418     $part = strtolower($attrib['part']);
8958d0 419
5e3034 420     switch ($part) {
4e17e6 421     case 'from':
5e3034 422         return $form_start . rcmail_compose_header_from($attrib);
4e17e6 423
T 424     case 'to':
425     case 'cc':
426     case 'bcc':
5e3034 427         $fname  = '_' . $part;
AM 428         $header = $param = $part;
8958d0 429
5e3034 430         $allow_attrib = array('id', 'class', 'style', 'cols', 'rows', 'tabindex');
AM 431         $field_type   = 'html_textarea';
432         break;
4e17e6 433
T 434     case 'replyto':
435     case 'reply-to':
5e3034 436         $fname  = '_replyto';
AM 437         $param  = 'replyto';
438         $header = 'reply-to';
e25a35 439
3ee5a7 440     case 'followupto':
A 441     case 'followup-to':
5e3034 442         if (!$fname) {
AM 443             $fname  = '_followupto';
444             $param  = 'followupto';
445             $header = 'mail-followup-to';
446         }
e25a35 447
5e3034 448         $allow_attrib = array('id', 'class', 'style', 'size', 'tabindex');
AM 449         $field_type   = 'html_inputfield';
450         break;
451     }
e99991 452
5e3034 453     if ($fname && $field_type) {
AM 454         // pass the following attributes to the form class
455         $field_attrib = array('name' => $fname, 'spellcheck' => 'false');
456         foreach ($attrib as $attr => $value) {
457             if (in_array($attr, $allow_attrib)) {
458                 $field_attrib[$attr] = $value;
459             }
460         }
4e17e6 461
5e3034 462         // create teaxtarea object
AM 463         $input = new $field_type($field_attrib);
1414e3 464         $out   = $input->show(rcmail_compose_header_value($param));
5e3034 465     }
0213f8 466
5e3034 467     if ($form_start) {
AM 468         $out = $form_start . $out;
469     }
1966c5 470
5e3034 471     // configure autocompletion
AM 472     $RCMAIL->autocomplete_init();
0213f8 473
5e3034 474     return $out;
4315b0 475 }
4e17e6 476
T 477
1cded8 478 function rcmail_compose_header_from($attrib)
4315b0 479 {
1414e3 480     global $MESSAGE, $OUTPUT, $RCMAIL, $COMPOSE;
da142b 481
5e3034 482     // pass the following attributes to the form class
AM 483     $field_attrib = array('name' => '_from');
484     foreach ($attrib as $attr => $value) {
485         if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex'))) {
486             $field_attrib[$attr] = $value;
a0109c 487         }
4315b0 488     }
e25a35 489
5e3034 490     if (count($MESSAGE->identities)) {
AM 491         $a_signatures = array();
492         $identities   = array();
09225a 493         $top_posting  = intval($RCMAIL->config->get('reply_mode')) > 0
AM 494             && !$RCMAIL->config->get('sig_below')
1414e3 495             && ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY || $COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD);
399a2d 496
AM 497         $separator     = $top_posting ? '---' : '-- ';
498         $add_separator = (bool) $RCMAIL->config->get('sig_separator');
4e17e6 499
5e3034 500         $field_attrib['onchange'] = rcmail_output::JS_OBJECT_NAME.".change_identity(this)";
AM 501         $select_from = new html_select($field_attrib);
4e17e6 502
5e3034 503         // create SELECT element
AM 504         foreach ($MESSAGE->identities as $sql_arr) {
505             $identity_id = $sql_arr['identity_id'];
506             $select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $identity_id);
507
508             // add signature to array
509             if (!empty($sql_arr['signature']) && empty($COMPOSE['param']['nosig'])) {
510                 $text = $html = $sql_arr['signature'];
511
512                 if ($sql_arr['html_signature']) {
c4daf3 513                     $text = $RCMAIL->html2text($html, array('links' => false));
399a2d 514                     $text = trim($text, "\r\n");
5e3034 515                 }
AM 516                 else {
aa38c5 517                     $t2h  = new rcube_text2html($text, false);
AM 518                     $html = $t2h->get_html();
5e3034 519                 }
AM 520
399a2d 521                 if ($add_separator && !preg_match('/^--[ -]\r?\n/m', $text)) {
AM 522                     $text = $separator . "\n" . ltrim($text, "\r\n");
5e3034 523                     $html = $separator . "<br>" . $html;
AM 524                 }
525
526                 $a_signatures[$identity_id]['text'] = $text;
527                 $a_signatures[$identity_id]['html'] = $html;
528             }
529
530             // add bcc and reply-to
531             if (!empty($sql_arr['reply-to'])) {
532                 $identities[$identity_id]['replyto'] = $sql_arr['reply-to'];
533             }
534             if (!empty($sql_arr['bcc'])) {
535                 $identities[$identity_id]['bcc'] = $sql_arr['bcc'];
536             }
40d152 537
TB 538             $identities[$identity_id]['email'] = $sql_arr['email'];
5e3034 539         }
AM 540
56a4e1 541         $out = $select_from->show($MESSAGE->compose['from']);
5e3034 542
AM 543         // add signatures to client
544         $OUTPUT->set_env('signatures', $a_signatures);
545         $OUTPUT->set_env('identities', $identities);
546     }
547     // no identities, display text input field
548     else {
549         $field_attrib['class'] = 'from_address';
550         $input_from = new html_inputfield($field_attrib);
56a4e1 551         $out = $input_from->show($MESSAGE->compose['from']);
5e3034 552     }
AM 553
554     return $out;
4315b0 555 }
4e17e6 556
1414e3 557 function rcmail_compose_header_value($header)
AM 558 {
559     global $COMPOSE, $MESSAGE;
560
561     $RCMAIL        = rcube::get_instance();
562     $fvalue        = '';
563     $decode_header = true;
564     $charset       = $MESSAGE->headers->charset;
565     $separator     = trim($RCMAIL->config->get('recipients_separator', ',')) . ' ';
566
567     // we have a set of recipients stored is session
568     if ($header == 'to' && ($mailto_id = $COMPOSE['param']['mailto'])
569         && $_SESSION['mailto'][$mailto_id]
570     ) {
571         $fvalue        = urldecode($_SESSION['mailto'][$mailto_id]);
572         $decode_header = false;
573         $charset       = $RCMAIL->output->charset;
574
575         // make session to not grow up too much
576         unset($_SESSION['mailto'][$mailto_id]);
577         $COMPOSE['param']['to'] = $fvalue;
578     }
579     else if (!empty($_POST['_' . $header])) {
580         $fvalue  = rcube_utils::get_input_value('_' . $header, rcube_utils::INPUT_POST, true);
581         $charset = $RCMAIL->output->charset;
582     }
583     else if (!empty($COMPOSE['param'][$header])) {
584         $fvalue  = $COMPOSE['param'][$header];
585         $charset = $RCMAIL->output->charset;
586     }
587     else if ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) {
588         // get recipent address(es) out of the message headers
589         if ($header == 'to') {
590             $mailfollowup = $MESSAGE->headers->others['mail-followup-to'];
591             $mailreplyto  = $MESSAGE->headers->others['mail-reply-to'];
592
593             // Reply to mailing list...
594             if ($MESSAGE->reply_all == 'list' && $mailfollowup) {
595                 $fvalue = $mailfollowup;
596             }
597             else if ($MESSAGE->reply_all == 'list'
598                 && preg_match('/<mailto:([^>]+)>/i', $MESSAGE->headers->others['list-post'], $m)
599             ) {
600                 $fvalue = $m[1];
601             }
602             // Reply to...
603             else if ($MESSAGE->reply_all && $mailfollowup) {
604                 $fvalue = $mailfollowup;
605             }
606             else if ($mailreplyto) {
607                 $fvalue = $mailreplyto;
608             }
609             else if (!empty($MESSAGE->headers->replyto)) {
610                 $fvalue  = $MESSAGE->headers->replyto;
611                 $replyto = true;
612             }
613             else if (!empty($MESSAGE->headers->from)) {
614                 $fvalue = $MESSAGE->headers->from;
615             }
616
617             // Reply to message sent by yourself (#1487074, #1489230, #1490439)
618             // Reply-To address need to be unset (#1490233)
619             if (!empty($MESSAGE->compose['ident']) && empty($replyto)) {
620                 foreach (array($fvalue, $MESSAGE->headers->from) as $sender) {
621                     $senders = rcube_mime::decode_address_list($sender, null, false, $charset, true);
622
623                     if (in_array($MESSAGE->compose['ident']['email_ascii'], $senders)) {
624                         $fvalue = $MESSAGE->headers->to;
625                         break;
626                     }
627                 }
628             }
629         }
630         // add recipient of original message if reply to all
631         else if ($header == 'cc' && !empty($MESSAGE->reply_all) && $MESSAGE->reply_all != 'list') {
632             if ($v = $MESSAGE->headers->to) {
633                 $fvalue .= $v;
634             }
635             if ($v = $MESSAGE->headers->cc) {
636                 $fvalue .= (!empty($fvalue) ? $separator : '') . $v;
637             }
638             // Use Sender header (#1489011)
192467 639             if ($v = $MESSAGE->headers->get('Sender', false)) {
AM 640                 // Skip common mailing lists addresses: *-bounces@ and *-request@ (#1490452)
641                 if (empty($MESSAGE->headers->others['list-post']) || !preg_match('/-(bounces|request)@/', $v)) {
642                     $fvalue .= (!empty($fvalue) ? $separator : '') . $v;
643                 }
1414e3 644             }
AM 645
646             // When To: and Reply-To: are the same we add From: address to the list (#1489037)
647             if ($v = $MESSAGE->headers->from) {
648                 $from    = rcube_mime::decode_address_list($v, null, false, $charset, true);
649                 $to      = rcube_mime::decode_address_list($MESSAGE->headers->to, null, false, $charset, true);
650                 $replyto = rcube_mime::decode_address_list($MESSAGE->headers->replyto, null, false, $charset, true);
651
652                 if (count($replyto) && !count(array_diff($to, $replyto)) && count(array_diff($from, $to))) {
653                     $fvalue .= (!empty($fvalue) ? $separator : '') . $v;
654                 }
655             }
656         }
657     }
658     else if (in_array($COMPOSE['mode'], array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) {
659         // get drafted headers
660         if ($header == 'to' && !empty($MESSAGE->headers->to)) {
661             $fvalue = $MESSAGE->get_header('to', true);
662         }
663         else if ($header == 'cc' && !empty($MESSAGE->headers->cc)) {
664             $fvalue = $MESSAGE->get_header('cc', true);
665         }
666         else if ($header == 'bcc' && !empty($MESSAGE->headers->bcc)) {
667             $fvalue = $MESSAGE->get_header('bcc', true);
668         }
669         else if ($header == 'replyto' && !empty($MESSAGE->headers->others['mail-reply-to'])) {
670             $fvalue = $MESSAGE->get_header('mail-reply-to');
671         }
672         else if ($header == 'replyto' && !empty($MESSAGE->headers->replyto)) {
673             $fvalue = $MESSAGE->get_header('reply-to');
674         }
675         else if ($header == 'followupto' && !empty($MESSAGE->headers->others['mail-followup-to'])) {
676             $fvalue = $MESSAGE->get_header('mail-followup-to');
677         }
678     }
679
680     // split recipients and put them back together in a unique way
681     if (!empty($fvalue) && in_array($header, array('to', 'cc', 'bcc'))) {
682         $from_email   = @mb_strtolower($MESSAGE->compose['ident']['email']);
683         $to_addresses = rcube_mime::decode_address_list($fvalue, null, $decode_header, $charset);
684         $fvalue       = array();
685
686         foreach ($to_addresses as $addr_part) {
687             if (empty($addr_part['mailto'])) {
688                 continue;
689             }
690
691             // According to RFC5321 local part of email address is case-sensitive
692             // however, here it is better to compare addresses in case-insensitive manner
693             $mailto    = format_email(rcube_utils::idn_to_utf8($addr_part['mailto']));
694             $mailto_lc = mb_strtolower($addr_part['mailto']);
695
696             if (($header == 'to' || $COMPOSE['mode'] != RCUBE_COMPOSE_REPLY || $mailto_lc != $from_email)
697                 && !in_array($mailto_lc, (array) $MESSAGE->recipients)
698             ) {
699                 if ($addr_part['name'] && $mailto != $addr_part['name']) {
700                     $mailto = format_email_recipient($mailto, $addr_part['name']);
701                 }
702
703                 $fvalue[]              = $mailto;
704                 $MESSAGE->recipients[] = $mailto_lc;
705             }
706         }
707
708         $fvalue = implode($separator, $fvalue);
709     }
710
711     return $fvalue;
712 }
4e17e6 713
868deb 714 function rcmail_compose_editor_mode()
A 715 {
8bed5e 716     global $RCMAIL, $COMPOSE;
5e3034 717     static $useHtml;
868deb 718
5e3034 719     if ($useHtml !== null) {
AM 720         return $useHtml;
721     }
722
1414e3 723     $html_editor  = intval($RCMAIL->config->get('htmleditor'));
AM 724     $compose_mode = $COMPOSE['mode'];
5e3034 725
b0a8e3 726     if (is_bool($COMPOSE['param']['html'])) {
AM 727         $useHtml = $COMPOSE['param']['html'];
728     }
729     else if (isset($_POST['_is_html'])) {
5e3034 730         $useHtml = !empty($_POST['_is_html']);
AM 731     }
732     else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) {
733         $useHtml = rcmail_message_is_html();
734     }
735     else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
6ca1e6 736         $useHtml = $html_editor == 1 || ($html_editor >= 2 && rcmail_message_is_html());
5e3034 737     }
AM 738     else if ($compose_mode == RCUBE_COMPOSE_FORWARD) {
6ca1e6 739         $useHtml = $html_editor == 1 || $html_editor == 4
AM 740             || ($html_editor == 3 && rcmail_message_is_html());
5e3034 741     }
AM 742     else {
6ca1e6 743         $useHtml = $html_editor == 1 || $html_editor == 4;
5e3034 744     }
AM 745
868deb 746     return $useHtml;
A 747 }
748
abf467 749 function rcmail_message_is_html()
AM 750 {
a02c77 751     global $RCMAIL, $MESSAGE;
5e3034 752
a02c77 753     return $RCMAIL->config->get('prefer_html') && ($MESSAGE instanceof rcube_message) && $MESSAGE->has_html_part(true);
abf467 754 }
868deb 755
087c7d 756 function rcmail_prepare_message_body()
4315b0 757 {
1414e3 758     global $RCMAIL, $MESSAGE, $COMPOSE, $HTML_MODE;
a0109c 759
5e3034 760     // use posted message body
AM 761     if (!empty($_POST['_message'])) {
762         $body   = rcube_utils::get_input_value('_message', rcube_utils::INPUT_POST, true);
763         $isHtml = (bool) rcube_utils::get_input_value('_is_html', rcube_utils::INPUT_POST);
b62049 764     }
5e3034 765     else if ($COMPOSE['param']['body']) {
AM 766         $body   = $COMPOSE['param']['body'];
767         $isHtml = (bool) $COMPOSE['param']['html'];
768     }
769     // forward as attachment
1414e3 770     else if ($COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD && $COMPOSE['as_attachment']) {
5e3034 771         $isHtml = rcmail_compose_editor_mode();
AM 772         $body   = '';
773
774         rcmail_write_forward_attachments();
775     }
776     // reply/edit/draft/forward
1414e3 777     else if ($COMPOSE['mode'] && ($COMPOSE['mode'] != RCUBE_COMPOSE_REPLY || intval($RCMAIL->config->get('reply_mode')) != -1)) {
5e3034 778         $isHtml   = rcmail_compose_editor_mode();
AM 779         $messages = array();
780
781         if (!empty($MESSAGE->parts)) {
782             // collect IDs of message/rfc822 parts
be3460 783             foreach ($MESSAGE->mime_parts as $part) {
AM 784                 if ($part->mimetype == 'message/rfc822') {
785                     $messages[] = $part->mime_id;
5e3034 786                 }
AM 787             }
788
789             foreach ($MESSAGE->parts as $part) {
3167e5 790                 if ($part->realtype == 'multipart/encrypted') {
TB 791                     // find the encrypted message payload part
f7f75f 792                     if ($pgp_mime_part = $MESSAGE->get_multipart_encrypted_part()) {
TB 793                         $RCMAIL->output->set_env('pgp_mime_message', array(
794                             '_mbox' => $RCMAIL->storage->get_folder(),
795                             '_uid'  => $MESSAGE->uid,
796                             '_part' => $pgp_mime_part->mime_id,
797                         ));
3167e5 798                     }
TB 799                     continue;
800                 }
801
5e3034 802                 // skip no-content and attachment parts (#1488557)
AM 803                 if ($part->type != 'content' || !$part->size || $MESSAGE->is_attachment($part)) {
804                     continue;
805                 }
806
be3460 807                 // skip all content parts inside the message/rfc822 part
5e3034 808                 foreach ($messages as $mimeid) {
AM 809                     if (strpos($part->mime_id, $mimeid . '.') === 0) {
810                         continue 2;
811                     }
812                 }
813
814                 if ($part_body = rcmail_compose_part_body($part, $isHtml)) {
815                     $body .= ($body ? ($isHtml ? '<br/>' : "\n") : '') . $part_body;
816                 }
817             }
818         }
819         else {
820             $body = rcmail_compose_part_body($MESSAGE, $isHtml);
821         }
822
823         // compose reply-body
f189d7 824         if ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) {
5e3034 825             $body = rcmail_create_reply_body($body, $isHtml);
3167e5 826
TB 827             if ($MESSAGE->pgp_mime) {
828                 $RCMAIL->output->set_env('compose_reply_header', rcmail_get_reply_header($MESSAGE));
829             }
830         }
5e3034 831         // forward message body inline
f189d7 832         else if ($COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD) {
5e3034 833             $body = rcmail_create_forward_body($body, $isHtml);
3167e5 834         }
5e3034 835         // load draft message body
f189d7 836         else if ($COMPOSE['mode'] == RCUBE_COMPOSE_DRAFT || $COMPOSE['mode'] == RCUBE_COMPOSE_EDIT) {
5e3034 837             $body = rcmail_create_draft_body($body, $isHtml);
3167e5 838         }
5e3034 839     }
AM 840     else { // new message
841         $isHtml = rcmail_compose_editor_mode();
4e17e6 842     }
80815d 843
5e3034 844     $plugin = $RCMAIL->plugins->exec_hook('message_compose_body',
1414e3 845         array('body' => $body, 'html' => $isHtml, 'mode' => $COMPOSE['mode']));
a0109c 846
5e3034 847     $body = $plugin['body'];
AM 848     unset($plugin);
8abe54 849
5e3034 850     // add blocked.gif attachment (#1486516)
c6efcf 851     if ($isHtml && preg_match('#<img src="program/resources/blocked\.gif"#', $body)) {
AM 852         $content = $RCMAIL->get_resource_content('blocked.gif');
853         if ($content && ($attachment = rcmail_save_image('blocked.gif', 'image/gif', $content))) {
5e3034 854             $COMPOSE['attachments'][$attachment['id']] = $attachment;
AM 855             $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s',
856                 $RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']);
c6efcf 857             $body = preg_replace('#program/resources/blocked\.gif#', $url, $body);
5e3034 858         }
b575fa 859     }
f52c4f 860
5e3034 861     $HTML_MODE = $isHtml;
f52c4f 862
5e3034 863     return $body;
33423a 864 }
A 865
866 function rcmail_compose_part_body($part, $isHtml = false)
867 {
f189d7 868     global $RCMAIL, $COMPOSE, $MESSAGE, $LINE_LENGTH;
33423a 869
A 870     // Check if we have enough memory to handle the message in it
871     // #1487424: we need up to 10x more memory than the body
6b2b2e 872     if (!rcube_utils::mem_check($part->size * 10)) {
33423a 873         return '';
A 874     }
875
876     // fetch part if not available
48ba44 877     $body = $MESSAGE->get_part_body($part->mime_id, true);
33423a 878
A 879     // message is cached but not exists (#1485443), or other error
48ba44 880     if ($body === false) {
33423a 881         return '';
3167e5 882     }
TB 883
884     // register this part as pgp encrypted
82dcbb 885     if (strpos($body, '-----BEGIN PGP MESSAGE-----') !== false) {
3167e5 886         $MESSAGE->pgp_mime = true;
TB 887         $RCMAIL->output->set_env('pgp_mime_message', array(
888             '_mbox' => $RCMAIL->storage->get_folder(), '_uid' => $MESSAGE->uid, '_part' => $part->mime_id,
889         ));
33423a 890     }
A 891
892     if ($isHtml) {
893         if ($part->ctype_secondary == 'html') {
894         }
52d0d9 895         else if ($part->ctype_secondary == 'enriched') {
0fa54d 896             $body = rcube_enriched::to_html($body);
52d0d9 897         }
33423a 898         else {
A 899             // try to remove the signature
1414e3 900             if ($COMPOSE['mode'] != RCUBE_COMPOSE_DRAFT && $COMPOSE['mode'] != RCUBE_COMPOSE_EDIT) {
a9bb50 901                 if ($RCMAIL->config->get('strip_existing_sig', true)) {
AM 902                     $body = rcmail_remove_signature($body);
903                 }
33423a 904             }
621a2e 905
33423a 906             // add HTML formatting
eda92e 907             $body = rcmail_plain_body($body, $part->ctype_parameters['format'] == 'flowed');
33423a 908         }
A 909     }
910     else {
52d0d9 911         if ($part->ctype_secondary == 'enriched') {
0fa54d 912             $body = rcube_enriched::to_html($body);
52d0d9 913             $part->ctype_secondary = 'html';
AM 914         }
915
33423a 916         if ($part->ctype_secondary == 'html') {
A 917             // use html part if it has been used for message (pre)viewing
918             // decrease line length for quoting
a63f14 919             $len  = $COMPOSE['mode'] == RCUBE_COMPOSE_REPLY ? $LINE_LENGTH-2 : $LINE_LENGTH;
AM 920             $body = $RCMAIL->html2text($body, array('width' => $len));
33423a 921         }
A 922         else {
923             if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed') {
924                 $body = rcube_mime::unfold_flowed($body);
925             }
926
927             // try to remove the signature
1414e3 928             if ($COMPOSE['mode'] != RCUBE_COMPOSE_DRAFT && $COMPOSE['mode'] != RCUBE_COMPOSE_EDIT) {
a9bb50 929                 if ($RCMAIL->config->get('strip_existing_sig', true)) {
AM 930                     $body = rcmail_remove_signature($body);
931                 }
33423a 932             }
A 933         }
934     }
935
936     return $body;
087c7d 937 }
A 938
939 function rcmail_compose_body($attrib)
940 {
f5d2ee 941     global $RCMAIL, $OUTPUT, $HTML_MODE, $MESSAGE_BODY;
f52c4f 942
5e3034 943     list($form_start, $form_end) = get_form_tags($attrib);
AM 944     unset($attrib['form']);
f52c4f 945
5e3034 946     if (empty($attrib['id']))
AM 947         $attrib['id'] = 'rcmComposeBody';
087c7d 948
5e3034 949     $attrib['name'] = '_message';
087c7d 950
5e3034 951     $isHtml = $HTML_MODE;
f52c4f 952
5e3034 953     $out = $form_start ? "$form_start\n" : '';
1966c5 954
5e3034 955     $saveid = new html_hiddenfield(array('name' => '_draft_saveid', 'value' => $RCMAIL->output->get_env('draft_id')));
AM 956     $out .= $saveid->show();
1966c5 957
5e3034 958     $drafttoggle = new html_hiddenfield(array('name' => '_draft', 'value' => 'yes'));
AM 959     $out .= $drafttoggle->show();
1966c5 960
5e3034 961     $msgtype = new html_hiddenfield(array('name' => '_is_html', 'value' => ($isHtml ? "1" : "0")));
AM 962     $out .= $msgtype->show();
a0109c 963
5e3034 964     $framed = new html_hiddenfield(array('name' => '_framed', 'value' => '1'));
AM 965     $out .= $framed->show();
85e60a 966
5e3034 967     // If desired, set this textarea to be editable by TinyMCE
AM 968     if ($isHtml) {
969         $MESSAGE_BODY = htmlentities($MESSAGE_BODY, ENT_NOQUOTES, RCUBE_CHARSET);
970         $attrib['class'] = 'mce_editor';
971         $attrib['is_escaped'] = true;
972         $textarea = new html_textarea($attrib);
973         $out .= $textarea->show($MESSAGE_BODY);
974     }
975     else {
976         $textarea = new html_textarea($attrib);
977         $out .= $textarea->show('');
6084d7 978
5e3034 979         // quote plain text, inject into textarea
AM 980         $table = get_html_translation_table(HTML_SPECIALCHARS);
981         $MESSAGE_BODY = strtr($MESSAGE_BODY, $table);
982         $out = substr($out, 0, -11) . $MESSAGE_BODY . '</textarea>';
c287f3 983     }
66df08 984
5e3034 985     $out .= $form_end ? "\n$form_end" : '';
ed5d29 986
5e3034 987     $OUTPUT->set_env('composebody', $attrib['id']);
f52c4f 988
5e3034 989     // include HTML editor
AM 990     $RCMAIL->html_editor();
41fa0b 991
5e3034 992     // Set language list
f5d2ee 993     if ($RCMAIL->config->get('enable_spellcheck')) {
5e3034 994         $engine           = new rcube_spellchecker();
AM 995         $dictionary       = (bool) $RCMAIL->config->get('spellcheck_dictionary');
996         $spellcheck_langs = $engine->languages();
997         $lang             = $_SESSION['language'];
998
999         // if not found in the list, try with two-letter code
1000         if (!$spellcheck_langs[$lang]) {
1001             $lang = strtolower(substr($lang, 0, 2));
1002         }
1003
1004         if (!$spellcheck_langs[$lang]) {
1005             $lang = 'en';
1006         }
1007
1008         $OUTPUT->set_env('spell_langs', $spellcheck_langs);
1009         $OUTPUT->set_env('spell_lang', $lang);
1010
1011         $editor_lang_set = array();
1012         foreach ($spellcheck_langs as $key => $name) {
1013             $editor_lang_set[] = ($key == $lang ? '+' : '') . rcube::JQ($name).'='.rcube::JQ($key);
1014         }
1015
1016         // include GoogieSpell
1017         $OUTPUT->include_script('googiespell.js');
1018         $OUTPUT->add_script(sprintf(
1019             "var googie = new GoogieSpell('%s/images/googiespell/','%s&lang=', %s);\n".
1020             "googie.lang_chck_spell = \"%s\";\n".
1021             "googie.lang_rsm_edt = \"%s\";\n".
1022             "googie.lang_close = \"%s\";\n".
1023             "googie.lang_revert = \"%s\";\n".
1024             "googie.lang_no_error_found = \"%s\";\n".
1025             "googie.lang_learn_word = \"%s\";\n".
1026             "googie.setLanguages(%s);\n".
1027             "googie.setCurrentLanguage('%s');\n".
1028             "googie.setDecoration(false);\n".
646b64 1029             "googie.decorateTextarea('%s');\n",
681ba6 1030             $RCMAIL->output->asset_url($RCMAIL->output->get_skin_path()),
5e3034 1031             $RCMAIL->url(array('_task' => 'utils', '_action' => 'spell', '_remote' => 1)),
AM 1032                 !empty($dictionary) ? 'true' : 'false',
1033             rcube::JQ(rcube::Q($RCMAIL->gettext('checkspelling'))),
1034             rcube::JQ(rcube::Q($RCMAIL->gettext('resumeediting'))),
1035             rcube::JQ(rcube::Q($RCMAIL->gettext('close'))),
1036             rcube::JQ(rcube::Q($RCMAIL->gettext('revertto'))),
1037             rcube::JQ(rcube::Q($RCMAIL->gettext('nospellerrors'))),
1038             rcube::JQ(rcube::Q($RCMAIL->gettext('addtodict'))),
1039             rcube_output::json_serialize($spellcheck_langs),
1040             $lang,
646b64 1041             $attrib['id']), 'foot');
5e3034 1042
AM 1043         $OUTPUT->add_label('checking');
1044         $OUTPUT->set_env('spellcheck_langs', join(',', $editor_lang_set));
1045     }
1046
b2992d 1047     $out .= "\n".'<iframe name="savetarget" src="program/resources/blank.gif" style="width:0;height:0;border:none;visibility:hidden;" aria-hidden="true"></iframe>';
5e3034 1048
AM 1049     return $out;
4315b0 1050 }
4e17e6 1051
T 1052
a0109c 1053 function rcmail_create_reply_body($body, $bodyIsHtml)
4315b0 1054 {
5e3034 1055     global $RCMAIL, $MESSAGE, $LINE_LENGTH;
4e17e6 1056
3167e5 1057     $prefix = rcmail_get_reply_header($MESSAGE);
0b96b1 1058     $reply_mode = intval($RCMAIL->config->get('reply_mode'));
AM 1059
5e3034 1060     if (!$bodyIsHtml) {
AM 1061         $body = preg_replace('/\r?\n/', "\n", $body);
1062         $body = trim($body, "\n");
9f9664 1063
5e3034 1064         // soft-wrap and quote message text
AM 1065         $body = rcmail_wrap_and_quote($body, $LINE_LENGTH);
4e17e6 1066
5e3034 1067         $prefix .= "\n";
9f9664 1068
0b96b1 1069         if ($reply_mode > 0) { // top-posting
5e3034 1070             $prefix = "\n\n\n" . $prefix;
0b96b1 1071             $suffix = '';
AM 1072         }
1073         else {
1074             $suffix = "\n";
5e3034 1075         }
50f56d 1076     }
A 1077     else {
5e3034 1078         // save inline images to files
AM 1079         $cid_map = rcmail_write_inline_attachments($MESSAGE);
1080         // set is_safe flag (we need this for html body washing)
1081         rcmail_check_safe($MESSAGE);
1082         // clean up html tags
1083         $body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
a0109c 1084
5e3034 1085         // build reply (quote content)
AM 1086         $prefix = '<p>' . rcube::Q($prefix) . "</p>\n";
1087         $prefix .= '<blockquote>';
1088
0b96b1 1089         if ($reply_mode > 0) { // top-posting
5e3034 1090             $prefix = '<br>' . $prefix;
AM 1091             $suffix = '</blockquote>';
1092         }
1093         else {
c4daf3 1094             $suffix = '</blockquote><p><br/></p>';
5e3034 1095         }
AM 1096     }
1097
1098     return $prefix . $body . $suffix;
4315b0 1099 }
4e17e6 1100
3167e5 1101 function rcmail_get_reply_header($message)
TB 1102 {
1103     global $RCMAIL;
1104
1105     $from = array_pop(rcube_mime::decode_address_list($message->get_header('from'), 1, false, $message->headers->charset));
1106     return $RCMAIL->gettext(array(
1107         'name' => 'mailreplyintro',
1108         'vars' => array(
1109             'date'   => $RCMAIL->format_date($message->headers->date, $RCMAIL->config->get('date_long')),
1110             'sender' => $from['name'] ?: rcube_utils::idn_to_utf8($from['mailto']),
1111         )
1112     ));
1113 }
4e17e6 1114
a0109c 1115 function rcmail_create_forward_body($body, $bodyIsHtml)
4315b0 1116 {
5e3034 1117     global $RCMAIL, $MESSAGE, $COMPOSE;
ec603f 1118
5e3034 1119     // add attachments
AM 1120     if (!isset($COMPOSE['forward_attachments']) && is_array($MESSAGE->mime_parts)) {
1121         $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
1122     }
4e17e6 1123
5e3034 1124     $date = $RCMAIL->format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long'));
f5c108 1125
5e3034 1126     if (!$bodyIsHtml) {
AM 1127         $prefix = "\n\n\n-------- " . $RCMAIL->gettext('originalmessage') . " --------\n";
1128         $prefix .= $RCMAIL->gettext('subject') . ': ' . $MESSAGE->subject . "\n";
1129         $prefix .= $RCMAIL->gettext('date')    . ': ' . $date . "\n";
1130         $prefix .= $RCMAIL->gettext('from')    . ': ' . $MESSAGE->get_header('from') . "\n";
1131         $prefix .= $RCMAIL->gettext('to')      . ': ' . $MESSAGE->get_header('to') . "\n";
a4cf45 1132
5e3034 1133         if ($cc = $MESSAGE->headers->get('cc')) {
AM 1134             $prefix .= $RCMAIL->gettext('cc') . ': ' . $cc . "\n";
1135         }
1136         if (($replyto = $MESSAGE->headers->get('reply-to')) && $replyto != $MESSAGE->get_header('from')) {
1137             $prefix .= $RCMAIL->gettext('replyto') . ': ' . $replyto . "\n";
1138         }
a4cf45 1139
5e3034 1140         $prefix .= "\n";
AM 1141         $body = trim($body, "\r\n");
1142     }
1143     else {
1144         // set is_safe flag (we need this for html body washing)
1145         rcmail_check_safe($MESSAGE);
ec603f 1146
5e3034 1147         // clean up html tags
AM 1148         $body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
5758b9 1149
5e3034 1150         $prefix = sprintf(
AM 1151             "<br /><p>-------- " . $RCMAIL->gettext('originalmessage') . " --------</p>" .
1152             "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" .
1153             "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
1154             "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
1155             "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
1156             "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
1157             $RCMAIL->gettext('subject'), rcube::Q($MESSAGE->subject),
1158             $RCMAIL->gettext('date'), rcube::Q($date),
1159             $RCMAIL->gettext('from'), rcube::Q($MESSAGE->get_header('from'), 'replace'),
1160             $RCMAIL->gettext('to'), rcube::Q($MESSAGE->get_header('to'), 'replace'));
a4cf45 1161
5e3034 1162         if ($cc = $MESSAGE->headers->get('cc'))
AM 1163             $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
1164                 $RCMAIL->gettext('cc'), rcube::Q($cc, 'replace'));
5758b9 1165
5e3034 1166         if (($replyto = $MESSAGE->headers->get('reply-to')) && $replyto != $MESSAGE->get_header('from'))
AM 1167             $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
1168                 $RCMAIL->gettext('replyto'), rcube::Q($replyto, 'replace'));
f52c4f 1169
5e3034 1170         $prefix .= "</tbody></table><br>";
AM 1171     }
1172
1173     return $prefix . $body;
4315b0 1174 }
4e17e6 1175
8d4bcd 1176
a0109c 1177 function rcmail_create_draft_body($body, $bodyIsHtml)
4315b0 1178 {
5e3034 1179     global $MESSAGE, $COMPOSE;
f52c4f 1180
5e3034 1181     // add attachments
AM 1182     // sizeof($MESSAGE->mime_parts can be 1 - e.g. attachment, but no text!
1183     if (empty($COMPOSE['forward_attachments'])
1184         && is_array($MESSAGE->mime_parts)
1185         && count($MESSAGE->mime_parts) > 0
1186     ) {
1187         $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
f2a9a9 1188     }
f52c4f 1189
5e3034 1190     // clean up HTML tags - XSS prevention (#1489251)
AM 1191     if ($bodyIsHtml) {
1192         $body = rcmail_wash_html($body, array('safe' => 1), $cid_map);
1193
d5f6d6 1194         // cleanup
AM 1195         $body = preg_replace(array(
1196                 // remove comments (produced by washtml)
1197                 '/<!--[^>]+-->/',
1198                 // remove <body> tags
1199                 '/<body([^>]*)>/i',
1200                 '/<\/body>/i',
1201                 // convert TinyMCE's empty-line sequence (#1490463)
1202                 '/<p>\xC2\xA0<\/p>/',
1203             ),
1204             array(
1205                 '',
1206                 '',
1207                 '',
1208                 '<p><br /></p>',
1209             ),
1210             $body
1211         );
5e3034 1212
AM 1213         // replace cid with href in inline images links
1214         if (!empty($cid_map)) {
1215             $body = str_replace(array_keys($cid_map), array_values($cid_map), $body);
1216         }
1217     }
1218
1219     return $body;
4315b0 1220 }
ba12c7 1221
A 1222
1223 function rcmail_remove_signature($body)
1224 {
5e3034 1225     global $RCMAIL;
ba12c7 1226
5e3034 1227     $body = str_replace("\r\n", "\n", $body);
AM 1228     $len  = strlen($body);
1229     $sig_max_lines = $RCMAIL->config->get('sig_max_lines', 15);
ba12c7 1230
5e3034 1231     while (($sp = strrpos($body, "-- \n", $sp ? -$len+$sp-1 : 0)) !== false) {
AM 1232         if ($sp == 0 || $body[$sp-1] == "\n") {
1233             // do not touch blocks with more that X lines
1234             if (substr_count($body, "\n", $sp) < $sig_max_lines) {
1235                 $body = substr($body, 0, max(0, $sp-1));
1236             }
1237             break;
1238         }
ba12c7 1239     }
A 1240
5e3034 1241     return $body;
ba12c7 1242 }
A 1243
1244
1bc48e 1245 function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
4315b0 1246 {
1414e3 1247     global $RCMAIL, $COMPOSE;
5503cc 1248
5e3034 1249     $loaded_attachments = array();
AM 1250     foreach ((array)$COMPOSE['attachments'] as $attachment) {
1251         $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment;
8d4bcd 1252     }
cc97ea 1253
5e3034 1254     $cid_map  = array();
AM 1255     $messages = array();
ec603f 1256
3167e5 1257     if ($message->pgp_mime) {
TB 1258         return $cid_map;
1259     }
1260
5e3034 1261     foreach ((array)$message->mime_parts as $pid => $part) {
be3460 1262         if ($part->mimetype == 'message/rfc822') {
AM 1263             $messages[] = $part->mime_id;
1264         }
1265
5e3034 1266         if ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename) {
AM 1267             // skip parts that aren't valid attachments
1268             if ($part->ctype_primary == 'multipart' || $part->mimetype == 'application/ms-tnef') {
1269                 continue;
1270             }
1271
1272             // skip message attachments in reply mode
1414e3 1273             if ($part->ctype_primary == 'message' && $COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) {
5e3034 1274                 continue;
AM 1275             }
1276
1277             // skip inline images when forwarding in text mode
1414e3 1278             if ($part->content_id && $part->disposition == 'inline' && !$bodyIsHtml && $COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD) {
5e3034 1279                 continue;
AM 1280             }
1281
f7f75f 1282             // skip version.txt parts of multipart/encrypted messages
TB 1283             if ($message->pgp_mime && $part->mimetype == 'application/pgp-encrypted' && $part->filename == 'version.txt') {
1284                 continue;
1285             }
1286
be3460 1287             // skip attachments included in message/rfc822 attachment (#1486487, #1490607)
AM 1288             foreach ($messages as $mimeid) {
1289                 if (strpos($part->mime_id, $mimeid . '.') === 0) {
1290                     continue 2;
5e3034 1291                 }
AM 1292             }
1293
1294             if (($attachment = $loaded_attachments[rcmail_attachment_name($part) . $part->mimetype])
d56091 1295                 || ($attachment = rcmail_save_attachment($message, $pid, $COMPOSE['id']))
5e3034 1296             ) {
AM 1297                 if ($bodyIsHtml && ($part->content_id || $part->content_location)) {
1298                     $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s',
1299                         $RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']);
1300
1301                     if ($part->content_id)
1302                         $cid_map['cid:'.$part->content_id] = $url;
1303                     else
1304                         $cid_map[$part->content_location] = $url;
1305                 }
1306             }
1307         }
1308     }
1309
1310     $COMPOSE['forward_attachments'] = true;
1311
1312     return $cid_map;
4315b0 1313 }
4e17e6 1314
T 1315
2f746d 1316 function rcmail_write_inline_attachments(&$message)
A 1317 {
5e3034 1318     global $RCMAIL, $COMPOSE;
ec603f 1319
be3460 1320     $cid_map  = array();
AM 1321     $messages = array();
3167e5 1322
TB 1323     if ($message->pgp_mime) {
1324         return $cid_map;
1325     }
1326
5e3034 1327     foreach ((array)$message->mime_parts as $pid => $part) {
be3460 1328         if ($part->mimetype == 'message/rfc822') {
AM 1329             $messages[] = $part->mime_id;
1330         }
1331
5e3034 1332         if (($part->content_id || $part->content_location) && $part->filename) {
be3460 1333             // skip attachments included in message/rfc822 attachment (#1486487, #1490607)
AM 1334             foreach ($messages as $mimeid) {
1335                 if (strpos($part->mime_id, $mimeid . '.') === 0) {
1336                     continue 2;
1337                 }
1338             }
1339
d56091 1340             if ($attachment = rcmail_save_attachment($message, $pid, $COMPOSE['id'])) {
5e3034 1341                 $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s',
AM 1342                     $RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']);
1343
1344                 if ($part->content_id)
1345                     $cid_map['cid:'.$part->content_id] = $url;
1346                 else
1347                     $cid_map[$part->content_location] = $url;
1348             }
1349         }
2f746d 1350     }
8f2b46 1351
5e3034 1352     return $cid_map;
2f746d 1353 }
A 1354
d9f109 1355 // Creates attachment(s) from the forwarded message(s)
AM 1356 function rcmail_write_forward_attachments()
a208a4 1357 {
5e3034 1358     global $RCMAIL, $COMPOSE, $MESSAGE;
a208a4 1359
5e3034 1360     $storage = $RCMAIL->get_storage();
AM 1361     $names   = array();
73076d 1362     $refs    = array();
d9f109 1363
3167e5 1364     if ($MESSAGE->pgp_mime) {
TB 1365         return;
1366     }
1367
5e3034 1368     $loaded_attachments = array();
AM 1369     foreach ((array)$COMPOSE['attachments'] as $attachment) {
1370         $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment;
d9f109 1371     }
AM 1372
5e3034 1373     if ($COMPOSE['forward_uid'] == '*') {
AM 1374         $index = $storage->index(null, rcmail_sort_column(), rcmail_sort_order());
1375         $COMPOSE['forward_uid'] = $index->get();
d9f109 1376     }
aafbe8 1377     else if (!is_array($COMPOSE['forward_uid']) && strpos($COMPOSE['forward_uid'], ':')) {
03de13 1378         $COMPOSE['forward_uid'] = rcube_imap_generic::uncompressMessageSet($COMPOSE['forward_uid']);
AM 1379     }
aafbe8 1380     else if (is_string($COMPOSE['forward_uid'])) {
5e3034 1381         $COMPOSE['forward_uid'] = explode(',', $COMPOSE['forward_uid']);
d9f109 1382     }
AM 1383
5e3034 1384     foreach ((array)$COMPOSE['forward_uid'] as $uid) {
AM 1385         $message = new rcube_message($uid);
d9f109 1386
5e3034 1387         if (empty($message->headers)) {
AM 1388             continue;
1389         }
d9f109 1390
5e3034 1391         if (!empty($message->headers->charset)) {
AM 1392             $storage->set_charset($message->headers->charset);
1393         }
1394
1395         if (empty($MESSAGE->subject)) {
1396             $MESSAGE->subject = $message->subject;
1397         }
1398
1399         // generate (unique) attachment name
1400         $name = strlen($message->subject) ? mb_substr($message->subject, 0, 64) : 'message_rfc822';
1401         if (!empty($names[$name])) {
1402             $names[$name]++;
1403             $name .= '_' . $names[$name];
1404         }
1405         $names[$name] = 1;
1406         $name .= '.eml';
1407
1408         if (!empty($loaded_attachments[$name . 'message/rfc822'])) {
1409             continue;
1410         }
1411
d56091 1412         rcmail_save_attachment($message, null, $COMPOSE['id'], array('filename' => $name));
73076d 1413
AM 1414         if ($message->headers->messageID) {
1415             $refs[] = $message->headers->messageID;
1416         }
1417     }
1418
1419     // set In-Reply-To and References headers
1420     if (count($refs) == 1) {
1421         $COMPOSE['reply_msgid'] = $refs[0];
1422     }
1423     if (!empty($refs)) {
1424         $COMPOSE['references'] = implode(' ', $refs);
d9f109 1425     }
2f746d 1426 }
A 1427
c6efcf 1428 function rcmail_save_image($path, $mimetype = '', $data = null)
b575fa 1429 {
5e3034 1430     global $COMPOSE;
72ff6a 1431
5e3034 1432     // handle attachments in memory
c6efcf 1433     if (empty($data)) {
AM 1434         $data    = file_get_contents($path);
1435         $is_file = true;
1436     }
1437
5e3034 1438     $name = rcmail_basename($path);
c6efcf 1439
AM 1440     if (empty($mimetype)) {
1441         if ($is_file) {
1442             $mimetype = rcube_mime::file_content_type($path, $name);
1443         }
1444         else {
1445             $mimetype = rcube_mime::file_content_type($data, $name, 'application/octet-stream', true);
1446         }
1447     }
b575fa 1448
5e3034 1449     $attachment = array(
AM 1450         'group'    => $COMPOSE['id'],
1451         'name'     => $name,
c6efcf 1452         'mimetype' => $mimetype,
5e3034 1453         'data'     => $data,
AM 1454         'size'     => strlen($data),
1455     );
b575fa 1456
5e3034 1457     $attachment = rcmail::get_instance()->plugins->exec_hook('attachment_save', $attachment);
b575fa 1458
5e3034 1459     if ($attachment['status']) {
AM 1460         unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
1461         return $attachment;
1462     }
f52c4f 1463
5e3034 1464     return false;
b575fa 1465 }
A 1466
1467 function rcmail_basename($filename)
1468 {
5e3034 1469     // basename() is not unicode safe and locale dependent
AM 1470     if (stristr(PHP_OS, 'win') || stristr(PHP_OS, 'netware')) {
1471         return preg_replace('/^.*[\\\\\\/]/', '', $filename);
1472     }
1473     else {
1474         return preg_replace('/^.*[\/]/', '', $filename);
1475     }
b575fa 1476 }
2f746d 1477
60ab55 1478 /**
AM 1479  * Creates reply subject by removing common subject
1480  * prefixes/suffixes from the original message subject
1481  */
1482 function rcmail_reply_subject($subject)
1483 {
1484     $subject = trim($subject);
1485
1486     // replace Re:, Re[x]:, Re-x (#1490497)
1487     $prefix = '/^(re:|re\[\d\]:|re-\d:)\s*/i';
1488     do {
1489         $subject = preg_replace($prefix, '', $subject, -1, $count);
1490     }
1491     while ($count);
1492
1493     // replace (was: ...) (#1489375)
1494     $subject = preg_replace('/\s*\([wW]as:[^\)]+\)\s*$/', '', $subject);
1495
1496     return 'Re: ' . $subject;
1497 }
1498
4e17e6 1499 function rcmail_compose_subject($attrib)
4315b0 1500 {
1414e3 1501     global $MESSAGE, $COMPOSE;
72ff6a 1502
5e3034 1503     list($form_start, $form_end) = get_form_tags($attrib);
AM 1504     unset($attrib['form']);
72ff6a 1505
5e3034 1506     $attrib['name']       = '_subject';
AM 1507     $attrib['spellcheck'] = 'true';
4e17e6 1508
5e3034 1509     $textfield = new html_inputfield($attrib);
AM 1510     $subject   = '';
4e17e6 1511
5e3034 1512     // use subject from post
AM 1513     if (isset($_POST['_subject'])) {
1514         $subject = rcube_utils::get_input_value('_subject', rcube_utils::INPUT_POST, TRUE);
1515     }
968601 1516     else if (!empty($COMPOSE['param']['subject'])) {
AM 1517         $subject = $COMPOSE['param']['subject'];
1518     }
5e3034 1519     // create a reply-subject
1414e3 1520     else if ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) {
60ab55 1521         $subject = rcmail_reply_subject($MESSAGE->subject);
5e3034 1522     }
AM 1523     // create a forward-subject
1414e3 1524     else if ($COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD) {
5e3034 1525         if (preg_match('/^fwd:/i', $MESSAGE->subject))
AM 1526             $subject = $MESSAGE->subject;
1527         else
1528             $subject = 'Fwd: '.$MESSAGE->subject;
1529     }
1530     // creeate a draft-subject
1414e3 1531     else if ($COMPOSE['mode'] == RCUBE_COMPOSE_DRAFT || $COMPOSE['mode'] == RCUBE_COMPOSE_EDIT) {
5e3034 1532         $subject = $MESSAGE->subject;
AM 1533     }
72ff6a 1534
5e3034 1535     $out = $form_start ? "$form_start\n" : '';
AM 1536     $out .= $textfield->show($subject);
1537     $out .= $form_end ? "\n$form_end" : '';
e99991 1538
5e3034 1539     return $out;
4315b0 1540 }
4e17e6 1541
T 1542 function rcmail_compose_attachment_list($attrib)
4315b0 1543 {
f5d2ee 1544     global $RCMAIL, $OUTPUT, $COMPOSE;
72ff6a 1545
5e3034 1546     // add ID if not given
AM 1547     if (!$attrib['id'])
1548         $attrib['id'] = 'rcmAttachmentList';
72ff6a 1549
8ccfc2 1550     $out    = "\n";
AM 1551     $jslist = array();
1552     $button = '';
087c7d 1553
5e3034 1554     if (is_array($COMPOSE['attachments'])) {
AM 1555         if ($attrib['deleteicon']) {
1556             $button = html::img(array(
8ccfc2 1557                 'src' => $RCMAIL->output->abs_url($attrib['deleteicon'], true),
5e3034 1558                 'alt' => $RCMAIL->gettext('delete')
AM 1559             ));
1560         }
1561         else if (rcube_utils::get_boolean($attrib['textbuttons'])) {
1562             $button = rcube::Q($RCMAIL->gettext('delete'));
1563         }
1564
1565         foreach ($COMPOSE['attachments'] as $id => $a_prop) {
1566             if (empty($a_prop)) {
1567                 continue;
1568             }
1569
bb1ed2 1570             $content = sprintf('%s <span class="attachment-size">(%s)</span>',
AM 1571                 rcube::Q($a_prop['name']), $RCMAIL->show_bytes($a_prop['size']));
1572
5e3034 1573             $out .= html::tag('li', array(
AM 1574                     'id'          => 'rcmfile'.$id,
1575                     'class'       => rcube_utils::file2class($a_prop['mimetype'], $a_prop['name']),
1576                     'onmouseover' => "rcube_webmail.long_subject_title_ex(this, 0)",
1577                 ),
1578                 html::a(array(
1579                         'href'    => "#delete",
1580                         'title'   => $RCMAIL->gettext('delete'),
1581                         'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", rcmail_output::JS_OBJECT_NAME, $id),
9240c9 1582                         'class'   => 'delete',
TB 1583                         'tabindex' => $attrib['tabindex'] ?: '0',
1584                         'aria-label'   => $RCMAIL->gettext('delete') . ' ' . $a_prop['name'],
5e3034 1585                     ),
AM 1586                     $button
bb1ed2 1587                 ) . $content
5e3034 1588             );
AM 1589
1590             $jslist['rcmfile'.$id] = array(
1591                 'name'     => $a_prop['name'],
1592                 'complete' => true,
1593                 'mimetype' => $a_prop['mimetype']
1594             );
1595         }
2efe33 1596     }
a894ba 1597
5e3034 1598     if ($attrib['deleteicon'])
8ccfc2 1599         $COMPOSE['deleteicon'] = $RCMAIL->output->abs_url($attrib['deleteicon'], true);
5e3034 1600     else if (rcube_utils::get_boolean($attrib['textbuttons']))
AM 1601         $COMPOSE['textbuttons'] = true;
1602     if ($attrib['cancelicon'])
8ccfc2 1603         $OUTPUT->set_env('cancelicon', $RCMAIL->output->abs_url($attrib['cancelicon'], true));
5e3034 1604     if ($attrib['loadingicon'])
8ccfc2 1605         $OUTPUT->set_env('loadingicon', $RCMAIL->output->abs_url($attrib['loadingicon'], true));
72ff6a 1606
5e3034 1607     $OUTPUT->set_env('attachments', $jslist);
AM 1608     $OUTPUT->add_gui_object('attachmentlist', $attrib['id']);
7152f5 1609
9240c9 1610     // put tabindex value into data-tabindex attribute
TB 1611     if (isset($attrib['tabindex'])) {
1612         $attrib['data-tabindex'] = $attrib['tabindex'];
1613         unset($attrib['tabindex']);
1614     }
1615
5e3034 1616     return html::tag('ul', $attrib, $out, html::$common_attrib);
4315b0 1617 }
4e17e6 1618
T 1619
1620 function rcmail_compose_attachment_form($attrib)
4315b0 1621 {
5e3034 1622     global $OUTPUT, $RCMAIL;
4e17e6 1623
5e3034 1624     // set defaults
AM 1625     $attrib += array('id' => 'rcmUploadbox', 'buttons' => 'yes');
7df0e3 1626
5e3034 1627     // Get filesize, enable upload progress bar
AM 1628     $max_filesize = $RCMAIL->upload_init();
4171c5 1629
5e3034 1630     $button  = new html_inputfield(array('type' => 'button'));
AM 1631     $content = html::div(null, rcmail_compose_attachment_field())
1632         . html::div('hint', $RCMAIL->gettext(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))));
fe0cb6 1633
5e3034 1634     if (rcube_utils::get_boolean($attrib['buttons'])) {
AM 1635         $content .= html::div('buttons',
1636             $button->show($RCMAIL->gettext('close'), array('class' => 'button', 'onclick' => "$('#$attrib[id]').hide()")) . ' ' .
1637             $button->show($RCMAIL->gettext('upload'), array('class' => 'button mainaction', 'onclick' => rcmail_output::JS_OBJECT_NAME . ".command('send-attachment', this.form)"))
1638         );
1639     }
fe0cb6 1640
5e3034 1641     $out = html::div($attrib, $OUTPUT->form_tag(array(
AM 1642             'id'      => $attrib['id'] . 'Frm',
1643             'name'    => 'uploadform',
1644             'method'  => 'post',
1645             'enctype' => 'multipart/form-data'
1646         ), $content
1647     ));
1648
1649     $OUTPUT->add_gui_object('uploadform', $attrib['id'] . 'Frm');
1650
1651     return $out;
4315b0 1652 }
4e17e6 1653
T 1654
5a8ee3 1655 function rcmail_compose_attachment_field($attrib = array())
4315b0 1656 {
5e3034 1657     $attrib['type']     = 'file';
AM 1658     $attrib['name']     = '_attachments[]';
1659     $attrib['multiple'] = 'multiple';
7fc056 1660
5e3034 1661     $field = new html_inputfield($attrib);
AM 1662
1663     return $field->show();
4315b0 1664 }
4e17e6 1665
66e2bf 1666
4e17e6 1667 function rcmail_priority_selector($attrib)
4315b0 1668 {
5e3034 1669     global $RCMAIL, $MESSAGE;
7152f5 1670
5e3034 1671     list($form_start, $form_end) = get_form_tags($attrib);
AM 1672     unset($attrib['form']);
8958d0 1673
5e3034 1674     $attrib['name'] = '_priority';
AM 1675     $prio_list = array(
1676         $RCMAIL->gettext('lowest')  => 5,
1677         $RCMAIL->gettext('low')     => 4,
1678         $RCMAIL->gettext('normal')  => 0,
1679         $RCMAIL->gettext('high')    => 2,
1680         $RCMAIL->gettext('highest') => 1,
1681     );
4e17e6 1682
5e3034 1683     $selector = new html_select($attrib);
AM 1684     $selector->add(array_keys($prio_list), array_values($prio_list));
8958d0 1685
5e3034 1686     if (isset($_POST['_priority']))
AM 1687         $sel = $_POST['_priority'];
1688     else if (isset($MESSAGE->headers->priority) && intval($MESSAGE->headers->priority) != 3)
1689         $sel = $MESSAGE->headers->priority;
1690     else
1691         $sel = 0;
4e17e6 1692
5e3034 1693     $out = $form_start ? "$form_start\n" : '';
ddc161 1694     $out .= $selector->show((int) $sel);
5e3034 1695     $out .= $form_end ? "\n$form_end" : '';
8958d0 1696
5e3034 1697     return $out;
4315b0 1698 }
4e17e6 1699
T 1700
f65021 1701 function rcmail_mdn_checkbox($attrib)
4315b0 1702 {
1414e3 1703     global $RCMAIL, $MESSAGE;
8958d0 1704
5e3034 1705     list($form_start, $form_end) = get_form_tags($attrib);
AM 1706     unset($attrib['form']);
8958d0 1707
5e3034 1708     if (!isset($attrib['id']))
AM 1709         $attrib['id'] = 'receipt';
620439 1710
f65021 1711     $attrib['name']  = '_mdn';
5e3034 1712     $attrib['value'] = '1';
620439 1713
5e3034 1714     $checkbox = new html_checkbox($attrib);
b3660b 1715
f65021 1716     if (isset($_POST['_mdn']))
AM 1717         $mdn_default = $_POST['_mdn'];
1414e3 1718     else if (in_array($COMPOSE['mode'], array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT)))
5e3034 1719         $mdn_default = (bool) $MESSAGE->headers->mdn_to;
AM 1720     else
1721         $mdn_default = $RCMAIL->config->get('mdn_default');
620439 1722
5e3034 1723     $out = $form_start ? "$form_start\n" : '';
AM 1724     $out .= $checkbox->show($mdn_default);
1725     $out .= $form_end ? "\n$form_end" : '';
1726
1727     return $out;
4315b0 1728 }
620439 1729
T 1730
f22ea7 1731 function rcmail_dsn_checkbox($attrib)
A 1732 {
5e3034 1733     global $RCMAIL;
f22ea7 1734
5e3034 1735     list($form_start, $form_end) = get_form_tags($attrib);
AM 1736     unset($attrib['form']);
f22ea7 1737
5e3034 1738     if (!isset($attrib['id']))
AM 1739         $attrib['id'] = 'dsn';
f22ea7 1740
5e3034 1741     $attrib['name']  = '_dsn';
AM 1742     $attrib['value'] = '1';
f22ea7 1743
5e3034 1744     $checkbox = new html_checkbox($attrib);
271efe 1745
5e3034 1746     if (isset($_POST['_dsn']))
AM 1747         $dsn_value = (int) $_POST['_dsn'];
1748     else
1749         $dsn_value = $RCMAIL->config->get('dsn_default');
f22ea7 1750
5e3034 1751     $out = $form_start ? "$form_start\n" : '';
AM 1752     $out .= $checkbox->show($dsn_value);
1753     $out .= $form_end ? "\n$form_end" : '';
1754
1755     return $out;
f22ea7 1756 }
A 1757
1758
a0109c 1759 function rcmail_editor_selector($attrib)
S 1760 {
5e3034 1761     global $RCMAIL;
6b2b2e 1762
5e3034 1763     // determine whether HTML or plain text should be checked
AM 1764     $useHtml = rcmail_compose_editor_mode();
d9344f 1765
5e3034 1766     if (empty($attrib['editorid']))
AM 1767         $attrib['editorid'] = 'rcmComposeBody';
79af0b 1768
5e3034 1769     if (empty($attrib['name']))
AM 1770         $attrib['name'] = 'editorSelect';
8958d0 1771
646b64 1772     $attrib['onchange'] = "return rcmail.command('toggle-editor', {id: '".$attrib['editorid']."', html: this.value == 'html'}, '', event)";
309d2f 1773
5e3034 1774     $select = new html_select($attrib);
309d2f 1775
5e3034 1776     $select->add(rcube::Q($RCMAIL->gettext('htmltoggle')), 'html');
AM 1777     $select->add(rcube::Q($RCMAIL->gettext('plaintoggle')), 'plain');
309d2f 1778
5e3034 1779     return $select->show($useHtml ? 'html' : 'plain');
a0109c 1780 }
S 1781
1782
faf876 1783 function rcmail_store_target_selection($attrib)
T 1784 {
5e3034 1785     global $COMPOSE, $RCMAIL;
72ff6a 1786
5e3034 1787     $attrib['name'] = '_store_target';
AM 1788     $select = $RCMAIL->folder_selector(array_merge($attrib, array(
1789         'noselection'   => '- ' . $RCMAIL->gettext('dontsave') . ' -',
1790         'folder_filter' => 'mail',
1791         'folder_rights' => 'w',
1792     )));
1793
1794     return $select->show(isset($_POST['_store_target']) ? $_POST['_store_target'] : $COMPOSE['param']['sent_mbox'], $attrib);
faf876 1795 }
T 1796
1797
eeb85f 1798 function rcmail_check_sent_folder($folder, $create=false)
A 1799 {
5e3034 1800     global $RCMAIL;
eeb85f 1801
5e3034 1802     // we'll not save the message, so it doesn't matter
AM 1803     if ($RCMAIL->config->get('no_save_sent_messages')) {
1804         return true;
1805     }
e04e31 1806
5e3034 1807     if ($RCMAIL->storage->folder_exists($folder, true)) {
AM 1808         return true;
1809     }
eeb85f 1810
5e3034 1811     // folder may exist but isn't subscribed (#1485241)
AM 1812     if ($create) {
1813         if (!$RCMAIL->storage->folder_exists($folder))
1814             return $RCMAIL->storage->create_folder($folder, true);
1815         else
1816             return $RCMAIL->storage->subscribe($folder);
1817     }
eeb85f 1818
5e3034 1819     return false;
eeb85f 1820 }
A 1821
1822
4e17e6 1823 function get_form_tags($attrib)
4315b0 1824 {
5e3034 1825     global $RCMAIL, $MESSAGE_FORM, $COMPOSE;
4e17e6 1826
5e3034 1827     $form_start = '';
AM 1828     if (!$MESSAGE_FORM) {
1829         $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $RCMAIL->task));
1830         $hiddenfields->add(array('name' => '_action', 'value' => 'send'));
1831         $hiddenfields->add(array('name' => '_id', 'value' => $COMPOSE['id']));
1832         $hiddenfields->add(array('name' => '_attachments'));
1966c5 1833
5e3034 1834         $form_start = empty($attrib['form']) ? $RCMAIL->output->form_tag(array('name' => "form", 'method' => "post")) : '';
AM 1835         $form_start .= $hiddenfields->show();
1836     }
eeb85f 1837
827159 1838     $form_end  = ($MESSAGE_FORM && !strlen($attrib['form'])) ? '</form>' : '';
AM 1839     $form_name = $attrib['form'] ?: 'form';
eeb85f 1840
5e3034 1841     if (!$MESSAGE_FORM)
AM 1842         $RCMAIL->output->add_gui_object('messageform', $form_name);
eeb85f 1843
5e3034 1844     $MESSAGE_FORM = $form_name;
4e17e6 1845
5e3034 1846     return array($form_start, $form_end);
4315b0 1847 }
4e17e6 1848
T 1849
635722 1850 function rcmail_addressbook_list($attrib = array())
eeb73c 1851 {
T 1852     global $RCMAIL, $OUTPUT;
1853
1854     $attrib += array('id' => 'rcmdirectorylist');
1855
1856     $out = '';
1857     $line_templ = html::tag('li', array(
1858         'id' => 'rcmli%s', 'class' => '%s'),
1859         html::a(array('href' => '#list',
1860             'rel' => '%s',
d686b5 1861             'onclick' => "return ".rcmail_output::JS_OBJECT_NAME.".command('list-addresses','%s',this)"), '%s'));
eeb73c 1862
65dff8 1863     foreach ($RCMAIL->get_address_sources(false, true) as $j => $source) {
eeb73c 1864         $id = strval(strlen($source['id']) ? $source['id'] : $j);
6b2b2e 1865         $js_id = rcube::JQ($id);
eeb73c 1866
T 1867         // set class name(s)
1868         $class_name = 'addressbook';
1869         if ($source['class_name'])
1870             $class_name .= ' ' . $source['class_name'];
1871
1872         $out .= sprintf($line_templ,
6b2b2e 1873             rcube_utils::html_identifier($id,true),
eeb73c 1874             $class_name,
T 1875             $source['id'],
827159 1876             $js_id, ($source['name'] ?: $id));
eeb73c 1877     }
T 1878
635722 1879     $OUTPUT->add_gui_object('addressbookslist', $attrib['id']);
eeb73c 1880
T 1881     return html::tag('ul', $attrib, $out, html::$common_attrib);
1882 }
1883
1884 // return the contacts list as HTML table
1885 function rcmail_contacts_list($attrib = array())
1886 {
6b2b2e 1887     global $RCMAIL, $OUTPUT;
eeb73c 1888
T 1889     $attrib += array('id' => 'rcmAddressList');
1890
1891     // set client env
1892     $OUTPUT->add_gui_object('contactslist', $attrib['id']);
1893     $OUTPUT->set_env('pagecount', 0);
1894     $OUTPUT->set_env('current_page', 0);
1895     $OUTPUT->include_script('list.js');
1896
6b2b2e 1897     return $RCMAIL->table_output($attrib, array(), array('name'), 'ID');
eeb73c 1898 }
T 1899
1900
ae6d2d 1901 /**
TB 1902  * Register a certain container as active area to drop files onto
1903  */
1904 function compose_file_drop_area($attrib)
1905 {
1906     global $OUTPUT;
1907
1908     if ($attrib['id']) {
1909         $OUTPUT->add_gui_object('filedrop', $attrib['id']);
1910         $OUTPUT->set_env('filedrop', array('action' => 'upload', 'fieldname' => '_attachments'));
1911     }
1912 }
1913
eeb73c 1914
0b1de8 1915 /**
TB 1916  *
1917  */
1918 function rcmail_compose_responses_list($attrib)
1919 {
1920     global $RCMAIL, $OUTPUT;
1921
1922     $attrib += array('id' => 'rcmresponseslist', 'tagname' => 'ul', 'cols' => 1);
1923
1924     $jsenv = array();
0ce212 1925     $list = new html_table($attrib);
TB 1926     foreach ($RCMAIL->get_compose_responses(true) as $response) {
1927         $key = $response['key'];
1928         $item = html::a(array(
b2992d 1929             'href' => '#'.urlencode($response['name']),
0b1de8 1930             'class' => rtrim('insertresponse ' . $attrib['itemclass']),
0933d6 1931             'unselectable' => 'on',
b2992d 1932             'tabindex' => '0',
0b1de8 1933             'rel' => $key,
6b2b2e 1934         ), rcube::Q($response['name']));
0b1de8 1935
TB 1936         $jsenv[$key] = $response;
1937         $list->add(array(), $item);
1938     }
1939
1940     // set client env
1941     $OUTPUT->set_env('textresponses', $jsenv);
1942     $OUTPUT->add_gui_object('responseslist', $attrib['id']);
1943
1944     return $list->show();
1945 }