5 * This code sends a mail.
7 * There are 4 modes of operation:
13 * @copyright © 1999-2007 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_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);
1228 $oTemplate->display('compose_header.tpl');
1230 if ($location_of_buttons == 'between') {
1231 //FIXME: DON'T ECHO HTML FROM CORE!
1232 showComposeButtonRow();
1236 if ($use_signature == true && $newmail == true && !isset($from_htmladdr_search)) {
1237 $signature = $idents[$identity]['signature'];
1239 if ($sig_first == '1') {
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
1246 if ($default_charset == 'iso-2022-jp') {
1247 $body_str = "\n\n".($prefix_sig==true?
"-- \n":'').mb_convert_encoding($signature, 'EUC-JP');
1249 $body_str = "\n\n".($prefix_sig==true?
"-- \n":'').decodeHeader($signature,false,false);
1251 $body_str .= "\n\n".htmlspecialchars(decodeHeader($body,false,false));
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');
1258 $body_str .= "\n\n".($prefix_sig==true?
"-- \n":'').decodeHeader($signature,false,false);
1262 $body_str = htmlspecialchars(decodeHeader($body,false,false));
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');
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);
1277 $oTemplate->display ('compose_body.tpl');
1279 if ($location_of_buttons == 'bottom') {
1280 //FIXME: DON'T ECHO HTML FROM CORE!
1281 showComposeButtonRow();
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;
1290 /* This code is for attachments */
1291 if ((bool) ini_get('file_uploads')) {
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. */
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' ) {
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
;
1321 $a['FileName'] = $attached_filename;
1322 $a['ContentType'] = $type;
1323 $a['Size'] = filesize($hashed_attachment_dir . '/' . $attached_file);
1330 $oTemplate->assign('max_file_size', empty($max) ?
-1 : $max);
1331 $oTemplate->assign('attachments', $attach);
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);
1341 $oTemplate->display('compose_attachments.tpl');
1342 } // End of file_uploads if-block
1343 /* End of attachment code */
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))) : '').
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.")
1365 if ($compose_new_win=='1') {
1366 $oTemplate->display('compose_newwin_close.tpl');
1369 do_hook('compose_bottom', $null);
1371 $oErrorHandler->setDelayedErrors(false);
1372 $oTemplate->display('footer.tpl');
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;
1382 global $oTemplate, $buffer_hook;
1384 if ($default_use_priority) {
1385 $priorities = array('1'=>_("High"), '3'=>_("Normal"), '5'=>_("Low"));
1386 $priority = isset($mailprio) ?
$mailprio : 3;
1388 $priorities = array();
1392 $mdn_user_support=getPref($data_dir, $username, 'mdn_user_support',$default_use_mdn);
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"),
1401 array_merge($address_book_button_attribs, array('onclick' => 'javascript:open_abook();')));
1403 $addr_book = addSubmit(_("Addresses"), 'html_addr_search', $address_book_button_attribs);
1406 $oTemplate->assign('allow_priority', $default_use_priority==1);
1407 $oTemplate->assign('priority_list', $priorities);
1408 $oTemplate->assign('current_priority', $priority);
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');
1414 $oTemplate->assign('drafts_enabled', $save_as_draft);
1415 $oTemplate->assign('address_book_button', $addr_book);
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);
1429 $oTemplate->display('compose_buttons.tpl');
1432 function checkInput ($show) {
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
1439 global $send_to, $send_to_cc, $send_to_bcc;
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)) {
1446 plain_error_message(_("You have not filled in the \"To:\" field."));
1451 } /* function checkInput() */
1454 /* True if FAILURE */
1455 function saveAttachedFiles($session) {
1456 global $composeMessage, $username, $attachment_dir;
1458 /* get out of here if no file was attached at all */
1459 if (! is_uploaded_file($_FILES['attachfile']['tmp_name']) ) {
1463 $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
1464 $localfilename = sq_get_attach_tempfile();
1465 $fullpath = $hashed_attachment_dir . '/' . $localfilename;
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))) {
1474 $type = strtolower($_FILES['attachfile']['type']);
1475 $name = $_FILES['attachfile']['name'];
1476 $composeMessage->initAttachment($type, $name, $localfilename);
1479 /* parse values like 8M and 2k into bytes */
1480 function getByteSize($ini_size) {
1486 $ini_size = trim($ini_size);
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))) {
1491 switch(strtoupper(substr($ini_size, -1))) {
1493 $bytesize = 1073741824;
1496 $bytesize = 1048576;
1503 return ($bytesize * (int)substr($ini_size, 0, -1));
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.
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.
1522 * @return boolean FALSE if delivery failed, or some non-FALSE value
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;
1533 $rfc822_header = $composeMessage->rfc822_header
;
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;
1542 $special_encoding='';
1543 if (strtolower($default_charset) == 'iso-2022-jp') {
1544 if (mb_detect_encoding($body) == 'ASCII') {
1545 $special_encoding = '8bit';
1547 $body = mb_convert_encoding($body, 'JIS');
1548 $special_encoding = '7bit';
1551 $composeMessage->setBody($body);
1554 $reply_to = $idents[$identity]['reply_to'];
1556 $from_addr = build_from_header($identity);
1557 $rfc822_header->from
= $rfc822_header->parseAddress($from_addr,true);
1559 $rfc822_header->reply_to
= $rfc822_header->parseAddress($reply_to,true);
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
);
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']);
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;
1587 $mime_header->encoding
= '8bit';
1589 if ($default_charset) {
1590 $mime_header->parameters
['charset'] = $default_charset;
1592 $message_body->mime_header
= $mime_header;
1593 array_unshift($composeMessage->entities
, $message_body);
1594 $content_type = new ContentType('multipart/mixed');
1596 $content_type = new ContentType('text/plain');
1597 if ($special_encoding) {
1598 $rfc822_header->encoding
= $special_encoding;
1600 $rfc822_header->encoding
= '8bit';
1602 if ($default_charset) {
1603 $content_type->properties
['charset']=$default_charset;
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;
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);
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;
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 = '';
1641 $sendmail_args = '-i -t';
1644 $deliver = new Deliver_SendMail(array('sendmail_args'=>$sendmail_args));
1645 $stream = $deliver->initStream($composeMessage,$sendmail_path);
1647 global $draft_folder;
1648 $imap_stream = sqimap_login($username, false, $imapServerAddress,
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();
1659 $msg = '<br />'.sprintf(_("Error: Draft folder %s does not exist."), htmlspecialchars($draft_folder));
1660 plain_error_message($msg);
1666 $deliver->mail($composeMessage, $stream, $reply_id, $reply_ent_id);
1667 $success = $deliver->finalizeStream($stream);
1670 // $deliver->dlv_server_msg is not always server's reply
1671 $msg = _("Message not sent.") . "<br />\n" .
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
1677 _("Server replied:") . ' ' . $deliver->dlv_ret_nr
. ' ' .
1678 nl2br(htmlspecialchars($deliver->dlv_server_msg
));
1680 plain_error_message($msg);
1683 $imap_stream = sqimap_login($username, false, $imapServerAddress, $imapPort, 0);
1686 // mark as replied or forwarded if applicable
1688 global $what, $iAccount, $startMessage, $passed_id, $mailbox;
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());
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'])) {
1701 * Only update the cached headers if the header is
1704 if (isset($aMailbox['MSG_HEADERS'][$passed_id])) {
1705 $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS'] = $aMsg['FLAGS'];
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'])) {
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'];
1727 * Write mailbox with updated seen flag information back to cache.
1729 if(isset($aUpdatedMsgs[$passed_id])) {
1730 $mailbox_cache[$iAccount.'_'.$aMailbox['NAME']] = $aMailbox;
1731 sqsession_register($mailbox_cache,'mailbox_cache');
1737 // move to sent folder
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;
1743 $svr_allow_sent = false;
1746 if (isset($sent_folder) && (($sent_folder != '') ||
($sent_folder != 'none'))
1747 && sqimap_mailbox_exists( $imap_stream, $sent_folder)) {
1753 if ((isset($move_to_sent) && ($move_to_sent != 0)) ||
(!isset($move_to_sent))) {
1754 $lcl_allow_sent = true;
1756 $lcl_allow_sent = false;
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;
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);
1775 $composeMessage->purgeAttachments();
1776 sqimap_logout($imap_stream);