Fix for #1093360.
[squirrelmail.git] / src / compose.php
index e0011165ca94786d5c91369d8bd1c09e1ae9c654..1678bfd73d588225a7c5fdaf9eeb8eaa1a9b70ad 100644 (file)
@@ -3,9 +3,6 @@
 /**
  * compose.php
  *
- * Copyright (c) 1999-2005 The SquirrelMail Project Team
- * Licensed under the GNU GPL. For full terms see the file COPYING.
- *
  * This code sends a mail.
  *
  * There are 4 modes of operation:
@@ -14,6 +11,8 @@
  *    - Send mail
  *    - Save As Draft
  *
+ * @copyright © 1999-2006 The SquirrelMail Project Team
+ * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  * @version $Id$
  * @package squirrelmail
  */
@@ -25,7 +24,7 @@
 define('SM_PATH','../');
 
 /* SquirrelMail required files. */
-require_once(SM_PATH . 'include/validate.php');
+include_once(SM_PATH . 'include/validate.php');
 require_once(SM_PATH . 'functions/global.php');
 require_once(SM_PATH . 'functions/imap.php');
 require_once(SM_PATH . 'functions/date.php');
@@ -49,6 +48,11 @@ sqgetGlobalVar('delimiter', $delimiter,     SQ_SESSION);
 
 sqgetGlobalVar('composesession',    $composesession,    SQ_SESSION);
 sqgetGlobalVar('compose_messages',  $compose_messages,  SQ_SESSION);
+sqgetGlobalVar('delayed_errors',  $delayed_errors,  SQ_SESSION);
+if (is_array($delayed_errors)) {
+    $oErrorHandler->AssignDelayedErrors($delayed_errors);
+    sqsession_unregister("delayed_errors");
+}
 
 /** SESSION/POST/GET VARS */
 sqgetGlobalVar('session',$session);
@@ -83,6 +87,7 @@ if ( sqgetGlobalVar('startMessage',$startMessage) ) {
     $startMessage = 1;
 }
 
+
 /** POST VARS */
 sqgetGlobalVar('sigappend',             $sigappend,             SQ_POST);
 sqgetGlobalVar('from_htmladdr_search',  $from_htmladdr_search,  SQ_POST);
@@ -97,6 +102,12 @@ if ( sqgetGlobalVar('return', $temp, SQ_POST) ) {
 
 /** GET VARS */
 sqgetGlobalVar('attachedmessages', $attachedmessages, SQ_GET);
+if ( sqgetGlobalVar('account', $temp,  SQ_GET) ) {
+    $iAccount = (int) $temp;
+} else {
+    $iAccount = 0;
+}
+
 
 /** get smaction */
 if ( !sqgetGlobalVar('smaction',$action) )
@@ -179,8 +190,6 @@ function getReplyCitation($orig_from, $orig_date) {
         $sOrig_from = decodeHeader($orig_from->getAddress(false),false,false,true);
     }
 
-//    $from = decodeHeader($orig_header->getAddr_s('from',"\n$indent"),false,false);
-
     /* First, return an empty string when no citation style selected. */
     if (($reply_citation_style == '') || ($reply_citation_style == 'none')) {
         return '';
@@ -200,8 +209,7 @@ function getReplyCitation($orig_from, $orig_date) {
         $full_reply_citation = sprintf(_("%s wrote:"),$sOrig_from);
         break;
     case 'quote_who':
-        // FIXME: do we have to translate xml formating?
-        $start = '<' . _("quote") . ' ' . _("who") . '="';
+        $start = '<quote who="';
         $end   = '">';
         $full_reply_citation = $start . $sOrig_from . $end;
         break;
@@ -342,7 +350,6 @@ if (!isset($compose_messages)) {
 }
 
 if (!isset($compose_messages[$session]) || ($compose_messages[$session] == NULL)) {
-    /* if (!array_key_exists($session, $compose_messages)) {  /* We can only do this in PHP >= 4.1 */
     $composeMessage = new Message();
     $rfc822_header = new Rfc822Header();
     $composeMessage->rfc822_header = $rfc822_header;
@@ -384,13 +391,17 @@ if ($draft) {
             }
             sqimap_logout($imap_stream);
         }
+        if (count($oErrorHandler->aErrors)) {
+            sqsession_register($oErrorHandler->aErrors,"delayed_errors");
+        }
+        session_write_close();
         if ($compose_new_win == '1') {
             if ( !isset($pageheader_sent) || !$pageheader_sent ) {
                 Header("Location: $location/compose.php?saved_draft=yes&session=$composesession");
             } else {
-                echo '   <br><br><center><a href="' . $location
+                echo '   <br><br><div style="text-align: center;"><a href="' . $location
                     . '/compose.php?saved_sent=yes&amp;session=' . $composesession . '">'
-                    . _("Return") . '</a></center>';
+                    . _("Return") . '</a></div>';
             }
             exit();
         } else {
@@ -398,10 +409,10 @@ if ($draft) {
                 Header("Location: $location/right_main.php?mailbox=" . urlencode($draft_folder) .
                    "&startMessage=1&note=".urlencode($draft_message));
             } else {
-                echo '   <br><br><center><a href="' . $location
+                echo '   <br><br><div style="text-align: center;"><a href="' . $location
                     . '/right_main.php?mailbox=' . urlencode($draft_folder)
                     . '&amp;startMessage=1&amp;note=' . urlencode($draft_message) .'">'
-                    . _("Return") . '</a></center>';
+                    . _("Return") . '</a></div>';
             }
             exit();
         }
@@ -417,7 +428,7 @@ if ($send) {
     if (checkInput(false) && !isset($AttachFailure)) {
         if ($mailbox == "All Folders") {
             /* We entered compose via the search results page */
-            $mailbox="INBOX"; /* Send 'em to INBOX, that's safe enough */
+            $mailbox = 'INBOX'; /* Send 'em to INBOX, that's safe enough */
         }
         $urlMailbox = urlencode (trim($mailbox));
         if (! isset($passed_id)) {
@@ -458,11 +469,14 @@ if ($send) {
         $composeMessage=$compose_messages[$session];
 
         $Result = deliverMessage($composeMessage);
+
+        do_hook('compose_send_after', $Result, $composeMessage);
         if (! $Result) {
             showInputForm($session);
             exit();
         }
         unset($compose_messages[$session]);
+
         /* if it is resumed draft, delete draft message */
         if ( isset($delete_draft)) {
             $imap_stream = sqimap_login($username, $key, $imapServerAddress, $imapPort, false);
@@ -476,13 +490,20 @@ if ($send) {
             }
             sqimap_logout($imap_stream);
         }
+        /*
+         * Store the error array in the session because they will be lost on a redirect
+         */
+        if (count($oErrorHandler->aErrors)) {
+            sqsession_register($oErrorHandler->aErrors,"delayed_errors");
+        }
+        session_write_close();
         if ($compose_new_win == '1') {
             if ( !isset($pageheader_sent) || !$pageheader_sent ) {
                 Header("Location: $location/compose.php?mail_sent=yes");
             } else {
-                echo '   <br><br><center><a href="' . $location
+                echo '   <br><br><div style="text-align: center;"><a href="' . $location
                     . '/compose.php?mail_sent=yes">'
-                    . _("Return") . '</a></center>';
+                    . _("Return") . '</a></div>';
             }
             exit();
         } else {
@@ -490,10 +511,10 @@ if ($send) {
                 Header("Location: $location/right_main.php?mailbox=$urlMailbox".
                     "&startMessage=$startMessage&mail_sent=yes");
             } else {
-                echo '   <br><br><center><a href="' . $location
+                echo '   <br><br><div style="text-align: center;"><a href="' . $location
                     . "/right_main.php?mailbox=$urlMailbox"
                     . "&amp;startMessage=$startMessage&amp;mail_sent=yes\">"
-                    . _("Return") . '</a></center>';
+                    . _("Return") . '</a></div>';
             }
             exit();
         }
@@ -587,9 +608,10 @@ elseif (isset($sigappend)) {
     if (isset($delete) && is_array($delete)) {
         $composeMessage = $compose_messages[$session];
         foreach($delete as $index) {
-            $attached_file = $composeMessage->entities[$index]->att_local_name;
-            unlink ($attached_file);
-            unset ($composeMessage->entities[$index]);
+            if (!empty($composeMessage->entities) && isset($composeMessage->entities[$index])) {
+                $composeMessage->entities[$index]->purgeAttachments();
+                unset ($composeMessage->entities[$index]);
+            }
         }
         $new_entities = array();
         foreach ($composeMessage->entities as $entity) {
@@ -756,7 +778,6 @@ function newMail ($mailbox='', $passed_id='', $passed_ent_id='', $action='', $se
         } else {
             $mailprio = '';
         }
-        //ClearAttachments($session);
 
         $identity = '';
         $from_o = $orig_header->from;
@@ -1018,7 +1039,8 @@ function showInputForm ($session, $values=false) {
         $from_htmladdr_search, $location_of_buttons, $attachment_dir,
         $username, $data_dir, $identity, $idents, $delete_draft,
         $mailprio, $compose_new_win, $saved_draft, $mail_sent, $sig_first,
-        $username, $compose_messages, $composesession, $default_charset;
+        $username, $compose_messages, $composesession, $default_charset,
+        $compose_onsubmit, $oTemplate;
 
     if (checkForJavascript()) {
         $onfocus = ' onfocus="alreadyFocused=true;"';
@@ -1045,7 +1067,7 @@ function showInputForm ($session, $values=false) {
     }
 
     if ($use_javascript_addr_book) {
-        echo "\n". '<script language="JavaScript">'."\n<!--\n" .
+        echo "\n". '<script type="text/javascript">'."\n<!--\n" .
             'function open_abook() { ' . "\n" .
             '  var nwin = window.open("addrbook_popup.php","abookpopup",' .
             '"width=670,height=300,resizable=yes,scrollbars=yes");' . "\n" .
@@ -1057,8 +1079,37 @@ function showInputForm ($session, $values=false) {
 
     echo "\n" . '<form name="compose" action="compose.php" method="post" ' .
         'enctype="multipart/form-data"';
+
+    $compose_onsubmit = array();
     do_hook('compose_form');
 
+    // Plugins that use compose_form hook can add an array entry
+    // to the globally scoped $compose_onsubmit; we add them up
+    // here and format the form tag's full onsubmit handler.
+    // Each plugin should use "return false" if they need to
+    // stop form submission but otherwise should NOT use "return
+    // true" to give other plugins the chance to do what they need
+    // to do; SquirrelMail itself will add the final "return true".
+    // Onsubmit text is enclosed inside of double quotes, so plugins
+    // need to quote accordingly.
+    if (checkForJavascript()) {
+        $onsubmit_text = ' onsubmit="';
+        if (empty($compose_onsubmit))
+            $compose_onsubmit = array();
+        else if (!is_array($compose_onsubmit))
+            $compose_onsubmit = array($compose_onsubmit);
+
+        foreach ($compose_onsubmit as $text) {
+            $text = trim($text);
+            if (substr($text, -1) != ';' && substr($text, -1) != '}')
+                $text .= '; ';
+            $onsubmit_text .= $text;
+        }
+
+        echo $onsubmit_text . ' return true;"';
+    }
+
+
     echo ">\n";
 
     echo addHidden('startMessage', $startMessage);
@@ -1078,10 +1129,10 @@ function showInputForm ($session, $values=false) {
     }
 
     if ($saved_draft == 'yes') {
-        echo '<br /><center><b>'. _("Draft Saved").'</center></b>';
+        echo '<br /><div style="text-align: center;"><b>'. _("Draft Saved").'</div></b>';
     }
     if ($mail_sent == 'yes') {
-        echo '<br /><center><b>'. _("Your Message has been sent.").'</center></b>';
+        echo '<br /><div style="text-align: center;"><b>'. _("Your Message has been sent.").'</div></b>';
     }
     if ($compose_new_win == '1') {
         echo '<table align="center" bgcolor="'.$color[0].'" width="100%" border="0">'."\n" .
@@ -1304,7 +1355,7 @@ function showInputForm ($session, $values=false) {
     }
 
     do_hook('compose_bottom');
-    echo '</body></html>' . "\n";
+    $oTemplate->display('footer.tpl');
 }
 
 
@@ -1343,7 +1394,7 @@ function showComposeButtonRow() {
         '      <td>' . "\n" .
         '         <input type="submit" name="sigappend" value="' . _("Signature") . '" />' . "\n";
     if ($use_javascript_addr_book) {
-        echo "         <script language=\"JavaScript\"><!--\n document.write(\"".
+        echo "         <script type=\"text/javascript\"><!--\n document.write(\"".
             "            <input type=button value=\\\""._("Addresses").
             "\\\" onclick=\\\"javascript:open_abook();\\\" />\");".
             "            // --></script><noscript>\n".
@@ -1387,7 +1438,7 @@ function checkInput ($show) {
 
 /* True if FAILURE */
 function saveAttachedFiles($session) {
-    global $_FILES, $attachment_dir, $attachments, $username,
+    global $_FILES, $attachment_dir, $username,
         $data_dir, $compose_messages;
 
     /* get out of here if no file was attached at all */
@@ -1403,10 +1454,10 @@ function saveAttachedFiles($session) {
         $full_localfilename = "$hashed_attachment_dir/$localfilename";
     }
 
-    // FIXME: we SHOULD prefer move_uploaded_file over rename because
-    // m_u_f works better with restricted PHP installs (safe_mode, open_basedir)
-    if (!@rename($_FILES['attachfile']['tmp_name'], $full_localfilename)) {
-        if (!@move_uploaded_file($_FILES['attachfile']['tmp_name'],$full_localfilename)) {
+    // m_u_f works better with restricted PHP installs (safe_mode, open_basedir),
+    // if that doesn't work, try a simple rename.
+    if (!@move_uploaded_file($_FILES['attachfile']['tmp_name'],$full_localfilename)) {
+        if (!@rename($_FILES['attachfile']['tmp_name'], $full_localfilename)) {
             return true;
         }
     }
@@ -1418,18 +1469,6 @@ function saveAttachedFiles($session) {
     sqsession_register($compose_messages , 'compose_messages');
 }
 
-function ClearAttachments($composeMessage) {
-    if ($composeMessage->att_local_name) {
-        $attached_file = $composeMessage->att_local_name;
-        if (file_exists($attached_file)) {
-            unlink($attached_file);
-        }
-    }
-    for ($i=0, $entCount=count($composeMessage->entities);$i< $entCount; ++$i) {
-        ClearAttachments($composeMessage->entities[$i]);
-    }
-}
-
 /* parse values like 8M and 2k into bytes */
 function getByteSize($ini_size) {
 
@@ -1463,7 +1502,7 @@ function getByteSize($ini_size) {
 
 /**
  * temporary function to make use of the deliver class.
- * In the future the responsable backend should be automaticly loaded
+ * In the future the responsible backend should be automaticly loaded
  * and conf.pl should show a list of available backends.
  * The message also should be constructed by the message class.
  */
@@ -1584,8 +1623,8 @@ function deliverMessage($composeMessage, $draft=false) {
                 $smtpServerAddress, $smtpPort, $user, $pass, $authPop);
     } elseif (!$draft) {
         require_once(SM_PATH . 'class/deliver/Deliver_SendMail.class.php');
-        global $sendmail_path;
-        $deliver = new Deliver_SendMail();
+        global $sendmail_path, $sendmail_args;
+        $deliver = new Deliver_SendMail(array('sendmail_args'=>$sendmail_args));
         $stream = $deliver->initStream($composeMessage,$sendmail_path);
     } elseif ($draft) {
         global $draft_folder;
@@ -1601,22 +1640,29 @@ function deliverMessage($composeMessage, $draft=false) {
             sqimap_append_done ($imap_stream, $draft_folder);
             sqimap_logout($imap_stream);
             unset ($imap_deliver);
+            $composeMessage->purgeAttachments();
             return $length;
         } else {
-            $msg  = '<br />'.sprintf(_("Error: Draft folder %s does not exist."), $draft_folder);
+            $msg  = '<br />'.sprintf(_("Error: Draft folder %s does not exist."), htmlspecialchars($draft_folder));
             plain_error_message($msg, $color);
             return false;
         }
     }
-    $succes = false;
+    $success = false;
     if ($stream) {
         $length = $deliver->mail($composeMessage, $stream);
-        $succes = $deliver->finalizeStream($stream);
-    }
-    if (!$succes) {
-        $msg  = $deliver->dlv_msg . '<br />' .
-            _("Server replied:") . ' ' . $deliver->dlv_ret_nr . ' ' .
-            $deliver->dlv_server_msg;
+        $success = $deliver->finalizeStream($stream);
+    }
+    if (!$success) {
+        // $deliver->dlv_server_msg is not always server's reply
+        $msg  = $deliver->dlv_msg;
+        if (!empty($deliver->dlv_server_msg)) {
+            // add 'server replied' part only when it is not empty.
+            // Delivery error can be generated by delivery class itself
+            $msg.='<br />' .
+                _("Server replied:") . ' ' . $deliver->dlv_ret_nr . ' ' .
+                nl2br(htmlspecialchars($deliver->dlv_server_msg));
+        }
         plain_error_message($msg, $color);
     } else {
         unset ($deliver);
@@ -1658,15 +1704,34 @@ function deliverMessage($composeMessage, $draft=false) {
             sqimap_append_done ($imap_stream, $sent_folder);
             unset ($imap_deliver);
         }
-        global $passed_id, $mailbox, $action;
-        ClearAttachments($composeMessage);
+
+        global $passed_id, $mailbox, $action, $what, $iAccount,$startMessage;
+
+        $composeMessage->purgeAttachments();
         if ($action == 'reply' || $action == 'reply_all') {
-            sqimap_mailbox_select ($imap_stream, $mailbox);
-            sqimap_toggle_flag($imap_stream, array($passed_id), '\\Answered', true, false);
+            $aMailbox = sqm_api_mailbox_select($imap_stream, $iAccount, $mailbox,array('setindex' => $what, 'offset' => $startMessage),array());
+            // check if we are allowed to set the \\Answered flag
+            if (in_array('\\answered',$aMailbox['PERMANENTFLAGS'], true)) {
+                $aUpdatedMsgs = sqimap_toggle_flag($imap_stream, array($passed_id), '\\Answered', true, false);
+                if (isset($aUpdatedMsgs[$passed_id]['FLAGS'])) {
+                    /**
+                     * Only update the cached headers if the header is
+                     * cached.
+                     */
+                    if (isset($aMailbox['MSG_HEADERS'][$passed_id])) {
+                        $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS'] = $aMsg['FLAGS'];
+                    }
+                }
+            }
+            /**
+             * Write mailbox with updated seen flag information back to cache.
+             */
+            $mailbox_cache[$iAccount.'_'.$aMailbox['NAME']] = $aMailbox;
+            sqsession_register($mailbox_cache,'mailbox_cache');
         }
         sqimap_logout($imap_stream);
     }
-    return $succes;
+    return $success;
 }
 
-?>
+?>
\ No newline at end of file