5 * This code sends a mail.
7 * There are 4 modes of operation:
13 * @copyright © 1999-2009 The SquirrelMail Project Team
14 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
16 * @package squirrelmail
19 /** This is the compose page */
20 define('PAGE_NAME', 'compose');
23 * Include the SquirrelMail initialization file.
25 require('../include/init.php');
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");
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');
45 /* --------------------- Get globals ------------------------------------- */
48 sqgetGlobalVar('delimiter', $delimiter, SQ_SESSION
);
50 sqgetGlobalVar('delayed_errors', $delayed_errors, SQ_SESSION
);
51 sqgetGlobalVar('composesession', $composesession, SQ_SESSION
);
52 sqgetGlobalVar('compose_messages', $compose_messages, SQ_SESSION
);
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');
59 // Turn on delayed error handling in case we wind up redirecting below
60 $oErrorHandler->setDelayedErrors(true);
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) {
73 sqgetGlobalVar('session',$session, $SQ_GLOBAL);
74 sqgetGlobalVar('mailbox',$mailbox, $SQ_GLOBAL);
75 if(!sqgetGlobalVar('identity',$identity, $SQ_GLOBAL)) {
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);
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
);
97 if ( sqgetGlobalVar('delete_draft',$delete_draft) ) {
98 $delete_draft = (int)$delete_draft;
101 if ( sqgetGlobalVar('startMessage',$startMessage) ) {
102 $startMessage = (int)$startMessage;
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';
122 if ( sqgetGlobalVar('account', $temp, SQ_GET
) ) {
123 $iAccount = (int) $temp;
130 if ( !sqgetGlobalVar('smaction',$action) )
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';
141 * Here we decode the data passed in from mailto.php.
143 if ( sqgetGlobalVar('mailtodata', $mailtodata, SQ_GET
) ) {
144 $trtable = array('to' => 'send_to',
145 'cc' => 'send_to_cc',
146 'bcc' => 'send_to_bcc',
148 'subject' => 'subject');
149 $mtdata = unserialize($mailtodata);
151 foreach ($trtable as $f => $t) {
152 if ( !empty($mtdata[$f]) ) {
156 unset($mailtodata,$mtdata, $trtable);
159 /* Location (For HTTP 1.1 header("Location: ...") redirects) */
160 $location = get_location();
161 /* Identities (fetch only once) */
162 $idents = get_identities();
164 /* --------------------- Specific Functions ------------------------------ */
166 function replyAllString($header) {
167 global $include_self_reply_all, $idents;
170 * 1) Remove the addresses we'll be sending the message 'to'
172 if (isset($header->reply_to
)) {
173 $excl_ar = $header->getAddr_a('reply_to');
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
179 if (!$include_self_reply_all) {
180 foreach($idents as $id) {
181 $excl_ar[strtolower(trim($id['email_address']))] = '';
186 * 3) get the addresses.
188 $url_replytoall_ar = $header->getAddr_a(array('to','cc'), $excl_ar);
191 * 4) generate the string.
193 $url_replytoallcc = '';
194 foreach( $url_replytoall_ar as $email => $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.'"';
201 $url_replytoallcc .= ", $personal <$email>";
203 $url_replytoallcc .= ', '. $email;
206 $url_replytoallcc = substr($url_replytoallcc,2);
208 return $url_replytoallcc;
212 * creates top line in reply citations
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
220 function getReplyCitation($orig_from, $orig_date) {
221 global $reply_citation_style, $reply_citation_start, $reply_citation_end;
223 if (!is_object($orig_from)) {
226 $sOrig_from = decodeHeader($orig_from->getAddress(false),false,false,true);
229 /* First, return an empty string when no citation style selected. */
230 if (($reply_citation_style == '') ||
($reply_citation_style == 'none')) {
234 /* Make sure our final value isn't an empty string. */
235 if ($sOrig_from == '') {
239 /* Otherwise, try to select the desired citation style. */
240 switch ($reply_citation_style) {
242 // i18n: %s is for author's name
243 $full_reply_citation = sprintf(_("%s wrote:"),$sOrig_from);
246 $start = '<quote who="';
248 $full_reply_citation = $start . $sOrig_from . $end;
250 case 'date_time_author':
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"
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);
262 $start = $reply_citation_start .
263 ($reply_citation_start == '' ?
'' : ' ');
264 $end = $reply_citation_end;
265 $full_reply_citation = $start . $sOrig_from . $end;
271 /* Add line feed and return the citation string. */
272 return ($full_reply_citation . "\n");
276 * Creates header fields in forwarded email body
278 * $default_charset global must be set correctly before you call this function.
279 * @param object $orig_header
282 function getforwardHeader($orig_header) {
283 global $editor_size, $default_charset;
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);
296 $from = decodeHeader($orig_header->getAddr_s('from',"\n$indent"),false,false,true);
297 $from = str_replace(' ',' ',$from);
298 $to = decodeHeader($orig_header->getAddr_s('to',"\n$indent"),false,false,true);
299 $to = str_replace(' ',' ',$to);
300 $subject = decodeHeader($orig_header->subject
,false,false,true);
301 $subject = str_replace(' ',' ',$subject);
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(' ',' ',$cc);
312 $bodyTop .= $display[_("Cc")] .$cc . "\n";
314 $bodyTop .= str_pad('', $editor_size -2 , '-') .
318 /* ----------------------------------------------------------------------- */
321 * If the session is expired during a post this restores the compose session
324 $session_expired = false;
325 if (sqsession_is_registered('session_expired_post')) {
326 sqgetGlobalVar('session_expired_post', $session_expired_post, SQ_SESSION
);
328 * extra check for username so we don't display previous post data from
329 * another user during this session.
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');
341 foreach ($compo_var_list as $var) {
342 if ( isset($session_expired_post[$var]) && !isset($
$var) ) {
343 $
$var = $session_expired_post[$var];
347 if (!empty($attachments))
348 $attachments = unserialize(urldecode($attachments));
350 sqsession_register($composesession,'composesession');
355 $session_expired = true;
357 unset($session_expired_post);
358 sqsession_unregister('session_expired_post');
359 session_write_close();
360 if (!isset($mailbox)) {
363 if ($compose_new_win == '1') {
364 compose_Header($color, $mailbox);
366 $sHeaderJs = (isset($sHeaderJs)) ?
$sHeaderJs : '';
367 if (strpos($action, 'reply') !== false && $reply_focus) {
368 $sOnload = 'checkForm(\''.$replyfocus.'\');';
370 $sOnload = 'checkForm();';
372 displayPageHeader($color, $mailbox,$sHeaderJs,$sOnload);
374 showInputForm($session, false);
378 if (!isset($composesession)) {
380 sqsession_register(0,'composesession');
382 $composesession = (int)$composesession;
385 if (!isset($session) ||
(isset($newmessage) && $newmessage)) {
386 sqsession_unregister('composesession');
387 $session = "$composesession" +
1;
388 $composesession = $session;
389 sqsession_register($composesession,'composesession');
391 if (!empty($compose_messages[$session])) {
392 $composeMessage = $compose_messages[$session];
394 $composeMessage = new Message();
395 $rfc822_header = new Rfc822Header();
396 $composeMessage->rfc822_header
= $rfc822_header;
397 $composeMessage->reply_rfc822_header
= '';
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;
409 if (empty($mailbox)) {
415 * Set $default_charset to correspond with the user's selection
416 * of language interface.
419 if (! deliverMessage($composeMessage, true)) {
420 showInputForm($session);
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);
433 sqimap_mailbox_expunge($imap_stream, $draft_folder, true);
435 sqimap_logout($imap_stream);
438 $oErrorHandler->saveDelayedErrors();
439 session_write_close();
441 if ($compose_new_win == '1') {
442 if ( !isset($pageheader_sent) ||
!$pageheader_sent ) {
443 header("Location: $location/compose.php?saved_draft=yes&session=$composesession");
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&session=' . $composesession . '">'
448 . _("Return") . '</a></div>';
452 if ( !isset($pageheader_sent) ||
!$pageheader_sent ) {
453 header("Location: $location/right_main.php?mailbox=" . urlencode($draft_folder) .
454 "&startMessage=1¬e=".urlencode($draft_message));
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 . '&startMessage=1&note=' . urlencode($draft_message) .'">'
460 . _("Return") . '</a></div>';
468 if (isset($_FILES['attachfile']) &&
469 $_FILES['attachfile']['tmp_name'] &&
470 $_FILES['attachfile']['tmp_name'] != 'none') {
471 $AttachFailure = saveAttachedFiles($session);
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 */
478 $urlMailbox = urlencode($mailbox);
479 if (! isset($passed_id)) {
483 * Set $default_charset to correspond with the user's selection
484 * of language interface.
488 * This is to change all newlines to \n
489 * We'll change them to \r\n later (in the sendMessage function)
491 $body = str_replace("\r\n", "\n", $body);
492 $body = str_replace("\r", "\n", $body);
495 * Rewrap $body so that no line is bigger than $editor_size
497 $body = explode("\n", $body);
499 foreach ($body as $line) {
500 if( $line <> '-- ' ) {
501 $line = rtrim($line);
503 if (sq_strlen($line, $default_charset) <= $editor_size +
1) {
504 $newBody .= $line . "\n";
506 sqWordWrap($line, $editor_size, $default_charset);
507 $newBody .= $line . "\n";
514 $Result = deliverMessage($composeMessage);
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
524 $temp = array(&$Result, &$composeMessage, &$mail_sent);
525 do_hook('compose_send_after', $temp);
527 showInputForm($session);
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);
540 sqimap_mailbox_expunge($imap_stream, $draft_folder, true);
542 sqimap_logout($imap_stream);
545 * Store the error array in the session because they will be lost on a redirect
547 $oErrorHandler->saveDelayedErrors();
548 session_write_close();
550 if ($compose_new_win == '1') {
551 if ( !isset($pageheader_sent) ||
!$pageheader_sent ) {
552 header("Location: $location/compose.php?mail_sent=$mail_sent");
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>';
561 if ( !isset($pageheader_sent) ||
!$pageheader_sent ) {
562 header("Location: $location/right_main.php?mailbox=$urlMailbox".
563 "&startMessage=$startMessage&mail_sent=$mail_sent");
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 . "&startMessage=$startMessage&mail_sent=$mail_sent\">"
569 . _("Return") . '</a></div>';
574 if ($compose_new_win == '1') {
575 compose_Header($color, $mailbox);
578 displayPageHeader($color, $mailbox);
580 if (isset($AttachFailure)) {
581 plain_error_message(_("Could not move/copy file. File not attached"),
585 showInputForm($session);
586 /* sqimap_logout($imapConnection); */
588 } elseif (isset($html_addr_search_done)) {
589 if ($compose_new_win == '1') {
590 compose_Header($color, $mailbox);
593 displayPageHeader($color, $mailbox);
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') {
604 elseif (substr($k, 0, 1) == 'C') {
610 elseif (substr($k, 0, 1) == 'B') {
612 $send_to_bcc .= ', ';
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"));
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.
631 include_once('./addrbook_search_html.php');
632 } elseif (isset($attach)) {
633 if ($compose_new_win == '1') {
634 compose_Header($color, $mailbox);
636 displayPageHeader($color, $mailbox);
638 if (saveAttachedFiles($session)) {
639 plain_error_message(_("Could not move/copy file. File not attached"));
641 showInputForm($session);
643 elseif (isset($sigappend)) {
644 $signature = $idents[$identity]['signature'];
646 $body .= "\n\n".($prefix_sig==true?
"-- \n":'').$signature;
647 if ($compose_new_win == '1') {
648 compose_Header($color, $mailbox);
650 displayPageHeader($color, $mailbox);
652 showInputForm($session);
653 } elseif (isset($do_delete)) {
654 if ($compose_new_win == '1') {
655 compose_Header($color, $mailbox);
657 displayPageHeader($color, $mailbox);
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]);
669 $new_entities = array();
670 foreach ($composeMessage->entities
as $entity) {
671 $new_entities[] = $entity;
673 $composeMessage->entities
= $new_entities;
675 showInputForm($session);
678 * This handles the default case as well as the error case
679 * (they had the same code) --> if (isset($smtpErrors))
682 if ($compose_new_win == '1') {
683 compose_Header($color, $mailbox);
685 displayPageHeader($color, $mailbox);
690 if (!isset($passed_ent_id)) {
693 if (!isset($passed_id)) {
696 if (!isset($mailbox)) {
699 if (!isset($action)) {
703 $values = newMail($mailbox,$passed_id,$passed_ent_id, $action, $session);
705 /* in case the origin is not read_body.php */
706 if (isset($send_to)) {
707 $values['send_to'] = $send_to;
709 if (isset($send_to_cc)) {
710 $values['send_to_cc'] = $send_to_cc;
712 if (isset($send_to_bcc)) {
713 $values['send_to_bcc'] = $send_to_bcc;
715 if (isset($subject)) {
716 $values['subject'] = $subject;
718 showInputForm($session, $values);
723 /**************** Only function definitions go below *************/
725 function getforwardSubject($subject)
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 . ']';
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,
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.
751 $send_to = $send_to_cc = $send_to_bcc = $subject = $identity = '';
755 $imapConnection = sqimap_login($username, false, $imapServerAddress,
758 sqimap_mailbox_select($imapConnection, $mailbox);
759 $message = sqimap_get_message($imapConnection, $passed_id, $mailbox);
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
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'));
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];
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'));
785 $orig_header = $message->rfc822_header
;
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(' ','>','<'),array(' ','>','<'),$bodypart);
802 $bodypart = strip_tags($bodypart);
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);
811 // charset encoding in compose form stuff
812 if (isset($body_part_entity->header
->parameters
['charset'])) {
813 $actual = $body_part_entity->header
->parameters
['charset'];
815 $actual = 'us-ascii';
818 if ( $actual && is_conversion_safe($actual) && $actual != $default_charset){
819 $bodypart = charset_convert($actual,$bodypart,$default_charset,false);
821 // end of charset encoding in compose
825 if ($default_use_priority) {
826 $mailprio = substr($orig_header->priority
,0,1);
834 $from_o = $orig_header->from
;
835 if (is_array($from_o)) {
836 if (isset($from_o[0])) {
837 $from_o = $from_o[0];
840 if (is_object($from_o)) {
841 $orig_from = $from_o->getAddress();
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)) {
852 // don't stop! need to build $identities array for idents match below
855 $identities[] = $enc_from_name;
858 $identity_match = $orig_header->findAddress($identities);
859 if ($identity_match) {
860 $identity = $identity_match;
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);
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';
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);
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
;
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);
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);
910 //add a blank line after the forward headers
911 $body = "\n" . $body;
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);
919 if(isset($orig_header->mail_followup_to
) && $orig_header->mail_followup_to
) {
920 $send_to = $orig_header->getAddr_s('mail_followup_to');
922 $send_to_cc = replyAllString($orig_header);
923 $send_to_cc = decodeHeader($send_to_cc,false,false,true);
926 // skip this if send_to was already set right above here
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');
934 $send_to = $orig_header->getAddr_s('from');
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;
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
;
948 $strip_sigs = getPref($data_dir, $username, 'strip_sigs');
949 foreach ($rewrap_body as $line) {
950 if ($strip_sigs && substr($line,0,3) == '-- ') {
953 if (preg_match("/^(>+)/", $line, $matches)) {
955 $body .= $body_quote . str_replace("\n", "\n$body_quote$gt ", rtrim($line)) ."\n";
957 $body .= $body_quote . (!empty($body_quote) ?
' ' : '') . str_replace("\n", "\n$body_quote" . (!empty($body_quote) ?
' ' : ''), rtrim($line)) . "\n";
961 //rewrap the body to clean up quotations and line lengths
962 $body = sqBodyWrap ($body, $editor_size);
964 $body = getReplyCitation($from , $orig_header->date
) . $body;
965 $composeMessage->reply_rfc822_header
= $orig_header;
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);
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,
981 'identity' => $identity );
984 } /* function newMail() */
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
996 function getAttachments($message, &$composeMessage, $passed_id, $entities, $imapConnection) {
997 global $squirrelmail_language, $languages, $username, $attachment_dir;
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
) {
1004 if ($message->type1
== 'rfc822') {
1005 $filename = $message->rfc822_header
->subject
;
1006 if ($filename == "") {
1007 $filename = "untitled-".$message->entity_id
;
1009 $filename .= '.eml';
1011 $filename = $message->getFilename();
1015 if (!$message->mime_header
) { /* temporary hack */
1016 $message->mime_header
= $message->header
;
1018 $filename = $message->getFilename();
1021 $filename = str_replace(' ', ' ', 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);
1027 $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
1028 $localfilename = sq_get_attach_tempfile();
1029 $message->att_local_name
= $localfilename;
1031 $composeMessage->initAttachment($message->type0
.'/'.$message->type1
,$filename,
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);
1040 for ($i=0, $entCount=count($message->entities
); $i<$entCount;$i++
) {
1041 $composeMessage=getAttachments($message->entities
[$i], $composeMessage, $passed_id, $entities, $imapConnection);
1044 return $composeMessage;
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,
1055 $body_a = sqimap_run_command($imapConnection,
1056 'FETCH '.$passed_id.' BODY['.$passed_ent_id.']',
1057 TRUE, $response, $readmessage, TRUE);
1058 $message = $message->parent
;
1060 if ($response == 'OK') {
1061 $subject = encodeHeader($message->rfc822_header
->subject
);
1062 array_shift($body_a);
1064 $body = implode('', $body_a) . "\r\n";
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);
1072 $composeMessage->initAttachment('message/rfc822',$subject.'.eml',
1075 return $composeMessage;
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;
1090 if (checkForJavascript()) {
1091 $onfocus = ' onfocus="alreadyFocused=true;"';
1092 $onfocus_array = array('onfocus' => 'alreadyFocused=true;');
1096 $onfocus_array = array();
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'];
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);
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" .
1122 "// -->\n</script>\n\n";
1125 //FIXME: NO HTML IN CORE!
1126 echo "\n" . '<form name="compose" action="compose.php" method="post" ' .
1127 'enctype="multipart/form-data"';
1129 $compose_onsubmit = array();
1131 do_hook('compose_form', $null);
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);
1149 foreach ($compose_onsubmit as $text) {
1150 $text = trim($text);
1151 if (substr($text, -1) != ';' && substr($text, -1) != '}')
1153 $onsubmit_text .= $text;
1156 //FIXME: DON'T ECHO HTML FROM CORE!
1157 echo $onsubmit_text . ' return true;"';
1161 //FIXME: NO HTML IN CORE!
1164 //FIXME: DON'T ECHO HTML FROM CORE!
1165 echo addHidden('startMessage', $startMessage);
1167 if ($action == 'draft') {
1168 //FIXME: DON'T ECHO HTML FROM CORE!
1169 echo addHidden('delete_draft', $passed_id);
1171 if (isset($delete_draft)) {
1172 //FIXME: DON'T ECHO HTML FROM CORE!
1173 echo addHidden('delete_draft', $delete_draft);
1175 if (isset($session)) {
1176 //FIXME: DON'T ECHO HTML FROM CORE!
1177 echo addHidden('session', $session);
1180 if (isset($passed_id)) {
1181 //FIXME: DON'T ECHO HTML FROM CORE!
1182 echo addHidden('passed_id', $passed_id);
1185 if ($saved_draft == 'yes') {
1186 $oTemplate->assign('note', _("Your draft has been saved."));
1187 $oTemplate->display('note.tpl');
1189 if ($mail_sent == 'yes') {
1190 $oTemplate->assign('note', _("Your mail has been sent."));
1191 $oTemplate->display('note.tpl');
1193 if ($compose_new_win == '1') {
1194 $oTemplate->display('compose_newwin_close.tpl');
1197 if ($location_of_buttons == 'top') {
1198 //FIXME: DON'T ECHO HTML FROM CORE!
1199 showComposeButtonRow();
1202 $identities = array();
1203 if (count($idents) > 1) {
1205 foreach($idents as $id => $data) {
1206 $identities[$id] = $data['full_name'].' <'.$data['email_address'].'>';
1210 $oTemplate->assign('identities', $identities);
1211 $oTemplate->assign('identity_def', $identity);
1212 $oTemplate->assign('input_onfocus', 'onfocus="'.join(' ', $onfocus_array).'"');
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));
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);
1230 $oTemplate->display('compose_header.tpl');
1232 if ($location_of_buttons == 'between') {
1233 //FIXME: DON'T ECHO HTML FROM CORE!
1234 showComposeButtonRow();
1238 if ($use_signature == true && $newmail == true && !isset($from_htmladdr_search)) {
1239 $signature = $idents[$identity]['signature'];
1241 if ($sig_first == '1') {
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
1248 if ($default_charset == 'iso-2022-jp') {
1249 $body_str = "\n\n".($prefix_sig==true?
"-- \n":'').mb_convert_encoding($signature, 'EUC-JP');
1251 $body_str = "\n\n".($prefix_sig==true?
"-- \n":'').decodeHeader($signature,false,false);
1253 $body_str .= "\n\n".htmlspecialchars(decodeHeader($body,false,false));
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');
1260 $body_str .= "\n\n".($prefix_sig==true?
"-- \n":'').decodeHeader($signature,false,false);
1264 $body_str = htmlspecialchars(decodeHeader($body,false,false));
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');
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);
1279 $oTemplate->display ('compose_body.tpl');
1281 if ($location_of_buttons == 'bottom') {
1282 //FIXME: DON'T ECHO HTML FROM CORE!
1283 showComposeButtonRow();
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;
1292 /* This code is for attachments */
1293 if ((bool) ini_get('file_uploads')) {
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. */
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' ) {
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
;
1323 $a['FileName'] = $attached_filename;
1324 $a['ContentType'] = $type;
1325 $a['Size'] = filesize($hashed_attachment_dir . '/' . $attached_file);
1332 $oTemplate->assign('max_file_size', empty($max) ?
-1 : $max);
1333 $oTemplate->assign('attachments', $attach);
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);
1343 $oTemplate->display('compose_attachments.tpl');
1344 } // End of file_uploads if-block
1345 /* End of attachment code */
1347 $oTemplate->assign('username', $username);
1348 $oTemplate->assign('smaction', $action);
1349 $oTemplate->assign('mailbox', $mailbox);
1350 sqgetGlobalVar('QUERY_STRING', $queryString, SQ_SERVER
);
1351 $oTemplate->assign('querystring', $queryString);
1352 $oTemplate->assign('composesession', $composesession);
1353 $oTemplate->assign('send_button_count', unique_widget_name('send', TRUE));
1354 if (!empty($attach_array))
1355 $oTemplate->assign('attachments', urlencode(serialize($attach_array)));
1357 $aUserNotices = array();
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 if (!(bool) ini_get('file_uploads')) {
1362 $aUserNotices[] = _("Because PHP file uploads are turned off, you can not attach files to this message. Please see your system administrator for details.");
1365 $oTemplate->assign('user_notices', $aUserNotices);
1367 $oTemplate->display('compose_form_close.tpl');
1369 if ($compose_new_win=='1') {
1370 $oTemplate->display('compose_newwin_close.tpl');
1373 $oErrorHandler->setDelayedErrors(false);
1374 $oTemplate->display('footer.tpl');
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;
1384 global $oTemplate, $buffer_hook;
1386 if ($default_use_priority) {
1387 $priorities = array('1'=>_("High"), '3'=>_("Normal"), '5'=>_("Low"));
1388 $priority = isset($mailprio) ?
$mailprio : 3;
1390 $priorities = array();
1394 $mdn_user_support=getPref($data_dir, $username, 'mdn_user_support',$default_use_mdn);
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"),
1403 array_merge($address_book_button_attribs, array('onclick' => 'javascript:open_abook();')));
1405 $addr_book = addSubmit(_("Addresses"), 'html_addr_search', $address_book_button_attribs);
1408 $oTemplate->assign('allow_priority', $default_use_priority==1);
1409 $oTemplate->assign('priority_list', $priorities);
1410 $oTemplate->assign('current_priority', $priority);
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');
1416 $oTemplate->assign('drafts_enabled', $save_as_draft);
1417 $oTemplate->assign('address_book_button', $addr_book);
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);
1431 $oTemplate->display('compose_buttons.tpl');
1434 function checkInput ($show) {
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
1441 global $send_to, $send_to_cc, $send_to_bcc;
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)) {
1448 plain_error_message(_("You have not filled in the \"To:\" field."));
1453 } /* function checkInput() */
1456 /* True if FAILURE */
1457 function saveAttachedFiles($session) {
1458 global $composeMessage, $username, $attachment_dir;
1460 /* get out of here if no file was attached at all */
1461 if (! is_uploaded_file($_FILES['attachfile']['tmp_name']) ) {
1465 $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
1466 $localfilename = sq_get_attach_tempfile();
1467 $fullpath = $hashed_attachment_dir . '/' . $localfilename;
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))) {
1476 $type = strtolower($_FILES['attachfile']['type']);
1477 $name = $_FILES['attachfile']['name'];
1478 $composeMessage->initAttachment($type, $name, $localfilename);
1481 /* parse values like 8M and 2k into bytes */
1482 function getByteSize($ini_size) {
1488 $ini_size = trim($ini_size);
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))) {
1493 switch(strtoupper(substr($ini_size, -1))) {
1495 $bytesize = 1073741824;
1498 $bytesize = 1048576;
1505 return ($bytesize * (int)substr($ini_size, 0, -1));
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.
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.
1524 * @return boolean FALSE if delivery failed, or some non-FALSE value
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;
1535 $rfc822_header = $composeMessage->rfc822_header
;
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;
1544 $special_encoding='';
1545 if (strtolower($default_charset) == 'iso-2022-jp') {
1546 if (mb_detect_encoding($body) == 'ASCII') {
1547 $special_encoding = '8bit';
1549 $body = mb_convert_encoding($body, 'JIS');
1550 $special_encoding = '7bit';
1553 $composeMessage->setBody($body);
1556 $reply_to = $idents[$identity]['reply_to'];
1558 $from_addr = build_from_header($identity);
1559 $rfc822_header->from
= $rfc822_header->parseAddress($from_addr,true);
1561 $rfc822_header->reply_to
= $rfc822_header->parseAddress($reply_to,true);
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
);
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']);
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;
1589 $mime_header->encoding
= '8bit';
1591 if ($default_charset) {
1592 $mime_header->parameters
['charset'] = $default_charset;
1594 $message_body->mime_header
= $mime_header;
1595 array_unshift($composeMessage->entities
, $message_body);
1596 $content_type = new ContentType('multipart/mixed');
1598 $content_type = new ContentType('text/plain');
1599 if ($special_encoding) {
1600 $rfc822_header->encoding
= $special_encoding;
1602 $rfc822_header->encoding
= '8bit';
1604 if ($default_charset) {
1605 $content_type->properties
['charset']=$default_charset;
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;
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);
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;
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 = '';
1643 $sendmail_args = '-i -t';
1646 $deliver = new Deliver_SendMail(array('sendmail_args'=>$sendmail_args));
1647 $stream = $deliver->initStream($composeMessage,$sendmail_path);
1649 global $draft_folder;
1650 $imap_stream = sqimap_login($username, false, $imapServerAddress,
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();
1661 $msg = '<br />'.sprintf(_("Error: Draft folder %s does not exist."), htmlspecialchars($draft_folder));
1662 plain_error_message($msg);
1668 $deliver->mail($composeMessage, $stream, $reply_id, $reply_ent_id);
1669 $success = $deliver->finalizeStream($stream);
1672 // $deliver->dlv_server_msg is not always server's reply
1673 $msg = _("Message not sent.") . "<br />\n" .
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
1679 _("Server replied:") . ' ' . $deliver->dlv_ret_nr
. ' ' .
1680 nl2br(htmlspecialchars($deliver->dlv_server_msg
));
1682 plain_error_message($msg);
1685 $imap_stream = sqimap_login($username, false, $imapServerAddress, $imapPort, 0);
1688 // mark as replied or forwarded if applicable
1690 global $what, $iAccount, $startMessage, $passed_id, $mailbox;
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());
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'])) {
1703 * Only update the cached headers if the header is
1706 if (isset($aMailbox['MSG_HEADERS'][$passed_id])) {
1707 $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS'] = $aMsg['FLAGS'];
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'])) {
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'];
1729 * Write mailbox with updated seen flag information back to cache.
1731 if(isset($aUpdatedMsgs[$passed_id])) {
1732 $mailbox_cache[$iAccount.'_'.$aMailbox['NAME']] = $aMailbox;
1733 sqsession_register($mailbox_cache,'mailbox_cache');
1739 // move to sent folder
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;
1745 $svr_allow_sent = false;
1748 if (isset($sent_folder) && (($sent_folder != '') ||
($sent_folder != 'none'))
1749 && sqimap_mailbox_exists( $imap_stream, $sent_folder)) {
1755 if ((isset($move_to_sent) && ($move_to_sent != 0)) ||
(!isset($move_to_sent))) {
1756 $lcl_allow_sent = true;
1758 $lcl_allow_sent = false;
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;
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);
1777 $composeMessage->purgeAttachments();
1778 sqimap_logout($imap_stream);