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