1#!/usr/bin/perl -w
2
3# Based on create-mime.assign.pl in debian lighttpd (1.4.x) package
4# Creates an example mime.conf from /etc/mime.types
5
6use strict;
7
8# text/* subtypes to serve as "text/...; charset=utf-8"
9# text/html IS NOT INCLUDED: html has its own method for defining charset
10#   (<meta>), but the standards specify that content-type in HTTP wins over
11#   the setting in the html document.
12# text/markdown doesn't have an official default charset, but requires
13#   one being specified - it seems reasonable to hardcode it to UTF-8
14my %text_utf8 = map { $_ => 1 } qw(
15	css
16	csv
17	markdown
18	plain
19	x-bibtex
20	x-boo
21	x-c++hdr
22	x-c++src
23	x-chdr
24	x-csh
25	x-csrc
26	x-dsrc
27	x-diff
28	x-haskell
29	x-java
30	x-lilypond
31	x-literate-haskell
32	x-makefile
33	x-moc
34	x-pascal
35	x-perl
36	x-python
37	x-scala
38	x-sh
39	x-tcl
40	x-tex
41);
42
43# map extension to hash which maps types to the type they should be replaced with
44my %manual_conflicts_resolve = (
45	'.ra' => {
46		'audio/x-pn-realaudio' => 'audio/x-realaudio',
47	},
48	# use font media types from iana registry
49	'.otf' => {
50		'application/font-sfnt' => 'font/ttf',
51		'font/sfnt' => 'font/ttf',
52		'font/ttf' => 'font/ttf',
53	},
54	'.ttf' => {
55		'application/font-sfnt' => 'font/ttf',
56		'font/otf' => 'font/ttf',
57		'font/sfnt' => 'font/ttf',
58	},
59	'.woff' => {
60		'application/font-woff' => 'font/woff',
61	},
62	# end of fonts
63	'.asn' => {
64		'chemical/x-ncbi-asn1-spec' => 'application/octet-stream',
65	},
66	'.ent' => {
67		'chemical/x-ncbi-asn1-ascii' => 'application/octet-stream',
68	},
69);
70
71open MIMETYPES, "/etc/mime.types" or die "Can't open /etc/mime.types: $!";
72
73my %extensions;
74sub set {
75	my ($extension, $mimetype) = @_;
76	$extensions{$extension} = $mimetype;
77}
78sub add {
79	my ($extension, $mimetype) = @_;
80	my $have = $extensions{$extension};
81
82	my $r = $manual_conflicts_resolve{$extension};
83	# update @_ too for calls to set
84	$_[1] = $mimetype = $r->{$mimetype} if $r && $r->{$mimetype};
85
86	# mime.types can have same extension for different mime types
87	if ($have) {
88		# application/octet-stream means we couldn't resolve another conflict
89		return if $have eq $mimetype || $have eq 'application/octet-stream';
90
91		my ($have_type, $have_subtype) = split /\//, $have, 2;
92		my ($type, $subtype) = split /\//, $mimetype, 2;
93
94		my $have_x = ($have_type =~ /^x-/ || $have_subtype =~ /^x-/);
95		my $x = ($type =~ /^x-/ || $subtype =~ /^x-/);
96
97		# entries without x- prefix in type/subtype win:
98		if ($have_x && !$x) {
99			return set @_; # overwrite
100		} elsif ($x && !$have_x) {
101			return; # ignore
102		}
103
104		# text/ wins over application/ for same subtype
105		if ($subtype eq $have_subtype) {
106			if ($type eq "text" && $have_type eq "application") {
107				return set @_; # overwrite
108			} elsif ($have_type eq "text" && $type eq "application") {
109				return; # ignore
110			}
111		}
112
113		print STDERR "Duplicate mimetype: '${extension}' => '${mimetype}' (already have '${have}'), merging to 'application/octet-stream'\n";
114		set ($extension, 'application/octet-stream');
115	} else {
116		set @_;
117	}
118}
119
120sub print_type {
121	my ($extension, $mimetype) = @_;
122	if ($mimetype =~ /^text\/(.*)$/) {
123		$mimetype .= "; charset=utf-8" if $text_utf8{$1};
124	}
125
126	print "\t\"${extension}\" => \"${mimetype}\",\n";
127}
128
129while (<MIMETYPES>) {
130	chomp;
131	s/\#.*//;
132	next if /^\w*$/;
133	if (/^([a-z0-9\/+.-]+)\s+((?:[a-z0-9+.-]+[ ]?)+)$/i) {
134		my $mimetype = $1;
135		my @extensions = split / /, $2;
136
137		foreach my $ext (@extensions) {
138			add(".${ext}", $mimetype);
139		}
140	}
141}
142
143# missing in /etc/mime.types;
144# from http://www.iana.org/assignments/media-types/media-types.xhtml
145add(".dtd", "application/xml-dtd");
146
147# other useful mappings
148my %useful = (
149	".tar.gz"  => "application/x-gtar-compressed",
150	".gz"      => "application/x-gzip",
151	".tbz"     => "application/x-gtar-compressed",
152	".tar.bz2" => "application/x-gtar-compressed",
153	".bz2"     => "application/x-bzip",
154	".log"     => "text/plain",
155	".conf"    => "text/plain",
156	".spec"    => "text/plain",
157	"README"   => "text/plain",
158	"Makefile" => "text/x-makefile",
159);
160
161while (my ($ext, $mimetype) = each %useful) {
162	add($ext, $mimetype) unless $extensions{$ext};
163}
164
165
166print <<EOF;
167# created by create-mime.conf.pl
168
169#######################################################################
170##
171##  MimeType handling
172## -------------------
173##
174## https://redmine.lighttpd.net/projects/lighttpd/wiki/Mimetype_assignDetails
175
176##
177## mimetype.xattr-name
178## Set the extended file attribute name used to obtain mime type
179## (must also set mimetype.use-xattr = "enable")
180##
181## Default value is "Content-Type"
182##
183## freedesktop.org Shared MIME-info Database specification suggests
184## user-defined value ("user.mime_type") as name for extended file attribute
185#mimetype.xattr-name = "user.mime_type"
186
187##
188## Use extended attribute named in mimetype.xattr-name (default "Content-Type")
189## to obtain mime type if possible
190##
191## Disabled by default
192##
193#mimetype.use-xattr = "enable"
194
195##
196## mimetype ("Content-Type" HTTP header) mapping for static file handling
197##
198## The first matching suffix is used. If no mapping is found
199## 'application/octet-stream' is used, and caching (etag/last-modified handling)
200## is disabled to prevent clients from caching "unknown" mime types.
201##
202## Therefore the last mapping is:
203##   "" => "application/octet-stream"
204## This matches all extensions and acts as default mime type, and enables
205## caching for those.
206mimetype.assign = (
207EOF
208
209# sort "x-" and "vnd." prefixed names after everything else
210sub mimecmpvalue {
211	my ($mimetype) = @_;
212	$mimetype =~ s/(^|\/)(x-|vnd\.)/~$1$2/g;
213	return $mimetype;
214}
215sub countdots {
216	my ($s) = @_;
217	return scalar(() = $s =~ /\./g);
218}
219# the first matching suffix wins, so we have to sort by "length"
220# as all extensions start with "." we use the number of "."s as length
221# the exceptions are "README" and "Makefile" which are assumed not to conflict
222#  (i.e. are not a suffix of any other extension)
223for my $ext (sort { countdots($b) <=> countdots($a) || mimecmpvalue($extensions{$a}) cmp mimecmpvalue($extensions{$b}) || $a cmp $b } keys(%extensions)) {
224	print_type($ext, $extensions{$ext});
225}
226
227print <<EOF;
228
229	# enable caching for unknown mime types:
230	"" => "application/octet-stream"
231)
232EOF
233