Encode outgoing attachments that have lines longer than allowed per RFC. Otherwise...
authorpdontthink <pdontthink@7612ce4b-ef26-0410-bec9-ea0150e637f0>
Fri, 3 Apr 2009 08:31:17 +0000 (08:31 +0000)
committerpdontthink <pdontthink@7612ce4b-ef26-0410-bec9-ea0150e637f0>
Fri, 3 Apr 2009 08:31:17 +0000 (08:31 +0000)
git-svn-id: https://svn.code.sf.net/p/squirrelmail/code/trunk/squirrelmail@13512 7612ce4b-ef26-0410-bec9-ea0150e637f0

class/deliver/Deliver.class.php
functions/files.php

index 80f6297..db9e355 100644 (file)
@@ -282,17 +282,52 @@ class Deliver {
                 global $username, $attachment_dir;
                 $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
                 $filename = $message->att_local_name;
+
+                // inspect attached file for lines longer than allowed by RFC,
+                // in which case we'll be using base64 encoding (so we can split
+                // the lines up without corrupting them) instead of 8bit unencoded...
+                // (see RFC 2822/2.1.1)
+                //
+                // using 990 because someone somewhere is folding lines at
+                // 990 instead of 998 and I'm too lazy to find who it is
+                //
+                $file_has_long_lines = file_has_long_lines($hashed_attachment_dir
+                                                           . '/' . $filename, 990);
+
                 $file = fopen ($hashed_attachment_dir . '/' . $filename, 'rb');
-                while ($body_part = fgets($file, 4096)) {
-                    // remove NUL characters
-                    $body_part = str_replace("\0",'',$body_part);
-                    $length += $this->clean_crlf($body_part);
-                    if ($stream) {
-                        $this->preWriteToStream($body_part);
-                        $this->writeToStream($stream, $body_part);
+
+                // long lines were found, need to use base64 encoding
+                //
+                if ($file_has_long_lines) {
+                    while ($tmp = fread($file, 570)) {
+                        $body_part = chunk_split(base64_encode($tmp));
+                        // Up to 4.3.10 chunk_split always appends a newline,
+                        // while in 4.3.11 it doesn't if the string to split
+                        // is shorter than the chunk length.
+                        if( substr($body_part, -1 , 1 ) != "\n" )
+                            $body_part .= "\n";
+                        $length += $this->clean_crlf($body_part);
+                        if ($stream) {
+                            $this->writeToStream($stream, $body_part);
+                        }
                     }
-                    $last = $body_part;
                 }
+
+                // no excessively long lines - normal 8bit
+                //
+                else {
+                    while ($body_part = fgets($file, 4096)) {
+                        // remove NUL characters
+                        $body_part = str_replace("\0",'',$body_part);
+                        $length += $this->clean_crlf($body_part);
+                        if ($stream) {
+                            $this->preWriteToStream($body_part);
+                            $this->writeToStream($stream, $body_part);
+                        }
+                        $last = $body_part;
+                    }
+                }
+
                 fclose($file);
             }
             break;
@@ -461,10 +496,29 @@ class Deliver {
         if ($mime_header->encoding) {
             $header[] = 'Content-Transfer-Encoding: ' . $mime_header->encoding . $rn;
         } else {
-            if ($mime_header->type0 == 'text' || $mime_header->type0 == 'message') {
-                $header[] = 'Content-Transfer-Encoding: 8bit' .  $rn;
-            } else if ($mime_header->type0 == 'multipart' || $mime_header->type0 == 'alternative') {
+
+            // inspect attached file for lines longer than allowed by RFC,
+            // in which case we'll be using base64 encoding (so we can split
+            // the lines up without corrupting them) instead of 8bit unencoded...
+            // (see RFC 2822/2.1.1)
+            //
+            if (!empty($message->att_local_name)) { // is this redundant? I have no idea
+                global $username, $attachment_dir;
+                $hashed_attachment_dir = getHashedDir($username, $attachment_dir);
+                $filename = $hashed_attachment_dir . '/' . $message->att_local_name;
+
+                // using 990 because someone somewhere is folding lines at
+                // 990 instead of 998 and I'm too lazy to find who it is
+                //
+                $file_has_long_lines = file_has_long_lines($filename, 990);
+            } else
+                $file_has_long_lines = FALSE;
+
+            if ($mime_header->type0 == 'multipart' || $mime_header->type0 == 'alternative') {
                 /* no-op; no encoding needed */
+            } else if (($mime_header->type0 == 'text' || $mime_header->type0 == 'message')
+                    && !$file_has_long_lines) {
+                $header[] = 'Content-Transfer-Encoding: 8bit' .  $rn;
             } else {
                 $header[] = 'Content-Transfer-Encoding: base64' .  $rn;
             }
index 0a297ea..69d3ddf 100644 (file)
@@ -263,3 +263,33 @@ function list_files($directory_path, $extensions='', $return_filenames_only=TRUE
 }
 
 
+/**
+ * Determine if there are lines in a file longer than a given length
+ *
+ * @param string $filename   The full file path of the file to inspect
+ * @param int    $max_length If any lines in the file are GREATER THAN
+ *                           this number, this function returns TRUE.
+ *
+ * @return boolean TRUE as explained above, otherwise, (no long lines
+ *                 found) FALSE is returned.
+ *
+ */
+function file_has_long_lines($filename, $max_length) {
+
+    $FILE = @fopen($filename, 'rb');
+
+    if ($FILE) {
+        while (!feof($FILE)) {
+            $buffer = fgets($FILE, 4096);
+            if (strlen($buffer) > $max_length) {
+                fclose($FILE);
+                return TRUE;
+            }
+        }
+        fclose($FILE);
+    }
+
+    return FALSE;
+}
+
+