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