]> git.donarmstrong.com Git - roundcube.git/blob - program/steps/mail/compose.inc
Imported Upstream version 0.2.2
[roundcube.git] / program / steps / mail / compose.inc
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-2009, RoundCube Dev. - Switzerland                 |
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: compose.inc 2483 2009-05-15 10:22:29Z thomasb $
19
20 */
21
22 // define constants for message compose mode
23 define('RCUBE_COMPOSE_REPLY', 0x0106);
24 define('RCUBE_COMPOSE_FORWARD', 0x0107);
25 define('RCUBE_COMPOSE_DRAFT', 0x0108);
26
27 $MESSAGE_FORM = NULL;
28 $MESSAGE = NULL;
29
30 // Nothing below is called during message composition, only at "new/forward/reply/draft" initialization or
31 // if a compose-ID is given (i.e. when the compose step is opened in a new window/tab).
32 // Since there are many ways to leave the compose page improperly, it seems necessary to clean-up an old
33 // compose when a "new/forward/reply/draft" is called - otherwise the old session attachments will appear
34
35 if (!is_array($_SESSION['compose']) || $_SESSION['compose']['id'] != get_input_value('_id', RCUBE_INPUT_GET))
36 {
37   rcmail_compose_cleanup();
38   $_SESSION['compose'] = array('id' => uniqid(rand()), 'param' => array_map('strip_tags', $_GET));
39   
40   // process values like "mailto:foo@bar.com?subject=new+message&cc=another"
41   if ($_SESSION['compose']['param']['_to']) {
42     $mailto = explode('?', $_SESSION['compose']['param']['_to']);
43     if (count($mailto) > 1) {
44       $_SESSION['compose']['param']['_to'] = $mailto[0];
45       parse_str($mailto[1], $query);
46       foreach ($query as $f => $val)
47         $_SESSION['compose']['param']["_$f"] = $val;
48     }
49   }
50
51   // redirect to a unique URL with all parameters stored in session
52   $OUTPUT->redirect(array('_action' => 'compose', '_id' => $_SESSION['compose']['id']));
53 }
54
55 // add some labels to client
56 $OUTPUT->add_label('nosubject', 'nosenderwarning', 'norecipientwarning', 'nosubjectwarning',
57     'nobodywarning', 'notsentwarning', 'savingmessage', 'sendingmessage', 'messagesaved',
58     'converting', 'editorwarning', 'searching');
59
60 // add config parameters to client script
61 if (!empty($CONFIG['drafts_mbox'])) {
62   $OUTPUT->set_env('drafts_mailbox', $CONFIG['drafts_mbox']);
63   $OUTPUT->set_env('draft_autosave', $CONFIG['draft_autosave']);
64 }
65 // set current mailbox in client environment
66 $OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name());
67
68 // get reference message and set compose mode
69 if ($msg_uid = $_SESSION['compose']['param']['_reply_uid'])
70   $compose_mode = RCUBE_COMPOSE_REPLY;
71 else if ($msg_uid = $_SESSION['compose']['param']['_forward_uid'])
72   $compose_mode = RCUBE_COMPOSE_FORWARD;
73 else if ($msg_uid = $_SESSION['compose']['param']['_draft_uid']) {
74   $RCMAIL->imap->set_mailbox($CONFIG['drafts_mbox']);
75   $compose_mode = RCUBE_COMPOSE_DRAFT;
76 }
77
78 if (!empty($msg_uid))
79 {
80   // similar as in program/steps/mail/show.inc
81   $MESSAGE = new rcube_message($msg_uid);
82   
83   if (!empty($MESSAGE->headers->charset))
84     $IMAP->set_charset($MESSAGE->headers->charset);
85     
86   if ($compose_mode == RCUBE_COMPOSE_REPLY)
87   {
88     $_SESSION['compose']['reply_uid'] = $msg_uid;
89     $_SESSION['compose']['reply_msgid'] = $MESSAGE->headers->messageID;
90     $_SESSION['compose']['references']  = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID);
91
92     if (!empty($_SESSION['compose']['param']['_all']))
93       $MESSAGE->reply_all = 1;
94   }
95   else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
96   {
97     if($MESSAGE->headers->in_reply_to)
98     {
99       // TODO: how to get reply_uid/forward_uid value, maybe we must set X-Reply-UID/X-Forward-UID
100       // $_SESSION['compose']['reply_uid'] = ?
101       // $_SESSION['compose']['forward_uid'] = ?
102       $_SESSION['compose']['reply_msgid'] = '<'.$MESSAGE->headers->in_reply_to.'>';
103     }
104     $_SESSION['compose']['references']  = $MESSAGE->headers->references;
105   }
106   else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
107   {
108     $_SESSION['compose']['forward_uid'] = $msg_uid;
109   }
110 }
111
112 /****** compose mode functions ********/
113
114
115 function rcmail_compose_headers($attrib)
116 {
117   global $IMAP, $MESSAGE, $DB, $compose_mode;
118   static $sa_recipients = array();
119
120   list($form_start, $form_end) = get_form_tags($attrib);
121   
122   $out = '';
123   $part = strtolower($attrib['part']);
124   
125   switch ($part)
126   {
127     case 'from':
128       return rcmail_compose_header_from($attrib);
129
130     case 'to':
131       $fname = '_to';
132       $header = 'to';
133       
134       // we have a set of recipients stored is session
135       if (($mailto_id = $_SESSION['compose']['param']['_mailto']) && $_SESSION['mailto'][$mailto_id])
136         $fvalue = urldecode($_SESSION['mailto'][$mailto_id]);
137       
138     case 'cc':
139       if (!$fname)
140       {
141         $fname = '_cc';
142         $header = 'cc';
143       }
144     case 'bcc':
145       if (!$fname)
146       {
147         $fname = '_bcc';
148         $header = 'bcc';
149       }
150         
151       $allow_attrib = array('id', 'class', 'style', 'cols', 'rows', 'tabindex');
152       $field_type = 'html_textarea';
153       break;
154
155     case 'replyto':
156     case 'reply-to':
157       $fname = '_replyto';
158       $allow_attrib = array('id', 'class', 'style', 'size', 'tabindex');
159       $field_type = 'html_inputfield';
160       break;
161   }
162  
163   if ($fname && !empty($_POST[$fname]))
164     $fvalue = get_input_value($fname, RCUBE_INPUT_POST, TRUE);
165   else if ($fname && !$fvalue && !empty($_SESSION['compose']['param'][$fname]))
166     $fvalue = $_SESSION['compose']['param'][$fname];
167
168   else if ($header && $compose_mode == RCUBE_COMPOSE_REPLY)
169   {
170     // get recipent address(es) out of the message headers
171     if ($header=='to' && !empty($MESSAGE->headers->replyto))
172       $fvalue = $MESSAGE->headers->replyto;
173
174     else if ($header=='to' && !empty($MESSAGE->headers->from))
175       $fvalue = $MESSAGE->headers->from;
176
177     // add recipent of original message if reply to all
178     else if ($header=='cc' && !empty($MESSAGE->reply_all))
179     {
180       if ($v = $MESSAGE->headers->to)
181         $fvalue .= $v;
182
183       if ($v = $MESSAGE->headers->cc)
184         $fvalue .= (!empty($fvalue) ? ', ' : '') . $v;
185     }
186
187     // split recipients and put them back together in a unique way
188     if (!empty($fvalue))
189     {
190       $to_addresses = $IMAP->decode_address_list($fvalue);
191       $fvalue = '';
192
193       foreach ($to_addresses as $addr_part)
194       {
195         if (!empty($addr_part['mailto'])
196             && !in_array($addr_part['mailto'], $sa_recipients)
197             && (!$MESSAGE->compose_from
198                 || !in_array_nocase($addr_part['mailto'], $MESSAGE->compose_from)
199                 || (count($to_addresses)==1 && $header=='to'))) // allow reply to yourself
200         {
201           $fvalue .= (strlen($fvalue) ? ', ':'').$addr_part['string'];
202           $sa_recipients[] = $addr_part['mailto'];
203         }
204       }
205     }
206   }
207   else if ($header && $compose_mode == RCUBE_COMPOSE_DRAFT)
208   {
209     // get drafted headers
210     if ($header=='to' && !empty($MESSAGE->headers->to))
211       $fvalue = $MESSAGE->get_header('to');
212
213     if ($header=='cc' && !empty($MESSAGE->headers->cc))
214       $fvalue = $MESSAGE->get_header('cc');
215
216     if ($header=='bcc' && !empty($MESSAGE->headers->bcc))
217       $fvalue = $MESSAGE->get_header('bcc');
218   }
219
220         
221   if ($fname && $field_type)
222   {
223     // pass the following attributes to the form class
224     $field_attrib = array('name' => $fname, 'spellcheck' => 'false');
225     foreach ($attrib as $attr => $value)
226       if (in_array($attr, $allow_attrib))
227         $field_attrib[$attr] = $value;
228
229     // create teaxtarea object
230     $input = new $field_type($field_attrib);
231     $out = $input->show($fvalue);
232   }
233   
234   if ($form_start)
235     $out = $form_start.$out;
236
237   return $out;  
238 }
239
240
241
242 function rcmail_compose_header_from($attrib)
243 {
244   global $IMAP, $MESSAGE, $DB, $USER, $OUTPUT, $compose_mode;
245     
246   // pass the following attributes to the form class
247   $field_attrib = array('name' => '_from');
248   foreach ($attrib as $attr => $value)
249     if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex')))
250       $field_attrib[$attr] = $value;
251
252   // extract all recipients of the reply-message
253   $a_recipients = array();
254   if ($compose_mode == RCUBE_COMPOSE_REPLY && is_object($MESSAGE->headers))
255   {
256     $MESSAGE->compose_from = array();
257
258     $a_to = $IMAP->decode_address_list($MESSAGE->headers->to);
259     foreach ($a_to as $addr)
260     {
261       if (!empty($addr['mailto']))
262         $a_recipients[] = rc_strtolower($addr['mailto']);
263     }
264
265     if (!empty($MESSAGE->headers->cc))
266     {
267       $a_cc = $IMAP->decode_address_list($MESSAGE->headers->cc);
268       foreach ($a_cc as $addr)
269       {
270         if (!empty($addr['mailto']))
271           $a_recipients[] = rc_strtolower($addr['mailto']);
272       }
273     }
274   }
275
276   // get this user's identities
277   $sql_result = $USER->list_identities();
278
279   if ($DB->num_rows($sql_result))
280   {
281     $from_id = 0;
282     $a_signatures = array();
283
284     $field_attrib['onchange'] = JS_OBJECT_NAME.".change_identity(this)";
285     $select_from = new html_select($field_attrib);
286
287     while ($sql_arr = $DB->fetch_assoc($sql_result))
288     {
289       $identity_id = $sql_arr['identity_id'];
290       $select_from->add(format_email_recipient($sql_arr['email'], $sql_arr['name']), $identity_id);
291
292       // add signature to array
293       if (!empty($sql_arr['signature']))
294       {
295         $a_signatures[$identity_id]['text'] = $sql_arr['signature'];
296         $a_signatures[$identity_id]['is_html'] = ($sql_arr['html_signature'] == 1) ? true : false;
297         if ($a_signatures[$identity_id]['is_html'])
298         {
299             $h2t = new html2text($a_signatures[$identity_id]['text'], false, false);
300             $a_signatures[$identity_id]['plain_text'] = trim($h2t->get_text());
301         }
302       }
303
304       if ($compose_mode == RCUBE_COMPOSE_REPLY && is_array($MESSAGE->compose_from))
305         $MESSAGE->compose_from[] = $sql_arr['email'];
306
307       if (empty($_POST['_from']))
308       {
309         // set draft's identity
310         if ($compose_mode == RCUBE_COMPOSE_DRAFT && strstr($MESSAGE->headers->from, $sql_arr['email']))
311           $from_id = $sql_arr['identity_id'];
312         // set identity if it's one of the reply-message recipients (with prio for default identity)
313         else if (in_array(rc_strtolower($sql_arr['email']), $a_recipients) && (empty($from_id) || $sql_arr['standard']))
314           $from_id = $sql_arr['identity_id'];
315       }
316     }
317
318     // overwrite identity selection with post parameter
319     if (!empty($_POST['_from']))
320       $from_id = get_input_value('_from', RCUBE_INPUT_POST);
321
322     $out = $select_from->show($from_id);
323
324     // add signatures to client
325     $OUTPUT->set_env('signatures', $a_signatures);
326   }
327   else
328   {
329     $input_from = new html_inputfield($field_attrib);
330     $out = $input_from->show($_POST['_from']);
331   }
332   
333   if ($form_start)
334     $out = $form_start.$out;
335
336   return $out;
337 }
338
339
340 function rcmail_compose_body($attrib)
341 {
342   global $RCMAIL, $CONFIG, $OUTPUT, $MESSAGE, $compose_mode;
343   
344   list($form_start, $form_end) = get_form_tags($attrib);
345   unset($attrib['form']);
346   
347   if (empty($attrib['id']))
348     $attrib['id'] = 'rcmComposeMessage';
349
350   $attrib['name'] = '_message';
351
352   if ($CONFIG['htmleditor'])
353     $isHtml = true;
354   else
355     $isHtml = false;
356
357   $body = '';
358
359   // use posted message body
360   if (!empty($_POST['_message']))
361   {
362     $body = get_input_value('_message', RCUBE_INPUT_POST, true);
363   }
364   else if ($compose_mode)
365   {
366     if ($isHtml && $MESSAGE->has_html_part())
367     {
368       $body = $MESSAGE->first_html_part();
369       $isHtml = true;
370     }
371     else
372     {
373       $body = $MESSAGE->first_text_part();
374       $isHtml = false;
375     }
376     
377     // compose reply-body
378     if ($compose_mode == RCUBE_COMPOSE_REPLY)
379       $body = rcmail_create_reply_body($body, $isHtml);
380     // forward message body inline
381     else if ($compose_mode == RCUBE_COMPOSE_FORWARD)
382       $body = rcmail_create_forward_body($body, $isHtml);
383     // load draft message body
384     else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
385       $body = rcmail_create_draft_body($body, $isHtml);
386   }
387   else if (!empty($_SESSION['compose']['param']['_body']))
388   {
389     $body = $_SESSION['compose']['param']['_body'];
390   }
391
392   $out = $form_start ? "$form_start\n" : '';
393
394   $saveid = new html_hiddenfield(array('name' => '_draft_saveid', 'value' => $compose_mode==RCUBE_COMPOSE_DRAFT ? str_replace(array('<','>'), "", $MESSAGE->headers->messageID) : ''));
395   $out .= $saveid->show();
396
397   $drafttoggle = new html_hiddenfield(array('name' => '_draft', 'value' => 'yes'));
398   $out .= $drafttoggle->show();
399
400   $msgtype = new html_hiddenfield(array('name' => '_is_html', 'value' => ($isHtml?"1":"0")));
401   $out .= $msgtype->show();
402
403   // If desired, set this textarea to be editable by TinyMCE
404   if ($isHtml) $attrib['class'] = 'mce_editor';
405   $textarea = new html_textarea($attrib);
406   $out .= $textarea->show($body);
407   $out .= $form_end ? "\n$form_end" : '';
408
409   // include HTML editor
410   rcube_html_editor();
411   
412   // include GoogieSpell
413   if (!empty($CONFIG['enable_spellcheck'])) {
414
415     $lang = strtolower(substr($_SESSION['language'], 0, 2));
416   
417     $spellcheck_langs = (array)$RCMAIL->config->get('spellcheck_languages', array('da'=>'Dansk', 'de'=>'Deutsch', 'en' => 'English', 'es'=>'Español', 'fr'=>'Français', 'it'=>'Italiano', 'nl'=>'Nederlands', 'pl'=>'Polski', 'pt'=>'Português', 'fi'=>'Suomi', 'sv'=>'Svenska'));
418     if (!$spellcheck_langs[$lang])
419       $lang = 'en';
420     
421     $editor_lang_set = array();
422     foreach ($spellcheck_langs as $key => $name) {
423       $editor_lang_set[] = ($key == $lang ? '+' : '') . JQ($name).'='.JQ($key);
424       }
425     
426     $OUTPUT->include_script('googiespell.js');
427     $OUTPUT->add_script(sprintf(
428       "var googie = new GoogieSpell('\$__skin_path/images/googiespell/','%s&_action=spell&lang=');\n".
429       "googie.lang_chck_spell = \"%s\";\n".
430       "googie.lang_rsm_edt = \"%s\";\n".
431       "googie.lang_close = \"%s\";\n".
432       "googie.lang_revert = \"%s\";\n".
433       "googie.lang_no_error_found = \"%s\";\n".
434       "googie.setLanguages(%s);\n".
435       "googie.setCurrentLanguage('%s');\n".
436       "googie.decorateTextarea('%s');\n".
437       "%s.set_env('spellcheck', googie);",
438       $RCMAIL->comm_path,
439       JQ(Q(rcube_label('checkspelling'))),
440       JQ(Q(rcube_label('resumeediting'))),
441       JQ(Q(rcube_label('close'))),
442       JQ(Q(rcube_label('revertto'))),
443       JQ(Q(rcube_label('nospellerrors'))),
444       json_serialize($spellcheck_langs),
445       $lang,
446       $attrib['id'],
447       JS_OBJECT_NAME), 'foot');
448
449     $OUTPUT->add_label('checking');
450     $OUTPUT->set_env('spellcheck_langs', join(',', $editor_lang_set));
451   }
452  
453   $out .= "\n".'<iframe name="savetarget" src="program/blank.gif" style="width:0;height:0;border:none;visibility:hidden;"></iframe>';
454
455   return $out;
456 }
457
458
459 function rcmail_create_reply_body($body, $bodyIsHtml)
460 {
461   global $IMAP, $MESSAGE, $OUTPUT;
462
463   if (! $bodyIsHtml)
464   {
465     // try to remove the signature
466     if (($sp = strrpos($body, '-- ')) !== false && ($sp == 0 || $body{$sp-1} == "\n"))
467       {
468       if ($body{$sp+3}==' ' || $body{$sp+3}=="\n" || $body{$sp+3}=="\r")
469         $body = substr($body, 0, max(0, $sp-1));
470       }
471
472     // soft-wrap message first
473     $body = rcmail_wrap_quoted($body, 75);
474
475     $body = rtrim($body, "\r\n");
476
477     if ($body) {
478       // split body into single lines
479       $a_lines = preg_split('/\r?\n/', $body);
480
481       // add > to each line
482       for($n=0; $n<sizeof($a_lines); $n++) {
483         if (strpos($a_lines[$n], '>')===0)
484           $a_lines[$n] = '>'.$a_lines[$n];
485         else
486           $a_lines[$n] = '> '.$a_lines[$n];
487         }
488  
489       $body = join("\n", $a_lines);
490       }
491
492     // add title line(s)
493     $prefix = rc_wordwrap(sprintf("On %s, %s wrote:\n",
494       $MESSAGE->headers->date,
495       $MESSAGE->get_header('from')), 76);
496
497     $suffix = '';
498   }
499   else
500   {
501     // save inline images to files
502     $cid_map = rcmail_write_inline_attachments($MESSAGE);
503     // set is_safe flag (we need this for html body washing)
504     rcmail_check_safe($MESSAGE);
505     // clean up html tags
506     $body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
507
508     // build reply (quote content)
509     $prefix = sprintf("On %s, %s wrote:<br />\n",
510       $MESSAGE->headers->date,
511       htmlspecialchars(Q($MESSAGE->get_header('from'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset()));
512     $prefix .= '<blockquote type="cite" style="padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px; width:100%">';
513     $suffix = "</blockquote><p></p>";
514   }
515
516   return $prefix.$body.$suffix;
517 }
518
519
520 function rcmail_create_forward_body($body, $bodyIsHtml)
521 {
522   global $IMAP, $MESSAGE, $OUTPUT;
523
524   // add attachments
525   if (!isset($_SESSION['compose']['forward_attachments']) && is_array($MESSAGE->mime_parts))
526     $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
527
528   if (!$bodyIsHtml)
529   {
530     $prefix = "\n\n\n-------- Original Message --------\n";
531     $prefix .= 'Subject: ' . $MESSAGE->subject . "\n";
532     $prefix .= 'Date: ' . $MESSAGE->headers->date . "\n";
533     $prefix .= 'From: ' . $MESSAGE->get_header('from') . "\n";
534     $prefix .= 'To: ' . $MESSAGE->get_header('to') . "\n";
535     if ($MESSAGE->headers->replyto && $MESSAGE->headers->replyto != $MESSAGE->headers->from)
536       $prefix .= 'Reply-To: ' . $MESSAGE->get_header('replyto') . "\n";
537     $prefix .= "\n";
538   }
539   else
540   {
541     // set is_safe flag (we need this for html body washing)
542     rcmail_check_safe($MESSAGE);
543     // clean up html tags
544     $body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
545
546     $prefix = sprintf(
547       "<br><br>-------- Original Message --------" .
548         "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" .
549         "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Subject: </th><td>%s</td></tr>" .
550         "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Date: </th><td>%s</td></tr>" .
551         "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">From: </th><td>%s</td></tr>" .
552         "<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">To: </th><td>%s</td></tr>",
553       Q($MESSAGE->subject),
554       Q($MESSAGE->headers->date),
555       htmlspecialchars(Q($MESSAGE->get_header('from'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset(), true),
556         htmlspecialchars(Q($MESSAGE->get_header('to'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset(), true));
557
558     if ($MESSAGE->headers->replyto && $MESSAGE->headers->replyto != $MESSAGE->headers->from)
559       $prefix .= sprintf("<tr><th align=\"right\" nowrap=\"nowrap\" valign=\"baseline\">Reply-To: </th><td>%s</td></tr>",
560         htmlspecialchars(Q($MESSAGE->get_header('replyto'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset(), true));
561
562     $prefix .= "</tbody></table><br>";
563   }
564     
565   return $prefix.$body;
566 }
567
568
569 function rcmail_create_draft_body($body, $bodyIsHtml)
570 {
571   global $MESSAGE, $OUTPUT;
572   
573   /**
574    * add attachments
575    * sizeof($MESSAGE->mime_parts can be 1 - e.g. attachment, but no text!
576    */
577   if (!isset($_SESSION['compose']['forward_attachments'])
578       && is_array($MESSAGE->mime_parts)
579       && count($MESSAGE->mime_parts) > 0)
580   {
581     $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
582
583     // replace cid with href in inline images links
584     if ($cid_map)
585       $body = str_replace(array_keys($cid_map), array_values($cid_map), $body);
586   }
587   
588   return $body;
589 }
590   
591   
592 function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
593 {
594   global $OUTPUT;
595   
596   $cid_map = array();
597   $id = 0;
598   
599   foreach ((array)$message->mime_parts as $pid => $part)
600   {
601     if (($part->ctype_primary != 'message' || !$bodyIsHtml) &&
602         ($part->disposition=='attachment' || $part->disposition=='inline' || $part->headers['content-id']
603          || (empty($part->disposition) && $part->filename)))
604     {
605       if ($attachment = rcmail_save_attachment($message, $pid)) {
606         $_SESSION['compose']['attachments'][$id] = $attachment;
607         if ($bodyIsHtml && $part->filename && $part->content_id) {
608           $cid_map['cid:'.$part->content_id] = 
609             $OUTPUT->app->comm_path.'&_action=display-attachment&_file=rcmfile'.$id;
610         }
611         $id++;
612       }
613     }
614   }
615         
616   $_SESSION['compose']['forward_attachments'] = true;
617
618   return $cid_map;
619 }
620
621
622 function rcmail_write_inline_attachments(&$message)
623 {
624   global $OUTPUT;
625
626   $cid_map = array();
627   $id = 0;
628   
629   foreach ((array)$message->mime_parts as $pid => $part) {
630     if ($part->content_id && $part->filename) {
631       if ($attachment = rcmail_save_attachment($message, $pid)) {
632         $_SESSION['compose']['attachments'][$id] = $attachment;
633         $cid_map['cid:'.$part->content_id] = 
634           $OUTPUT->app->comm_path.'&_action=display-attachment&_file=rcmfile'.$id;
635         $id++;
636       }
637     }
638   }
639   
640   return $cid_map;
641 }
642
643 function rcmail_save_attachment(&$message, $pid)
644 {
645   global $RCMAIL;
646
647   $temp_dir = unslashify($RCMAIL->config->get('temp_dir'));
648   $tmp_path = tempnam($temp_dir, 'rcmAttmnt');
649   $part = $message->mime_parts[$pid];
650   
651   if ($fp = fopen($tmp_path, 'w'))
652   {
653     $message->get_part_content($pid, $fp);
654     fclose($fp);
655
656     return array(
657         'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary,
658         'name' => $part->filename,
659         'path' => $tmp_path,
660         'content_id' => $part->content_id
661     );
662   }
663 }
664
665
666 function rcmail_compose_subject($attrib)
667 {
668   global $MESSAGE, $compose_mode;
669   
670   list($form_start, $form_end) = get_form_tags($attrib);
671   unset($attrib['form']);
672   
673   $attrib['name'] = '_subject';
674   $attrib['spellcheck'] = 'true';
675   $textfield = new html_inputfield($attrib);
676
677   $subject = '';
678
679   // use subject from post
680   if (isset($_POST['_subject'])) {
681     $subject = get_input_value('_subject', RCUBE_INPUT_POST, TRUE);
682   }
683   // create a reply-subject
684   else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
685     if (eregi('^re:', $MESSAGE->subject))
686       $subject = $MESSAGE->subject;
687     else
688       $subject = 'Re: '.$MESSAGE->subject;
689   }
690   // create a forward-subject
691   else if ($compose_mode == RCUBE_COMPOSE_FORWARD) {
692     if (eregi('^fwd:', $MESSAGE->subject))
693       $subject = $MESSAGE->subject;
694     else
695       $subject = 'Fwd: '.$MESSAGE->subject;
696   }
697   // creeate a draft-subject
698   else if ($compose_mode == RCUBE_COMPOSE_DRAFT) {
699     $subject = $MESSAGE->subject;
700   }
701   else if (!empty($_SESSION['compose']['param']['_subject'])) {
702     $subject = $_SESSION['compose']['param']['_subject'];
703   }
704   
705   $out = $form_start ? "$form_start\n" : '';
706   $out .= $textfield->show($subject);
707   $out .= $form_end ? "\n$form_end" : '';
708          
709   return $out;
710 }
711
712
713 function rcmail_compose_attachment_list($attrib)
714 {
715   global $OUTPUT, $CONFIG;
716   
717   // add ID if not given
718   if (!$attrib['id'])
719     $attrib['id'] = 'rcmAttachmentList';
720   
721   $out = "\n";
722   
723   if (is_array($_SESSION['compose']['attachments']))
724   {
725     if ($attrib['deleteicon'])
726       $button = html::img(array(
727         'src' => $CONFIG['skin_path'] . $attrib['deleteicon'],
728         'alt' => rcube_label('delete'),
729         'style' => "padding-right:2px;vertical-align:middle"));
730     else
731       $button = Q(rcube_label('delete'));
732
733     foreach ($_SESSION['compose']['attachments'] as $id => $a_prop)
734     {
735       if (empty($a_prop))
736         continue;
737       
738       $out .= html::tag('li', array('id' => "rcmfile".$id),
739         html::a(array(
740             'href' => "#delete",
741             'title' => rcube_label('delete'),
742             'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%d', this)", JS_OBJECT_NAME, $id)),
743           $button) . Q($a_prop['name']));
744     }
745   }
746
747   $OUTPUT->add_gui_object('attachmentlist', $attrib['id']);
748     
749   return html::tag('ul', $attrib, $out, html::$common_attrib);
750 }
751
752
753 function rcmail_compose_attachment_form($attrib)
754 {
755   global $OUTPUT;
756
757   // add ID if not given
758   if (!$attrib['id'])
759     $attrib['id'] = 'rcmUploadbox';
760   
761   $button = new html_inputfield(array('type' => 'button', 'class' => 'button'));
762   
763   $out = html::div($attrib,
764     $OUTPUT->form_tag(array('name' => 'form', 'method' => 'post', 'enctype' => 'multipart/form-data'),
765       html::div(null, rcmail_compose_attachment_field(array())) .
766       html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => show_bytes(parse_bytes(ini_get('upload_max_filesize'))))))) .
767       html::div('buttons',
768         $button->show(rcube_label('close'), array('onclick' => "document.getElementById('$attrib[id]').style.visibility='hidden'")) . ' ' .
769         $button->show(rcube_label('upload'), array('onclick' => JS_OBJECT_NAME . ".command('send-attachment', this.form)"))
770       )
771     )
772   );
773   
774   $OUTPUT->add_gui_object('uploadbox', $attrib['id']);
775   return $out;
776 }
777
778
779 function rcmail_compose_attachment_field($attrib)
780 {
781   $attrib['type'] = 'file';
782   $attrib['name'] = '_attachments[]';
783   $field = new html_inputfield($attrib);
784   return $field->show();
785 }
786
787
788 function rcmail_priority_selector($attrib)
789 {
790   global $MESSAGE;
791   
792   list($form_start, $form_end) = get_form_tags($attrib);
793   unset($attrib['form']);
794   
795   $attrib['name'] = '_priority';
796   $selector = new html_select($attrib);
797
798   $selector->add(array(rcube_label('lowest'),
799                        rcube_label('low'),
800                        rcube_label('normal'),
801                        rcube_label('high'),
802                        rcube_label('highest')),
803                  array(5, 4, 0, 2, 1));
804                  
805   $sel = isset($_POST['_priority']) ? $_POST['_priority'] : intval($MESSAGE->headers->priority);
806
807   $out = $form_start ? "$form_start\n" : '';
808   $out .= $selector->show($sel);
809   $out .= $form_end ? "\n$form_end" : '';
810          
811   return $out;
812 }
813
814
815 function rcmail_receipt_checkbox($attrib)
816 {
817   global $MESSAGE, $compose_mode;
818   
819   list($form_start, $form_end) = get_form_tags($attrib);
820   unset($attrib['form']);
821   
822   if (!isset($attrib['id']))
823     $attrib['id'] = 'receipt';  
824
825   $attrib['name'] = '_receipt';
826   $attrib['value'] = '1';
827   $checkbox = new html_checkbox($attrib);
828
829   $out = $form_start ? "$form_start\n" : '';
830   $out .= $checkbox->show(
831     $compose_mode == RCUBE_COMPOSE_DRAFT && $MESSAGE->headers->mdn_to ? 1 : 0);
832   $out .= $form_end ? "\n$form_end" : '';
833
834   return $out;
835 }
836
837
838 function rcmail_editor_selector($attrib)
839 {
840   global $CONFIG, $MESSAGE, $compose_mode;
841
842   $choices = array(
843     'html'  => 'htmltoggle',
844     'plain' => 'plaintoggle'
845   );
846
847   // determine whether HTML or plain text should be checked
848   $useHtml = $CONFIG['htmleditor'] ? true : false;
849
850   if ($compose_mode)
851     $useHtml = ($useHtml && $MESSAGE->has_html_part());
852
853   $editorid = empty($attrib['editorid']) ? 'rcmComposeMessage' : $attrib['editorid'];
854
855   $selector = '';
856   $chosenvalue = $useHtml ? 'html' : 'plain';
857   $radio = new html_radiobutton(array('name' => '_editorSelect',
858     'onclick' => "return rcmail_toggle_editor(this.value=='html', '$editorid', '_is_html')"));
859
860   foreach ($choices as $value => $text)
861   {
862     $attrib['id'] = '_' . $value;
863     $attrib['value'] = $value;
864     $selector .= $radio->show($chosenvalue, $attrib) . html::label($attrib['id'], Q(rcube_label($text)));
865   }
866
867   return $selector;
868 }
869
870
871 function rcmail_store_target_selection($attrib)
872 {
873   $attrib['name'] = '_store_target';
874   $select = rcmail_mailbox_select(array_merge($attrib, array('noselection' => '- '.rcube_label('dontsave').' -')));
875   return $select->show(rcmail::get_instance()->config->get('sent_mbox'), $attrib);
876 }
877
878
879 function get_form_tags($attrib)
880 {
881   global $RCMAIL, $MESSAGE_FORM;
882
883   $form_start = '';
884   if (!strlen($MESSAGE_FORM))
885   {
886     $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $RCMAIL->task));
887     $hiddenfields->add(array('name' => '_action', 'value' => 'send'));
888
889     $form_start = empty($attrib['form']) ? $RCMAIL->output->form_tag(array('name' => "form", 'method' => "post")) : '';
890     $form_start .= $hiddenfields->show();
891   }
892     
893   $form_end = (strlen($MESSAGE_FORM) && !strlen($attrib['form'])) ? '</form>' : '';
894   $form_name = !empty($attrib['form']) ? $attrib['form'] : 'form';
895   
896   if (!strlen($MESSAGE_FORM))
897     $RCMAIL->output->add_gui_object('messageform', $form_name);
898   
899   $MESSAGE_FORM = $form_name;
900
901   return array($form_start, $form_end);
902 }
903
904
905 // register UI objects
906 $OUTPUT->add_handlers(array(
907   'composeheaders' => 'rcmail_compose_headers',
908   'composesubject' => 'rcmail_compose_subject',
909   'composebody' => 'rcmail_compose_body',
910   'composeattachmentlist' => 'rcmail_compose_attachment_list',
911   'composeattachmentform' => 'rcmail_compose_attachment_form',
912   'composeattachment' => 'rcmail_compose_attachment_field',
913   'priorityselector' => 'rcmail_priority_selector',
914   'editorselector' => 'rcmail_editor_selector',
915   'receiptcheckbox' => 'rcmail_receipt_checkbox',
916   'storetarget' => 'rcmail_store_target_selection',
917 ));
918
919 $OUTPUT->send('compose');
920
921 ?>