Add accesskey to the From drop-down too
[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_bcc, $accesskey_compose_subject;
1223 $oTemplate->assign('accesskey_compose_to', $accesskey_compose_to);
1224 $oTemplate->assign('accesskey_compose_cc', $accesskey_compose_cc);
1225 $oTemplate->assign('accesskey_compose_bcc', $accesskey_compose_bcc);
1226 $oTemplate->assign('accesskey_compose_subject', $accesskey_compose_subject);
1227
1228 $oTemplate->display('compose_header.tpl');
1229
1230 if ($location_of_buttons == 'between') {
1231 //FIXME: DON'T ECHO HTML FROM CORE!
1232 showComposeButtonRow();
1233 }
1234
1235 $body_str = '';
1236 if ($use_signature == true && $newmail == true && !isset($from_htmladdr_search)) {
1237 $signature = $idents[$identity]['signature'];
1238
1239 if ($sig_first == '1') {
1240 /*
1241 * FIXME: test is specific to ja_JP translation implementation.
1242 * This test might apply incorrect conversion to other translations, but
1243 * use of 7bit iso-2022-jp charset in other translations might have other
1244 * issues too.
1245 */
1246 if ($default_charset == 'iso-2022-jp') {
1247 $body_str = "\n\n".($prefix_sig==true? "-- \n":'').mb_convert_encoding($signature, 'EUC-JP');
1248 } else {
1249 $body_str = "\n\n".($prefix_sig==true? "-- \n":'').decodeHeader($signature,false,false);
1250 }
1251 $body_str .= "\n\n".htmlspecialchars(decodeHeader($body,false,false));
1252 } else {
1253 $body_str = "\n\n".htmlspecialchars(decodeHeader($body,false,false));
1254 // FIXME: test is specific to ja_JP translation implementation. See above comments.
1255 if ($default_charset == 'iso-2022-jp') {
1256 $body_str .= "\n\n".($prefix_sig==true? "-- \n":'').mb_convert_encoding($signature, 'EUC-JP');
1257 } else {
1258 $body_str .= "\n\n".($prefix_sig==true? "-- \n":'').decodeHeader($signature,false,false);
1259 }
1260 }
1261 } else {
1262 $body_str = htmlspecialchars(decodeHeader($body,false,false));
1263 }
1264
1265 $oTemplate->assign('editor_width', (int)$editor_size);
1266 $oTemplate->assign('editor_height', (int)$editor_height);
1267 $oTemplate->assign('input_onfocus', 'onfocus="'.join(' ', $onfocus_array).'"');
1268 $oTemplate->assign('body', $body_str);
1269 $oTemplate->assign('show_bottom_send', $location_of_buttons!='bottom');
1270
1271 // access keys...
1272 //
1273 global $accesskey_compose_body, $accesskey_compose_send;
1274 $oTemplate->assign('accesskey_compose_body', $accesskey_compose_body);
1275 $oTemplate->assign('accesskey_compose_send', $accesskey_compose_send);
1276
1277 $oTemplate->display ('compose_body.tpl');
1278
1279 if ($location_of_buttons == 'bottom') {
1280 //FIXME: DON'T ECHO HTML FROM CORE!
1281 showComposeButtonRow();
1282 }
1283
1284 // composeMessage can be empty when coming from a restored session
1285 if (is_object($composeMessage) && $composeMessage->entities)
1286 $attach_array = $composeMessage->entities;
1287 if ($session_expired && !empty($attachments) && is_array($attachments))
1288 $attach_array = $attachments;
1289
1290 /* This code is for attachments */
1291 if ((bool) ini_get('file_uploads')) {
1292
1293 /* Calculate the max size for an uploaded file.
1294 * This is advisory for the user because we can't actually prevent
1295 * people to upload too large files. */
1296 $sizes = array();
1297 /* php.ini vars which influence the max for uploads */
1298 $configvars = array('post_max_size', 'memory_limit', 'upload_max_filesize');
1299 foreach($configvars as $var) {
1300 /* skip 0 or empty values, and -1 which means 'unlimited' */
1301 if( $size = getByteSize(ini_get($var)) ) {
1302 if ( $size != '-1' ) {
1303 $sizes[] = $size;
1304 }
1305 }
1306 }
1307
1308 $attach = array();
1309 global $username, $attachment_dir;
1310 $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
1311 if (!empty($attach_array)) {
1312 foreach ($attach_array as $key => $attachment) {
1313 $attached_file = $attachment->att_local_name;
1314 if ($attachment->att_local_name || $attachment->body_part) {
1315 $attached_filename = decodeHeader($attachment->mime_header->getParameter('name'));
1316 $type = $attachment->mime_header->type0.'/'.
1317 $attachment->mime_header->type1;
1318
1319 $a = array();
1320 $a['Key'] = $key;
1321 $a['FileName'] = $attached_filename;
1322 $a['ContentType'] = $type;
1323 $a['Size'] = filesize($hashed_attachment_dir . '/' . $attached_file);
1324 $attach[$key] = $a;
1325 }
1326 }
1327 }
1328
1329 $max = min($sizes);
1330 $oTemplate->assign('max_file_size', empty($max) ? -1 : $max);
1331 $oTemplate->assign('attachments', $attach);
1332
1333 // access keys...
1334 //
1335 global $accesskey_compose_attach_browse, $accesskey_compose_attach,
1336 $accesskey_compose_delete_attach;
1337 $oTemplate->assign('accesskey_compose_attach_browse', $accesskey_compose_attach_browse);
1338 $oTemplate->assign('accesskey_compose_attach', $accesskey_compose_attach);
1339 $oTemplate->assign('accesskey_compose_delete_attach', $accesskey_compose_delete_attach);
1340
1341 $oTemplate->display('compose_attachments.tpl');
1342 } // End of file_uploads if-block
1343 /* End of attachment code */
1344
1345 //FIXME: no direct echoing to browser, no HTML output in core!
1346 echo addHidden('username', $username).
1347 addHidden('smaction', $action).
1348 addHidden('mailbox', $mailbox);
1349 sqgetGlobalVar('QUERY_STRING', $queryString, SQ_SERVER);
1350 //FIXME: no direct echoing to browser, no HTML output in core!
1351 echo addHidden('composesession', $composesession).
1352 addHidden('querystring', $queryString).
1353 (!empty($attach_array) ?
1354 addHidden('attachments', urlencode(serialize($attach_array))) : '').
1355 "</form>\n";
1356 if (!(bool) ini_get('file_uploads')) {
1357 /* File uploads are off, so we didn't show that part of the form.
1358 To avoid bogus bug reports, tell the user why. */
1359 //FIXME: no direct echoing to browser, no HTML output in core!
1360 echo '<p style="text-align:center">'
1361 . _("Because PHP file uploads are turned off, you can not attach files to this message. Please see your system administrator for details.")
1362 . "</p>\r\n";
1363 }
1364
1365 if ($compose_new_win=='1') {
1366 $oTemplate->display('compose_newwin_close.tpl');
1367 }
1368
1369 do_hook('compose_bottom', $null);
1370
1371 $oErrorHandler->setDelayedErrors(false);
1372 $oTemplate->display('footer.tpl');
1373 }
1374
1375
1376 function showComposeButtonRow() {
1377 global $use_javascript_addr_book, $save_as_draft,
1378 $default_use_priority, $mailprio, $default_use_mdn,
1379 $request_mdn, $request_dr,
1380 $data_dir, $username;
1381
1382 global $oTemplate, $buffer_hook;
1383
1384 if ($default_use_priority) {
1385 $priorities = array('1'=>_("High"), '3'=>_("Normal"), '5'=>_("Low"));
1386 $priority = isset($mailprio) ? $mailprio : 3;
1387 } else {
1388 $priorities = array();
1389 $priority = NULL;
1390 }
1391
1392 $mdn_user_support=getPref($data_dir, $username, 'mdn_user_support',$default_use_mdn);
1393
1394 $address_book_button_attribs = array();
1395 global $accesskey_compose_addresses;
1396 if ($accesskey_compose_addresses != 'NONE')
1397 $address_book_button_attribs['accesskey'] = $accesskey_compose_addresses;
1398 if ($use_javascript_addr_book && checkForJavascript()) {
1399 $addr_book = addButton(_("Addresses"),
1400 null,
1401 array_merge($address_book_button_attribs, array('onclick' => 'javascript:open_abook();')));
1402 } else {
1403 $addr_book = addSubmit(_("Addresses"), 'html_addr_search', $address_book_button_attribs);
1404 }
1405
1406 $oTemplate->assign('allow_priority', $default_use_priority==1);
1407 $oTemplate->assign('priority_list', $priorities);
1408 $oTemplate->assign('current_priority', $priority);
1409
1410 $oTemplate->assign('notifications_enabled', $mdn_user_support==1);
1411 $oTemplate->assign('read_receipt', $request_mdn=='1');
1412 $oTemplate->assign('delivery_receipt', $request_dr=='1');
1413
1414 $oTemplate->assign('drafts_enabled', $save_as_draft);
1415 $oTemplate->assign('address_book_button', $addr_book);
1416
1417 // access keys...
1418 //
1419 global $accesskey_compose_priority, $accesskey_compose_on_read,
1420 $accesskey_compose_on_delivery, $accesskey_compose_signature,
1421 $accesskey_compose_save_draft, $accesskey_compose_send;
1422 $oTemplate->assign('accesskey_compose_priority', $accesskey_compose_priority);
1423 $oTemplate->assign('accesskey_compose_on_read', $accesskey_compose_on_read);
1424 $oTemplate->assign('accesskey_compose_on_delivery', $accesskey_compose_on_delivery);
1425 $oTemplate->assign('accesskey_compose_signature', $accesskey_compose_signature);
1426 $oTemplate->assign('accesskey_compose_save_draft', $accesskey_compose_save_draft);
1427 $oTemplate->assign('accesskey_compose_send', $accesskey_compose_send);
1428
1429 $oTemplate->display('compose_buttons.tpl');
1430 }
1431
1432 function checkInput ($show) {
1433 /*
1434 * I implemented the $show variable because the error messages
1435 * were getting sent before the page header. So, I check once
1436 * using $show=false, and then when i'm ready to display the error
1437 * message, show=true
1438 */
1439 global $send_to, $send_to_cc, $send_to_bcc;
1440
1441 $send_to = trim($send_to);
1442 $send_to_cc = trim($send_to_cc);
1443 $send_to_bcc = trim($send_to_bcc);
1444 if (empty($send_to) && empty($send_to_cc) && empty($send_to_bcc)) {
1445 if ($show) {
1446 plain_error_message(_("You have not filled in the \"To:\" field."));
1447 }
1448 return false;
1449 }
1450 return true;
1451 } /* function checkInput() */
1452
1453
1454 /* True if FAILURE */
1455 function saveAttachedFiles($session) {
1456 global $composeMessage, $username, $attachment_dir;
1457
1458 /* get out of here if no file was attached at all */
1459 if (! is_uploaded_file($_FILES['attachfile']['tmp_name']) ) {
1460 return true;
1461 }
1462
1463 $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
1464 $localfilename = sq_get_attach_tempfile();
1465 $fullpath = $hashed_attachment_dir . '/' . $localfilename;
1466
1467 // m_u_f works better with restricted PHP installs (safe_mode, open_basedir),
1468 // if that doesn't work, try a simple rename.
1469 if (!sq_call_function_suppress_errors('move_uploaded_file', array($_FILES['attachfile']['tmp_name'], $fullpath))) {
1470 if (!sq_call_function_suppress_errors('rename', array($_FILES['attachfile']['tmp_name'], $fullpath))) {
1471 return true;
1472 }
1473 }
1474 $type = strtolower($_FILES['attachfile']['type']);
1475 $name = $_FILES['attachfile']['name'];
1476 $composeMessage->initAttachment($type, $name, $localfilename);
1477 }
1478
1479 /* parse values like 8M and 2k into bytes */
1480 function getByteSize($ini_size) {
1481
1482 if(!$ini_size) {
1483 return FALSE;
1484 }
1485
1486 $ini_size = trim($ini_size);
1487
1488 // if there's some kind of letter at the end of the string we need to multiply.
1489 if(!is_numeric(substr($ini_size, -1))) {
1490
1491 switch(strtoupper(substr($ini_size, -1))) {
1492 case 'G':
1493 $bytesize = 1073741824;
1494 break;
1495 case 'M':
1496 $bytesize = 1048576;
1497 break;
1498 case 'K':
1499 $bytesize = 1024;
1500 break;
1501 }
1502
1503 return ($bytesize * (int)substr($ini_size, 0, -1));
1504 }
1505
1506 return $ini_size;
1507 }
1508
1509
1510 /**
1511 * temporary function to make use of the deliver class.
1512 * In the future the responsible backend should be automaticly loaded
1513 * and conf.pl should show a list of available backends.
1514 * The message also should be constructed by the message class.
1515 *
1516 * @param object $composeMessage The message being sent. Please note
1517 * that it is passed by reference and
1518 * will be returned modified, with additional
1519 * headers, such as Message-ID, Date, In-Reply-To,
1520 * References, and so forth.
1521 *
1522 * @return boolean FALSE if delivery failed, or some non-FALSE value
1523 * upon success.
1524 *
1525 */
1526 function deliverMessage(&$composeMessage, $draft=false) {
1527 global $send_to, $send_to_cc, $send_to_bcc, $mailprio, $subject, $body,
1528 $username, $identity, $idents, $data_dir,
1529 $request_mdn, $request_dr, $default_charset, $useSendmail,
1530 $domain, $action, $default_move_to_sent, $move_to_sent,
1531 $imapServerAddress, $imapPort, $sent_folder, $key;
1532
1533 $rfc822_header = $composeMessage->rfc822_header;
1534
1535 $abook = addressbook_init(false, true);
1536 $rfc822_header->to = $rfc822_header->parseAddress($send_to,true, array(), '', $domain, array(&$abook,'lookup'));
1537 $rfc822_header->cc = $rfc822_header->parseAddress($send_to_cc,true,array(), '',$domain, array(&$abook,'lookup'));
1538 $rfc822_header->bcc = $rfc822_header->parseAddress($send_to_bcc,true, array(), '',$domain, array(&$abook,'lookup'));
1539 $rfc822_header->priority = $mailprio;
1540 $rfc822_header->subject = $subject;
1541
1542 $special_encoding='';
1543 if (strtolower($default_charset) == 'iso-2022-jp') {
1544 if (mb_detect_encoding($body) == 'ASCII') {
1545 $special_encoding = '8bit';
1546 } else {
1547 $body = mb_convert_encoding($body, 'JIS');
1548 $special_encoding = '7bit';
1549 }
1550 }
1551 $composeMessage->setBody($body);
1552
1553 $reply_to = '';
1554 $reply_to = $idents[$identity]['reply_to'];
1555
1556 $from_addr = build_from_header($identity);
1557 $rfc822_header->from = $rfc822_header->parseAddress($from_addr,true);
1558 if ($reply_to) {
1559 $rfc822_header->reply_to = $rfc822_header->parseAddress($reply_to,true);
1560 }
1561 /* Receipt: On Read */
1562 if (isset($request_mdn) && $request_mdn) {
1563 $rfc822_header->dnt = $rfc822_header->parseAddress($from_addr,true);
1564 } elseif (isset($rfc822_header->dnt)) {
1565 unset($rfc822_header->dnt);
1566 }
1567
1568 /* Receipt: On Delivery */
1569 if (!empty($request_dr)) {
1570 //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?
1571 $rfc822_header->more_headers['Return-Receipt-To'] = $from_addr;
1572 } elseif (isset($rfc822_header->more_headers['Return-Receipt-To'])) {
1573 unset($rfc822_header->more_headers['Return-Receipt-To']);
1574 }
1575
1576 /* multipart messages */
1577 if (count($composeMessage->entities)) {
1578 $message_body = new Message();
1579 $message_body->body_part = $composeMessage->body_part;
1580 $composeMessage->body_part = '';
1581 $mime_header = new MessageHeader;
1582 $mime_header->type0 = 'text';
1583 $mime_header->type1 = 'plain';
1584 if ($special_encoding) {
1585 $mime_header->encoding = $special_encoding;
1586 } else {
1587 $mime_header->encoding = '8bit';
1588 }
1589 if ($default_charset) {
1590 $mime_header->parameters['charset'] = $default_charset;
1591 }
1592 $message_body->mime_header = $mime_header;
1593 array_unshift($composeMessage->entities, $message_body);
1594 $content_type = new ContentType('multipart/mixed');
1595 } else {
1596 $content_type = new ContentType('text/plain');
1597 if ($special_encoding) {
1598 $rfc822_header->encoding = $special_encoding;
1599 } else {
1600 $rfc822_header->encoding = '8bit';
1601 }
1602 if ($default_charset) {
1603 $content_type->properties['charset']=$default_charset;
1604 }
1605 }
1606
1607 $rfc822_header->content_type = $content_type;
1608 $composeMessage->rfc822_header = $rfc822_header;
1609 if ($action == 'reply' || $action == 'reply_all') {
1610 global $passed_id, $passed_ent_id;
1611 $reply_id = $passed_id;
1612 $reply_ent_id = $passed_ent_id;
1613 } else {
1614 $reply_id = '';
1615 $reply_ent_id = '';
1616 }
1617
1618 /* Here you can modify the message structure just before we hand
1619 it over to deliver; plugin authors note that $composeMessage
1620 is sent and modified by reference since 1.5.2 */
1621 do_hook('compose_send', $composeMessage);
1622
1623 if (!$useSendmail && !$draft) {
1624 require_once(SM_PATH . 'class/deliver/Deliver_SMTP.class.php');
1625 $deliver = new Deliver_SMTP();
1626 global $smtpServerAddress, $smtpPort, $pop_before_smtp, $pop_before_smtp_host;
1627
1628 $authPop = (isset($pop_before_smtp) && $pop_before_smtp) ? true : false;
1629 if (empty($pop_before_smtp_host)) $pop_before_smtp_host = $smtpServerAddress;
1630 get_smtp_user($user, $pass);
1631 $stream = $deliver->initStream($composeMessage,$domain,0,
1632 $smtpServerAddress, $smtpPort, $user, $pass, $authPop, $pop_before_smtp_host);
1633 } elseif (!$draft) {
1634 require_once(SM_PATH . 'class/deliver/Deliver_SendMail.class.php');
1635 global $sendmail_path, $sendmail_args;
1636 // Check for outdated configuration
1637 if (!isset($sendmail_args)) {
1638 if ($sendmail_path=='/var/qmail/bin/qmail-inject') {
1639 $sendmail_args = '';
1640 } else {
1641 $sendmail_args = '-i -t';
1642 }
1643 }
1644 $deliver = new Deliver_SendMail(array('sendmail_args'=>$sendmail_args));
1645 $stream = $deliver->initStream($composeMessage,$sendmail_path);
1646 } elseif ($draft) {
1647 global $draft_folder;
1648 $imap_stream = sqimap_login($username, false, $imapServerAddress,
1649 $imapPort, 0);
1650 if (sqimap_mailbox_exists ($imap_stream, $draft_folder)) {
1651 require_once(SM_PATH . 'class/deliver/Deliver_IMAP.class.php');
1652 $imap_deliver = new Deliver_IMAP();
1653 $success = $imap_deliver->mail($composeMessage, $imap_stream, $reply_id, $reply_ent_id, $imap_stream, $draft_folder);
1654 sqimap_logout($imap_stream);
1655 unset ($imap_deliver);
1656 $composeMessage->purgeAttachments();
1657 return $success;
1658 } else {
1659 $msg = '<br />'.sprintf(_("Error: Draft folder %s does not exist."), htmlspecialchars($draft_folder));
1660 plain_error_message($msg);
1661 return false;
1662 }
1663 }
1664 $success = false;
1665 if ($stream) {
1666 $deliver->mail($composeMessage, $stream, $reply_id, $reply_ent_id);
1667 $success = $deliver->finalizeStream($stream);
1668 }
1669 if (!$success) {
1670 // $deliver->dlv_server_msg is not always server's reply
1671 $msg = _("Message not sent.") . "<br />\n" .
1672 $deliver->dlv_msg;
1673 if (!empty($deliver->dlv_server_msg)) {
1674 // add 'server replied' part only when it is not empty.
1675 // Delivery error can be generated by delivery class itself
1676 $msg.='<br />' .
1677 _("Server replied:") . ' ' . $deliver->dlv_ret_nr . ' ' .
1678 nl2br(htmlspecialchars($deliver->dlv_server_msg));
1679 }
1680 plain_error_message($msg);
1681 } else {
1682 unset ($deliver);
1683 $imap_stream = sqimap_login($username, false, $imapServerAddress, $imapPort, 0);
1684
1685
1686 // mark as replied or forwarded if applicable
1687 //
1688 global $what, $iAccount, $startMessage, $passed_id, $mailbox;
1689
1690 if ($action=='reply' || $action=='reply_all' || $action=='forward' || $action=='forward_as_attachment') {
1691 require(SM_PATH . 'functions/mailbox_display.php');
1692 $aMailbox = sqm_api_mailbox_select($imap_stream, $iAccount, $mailbox,array('setindex' => $what, 'offset' => $startMessage),array());
1693 switch($action) {
1694 case 'reply':
1695 case 'reply_all':
1696 // check if we are allowed to set the \\Answered flag
1697 if (in_array('\\answered',$aMailbox['PERMANENTFLAGS'], true)) {
1698 $aUpdatedMsgs = sqimap_toggle_flag($imap_stream, array($passed_id), '\\Answered', true, false);
1699 if (isset($aUpdatedMsgs[$passed_id]['FLAGS'])) {
1700 /**
1701 * Only update the cached headers if the header is
1702 * cached.
1703 */
1704 if (isset($aMailbox['MSG_HEADERS'][$passed_id])) {
1705 $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS'] = $aMsg['FLAGS'];
1706 }
1707 }
1708 }
1709 break;
1710 case 'forward':
1711 case 'forward_as_attachment':
1712 // check if we are allowed to set the $Forwarded flag (RFC 4550 paragraph 2.8)
1713 if (in_array('$forwarded',$aMailbox['PERMANENTFLAGS'], true) ||
1714 in_array('\\*',$aMailbox['PERMANENTFLAGS'])) {
1715
1716 $aUpdatedMsgs = sqimap_toggle_flag($imap_stream, array($passed_id), '$Forwarded', true, false);
1717 if (isset($aUpdatedMsgs[$passed_id]['FLAGS'])) {
1718 if (isset($aMailbox['MSG_HEADERS'][$passed_id])) {
1719 $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS'] = $aMsg['FLAGS'];
1720 }
1721 }
1722 }
1723 break;
1724 }
1725
1726 /**
1727 * Write mailbox with updated seen flag information back to cache.
1728 */
1729 if(isset($aUpdatedMsgs[$passed_id])) {
1730 $mailbox_cache[$iAccount.'_'.$aMailbox['NAME']] = $aMailbox;
1731 sqsession_register($mailbox_cache,'mailbox_cache');
1732 }
1733
1734 }
1735
1736
1737 // move to sent folder
1738 //
1739 $move_to_sent = getPref($data_dir,$username,'move_to_sent');
1740 if (isset($default_move_to_sent) && ($default_move_to_sent != 0)) {
1741 $svr_allow_sent = true;
1742 } else {
1743 $svr_allow_sent = false;
1744 }
1745
1746 if (isset($sent_folder) && (($sent_folder != '') || ($sent_folder != 'none'))
1747 && sqimap_mailbox_exists( $imap_stream, $sent_folder)) {
1748 $fld_sent = true;
1749 } else {
1750 $fld_sent = false;
1751 }
1752
1753 if ((isset($move_to_sent) && ($move_to_sent != 0)) || (!isset($move_to_sent))) {
1754 $lcl_allow_sent = true;
1755 } else {
1756 $lcl_allow_sent = false;
1757 }
1758
1759 if (($fld_sent && $svr_allow_sent && !$lcl_allow_sent) || ($fld_sent && $lcl_allow_sent)) {
1760 if ($action == 'reply' || $action == 'reply_all') {
1761 $save_reply_with_orig=getPref($data_dir,$username,'save_reply_with_orig');
1762 if ($save_reply_with_orig) {
1763 $sent_folder = $mailbox;
1764 }
1765 }
1766 require_once(SM_PATH . 'class/deliver/Deliver_IMAP.class.php');
1767 $imap_deliver = new Deliver_IMAP();
1768 $imap_deliver->mail($composeMessage, $imap_stream, $reply_id, $reply_ent_id, $imap_stream, $sent_folder);
1769 unset ($imap_deliver);
1770 }
1771
1772
1773 // final cleanup
1774 //
1775 $composeMessage->purgeAttachments();
1776 sqimap_logout($imap_stream);
1777
1778 }
1779 return $success;
1780 }