raw.go
1471 bytes
1package pages
2
3import (
4 "mime"
5 "path"
6 "strings"
7 "unicode"
8)
9
10// RawMetadata describes how a raw blob may be sent without executable content.
11type RawMetadata struct {
12 ContentType string
13 ContentDisposition string
14 Inline bool
15}
16
17var safeMediaTypes = map[string]string{
18 ".avif": "image/avif",
19 ".gif": "image/gif",
20 ".jpeg": "image/jpeg",
21 ".jpg": "image/jpeg",
22 ".png": "image/png",
23 ".webp": "image/webp",
24 ".flac": "audio/flac",
25 ".m4a": "audio/mp4",
26 ".mp3": "audio/mpeg",
27 ".ogg": "audio/ogg",
28 ".opus": "audio/opus",
29 ".wav": "audio/wav",
30 ".m4v": "video/mp4",
31 ".mp4": "video/mp4",
32 ".ogv": "video/ogg",
33 ".webm": "video/webm",
34}
35
36// RawFileMetadata allows only known safe media types inline; all other files download.
37func RawFileMetadata(filename string) RawMetadata {
38 if mediaType, ok := safeMediaTypes[strings.ToLower(path.Ext(filename))]; ok {
39 return RawMetadata{ContentType: mediaType, Inline: true}
40 }
41 return RawMetadata{
42 ContentType: "application/octet-stream",
43 ContentDisposition: mime.FormatMediaType("attachment", map[string]string{"filename": safeDownloadFilename(filename)}),
44 }
45}
46
47func safeDownloadFilename(filename string) string {
48 filename = path.Base(filename)
49 filename = strings.Map(func(char rune) rune {
50 if char == '/' || char == '\\' || unicode.IsControl(char) {
51 return -1
52 }
53 return char
54 }, filename)
55 if filename == "" || filename == "." {
56 return "download"
57 }
58 return filename
59}