c42266a254874a3ca742a03b6e91b1105b567142
[squirrelmail.git] / src / compose.php
1 <?php
2 /**
3 * compose.php
4 *
5 * This code sends a mail.
6 *
7 * There are 4 modes of operation:
8 * - Start new mail
9 * - Add an attachment
10 * - Send mail
11 * - Save As Draft
12 *
13 * @copyright &copy; 1999-2007 The SquirrelMail Project Team
14 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
15 * @version $Id$
16 * @package squirrelmail
17 */
18
19 /** This is the compose page */
20 define('PAGE_NAME', 'compose');
21
22 /**
23 * Include the SquirrelMail initialization file.
24 */
25 require('../include/init.php');
26
27 /* If email_address not set and admin wants us to ask user for it,
28 * redirect to options page. */
29 if ( $ask_user_info && getPref($data_dir, $username,'email_address') == "" ) {
30 header("Location: " . get_location() . "/options.php?optpage=personal");
31 exit;
32 }
33
34 /* SquirrelMail required files. */
35 require_once(SM_PATH . 'functions/imap_general.php');
36 require_once(SM_PATH . 'functions/imap_messages.php');
37 require_once(SM_PATH . 'functions/date.php');
38 require_once(SM_PATH . 'functions/mime.php');
39 require_once(SM_PATH . 'functions/compose.php');
40 require_once(SM_PATH . 'class/deliver/Deliver.class.php');
41 require_once(SM_PATH . 'functions/addressbook.php');
42 require_once(SM_PATH . 'functions/forms.php');
43 require_once(SM_PATH . 'functions/identity.php');
44
45 /* --------------------- Get globals ------------------------------------- */
46
47 /** SESSION VARS */
48 sqgetGlobalVar('delimiter', $delimiter, SQ_SESSION);
49
50 sqgetGlobalVar('delayed_errors', $delayed_errors, SQ_SESSION);
51 sqgetGlobalVar('composesession', $composesession, SQ_SESSION);
52 sqgetGlobalVar('compose_messages', $compose_messages, SQ_SESSION);
53
54 // compose_messages only useful in SESSION when a forward-as-attachment
55 // has been preconstructed for us and passed in via that mechanism; once
56 // we have it, we can clear it from the SESSION
57 sqsession_unregister('compose_messages');
58
59 // Turn on delayed error handling in case we wind up redirecting below
60 $oErrorHandler->setDelayedErrors(true);
61
62 /** SESSION/POST/GET VARS */
63 sqgetGlobalVar('send_button_count', $send_button_count, SQ_POST, 1, SQ_TYPE_INT);
64 for ($i = 1; $i <= $send_button_count; $i++)
65 if (sqgetGlobalVar('send' . $i, $send, SQ_POST)) break;
66 // Send can only be achieved by setting $_POST var. If Send = true then
67 // retrieve other form fields from $_POST
68 if (isset($send) && $send) {
69 $SQ_GLOBAL = SQ_POST;
70 } else {
71 $SQ_GLOBAL = SQ_FORM;
72 }
73 sqgetGlobalVar('session',$session, $SQ_GLOBAL);
74 sqgetGlobalVar('mailbox',$mailbox, $SQ_GLOBAL);
75 if(!sqgetGlobalVar('identity',$identity, $SQ_GLOBAL)) {
76 $identity=0;
77 }
78 sqgetGlobalVar('send_to',$send_to, $SQ_GLOBAL);
79 sqgetGlobalVar('send_to_cc',$send_to_cc, $SQ_GLOBAL);
80 sqgetGlobalVar('send_to_bcc',$send_to_bcc, $SQ_GLOBAL);
81 sqgetGlobalVar('subject',$subject, $SQ_GLOBAL);
82 sqgetGlobalVar('body',$body, $SQ_GLOBAL);
83 sqgetGlobalVar('mailprio',$mailprio, $SQ_GLOBAL);
84 sqgetGlobalVar('request_mdn',$request_mdn, $SQ_GLOBAL);
85 sqgetGlobalVar('request_dr',$request_dr, $SQ_GLOBAL);
86 sqgetGlobalVar('html_addr_search',$html_addr_search, $SQ_GLOBAL);
87 sqgetGlobalVar('mail_sent',$mail_sent, $SQ_GLOBAL);
88 sqgetGlobalVar('passed_id',$passed_id, $SQ_GLOBAL, NULL, SQ_TYPE_BIGINT);
89 sqgetGlobalVar('passed_ent_id',$passed_ent_id, $SQ_GLOBAL);
90
91 sqgetGlobalVar('attach',$attach, SQ_POST);
92 sqgetGlobalVar('draft',$draft, SQ_POST);
93 sqgetGlobalVar('draft_id',$draft_id, $SQ_GLOBAL);
94 sqgetGlobalVar('ent_num',$ent_num, $SQ_GLOBAL);
95 sqgetGlobalVar('saved_draft',$saved_draft, SQ_FORM);
96
97 if ( sqgetGlobalVar('delete_draft',$delete_draft) ) {
98 $delete_draft = (int)$delete_draft;
99 }
100
101 if ( sqgetGlobalVar('startMessage',$startMessage) ) {
102 $startMessage = (int)$startMessage;
103 } else {
104 $startMessage = 1;
105 }
106
107
108 /** POST VARS */
109 sqgetGlobalVar('sigappend', $sigappend, SQ_POST);
110 sqgetGlobalVar('from_htmladdr_search', $from_htmladdr_search, SQ_POST);
111 sqgetGlobalVar('addr_search_done', $html_addr_search_done, SQ_POST);
112 sqgetGlobalVar('addr_search_cancel', $html_addr_search_cancel, SQ_POST);
113 sqgetGlobalVar('send_to_search', $send_to_search, SQ_POST);
114 sqgetGlobalVar('do_delete', $do_delete, SQ_POST);
115 sqgetGlobalVar('delete', $delete, SQ_POST);
116 sqgetGlobalVar('attachments', $attachments, SQ_POST);
117 if ( sqgetGlobalVar('return', $temp, SQ_POST) ) {
118 $html_addr_search_done = 'Use Addresses';
119 }
120
121 /** GET VARS */
122 if ( sqgetGlobalVar('account', $temp, SQ_GET) ) {
123 $iAccount = (int) $temp;
124 } else {
125 $iAccount = 0;
126 }
127
128
129 /** get smaction */
130 if ( !sqgetGlobalVar('smaction',$action) )
131 {
132 if ( sqgetGlobalVar('smaction_reply',$tmp) ) $action = 'reply';
133 if ( sqgetGlobalVar('smaction_reply_all',$tmp) ) $action = 'reply_all';
134 if ( sqgetGlobalVar('smaction_forward',$tmp) ) $action = 'forward';
135 if ( sqgetGlobalVar('smaction_attache',$tmp) ) $action = 'forward_as_attachment';
136 if ( sqgetGlobalVar('smaction_draft',$tmp) ) $action = 'draft';
137 if ( sqgetGlobalVar('smaction_edit_new',$tmp) ) $action = 'edit_as_new';
138 }
139
140 /**
141 * Here we decode the data passed in from mailto.php.
142 */
143 if ( sqgetGlobalVar('mailtodata', $mailtodata, SQ_GET) ) {
144 $trtable = array('to' => 'send_to',
145 'cc' => 'send_to_cc',
146 'bcc' => 'send_to_bcc',
147 'body' => 'body',
148 'subject' => 'subject');
149 $mtdata = unserialize($mailtodata);
150
151 foreach ($trtable as $f => $t) {
152 if ( !empty($mtdata[$f]) ) {
153 $$t = $mtdata[$f];
154 }
155 }
156 unset($mailtodata,$mtdata, $trtable);
157 }
158
159 /* Location (For HTTP 1.1 header("Location: ...") redirects) */
160 $location = get_location();
161 /* Identities (fetch only once) */
162 $idents = get_identities();
163
164 /* --------------------- Specific Functions ------------------------------ */
165
166 function replyAllString($header) {
167 global $include_self_reply_all, $idents;
168 $excl_ar = array();
169 /**
170 * 1) Remove the addresses we'll be sending the message 'to'
171 */
172 if (isset($header->reply_to)) {
173 $excl_ar = $header->getAddr_a('reply_to');
174 }
175 /**
176 * 2) Remove our identities from the CC list (they still can be in the
177 * TO list) only if $include_self_reply_all is turned off
178 */
179 if (!$include_self_reply_all) {
180 foreach($idents as $id) {
181 $excl_ar[strtolower(trim($id['email_address']))] = '';
182 }
183 }
184
185 /**
186 * 3) get the addresses.
187 */
188 $url_replytoall_ar = $header->getAddr_a(array('to','cc'), $excl_ar);
189
190 /**
191 * 4) generate the string.
192 */
193 $url_replytoallcc = '';
194 foreach( $url_replytoall_ar as $email => $personal) {
195 if ($personal) {
196 // if personal name contains address separator then surround
197 // the personal name with double quotes.
198 if (strpos($personal,',') !== false) {
199 $personal = '"'.$personal.'"';
200 }
201 $url_replytoallcc .= ", $personal <$email>";
202 } else {
203 $url_replytoallcc .= ', '. $email;
204 }
205 }
206 $url_replytoallcc = substr($url_replytoallcc,2);
207
208 return $url_replytoallcc;
209 }
210
211 /**
212 * creates top line in reply citations
213 *
214 * Line style depends on user preferences.
215 * $orig_date argument is available only from 1.4.3 and 1.5.1 version.
216 * @param object $orig_from From: header object.
217 * @param integer $orig_date email's timestamp
218 * @return string reply citation
219 */
220 function getReplyCitation($orig_from, $orig_date) {
221 global $reply_citation_style, $reply_citation_start, $reply_citation_end;
222
223 if (!is_object($orig_from)) {
224 $sOrig_from = '';
225 } else {
226 $sOrig_from = decodeHeader($orig_from->getAddress(false),false,false,true);
227 }
228
229 /* First, return an empty string when no citation style selected. */
230 if (($reply_citation_style == '') || ($reply_citation_style == 'none')) {
231 return '';
232 }
233
234 /* Make sure our final value isn't an empty string. */
235 if ($sOrig_from == '') {
236 return '';
237 }
238
239 /* Otherwise, try to select the desired citation style. */
240 switch ($reply_citation_style) {
241 case 'author_said':
242 // i18n: %s is for author's name
243 $full_reply_citation = sprintf(_("%s wrote:"),$sOrig_from);
244 break;
245 case 'quote_who':
246 $start = '<quote who="';
247 $end = '">';
248 $full_reply_citation = $start . $sOrig_from . $end;
249 break;
250 case 'date_time_author':
251 // i18n:
252 // The first %s is for date string, the second %s is for author's name.
253 // The date uses formating from "D, F j, Y g:i a" and "D, F j, Y H:i"
254 // translations.
255 // Example string:
256 // "On Sat, December 24, 2004 23:59, Santa wrote:"
257 // If you have to put author's name in front of date string, check comments about
258 // argument swapping at http://php.net/sprintf
259 $full_reply_citation = sprintf(_("On %s, %s wrote:"), getLongDateString($orig_date), $sOrig_from);
260 break;
261 case 'user-defined':
262 $start = $reply_citation_start .
263 ($reply_citation_start == '' ? '' : ' ');
264 $end = $reply_citation_end;
265 $full_reply_citation = $start . $sOrig_from . $end;
266 break;
267 default:
268 return '';
269 }
270
271 /* Add line feed and return the citation string. */
272 return ($full_reply_citation . "\n");
273 }
274
275 /**
276 * Creates header fields in forwarded email body
277 *
278 * $default_charset global must be set correctly before you call this function.
279 * @param object $orig_header
280 * @return $string
281 */
282 function getforwardHeader($orig_header) {
283 global $editor_size, $default_charset;
284
285 // using own strlen function in order to detect correct string length
286 $display = array( _("Subject") => sq_strlen(_("Subject"),$default_charset),
287 _("From") => sq_strlen(_("From"),$default_charset),
288 _("Date") => sq_strlen(_("Date"),$default_charset),
289 _("To") => sq_strlen(_("To"),$default_charset),
290 _("Cc") => sq_strlen(_("Cc"),$default_charset) );
291 $maxsize = max($display);
292 $indent = str_pad('',$maxsize+2);
293 foreach($display as $key => $val) {
294 $display[$key] = $key .': '. str_pad('', $maxsize - $val);
295 }
296 $from = decodeHeader($orig_header->getAddr_s('from',"\n$indent"),false,false,true);
297 $from = str_replace('&nbsp;',' ',$from);
298 $to = decodeHeader($orig_header->getAddr_s('to',"\n$indent"),false,false,true);
299 $to = str_replace('&nbsp;',' ',$to);
300 $subject = decodeHeader($orig_header->subject,false,false,true);
301 $subject = str_replace('&nbsp;',' ',$subject);
302
303 // using own str_pad function in order to create correct string pad
304 $bodyTop = sq_str_pad(' '._("Original Message").' ',$editor_size -2,'-',STR_PAD_BOTH,$default_charset) .
305 "\n". $display[_("Subject")] . $subject . "\n" .
306 $display[_("From")] . $from . "\n" .
307 $display[_("Date")] . getLongDateString( $orig_header->date, $orig_header->date_unparsed ). "\n" .
308 $display[_("To")] . $to . "\n";
309 if ($orig_header->cc != array() && $orig_header->cc !='') {
310 $cc = decodeHeader($orig_header->getAddr_s('cc',"\n$indent"),false,false,true);
311 $cc = str_replace('&nbsp;',' ',$cc);
312 $bodyTop .= $display[_("Cc")] .$cc . "\n";
313 }
314 $bodyTop .= str_pad('', $editor_size -2 , '-') .
315 "\n\n";
316 return $bodyTop;
317 }
318 /* ----------------------------------------------------------------------- */
319
320 /*
321 * If the session is expired during a post this restores the compose session
322 * vars.
323 */
324 $session_expired = false;
325 if (sqsession_is_registered('session_expired_post')) {
326 sqgetGlobalVar('session_expired_post', $session_expired_post, SQ_SESSION);
327 /*
328 * extra check for username so we don't display previous post data from
329 * another user during this session.
330 */
331 if (!empty($session_expired_post['username'])
332 && $session_expired_post['username'] == $username) {
333 // these are the vars that we can set from the expired composed session
334 $compo_var_list = array ('send_to', 'send_to_cc', 'body',
335 'startMessage', 'passed_body', 'use_signature', 'signature',
336 'subject', 'newmail', 'send_to_bcc', 'passed_id', 'mailbox',
337 'from_htmladdr_search', 'identity', 'draft_id', 'delete_draft',
338 'mailprio', 'edit_as_new', 'attachments', 'composesession',
339 'request_mdn', 'request_dr');
340
341 foreach ($compo_var_list as $var) {
342 if ( isset($session_expired_post[$var]) && !isset($$var) ) {
343 $$var = $session_expired_post[$var];
344 }
345 }
346
347 if (!empty($attachments))
348 $attachments = unserialize(urldecode($attachments));
349
350 sqsession_register($composesession,'composesession');
351
352 if (isset($send)) {
353 unset($send);
354 }
355 $session_expired = true;
356 }
357 unset($session_expired_post);
358 sqsession_unregister('session_expired_post');
359 session_write_close();
360 if (!isset($mailbox)) {
361 $mailbox = '';
362 }
363 if ($compose_new_win == '1') {
364 compose_Header($color, $mailbox);
365 } else {
366 $sHeaderJs = (isset($sHeaderJs)) ? $sHeaderJs : '';
367 if (strpos($action, 'reply') !== false && $reply_focus) {
368 $sOnload = 'checkForm(\''.$replyfocus.'\');';
369 } else {
370 $sOnload = 'checkForm();';
371 }
372 displayPageHeader($color, $mailbox,$sHeaderJs,$sOnload);
373 }
374 showInputForm($session, false);
375 exit();
376 }
377
378 if (!isset($composesession)) {
379 $composesession = 0;
380 sqsession_register(0,'composesession');
381 } else {
382 $composesession = (int)$composesession;
383 }
384
385 if (!isset($session) || (isset($newmessage) && $newmessage)) {
386 sqsession_unregister('composesession');
387 $session = "$composesession" +1;
388 $composesession = $session;
389 sqsession_register($composesession,'composesession');
390 }
391 if (!empty($compose_messages[$session])) {
392 $composeMessage = $compose_messages[$session];
393 } else {
394 $composeMessage = new Message();
395 $rfc822_header = new Rfc822Header();
396 $composeMessage->rfc822_header = $rfc822_header;
397 $composeMessage->reply_rfc822_header = '';
398 }
399
400 // re-add attachments that were already in this message
401 // FIXME: note that technically this is very bad form -
402 // should never directly manipulate an object like this
403 if (!empty($attachments)) {
404 $attachments = unserialize(urldecode($attachments));
405 if (!empty($attachments) && is_array($attachments))
406 $composeMessage->entities = $attachments;
407 }
408
409 if (empty($mailbox)) {
410 $mailbox = 'INBOX';
411 }
412
413 if ($draft) {
414 /*
415 * Set $default_charset to correspond with the user's selection
416 * of language interface.
417 */
418 set_my_charset();
419 if (! deliverMessage($composeMessage, true)) {
420 showInputForm($session);
421 exit();
422 } else {
423 $draft_message = _("Draft Email Saved");
424 /* If this is a resumed draft, then delete the original */
425 if(isset($delete_draft)) {
426 $imap_stream = sqimap_login($username, false, $imapServerAddress, $imapPort, false);
427 sqimap_mailbox_select($imap_stream, $draft_folder);
428 // force bypass_trash=true because message should be saved when deliverMessage() returns true.
429 // in current implementation of sqimap_msgs_list_flag() single message id can
430 // be submitted as string. docs state that it should be array.
431 sqimap_msgs_list_delete($imap_stream, $draft_folder, $delete_draft, true);
432 if ($auto_expunge) {
433 sqimap_mailbox_expunge($imap_stream, $draft_folder, true);
434 }
435 sqimap_logout($imap_stream);
436 }
437
438 $oErrorHandler->saveDelayedErrors();
439 session_write_close();
440
441 if ($compose_new_win == '1') {
442 if ( !isset($pageheader_sent) || !$pageheader_sent ) {
443 header("Location: $location/compose.php?saved_draft=yes&session=$composesession");
444 } else {
445 //FIXME: DON'T ECHO HTML FROM CORE!
446 echo ' <br><br><div style="text-align: center;"><a href="' . $location
447 . '/compose.php?saved_sent=yes&amp;session=' . $composesession . '">'
448 . _("Return") . '</a></div>';
449 }
450 exit();
451 } else {
452 if ( !isset($pageheader_sent) || !$pageheader_sent ) {
453 header("Location: $location/right_main.php?mailbox=" . urlencode($draft_folder) .
454 "&startMessage=1&note=".urlencode($draft_message));
455 } else {
456 //FIXME: DON'T ECHO HTML FROM CORE!
457 echo ' <br><br><div style="text-align: center;"><a href="' . $location
458 . '/right_main.php?mailbox=' . urlencode($draft_folder)
459 . '&amp;startMessage=1&amp;note=' . urlencode($draft_message) .'">'
460 . _("Return") . '</a></div>';
461 }
462 exit();
463 }
464 }
465 }
466
467 if ($send) {
468 if (isset($_FILES['attachfile']) &&
469 $_FILES['attachfile']['tmp_name'] &&
470 $_FILES['attachfile']['tmp_name'] != 'none') {
471 $AttachFailure = saveAttachedFiles($session);
472 }
473 if (checkInput(false) && !isset($AttachFailure)) {
474 if ($mailbox == "All Folders") {
475 /* We entered compose via the search results page */
476 $mailbox = 'INBOX'; /* Send 'em to INBOX, that's safe enough */
477 }
478 $urlMailbox = urlencode($mailbox);
479 if (! isset($passed_id)) {
480 $passed_id = 0;
481 }
482 /**
483 * Set $default_charset to correspond with the user's selection
484 * of language interface.
485 */
486 set_my_charset();
487 /**
488 * This is to change all newlines to \n
489 * We'll change them to \r\n later (in the sendMessage function)
490 */
491 $body = str_replace("\r\n", "\n", $body);
492 $body = str_replace("\r", "\n", $body);
493
494 /**
495 * Rewrap $body so that no line is bigger than $editor_size
496 */
497 $body = explode("\n", $body);
498 $newBody = '';
499 foreach ($body as $line) {
500 if( $line <> '-- ' ) {
501 $line = rtrim($line);
502 }
503 if (sq_strlen($line, $default_charset) <= $editor_size + 1) {
504 $newBody .= $line . "\n";
505 } else {
506 sqWordWrap($line, $editor_size, $default_charset);
507 $newBody .= $line . "\n";
508
509 }
510
511 }
512 $body = $newBody;
513
514 $Result = deliverMessage($composeMessage);
515
516 if ($Result)
517 $mail_sent = 'yes';
518 else
519 $mail_sent = 'no';
520
521 // NOTE: this hook changed in 1.5.2 from sending $Result and
522 // $composeMessage as args #2 and #3 to being in an array
523 // under arg #2
524 $temp = array(&$Result, &$composeMessage, &$mail_sent);
525 do_hook('compose_send_after', $temp);
526 if (! $Result) {
527 showInputForm($session);
528 exit();
529 }
530
531 /* if it is resumed draft, delete draft message */
532 if ( isset($delete_draft)) {
533 $imap_stream = sqimap_login($username, false, $imapServerAddress, $imapPort, false);
534 sqimap_mailbox_select($imap_stream, $draft_folder);
535 // bypass_trash=true because message should be saved when deliverMessage() returns true.
536 // in current implementation of sqimap_msgs_list_flag() single message id can
537 // be submitted as string. docs state that it should be array.
538 sqimap_msgs_list_delete($imap_stream, $draft_folder, $delete_draft, true);
539 if ($auto_expunge) {
540 sqimap_mailbox_expunge($imap_stream, $draft_folder, true);
541 }
542 sqimap_logout($imap_stream);
543 }
544 /*
545 * Store the error array in the session because they will be lost on a redirect
546 */
547 $oErrorHandler->saveDelayedErrors();
548 session_write_close();
549
550 if ($compose_new_win == '1') {
551 if ( !isset($pageheader_sent) || !$pageheader_sent ) {
552 header("Location: $location/compose.php?mail_sent=$mail_sent");
553 } else {
554 //FIXME: DON'T ECHO HTML FROM CORE!
555 echo ' <br><br><div style="text-align: center;"><a href="' . $location
556 . '/compose.php?mail_sent=$mail_sent">'
557 . _("Return") . '</a></div>';
558 }
559 exit();
560 } else {
561 if ( !isset($pageheader_sent) || !$pageheader_sent ) {
562 header("Location: $location/right_main.php?mailbox=$urlMailbox".
563 "&startMessage=$startMessage&mail_sent=$mail_sent");
564 } else {
565 //FIXME: DON'T ECHO HTML FROM CORE!
566 echo ' <br><br><div style="text-align: center;"><a href="' . $location
567 . "/right_main.php?mailbox=$urlMailbox"
568 . "&amp;startMessage=$startMessage&amp;mail_sent=$mail_sent\">"
569 . _("Return") . '</a></div>';
570 }
571 exit();
572 }
573 } else {
574 if ($compose_new_win == '1') {
575 compose_Header($color, $mailbox);
576 }
577 else {
578 displayPageHeader($color, $mailbox);
579 }
580 if (isset($AttachFailure)) {
581 plain_error_message(_("Could not move/copy file. File not attached"),
582 $color);
583 }
584 checkInput(true);
585 showInputForm($session);
586 /* sqimap_logout($imapConnection); */
587 }
588 } elseif (isset($html_addr_search_done)) {
589 if ($compose_new_win == '1') {
590 compose_Header($color, $mailbox);
591 }
592 else {
593 displayPageHeader($color, $mailbox);
594 }
595
596 if (isset($send_to_search) && is_array($send_to_search)) {
597 foreach ($send_to_search as $k => $v) {
598 if (substr($k, 0, 1) == 'T') {
599 if ($send_to) {
600 $send_to .= ', ';
601 }
602 $send_to .= $v;
603 }
604 elseif (substr($k, 0, 1) == 'C') {
605 if ($send_to_cc) {
606 $send_to_cc .= ', ';
607 }
608 $send_to_cc .= $v;
609 }
610 elseif (substr($k, 0, 1) == 'B') {
611 if ($send_to_bcc) {
612 $send_to_bcc .= ', ';
613 }
614 $send_to_bcc .= $v;
615 }
616 }
617 }
618 showInputForm($session);
619 } elseif (isset($html_addr_search) && !isset($html_addr_search_cancel)) {
620 if (isset($_FILES['attachfile']) &&
621 $_FILES['attachfile']['tmp_name'] &&
622 $_FILES['attachfile']['tmp_name'] != 'none') {
623 if(saveAttachedFiles($session)) {
624 plain_error_message(_("Could not move/copy file. File not attached"));
625 }
626 }
627 /*
628 * I am using an include so as to elminiate an extra unnecessary
629 * click. If you can think of a better way, please implement it.
630 */
631 include_once('./addrbook_search_html.php');
632 } elseif (isset($attach)) {
633 if ($compose_new_win == '1') {
634 compose_Header($color, $mailbox);
635 } else {
636 displayPageHeader($color, $mailbox);
637 }
638 if (saveAttachedFiles($session)) {
639 plain_error_message(_("Could not move/copy file. File not attached"));
640 }
641 showInputForm($session);
642 }
643 elseif (isset($sigappend)) {
644 $signature = $idents[$identity]['signature'];
645
646 $body .= "\n\n".($prefix_sig==true? "-- \n":'').$signature;
647 if ($compose_new_win == '1') {
648 compose_Header($color, $mailbox);
649 } else {
650 displayPageHeader($color, $mailbox);
651 }
652 showInputForm($session);
653 } elseif (isset($do_delete)) {
654 if ($compose_new_win == '1') {
655 compose_Header($color, $mailbox);
656 } else {
657 displayPageHeader($color, $mailbox);
658 }
659
660 if (isset($delete) && is_array($delete)) {
661 foreach($delete as $index) {
662 if (!empty($composeMessage->entities) && isset($composeMessage->entities[$index])) {
663 $composeMessage->entities[$index]->purgeAttachments();
664 // FIXME: one person reported that unset() didn't do anything at all here, so this is a work-around... but it triggers PHP notices if the unset() doesn't work, which should be fixed... but bigger question is if unset() doesn't work here, what about everywhere else? Anyway, uncomment this if you think you need it
665 //$composeMessage->entities[$index] = NULL;
666 unset ($composeMessage->entities[$index]);
667 }
668 }
669 $new_entities = array();
670 foreach ($composeMessage->entities as $entity) {
671 $new_entities[] = $entity;
672 }
673 $composeMessage->entities = $new_entities;
674 }
675 showInputForm($session);
676 } else {
677 /*
678 * This handles the default case as well as the error case
679 * (they had the same code) --> if (isset($smtpErrors))
680 */
681
682 if ($compose_new_win == '1') {
683 compose_Header($color, $mailbox);
684 } else {
685 displayPageHeader($color, $mailbox);
686 }
687
688 $newmail = true;
689
690 if (!isset($passed_ent_id)) {
691 $passed_ent_id = '';
692 }
693 if (!isset($passed_id)) {
694 $passed_id = '';
695 }
696 if (!isset($mailbox)) {
697 $mailbox = '';
698 }
699 if (!isset($action)) {
700 $action = '';
701 }
702
703 $values = newMail($mailbox,$passed_id,$passed_ent_id, $action, $session);
704
705 /* in case the origin is not read_body.php */
706 if (isset($send_to)) {
707 $values['send_to'] = $send_to;
708 }
709 if (isset($send_to_cc)) {
710 $values['send_to_cc'] = $send_to_cc;
711 }
712 if (isset($send_to_bcc)) {
713 $values['send_to_bcc'] = $send_to_bcc;
714 }
715 if (isset($subject)) {
716 $values['subject'] = $subject;
717 }
718 showInputForm($session, $values);
719 }
720
721 exit();
722
723 /**************** Only function definitions go below *************/
724
725 function getforwardSubject($subject)
726 {
727 if ((substr(strtolower($subject), 0, 4) != 'fwd:') &&
728 (substr(strtolower($subject), 0, 5) != '[fwd:') &&
729 (substr(strtolower($subject), 0, 6) != '[ fwd:')) {
730 $subject = '[Fwd: ' . $subject . ']';
731 }
732 return $subject;
733 }
734
735 /* This function is used when not sending or adding attachments */
736 function newMail ($mailbox='', $passed_id='', $passed_ent_id='', $action='', $session='') {
737 global $editor_size, $default_use_priority, $body, $idents,
738 $use_signature, $data_dir, $username,
739 $key, $imapServerAddress, $imapPort,
740 $composeMessage, $body_quote, $request_mdn, $request_dr,
741 $mdn_user_support, $languages, $squirrelmail_language,
742 $default_charset;
743
744 /*
745 * Set $default_charset to correspond with the user's selection
746 * of language interface. $default_charset global is not correct,
747 * if message is composed in new window.
748 */
749 set_my_charset();
750
751 $send_to = $send_to_cc = $send_to_bcc = $subject = $identity = '';
752 $mailprio = 3;
753
754 if ($passed_id) {
755 $imapConnection = sqimap_login($username, false, $imapServerAddress,
756 $imapPort, 0);
757
758 sqimap_mailbox_select($imapConnection, $mailbox);
759 $message = sqimap_get_message($imapConnection, $passed_id, $mailbox);
760
761 $body = '';
762 if ($passed_ent_id) {
763 /* redefine the messsage in case of message/rfc822 */
764 $message = $message->getEntity($passed_ent_id);
765 /* message is an entity which contains the envelope and type0=message
766 * and type1=rfc822. The actual entities are childs from
767 * $message->entities[0]. That's where the encoding and is located
768 */
769
770 $entities = $message->entities[0]->findDisplayEntity
771 (array(), $alt_order = array('text/plain'));
772 if (!count($entities)) {
773 $entities = $message->entities[0]->findDisplayEntity
774 (array(), $alt_order = array('text/plain','text/html'));
775 }
776 $orig_header = $message->rfc822_header; /* here is the envelope located */
777 /* redefine the message for picking up the attachments */
778 $message = $message->entities[0];
779
780 } else {
781 $entities = $message->findDisplayEntity (array(), $alt_order = array('text/plain'));
782 if (!count($entities)) {
783 $entities = $message->findDisplayEntity (array(), $alt_order = array('text/plain','text/html'));
784 }
785 $orig_header = $message->rfc822_header;
786 }
787
788 $type0 = $message->type0;
789 $type1 = $message->type1;
790 foreach ($entities as $ent) {
791 $msg = $message->getEntity($ent);
792 $type0 = $msg->type0;
793 $type1 = $msg->type1;
794 $unencoded_bodypart = mime_fetch_body($imapConnection, $passed_id, $ent);
795 $body_part_entity = $message->getEntity($ent);
796 $bodypart = decodeBody($unencoded_bodypart,
797 $body_part_entity->header->encoding);
798 if ($type1 == 'html') {
799 $bodypart = str_replace("\n", ' ', $bodypart);
800 $bodypart = preg_replace(array('/<\/?p>/i','/<div><\/div>/i','/<br\s*(\/)*>/i','/<\/?div>/i'), "\n", $bodypart);
801 $bodypart = str_replace(array('&nbsp;','&gt;','&lt;'),array(' ','>','<'),$bodypart);
802 $bodypart = strip_tags($bodypart);
803 }
804 if (isset($languages[$squirrelmail_language]['XTRA_CODE']) &&
805 function_exists($languages[$squirrelmail_language]['XTRA_CODE'] . '_decode')) {
806 if (mb_detect_encoding($bodypart) != 'ASCII') {
807 $bodypart = call_user_func($languages[$squirrelmail_language]['XTRA_CODE'] . '_decode', $bodypart);
808 }
809 }
810
811 // charset encoding in compose form stuff
812 if (isset($body_part_entity->header->parameters['charset'])) {
813 $actual = $body_part_entity->header->parameters['charset'];
814 } else {
815 $actual = 'us-ascii';
816 }
817
818 if ( $actual && is_conversion_safe($actual) && $actual != $default_charset){
819 $bodypart = charset_convert($actual,$bodypart,$default_charset,false);
820 }
821 // end of charset encoding in compose
822
823 $body .= $bodypart;
824 }
825 if ($default_use_priority) {
826 $mailprio = substr($orig_header->priority,0,1);
827 if (!$mailprio) {
828 $mailprio = 3;
829 }
830 } else {
831 $mailprio = '';
832 }
833
834 $from_o = $orig_header->from;
835 if (is_array($from_o)) {
836 if (isset($from_o[0])) {
837 $from_o = $from_o[0];
838 }
839 }
840 if (is_object($from_o)) {
841 $orig_from = $from_o->getAddress();
842 } else {
843 $orig_from = '';
844 }
845
846 $identities = array();
847 if (count($idents) > 1) {
848 foreach($idents as $nr=>$data) {
849 $enc_from_name = '"'.$data['full_name'].'" <'. $data['email_address'].'>';
850 if(strtolower($enc_from_name) == strtolower($orig_from)) {
851 $identity = $nr;
852 // don't stop! need to build $identities array for idents match below
853 //break;
854 }
855 $identities[] = $enc_from_name;
856 }
857
858 $identity_match = $orig_header->findAddress($identities);
859 if ($identity_match) {
860 $identity = $identity_match;
861 }
862 }
863
864 switch ($action) {
865 case ('draft'):
866 $use_signature = FALSE;
867 $composeMessage->rfc822_header = $orig_header;
868 $send_to = decodeHeader($orig_header->getAddr_s('to'),false,false,true);
869 $send_to_cc = decodeHeader($orig_header->getAddr_s('cc'),false,false,true);
870 $send_to_bcc = decodeHeader($orig_header->getAddr_s('bcc'),false,false,true);
871 $send_from = $orig_header->getAddr_s('from');
872 $send_from_parts = new AddressStructure();
873 $send_from_parts = $orig_header->parseAddress($send_from);
874 $send_from_add = $send_from_parts->mailbox . '@' . $send_from_parts->host;
875 $identity = find_identity(array($send_from_add));
876 $subject = decodeHeader($orig_header->subject,false,false,true);
877
878 // Remember the receipt settings
879 $request_mdn = $mdn_user_support && !empty($orig_header->dnt) ? '1' : '0';
880 $request_dr = $mdn_user_support && !empty($orig_header->drnt) ? '1' : '0';
881
882 /* remember the references and in-reply-to headers in case of an reply */
883 //FIXME: it would be better to fiddle with headers inside of the message object or possibly when delivering the message to its destination (drafts folder?); is this possible?
884 $composeMessage->rfc822_header->more_headers['References'] = $orig_header->references;
885 $composeMessage->rfc822_header->more_headers['In-Reply-To'] = $orig_header->in_reply_to;
886 // rewrap the body to clean up quotations and line lengths
887 sqBodyWrap($body, $editor_size);
888 $composeMessage = getAttachments($message, $composeMessage, $passed_id, $entities, $imapConnection);
889 break;
890 case ('edit_as_new'):
891 $send_to = decodeHeader($orig_header->getAddr_s('to'),false,false,true);
892 $send_to_cc = decodeHeader($orig_header->getAddr_s('cc'),false,false,true);
893 $send_to_bcc = decodeHeader($orig_header->getAddr_s('bcc'),false,false,true);
894 $subject = decodeHeader($orig_header->subject,false,false,true);
895 $mailprio = $orig_header->priority;
896 $orig_from = '';
897 $composeMessage = getAttachments($message, $composeMessage, $passed_id, $entities, $imapConnection);
898 // rewrap the body to clean up quotations and line lengths
899 sqBodyWrap($body, $editor_size);
900 break;
901 case ('forward'):
902 $send_to = '';
903 $subject = getforwardSubject(decodeHeader($orig_header->subject,false,false,true));
904 $body = getforwardHeader($orig_header) . $body;
905 // the logic for calling sqUnWordWrap here would be to allow the browser to wrap the lines
906 // forwarded message text should be as undisturbed as possible, so commenting out this call
907 // sqUnWordWrap($body);
908 $composeMessage = getAttachments($message, $composeMessage, $passed_id, $entities, $imapConnection);
909
910 //add a blank line after the forward headers
911 $body = "\n" . $body;
912 break;
913 case ('forward_as_attachment'):
914 $subject = getforwardSubject(decodeHeader($orig_header->subject,false,false,true));
915 $composeMessage = getMessage_RFC822_Attachment($message, $composeMessage, $passed_id, $passed_ent_id, $imapConnection);
916 $body = '';
917 break;
918 case ('reply_all'):
919 if(isset($orig_header->mail_followup_to) && $orig_header->mail_followup_to) {
920 $send_to = $orig_header->getAddr_s('mail_followup_to');
921 } else {
922 $send_to_cc = replyAllString($orig_header);
923 $send_to_cc = decodeHeader($send_to_cc,false,false,true);
924 }
925 case ('reply'):
926 // skip this if send_to was already set right above here
927 if(!$send_to) {
928 $send_to = $orig_header->reply_to;
929 if (is_array($send_to) && count($send_to)) {
930 $send_to = $orig_header->getAddr_s('reply_to');
931 } else if (is_object($send_to)) { /* unneccesarry, just for failsafe purpose */
932 $send_to = $orig_header->getAddr_s('reply_to');
933 } else {
934 $send_to = $orig_header->getAddr_s('from');
935 }
936 }
937 $send_to = decodeHeader($send_to,false,false,true);
938 $subject = decodeHeader($orig_header->subject,false,false,true);
939 $subject = str_replace('"', "'", $subject);
940 $subject = trim($subject);
941 if (substr(strtolower($subject), 0, 3) != 're:') {
942 $subject = 'Re: ' . $subject;
943 }
944 /* this corrects some wrapping/quoting problems on replies */
945 $rewrap_body = explode("\n", $body);
946 $from = (is_array($orig_header->from) && !empty($orig_header->from)) ? $orig_header->from[0] : $orig_header->from;
947 $body = '';
948 $strip_sigs = getPref($data_dir, $username, 'strip_sigs');
949 foreach ($rewrap_body as $line) {
950 if ($strip_sigs && substr($line,0,3) == '-- ') {
951 break;
952 }
953 if (preg_match("/^(>+)/", $line, $matches)) {
954 $gt = $matches[1];
955 $body .= $body_quote . str_replace("\n", "\n$body_quote$gt ", rtrim($line)) ."\n";
956 } else {
957 $body .= $body_quote . (!empty($body_quote) ? ' ' : '') . str_replace("\n", "\n$body_quote" . (!empty($body_quote) ? ' ' : ''), rtrim($line)) . "\n";
958 }
959 }
960
961 //rewrap the body to clean up quotations and line lengths
962 $body = sqBodyWrap ($body, $editor_size);
963
964 $body = getReplyCitation($from , $orig_header->date) . $body;
965 $composeMessage->reply_rfc822_header = $orig_header;
966
967 break;
968 default:
969 break;
970 }
971 //FIXME: we used to register $compose_messages in the session here, but not any more - so do we still need the session_write_close() and sqimap_logout() here? We probably need the IMAP logout, but what about the session closure?
972 session_write_close();
973 sqimap_logout($imapConnection);
974 }
975 $ret = array( 'send_to' => $send_to,
976 'send_to_cc' => $send_to_cc,
977 'send_to_bcc' => $send_to_bcc,
978 'subject' => $subject,
979 'mailprio' => $mailprio,
980 'body' => $body,
981 'identity' => $identity );
982
983 return ($ret);
984 } /* function newMail() */
985
986 /**
987 * downloads attachments from original message, stores them in attachment directory and adds
988 * them to composed message.
989 * @param object $message
990 * @param object $composeMessage
991 * @param integer $passed_id
992 * @param mixed $entities
993 * @param mixed $imapConnection
994 * @return object
995 */
996 function getAttachments($message, &$composeMessage, $passed_id, $entities, $imapConnection) {
997 global $squirrelmail_language, $languages, $username, $attachment_dir;
998
999 if (!count($message->entities) ||
1000 ($message->type0 == 'message' && $message->type1 == 'rfc822')) {
1001 if ( !in_array($message->entity_id, $entities) && $message->entity_id) {
1002 switch ($message->type0) {
1003 case 'message':
1004 if ($message->type1 == 'rfc822') {
1005 $filename = $message->rfc822_header->subject;
1006 if ($filename == "") {
1007 $filename = "untitled-".$message->entity_id;
1008 }
1009 $filename .= '.eml';
1010 } else {
1011 $filename = $message->getFilename();
1012 }
1013 break;
1014 default:
1015 if (!$message->mime_header) { /* temporary hack */
1016 $message->mime_header = $message->header;
1017 }
1018 $filename = $message->getFilename();
1019 break;
1020 }
1021 $filename = str_replace('&#32;', ' ', decodeHeader($filename));
1022 if (isset($languages[$squirrelmail_language]['XTRA_CODE']) &&
1023 function_exists($languages[$squirrelmail_language]['XTRA_CODE'] . '_encode')) {
1024 $filename = call_user_func($languages[$squirrelmail_language]['XTRA_CODE'] . '_encode', $filename);
1025 }
1026
1027 $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
1028 $localfilename = sq_get_attach_tempfile();
1029 $message->att_local_name = $localfilename;
1030
1031 $composeMessage->initAttachment($message->type0.'/'.$message->type1,$filename,
1032 $localfilename);
1033
1034 /* Write Attachment to file */
1035 $fp = fopen ($hashed_attachment_dir . '/' . $localfilename, 'wb');
1036 mime_print_body_lines ($imapConnection, $passed_id, $message->entity_id, $message->header->encoding, $fp);
1037 fclose ($fp);
1038 }
1039 } else {
1040 for ($i=0, $entCount=count($message->entities); $i<$entCount;$i++) {
1041 $composeMessage=getAttachments($message->entities[$i], $composeMessage, $passed_id, $entities, $imapConnection);
1042 }
1043 }
1044 return $composeMessage;
1045 }
1046
1047 function getMessage_RFC822_Attachment($message, $composeMessage, $passed_id,
1048 $passed_ent_id='', $imapConnection) {
1049 if (!$passed_ent_id) {
1050 $body_a = sqimap_run_command($imapConnection,
1051 'FETCH '.$passed_id.' RFC822',
1052 TRUE, $response, $readmessage,
1053 TRUE);
1054 } else {
1055 $body_a = sqimap_run_command($imapConnection,
1056 'FETCH '.$passed_id.' BODY['.$passed_ent_id.']',
1057 TRUE, $response, $readmessage, TRUE);
1058 $message = $message->parent;
1059 }
1060 if ($response == 'OK') {
1061 $subject = encodeHeader($message->rfc822_header->subject);
1062 array_shift($body_a);
1063 array_pop($body_a);
1064 $body = implode('', $body_a) . "\r\n";
1065
1066 global $username, $attachment_dir;
1067 $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
1068 $localfilename = sq_get_attach_tempfile();
1069 $fp = fopen($hashed_attachment_dir . '/' . $localfilename, 'wb');
1070 fwrite ($fp, $body);
1071 fclose($fp);
1072 $composeMessage->initAttachment('message/rfc822',$subject.'.eml',
1073 $localfilename);
1074 }
1075 return $composeMessage;
1076 }
1077
1078 function showInputForm ($session, $values=false) {
1079 global $send_to, $send_to_cc, $send_to_bcc,
1080 $body, $startMessage, $action, $attachments,
1081 $use_signature, $signature, $prefix_sig, $session_expired,
1082 $editor_size, $editor_height, $subject, $newmail,
1083 $use_javascript_addr_book, $passed_id, $mailbox,
1084 $from_htmladdr_search, $location_of_buttons, $attachment_dir,
1085 $username, $data_dir, $identity, $idents, $delete_draft,
1086 $mailprio, $compose_new_win, $saved_draft, $mail_sent, $sig_first,
1087 $composeMessage, $composesession, $default_charset,
1088 $compose_onsubmit, $oTemplate, $oErrorHandler;
1089
1090 if (checkForJavascript()) {
1091 $onfocus = ' onfocus="alreadyFocused=true;"';
1092 $onfocus_array = array('onfocus' => 'alreadyFocused=true;');
1093 }
1094 else {
1095 $onfocus = '';
1096 $onfocus_array = array();
1097 }
1098
1099 if ($values) {
1100 $send_to = $values['send_to'];
1101 $send_to_cc = $values['send_to_cc'];
1102 $send_to_bcc = $values['send_to_bcc'];
1103 $subject = $values['subject'];
1104 $mailprio = $values['mailprio'];
1105 $body = $values['body'];
1106 $identity = (int) $values['identity'];
1107 } else {
1108 $send_to = decodeHeader($send_to, true, false);
1109 $send_to_cc = decodeHeader($send_to_cc, true, false);
1110 $send_to_bcc = decodeHeader($send_to_bcc, true, false);
1111 }
1112
1113 if ($use_javascript_addr_book) {
1114 //FIXME: NO HTML IN CORE!
1115 echo "\n". '<script type="text/javascript">'."\n<!--\n" .
1116 'function open_abook() { ' . "\n" .
1117 ' var nwin = window.open("addrbook_popup.php","abookpopup",' .
1118 '"width=670,height=300,resizable=yes,scrollbars=yes");' . "\n" .
1119 ' if((!nwin.opener) && (document.windows != null))' . "\n" .
1120 ' nwin.opener = document.windows;' . "\n" .
1121 "}\n" .
1122 "// -->\n</script>\n\n";
1123 }
1124
1125 //FIXME: NO HTML IN CORE!
1126 echo "\n" . '<form name="compose" action="compose.php" method="post" ' .
1127 'enctype="multipart/form-data"';
1128
1129 $compose_onsubmit = array();
1130 global $null;
1131 do_hook('compose_form', $null);
1132
1133 // Plugins that use compose_form hook can add an array entry
1134 // to the globally scoped $compose_onsubmit; we add them up
1135 // here and format the form tag's full onsubmit handler.
1136 // Each plugin should use "return false" if they need to
1137 // stop form submission but otherwise should NOT use "return
1138 // true" to give other plugins the chance to do what they need
1139 // to do; SquirrelMail itself will add the final "return true".
1140 // Onsubmit text is enclosed inside of double quotes, so plugins
1141 // need to quote accordingly.
1142 if (checkForJavascript()) {
1143 $onsubmit_text = ' onsubmit="';
1144 if (empty($compose_onsubmit))
1145 $compose_onsubmit = array();
1146 else if (!is_array($compose_onsubmit))
1147 $compose_onsubmit = array($compose_onsubmit);
1148
1149 foreach ($compose_onsubmit as $text) {
1150 $text = trim($text);
1151 if (substr($text, -1) != ';' && substr($text, -1) != '}')
1152 $text .= '; ';
1153 $onsubmit_text .= $text;
1154 }
1155
1156 //FIXME: DON'T ECHO HTML FROM CORE!
1157 echo $onsubmit_text . ' return true;"';
1158 }
1159
1160
1161 //FIXME: NO HTML IN CORE!
1162 echo ">\n";
1163
1164 //FIXME: DON'T ECHO HTML FROM CORE!
1165 echo addHidden('startMessage', $startMessage);
1166
1167 if ($action == 'draft') {
1168 //FIXME: DON'T ECHO HTML FROM CORE!
1169 echo addHidden('delete_draft', $passed_id);
1170 }
1171 if (isset($delete_draft)) {
1172 //FIXME: DON'T ECHO HTML FROM CORE!
1173 echo addHidden('delete_draft', $delete_draft);
1174 }
1175 if (isset($session)) {
1176 //FIXME: DON'T ECHO HTML FROM CORE!
1177 echo addHidden('session', $session);
1178 }
1179
1180 if (isset($passed_id)) {
1181 //FIXME: DON'T ECHO HTML FROM CORE!
1182 echo addHidden('passed_id', $passed_id);
1183 }
1184
1185 if ($saved_draft == 'yes') {
1186 $oTemplate->assign('note', _("Your draft has been saved."));
1187 $oTemplate->display('note.tpl');
1188 }
1189 if ($mail_sent == 'yes') {
1190 $oTemplate->assign('note', _("Your mail has been sent."));
1191 $oTemplate->display('note.tpl');
1192 }
1193 if ($compose_new_win == '1') {
1194 $oTemplate->display('compose_newwin_close.tpl');
1195 }
1196
1197 if ($location_of_buttons == 'top') {
1198 //FIXME: DON'T ECHO HTML FROM CORE!
1199 showComposeButtonRow();
1200 }
1201
1202 $identities = array();
1203 if (count($idents) > 1) {
1204 reset($idents);
1205 foreach($idents as $id => $data) {
1206 $identities[$id] = $data['full_name'].' &lt;'.$data['email_address'].'&gt;';
1207 }
1208 }
1209
1210 $oTemplate->assign('identities', $identities);
1211 $oTemplate->assign('identity_def', $identity);
1212 $oTemplate->assign('input_onfocus', 'onfocus="'.join(' ', $onfocus_array).'"');
1213
1214 $oTemplate->assign('to', htmlspecialchars($send_to));
1215 $oTemplate->assign('cc', htmlspecialchars($send_to_cc));
1216 $oTemplate->assign('bcc', htmlspecialchars($send_to_bcc));
1217 $oTemplate->assign('subject', htmlspecialchars($subject));
1218
1219 // access keys...
1220 //
1221 global $accesskey_compose_to, $accesskey_compose_cc,
1222 $accesskey_compose_identity, $accesskey_compose_bcc,
1223 $accesskey_compose_subject;
1224 $oTemplate->assign('accesskey_compose_identity', $accesskey_compose_identity);
1225 $oTemplate->assign('accesskey_compose_to', $accesskey_compose_to);
1226 $oTemplate->assign('accesskey_compose_cc', $accesskey_compose_cc);
1227 $oTemplate->assign('accesskey_compose_bcc', $accesskey_compose_bcc);
1228 $oTemplate->assign('accesskey_compose_subject', $accesskey_compose_subject);
1229
1230 $oTemplate->display('compose_header.tpl');
1231
1232 if ($location_of_buttons == 'between') {
1233 //FIXME: DON'T ECHO HTML FROM CORE!
1234 showComposeButtonRow();
1235 }
1236
1237 $body_str = '';
1238 if ($use_signature == true && $newmail == true && !isset($from_htmladdr_search)) {
1239 $signature = $idents[$identity]['signature'];
1240
1241 if ($sig_first == '1') {
1242 /*
1243 * FIXME: test is specific to ja_JP translation implementation.
1244 * This test might apply incorrect conversion to other translations, but
1245 * use of 7bit iso-2022-jp charset in other translations might have other
1246 * issues too.
1247 */
1248 if ($default_charset == 'iso-2022-jp') {
1249 $body_str = "\n\n".($prefix_sig==true? "-- \n":'').mb_convert_encoding($signature, 'EUC-JP');
1250 } else {
1251 $body_str = "\n\n".($prefix_sig==true? "-- \n":'').decodeHeader($signature,false,false);
1252 }
1253 $body_str .= "\n\n".htmlspecialchars(decodeHeader($body,false,false));
1254 } else {
1255 $body_str = "\n\n".htmlspecialchars(decodeHeader($body,false,false));
1256 // FIXME: test is specific to ja_JP translation implementation. See above comments.
1257 if ($default_charset == 'iso-2022-jp') {
1258 $body_str .= "\n\n".($prefix_sig==true? "-- \n":'').mb_convert_encoding($signature, 'EUC-JP');
1259 } else {
1260 $body_str .= "\n\n".($prefix_sig==true? "-- \n":'').decodeHeader($signature,false,false);
1261 }
1262 }
1263 } else {
1264 $body_str = htmlspecialchars(decodeHeader($body,false,false));
1265 }
1266
1267 $oTemplate->assign('editor_width', (int)$editor_size);
1268 $oTemplate->assign('editor_height', (int)$editor_height);
1269 $oTemplate->assign('input_onfocus', 'onfocus="'.join(' ', $onfocus_array).'"');
1270 $oTemplate->assign('body', $body_str);
1271 $oTemplate->assign('show_bottom_send', $location_of_buttons!='bottom');
1272
1273 // access keys...
1274 //
1275 global $accesskey_compose_body, $accesskey_compose_send;
1276 $oTemplate->assign('accesskey_compose_body', $accesskey_compose_body);
1277 $oTemplate->assign('accesskey_compose_send', $accesskey_compose_send);
1278
1279 $oTemplate->display ('compose_body.tpl');
1280
1281 if ($location_of_buttons == 'bottom') {
1282 //FIXME: DON'T ECHO HTML FROM CORE!
1283 showComposeButtonRow();
1284 }
1285
1286 // composeMessage can be empty when coming from a restored session
1287 if (is_object($composeMessage) && $composeMessage->entities)
1288 $attach_array = $composeMessage->entities;
1289 if ($session_expired && !empty($attachments) && is_array($attachments))
1290 $attach_array = $attachments;
1291
1292 /* This code is for attachments */
1293 if ((bool) ini_get('file_uploads')) {
1294
1295 /* Calculate the max size for an uploaded file.
1296 * This is advisory for the user because we can't actually prevent
1297 * people to upload too large files. */
1298 $sizes = array();
1299 /* php.ini vars which influence the max for uploads */
1300 $configvars = array('post_max_size', 'memory_limit', 'upload_max_filesize');
1301 foreach($configvars as $var) {
1302 /* skip 0 or empty values, and -1 which means 'unlimited' */
1303 if( $size = getByteSize(ini_get($var)) ) {
1304 if ( $size != '-1' ) {
1305 $sizes[] = $size;
1306 }
1307 }
1308 }
1309
1310 $attach = array();
1311 global $username, $attachment_dir;
1312 $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
1313 if (!empty($attach_array)) {
1314 foreach ($attach_array as $key => $attachment) {
1315 $attached_file = $attachment->att_local_name;
1316 if ($attachment->att_local_name || $attachment->body_part) {
1317 $attached_filename = decodeHeader($attachment->mime_header->getParameter('name'));
1318 $type = $attachment->mime_header->type0.'/'.
1319 $attachment->mime_header->type1;
1320
1321 $a = array();
1322 $a['Key'] = $key;
1323 $a['FileName'] = $attached_filename;
1324 $a['ContentType'] = $type;
1325 $a['Size'] = filesize($hashed_attachment_dir . '/' . $attached_file);
1326 $attach[$key] = $a;
1327 }
1328 }
1329 }
1330
1331 $max = min($sizes);
1332 $oTemplate->assign('max_file_size', empty($max) ? -1 : $max);
1333 $oTemplate->assign('attachments', $attach);
1334
1335 // access keys...
1336 //
1337 global $accesskey_compose_attach_browse, $accesskey_compose_attach,
1338 $accesskey_compose_delete_attach;
1339 $oTemplate->assign('accesskey_compose_attach_browse', $accesskey_compose_attach_browse);
1340 $oTemplate->assign('accesskey_compose_attach', $accesskey_compose_attach);
1341 $oTemplate->assign('accesskey_compose_delete_attach', $accesskey_compose_delete_attach);
1342
1343 $oTemplate->display('compose_attachments.tpl');
1344 } // End of file_uploads if-block
1345 /* End of attachment code */
1346
1347 //FIXME: no direct echoing to browser, no HTML output in core!
1348 echo addHidden('username', $username).
1349 addHidden('smaction', $action).
1350 addHidden('mailbox', $mailbox);
1351 sqgetGlobalVar('QUERY_STRING', $queryString, SQ_SERVER);
1352 //FIXME: no direct echoing to browser, no HTML output in core!
1353 echo addHidden('composesession', $composesession).
1354 addHidden('querystring', $queryString).
1355 (!empty($attach_array) ?
1356 addHidden('attachments', urlencode(serialize($attach_array))) : '').
1357 "</form>\n";
1358 if (!(bool) ini_get('file_uploads')) {
1359 /* File uploads are off, so we didn't show that part of the form.
1360 To avoid bogus bug reports, tell the user why. */
1361 //FIXME: no direct echoing to browser, no HTML output in core!
1362 echo '<p style="text-align:center">'
1363 . _("Because PHP file uploads are turned off, you can not attach files to this message. Please see your system administrator for details.")
1364 . "</p>\r\n";
1365 }
1366
1367 if ($compose_new_win=='1') {
1368 $oTemplate->display('compose_newwin_close.tpl');
1369 }
1370
1371 do_hook('compose_bottom', $null);
1372
1373 $oErrorHandler->setDelayedErrors(false);
1374 $oTemplate->display('footer.tpl');
1375 }
1376
1377
1378 function showComposeButtonRow() {
1379 global $use_javascript_addr_book, $save_as_draft,
1380 $default_use_priority, $mailprio, $default_use_mdn,
1381 $request_mdn, $request_dr,
1382 $data_dir, $username;
1383
1384 global $oTemplate, $buffer_hook;
1385
1386 if ($default_use_priority) {
1387 $priorities = array('1'=>_("High"), '3'=>_("Normal"), '5'=>_("Low"));
1388 $priority = isset($mailprio) ? $mailprio : 3;
1389 } else {
1390 $priorities = array();
1391 $priority = NULL;
1392 }
1393
1394 $mdn_user_support=getPref($data_dir, $username, 'mdn_user_support',$default_use_mdn);
1395
1396 $address_book_button_attribs = array();
1397 global $accesskey_compose_addresses;
1398 if ($accesskey_compose_addresses != 'NONE')
1399 $address_book_button_attribs['accesskey'] = $accesskey_compose_addresses;
1400 if ($use_javascript_addr_book && checkForJavascript()) {
1401 $addr_book = addButton(_("Addresses"),
1402 null,
1403 array_merge($address_book_button_attribs, array('onclick' => 'javascript:open_abook();')));
1404 } else {
1405 $addr_book = addSubmit(_("Addresses"), 'html_addr_search', $address_book_button_attribs);
1406 }
1407
1408 $oTemplate->assign('allow_priority', $default_use_priority==1);
1409 $oTemplate->assign('priority_list', $priorities);
1410 $oTemplate->assign('current_priority', $priority);
1411
1412 $oTemplate->assign('notifications_enabled', $mdn_user_support==1);
1413 $oTemplate->assign('read_receipt', $request_mdn=='1');
1414 $oTemplate->assign('delivery_receipt', $request_dr=='1');
1415
1416 $oTemplate->assign('drafts_enabled', $save_as_draft);
1417 $oTemplate->assign('address_book_button', $addr_book);
1418
1419 // access keys...
1420 //
1421 global $accesskey_compose_priority, $accesskey_compose_on_read,
1422 $accesskey_compose_on_delivery, $accesskey_compose_signature,
1423 $accesskey_compose_save_draft, $accesskey_compose_send;
1424 $oTemplate->assign('accesskey_compose_priority', $accesskey_compose_priority);
1425 $oTemplate->assign('accesskey_compose_on_read', $accesskey_compose_on_read);
1426 $oTemplate->assign('accesskey_compose_on_delivery', $accesskey_compose_on_delivery);
1427 $oTemplate->assign('accesskey_compose_signature', $accesskey_compose_signature);
1428 $oTemplate->assign('accesskey_compose_save_draft', $accesskey_compose_save_draft);
1429 $oTemplate->assign('accesskey_compose_send', $accesskey_compose_send);
1430
1431 $oTemplate->display('compose_buttons.tpl');
1432 }
1433
1434 function checkInput ($show) {
1435 /*
1436 * I implemented the $show variable because the error messages
1437 * were getting sent before the page header. So, I check once
1438 * using $show=false, and then when i'm ready to display the error
1439 * message, show=true
1440 */
1441 global $send_to, $send_to_cc, $send_to_bcc;
1442
1443 $send_to = trim($send_to);
1444 $send_to_cc = trim($send_to_cc);
1445 $send_to_bcc = trim($send_to_bcc);
1446 if (empty($send_to) && empty($send_to_cc) && empty($send_to_bcc)) {
1447 if ($show) {
1448 plain_error_message(_("You have not filled in the \"To:\" field."));
1449 }
1450 return false;
1451 }
1452 return true;
1453 } /* function checkInput() */
1454
1455
1456 /* True if FAILURE */
1457 function saveAttachedFiles($session) {
1458 global $composeMessage, $username, $attachment_dir;
1459
1460 /* get out of here if no file was attached at all */
1461 if (! is_uploaded_file($_FILES['attachfile']['tmp_name']) ) {
1462 return true;
1463 }
1464
1465 $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
1466 $localfilename = sq_get_attach_tempfile();
1467 $fullpath = $hashed_attachment_dir . '/' . $localfilename;
1468
1469 // m_u_f works better with restricted PHP installs (safe_mode, open_basedir),
1470 // if that doesn't work, try a simple rename.
1471 if (!sq_call_function_suppress_errors('move_uploaded_file', array($_FILES['attachfile']['tmp_name'], $fullpath))) {
1472 if (!sq_call_function_suppress_errors('rename', array($_FILES['attachfile']['tmp_name'], $fullpath))) {
1473 return true;
1474 }
1475 }
1476 $type = strtolower($_FILES['attachfile']['type']);
1477 $name = $_FILES['attachfile']['name'];
1478 $composeMessage->initAttachment($type, $name, $localfilename);
1479 }
1480
1481 /* parse values like 8M and 2k into bytes */
1482 function getByteSize($ini_size) {
1483
1484 if(!$ini_size) {
1485 return FALSE;
1486 }
1487
1488 $ini_size = trim($ini_size);
1489
1490 // if there's some kind of letter at the end of the string we need to multiply.
1491 if(!is_numeric(substr($ini_size, -1))) {
1492
1493 switch(strtoupper(substr($ini_size, -1))) {
1494 case 'G':
1495 $bytesize = 1073741824;
1496 break;
1497 case 'M':
1498 $bytesize = 1048576;
1499 break;
1500 case 'K':
1501 $bytesize = 1024;
1502 break;
1503 }
1504
1505 return ($bytesize * (int)substr($ini_size, 0, -1));
1506 }
1507
1508 return $ini_size;
1509 }
1510
1511
1512 /**
1513 * temporary function to make use of the deliver class.
1514 * In the future the responsible backend should be automaticly loaded
1515 * and conf.pl should show a list of available backends.
1516 * The message also should be constructed by the message class.
1517 *
1518 * @param object $composeMessage The message being sent. Please note
1519 * that it is passed by reference and
1520 * will be returned modified, with additional
1521 * headers, such as Message-ID, Date, In-Reply-To,
1522 * References, and so forth.
1523 *
1524 * @return boolean FALSE if delivery failed, or some non-FALSE value
1525 * upon success.
1526 *
1527 */
1528 function deliverMessage(&$composeMessage, $draft=false) {
1529 global $send_to, $send_to_cc, $send_to_bcc, $mailprio, $subject, $body,
1530 $username, $identity, $idents, $data_dir,
1531 $request_mdn, $request_dr, $default_charset, $useSendmail,
1532 $domain, $action, $default_move_to_sent, $move_to_sent,
1533 $imapServerAddress, $imapPort, $sent_folder, $key;
1534
1535 $rfc822_header = $composeMessage->rfc822_header;
1536
1537 $abook = addressbook_init(false, true);
1538 $rfc822_header->to = $rfc822_header->parseAddress($send_to,true, array(), '', $domain, array(&$abook,'lookup'));
1539 $rfc822_header->cc = $rfc822_header->parseAddress($send_to_cc,true,array(), '',$domain, array(&$abook,'lookup'));
1540 $rfc822_header->bcc = $rfc822_header->parseAddress($send_to_bcc,true, array(), '',$domain, array(&$abook,'lookup'));
1541 $rfc822_header->priority = $mailprio;
1542 $rfc822_header->subject = $subject;
1543
1544 $special_encoding='';
1545 if (strtolower($default_charset) == 'iso-2022-jp') {
1546 if (mb_detect_encoding($body) == 'ASCII') {
1547 $special_encoding = '8bit';
1548 } else {
1549 $body = mb_convert_encoding($body, 'JIS');
1550 $special_encoding = '7bit';
1551 }
1552 }
1553 $composeMessage->setBody($body);
1554
1555 $reply_to = '';
1556 $reply_to = $idents[$identity]['reply_to'];
1557
1558 $from_addr = build_from_header($identity);
1559 $rfc822_header->from = $rfc822_header->parseAddress($from_addr,true);
1560 if ($reply_to) {
1561 $rfc822_header->reply_to = $rfc822_header->parseAddress($reply_to,true);
1562 }
1563 /* Receipt: On Read */
1564 if (isset($request_mdn) && $request_mdn) {
1565 $rfc822_header->dnt = $rfc822_header->parseAddress($from_addr,true);
1566 } elseif (isset($rfc822_header->dnt)) {
1567 unset($rfc822_header->dnt);
1568 }
1569
1570 /* Receipt: On Delivery */
1571 if (!empty($request_dr)) {
1572 //FIXME: it would be better to fiddle with headers inside of the message object or possibly when delivering the message to its destination; is this possible?
1573 $rfc822_header->more_headers['Return-Receipt-To'] = $from_addr;
1574 } elseif (isset($rfc822_header->more_headers['Return-Receipt-To'])) {
1575 unset($rfc822_header->more_headers['Return-Receipt-To']);
1576 }
1577
1578 /* multipart messages */
1579 if (count($composeMessage->entities)) {
1580 $message_body = new Message();
1581 $message_body->body_part = $composeMessage->body_part;
1582 $composeMessage->body_part = '';
1583 $mime_header = new MessageHeader;
1584 $mime_header->type0 = 'text';
1585 $mime_header->type1 = 'plain';
1586 if ($special_encoding) {
1587 $mime_header->encoding = $special_encoding;
1588 } else {
1589 $mime_header->encoding = '8bit';
1590 }
1591 if ($default_charset) {
1592 $mime_header->parameters['charset'] = $default_charset;
1593 }
1594 $message_body->mime_header = $mime_header;
1595 array_unshift($composeMessage->entities, $message_body);
1596 $content_type = new ContentType('multipart/mixed');
1597 } else {
1598 $content_type = new ContentType('text/plain');
1599 if ($special_encoding) {
1600 $rfc822_header->encoding = $special_encoding;
1601 } else {
1602 $rfc822_header->encoding = '8bit';
1603 }
1604 if ($default_charset) {
1605 $content_type->properties['charset']=$default_charset;
1606 }
1607 }
1608
1609 $rfc822_header->content_type = $content_type;
1610 $composeMessage->rfc822_header = $rfc822_header;
1611 if ($action == 'reply' || $action == 'reply_all') {
1612 global $passed_id, $passed_ent_id;
1613 $reply_id = $passed_id;
1614 $reply_ent_id = $passed_ent_id;
1615 } else {
1616 $reply_id = '';
1617 $reply_ent_id = '';
1618 }
1619
1620 /* Here you can modify the message structure just before we hand
1621 it over to deliver; plugin authors note that $composeMessage
1622 is sent and modified by reference since 1.5.2 */
1623 do_hook('compose_send', $composeMessage);
1624
1625 if (!$useSendmail && !$draft) {
1626 require_once(SM_PATH . 'class/deliver/Deliver_SMTP.class.php');
1627 $deliver = new Deliver_SMTP();
1628 global $smtpServerAddress, $smtpPort, $pop_before_smtp, $pop_before_smtp_host;
1629
1630 $authPop = (isset($pop_before_smtp) && $pop_before_smtp) ? true : false;
1631 if (empty($pop_before_smtp_host)) $pop_before_smtp_host = $smtpServerAddress;
1632 get_smtp_user($user, $pass);
1633 $stream = $deliver->initStream($composeMessage,$domain,0,
1634 $smtpServerAddress, $smtpPort, $user, $pass, $authPop, $pop_before_smtp_host);
1635 } elseif (!$draft) {
1636 require_once(SM_PATH . 'class/deliver/Deliver_SendMail.class.php');
1637 global $sendmail_path, $sendmail_args;
1638 // Check for outdated configuration
1639 if (!isset($sendmail_args)) {
1640 if ($sendmail_path=='/var/qmail/bin/qmail-inject') {
1641 $sendmail_args = '';
1642 } else {
1643 $sendmail_args = '-i -t';
1644 }
1645 }
1646 $deliver = new Deliver_SendMail(array('sendmail_args'=>$sendmail_args));
1647 $stream = $deliver->initStream($composeMessage,$sendmail_path);
1648 } elseif ($draft) {
1649 global $draft_folder;
1650 $imap_stream = sqimap_login($username, false, $imapServerAddress,
1651 $imapPort, 0);
1652 if (sqimap_mailbox_exists ($imap_stream, $draft_folder)) {
1653 require_once(SM_PATH . 'class/deliver/Deliver_IMAP.class.php');
1654 $imap_deliver = new Deliver_IMAP();
1655 $success = $imap_deliver->mail($composeMessage, $imap_stream, $reply_id, $reply_ent_id, $imap_stream, $draft_folder);
1656 sqimap_logout($imap_stream);
1657 unset ($imap_deliver);
1658 $composeMessage->purgeAttachments();
1659 return $success;
1660 } else {
1661 $msg = '<br />'.sprintf(_("Error: Draft folder %s does not exist."), htmlspecialchars($draft_folder));
1662 plain_error_message($msg);
1663 return false;
1664 }
1665 }
1666 $success = false;
1667 if ($stream) {
1668 $deliver->mail($composeMessage, $stream, $reply_id, $reply_ent_id);
1669 $success = $deliver->finalizeStream($stream);
1670 }
1671 if (!$success) {
1672 // $deliver->dlv_server_msg is not always server's reply
1673 $msg = _("Message not sent.") . "<br />\n" .
1674 $deliver->dlv_msg;
1675 if (!empty($deliver->dlv_server_msg)) {
1676 // add 'server replied' part only when it is not empty.
1677 // Delivery error can be generated by delivery class itself
1678 $msg.='<br />' .
1679 _("Server replied:") . ' ' . $deliver->dlv_ret_nr . ' ' .
1680 nl2br(htmlspecialchars($deliver->dlv_server_msg));
1681 }
1682 plain_error_message($msg);
1683 } else {
1684 unset ($deliver);
1685 $imap_stream = sqimap_login($username, false, $imapServerAddress, $imapPort, 0);
1686
1687
1688 // mark as replied or forwarded if applicable
1689 //
1690 global $what, $iAccount, $startMessage, $passed_id, $mailbox;
1691
1692 if ($action=='reply' || $action=='reply_all' || $action=='forward' || $action=='forward_as_attachment') {
1693 require(SM_PATH . 'functions/mailbox_display.php');
1694 $aMailbox = sqm_api_mailbox_select($imap_stream, $iAccount, $mailbox,array('setindex' => $what, 'offset' => $startMessage),array());
1695 switch($action) {
1696 case 'reply':
1697 case 'reply_all':
1698 // check if we are allowed to set the \\Answered flag
1699 if (in_array('\\answered',$aMailbox['PERMANENTFLAGS'], true)) {
1700 $aUpdatedMsgs = sqimap_toggle_flag($imap_stream, array($passed_id), '\\Answered', true, false);
1701 if (isset($aUpdatedMsgs[$passed_id]['FLAGS'])) {
1702 /**
1703 * Only update the cached headers if the header is
1704 * cached.
1705 */
1706 if (isset($aMailbox['MSG_HEADERS'][$passed_id])) {
1707 $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS'] = $aMsg['FLAGS'];
1708 }
1709 }
1710 }
1711 break;
1712 case 'forward':
1713 case 'forward_as_attachment':
1714 // check if we are allowed to set the $Forwarded flag (RFC 4550 paragraph 2.8)
1715 if (in_array('$forwarded',$aMailbox['PERMANENTFLAGS'], true) ||
1716 in_array('\\*',$aMailbox['PERMANENTFLAGS'])) {
1717
1718 $aUpdatedMsgs = sqimap_toggle_flag($imap_stream, array($passed_id), '$Forwarded', true, false);
1719 if (isset($aUpdatedMsgs[$passed_id]['FLAGS'])) {
1720 if (isset($aMailbox['MSG_HEADERS'][$passed_id])) {
1721 $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS'] = $aMsg['FLAGS'];
1722 }
1723 }
1724 }
1725 break;
1726 }
1727
1728 /**
1729 * Write mailbox with updated seen flag information back to cache.
1730 */
1731 if(isset($aUpdatedMsgs[$passed_id])) {
1732 $mailbox_cache[$iAccount.'_'.$aMailbox['NAME']] = $aMailbox;
1733 sqsession_register($mailbox_cache,'mailbox_cache');
1734 }
1735
1736 }
1737
1738
1739 // move to sent folder
1740 //
1741 $move_to_sent = getPref($data_dir,$username,'move_to_sent');
1742 if (isset($default_move_to_sent) && ($default_move_to_sent != 0)) {
1743 $svr_allow_sent = true;
1744 } else {
1745 $svr_allow_sent = false;
1746 }
1747
1748 if (isset($sent_folder) && (($sent_folder != '') || ($sent_folder != 'none'))
1749 && sqimap_mailbox_exists( $imap_stream, $sent_folder)) {
1750 $fld_sent = true;
1751 } else {
1752 $fld_sent = false;
1753 }
1754
1755 if ((isset($move_to_sent) && ($move_to_sent != 0)) || (!isset($move_to_sent))) {
1756 $lcl_allow_sent = true;
1757 } else {
1758 $lcl_allow_sent = false;
1759 }
1760
1761 if (($fld_sent && $svr_allow_sent && !$lcl_allow_sent) || ($fld_sent && $lcl_allow_sent)) {
1762 if ($action == 'reply' || $action == 'reply_all') {
1763 $save_reply_with_orig=getPref($data_dir,$username,'save_reply_with_orig');
1764 if ($save_reply_with_orig) {
1765 $sent_folder = $mailbox;
1766 }
1767 }
1768 require_once(SM_PATH . 'class/deliver/Deliver_IMAP.class.php');
1769 $imap_deliver = new Deliver_IMAP();
1770 $imap_deliver->mail($composeMessage, $imap_stream, $reply_id, $reply_ent_id, $imap_stream, $sent_folder);
1771 unset ($imap_deliver);
1772 }
1773
1774
1775 // final cleanup
1776 //
1777 $composeMessage->purgeAttachments();
1778 sqimap_logout($imap_stream);
1779
1780 }
1781 return $success;
1782 }