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