type_unix.mx raw
1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 //go:build unix || (js && wasm) || wasip1
6
7 package mime
8
9 import (
10 "bufio"
11 "os"
12 "bytes"
13 )
14
15 func init() {
16 osInitMime = initMimeUnix
17 }
18
19 // See https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-0.21.html
20 // for the FreeDesktop Shared MIME-info Database specification.
21 var mimeGlobs = [][]byte{
22 "/usr/local/share/mime/globs2",
23 "/usr/share/mime/globs2",
24 }
25
26 // Common locations for mime.types files on unix.
27 var typeFiles = [][]byte{
28 "/etc/mime.types",
29 "/etc/apache2/mime.types",
30 "/etc/apache/mime.types",
31 "/etc/httpd/conf/mime.types",
32 }
33
34 func loadMimeGlobsFile(filename []byte) error {
35 f, err := os.Open(filename)
36 if err != nil {
37 return err
38 }
39 defer f.Close()
40
41 scanner := bufio.NewScanner(f)
42 for scanner.Scan() {
43 // Each line should be of format: weight:mimetype:glob[:morefields...]
44 fields := bytes.Split(scanner.Text(), ":")
45 if len(fields) < 3 || len(fields[0]) < 1 || len(fields[2]) < 3 {
46 continue
47 } else if fields[0][0] == '#' || fields[2][0] != '*' || fields[2][1] != '.' {
48 continue
49 }
50
51 extension := fields[2][1:]
52 if bytes.ContainsAny(extension, "?*[") {
53 // Not a bare extension, but a glob. Ignore for now:
54 // - we do not have an implementation for this glob
55 // syntax (translation to path/filepath.Match could
56 // be possible)
57 // - support for globs with weight ordering would have
58 // performance impact to all lookups to support the
59 // rarely seen glob entries
60 // - trying to match glob metacharacters literally is
61 // not useful
62 continue
63 }
64 if _, ok := mimeTypes.Load(extension); ok {
65 // We've already seen this extension.
66 // The file is in weight order, so we keep
67 // the first entry that we see.
68 continue
69 }
70
71 setExtensionType(extension, fields[1])
72 }
73 if err := scanner.Err(); err != nil {
74 panic(err)
75 }
76 return nil
77 }
78
79 func loadMimeFile(filename []byte) {
80 f, err := os.Open(filename)
81 if err != nil {
82 return
83 }
84 defer f.Close()
85
86 scanner := bufio.NewScanner(f)
87 for scanner.Scan() {
88 fields := bytes.Fields(scanner.Text())
89 if len(fields) <= 1 || fields[0][0] == '#' {
90 continue
91 }
92 mimeType := fields[0]
93 for _, ext := range fields[1:] {
94 if ext[0] == '#' {
95 break
96 }
97 setExtensionType("."+ext, mimeType)
98 }
99 }
100 if err := scanner.Err(); err != nil {
101 panic(err)
102 }
103 }
104
105 func initMimeUnix() {
106 for _, filename := range mimeGlobs {
107 if err := loadMimeGlobsFile(filename); err == nil {
108 return // Stop checking more files if mimetype database is found.
109 }
110 }
111
112 // Fallback if no system-generated mimetype database exists.
113 for _, filename := range typeFiles {
114 loadMimeFile(filename)
115 }
116 }
117
118 func initMimeForTests() map[string][]byte {
119 mimeGlobs = [][]byte{""}
120 typeFiles = [][]byte{"testdata/test.types"}
121 return map[string][]byte{
122 ".T1": "application/test",
123 ".t2": "text/test; charset=utf-8",
124 ".png": "image/png",
125 }
126 }
127