Aleksander Machniak
2016-05-01 013aaeb895d3ec8f6758db4c2521599404ad22c7
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         // soft-wrap and quote message text
1062         $body = rcmail_wrap_and_quote($body, $LINE_LENGTH);
4e17e6 1063
5e3034 1064         $prefix .= "\n";
9f9664 1065
0b96b1 1066         if ($reply_mode > 0) { // top-posting
5e3034 1067             $prefix = "\n\n\n" . $prefix;
0b96b1 1068             $suffix = '';
AM 1069         }
1070         else {
1071             $suffix = "\n";
5e3034 1072         }
50f56d 1073     }
A 1074     else {
5e3034 1075         // save inline images to files
AM 1076         $cid_map = rcmail_write_inline_attachments($MESSAGE);
1077         // set is_safe flag (we need this for html body washing)
1078         rcmail_check_safe($MESSAGE);
1079         // clean up html tags
1080         $body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
a0109c 1081
5e3034 1082         // build reply (quote content)
AM 1083         $prefix = '<p>' . rcube::Q($prefix) . "</p>\n";
1084         $prefix .= '<blockquote>';
1085
0b96b1 1086         if ($reply_mode > 0) { // top-posting
5e3034 1087             $prefix = '<br>' . $prefix;
AM 1088             $suffix = '</blockquote>';
1089         }
1090         else {
c4daf3 1091             $suffix = '</blockquote><p><br/></p>';
5e3034 1092         }
AM 1093     }
1094
1095     return $prefix . $body . $suffix;
4315b0 1096 }
4e17e6 1097
3167e5 1098 function rcmail_get_reply_header($message)
TB 1099 {
1100     global $RCMAIL;
1101
1102     $from = array_pop(rcube_mime::decode_address_list($message->get_header('from'), 1, false, $message->headers->charset));
1103     return $RCMAIL->gettext(array(
1104         'name' => 'mailreplyintro',
1105         'vars' => array(
1106             'date'   => $RCMAIL->format_date($message->headers->date, $RCMAIL->config->get('date_long')),
1107             'sender' => $from['name'] ?: rcube_utils::idn_to_utf8($from['mailto']),
1108         )
1109     ));
1110 }
4e17e6 1111
a0109c 1112 function rcmail_create_forward_body($body, $bodyIsHtml)
4315b0 1113 {
5e3034 1114     global $RCMAIL, $MESSAGE, $COMPOSE;
ec603f 1115
5e3034 1116     // add attachments
AM 1117     if (!isset($COMPOSE['forward_attachments']) && is_array($MESSAGE->mime_parts)) {
1118         $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
1119     }
4e17e6 1120
5e3034 1121     $date = $RCMAIL->format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long'));
f5c108 1122
5e3034 1123     if (!$bodyIsHtml) {
AM 1124         $prefix = "\n\n\n-------- " . $RCMAIL->gettext('originalmessage') . " --------\n";
1125         $prefix .= $RCMAIL->gettext('subject') . ': ' . $MESSAGE->subject . "\n";
1126         $prefix .= $RCMAIL->gettext('date')    . ': ' . $date . "\n";
1127         $prefix .= $RCMAIL->gettext('from')    . ': ' . $MESSAGE->get_header('from') . "\n";
1128         $prefix .= $RCMAIL->gettext('to')      . ': ' . $MESSAGE->get_header('to') . "\n";
a4cf45 1129
5e3034 1130         if ($cc = $MESSAGE->headers->get('cc')) {
AM 1131             $prefix .= $RCMAIL->gettext('cc') . ': ' . $cc . "\n";
1132         }
1133         if (($replyto = $MESSAGE->headers->get('reply-to')) && $replyto != $MESSAGE->get_header('from')) {
1134             $prefix .= $RCMAIL->gettext('replyto') . ': ' . $replyto . "\n";
1135         }
a4cf45 1136
5e3034 1137         $prefix .= "\n";
AM 1138         $body = trim($body, "\r\n");
1139     }
1140     else {
1141         // set is_safe flag (we need this for html body washing)
1142         rcmail_check_safe($MESSAGE);
ec603f 1143
5e3034 1144         // clean up html tags
AM 1145         $body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
5758b9 1146
5e3034 1147         $prefix = sprintf(
AM 1148             "<br /><p>-------- " . $RCMAIL->gettext('originalmessage') . " --------</p>" .
1149             "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" .
1150             "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
1151             "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
1152             "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
1153             "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
1154             $RCMAIL->gettext('subject'), rcube::Q($MESSAGE->subject),
1155             $RCMAIL->gettext('date'), rcube::Q($date),
1156             $RCMAIL->gettext('from'), rcube::Q($MESSAGE->get_header('from'), 'replace'),
1157             $RCMAIL->gettext('to'), rcube::Q($MESSAGE->get_header('to'), 'replace'));
a4cf45 1158
5e3034 1159         if ($cc = $MESSAGE->headers->get('cc'))
AM 1160             $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
1161                 $RCMAIL->gettext('cc'), rcube::Q($cc, 'replace'));
5758b9 1162
5e3034 1163         if (($replyto = $MESSAGE->headers->get('reply-to')) && $replyto != $MESSAGE->get_header('from'))
AM 1164             $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
1165                 $RCMAIL->gettext('replyto'), rcube::Q($replyto, 'replace'));
f52c4f 1166
5e3034 1167         $prefix .= "</tbody></table><br>";
AM 1168     }
1169
1170     return $prefix . $body;
4315b0 1171 }
4e17e6 1172
8d4bcd 1173
a0109c 1174 function rcmail_create_draft_body($body, $bodyIsHtml)
4315b0 1175 {
5e3034 1176     global $MESSAGE, $COMPOSE;
f52c4f 1177
5e3034 1178     // add attachments
AM 1179     // sizeof($MESSAGE->mime_parts can be 1 - e.g. attachment, but no text!
1180     if (empty($COMPOSE['forward_attachments'])
1181         && is_array($MESSAGE->mime_parts)
1182         && count($MESSAGE->mime_parts) > 0
1183     ) {
1184         $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
f2a9a9 1185     }
f52c4f 1186
5e3034 1187     // clean up HTML tags - XSS prevention (#1489251)
AM 1188     if ($bodyIsHtml) {
1189         $body = rcmail_wash_html($body, array('safe' => 1), $cid_map);
1190
d5f6d6 1191         // cleanup
AM 1192         $body = preg_replace(array(
1193                 // remove comments (produced by washtml)
1194                 '/<!--[^>]+-->/',
1195                 // remove <body> tags
1196                 '/<body([^>]*)>/i',
1197                 '/<\/body>/i',
1198                 // convert TinyMCE's empty-line sequence (#1490463)
1199                 '/<p>\xC2\xA0<\/p>/',
1200             ),
1201             array(
1202                 '',
1203                 '',
1204                 '',
1205                 '<p><br /></p>',
1206             ),
1207             $body
1208         );
5e3034 1209
AM 1210         // replace cid with href in inline images links
1211         if (!empty($cid_map)) {
1212             $body = str_replace(array_keys($cid_map), array_values($cid_map), $body);
1213         }
1214     }
1215
1216     return $body;
4315b0 1217 }
ba12c7 1218
A 1219
1220 function rcmail_remove_signature($body)
1221 {
5e3034 1222     global $RCMAIL;
ba12c7 1223
5e3034 1224     $body = str_replace("\r\n", "\n", $body);
AM 1225     $len  = strlen($body);
1226     $sig_max_lines = $RCMAIL->config->get('sig_max_lines', 15);
ba12c7 1227
5e3034 1228     while (($sp = strrpos($body, "-- \n", $sp ? -$len+$sp-1 : 0)) !== false) {
AM 1229         if ($sp == 0 || $body[$sp-1] == "\n") {
1230             // do not touch blocks with more that X lines
1231             if (substr_count($body, "\n", $sp) < $sig_max_lines) {
1232                 $body = substr($body, 0, max(0, $sp-1));
1233             }
1234             break;
1235         }
ba12c7 1236     }
A 1237
5e3034 1238     return $body;
ba12c7 1239 }
A 1240
1241
1bc48e 1242 function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
4315b0 1243 {
1414e3 1244     global $RCMAIL, $COMPOSE;
5503cc 1245
5e3034 1246     $loaded_attachments = array();
AM 1247     foreach ((array)$COMPOSE['attachments'] as $attachment) {
1248         $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment;
8d4bcd 1249     }
cc97ea 1250
5e3034 1251     $cid_map  = array();
AM 1252     $messages = array();
ec603f 1253
3167e5 1254     if ($message->pgp_mime) {
TB 1255         return $cid_map;
1256     }
1257
5e3034 1258     foreach ((array)$message->mime_parts as $pid => $part) {
be3460 1259         if ($part->mimetype == 'message/rfc822') {
AM 1260             $messages[] = $part->mime_id;
1261         }
1262
5e3034 1263         if ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename) {
AM 1264             // skip parts that aren't valid attachments
1265             if ($part->ctype_primary == 'multipart' || $part->mimetype == 'application/ms-tnef') {
1266                 continue;
1267             }
1268
1269             // skip message attachments in reply mode
1414e3 1270             if ($part->ctype_primary == 'message' && $COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) {
5e3034 1271                 continue;
AM 1272             }
1273
1274             // skip inline images when forwarding in text mode
1414e3 1275             if ($part->content_id && $part->disposition == 'inline' && !$bodyIsHtml && $COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD) {
5e3034 1276                 continue;
AM 1277             }
1278
f7f75f 1279             // skip version.txt parts of multipart/encrypted messages
TB 1280             if ($message->pgp_mime && $part->mimetype == 'application/pgp-encrypted' && $part->filename == 'version.txt') {
1281                 continue;
1282             }
1283
be3460 1284             // skip attachments included in message/rfc822 attachment (#1486487, #1490607)
AM 1285             foreach ($messages as $mimeid) {
1286                 if (strpos($part->mime_id, $mimeid . '.') === 0) {
1287                     continue 2;
5e3034 1288                 }
AM 1289             }
1290
1291             if (($attachment = $loaded_attachments[rcmail_attachment_name($part) . $part->mimetype])
d56091 1292                 || ($attachment = rcmail_save_attachment($message, $pid, $COMPOSE['id']))
5e3034 1293             ) {
AM 1294                 if ($bodyIsHtml && ($part->content_id || $part->content_location)) {
1295                     $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s',
1296                         $RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']);
1297
1298                     if ($part->content_id)
1299                         $cid_map['cid:'.$part->content_id] = $url;
1300                     else
1301                         $cid_map[$part->content_location] = $url;
1302                 }
1303             }
1304         }
1305     }
1306
1307     $COMPOSE['forward_attachments'] = true;
1308
1309     return $cid_map;
4315b0 1310 }
4e17e6 1311
T 1312
2f746d 1313 function rcmail_write_inline_attachments(&$message)
A 1314 {
5e3034 1315     global $RCMAIL, $COMPOSE;
ec603f 1316
be3460 1317     $cid_map  = array();
AM 1318     $messages = array();
3167e5 1319
TB 1320     if ($message->pgp_mime) {
1321         return $cid_map;
1322     }
1323
5e3034 1324     foreach ((array)$message->mime_parts as $pid => $part) {
be3460 1325         if ($part->mimetype == 'message/rfc822') {
AM 1326             $messages[] = $part->mime_id;
1327         }
1328
5e3034 1329         if (($part->content_id || $part->content_location) && $part->filename) {
be3460 1330             // skip attachments included in message/rfc822 attachment (#1486487, #1490607)
AM 1331             foreach ($messages as $mimeid) {
1332                 if (strpos($part->mime_id, $mimeid . '.') === 0) {
1333                     continue 2;
1334                 }
1335             }
1336
d56091 1337             if ($attachment = rcmail_save_attachment($message, $pid, $COMPOSE['id'])) {
5e3034 1338                 $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s',
AM 1339                     $RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']);
1340
1341                 if ($part->content_id)
1342                     $cid_map['cid:'.$part->content_id] = $url;
1343                 else
1344                     $cid_map[$part->content_location] = $url;
1345             }
1346         }
2f746d 1347     }
8f2b46 1348
5e3034 1349     return $cid_map;
2f746d 1350 }
A 1351
d9f109 1352 // Creates attachment(s) from the forwarded message(s)
AM 1353 function rcmail_write_forward_attachments()
a208a4 1354 {
5e3034 1355     global $RCMAIL, $COMPOSE, $MESSAGE;
a208a4 1356
5e3034 1357     $storage = $RCMAIL->get_storage();
AM 1358     $names   = array();
73076d 1359     $refs    = array();
d9f109 1360
3167e5 1361     if ($MESSAGE->pgp_mime) {
TB 1362         return;
1363     }
1364
5e3034 1365     $loaded_attachments = array();
AM 1366     foreach ((array)$COMPOSE['attachments'] as $attachment) {
1367         $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment;
d9f109 1368     }
AM 1369
5e3034 1370     if ($COMPOSE['forward_uid'] == '*') {
AM 1371         $index = $storage->index(null, rcmail_sort_column(), rcmail_sort_order());
1372         $COMPOSE['forward_uid'] = $index->get();
d9f109 1373     }
aafbe8 1374     else if (!is_array($COMPOSE['forward_uid']) && strpos($COMPOSE['forward_uid'], ':')) {
03de13 1375         $COMPOSE['forward_uid'] = rcube_imap_generic::uncompressMessageSet($COMPOSE['forward_uid']);
AM 1376     }
aafbe8 1377     else if (is_string($COMPOSE['forward_uid'])) {
5e3034 1378         $COMPOSE['forward_uid'] = explode(',', $COMPOSE['forward_uid']);
d9f109 1379     }
AM 1380
5e3034 1381     foreach ((array)$COMPOSE['forward_uid'] as $uid) {
AM 1382         $message = new rcube_message($uid);
d9f109 1383
5e3034 1384         if (empty($message->headers)) {
AM 1385             continue;
1386         }
d9f109 1387
5e3034 1388         if (!empty($message->headers->charset)) {
AM 1389             $storage->set_charset($message->headers->charset);
1390         }
1391
1392         if (empty($MESSAGE->subject)) {
1393             $MESSAGE->subject = $message->subject;
1394         }
1395
1396         // generate (unique) attachment name
1397         $name = strlen($message->subject) ? mb_substr($message->subject, 0, 64) : 'message_rfc822';
1398         if (!empty($names[$name])) {
1399             $names[$name]++;
1400             $name .= '_' . $names[$name];
1401         }
1402         $names[$name] = 1;
1403         $name .= '.eml';
1404
1405         if (!empty($loaded_attachments[$name . 'message/rfc822'])) {
1406             continue;
1407         }
1408
d56091 1409         rcmail_save_attachment($message, null, $COMPOSE['id'], array('filename' => $name));
73076d 1410
AM 1411         if ($message->headers->messageID) {
1412             $refs[] = $message->headers->messageID;
1413         }
1414     }
1415
1416     // set In-Reply-To and References headers
1417     if (count($refs) == 1) {
1418         $COMPOSE['reply_msgid'] = $refs[0];
1419     }
1420     if (!empty($refs)) {
1421         $COMPOSE['references'] = implode(' ', $refs);
d9f109 1422     }
2f746d 1423 }
A 1424
c6efcf 1425 function rcmail_save_image($path, $mimetype = '', $data = null)
b575fa 1426 {
5e3034 1427     global $COMPOSE;
72ff6a 1428
5e3034 1429     // handle attachments in memory
c6efcf 1430     if (empty($data)) {
AM 1431         $data    = file_get_contents($path);
1432         $is_file = true;
1433     }
1434
5e3034 1435     $name = rcmail_basename($path);
c6efcf 1436
AM 1437     if (empty($mimetype)) {
1438         if ($is_file) {
1439             $mimetype = rcube_mime::file_content_type($path, $name);
1440         }
1441         else {
1442             $mimetype = rcube_mime::file_content_type($data, $name, 'application/octet-stream', true);
1443         }
1444     }
b575fa 1445
5e3034 1446     $attachment = array(
AM 1447         'group'    => $COMPOSE['id'],
1448         'name'     => $name,
c6efcf 1449         'mimetype' => $mimetype,
5e3034 1450         'data'     => $data,
AM 1451         'size'     => strlen($data),
1452     );
b575fa 1453
5e3034 1454     $attachment = rcmail::get_instance()->plugins->exec_hook('attachment_save', $attachment);
b575fa 1455
5e3034 1456     if ($attachment['status']) {
AM 1457         unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
1458         return $attachment;
1459     }
f52c4f 1460
5e3034 1461     return false;
b575fa 1462 }
A 1463
1464 function rcmail_basename($filename)
1465 {
5e3034 1466     // basename() is not unicode safe and locale dependent
AM 1467     if (stristr(PHP_OS, 'win') || stristr(PHP_OS, 'netware')) {
1468         return preg_replace('/^.*[\\\\\\/]/', '', $filename);
1469     }
1470     else {
1471         return preg_replace('/^.*[\/]/', '', $filename);
1472     }
b575fa 1473 }
2f746d 1474
60ab55 1475 /**
AM 1476  * Creates reply subject by removing common subject
1477  * prefixes/suffixes from the original message subject
1478  */
1479 function rcmail_reply_subject($subject)
1480 {
1481     $subject = trim($subject);
1482
1483     // replace Re:, Re[x]:, Re-x (#1490497)
1484     $prefix = '/^(re:|re\[\d\]:|re-\d:)\s*/i';
1485     do {
1486         $subject = preg_replace($prefix, '', $subject, -1, $count);
1487     }
1488     while ($count);
1489
1490     // replace (was: ...) (#1489375)
1491     $subject = preg_replace('/\s*\([wW]as:[^\)]+\)\s*$/', '', $subject);
1492
1493     return 'Re: ' . $subject;
1494 }
1495
4e17e6 1496 function rcmail_compose_subject($attrib)
4315b0 1497 {
1414e3 1498     global $MESSAGE, $COMPOSE;
72ff6a 1499
5e3034 1500     list($form_start, $form_end) = get_form_tags($attrib);
AM 1501     unset($attrib['form']);
72ff6a 1502
5e3034 1503     $attrib['name']       = '_subject';
AM 1504     $attrib['spellcheck'] = 'true';
4e17e6 1505
5e3034 1506     $textfield = new html_inputfield($attrib);
AM 1507     $subject   = '';
4e17e6 1508
5e3034 1509     // use subject from post
AM 1510     if (isset($_POST['_subject'])) {
1511         $subject = rcube_utils::get_input_value('_subject', rcube_utils::INPUT_POST, TRUE);
1512     }
968601 1513     else if (!empty($COMPOSE['param']['subject'])) {
AM 1514         $subject = $COMPOSE['param']['subject'];
1515     }
5e3034 1516     // create a reply-subject
1414e3 1517     else if ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) {
60ab55 1518         $subject = rcmail_reply_subject($MESSAGE->subject);
5e3034 1519     }
AM 1520     // create a forward-subject
1414e3 1521     else if ($COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD) {
5e3034 1522         if (preg_match('/^fwd:/i', $MESSAGE->subject))
AM 1523             $subject = $MESSAGE->subject;
1524         else
1525             $subject = 'Fwd: '.$MESSAGE->subject;
1526     }
1527     // creeate a draft-subject
1414e3 1528     else if ($COMPOSE['mode'] == RCUBE_COMPOSE_DRAFT || $COMPOSE['mode'] == RCUBE_COMPOSE_EDIT) {
5e3034 1529         $subject = $MESSAGE->subject;
AM 1530     }
72ff6a 1531
5e3034 1532     $out = $form_start ? "$form_start\n" : '';
AM 1533     $out .= $textfield->show($subject);
1534     $out .= $form_end ? "\n$form_end" : '';
e99991 1535
5e3034 1536     return $out;
4315b0 1537 }
4e17e6 1538
T 1539 function rcmail_compose_attachment_list($attrib)
4315b0 1540 {
f5d2ee 1541     global $RCMAIL, $OUTPUT, $COMPOSE;
72ff6a 1542
5e3034 1543     // add ID if not given
AM 1544     if (!$attrib['id'])
1545         $attrib['id'] = 'rcmAttachmentList';
72ff6a 1546
8ccfc2 1547     $out    = "\n";
AM 1548     $jslist = array();
1549     $button = '';
087c7d 1550
5e3034 1551     if (is_array($COMPOSE['attachments'])) {
AM 1552         if ($attrib['deleteicon']) {
1553             $button = html::img(array(
8ccfc2 1554                 'src' => $RCMAIL->output->abs_url($attrib['deleteicon'], true),
5e3034 1555                 'alt' => $RCMAIL->gettext('delete')
AM 1556             ));
1557         }
1558         else if (rcube_utils::get_boolean($attrib['textbuttons'])) {
1559             $button = rcube::Q($RCMAIL->gettext('delete'));
1560         }
1561
1562         foreach ($COMPOSE['attachments'] as $id => $a_prop) {
1563             if (empty($a_prop)) {
1564                 continue;
1565             }
1566
bb1ed2 1567             $content = sprintf('%s <span class="attachment-size">(%s)</span>',
AM 1568                 rcube::Q($a_prop['name']), $RCMAIL->show_bytes($a_prop['size']));
1569
5e3034 1570             $out .= html::tag('li', array(
AM 1571                     'id'          => 'rcmfile'.$id,
1572                     'class'       => rcube_utils::file2class($a_prop['mimetype'], $a_prop['name']),
1573                     'onmouseover' => "rcube_webmail.long_subject_title_ex(this, 0)",
1574                 ),
1575                 html::a(array(
1576                         'href'    => "#delete",
1577                         'title'   => $RCMAIL->gettext('delete'),
1578                         'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", rcmail_output::JS_OBJECT_NAME, $id),
9240c9 1579                         'class'   => 'delete',
TB 1580                         'tabindex' => $attrib['tabindex'] ?: '0',
1581                         'aria-label'   => $RCMAIL->gettext('delete') . ' ' . $a_prop['name'],
5e3034 1582                     ),
AM 1583                     $button
bb1ed2 1584                 ) . $content
5e3034 1585             );
AM 1586
1587             $jslist['rcmfile'.$id] = array(
1588                 'name'     => $a_prop['name'],
1589                 'complete' => true,
1590                 'mimetype' => $a_prop['mimetype']
1591             );
1592         }
2efe33 1593     }
a894ba 1594
5e3034 1595     if ($attrib['deleteicon'])
8ccfc2 1596         $COMPOSE['deleteicon'] = $RCMAIL->output->abs_url($attrib['deleteicon'], true);
5e3034 1597     else if (rcube_utils::get_boolean($attrib['textbuttons']))
AM 1598         $COMPOSE['textbuttons'] = true;
1599     if ($attrib['cancelicon'])
8ccfc2 1600         $OUTPUT->set_env('cancelicon', $RCMAIL->output->abs_url($attrib['cancelicon'], true));
5e3034 1601     if ($attrib['loadingicon'])
8ccfc2 1602         $OUTPUT->set_env('loadingicon', $RCMAIL->output->abs_url($attrib['loadingicon'], true));
72ff6a 1603
5e3034 1604     $OUTPUT->set_env('attachments', $jslist);
AM 1605     $OUTPUT->add_gui_object('attachmentlist', $attrib['id']);
7152f5 1606
9240c9 1607     // put tabindex value into data-tabindex attribute
TB 1608     if (isset($attrib['tabindex'])) {
1609         $attrib['data-tabindex'] = $attrib['tabindex'];
1610         unset($attrib['tabindex']);
1611     }
1612
5e3034 1613     return html::tag('ul', $attrib, $out, html::$common_attrib);
4315b0 1614 }
4e17e6 1615
T 1616
1617 function rcmail_compose_attachment_form($attrib)
4315b0 1618 {
5e3034 1619     global $OUTPUT, $RCMAIL;
4e17e6 1620
5e3034 1621     // set defaults
AM 1622     $attrib += array('id' => 'rcmUploadbox', 'buttons' => 'yes');
7df0e3 1623
5e3034 1624     // Get filesize, enable upload progress bar
AM 1625     $max_filesize = $RCMAIL->upload_init();
4171c5 1626
5e3034 1627     $button  = new html_inputfield(array('type' => 'button'));
AM 1628     $content = html::div(null, rcmail_compose_attachment_field())
1629         . html::div('hint', $RCMAIL->gettext(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize))));
fe0cb6 1630
5e3034 1631     if (rcube_utils::get_boolean($attrib['buttons'])) {
AM 1632         $content .= html::div('buttons',
1633             $button->show($RCMAIL->gettext('close'), array('class' => 'button', 'onclick' => "$('#$attrib[id]').hide()")) . ' ' .
1634             $button->show($RCMAIL->gettext('upload'), array('class' => 'button mainaction', 'onclick' => rcmail_output::JS_OBJECT_NAME . ".command('send-attachment', this.form)"))
1635         );
1636     }
fe0cb6 1637
5e3034 1638     $out = html::div($attrib, $OUTPUT->form_tag(array(
AM 1639             'id'      => $attrib['id'] . 'Frm',
1640             'name'    => 'uploadform',
1641             'method'  => 'post',
1642             'enctype' => 'multipart/form-data'
1643         ), $content
1644     ));
1645
1646     $OUTPUT->add_gui_object('uploadform', $attrib['id'] . 'Frm');
1647
1648     return $out;
4315b0 1649 }
4e17e6 1650
T 1651
5a8ee3 1652 function rcmail_compose_attachment_field($attrib = array())
4315b0 1653 {
5e3034 1654     $attrib['type']     = 'file';
AM 1655     $attrib['name']     = '_attachments[]';
1656     $attrib['multiple'] = 'multiple';
7fc056 1657
5e3034 1658     $field = new html_inputfield($attrib);
AM 1659
1660     return $field->show();
4315b0 1661 }
4e17e6 1662
66e2bf 1663
4e17e6 1664 function rcmail_priority_selector($attrib)
4315b0 1665 {
5e3034 1666     global $RCMAIL, $MESSAGE;
7152f5 1667
5e3034 1668     list($form_start, $form_end) = get_form_tags($attrib);
AM 1669     unset($attrib['form']);
8958d0 1670
5e3034 1671     $attrib['name'] = '_priority';
AM 1672     $prio_list = array(
1673         $RCMAIL->gettext('lowest')  => 5,
1674         $RCMAIL->gettext('low')     => 4,
1675         $RCMAIL->gettext('normal')  => 0,
1676         $RCMAIL->gettext('high')    => 2,
1677         $RCMAIL->gettext('highest') => 1,
1678     );
4e17e6 1679
5e3034 1680     $selector = new html_select($attrib);
AM 1681     $selector->add(array_keys($prio_list), array_values($prio_list));
8958d0 1682
5e3034 1683     if (isset($_POST['_priority']))
AM 1684         $sel = $_POST['_priority'];
1685     else if (isset($MESSAGE->headers->priority) && intval($MESSAGE->headers->priority) != 3)
1686         $sel = $MESSAGE->headers->priority;
1687     else
1688         $sel = 0;
4e17e6 1689
5e3034 1690     $out = $form_start ? "$form_start\n" : '';
ddc161 1691     $out .= $selector->show((int) $sel);
5e3034 1692     $out .= $form_end ? "\n$form_end" : '';
8958d0 1693
5e3034 1694     return $out;
4315b0 1695 }
4e17e6 1696
T 1697
f65021 1698 function rcmail_mdn_checkbox($attrib)
4315b0 1699 {
1414e3 1700     global $RCMAIL, $MESSAGE;
8958d0 1701
5e3034 1702     list($form_start, $form_end) = get_form_tags($attrib);
AM 1703     unset($attrib['form']);
8958d0 1704
5e3034 1705     if (!isset($attrib['id']))
AM 1706         $attrib['id'] = 'receipt';
620439 1707
f65021 1708     $attrib['name']  = '_mdn';
5e3034 1709     $attrib['value'] = '1';
620439 1710
5e3034 1711     $checkbox = new html_checkbox($attrib);
b3660b 1712
f65021 1713     if (isset($_POST['_mdn']))
AM 1714         $mdn_default = $_POST['_mdn'];
1414e3 1715     else if (in_array($COMPOSE['mode'], array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT)))
5e3034 1716         $mdn_default = (bool) $MESSAGE->headers->mdn_to;
AM 1717     else
1718         $mdn_default = $RCMAIL->config->get('mdn_default');
620439 1719
5e3034 1720     $out = $form_start ? "$form_start\n" : '';
AM 1721     $out .= $checkbox->show($mdn_default);
1722     $out .= $form_end ? "\n$form_end" : '';
1723
1724     return $out;
4315b0 1725 }
620439 1726
T 1727
f22ea7 1728 function rcmail_dsn_checkbox($attrib)
A 1729 {
5e3034 1730     global $RCMAIL;
f22ea7 1731
5e3034 1732     list($form_start, $form_end) = get_form_tags($attrib);
AM 1733     unset($attrib['form']);
f22ea7 1734
5e3034 1735     if (!isset($attrib['id']))
AM 1736         $attrib['id'] = 'dsn';
f22ea7 1737
5e3034 1738     $attrib['name']  = '_dsn';
AM 1739     $attrib['value'] = '1';
f22ea7 1740
5e3034 1741     $checkbox = new html_checkbox($attrib);
271efe 1742
5e3034 1743     if (isset($_POST['_dsn']))
AM 1744         $dsn_value = (int) $_POST['_dsn'];
1745     else
1746         $dsn_value = $RCMAIL->config->get('dsn_default');
f22ea7 1747
5e3034 1748     $out = $form_start ? "$form_start\n" : '';
AM 1749     $out .= $checkbox->show($dsn_value);
1750     $out .= $form_end ? "\n$form_end" : '';
1751
1752     return $out;
f22ea7 1753 }
A 1754
1755
a0109c 1756 function rcmail_editor_selector($attrib)
S 1757 {
5e3034 1758     global $RCMAIL;
6b2b2e 1759
5e3034 1760     // determine whether HTML or plain text should be checked
AM 1761     $useHtml = rcmail_compose_editor_mode();
d9344f 1762
5e3034 1763     if (empty($attrib['editorid']))
AM 1764         $attrib['editorid'] = 'rcmComposeBody';
79af0b 1765
5e3034 1766     if (empty($attrib['name']))
AM 1767         $attrib['name'] = 'editorSelect';
8958d0 1768
646b64 1769     $attrib['onchange'] = "return rcmail.command('toggle-editor', {id: '".$attrib['editorid']."', html: this.value == 'html'}, '', event)";
309d2f 1770
5e3034 1771     $select = new html_select($attrib);
309d2f 1772
5e3034 1773     $select->add(rcube::Q($RCMAIL->gettext('htmltoggle')), 'html');
AM 1774     $select->add(rcube::Q($RCMAIL->gettext('plaintoggle')), 'plain');
309d2f 1775
5e3034 1776     return $select->show($useHtml ? 'html' : 'plain');
a0109c 1777 }
S 1778
1779
faf876 1780 function rcmail_store_target_selection($attrib)
T 1781 {
5e3034 1782     global $COMPOSE, $RCMAIL;
72ff6a 1783
5e3034 1784     $attrib['name'] = '_store_target';
AM 1785     $select = $RCMAIL->folder_selector(array_merge($attrib, array(
1786         'noselection'   => '- ' . $RCMAIL->gettext('dontsave') . ' -',
1787         'folder_filter' => 'mail',
1788         'folder_rights' => 'w',
1789     )));
1790
1791     return $select->show(isset($_POST['_store_target']) ? $_POST['_store_target'] : $COMPOSE['param']['sent_mbox'], $attrib);
faf876 1792 }
T 1793
1794
eeb85f 1795 function rcmail_check_sent_folder($folder, $create=false)
A 1796 {
5e3034 1797     global $RCMAIL;
eeb85f 1798
5e3034 1799     // we'll not save the message, so it doesn't matter
AM 1800     if ($RCMAIL->config->get('no_save_sent_messages')) {
1801         return true;
1802     }
e04e31 1803
5e3034 1804     if ($RCMAIL->storage->folder_exists($folder, true)) {
AM 1805         return true;
1806     }
eeb85f 1807
5e3034 1808     // folder may exist but isn't subscribed (#1485241)
AM 1809     if ($create) {
1810         if (!$RCMAIL->storage->folder_exists($folder))
1811             return $RCMAIL->storage->create_folder($folder, true);
1812         else
1813             return $RCMAIL->storage->subscribe($folder);
1814     }
eeb85f 1815
5e3034 1816     return false;
eeb85f 1817 }
A 1818
1819
4e17e6 1820 function get_form_tags($attrib)
4315b0 1821 {
5e3034 1822     global $RCMAIL, $MESSAGE_FORM, $COMPOSE;
4e17e6 1823
5e3034 1824     $form_start = '';
AM 1825     if (!$MESSAGE_FORM) {
1826         $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $RCMAIL->task));
1827         $hiddenfields->add(array('name' => '_action', 'value' => 'send'));
1828         $hiddenfields->add(array('name' => '_id', 'value' => $COMPOSE['id']));
1829         $hiddenfields->add(array('name' => '_attachments'));
1966c5 1830
5e3034 1831         $form_start = empty($attrib['form']) ? $RCMAIL->output->form_tag(array('name' => "form", 'method' => "post")) : '';
AM 1832         $form_start .= $hiddenfields->show();
1833     }
eeb85f 1834
827159 1835     $form_end  = ($MESSAGE_FORM && !strlen($attrib['form'])) ? '</form>' : '';
AM 1836     $form_name = $attrib['form'] ?: 'form';
eeb85f 1837
5e3034 1838     if (!$MESSAGE_FORM)
AM 1839         $RCMAIL->output->add_gui_object('messageform', $form_name);
eeb85f 1840
5e3034 1841     $MESSAGE_FORM = $form_name;
4e17e6 1842
5e3034 1843     return array($form_start, $form_end);
4315b0 1844 }
4e17e6 1845
T 1846
635722 1847 function rcmail_addressbook_list($attrib = array())
eeb73c 1848 {
T 1849     global $RCMAIL, $OUTPUT;
1850
1851     $attrib += array('id' => 'rcmdirectorylist');
1852
1853     $out = '';
1854     $line_templ = html::tag('li', array(
1855         'id' => 'rcmli%s', 'class' => '%s'),
1856         html::a(array('href' => '#list',
1857             'rel' => '%s',
d686b5 1858             'onclick' => "return ".rcmail_output::JS_OBJECT_NAME.".command('list-addresses','%s',this)"), '%s'));
eeb73c 1859
65dff8 1860     foreach ($RCMAIL->get_address_sources(false, true) as $j => $source) {
eeb73c 1861         $id = strval(strlen($source['id']) ? $source['id'] : $j);
6b2b2e 1862         $js_id = rcube::JQ($id);
eeb73c 1863
T 1864         // set class name(s)
1865         $class_name = 'addressbook';
1866         if ($source['class_name'])
1867             $class_name .= ' ' . $source['class_name'];
1868
1869         $out .= sprintf($line_templ,
6b2b2e 1870             rcube_utils::html_identifier($id,true),
eeb73c 1871             $class_name,
T 1872             $source['id'],
827159 1873             $js_id, ($source['name'] ?: $id));
eeb73c 1874     }
T 1875
635722 1876     $OUTPUT->add_gui_object('addressbookslist', $attrib['id']);
eeb73c 1877
T 1878     return html::tag('ul', $attrib, $out, html::$common_attrib);
1879 }
1880
1881 // return the contacts list as HTML table
1882 function rcmail_contacts_list($attrib = array())
1883 {
6b2b2e 1884     global $RCMAIL, $OUTPUT;
eeb73c 1885
T 1886     $attrib += array('id' => 'rcmAddressList');
1887
1888     // set client env
1889     $OUTPUT->add_gui_object('contactslist', $attrib['id']);
1890     $OUTPUT->set_env('pagecount', 0);
1891     $OUTPUT->set_env('current_page', 0);
1892     $OUTPUT->include_script('list.js');
1893
6b2b2e 1894     return $RCMAIL->table_output($attrib, array(), array('name'), 'ID');
eeb73c 1895 }
T 1896
1897
ae6d2d 1898 /**
TB 1899  * Register a certain container as active area to drop files onto
1900  */
1901 function compose_file_drop_area($attrib)
1902 {
1903     global $OUTPUT;
1904
1905     if ($attrib['id']) {
1906         $OUTPUT->add_gui_object('filedrop', $attrib['id']);
1907         $OUTPUT->set_env('filedrop', array('action' => 'upload', 'fieldname' => '_attachments'));
1908     }
1909 }
1910
eeb73c 1911
0b1de8 1912 /**
TB 1913  *
1914  */
1915 function rcmail_compose_responses_list($attrib)
1916 {
1917     global $RCMAIL, $OUTPUT;
1918
1919     $attrib += array('id' => 'rcmresponseslist', 'tagname' => 'ul', 'cols' => 1);
1920
1921     $jsenv = array();
0ce212 1922     $list = new html_table($attrib);
TB 1923     foreach ($RCMAIL->get_compose_responses(true) as $response) {
1924         $key = $response['key'];
1925         $item = html::a(array(
b2992d 1926             'href' => '#'.urlencode($response['name']),
0b1de8 1927             'class' => rtrim('insertresponse ' . $attrib['itemclass']),
0933d6 1928             'unselectable' => 'on',
b2992d 1929             'tabindex' => '0',
0b1de8 1930             'rel' => $key,
6b2b2e 1931         ), rcube::Q($response['name']));
0b1de8 1932
TB 1933         $jsenv[$key] = $response;
1934         $list->add(array(), $item);
1935     }
1936
1937     // set client env
1938     $OUTPUT->set_env('textresponses', $jsenv);
1939     $OUTPUT->add_gui_object('responseslist', $attrib['id']);
1940
1941     return $list->show();
1942 }