From b4b02114b24363b8500facaaf911edae3aeb4321 Mon Sep 17 00:00:00 2001
From: Jared Hancock <jared@osticket.com>
Date: Thu, 5 Sep 2013 20:34:25 +0000
Subject: [PATCH] Handle attachment filenames better

Previously, filenames saved in the database had the spaces changed for
underbars; however, other characters (such as commas and non-ascii
characters) presented issues with user agents downloading the attachments.

This patch handles the filename encoding for two special cases -- internet
explorer and safari, and provides the semi-standard RFC5987 method of
encoding the filename for the remaining browsers.

Attachments are no longer forced to be downloaded. It is up to the browser
to decide if the attachment should be shown in the browser or downloaded.

This patch also fixes a slight bug in the caching mechanism for downloads
concerning the last-modified time. The date sent to the browser was not
properly converted to GMT time, although the server claimed that it was.
---
 include/class.file.php | 36 +++++++++++++++++++-----------------
 1 file changed, 19 insertions(+), 17 deletions(-)

diff --git a/include/class.file.php b/include/class.file.php
index 9e85630c7..a896d9e9c 100644
--- a/include/class.file.php
+++ b/include/class.file.php
@@ -136,21 +136,23 @@ class AttachmentFile {
         return true;
     }
 
-
-    function display() {
-
+    function makeCacheable($ttl=3600) {
         // Thanks, http://stackoverflow.com/a/1583753/1025836
-        $last_modified = strtotime($this->lastModified());
-        header("Last-Modified: ".gmdate(DATE_RFC822, $last_modified)." GMT", false);
+        $last_modified = Misc::db2gmtime($this->lastModified());
+        header("Last-Modified: ".date('D, d M y H:i:s', $last_modified)." GMT", false);
         header('ETag: "'.$this->getHash().'"');
-        header('Cache-Control: private, max-age=3600');
-        header('Expires: ' . date(DATE_RFC822, time() + 3600) . ' GMT');
+        header("Cache-Control: private, max-age=$ttl");
+        header('Expires: ' . gmdate(DATE_RFC822, time() + $ttl)." GMT");
         header('Pragma: private');
         if (@strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $last_modified ||
             @trim($_SERVER['HTTP_IF_NONE_MATCH']) == $this->getHash()) {
                 header("HTTP/1.1 304 Not Modified");
                 exit();
         }
+    }
+
+    function display() {
+        $this->makeCacheable();
 
         header('Content-Type: '.($this->getType()?$this->getType():'application/octet-stream'));
         header('Content-Length: '.$this->getSize());
@@ -159,20 +161,20 @@ class AttachmentFile {
     }
 
     function download() {
+        $this->makeCacheable();
 
-        header('Pragma: public');
-        header('Expires: 0');
-        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
-        header('Cache-Control: public');
         header('Content-Type: '.($this->getType()?$this->getType():'application/octet-stream'));
 
         $filename=basename($this->getName());
         $user_agent = strtolower ($_SERVER['HTTP_USER_AGENT']);
-        if ((is_integer(strpos($user_agent,'msie'))) && (is_integer(strpos($user_agent,'win')))) {
-            header('Content-Disposition: filename='.$filename.';');
-        }else{
-            header('Content-Disposition: attachment; filename='.$filename.';' );
-        }
+        if (false !== strpos($user_agent,'msie') && false !== strpos($user_agent,'win'))
+            header('Content-Disposition: filename='.rawurlencode($filename).';');
+        elseif (false !== strpos($user_agent, 'safari') && false === strpos($user_agent, 'chrome'))
+            // Safari and Safari only can handle the filename as is
+            header('Content-Disposition: filename='.str_replace(',', '', $filename).';');
+        else
+            // Use RFC5987
+            header("Content-Disposition: filename*=UTF-8''".rawurlencode($filename).';' );
 
         header('Content-Transfer-Encoding: binary');
         header('Content-Length: '.$this->getSize());
@@ -238,7 +240,7 @@ class AttachmentFile {
         $sql='INSERT INTO '.FILE_TABLE.' SET created=NOW() '
             .',type='.db_input($file['type'])
             .',size='.db_input($file['size'])
-            .',name='.db_input(Format::file_name($file['name']))
+            .',name='.db_input($file['name'])
             .',hash='.db_input($file['hash']);
 
         # XXX: ft does not exists during the upgrade when attachments are
-- 
GitLab