blob: 39bf40ee89aa99e16f2e3e1c0a8d3f9e8e6589bd [file] [log] [blame]
Michael Hoisie0cba5fc2010-02-09 20:47:45 -08001// 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
Nigel Tao6a186d32011-04-20 09:57:05 +10005// Package mime implements parts of the MIME spec.
Michael Hoisie0cba5fc2010-02-09 20:47:45 -08006package mime
7
8import (
9 "bufio"
Pascal S. de Kloebd3627c2011-08-26 16:55:25 -040010 "fmt"
Michael Hoisie0cba5fc2010-02-09 20:47:45 -080011 "os"
12 "strings"
Yuusei Kuwanac21e2f32010-07-29 14:12:04 -070013 "sync"
Michael Hoisie0cba5fc2010-02-09 20:47:45 -080014)
15
16var typeFiles = []string{
17 "/etc/mime.types",
18 "/etc/apache2/mime.types",
19 "/etc/apache/mime.types",
20}
21
22var mimeTypes = map[string]string{
Russ Cox91643ac2011-08-26 17:19:52 -040023 ".css": "text/css; charset=utf-8",
Robert Griesemerf44fa9b2010-03-02 13:46:51 -080024 ".gif": "image/gif",
Russ Cox91643ac2011-08-26 17:19:52 -040025 ".htm": "text/html; charset=utf-8",
26 ".html": "text/html; charset=utf-8",
Robert Griesemerf44fa9b2010-03-02 13:46:51 -080027 ".jpg": "image/jpeg",
28 ".js": "application/x-javascript",
29 ".pdf": "application/pdf",
30 ".png": "image/png",
Russ Cox91643ac2011-08-26 17:19:52 -040031 ".xml": "text/xml; charset=utf-8",
Michael Hoisie0cba5fc2010-02-09 20:47:45 -080032}
33
Yuusei Kuwanac21e2f32010-07-29 14:12:04 -070034var mimeLock sync.RWMutex
35
Michael Hoisie0cba5fc2010-02-09 20:47:45 -080036func loadMimeFile(filename string) {
Rob Pike8a90fd32011-04-04 23:42:14 -070037 f, err := os.Open(filename)
Michael Hoisie0cba5fc2010-02-09 20:47:45 -080038 if err != nil {
39 return
40 }
41
42 reader := bufio.NewReader(f)
43 for {
44 line, err := reader.ReadString('\n')
45 if err != nil {
46 f.Close()
47 return
48 }
49 fields := strings.Fields(line)
50 if len(fields) <= 1 || fields[0][0] == '#' {
51 continue
52 }
Pascal S. de Kloebd3627c2011-08-26 16:55:25 -040053 mimeType := fields[0]
Michael Hoisie0cba5fc2010-02-09 20:47:45 -080054 for _, ext := range fields[1:] {
55 if ext[0] == '#' {
56 break
57 }
Pascal S. de Kloebd3627c2011-08-26 16:55:25 -040058 setExtensionType("."+ext, mimeType)
Michael Hoisie0cba5fc2010-02-09 20:47:45 -080059 }
60 }
61}
62
63func initMime() {
64 for _, filename := range typeFiles {
65 loadMimeFile(filename)
66 }
67}
68
Rob Pikec78be462010-08-06 06:14:41 +100069var once sync.Once
70
Michael Hoisie0cba5fc2010-02-09 20:47:45 -080071// TypeByExtension returns the MIME type associated with the file extension ext.
72// The extension ext should begin with a leading dot, as in ".html".
73// When ext has no associated type, TypeByExtension returns "".
Brad Fitzpatrick9b64fef2010-07-14 17:26:14 -070074//
75// The built-in table is small but is is augmented by the local
76// system's mime.types file(s) if available under one or more of these
77// names:
78//
79// /etc/mime.types
80// /etc/apache2/mime.types
81// /etc/apache/mime.types
Pascal S. de Kloebd3627c2011-08-26 16:55:25 -040082//
83// Text types have the charset parameter set to "utf-8" by default.
Michael Hoisie0cba5fc2010-02-09 20:47:45 -080084func TypeByExtension(ext string) string {
85 once.Do(initMime)
Yuusei Kuwanac21e2f32010-07-29 14:12:04 -070086 mimeLock.RLock()
87 typename := mimeTypes[ext]
88 mimeLock.RUnlock()
89 return typename
90}
91
92// AddExtensionType sets the MIME type associated with
93// the extension ext to typ. The extension should begin with
94// a leading dot, as in ".html".
95func AddExtensionType(ext, typ string) os.Error {
Pascal S. de Kloebd3627c2011-08-26 16:55:25 -040096 if ext == "" || ext[0] != '.' {
97 return fmt.Errorf(`mime: extension "%s" misses dot`, ext)
Yuusei Kuwanac21e2f32010-07-29 14:12:04 -070098 }
Pascal S. de Kloebd3627c2011-08-26 16:55:25 -040099 once.Do(initMime)
100 return setExtensionType(ext, typ)
101}
102
103func setExtensionType(extension, mimeType string) os.Error {
104 full, param, err := ParseMediaType(mimeType)
105 if err != nil {
106 return err
107 }
108 if split := strings.Index(full, "/"); split < 0 {
109 return fmt.Errorf(`mime: malformed MIME type "%s"`, mimeType)
110 } else {
111 main := full[:split]
112 sub := full[split+1:]
113 if main == "text" && param["charset"] == "" {
114 param["charset"] = "utf-8"
115 }
116 mimeType = FormatMediaType(main, sub, param)
117 }
118
Yuusei Kuwanac21e2f32010-07-29 14:12:04 -0700119 mimeLock.Lock()
Pascal S. de Kloebd3627c2011-08-26 16:55:25 -0400120 mimeTypes[extension] = mimeType
Yuusei Kuwanac21e2f32010-07-29 14:12:04 -0700121 mimeLock.Unlock()
122 return nil
Michael Hoisie0cba5fc2010-02-09 20:47:45 -0800123}