Account for case when no reply-to is set in user prefs
[squirrelmail.git] / src / compose.php
index fcd707fa708bfe54d6645552fb6ee9a4bb9132bb..fb0a9c57f5af6233667736e6e71d4165b933c0b4 100644 (file)
@@ -10,7 +10,7 @@
  *    - Send mail
  *    - Save As Draft
  *
- * @copyright © 1999-2007 The SquirrelMail Project Team
+ * @copyright 1999-2012 The SquirrelMail Project Team
  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  * @version $Id$
  * @package squirrelmail
@@ -87,6 +87,7 @@ sqgetGlobalVar('html_addr_search',$html_addr_search, $SQ_GLOBAL);
 sqgetGlobalVar('mail_sent',$mail_sent, $SQ_GLOBAL);
 sqgetGlobalVar('passed_id',$passed_id, $SQ_GLOBAL, NULL, SQ_TYPE_BIGINT);
 sqgetGlobalVar('passed_ent_id',$passed_ent_id, $SQ_GLOBAL);
+sqgetGlobalVar('fwduid',$fwduid, $SQ_GLOBAL, '');
 
 sqgetGlobalVar('attach',$attach, SQ_POST);
 sqgetGlobalVar('draft',$draft, SQ_POST);
@@ -137,6 +138,8 @@ if ( !sqgetGlobalVar('smaction',$action) )
     if ( sqgetGlobalVar('smaction_edit_new',$tmp) )   $action = 'edit_as_new';
 }
 
+sqgetGlobalVar('smtoken', $submitted_token, $SQ_GLOBAL, '');
+
 /**
  * Here we decode the data passed in from mailto.php.
  */
@@ -193,12 +196,10 @@ function replyAllString($header) {
     $url_replytoallcc = '';
     foreach( $url_replytoall_ar as $email => $personal) {
         if ($personal) {
-            // if personal name contains address separator then surround
-            // the personal name with double quotes.
-            if (strpos($personal,',') !== false) {
-                $personal = '"'.$personal.'"';
-            }
-            $url_replytoallcc .= ", $personal <$email>";
+            // always quote personal name (can't just quote it if
+            // it contains a comma separator, since it might still
+            // be encoded)
+            $url_replytoallcc .= ", \"$personal\" <$email>";
         } else {
             $url_replytoallcc .= ', '. $email;
         }
@@ -336,7 +337,7 @@ if (sqsession_is_registered('session_expired_post')) {
             'subject', 'newmail', 'send_to_bcc', 'passed_id', 'mailbox', 
             'from_htmladdr_search', 'identity', 'draft_id', 'delete_draft', 
             'mailprio', 'edit_as_new', 'attachments', 'composesession', 
-            'request_mdn', 'request_dr');
+            'request_mdn', 'request_dr', 'fwduid');
 
         foreach ($compo_var_list as $var) {
             if ( isset($session_expired_post[$var]) && !isset($$var) ) {
@@ -411,6 +412,11 @@ if (empty($mailbox)) {
 }
 
 if ($draft) {
+
+    // validate security token
+    //
+    sm_validate_security_token($submitted_token, 3600, TRUE);
+
     /*
      * Set $default_charset to correspond with the user's selection
      * of language interface.
@@ -465,11 +471,17 @@ if ($draft) {
 }
 
 if ($send) {
+
+    // validate security token
+    //
+    sm_validate_security_token($submitted_token, 3600, TRUE);
+
     if (isset($_FILES['attachfile']) &&
             $_FILES['attachfile']['tmp_name'] &&
             $_FILES['attachfile']['tmp_name'] != 'none') {
         $AttachFailure = saveAttachedFiles($session);
     }
+    
     if (checkInput(false) && !isset($AttachFailure)) {
         if ($mailbox == "All Folders") {
             /* We entered compose via the search results page */
@@ -586,6 +598,11 @@ if ($send) {
         /* sqimap_logout($imapConnection); */
     }
 } elseif (isset($html_addr_search_done)) {
+
+    // validate security token
+    //
+    sm_validate_security_token($submitted_token, 3600, TRUE);
+
     if ($compose_new_win == '1') {
         compose_Header($color, $mailbox);
     }
@@ -630,6 +647,11 @@ if ($send) {
      */
     include_once('./addrbook_search_html.php');
 } elseif (isset($attach)) {
+
+    // validate security token
+    //
+    sm_validate_security_token($submitted_token, 3600, TRUE);
+
     if ($compose_new_win == '1') {
         compose_Header($color, $mailbox);
     } else {
@@ -641,6 +663,11 @@ if ($send) {
     showInputForm($session);
 }
 elseif (isset($sigappend)) {
+
+    // validate security token
+    //
+    sm_validate_security_token($submitted_token, 3600, TRUE);
+
     $signature = $idents[$identity]['signature'];
 
     $body .= "\n\n".($prefix_sig==true? "-- \n":'').$signature;
@@ -651,6 +678,11 @@ elseif (isset($sigappend)) {
     }
     showInputForm($session);
 } elseif (isset($do_delete)) {
+
+    // validate security token
+    //
+    sm_validate_security_token($submitted_token, 3600, TRUE);
+
     if ($compose_new_win == '1') {
         compose_Header($color, $mailbox);
     } else {
@@ -702,6 +734,11 @@ elseif (isset($sigappend)) {
 
     $values = newMail($mailbox,$passed_id,$passed_ent_id, $action, $session);
 
+    // forward as attachment - subject is in the message in session
+    //
+    if ($action == 'forward_as_attachment' && empty($values['subject']))
+        $subject = $composeMessage->rfc822_header->subject;
+
     /* in case the origin is not read_body.php */
     if (isset($send_to)) {
         $values['send_to'] = $send_to;
@@ -913,6 +950,12 @@ function newMail ($mailbox='', $passed_id='', $passed_ent_id='', $action='', $se
             case ('forward_as_attachment'):
                 $subject = getforwardSubject(decodeHeader($orig_header->subject,false,false,true));
                 $composeMessage = getMessage_RFC822_Attachment($message, $composeMessage, $passed_id, $passed_ent_id, $imapConnection);
+                $subject = decodeHeader($orig_header->subject,false,false,true);
+                $subject = str_replace('"', "'", $subject);
+                $subject = trim($subject);
+                if (substr(strtolower($subject), 0, 4) != 'fwd:') {
+                    $subject = 'Fwd: ' . $subject;
+                }
                 $body = '';
                 break;
             case ('reply_all'):
@@ -921,20 +964,22 @@ function newMail ($mailbox='', $passed_id='', $passed_ent_id='', $action='', $se
                 } else {
                     $send_to_cc = replyAllString($orig_header);
                     $send_to_cc = decodeHeader($send_to_cc,false,false,true);
+                    $send_to_cc = str_replace('""', '"', $send_to_cc);
                 }
             case ('reply'):
                 // skip this if send_to was already set right above here
                 if(!$send_to) {
                     $send_to = $orig_header->reply_to;
                     if (is_array($send_to) && count($send_to)) {
-                        $send_to = $orig_header->getAddr_s('reply_to');
+                        $send_to = $orig_header->getAddr_s('reply_to', ',', FALSE, TRUE);
                     } else if (is_object($send_to)) { /* unneccesarry, just for failsafe purpose */
-                        $send_to = $orig_header->getAddr_s('reply_to');
+                        $send_to = $orig_header->getAddr_s('reply_to', ',', FALSE, TRUE);
                     } else {
-                        $send_to = $orig_header->getAddr_s('from');
+                        $send_to = $orig_header->getAddr_s('from', ',', FALSE, TRUE);
                     }
                 }
                 $send_to = decodeHeader($send_to,false,false,true);
+                $send_to = str_replace('""', '"', $send_to);
                 $subject = decodeHeader($orig_header->subject,false,false,true);
                 $subject = str_replace('"', "'", $subject);
                 $subject = trim($subject);
@@ -947,7 +992,7 @@ function newMail ($mailbox='', $passed_id='', $passed_ent_id='', $action='', $se
                 $body = '';
                 $strip_sigs = getPref($data_dir, $username, 'strip_sigs');
                 foreach ($rewrap_body as $line) {
-                    if ($strip_sigs && substr($line,0,3) == '-- ') {
+                    if ($strip_sigs && rtrim($line, "\r\n") == '-- ') {
                         break;
                     }
                     if (preg_match("/^(>+)/", $line, $matches)) {
@@ -1018,7 +1063,8 @@ function getAttachments($message, &$composeMessage, $passed_id, $entities, $imap
                     $filename = $message->getFilename();
                     break;
             }
-            $filename = str_replace('&#32;', ' ', decodeHeader($filename));
+//FIXME: added three args to the following, so as to set the last one to TRUE, to mimick a fix in 1.4.21 (#2994865), but didn't test this (note that in 1.4.21, the 2nd and 3rd args are FALSE, but here in this code, they weren't being specified (thus defaulting to TRUE), so I don't know if that means this code is outdated and should have been changed to FALSE, FALSE or if this code is completely different and the addition of the TRUE for arg #4 is wrong
+            $filename = str_replace('&#32;', ' ', decodeHeader($filename, true, true, true));
             if (isset($languages[$squirrelmail_language]['XTRA_CODE']) &&
                     function_exists($languages[$squirrelmail_language]['XTRA_CODE'] . '_encode')) {
                 $filename =  call_user_func($languages[$squirrelmail_language]['XTRA_CODE'] . '_encode', $filename);
@@ -1080,7 +1126,7 @@ function showInputForm ($session, $values=false) {
         $body, $startMessage, $action, $attachments,
         $use_signature, $signature, $prefix_sig, $session_expired,
         $editor_size, $editor_height, $subject, $newmail,
-        $use_javascript_addr_book, $passed_id, $mailbox,
+        $use_javascript_addr_book, $passed_id, $mailbox, $fwduid,
         $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,
@@ -1139,28 +1185,40 @@ function showInputForm ($session, $values=false) {
     // to do; SquirrelMail itself will add the final "return true".
     // Onsubmit text is enclosed inside of double quotes, so plugins
     // need to quote accordingly.
+    //
+    // Also, plugin authors should try to retain compatibility with
+    // the Compose Extras plugin by resetting its compose submit
+    // counter when preventing form submit.  Use this code: 
+    // if (your-code-here) { submit_count = 0; return false; }
+    //
     if (checkForJavascript()) {
-        $onsubmit_text = ' onsubmit="';
         if (empty($compose_onsubmit))
             $compose_onsubmit = array();
         else if (!is_array($compose_onsubmit))
             $compose_onsubmit = array($compose_onsubmit);
 
+        $onsubmit_text = '';
         foreach ($compose_onsubmit as $text) {
             $text = trim($text);
-            if (substr($text, -1) != ';' && substr($text, -1) != '}')
-                $text .= '; ';
-            $onsubmit_text .= $text;
+            if (!empty($text)) {
+                if (substr($text, -1) != ';' && substr($text, -1) != '}')
+                    $text .= '; ';
+                $onsubmit_text .= $text;
+            }
         }
 
+        if (!empty($onsubmit_text))
 //FIXME: DON'T ECHO HTML FROM CORE!
-        echo $onsubmit_text . ' return true;"';
+            echo ' onsubmit="' . $onsubmit_text . ' return true;"';
     }
 
 
 //FIXME: NO HTML IN CORE!
     echo ">\n";
 
+//FIXME: DON'T ECHO HTML FROM CORE!
+    echo addHidden('smtoken', sm_generate_security_token());
+
 //FIXME: DON'T ECHO HTML FROM CORE!
     echo addHidden('startMessage', $startMessage);
 
@@ -1182,6 +1240,11 @@ function showInputForm ($session, $values=false) {
         echo addHidden('passed_id', $passed_id);
     }
 
+    if (isset($fwduid)) {
+//FIXME: DON'T ECHO HTML FROM CORE!
+        echo addHidden('fwduid', $fwduid);
+    }
+
     if ($saved_draft == 'yes') {
         $oTemplate->assign('note', _("Your draft has been saved."));
         $oTemplate->display('note.tpl');
@@ -1219,7 +1282,9 @@ function showInputForm ($session, $values=false) {
     // access keys...
     //
     global $accesskey_compose_to, $accesskey_compose_cc,
-           $accesskey_compose_bcc, $accesskey_compose_subject;
+           $accesskey_compose_identity, $accesskey_compose_bcc,
+           $accesskey_compose_subject;
+    $oTemplate->assign('accesskey_compose_identity', $accesskey_compose_identity);
     $oTemplate->assign('accesskey_compose_to', $accesskey_compose_to);
     $oTemplate->assign('accesskey_compose_cc', $accesskey_compose_cc);
     $oTemplate->assign('accesskey_compose_bcc', $accesskey_compose_bcc);
@@ -1342,32 +1407,32 @@ function showInputForm ($session, $values=false) {
     } // End of file_uploads if-block
     /* End of attachment code */
 
-//FIXME: no direct echoing to browser, no HTML output in core!
-    echo addHidden('username', $username).
-         addHidden('smaction', $action).
-         addHidden('mailbox', $mailbox);
+    $oTemplate->assign('username', $username);
+    $oTemplate->assign('smaction', $action);
+    $oTemplate->assign('mailbox', $mailbox);
     sqgetGlobalVar('QUERY_STRING', $queryString, SQ_SERVER);
-//FIXME: no direct echoing to browser, no HTML output in core!
-    echo addHidden('composesession', $composesession).
-        addHidden('querystring', $queryString).
-        (!empty($attach_array) ?
-         addHidden('attachments', urlencode(serialize($attach_array))) : '').
-        "</form>\n";
+    $oTemplate->assign('querystring', $queryString);
+    $oTemplate->assign('composesession', $composesession);
+    $oTemplate->assign('send_button_count', unique_widget_name('send', TRUE));
+    if (!empty($attach_array))
+        $oTemplate->assign('attachments', urlencode(serialize($attach_array)));
+
+    $aUserNotices = array();
+
+    // File uploads are off, so we didn't show that part of the form.
+    // To avoid bogus bug reports, tell the user why. 
     if (!(bool) ini_get('file_uploads')) {
-        /* File uploads are off, so we didn't show that part of the form.
-           To avoid bogus bug reports, tell the user why. */
-//FIXME: no direct echoing to browser, no HTML output in core!
-        echo '<p style="text-align:center">'
-            . _("Because PHP file uploads are turned off, you can not attach files to this message. Please see your system administrator for details.")
-            . "</p>\r\n";
+        $aUserNotices[] = _("Because PHP file uploads are turned off, you can not attach files to this message. Please see your system administrator for details.");
     }
 
+    $oTemplate->assign('user_notices', $aUserNotices);
+
+    $oTemplate->display('compose_form_close.tpl');
+
     if ($compose_new_win=='1') {
         $oTemplate->display('compose_newwin_close.tpl');
     }
 
-    do_hook('compose_bottom', $null);
-
     $oErrorHandler->setDelayedErrors(false);
     $oTemplate->display('footer.tpl');
 }
@@ -1476,7 +1541,21 @@ function saveAttachedFiles($session) {
     $composeMessage->initAttachment($type, $name, $localfilename);
 }
 
-/* parse values like 8M and 2k into bytes */
+/**
+  * Parse strings such as "8M" and "2k" into their corresponding size in bytes
+  *
+  * NOTE: This function only recognizes the suffixes "K", "M" and "G"
+  *       and will probably break very easily if the given size is in
+  *       some completely different format.
+  *
+  * @param string $ini_size The input string to be converted
+  *
+  * @return mixed Boolean FALSE if something went wrong (the value passed in
+  *               was empty?, the suffix was not recognized?), otherwise, the
+  *               converted size in bytes (just the number (as an integer),
+  *               no unit identifier included)
+  *
+  */
 function getByteSize($ini_size) {
 
     if(!$ini_size) {
@@ -1498,6 +1577,8 @@ function getByteSize($ini_size) {
             case 'K':
                 $bytesize = 1024;
                 break;
+             default:
+                return FALSE;
         }
 
         return ($bytesize * (int)substr($ini_size, 0, -1));
@@ -1552,6 +1633,8 @@ function deliverMessage(&$composeMessage, $draft=false) {
 
     $reply_to = '';
     $reply_to  = $idents[$identity]['reply_to'];
+    if ($reply_to && strpos($reply_to, '@') === FALSE)
+        $reply_to .= '@' . $domain;
     
     $from_addr = build_from_header($identity);
     $rfc822_header->from = $rfc822_header->parseAddress($from_addr,true);
@@ -1685,7 +1768,7 @@ function deliverMessage(&$composeMessage, $draft=false) {
 
         // mark as replied or forwarded if applicable
         //
-        global $what, $iAccount, $startMessage, $passed_id, $mailbox;
+        global $what, $iAccount, $startMessage, $passed_id, $fwduid, $mailbox;
 
         if ($action=='reply' || $action=='reply_all' || $action=='forward' || $action=='forward_as_attachment') {
             require(SM_PATH . 'functions/mailbox_display.php');
@@ -1713,10 +1796,22 @@ function deliverMessage(&$composeMessage, $draft=false) {
                 if (in_array('$forwarded',$aMailbox['PERMANENTFLAGS'], true) ||
                     in_array('\\*',$aMailbox['PERMANENTFLAGS'])) {
 
-                    $aUpdatedMsgs = sqimap_toggle_flag($imap_stream, array($passed_id), '$Forwarded', true, false);
-                    if (isset($aUpdatedMsgs[$passed_id]['FLAGS'])) {
-                        if (isset($aMailbox['MSG_HEADERS'][$passed_id])) {
-                            $aMailbox['MSG_HEADERS'][$passed_id]['FLAGS'] = $aMsg['FLAGS'];
+                    // when forwarding as an attachment from the message
+                    // list, passed_id is not used, need to get UID(s)
+                    // from the query string
+                    //
+                    if (empty($passed_id) && !empty($fwduid))
+                        $ids = explode('_', $fwduid);
+                    else
+                        $ids = array($passed_id);
+
+                    $aUpdatedMsgs = sqimap_toggle_flag($imap_stream, $ids, '$Forwarded', true, false);
+
+                    foreach ($ids as $id) {
+                        if (isset($aUpdatedMsgs[$id]['FLAGS'])) {
+                            if (isset($aMailbox['MSG_HEADERS'][$id])) {
+                                $aMailbox['MSG_HEADERS'][$id]['FLAGS'] = $aMsg['FLAGS'];
+                            }
                         }
                     }
                 }