From 586ea90fada4d007947d453febbb7e2ce9d93f6f Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Fri, 27 May 2016 17:24:57 +0100 Subject: [PATCH 1/3] Set attachment filename to name from Content-Type --- api/v1.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/api/v1.go b/api/v1.go index 9bebf4a..78f9e66 100644 --- a/api/v1.go +++ b/api/v1.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" "net/smtp" + "regexp" "strconv" "strings" "time" @@ -204,7 +205,8 @@ func (apiv1 *APIv1) download_part(w http.ResponseWriter, req *http.Request) { // TODO extension from content-type? apiv1.defaultOptions(w, req) - w.Header().Set("Content-Disposition", "attachment; filename=\""+id+"-part-"+part+"\"") + filename := `"` + id + "-part-" + part + `"` + contentDisposition := "attachment" message, _ := apiv1.config.Storage.Load(id) contentTransferEncoding := "" @@ -212,9 +214,13 @@ func (apiv1 *APIv1) download_part(w http.ResponseWriter, req *http.Request) { for h, l := range message.MIME.Parts[pid].Headers { for _, v := range l { switch strings.ToLower(h) { + case "content-type": + re := regexp.MustCompile(`name=([^\s;"]+|"[^"]+")`) + if matches := re.FindStringSubmatch(v); matches != nil { + filename = matches[1] + } case "content-disposition": - // Prevent duplicate "content-disposition" - w.Header().Set(h, v) + contentDisposition = v case "content-transfer-encoding": if contentTransferEncoding == "" { contentTransferEncoding = v @@ -225,6 +231,12 @@ func (apiv1 *APIv1) download_part(w http.ResponseWriter, req *http.Request) { } } } + + if !strings.Contains(contentDisposition, "filename") { + contentDisposition += "; filename=" + filename + } + w.Header().Set("Content-Disposition", contentDisposition) + body := []byte(message.MIME.Parts[pid].Body) if strings.ToLower(contentTransferEncoding) == "base64" { var e error From 9a2f2caf04682d2f76ccd0e61b3bff1f764f37f0 Mon Sep 17 00:00:00 2001 From: GREsau Date: Sun, 4 Jun 2017 10:44:55 +0100 Subject: [PATCH 2/3] Allow filenames to contain escaped characters --- api/v1.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/v1.go b/api/v1.go index 78f9e66..6684b55 100644 --- a/api/v1.go +++ b/api/v1.go @@ -215,7 +215,7 @@ func (apiv1 *APIv1) download_part(w http.ResponseWriter, req *http.Request) { for _, v := range l { switch strings.ToLower(h) { case "content-type": - re := regexp.MustCompile(`name=([^\s;"]+|"[^"]+")`) + re := regexp.MustCompile(`name=([^\s;"]+|"(?:[^"\\]|\\.)*")`) if matches := re.FindStringSubmatch(v); matches != nil { filename = matches[1] } From 69f2d60e27a5b652c01ceacea221a2847a139db2 Mon Sep 17 00:00:00 2001 From: GREsau Date: Sun, 4 Jun 2017 11:30:25 +0100 Subject: [PATCH 3/3] Pass part's Content-Type through when downloading This header was previously being accidentally removed from the response. --- api/v1.go | 1 + 1 file changed, 1 insertion(+) diff --git a/api/v1.go b/api/v1.go index 6684b55..5428af8 100644 --- a/api/v1.go +++ b/api/v1.go @@ -219,6 +219,7 @@ func (apiv1 *APIv1) download_part(w http.ResponseWriter, req *http.Request) { if matches := re.FindStringSubmatch(v); matches != nil { filename = matches[1] } + w.Header().Add(h, v) case "content-disposition": contentDisposition = v case "content-transfer-encoding":