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