1 /*        $NetBSD: content-bozo.c,v 1.22 2023/06/07 20:12:31 mrg Exp $          */
2 
3 /*        $eterna: content-bozo.c,v 1.17 2011/11/18 09:21:15 mrg Exp $          */
4 
5 /*
6  * Copyright (c) 1997-2023 Matthew R. Green
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer and
16  *    dedication in the documentation and/or other materials provided
17  *    with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  */
32 
33 /* this code implements content-type handling for bozohttpd */
34 
35 #include <sys/param.h>
36 
37 #include <errno.h>
38 #include <string.h>
39 
40 #include "bozohttpd.h"
41 
42 /*
43  * this map and the functions below map between filenames and the
44  * content type and content encoding definitions.  this should become
45  * a configuration file, perhaps like apache's mime.types (but that
46  * has less info per-entry).
47  */
48 
49 static bozo_content_map_t static_content_map[] = {
50           { ".html",          "text/html",                            "",                 "", NULL },
51           { ".htm", "text/html",                            "",                 "", NULL },
52           { ".gif", "image/gif",                            "",                 "", NULL },
53           { ".jpeg",          "image/jpeg",                           "",                 "", NULL },
54           { ".jpg", "image/jpeg",                           "",                 "", NULL },
55           { ".jpe", "image/jpeg",                           "",                 "", NULL },
56           { ".png", "image/png",                            "",                 "", NULL },
57           { ".mp3", "audio/mpeg",                           "",                 "", NULL },
58           { ".css", "text/css",                             "",                 "", NULL },
59           { ".txt", "text/plain",                           "",                 "", NULL },
60           { ".swf", "application/x-shockwave-flash","",               "", NULL },
61           { ".dcr", "application/x-director",     "",                 "", NULL },
62           { ".pac", "application/x-ns-proxy-autoconfig", "",          "", NULL },
63           { ".pa",  "application/x-ns-proxy-autoconfig", "",          "", NULL },
64           { ".tar", "multipart/x-tar",            "",                 "", NULL },
65           { ".gtar",          "application/x-gtar-compressed", "",              "", NULL },
66           { ".tar.gz",        "application/x-gtar-compressed", "",              "", NULL },
67           { ".tar.bz2",       "application/x-gtar-compressed", "",              "", NULL },
68           { ".tar.xz",        "application/x-gtar-compressed", "",              "", NULL },
69           { ".tar.lz",        "application/x-gtar-compressed", "",              "", NULL },
70           { ".tar.zst",       "application/x-gtar-compressed", "",              "", NULL },
71           { ".tgz", "application/x-gtar-compressed", "",              "", NULL },
72           { ".tbz2",          "application/x-gtar-compressed", "",              "", NULL },
73           { ".txz", "application/x-gtar-compressed", "",              "", NULL },
74           { ".tlz", "application/x-gtar-compressed", "",              "", NULL },
75           { ".tar.z",         "application/x-gtar-compressed", "",              "", NULL },
76           { ".tar.Z",         "application/x-gtar-compressed", "",              "", NULL },
77           { ".taz", "application/x-gtar-compressed", "",              "", NULL },
78           { ".Z",             "application/x-compress",     "",                 "", NULL },
79           { ".gz",  "application/x-gzip",                   "",                 "", NULL },
80           { ".z",             "unknown",                              "",                 "", NULL },
81           { ".bz2", "application/x-bzip2",                  "",                 "", NULL },
82           { ".ogg", "application/x-ogg",                    "",                 "", NULL },
83           { ".mkv", "video/x-matroska",           "",                 "", NULL },
84           { ".xbel",          "text/xml",                             "",                 "", NULL },
85           { ".xml", "text/xml",                             "",                 "", NULL },
86           { ".xsl", "text/xml",                             "",                 "", NULL },
87           { ".hqx", "application/mac-binhex40",   "",                 "", NULL },
88           { ".cpt", "application/mac-compactpro", "",                 "", NULL },
89           { ".doc", "application/msword",                   "",                 "", NULL },
90           { ".bin", "application/octet-stream",   "",                 "", NULL },
91           { ".dms", "application/octet-stream",   "",                 "", NULL },
92           { ".lha", "application/octet-stream",   "",                 "", NULL },
93           { ".lzh", "application/octet-stream",   "",                 "", NULL },
94           { ".exe", "application/octet-stream",   "",                 "", NULL },
95           { ".class",         "application/octet-stream",   "",                 "", NULL },
96           { ".oda", "application/oda",            "",                 "", NULL },
97           { ".pdf", "application/pdf",            "",                 "", NULL },
98           { ".ai",  "application/postscript",     "",                 "", NULL },
99           { ".eps", "application/postscript",     "",                 "", NULL },
100           { ".ps",  "application/postscript",     "",                 "", NULL },
101           { ".ppt", "application/powerpoint",     "",                 "", NULL },
102           { ".rtf", "application/rtf",            "",                 "", NULL },
103           { ".bcpio",         "application/x-bcpio",                  "",                 "", NULL },
104           { ".torrent",       "application/x-bittorrent",   "",                 "", NULL },
105           { ".vcd", "application/x-cdlink",                 "",                 "", NULL },
106           { ".cpio",          "application/x-cpio",                   "",                 "", NULL },
107           { ".csh", "application/x-csh",                    "",                 "", NULL },
108           { ".dir", "application/x-director",     "",                 "", NULL },
109           { ".dxr", "application/x-director",     "",                 "", NULL },
110           { ".dvi", "application/x-dvi",                    "",                 "", NULL },
111           { ".hdf", "application/x-hdf",                    "",                 "", NULL },
112           { ".cgi", "application/x-httpd-cgi",    "",                 "", NULL },
113           { ".skp", "application/x-koan",                   "",                 "", NULL },
114           { ".skd", "application/x-koan",                   "",                 "", NULL },
115           { ".skt", "application/x-koan",                   "",                 "", NULL },
116           { ".skm", "application/x-koan",                   "",                 "", NULL },
117           { ".latex",         "application/x-latex",                  "",                 "", NULL },
118           { ".mif", "application/x-mif",                    "",                 "", NULL },
119           { ".nc",  "application/x-netcdf",                 "",                 "", NULL },
120           { ".cdf", "application/x-netcdf",                 "",                 "", NULL },
121           { ".patch",         "application/x-patch",                  "",                 "", NULL },
122           { ".sh",  "application/x-sh",           "",                 "", NULL },
123           { ".shar",          "application/x-shar",                   "",                 "", NULL },
124           { ".sit", "application/x-stuffit",      "",                 "", NULL },
125           { ".sv4cpio",       "application/x-sv4cpio",      "",                 "", NULL },
126           { ".sv4crc",        "application/x-sv4crc",                 "",                 "", NULL },
127           { ".tar", "application/x-tar",                    "",                 "", NULL },
128           { ".tcl", "application/x-tcl",                    "",                 "", NULL },
129           { ".tex", "application/x-tex",                    "",                 "", NULL },
130           { ".texinfo",       "application/x-texinfo",      "",                 "", NULL },
131           { ".texi",          "application/x-texinfo",      "",                 "", NULL },
132           { ".t",             "application/x-troff",                  "",                 "", NULL },
133           { ".tr",  "application/x-troff",                  "",                 "", NULL },
134           { ".roff",          "application/x-troff",                  "",                 "", NULL },
135           { ".man", "application/x-troff-man",    "",                 "", NULL },
136           { ".me",  "application/x-troff-me",     "",                 "", NULL },
137           { ".ms",  "application/x-troff-ms",     "",                 "", NULL },
138           { ".ustar",         "application/x-ustar",                  "",                 "", NULL },
139           { ".src", "application/x-wais-source",  "",                 "", NULL },
140           { ".zip", "application/zip",            "",                 "", NULL },
141           { ".zipx",          "application/zip",            "",                 "", NULL },
142           { ".xz",  "application/x-xz",           "",                 "", NULL },
143           { ".zst", "application/x-zstd",                   "",                 "", NULL },
144           { ".sz",  "application/x-snappy-framed",          "",                 "", NULL },
145           { ".lz",  "application/x-lzip",                   "",                 "", NULL },
146           { ".lzma",          "application/x-lzma",                   "",                 "", NULL },
147           { ".lzo", "application/x-lzop",                   "",                 "", NULL },
148           { ".7z",  "application/x-7z-compressed",          "",                 "", NULL },
149           { ".lzo", "application/x-lzop",                   "",                 "", NULL },
150           { ".cab", "application/vnd.ms-cab-compressed", "",          "", NULL },
151           { ".dmg", "application/x-apple-diskimage","",               "", NULL },
152           { ".jar", "application/java-archive",   "",                 "", NULL },
153           { ".rar", "application/x-rar-compressed",         "",                 "", NULL },
154           { ".au",  "audio/basic",                          "",                 "", NULL },
155           { ".snd", "audio/basic",                          "",                 "", NULL },
156           { ".mpga",          "audio/mpeg",                           "",                 "", NULL },
157           { ".mp2", "audio/mpeg",                           "",                 "", NULL },
158           { ".m4a", "audio/mp4",                            "",                 "", NULL },
159           { ".aif", "audio/x-aiff",                         "",                 "", NULL },
160           { ".aiff",          "audio/x-aiff",                         "",                 "", NULL },
161           { ".aifc",          "audio/x-aiff",                         "",                 "", NULL },
162           { ".ram", "audio/x-pn-realaudio",                 "",                 "", NULL },
163           { ".rpm", "audio/x-pn-realaudio-plugin",          "",                 "", NULL },
164           { ".ra",  "audio/x-realaudio",                    "",                 "", NULL },
165           { ".wav", "audio/x-wav",                          "",                 "", NULL },
166           { ".pdb", "chemical/x-pdb",             "",                 "", NULL },
167           { ".xyz", "chemical/x-pdb",             "",                 "", NULL },
168           { ".ief", "image/ief",                            "",                 "", NULL },
169           { ".tiff",          "image/tiff",                           "",                 "", NULL },
170           { ".tif", "image/tiff",                           "",                 "", NULL },
171           { ".ras", "image/x-cmu-raster",                   "",                 "", NULL },
172           { ".pnm", "image/x-portable-anymap",    "",                 "", NULL },
173           { ".pbm", "image/x-portable-bitmap",    "",                 "", NULL },
174           { ".pgm", "image/x-portable-graymap",   "",                 "", NULL },
175           { ".ppm", "image/x-portable-pixmap",    "",                 "", NULL },
176           { ".rgb", "image/x-rgb",                          "",                 "", NULL },
177           { ".xbm", "image/x-xbitmap",            "",                 "", NULL },
178           { ".xpm", "image/x-xpixmap",            "",                 "", NULL },
179           { ".xwd", "image/x-xwindowdump",                  "",                 "", NULL },
180           { ".rtx", "text/richtext",              "",                 "", NULL },
181           { ".tsv", "text/tab-separated-values",  "",                 "", NULL },
182           { ".etx", "text/x-setext",              "",                 "", NULL },
183           { ".sgml",          "text/x-sgml",                          "",                 "", NULL },
184           { ".sgm", "text/x-sgml",                          "",                 "", NULL },
185           { ".mpeg",          "video/mpeg",                           "",                 "", NULL },
186           { ".mpg", "video/mpeg",                           "",                 "", NULL },
187           { ".mpe", "video/mpeg",                           "",                 "", NULL },
188           { ".ts",  "video/mpeg",                           "",                 "", NULL },
189           { ".vob", "video/mpeg",                           "",                 "", NULL },
190           { ".mp4", "video/mp4",                            "",                 "", NULL },
191           { ".m4v", "video/mp4",                            "",                 "", NULL },
192           { ".qt",  "video/quicktime",            "",                 "", NULL },
193           { ".mov", "video/quicktime",            "",                 "", NULL },
194           { ".avi", "video/x-msvideo",            "",                 "", NULL },
195           { ".movie",         "video/x-sgi-movie",                    "",                 "", NULL },
196           { ".ice", "x-conference/x-cooltalk",    "",                 "", NULL },
197           { ".wrl", "x-world/x-vrml",             "",                 "", NULL },
198           { ".vrml",          "x-world/x-vrml",             "",                 "", NULL },
199           { ".svg", "image/svg+xml",              "",                 "", NULL },
200           { ".mobi",          "application/x-mobipocket-ebook", "",             "", NULL },
201           { ".iso", "application/octet-stream",   "",                 "", NULL },
202           { NULL,             NULL,               NULL,               NULL, NULL }
203 };
204 
205 static bozo_content_map_t *
search_map(bozo_content_map_t * map,const char * name,size_t len)206 search_map(bozo_content_map_t *map, const char *name, size_t len)
207 {
208 
209           for ( ; map && map->name; map++) {
210                     const size_t namelen = strlen(map->name);
211 
212                     if (namelen < len &&
213                         strcasecmp(map->name, name + (len - namelen)) == 0)
214                               return map;
215           }
216           return NULL;
217 }
218 
219 /* match a suffix on a file - dynamiconly means no static content search */
220 bozo_content_map_t *
bozo_match_content_map(bozohttpd_t * httpd,const char * name,const int dynamiconly)221 bozo_match_content_map(bozohttpd_t *httpd, const char *name,
222                            const int dynamiconly)
223 {
224           bozo_content_map_t  *map;
225           size_t                         len;
226 
227           len = strlen(name);
228           map = search_map(httpd->dynamic_content_map, name, len);
229           if (map == NULL && !dynamiconly)
230                     map = search_map(static_content_map, name, len);
231 
232           return map;
233 }
234 
235 /*
236  * given the file name, return a valid Content-Type: value.
237  */
238 const char *
bozo_content_type(bozo_httpreq_t * request,const char * file)239 bozo_content_type(bozo_httpreq_t *request, const char *file)
240 {
241           bozohttpd_t *httpd = request->hr_httpd;
242           bozo_content_map_t  *map;
243 
244           map = bozo_match_content_map(httpd, file, 0);
245           if (map)
246                     return map->type;
247           return httpd->consts.text_plain;
248 }
249 
250 /*
251  * given the file name, return a valid Content-Encoding: value.
252  */
253 const char *
bozo_content_encoding(bozo_httpreq_t * request,const char * file)254 bozo_content_encoding(bozo_httpreq_t *request, const char *file)
255 {
256           bozohttpd_t *httpd = request->hr_httpd;
257           bozo_content_map_t  *map;
258 
259           map = bozo_match_content_map(httpd, file, 0);
260           if (map)
261                     return (request->hr_proto == httpd->consts.http_11) ?
262                         map->encoding11 : map->encoding;
263           return NULL;
264 }
265 
266 #ifndef NO_DYNAMIC_CONTENT
267 
268 bozo_content_map_t *
bozo_get_content_map(bozohttpd_t * httpd,const char * name)269 bozo_get_content_map(bozohttpd_t *httpd, const char *name)
270 {
271           bozo_content_map_t  *map;
272 
273           if ((map = bozo_match_content_map(httpd, name, 1)) != NULL)
274                     return map;
275 
276           httpd->dynamic_content_map_size++;
277           httpd->dynamic_content_map = bozorealloc(httpd,
278                     httpd->dynamic_content_map,
279                     (httpd->dynamic_content_map_size + 1) * sizeof *map);
280           if (httpd->dynamic_content_map == NULL)
281                     bozoerr(httpd, 1, "out of memory allocating content map");
282           map = &httpd->dynamic_content_map[httpd->dynamic_content_map_size];
283           map->name = map->type = map->encoding = map->encoding11 =
284                     map->cgihandler = NULL;
285           map--;
286 
287           return map;
288 }
289 
290 /*
291  * mime content maps look like:
292  *        ".name type encoding encoding11"
293  * where any of type, encoding or encoding11 a dash "-" means "".
294  * eg the .gtar, .tar.Z from above  could be written like:
295  *        ".gtar multipart/x-gtar - -"
296  *        ".tar.Z multipart/x-tar x-compress compress"
297  * or
298  *        ".gtar multipart/x-gtar"
299  *        ".tar.Z multipart/x-tar x-compress compress"
300  * NOTE: we destroy 'arg'
301  */
302 void
bozo_add_content_map_mime(bozohttpd_t * httpd,const char * cmap0,const char * cmap1,const char * cmap2,const char * cmap3)303 bozo_add_content_map_mime(bozohttpd_t *httpd, const char *cmap0,
304                     const char *cmap1, const char *cmap2, const char *cmap3)
305 {
306           bozo_content_map_t *map;
307 
308           debug((httpd, DEBUG_FAT,
309                     "add_content_map: name %s type %s enc %s enc11 %s ",
310                     cmap0, cmap1, cmap2, cmap3));
311 
312           map = bozo_get_content_map(httpd, cmap0);
313 #define CHECKMAP(s) (!s || ((s)[0] == '-' && (s)[1] == '\0') ? "" : (s))
314           map->name = CHECKMAP(cmap0);
315           map->type = CHECKMAP(cmap1);
316           map->encoding = CHECKMAP(cmap2);
317           map->encoding11 = CHECKMAP(cmap3);
318 #undef CHECKMAP
319           map->cgihandler = NULL;
320 }
321 #endif /* NO_DYNAMIC_CONTENT */
322