source: subversion/branches/release-0.7/program/steps/mail/compose.inc @ 5434

Last change on this file since 5434 was 5434, checked in by alec, 18 months ago
  • Apply fixes from trunk up to r5433
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 47.6 KB
Line 
1<?php
2
3/*
4 +-----------------------------------------------------------------------+
5 | program/steps/mail/compose.inc                                        |
6 |                                                                       |
7 | This file is part of the Roundcube Webmail client                     |
8 | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
9 | Licensed under the GNU GPL                                            |
10 |                                                                       |
11 | PURPOSE:                                                              |
12 |   Compose a new mail message with all headers and attachments         |
13 |                                                                       |
14 +-----------------------------------------------------------------------+
15 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16 +-----------------------------------------------------------------------+
17
18 $Id$
19
20*/
21
22// define constants for message compose mode
23define('RCUBE_COMPOSE_REPLY', 0x0106);
24define('RCUBE_COMPOSE_FORWARD', 0x0107);
25define('RCUBE_COMPOSE_DRAFT', 0x0108);
26define('RCUBE_COMPOSE_EDIT', 0x0109);
27
28$MESSAGE_FORM = NULL;
29$MESSAGE = NULL;
30
31$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GET);
32$_SESSION['compose'] = $_SESSION['compose_data_'.$COMPOSE_ID];
33
34// give replicated session storage some time to synchronize
35$retries = 0;
36while ($COMPOSE_ID && !is_array($_SESSION['compose']) && $RCMAIL->db->is_replicated() && $retries++ < 5) {
37  usleep(500000);
38  $RCMAIL->session->reload();
39  $_SESSION['compose'] = $_SESSION['compose_data_'.$COMPOSE_ID];
40}
41
42// Nothing below is called during message composition, only at "new/forward/reply/draft" initialization or
43// if a compose-ID is given (i.e. when the compose step is opened in a new window/tab).
44if (!is_array($_SESSION['compose']))
45{
46  // Infinite redirect prevention in case of broken session (#1487028)
47  if ($COMPOSE_ID)
48    raise_error(array('code' => 500, 'type' => 'php',
49      'file' => __FILE__, 'line' => __LINE__,
50      'message' => "Invalid compose ID"), true, true);
51
52  $_SESSION['compose'] = array(
53    'id' => uniqid(mt_rand()),
54    'param' => request2param(RCUBE_INPUT_GET),
55    'mailbox' => $IMAP->get_mailbox_name(),
56  );
57
58  // process values like "mailto:foo@bar.com?subject=new+message&cc=another"
59  if ($_SESSION['compose']['param']['to']) {
60    // #1486037: remove "mailto:" prefix
61    $_SESSION['compose']['param']['to'] = preg_replace('/^mailto:/i', '', $_SESSION['compose']['param']['to']);
62    $mailto = explode('?', $_SESSION['compose']['param']['to']);
63    if (count($mailto) > 1) {
64      $_SESSION['compose']['param']['to'] = $mailto[0];
65      parse_str($mailto[1], $query);
66      foreach ($query as $f => $val)
67        $_SESSION['compose']['param'][$f] = $val;
68    }
69  }
70
71  // select folder where to save the sent message
72  $_SESSION['compose']['param']['sent_mbox'] = $RCMAIL->config->get('sent_mbox');
73
74  // pipe compose parameters thru plugins
75  $plugin = $RCMAIL->plugins->exec_hook('message_compose', $_SESSION['compose']);
76  $_SESSION['compose']['param'] = array_merge($_SESSION['compose']['param'], $plugin['param']);
77
78  // add attachments listed by message_compose hook
79  if (is_array($plugin['attachments'])) {
80    foreach ($plugin['attachments'] as $attach) {
81      // we have structured data
82      if (is_array($attach)) {
83        $attachment = $attach;
84      }
85      // only a file path is given
86      else {
87        $filename = basename($attach);
88        $attachment = array(
89          'group' => $COMPOSE_ID,
90          'name' => $filename,
91          'mimetype' => rc_mime_content_type($attach, $filename),
92          'path' => $attach,
93        );
94      }
95
96      // save attachment if valid
97      if (($attachment['data'] && $attachment['name']) || ($attachment['path'] && file_exists($attachment['path']))) {
98        $attachment = rcmail::get_instance()->plugins->exec_hook('attachment_save', $attachment);
99      }
100
101      if ($attachment['status'] && !$attachment['abort']) {
102        unset($attachment['data'], $attachment['status'], $attachment['abort']);
103        $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
104      }
105    }
106  }
107
108  // check if folder for saving sent messages exists and is subscribed (#1486802)
109  if ($sent_folder = $_SESSION['compose']['param']['sent_mbox']) {
110    rcmail_check_sent_folder($sent_folder, true);
111  }
112
113  // redirect to a unique URL with all parameters stored in session
114  $OUTPUT->redirect(array('_action' => 'compose', '_id' => $_SESSION['compose']['id']));
115}
116
117
118// add some labels to client
119$OUTPUT->add_label('nosubject', 'nosenderwarning', 'norecipientwarning', 'nosubjectwarning', 'cancel',
120    'nobodywarning', 'notsentwarning', 'notuploadedwarning', 'savingmessage', 'sendingmessage',
121    'messagesaved', 'converting', 'editorwarning', 'searching', 'uploading', 'uploadingmany',
122    'fileuploaderror');
123
124$OUTPUT->set_env('compose_id', $COMPOSE_ID);
125
126// add config parameters to client script
127if (!empty($CONFIG['drafts_mbox'])) {
128  $OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
129  $OUTPUT->set_env('draft_autosave', $CONFIG['draft_autosave']);
130}
131// set current mailbox in client environment
132$OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name());
133$OUTPUT->set_env('sig_above', $RCMAIL->config->get('sig_above', false));
134$OUTPUT->set_env('top_posting', $RCMAIL->config->get('top_posting', false));
135$OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ',')));
136
137// get reference message and set compose mode
138if ($msg_uid = $_SESSION['compose']['param']['draft_uid']) {
139  $RCMAIL->imap->set_mailbox($CONFIG['drafts_mbox']);
140  $compose_mode = RCUBE_COMPOSE_DRAFT;
141}
142else if ($msg_uid = $_SESSION['compose']['param']['reply_uid'])
143  $compose_mode = RCUBE_COMPOSE_REPLY;
144else if ($msg_uid = $_SESSION['compose']['param']['forward_uid'])
145  $compose_mode = RCUBE_COMPOSE_FORWARD;
146else if ($msg_uid = $_SESSION['compose']['param']['uid'])
147  $compose_mode = RCUBE_COMPOSE_EDIT;
148
149$config_show_sig = $RCMAIL->config->get('show_sig', 1);
150if ($config_show_sig == 1)
151  $OUTPUT->set_env('show_sig', true);
152else if ($config_show_sig == 2 && (empty($compose_mode) || $compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT))
153  $OUTPUT->set_env('show_sig', true);
154else if ($config_show_sig == 3 && ($compose_mode == RCUBE_COMPOSE_REPLY || $compose_mode == RCUBE_COMPOSE_FORWARD))
155  $OUTPUT->set_env('show_sig', true);
156else
157  $OUTPUT->set_env('show_sig', false);
158
159// set line length for body wrapping
160$LINE_LENGTH = $RCMAIL->config->get('line_length', 72);
161
162if (!empty($msg_uid))
163{
164  // similar as in program/steps/mail/show.inc
165  // re-set 'prefer_html' to have possibility to use html part for compose
166  $CONFIG['prefer_html'] = $CONFIG['prefer_html'] || $CONFIG['htmleditor'] || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT;
167  $MESSAGE = new rcube_message($msg_uid);
168
169  // make sure message is marked as read
170  if ($MESSAGE && $MESSAGE->headers && empty($MESSAGE->headers->flags['SEEN']))
171    $IMAP->set_flag($msg_uid, 'SEEN');
172
173  if (!empty($MESSAGE->headers->charset))
174    $IMAP->set_charset($MESSAGE->headers->charset);
175
176  if ($compose_mode == RCUBE_COMPOSE_REPLY)
177  {
178    $_SESSION['compose']['reply_uid'] = $msg_uid;
179    $_SESSION['compose']['reply_msgid'] = $MESSAGE->headers->messageID;
180    $_SESSION['compose']['references']  = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID);
181
182    if (!empty($_SESSION['compose']['param']['all']))
183      $MESSAGE->reply_all = $_SESSION['compose']['param']['all'];
184
185    $OUTPUT->set_env('compose_mode', 'reply');
186
187    // Save the sent message in the same folder of the message being replied to
188    if ($RCMAIL->config->get('reply_same_folder') && ($sent_folder = $_SESSION['compose']['mailbox'])
189      && rcmail_check_sent_folder($sent_folder, false)
190    ) {
191      $_SESSION['compose']['param']['sent_mbox'] = $sent_folder;
192    }
193  }
194  else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
195  {
196    if ($MESSAGE->headers->others['x-draft-info'])
197    {
198      // get reply_uid/forward_uid to flag the original message when sending
199      $info = rcmail_draftinfo_decode($MESSAGE->headers->others['x-draft-info']);
200
201      if ($info['type'] == 'reply')
202        $_SESSION['compose']['reply_uid'] = $info['uid'];
203      else if ($info['type'] == 'forward')
204        $_SESSION['compose']['forward_uid'] = $info['uid'];
205
206      $_SESSION['compose']['mailbox'] = $info['folder'];
207
208      // Save the sent message in the same folder of the message being replied to
209      if ($RCMAIL->config->get('reply_same_folder') && ($sent_folder = $info['folder'])
210        && rcmail_check_sent_folder($sent_folder, false)
211      ) {
212        $_SESSION['compose']['param']['sent_mbox'] = $sent_folder;
213      }
214    }
215
216    if ($MESSAGE->headers->in_reply_to)
217      $_SESSION['compose']['reply_msgid'] = '<'.$MESSAGE->headers->in_reply_to.'>';
218
219    $_SESSION['compose']['references']  = $MESSAGE->headers->references;
220  }
221  else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
222  {
223    $_SESSION['compose']['forward_uid'] = $msg_uid;
224    $OUTPUT->set_env('compose_mode', 'forward');
225
226    if (!empty($_SESSION['compose']['param']['attachment']))
227      $MESSAGE->forward_attachment = true;
228  }
229}
230
231$MESSAGE->compose = array();
232
233// get user's identities
234$MESSAGE->identities = $USER->list_identities();
235if (count($MESSAGE->identities))
236{
237  foreach ($MESSAGE->identities as $idx => $ident) {
238    $email = mb_strtolower(rcube_idn_to_utf8($ident['email']));
239
240    $MESSAGE->identities[$idx]['email_ascii'] = $ident['email'];
241    $MESSAGE->identities[$idx]['ident']       = format_email_recipient($ident['email'], $ident['name']);
242    $MESSAGE->identities[$idx]['email']       = $email;
243  }
244}
245
246// Set From field value
247if (!empty($_POST['_from'])) {
248  $MESSAGE->compose['from'] = get_input_value('_from', RCUBE_INPUT_POST);
249}
250else if (!empty($_SESSION['compose']['param']['from'])) {
251  $MESSAGE->compose['from'] = $_SESSION['compose']['param']['from'];
252}
253else if (count($MESSAGE->identities)) {
254  $a_recipients = array();
255  $a_names      = array();
256
257  // extract all recipients of the reply-message
258  if (is_object($MESSAGE->headers) && in_array($compose_mode, array(RCUBE_COMPOSE_REPLY, RCUBE_COMPOSE_FORWARD)))
259  {
260    $a_to = $IMAP->decode_address_list($MESSAGE->headers->to);
261    foreach ($a_to as $addr) {
262      if (!empty($addr['mailto'])) {
263        $a_recipients[] = strtolower($addr['mailto']);
264        $a_names[]      = $addr['name'];
265      }
266    }
267
268    if (!empty($MESSAGE->headers->cc)) {
269      $a_cc = $IMAP->decode_address_list($MESSAGE->headers->cc);
270      foreach ($a_cc as $addr) {
271        if (!empty($addr['mailto'])) {
272          $a_recipients[] = strtolower($addr['mailto']);
273          $a_names[]      = $addr['name'];
274        }
275      }
276    }
277  }
278
279  $from_idx         = null;
280  $default_identity = null;
281  $return_path      = $MESSAGE->headers->others['return-path'];
282
283  // Select identity
284  foreach ($MESSAGE->identities as $idx => $ident) {
285    // save default identity ID
286    if ($ident['standard']) {
287      $default_identity = $idx;
288    }
289
290    // use From header
291    if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) {
292      if ($MESSAGE->headers->from == $ident['ident']) {
293        $from_idx = $idx;
294        break;
295      }
296    }
297    // reply to yourself
298    else if ($compose_mode == RCUBE_COMPOSE_REPLY && $MESSAGE->headers->from == $ident['ident']) {
299      $from_idx = $idx;
300      break;
301    }
302    // use replied message recipients
303    else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) {
304      // match identity name, prefer default identity
305      if ($from_idx === null || ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name'])) {
306        $from_idx = $idx;
307      }
308    }
309  }
310
311  // Fallback using Return-Path
312  if ($from_idx === null && $return_path) {
313    foreach ($MESSAGE->identities as $idx => $ident) {
314      if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) {
315        $from_idx = $idx;
316        break;
317      }
318    }
319  }
320
321  // Still no ID, use default/first identity
322  if ($from_idx === null) {
323    $from_idx = $default_identity !== null ? $default_identity : key(reset($MESSAGE->identities));
324  }
325
326  $ident   = $MESSAGE->identities[$from_idx];
327  $from_id = $ident['identity_id'];
328
329  $MESSAGE->compose['from_email'] = $ident['email'];
330  $MESSAGE->compose['from']       = $from_id;
331}
332
333// Set other headers
334$a_recipients = array();
335$parts        = array('to', 'cc', 'bcc', 'replyto', 'followupto');
336$separator    = trim($RCMAIL->config->get('recipients_separator', ',')) . ' ';
337
338foreach ($parts as $header) {
339  $fvalue = '';
340  $decode_header = true;
341
342  // we have a set of recipients stored is session
343  if ($header == 'to' && ($mailto_id = $_SESSION['compose']['param']['mailto'])
344      && $_SESSION['mailto'][$mailto_id]
345  ) {
346    $fvalue = urldecode($_SESSION['mailto'][$mailto_id]);
347    $decode_header = false;
348  }
349  else if (!empty($_POST['_'.$header])) {
350    $fvalue = get_input_value('_'.$header, RCUBE_INPUT_POST, TRUE);
351  }
352  else if (!empty($_SESSION['compose']['param'][$header])) {
353    $fvalue = $_SESSION['compose']['param'][$header];
354  }
355  else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
356    // get recipent address(es) out of the message headers
357    if ($header == 'to') {
358      $mailfollowup = $MESSAGE->headers->others['mail-followup-to'];
359      $mailreplyto  = $MESSAGE->headers->others['mail-reply-to'];
360
361      // Reply to mailing list...
362      if ($MESSAGE->reply_all == 'list' && $mailfollowup)
363        $fvalue = $mailfollowup;
364      else if ($MESSAGE->reply_all == 'list'
365        && preg_match('/<mailto:([^>]+)>/i', $MESSAGE->headers->others['list-post'], $m))
366        $fvalue = $m[1];
367      // Reply to...
368      else if ($MESSAGE->reply_all && $mailfollowup)
369        $fvalue = $mailfollowup;
370      else if ($mailreplyto)
371        $fvalue = $mailreplyto;
372      else if (!empty($MESSAGE->headers->replyto))
373        $fvalue = $MESSAGE->headers->replyto;
374      else if (!empty($MESSAGE->headers->from))
375        $fvalue = $MESSAGE->headers->from;
376
377      // Reply to message sent by yourself (#1487074)
378      if (!empty($ident) && $fvalue == $ident['ident']) {
379        $fvalue = $MESSAGE->headers->to;
380      }
381    }
382    // add recipient of original message if reply to all
383    else if ($header == 'cc' && !empty($MESSAGE->reply_all) && $MESSAGE->reply_all != 'list') {
384      if ($v = $MESSAGE->headers->to)
385        $fvalue .= $v;
386      if ($v = $MESSAGE->headers->cc)
387        $fvalue .= (!empty($fvalue) ? $separator : '') . $v;
388    }
389  }
390  else if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) {
391    // get drafted headers
392    if ($header=='to' && !empty($MESSAGE->headers->to))
393      $fvalue = $MESSAGE->get_header('to');
394    else if ($header=='cc' && !empty($MESSAGE->headers->cc))
395      $fvalue = $MESSAGE->get_header('cc');
396    else if ($header=='bcc' && !empty($MESSAGE->headers->bcc))
397      $fvalue = $MESSAGE->get_header('bcc');
398    else if ($header=='replyto' && !empty($MESSAGE->headers->others['mail-reply-to']))
399      $fvalue = $MESSAGE->get_header('mail-reply-to');
400    else if ($header=='replyto' && !empty($MESSAGE->headers->replyto))
401      $fvalue = $MESSAGE->get_header('reply-to');
402    else if ($header=='followupto' && !empty($MESSAGE->headers->others['mail-followup-to']))
403      $fvalue = $MESSAGE->get_header('mail-followup-to');
404  }
405
406  // split recipients and put them back together in a unique way
407  if (!empty($fvalue) && in_array($header, array('to', 'cc', 'bcc'))) {
408    $to_addresses = $IMAP->decode_address_list($fvalue, null, $decode_header);
409    $fvalue = array();
410
411    foreach ($to_addresses as $addr_part) {
412      if (empty($addr_part['mailto']))
413        continue;
414
415      $mailto = mb_strtolower(rcube_idn_to_utf8($addr_part['mailto']));
416
417      if (!in_array($mailto, $a_recipients)
418        && ($header == 'to' || empty($MESSAGE->compose['from_email']) || $mailto != $MESSAGE->compose['from_email'])
419      ) {
420        if ($addr_part['name'] && $addr_part['mailto'] != $addr_part['name'])
421          $string = format_email_recipient($mailto, $addr_part['name']);
422        else
423          $string = $mailto;
424
425        $fvalue[] = $string;
426        $a_recipients[] = $addr_part['mailto'];
427      }
428    }
429
430    $fvalue = implode($separator, $fvalue);
431  }
432
433  $MESSAGE->compose[$header] = $fvalue;
434}
435unset($a_recipients);
436
437// process $MESSAGE body/attachments, set $MESSAGE_BODY/$HTML_MODE vars and some session data
438$MESSAGE_BODY = rcmail_prepare_message_body();
439
440
441/****** compose mode functions ********/
442
443function rcmail_compose_headers($attrib)
444{
445  global $MESSAGE;
446
447  list($form_start, $form_end) = get_form_tags($attrib);
448
449  $out  = '';
450  $part = strtolower($attrib['part']);
451
452  switch ($part)
453  {
454    case 'from':
455      return $form_start . rcmail_compose_header_from($attrib);
456
457    case 'to':
458    case 'cc':
459    case 'bcc':
460      $fname = '_' . $part;
461      $header = $param = $part;
462
463      $allow_attrib = array('id', 'class', 'style', 'cols', 'rows', 'tabindex');
464      $field_type = 'html_textarea';
465      break;
466
467    case 'replyto':
468    case 'reply-to':
469      $fname = '_replyto';
470      $param = 'replyto';
471      $header = 'reply-to';
472
473    case 'followupto':
474    case 'followup-to':
475      if (!$fname) {
476        $fname = '_followupto';
477        $param = 'followupto';
478        $header = 'mail-followup-to';
479      }
480
481      $allow_attrib = array('id', 'class', 'style', 'size', 'tabindex');
482      $field_type = 'html_inputfield';
483      break;
484  }
485
486  if ($fname && $field_type)
487  {
488    // pass the following attributes to the form class
489    $field_attrib = array('name' => $fname, 'spellcheck' => 'false');
490    foreach ($attrib as $attr => $value)
491      if (in_array($attr, $allow_attrib))
492        $field_attrib[$attr] = $value;
493
494    // create teaxtarea object
495    $input = new $field_type($field_attrib);
496    $out = $input->show($MESSAGE->compose[$param]);
497  }
498
499  if ($form_start)
500    $out = $form_start.$out;
501
502  // configure autocompletion
503  rcube_autocomplete_init();
504
505  return $out;
506}
507
508
509function rcmail_compose_header_from($attrib)
510{
511  global $MESSAGE, $OUTPUT;
512
513  // pass the following attributes to the form class
514  $field_attrib = array('name' => '_from');
515  foreach ($attrib as $attr => $value)
516    if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex')))
517      $field_attrib[$attr] = $value;
518
519  if (count($MESSAGE->identities))
520  {
521    $a_signatures = array();
522
523    $field_attrib['onchange'] = JS_OBJECT_NAME.".change_identity(this)";
524    $select_from = new html_select($field_attrib);
525
526    // create SELECT element
527    foreach ($MESSAGE->identities as $sql_arr)
528    {
529      $identity_id = $sql_arr['identity_id'];
530      $select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $identity_id);
531
532      // add signature to array
533      if (!empty($sql_arr['signature']) && empty($_SESSION['compose']['param']['nosig']))
534      {
535        $a_signatures[$identity_id]['text'] = $sql_arr['signature'];
536        $a_signatures[$identity_id]['is_html'] = ($sql_arr['html_signature'] == 1) ? true : false;
537        if ($a_signatures[$identity_id]['is_html'])
538        {
539            $h2t = new html2text($a_signatures[$identity_id]['text'], false, false);
540            $a_signatures[$identity_id]['plain_text'] = trim($h2t->get_text());
541        }
542      }
543    }
544
545    $out = $select_from->show($MESSAGE->compose['from']);
546
547    // add signatures to client
548    $OUTPUT->set_env('signatures', $a_signatures);
549  }
550  // no identities, display text input field
551  else {
552    $field_attrib['class'] = 'from_address';
553    $input_from = new html_inputfield($field_attrib);
554    $out = $input_from->show($MESSAGE->compose['from']);
555  }
556
557  return $out;
558}
559
560
561function rcmail_compose_editor_mode()
562{
563  global $RCMAIL, $MESSAGE, $compose_mode;
564  static $useHtml;
565
566  if ($useHtml !== null)
567    return $useHtml;
568
569  $html_editor = intval($RCMAIL->config->get('htmleditor'));
570
571  if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) {
572    $useHtml = $MESSAGE->has_html_part();
573  }
574  else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
575    $useHtml = ($html_editor == 1 || ($html_editor == 2 && $MESSAGE->has_html_part()));
576  }
577  else { // RCUBE_COMPOSE_FORWARD or NEW
578    $useHtml = ($html_editor == 1);
579  }
580
581  return $useHtml;
582}
583
584
585function rcmail_prepare_message_body()
586{
587  global $RCMAIL, $MESSAGE, $compose_mode, $LINE_LENGTH, $HTML_MODE;
588
589  // use posted message body
590  if (!empty($_POST['_message'])) {
591    $body = get_input_value('_message', RCUBE_INPUT_POST, true);
592    $isHtml = (bool) get_input_value('_is_html', RCUBE_INPUT_POST);
593  }
594  else if ($_SESSION['compose']['param']['body']) {
595    $body = $_SESSION['compose']['param']['body'];
596    $isHtml = false;
597  }
598  // forward as attachment
599  else if ($compose_mode == RCUBE_COMPOSE_FORWARD && $MESSAGE->forward_attachment) {
600    $isHtml = rcmail_compose_editor_mode();
601    $body = '';
602    if (empty($_SESSION['compose']['attachments']))
603      rcmail_write_forward_attachment($MESSAGE);
604  }
605  // reply/edit/draft/forward
606  else if ($compose_mode) {
607    $has_html_part = $MESSAGE->has_html_part();
608    $isHtml = rcmail_compose_editor_mode();
609
610    if ($isHtml) {
611      if ($has_html_part) {
612        $body = $MESSAGE->first_html_part();
613      }
614      else {
615        $body = $MESSAGE->first_text_part();
616        // try to remove the signature
617        if ($RCMAIL->config->get('strip_existing_sig', true))
618          $body = rcmail_remove_signature($body);
619        // add HTML formatting
620        $body = rcmail_plain_body($body);
621        if ($body)
622          $body = '<pre>' . $body . '</pre>';
623      }
624    }
625    else {
626      if ($has_html_part) {
627        // use html part if it has been used for message (pre)viewing
628        // decrease line length for quoting
629        $len = $compose_mode == RCUBE_COMPOSE_REPLY ? $LINE_LENGTH-2 : $LINE_LENGTH;
630        $txt = new html2text($MESSAGE->first_html_part(), false, true, $len);
631        $body = $txt->get_text();
632      }
633      else {
634        $body = $MESSAGE->first_text_part($part);
635        if ($body && $part && $part->ctype_secondary == 'plain'
636            && $part->ctype_parameters['format'] == 'flowed'
637        ) {
638          $body = rcube_message::unfold_flowed($body);
639        }
640      }
641    }
642
643    // compose reply-body
644    if ($compose_mode == RCUBE_COMPOSE_REPLY)
645      $body = rcmail_create_reply_body($body, $isHtml);
646    // forward message body inline
647    else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
648      $body = rcmail_create_forward_body($body, $isHtml);
649    // load draft message body
650    else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT)
651      $body = rcmail_create_draft_body($body, $isHtml);
652  }
653  else { // new message
654    $isHtml = rcmail_compose_editor_mode();
655  }
656
657  $plugin = $RCMAIL->plugins->exec_hook('message_compose_body',
658    array('body' => $body, 'html' => $isHtml, 'mode' => $compose_mode));
659  $body = $plugin['body'];
660  unset($plugin);
661
662  // add blocked.gif attachment (#1486516)
663  if ($isHtml && preg_match('#<img src="\./program/blocked\.gif"#', $body)) {
664    if ($attachment = rcmail_save_image('program/blocked.gif', 'image/gif')) {
665      $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
666      $body = preg_replace('#\./program/blocked\.gif#',
667        $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$_SESSION['compose']['id'],
668        $body);
669    }
670  }
671
672  $HTML_MODE = $isHtml;
673
674  return $body;
675}
676
677function rcmail_compose_body($attrib)
678{
679  global $RCMAIL, $CONFIG, $OUTPUT, $MESSAGE, $compose_mode, $LINE_LENGTH, $HTML_MODE, $MESSAGE_BODY;
680
681  list($form_start, $form_end) = get_form_tags($attrib);
682  unset($attrib['form']);
683
684  if (empty($attrib['id']))
685    $attrib['id'] = 'rcmComposeBody';
686
687  $attrib['name'] = '_message';
688
689  $isHtml = $HTML_MODE;
690
691  $out = $form_start ? "$form_start\n" : '';
692
693  $saveid = new html_hiddenfield(array('name' => '_draft_saveid', 'value' => $compose_mode==RCUBE_COMPOSE_DRAFT ? str_replace(array('<','>'), "", $MESSAGE->headers->messageID) : ''));
694  $out .= $saveid->show();
695
696  $drafttoggle = new html_hiddenfield(array('name' => '_draft', 'value' => 'yes'));
697  $out .= $drafttoggle->show();
698
699  $msgtype = new html_hiddenfield(array('name' => '_is_html', 'value' => ($isHtml?"1":"0")));
700  $out .= $msgtype->show();
701
702  // If desired, set this textarea to be editable by TinyMCE
703  if ($isHtml) {
704    $attrib['class'] = 'mce_editor';
705    $textarea = new html_textarea($attrib);
706    $out .= $textarea->show($MESSAGE_BODY);
707  }
708  else {
709    $textarea = new html_textarea($attrib);
710    $out .= $textarea->show('');
711    // quote plain text, inject into textarea
712    $table = get_html_translation_table(HTML_SPECIALCHARS);
713    $MESSAGE_BODY = strtr($MESSAGE_BODY, $table);
714    $out = substr($out, 0, -11) . $MESSAGE_BODY . '</textarea>';
715  }
716
717  $out .= $form_end ? "\n$form_end" : '';
718
719  $OUTPUT->set_env('composebody', $attrib['id']);
720
721  // include HTML editor
722  rcube_html_editor();
723
724  // include GoogieSpell
725  if (!empty($CONFIG['enable_spellcheck'])) {
726    $engine           = $RCMAIL->config->get('spellcheck_engine','googie');
727    $dictionary       = (bool) $RCMAIL->config->get('spellcheck_dictionary');
728    $spellcheck_langs = (array) $RCMAIL->config->get('spellcheck_languages',
729      array('da'=>'Dansk', 'de'=>'Deutsch', 'en' => 'English', 'es'=>'Español',
730            'fr'=>'Français', 'it'=>'Italiano', 'nl'=>'Nederlands', 'pl'=>'Polski',
731            'pt'=>'Português', 'fi'=>'Suomi', 'sv'=>'Svenska'));
732
733    // googie works only with two-letter codes
734    if ($engine == 'googie') {
735      $lang = strtolower(substr($_SESSION['language'], 0, 2));
736
737      $spellcheck_langs_googie = array();
738      foreach ($spellcheck_langs as $key => $name)
739        $spellcheck_langs_googie[strtolower(substr($key,0,2))] = $name;
740        $spellcheck_langs = $spellcheck_langs_googie;
741    }
742    else {
743      $lang = $_SESSION['language'];
744
745      // if not found in the list, try with two-letter code
746      if (!$spellcheck_langs[$lang])
747        $lang = strtolower(substr($lang, 0, 2));
748    }
749
750    if (!$spellcheck_langs[$lang])
751      $lang = 'en';
752
753    $editor_lang_set = array();
754    foreach ($spellcheck_langs as $key => $name) {
755      $editor_lang_set[] = ($key == $lang ? '+' : '') . JQ($name).'='.JQ($key);
756    }
757
758    $OUTPUT->include_script('googiespell.js');
759    $OUTPUT->add_script(sprintf(
760      "var googie = new GoogieSpell('\$__skin_path/images/googiespell/','?_task=utils&_action=spell&lang=', %s);\n".
761      "googie.lang_chck_spell = \"%s\";\n".
762      "googie.lang_rsm_edt = \"%s\";\n".
763      "googie.lang_close = \"%s\";\n".
764      "googie.lang_revert = \"%s\";\n".
765      "googie.lang_no_error_found = \"%s\";\n".
766      "googie.lang_learn_word = \"%s\";\n".
767      "googie.setLanguages(%s);\n".
768      "googie.setCurrentLanguage('%s');\n".
769      "googie.setSpellContainer('spellcheck-control');\n".
770      "googie.decorateTextarea('%s');\n".
771      "%s.set_env('spellcheck', googie);",
772      !empty($dictionary) ? 'true' : 'false',
773      JQ(Q(rcube_label('checkspelling'))),
774      JQ(Q(rcube_label('resumeediting'))),
775      JQ(Q(rcube_label('close'))),
776      JQ(Q(rcube_label('revertto'))),
777      JQ(Q(rcube_label('nospellerrors'))),
778      JQ(Q(rcube_label('addtodict'))),
779      json_serialize($spellcheck_langs),
780      $lang,
781      $attrib['id'],
782      JS_OBJECT_NAME), 'foot');
783
784    $OUTPUT->add_label('checking');
785    $OUTPUT->set_env('spellcheck_langs', join(',', $editor_lang_set));
786  }
787
788  $out .= "\n".'<iframe name="savetarget" src="program/blank.gif" style="width:0;height:0;border:none;visibility:hidden;"></iframe>';
789
790  return $out;
791}
792
793
794function rcmail_create_reply_body($body, $bodyIsHtml)
795{
796  global $RCMAIL, $MESSAGE, $LINE_LENGTH;
797
798  // build reply prefix
799  $from = array_pop($RCMAIL->imap->decode_address_list($MESSAGE->get_header('from'), 1, false));
800  $prefix = rcube_label(array(
801    'name' => 'mailreplyintro',
802    'vars' => array(
803      'date' => format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long')),
804      'sender' => $from['name'] ? $from['name'] : rcube_idn_to_utf8($from['mailto']),
805    )
806  ));
807
808  if (!$bodyIsHtml) {
809    $body = preg_replace('/\r?\n/', "\n", $body);
810
811    // try to remove the signature
812    if ($RCMAIL->config->get('strip_existing_sig', true))
813      $body = rcmail_remove_signature($body);
814
815    // soft-wrap and quote message text
816    $body = rcmail_wrap_and_quote(rtrim($body, "\n"), $LINE_LENGTH);
817
818    $prefix .= "\n";
819    $suffix = '';
820
821    if ($RCMAIL->config->get('top_posting'))
822      $prefix = "\n\n\n" . $prefix;
823  }
824  else {
825    // save inline images to files
826    $cid_map = rcmail_write_inline_attachments($MESSAGE);
827    // set is_safe flag (we need this for html body washing)
828    rcmail_check_safe($MESSAGE);
829    // clean up html tags
830    $body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
831
832    // build reply (quote content)
833    $prefix = '<p>' . Q($prefix) . "</p>\n";
834    $prefix .= '<blockquote>';
835
836    if ($RCMAIL->config->get('top_posting')) {
837      $prefix = '<br>' . $prefix;
838      $suffix = '</blockquote>';
839    }
840    else {
841      $suffix = '</blockquote><p></p>';
842    }
843  }
844
845  return $prefix.$body.$suffix;
846}
847
848
849function rcmail_create_forward_body($body, $bodyIsHtml)
850{
851  global $RCMAIL, $MESSAGE;
852
853  // add attachments
854  if (!isset($_SESSION['compose']['forward_attachments']) && is_array($MESSAGE->mime_parts))
855    $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
856
857  $date    = format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long'));
858  $charset = $RCMAIL->output->get_charset();
859
860  if (!$bodyIsHtml)
861  {
862    $prefix = "\n\n\n-------- " . rcube_label('originalmessage') . " --------\n";
863    $prefix .= rcube_label('subject') . ': ' . $MESSAGE->subject . "\n";
864    $prefix .= rcube_label('date')    . ': ' . $date . "\n";
865    $prefix .= rcube_label('from')    . ': ' . $MESSAGE->get_header('from') . "\n";
866    $prefix .= rcube_label('to')      . ': ' . $MESSAGE->get_header('to') . "\n";
867
868    if ($MESSAGE->headers->cc)
869      $prefix .= rcube_label('cc') . ': ' . $MESSAGE->get_header('cc') . "\n";
870    if ($MESSAGE->headers->replyto && $MESSAGE->headers->replyto != $MESSAGE->headers->from)
871      $prefix .= rcube_label('replyto') . ': ' . $MESSAGE->get_header('replyto') . "\n";
872
873    $prefix .= "\n";
874  }
875  else
876  {
877    // set is_safe flag (we need this for html body washing)
878    rcmail_check_safe($MESSAGE);
879    // clean up html tags
880    $body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
881
882    $prefix = sprintf(
883      "<br /><p>-------- " . rcube_label('originalmessage') . " --------</p>" .
884        "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" .
885        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
886        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
887        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>" .
888        "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
889      rcube_label('subject'), Q($MESSAGE->subject),
890      rcube_label('date'), Q($date),
891      rcube_label('from'), htmlspecialchars(Q($MESSAGE->get_header('from'), 'replace'), ENT_COMPAT, $charset),
892      rcube_label('to'), htmlspecialchars(Q($MESSAGE->get_header('to'), 'replace'), ENT_COMPAT, $charset));
893
894    if ($MESSAGE->headers->cc)
895      $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
896        rcube_label('cc'),
897        htmlspecialchars(Q($MESSAGE->get_header('cc'), 'replace'), ENT_COMPAT, $charset));
898
899    if ($MESSAGE->headers->replyto && $MESSAGE->headers->replyto != $MESSAGE->headers->from)
900      $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">%s: </th><td>%s</td></tr>",
901        rcube_label('replyto'),
902        htmlspecialchars(Q($MESSAGE->get_header('replyto'), 'replace'), ENT_COMPAT, $charset));
903
904    $prefix .= "</tbody></table><br>";
905  }
906
907  return $prefix.$body;
908}
909
910
911function rcmail_create_draft_body($body, $bodyIsHtml)
912{
913  global $MESSAGE, $OUTPUT;
914
915  /**
916   * add attachments
917   * sizeof($MESSAGE->mime_parts can be 1 - e.g. attachment, but no text!
918   */
919  if (empty($_SESSION['compose']['forward_attachments'])
920      && is_array($MESSAGE->mime_parts)
921      && count($MESSAGE->mime_parts) > 0)
922  {
923    $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
924
925    // replace cid with href in inline images links
926    if ($cid_map)
927      $body = str_replace(array_keys($cid_map), array_values($cid_map), $body);
928  }
929
930  return $body;
931}
932
933
934function rcmail_remove_signature($body)
935{
936  global $RCMAIL;
937
938  $len = strlen($body);
939  $sig_max_lines = $RCMAIL->config->get('sig_max_lines', 15);
940
941  while (($sp = strrpos($body, "-- \n", $sp ? -$len+$sp-1 : 0)) !== false) {
942    if ($sp == 0 || $body[$sp-1] == "\n") {
943      // do not touch blocks with more that X lines
944      if (substr_count($body, "\n", $sp) < $sig_max_lines) {
945        $body = substr($body, 0, max(0, $sp-1));
946      }
947      break;
948    }
949  }
950
951  return $body;
952}
953
954
955function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
956{
957  global $RCMAIL;
958
959  $cid_map = $messages = array();
960  foreach ((array)$message->mime_parts as $pid => $part)
961  {
962    if (($part->ctype_primary != 'message' || !$bodyIsHtml) && $part->ctype_primary != 'multipart' &&
963        ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename)
964        && $part->mimetype != 'application/ms-tnef'
965    ) {
966      $skip = false;
967      if ($part->mimetype == 'message/rfc822') {
968        $messages[] = $part->mime_id;
969      } else if ($messages) {
970        // skip attachments included in message/rfc822 attachment (#1486487)
971        foreach ($messages as $mimeid)
972          if (strpos($part->mime_id, $mimeid.'.') === 0) {
973            $skip = true;
974            break;
975          }
976      }
977
978      if (!$skip && ($attachment = rcmail_save_attachment($message, $pid))) {
979        $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
980        if ($bodyIsHtml && ($part->content_id || $part->content_location)) {
981          $url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$_SESSION['compose']['id'];
982          if ($part->content_id)
983            $cid_map['cid:'.$part->content_id] = $url;
984          else
985            $cid_map[$part->content_location] = $url;
986        }
987      }
988    }
989  }
990
991  $_SESSION['compose']['forward_attachments'] = true;
992
993  return $cid_map;
994}
995
996
997function rcmail_write_inline_attachments(&$message)
998{
999  global $RCMAIL;
1000
1001  $cid_map = array();
1002  foreach ((array)$message->mime_parts as $pid => $part) {
1003    if (($part->content_id || $part->content_location) && $part->filename) {
1004      if ($attachment = rcmail_save_attachment($message, $pid)) {
1005        $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
1006        $url = $RCMAIL->comm_path.'&_action=display-attachment&_file=rcmfile'.$attachment['id'].'&_id='.$_SESSION['compose']['id'];
1007        if ($part->content_id)
1008          $cid_map['cid:'.$part->content_id] = $url;
1009        else
1010          $cid_map[$part->content_location] = $url;
1011      }
1012    }
1013  }
1014
1015  return $cid_map;
1016}
1017
1018// Creates an attachment from the forwarded message
1019function rcmail_write_forward_attachment(&$message)
1020{
1021  global $RCMAIL;
1022
1023  if (strlen($message->subject)) {
1024    $name = mb_substr($message->subject, 0, 64) . '.eml';
1025  }
1026  else {
1027    $name = 'message_rfc822.eml';
1028  }
1029
1030  $mem_limit = parse_bytes(ini_get('memory_limit'));
1031  $curr_mem = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
1032  $data = $path = null;
1033
1034  // don't load too big attachments into memory
1035  if ($mem_limit > 0 && $message->size > $mem_limit - $curr_mem) {
1036    $temp_dir = unslashify($RCMAIL->config->get('temp_dir'));
1037    $path = tempnam($temp_dir, 'rcmAttmnt');
1038    if ($fp = fopen($path, 'w')) {
1039      $RCMAIL->imap->get_raw_body($message->uid, $fp);
1040      fclose($fp);
1041    } else
1042      return false;
1043  } else {
1044    $data = $RCMAIL->imap->get_raw_body($message->uid);
1045  }
1046
1047  $attachment = array(
1048    'group' => $_SESSION['compose']['id'],
1049    'name' => $name,
1050    'mimetype' => 'message/rfc822',
1051    'data' => $data,
1052    'path' => $path,
1053    'size' => $path ? filesize($path) : strlen($data),
1054  );
1055
1056  $attachment = $RCMAIL->plugins->exec_hook('attachment_save', $attachment);
1057
1058  if ($attachment['status']) {
1059    unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
1060    $_SESSION['compose']['attachments'][$attachment['id']] = $attachment;
1061    return true;
1062  } else if ($path) {
1063    @unlink($path);
1064  }
1065
1066  return false;
1067}
1068
1069
1070function rcmail_save_attachment(&$message, $pid)
1071{
1072  $rcmail = rcmail::get_instance();
1073  $part = $message->mime_parts[$pid];
1074  $mem_limit = parse_bytes(ini_get('memory_limit'));
1075  $curr_mem = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
1076  $data = $path = null;
1077
1078  // don't load too big attachments into memory
1079  if ($mem_limit > 0 && $part->size > $mem_limit - $curr_mem) {
1080    $temp_dir = unslashify($rcmail->config->get('temp_dir'));
1081    $path = tempnam($temp_dir, 'rcmAttmnt');
1082    if ($fp = fopen($path, 'w')) {
1083      $message->get_part_content($pid, $fp);
1084      fclose($fp);
1085    } else
1086      return false;
1087  } else {
1088    $data = $message->get_part_content($pid);
1089  }
1090
1091  $attachment = array(
1092    'group' => $_SESSION['compose']['id'],
1093    'name' => $part->filename ? $part->filename : 'Part_'.$pid.'.'.$part->ctype_secondary,
1094    'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
1095    'content_id' => $part->content_id,
1096    'data' => $data,
1097    'path' => $path,
1098    'size' => $path ? filesize($path) : strlen($data),
1099  );
1100
1101  $attachment = $rcmail->plugins->exec_hook('attachment_save', $attachment);
1102
1103  if ($attachment['status']) {
1104    unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
1105    return $attachment;
1106  } else if ($path) {
1107    @unlink($path);
1108  }
1109
1110  return false;
1111}
1112
1113function rcmail_save_image($path, $mimetype='')
1114{
1115  // handle attachments in memory
1116  $data = file_get_contents($path);
1117
1118  $attachment = array(
1119    'group' => $_SESSION['compose']['id'],
1120    'name' => rcmail_basename($path),
1121    'mimetype' => $mimetype ? $mimetype : rc_mime_content_type($path, $name),
1122    'data' => $data,
1123    'size' => strlen($data),
1124  );
1125
1126  $attachment = rcmail::get_instance()->plugins->exec_hook('attachment_save', $attachment);
1127
1128  if ($attachment['status']) {
1129    unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']);
1130    return $attachment;
1131  }
1132
1133  return false;
1134}
1135
1136function rcmail_basename($filename)
1137{
1138  // basename() is not unicode safe and locale dependent
1139  if (stristr(PHP_OS, 'win') || stristr(PHP_OS, 'netware')) {
1140    return preg_replace('/^.*[\\\\\\/]/', '', $filename);
1141  } else {
1142    return preg_replace('/^.*[\/]/', '', $filename);
1143  }
1144}
1145
1146function rcmail_compose_subject($attrib)
1147{
1148  global $MESSAGE, $compose_mode;
1149 
1150  list($form_start, $form_end) = get_form_tags($attrib);
1151  unset($attrib['form']);
1152 
1153  $attrib['name'] = '_subject';
1154  $attrib['spellcheck'] = 'true';
1155  $textfield = new html_inputfield($attrib);
1156
1157  $subject = '';
1158
1159  // use subject from post
1160  if (isset($_POST['_subject'])) {
1161    $subject = get_input_value('_subject', RCUBE_INPUT_POST, TRUE);
1162  }
1163  // create a reply-subject
1164  else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
1165    if (preg_match('/^re:/i', $MESSAGE->subject))
1166      $subject = $MESSAGE->subject;
1167    else
1168      $subject = 'Re: '.$MESSAGE->subject;
1169  }
1170  // create a forward-subject
1171  else if ($compose_mode == RCUBE_COMPOSE_FORWARD) {
1172    if (preg_match('/^fwd:/i', $MESSAGE->subject))
1173      $subject = $MESSAGE->subject;
1174    else
1175      $subject = 'Fwd: '.$MESSAGE->subject;
1176  }
1177  // creeate a draft-subject
1178  else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) {
1179    $subject = $MESSAGE->subject;
1180  }
1181  else if (!empty($_SESSION['compose']['param']['subject'])) {
1182    $subject = $_SESSION['compose']['param']['subject'];
1183  }
1184 
1185  $out = $form_start ? "$form_start\n" : '';
1186  $out .= $textfield->show($subject);
1187  $out .= $form_end ? "\n$form_end" : '';
1188
1189  return $out;
1190}
1191
1192
1193function rcmail_compose_attachment_list($attrib)
1194{
1195  global $OUTPUT, $CONFIG;
1196 
1197  // add ID if not given
1198  if (!$attrib['id'])
1199    $attrib['id'] = 'rcmAttachmentList';
1200 
1201  $out = "\n";
1202  $jslist = array();
1203
1204  if (is_array($_SESSION['compose']['attachments']))
1205  {
1206    if ($attrib['deleteicon']) {
1207      $button = html::img(array(
1208        'src' => $CONFIG['skin_path'] . $attrib['deleteicon'],
1209        'alt' => rcube_label('delete')
1210      ));
1211    }
1212    else
1213      $button = Q(rcube_label('delete'));
1214
1215    foreach ($_SESSION['compose']['attachments'] as $id => $a_prop)
1216    {
1217      if (empty($a_prop))
1218        continue;
1219     
1220      $out .= html::tag('li', array('id' => 'rcmfile'.$id),
1221        html::a(array(
1222            'href' => "#delete",
1223            'title' => rcube_label('delete'),
1224            'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", JS_OBJECT_NAME, $id)),
1225          $button) . Q($a_prop['name']));
1226       
1227        $jslist['rcmfile'.$id] = array('name' => $a_prop['name'], 'complete' => true, 'mimetype' => $a_prop['mimetype']);
1228    }
1229  }
1230
1231  if ($attrib['deleteicon'])
1232    $_SESSION['compose']['deleteicon'] = $CONFIG['skin_path'] . $attrib['deleteicon'];
1233  if ($attrib['cancelicon'])
1234    $OUTPUT->set_env('cancelicon', $CONFIG['skin_path'] . $attrib['cancelicon']);
1235  if ($attrib['loadingicon'])
1236    $OUTPUT->set_env('loadingicon', $CONFIG['skin_path'] . $attrib['loadingicon']);
1237
1238  $OUTPUT->set_env('attachments', $jslist);
1239  $OUTPUT->add_gui_object('attachmentlist', $attrib['id']);
1240   
1241  return html::tag('ul', $attrib, $out, html::$common_attrib);
1242}
1243
1244
1245function rcmail_compose_attachment_form($attrib)
1246{
1247  global $RCMAIL, $OUTPUT;
1248
1249  // add ID if not given
1250  if (!$attrib['id'])
1251    $attrib['id'] = 'rcmUploadbox';
1252
1253  // Get filesize, enable upload progress bar
1254  $max_filesize = rcube_upload_init();
1255
1256  $button = new html_inputfield(array('type' => 'button'));
1257
1258  $out = html::div($attrib,
1259    $OUTPUT->form_tag(array('name' => 'uploadform', 'method' => 'post', 'enctype' => 'multipart/form-data'),
1260      html::div(null, rcmail_compose_attachment_field(array('size' => $attrib['attachmentfieldsize']))) .
1261      html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize)))) .
1262      html::div('buttons',
1263        $button->show(rcube_label('close'), array('class' => 'button', 'onclick' => "$('#$attrib[id]').hide()")) . ' ' .
1264        $button->show(rcube_label('upload'), array('class' => 'button mainaction', 'onclick' => JS_OBJECT_NAME . ".command('send-attachment', this.form)"))
1265      )
1266    )
1267  );
1268
1269  $OUTPUT->add_gui_object('uploadbox', $attrib['id']);
1270  return $out;
1271}
1272
1273
1274function rcmail_compose_attachment_field($attrib)
1275{
1276  $attrib['type'] = 'file';
1277  $attrib['name'] = '_attachments[]';
1278  $attrib['multiple'] = 'multiple';
1279
1280  $field = new html_inputfield($attrib);
1281  return $field->show();
1282}
1283
1284
1285function rcmail_priority_selector($attrib)
1286{
1287  global $MESSAGE;
1288 
1289  list($form_start, $form_end) = get_form_tags($attrib);
1290  unset($attrib['form']);
1291
1292  $attrib['name'] = '_priority';
1293  $selector = new html_select($attrib);
1294
1295  $selector->add(array(rcube_label('lowest'),
1296                       rcube_label('low'),
1297                       rcube_label('normal'),
1298                       rcube_label('high'),
1299                       rcube_label('highest')),
1300                 array(5, 4, 0, 2, 1));
1301
1302  if (isset($_POST['_priority']))
1303    $sel = $_POST['_priority'];
1304  else if (intval($MESSAGE->headers->priority) != 3)
1305    $sel = intval($MESSAGE->headers->priority);
1306  else
1307    $sel = 0;
1308
1309  $out = $form_start ? "$form_start\n" : '';
1310  $out .= $selector->show($sel);
1311  $out .= $form_end ? "\n$form_end" : '';
1312
1313  return $out;
1314}
1315
1316
1317function rcmail_receipt_checkbox($attrib)
1318{
1319  global $RCMAIL, $MESSAGE, $compose_mode;
1320
1321  list($form_start, $form_end) = get_form_tags($attrib);
1322  unset($attrib['form']);
1323
1324  if (!isset($attrib['id']))
1325    $attrib['id'] = 'receipt'; 
1326
1327  $attrib['name'] = '_receipt';
1328  $attrib['value'] = '1';
1329  $checkbox = new html_checkbox($attrib);
1330
1331  if ($MESSAGE && in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT)))
1332    $mdn_default = (bool) $MESSAGE->headers->mdn_to;
1333  else
1334    $mdn_default = $RCMAIL->config->get('mdn_default');
1335
1336  $out = $form_start ? "$form_start\n" : '';
1337  $out .= $checkbox->show($mdn_default);
1338  $out .= $form_end ? "\n$form_end" : '';
1339
1340  return $out;
1341}
1342
1343
1344function rcmail_dsn_checkbox($attrib)
1345{
1346  global $RCMAIL;
1347
1348  list($form_start, $form_end) = get_form_tags($attrib);
1349  unset($attrib['form']);
1350
1351  if (!isset($attrib['id']))
1352    $attrib['id'] = 'dsn';
1353
1354  $attrib['name'] = '_dsn';
1355  $attrib['value'] = '1';
1356  $checkbox = new html_checkbox($attrib);
1357
1358  $out = $form_start ? "$form_start\n" : '';
1359  $out .= $checkbox->show($RCMAIL->config->get('dsn_default'));
1360  $out .= $form_end ? "\n$form_end" : '';
1361
1362  return $out;
1363}
1364
1365
1366function rcmail_editor_selector($attrib)
1367{
1368  global $CONFIG, $MESSAGE, $compose_mode;
1369
1370  // determine whether HTML or plain text should be checked
1371  $useHtml = rcmail_compose_editor_mode();
1372
1373  if (empty($attrib['editorid']))
1374    $attrib['editorid'] = 'rcmComposeBody';
1375
1376  if (empty($attrib['name']))
1377    $attrib['name'] = 'editorSelect';
1378
1379  $attrib['onchange'] = "return rcmail_toggle_editor(this, '".$attrib['editorid']."', '_is_html')";
1380
1381  $select = new html_select($attrib);
1382
1383  $select->add(Q(rcube_label('htmltoggle')), 'html');
1384  $select->add(Q(rcube_label('plaintoggle')), 'plain');
1385
1386  return $select->show($useHtml ? 'html' : 'plain');
1387
1388  foreach ($choices as $value => $text) {
1389    $attrib['id'] = '_' . $value;
1390    $attrib['value'] = $value;
1391    $selector .= $radio->show($chosenvalue, $attrib) . html::label($attrib['id'], Q(rcube_label($text)));
1392  }
1393
1394  return $selector;
1395}
1396
1397
1398function rcmail_store_target_selection($attrib)
1399{
1400  $attrib['name'] = '_store_target';
1401  $select = rcmail_mailbox_select(array_merge($attrib, array(
1402    'noselection' => '- '.rcube_label('dontsave').' -',
1403    'folder_filter' => 'mail',
1404    'folder_rights' => 'w',
1405  )));
1406  return $select->show($_SESSION['compose']['param']['sent_mbox'], $attrib);
1407}
1408
1409
1410function rcmail_check_sent_folder($folder, $create=false)
1411{
1412  global $IMAP;
1413
1414  if ($IMAP->mailbox_exists($folder, true)) {
1415    return true;
1416  }
1417
1418  // folder may exist but isn't subscribed (#1485241)
1419  if ($create) {
1420    if (!$IMAP->mailbox_exists($folder))
1421      return $IMAP->create_mailbox($folder, true);
1422    else
1423      return $IMAP->subscribe($folder);
1424  }
1425
1426  return false;
1427}
1428
1429
1430function get_form_tags($attrib)
1431{
1432  global $RCMAIL, $MESSAGE_FORM;
1433
1434  $form_start = '';
1435  if (!$MESSAGE_FORM)
1436  {
1437    $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $RCMAIL->task));
1438    $hiddenfields->add(array('name' => '_action', 'value' => 'send'));
1439    $hiddenfields->add(array('name' => '_id', 'value' => $_SESSION['compose']['id']));
1440
1441    $form_start = empty($attrib['form']) ? $RCMAIL->output->form_tag(array('name' => "form", 'method' => "post")) : '';
1442    $form_start .= $hiddenfields->show();
1443  }
1444
1445  $form_end = ($MESSAGE_FORM && !strlen($attrib['form'])) ? '</form>' : '';
1446  $form_name = !empty($attrib['form']) ? $attrib['form'] : 'form';
1447
1448  if (!$MESSAGE_FORM)
1449    $RCMAIL->output->add_gui_object('messageform', $form_name);
1450
1451  $MESSAGE_FORM = $form_name;
1452
1453  return array($form_start, $form_end);
1454}
1455
1456
1457// register UI objects
1458$OUTPUT->add_handlers(array(
1459  'composeheaders' => 'rcmail_compose_headers',
1460  'composesubject' => 'rcmail_compose_subject',
1461  'composebody' => 'rcmail_compose_body',
1462  'composeattachmentlist' => 'rcmail_compose_attachment_list',
1463  'composeattachmentform' => 'rcmail_compose_attachment_form',
1464  'composeattachment' => 'rcmail_compose_attachment_field',
1465  'priorityselector' => 'rcmail_priority_selector',
1466  'editorselector' => 'rcmail_editor_selector',
1467  'receiptcheckbox' => 'rcmail_receipt_checkbox',
1468  'dsncheckbox' => 'rcmail_dsn_checkbox',
1469  'storetarget' => 'rcmail_store_target_selection',
1470));
1471
1472$OUTPUT->send('compose');
1473
1474
Note: See TracBrowser for help on using the repository browser.