Skip to content
This repository was archived by the owner on Nov 25, 2024. It is now read-only.

Commit 185ad6b

Browse files
authored
Allow some content types to be inlined (#3274)
"Shamelessly" stolen from matrix-org/synapse#15988
1 parent fd11e65 commit 185ad6b

File tree

2 files changed

+62
-5
lines changed

2 files changed

+62
-5
lines changed

mediaapi/routing/download.go

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,40 @@ type downloadRequest struct {
6363
DownloadFilename string
6464
}
6565

66+
// Taken from: https://github.com/matrix-org/synapse/blob/c3627d0f99ed5a23479305dc2bd0e71ca25ce2b1/synapse/media/_base.py#L53C1-L84
67+
// A list of all content types that are "safe" to be rendered inline in a browser.
68+
var allowInlineTypes = map[types.ContentType]struct{}{
69+
"text/css": {},
70+
"text/plain": {},
71+
"text/csv": {},
72+
"application/json": {},
73+
"application/ld+json": {},
74+
// We allow some media files deemed as safe, which comes from the matrix-react-sdk.
75+
// https://github.com/matrix-org/matrix-react-sdk/blob/a70fcfd0bcf7f8c85986da18001ea11597989a7c/src/utils/blobs.ts#L51
76+
// SVGs are *intentionally* omitted.
77+
"image/jpeg": {},
78+
"image/gif": {},
79+
"image/png": {},
80+
"image/apng": {},
81+
"image/webp": {},
82+
"image/avif": {},
83+
"video/mp4": {},
84+
"video/webm": {},
85+
"video/ogg": {},
86+
"video/quicktime": {},
87+
"audio/mp4": {},
88+
"audio/webm": {},
89+
"audio/aac": {},
90+
"audio/mpeg": {},
91+
"audio/ogg": {},
92+
"audio/wave": {},
93+
"audio/wav": {},
94+
"audio/x-wav": {},
95+
"audio/x-pn-wav": {},
96+
"audio/flac": {},
97+
"audio/x-flac": {},
98+
}
99+
66100
// Download implements GET /download and GET /thumbnail
67101
// Files from this server (i.e. origin == cfg.ServerName) are served directly
68102
// Files from remote servers (i.e. origin != cfg.ServerName) are cached locally.
@@ -353,7 +387,7 @@ func (r *downloadRequest) addDownloadFilenameToHeaders(
353387
}
354388

355389
if len(filename) == 0 {
356-
w.Header().Set("Content-Disposition", "attachment")
390+
w.Header().Set("Content-Disposition", contentDispositionFor(""))
357391
return nil
358392
}
359393

@@ -383,20 +417,21 @@ func (r *downloadRequest) addDownloadFilenameToHeaders(
383417
unescaped = strings.ReplaceAll(unescaped, `\`, `\\"`)
384418
unescaped = strings.ReplaceAll(unescaped, `"`, `\"`)
385419

420+
disposition := contentDispositionFor(responseMetadata.ContentType)
386421
if isASCII {
387422
// For ASCII filenames, we should only quote the filename if
388423
// it needs to be done, e.g. it contains a space or a character
389424
// that would otherwise be parsed as a control character in the
390425
// Content-Disposition header
391426
w.Header().Set("Content-Disposition", fmt.Sprintf(
392-
`attachment; filename=%s%s%s`,
393-
quote, unescaped, quote,
427+
`%s; filename=%s%s%s`,
428+
disposition, quote, unescaped, quote,
394429
))
395430
} else {
396431
// For UTF-8 filenames, we quote always, as that's the standard
397432
w.Header().Set("Content-Disposition", fmt.Sprintf(
398-
`attachment; filename*=utf-8''%s`,
399-
url.QueryEscape(unescaped),
433+
`%s; filename*=utf-8''%s`,
434+
disposition, url.QueryEscape(unescaped),
400435
))
401436
}
402437

@@ -808,3 +843,12 @@ func (r *downloadRequest) fetchRemoteFile(
808843

809844
return types.Path(finalPath), duplicate, nil
810845
}
846+
847+
// contentDispositionFor returns the Content-Disposition for a given
848+
// content type.
849+
func contentDispositionFor(contentType types.ContentType) string {
850+
if _, ok := allowInlineTypes[contentType]; ok {
851+
return "inline"
852+
}
853+
return "attachment"
854+
}

mediaapi/routing/download_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package routing
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func Test_dispositionFor(t *testing.T) {
10+
assert.Equal(t, "attachment", contentDispositionFor(""), "empty content type")
11+
assert.Equal(t, "attachment", contentDispositionFor("image/svg"), "image/svg")
12+
assert.Equal(t, "inline", contentDispositionFor("image/jpeg"), "image/jpg")
13+
}

0 commit comments

Comments
 (0)