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