]>
Commit | Line | Data |
---|---|---|
067bea91 | 1 | |
30a4f2a8 | 2 | /* |
1f7c9178 | 3 | * $Id: mime.cc,v 1.100 2001/04/14 00:03:23 hno Exp $ |
30a4f2a8 | 4 | * |
5 | * DEBUG: section 25 MIME Parsing | |
6 | * AUTHOR: Harvest Derived | |
7 | * | |
2b6662ba | 8 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 9 | * ---------------------------------------------------------- |
30a4f2a8 | 10 | * |
2b6662ba | 11 | * Squid is the result of efforts by numerous individuals from |
12 | * the Internet community; see the CONTRIBUTORS file for full | |
13 | * details. Many organizations have provided support for Squid's | |
14 | * development; see the SPONSORS file for full details. Squid is | |
15 | * Copyrighted (C) 2001 by the Regents of the University of | |
16 | * California; see the COPYRIGHT file for full details. Squid | |
17 | * incorporates software developed and/or copyrighted by other | |
18 | * sources; see the CREDITS file for full details. | |
30a4f2a8 | 19 | * |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
cbdec147 | 32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 33 | * |
6eb42cae | 34 | */ |
ed43818f | 35 | |
44a47c6e | 36 | #include "squid.h" |
090089c4 | 37 | |
09610784 | 38 | #define GET_HDR_SZ 1024 |
39 | ||
812ed90c | 40 | typedef struct _mime_entry { |
41 | char *pattern; | |
42 | regex_t compiled_pattern; | |
43 | char *icon; | |
44 | char *content_type; | |
45 | char *content_encoding; | |
46 | char transfer_mode; | |
0cdcddb9 | 47 | unsigned int view_option:1, download_option:1; |
812ed90c | 48 | struct _mime_entry *next; |
49 | } mimeEntry; | |
50 | ||
51 | static mimeEntry *MimeTable = NULL; | |
00fa6528 | 52 | static mimeEntry **MimeTableTail = &MimeTable; |
812ed90c | 53 | |
f5b8bbc4 | 54 | static void mimeLoadIconFile(const char *icon); |
365cb147 | 55 | |
4e41e277 | 56 | /* returns a pointer to a field-value of the first matching field-name */ |
b8d8561b | 57 | char * |
0ee4272b | 58 | mime_get_header(const char *mime, const char *name) |
4e41e277 | 59 | { |
60 | return mime_get_header_field(mime, name, NULL); | |
61 | } | |
62 | ||
63 | /* | |
64 | * returns a pointer to a field-value of the first matching field-name where | |
65 | * field-value matches prefix if any | |
66 | */ | |
67 | char * | |
68 | mime_get_header_field(const char *mime, const char *name, const char *prefix) | |
7f2e38dc | 69 | { |
95d659f0 | 70 | LOCAL_ARRAY(char, header, GET_HDR_SZ); |
0ee4272b | 71 | const char *p = NULL; |
6eb42cae | 72 | char *q = NULL; |
7f2e38dc | 73 | char got = 0; |
4e41e277 | 74 | const int namelen = name ? strlen(name) : 0; |
75 | const int preflen = prefix ? strlen(prefix) : 0; | |
e274cdf3 | 76 | int l; |
7f2e38dc | 77 | |
18c36ffc | 78 | if (NULL == mime) |
09610784 | 79 | return NULL; |
18c36ffc | 80 | assert(NULL != name); |
09610784 | 81 | |
a3d5953d | 82 | debug(25, 5) ("mime_get_header: looking for '%s'\n", name); |
6eb42cae | 83 | |
7f2e38dc | 84 | for (p = mime; *p; p += strcspn(p, "\n\r")) { |
85 | if (strcmp(p, "\r\n\r\n") == 0 || strcmp(p, "\n\n") == 0) | |
86 | return NULL; | |
b6a2f15e | 87 | while (xisspace(*p)) |
7f2e38dc | 88 | p++; |
6eb42cae | 89 | if (strncasecmp(p, name, namelen)) |
90 | continue; | |
b6a2f15e | 91 | if (!xisspace(p[namelen]) && p[namelen] != ':') |
6eb42cae | 92 | continue; |
e274cdf3 | 93 | l = strcspn(p, "\n\r") + 1; |
94 | if (l > GET_HDR_SZ) | |
b785f8ca | 95 | l = GET_HDR_SZ; |
e274cdf3 | 96 | xstrncpy(header, p, l); |
a3d5953d | 97 | debug(25, 5) ("mime_get_header: checking '%s'\n", header); |
6eb42cae | 98 | q = header; |
99 | q += namelen; | |
100 | if (*q == ':') | |
101 | q++, got = 1; | |
b6a2f15e | 102 | while (xisspace(*q)) |
6eb42cae | 103 | q++, got = 1; |
4e41e277 | 104 | if (got && prefix) { |
105 | /* we could process list entries here if we had strcasestr(). */ | |
106 | /* make sure we did not match a part of another field-value */ | |
b6a2f15e | 107 | got = !strncasecmp(q, prefix, preflen) && !xisalpha(q[preflen]); |
4e41e277 | 108 | } |
6eb42cae | 109 | if (got) { |
a3d5953d | 110 | debug(25, 5) ("mime_get_header: returning '%s'\n", q); |
6eb42cae | 111 | return q; |
7f2e38dc | 112 | } |
113 | } | |
114 | return NULL; | |
115 | } | |
116 | ||
2334c194 | 117 | size_t |
118 | headersEnd(const char *mime, size_t l) | |
119 | { | |
120 | size_t e = 0; | |
121 | int state = 0; | |
122 | while (e < l && state < 3) { | |
1afe05c5 | 123 | switch (state) { |
124 | case 0: | |
125 | if ('\n' == mime[e]) | |
126 | state = 1; | |
127 | break; | |
128 | case 1: | |
129 | if ('\r' == mime[e]) | |
130 | state = 2; | |
131 | else if ('\n' == mime[e]) | |
132 | state = 3; | |
133 | else | |
134 | state = 0; | |
135 | break; | |
136 | case 2: | |
6123e071 | 137 | if ('\r' == mime[e]) /* ignore repeated CR */ |
138 | (void) 0; | |
139 | else if ('\n' == mime[e]) | |
1afe05c5 | 140 | state = 3; |
141 | else | |
142 | state = 0; | |
143 | break; | |
144 | default: | |
145 | break; | |
146 | } | |
147 | e++; | |
2334c194 | 148 | } |
149 | if (3 == state) | |
1afe05c5 | 150 | return e; |
2334c194 | 151 | return 0; |
152 | } | |
30a4f2a8 | 153 | |
63259c34 | 154 | const char * |
155 | mime_get_auth(const char *hdr, const char *auth_scheme, const char **auth_field) | |
156 | { | |
157 | char *auth_hdr; | |
158 | char *t; | |
2ac76861 | 159 | if (auth_field) |
160 | *auth_field = NULL; | |
63259c34 | 161 | if (hdr == NULL) |
162 | return NULL; | |
163 | if ((auth_hdr = mime_get_header(hdr, "Authorization")) == NULL) | |
164 | return NULL; | |
2ac76861 | 165 | if (auth_field) |
166 | *auth_field = auth_hdr; | |
63259c34 | 167 | if ((t = strtok(auth_hdr, " \t")) == NULL) |
168 | return NULL; | |
169 | if (strcasecmp(t, auth_scheme) != 0) | |
170 | return NULL; | |
171 | if ((t = strtok(NULL, " \t")) == NULL) | |
172 | return NULL; | |
173 | return base64_decode(t); | |
174 | } | |
175 | ||
2a1ca944 | 176 | static mimeEntry * |
177 | mimeGetEntry(const char *fn, int skip_encodings) | |
812ed90c | 178 | { |
179 | mimeEntry *m; | |
2a1ca944 | 180 | char *t; |
181 | char *name = xstrdup(fn); | |
0cdcddb9 | 182 | try_again: |
812ed90c | 183 | for (m = MimeTable; m; m = m->next) { |
2a1ca944 | 184 | if (regexec(&m->compiled_pattern, name, 0, 0, 0) == 0) |
812ed90c | 185 | break; |
186 | } | |
5248dfb8 | 187 | if (!skip_encodings) |
188 | (void) 0; | |
189 | else if (m == NULL) | |
190 | (void) 0; | |
191 | else if (strcmp(m->content_type, dash_str)) | |
192 | (void) 0; | |
193 | else if (!strcmp(m->content_encoding, dash_str)) | |
194 | (void) 0; | |
195 | else { | |
2a1ca944 | 196 | /* Assume we matched /\.\w$/ and cut off the last extension */ |
197 | if ((t = strrchr(name, '.'))) { | |
198 | *t = '\0'; | |
199 | goto try_again; | |
200 | } | |
201 | /* What? A encoding without a extension? */ | |
0cdcddb9 | 202 | m = NULL; |
2a1ca944 | 203 | } |
204 | xfree(name); | |
205 | return m; | |
206 | } | |
207 | ||
208 | char * | |
209 | mimeGetIcon(const char *fn) | |
210 | { | |
211 | mimeEntry *m = mimeGetEntry(fn, 1); | |
812ed90c | 212 | if (m == NULL) |
213 | return NULL; | |
214 | if (!strcmp(m->icon, dash_str)) | |
215 | return NULL; | |
216 | return m->icon; | |
217 | } | |
218 | ||
d20b1cd0 | 219 | const char * |
4162ee3b | 220 | mimeGetIconURL(const char *fn) |
221 | { | |
222 | char *icon = mimeGetIcon(fn); | |
4162ee3b | 223 | if (icon == NULL) |
d20b1cd0 | 224 | return null_string; |
1da5651f | 225 | return internalLocalUri("/squid-internal-static/icons/", icon); |
4162ee3b | 226 | } |
227 | ||
812ed90c | 228 | char * |
229 | mimeGetContentType(const char *fn) | |
230 | { | |
2a1ca944 | 231 | mimeEntry *m = mimeGetEntry(fn, 1); |
812ed90c | 232 | if (m == NULL) |
233 | return NULL; | |
234 | if (!strcmp(m->content_type, dash_str)) | |
235 | return NULL; | |
236 | return m->content_type; | |
237 | } | |
238 | ||
239 | char * | |
240 | mimeGetContentEncoding(const char *fn) | |
241 | { | |
2a1ca944 | 242 | mimeEntry *m = mimeGetEntry(fn, 0); |
812ed90c | 243 | if (m == NULL) |
244 | return NULL; | |
245 | if (!strcmp(m->content_encoding, dash_str)) | |
246 | return NULL; | |
247 | return m->content_encoding; | |
248 | } | |
249 | ||
250 | char | |
251 | mimeGetTransferMode(const char *fn) | |
252 | { | |
2a1ca944 | 253 | mimeEntry *m = mimeGetEntry(fn, 0); |
812ed90c | 254 | return m ? m->transfer_mode : 'I'; |
255 | } | |
256 | ||
2a1ca944 | 257 | int |
258 | mimeGetDownloadOption(const char *fn) | |
259 | { | |
260 | mimeEntry *m = mimeGetEntry(fn, 1); | |
261 | return m ? m->download_option : 0; | |
262 | } | |
263 | ||
264 | int | |
265 | mimeGetViewOption(const char *fn) | |
266 | { | |
267 | mimeEntry *m = mimeGetEntry(fn, 0); | |
268 | return m ? m->view_option : 0; | |
269 | } | |
270 | ||
00fa6528 | 271 | /* Initializes/reloads the mime table |
272 | * Note: Due to Solaris STDIO problems the caller should NOT | |
273 | * call mimeFreeMemory on reconfigure. This way, if STDIO | |
274 | * fails we at least have the old copy loaded. | |
275 | */ | |
812ed90c | 276 | void |
277 | mimeInit(char *filename) | |
278 | { | |
279 | FILE *fp; | |
280 | char buf[BUFSIZ]; | |
281 | char chopbuf[BUFSIZ]; | |
282 | char *t; | |
283 | char *pattern; | |
284 | char *icon; | |
285 | char *type; | |
286 | char *encoding; | |
287 | char *mode; | |
2a1ca944 | 288 | char *option; |
289 | int view_option; | |
290 | int download_option; | |
812ed90c | 291 | regex_t re; |
292 | mimeEntry *m; | |
293 | int re_flags = REG_EXTENDED | REG_NOSUB | REG_ICASE; | |
294 | if (filename == NULL) | |
295 | return; | |
296 | if ((fp = fopen(filename, "r")) == NULL) { | |
a3d5953d | 297 | debug(50, 1) ("mimeInit: %s: %s\n", filename, xstrerror()); |
812ed90c | 298 | return; |
299 | } | |
c4aefe96 | 300 | #if defined (_SQUID_CYGWIN_) |
301 | setmode(fileno(fp), O_TEXT); | |
302 | #endif | |
00fa6528 | 303 | mimeFreeMemory(); |
812ed90c | 304 | while (fgets(buf, BUFSIZ, fp)) { |
305 | if ((t = strchr(buf, '#'))) | |
306 | *t = '\0'; | |
76f87348 | 307 | if ((t = strchr(buf, '\r'))) |
308 | *t = '\0'; | |
309 | if ((t = strchr(buf, '\n'))) | |
310 | *t = '\0'; | |
812ed90c | 311 | if (buf[0] == '\0') |
312 | continue; | |
313 | xstrncpy(chopbuf, buf, BUFSIZ); | |
314 | if ((pattern = strtok(chopbuf, w_space)) == NULL) { | |
a3d5953d | 315 | debug(25, 1) ("mimeInit: parse error: '%s'\n", buf); |
812ed90c | 316 | continue; |
317 | } | |
318 | if ((type = strtok(NULL, w_space)) == NULL) { | |
a3d5953d | 319 | debug(25, 1) ("mimeInit: parse error: '%s'\n", buf); |
812ed90c | 320 | continue; |
321 | } | |
322 | if ((icon = strtok(NULL, w_space)) == NULL) { | |
a3d5953d | 323 | debug(25, 1) ("mimeInit: parse error: '%s'\n", buf); |
812ed90c | 324 | continue; |
325 | } | |
326 | if ((encoding = strtok(NULL, w_space)) == NULL) { | |
a3d5953d | 327 | debug(25, 1) ("mimeInit: parse error: '%s'\n", buf); |
812ed90c | 328 | continue; |
329 | } | |
330 | if ((mode = strtok(NULL, w_space)) == NULL) { | |
a3d5953d | 331 | debug(25, 1) ("mimeInit: parse error: '%s'\n", buf); |
812ed90c | 332 | continue; |
333 | } | |
0cdcddb9 | 334 | download_option = 0; |
335 | view_option = 0; | |
2a1ca944 | 336 | while ((option = strtok(NULL, w_space)) != NULL) { |
0cdcddb9 | 337 | if (!strcmp(option, "+download")) |
2a1ca944 | 338 | download_option = 1; |
0cdcddb9 | 339 | else if (!strcmp(option, "+view")) |
2a1ca944 | 340 | view_option = 1; |
0cdcddb9 | 341 | else |
2a1ca944 | 342 | debug(25, 1) ("mimeInit: unknown option: '%s' (%s)\n", buf, option); |
343 | } | |
812ed90c | 344 | if (regcomp(&re, pattern, re_flags) != 0) { |
a3d5953d | 345 | debug(25, 1) ("mimeInit: regcomp error: '%s'\n", buf); |
812ed90c | 346 | continue; |
347 | } | |
348 | m = xcalloc(1, sizeof(mimeEntry)); | |
349 | m->pattern = xstrdup(pattern); | |
350 | m->content_type = xstrdup(type); | |
351 | m->icon = xstrdup(icon); | |
352 | m->content_encoding = xstrdup(encoding); | |
353 | m->compiled_pattern = re; | |
354 | if (!strcasecmp(mode, "ascii")) | |
355 | m->transfer_mode = 'A'; | |
356 | else if (!strcasecmp(mode, "text")) | |
357 | m->transfer_mode = 'A'; | |
358 | else | |
359 | m->transfer_mode = 'I'; | |
2a1ca944 | 360 | m->view_option = view_option; |
361 | m->download_option = download_option; | |
812ed90c | 362 | *MimeTableTail = m; |
363 | MimeTableTail = &m->next; | |
4cd746ee | 364 | debug(25, 5) ("mimeInit: added '%s'\n", buf); |
812ed90c | 365 | } |
337d4867 | 366 | fclose(fp); |
8ca66e48 | 367 | /* |
368 | * Create Icon StoreEntry's | |
369 | */ | |
370 | for (m = MimeTable; m != NULL; m = m->next) | |
371 | mimeLoadIconFile(m->icon); | |
f53b06f9 | 372 | debug(25, 1) ("Loaded Icons.\n"); |
812ed90c | 373 | } |
365cb147 | 374 | |
c68e9c6b | 375 | void |
376 | mimeFreeMemory(void) | |
377 | { | |
378 | mimeEntry *m; | |
379 | while ((m = MimeTable)) { | |
380 | MimeTable = m->next; | |
381 | safe_free(m->pattern); | |
382 | safe_free(m->content_type); | |
383 | safe_free(m->icon); | |
384 | safe_free(m->content_encoding); | |
385 | regfree(&m->compiled_pattern); | |
386 | safe_free(m); | |
387 | } | |
388 | MimeTableTail = &MimeTable; | |
389 | } | |
390 | ||
365cb147 | 391 | static void |
392 | mimeLoadIconFile(const char *icon) | |
393 | { | |
f9e5a344 | 394 | int fd; |
395 | int n; | |
92695e5e | 396 | request_flags flags; |
f9e5a344 | 397 | struct stat sb; |
398 | StoreEntry *e; | |
399 | LOCAL_ARRAY(char, path, MAXPATHLEN); | |
400 | LOCAL_ARRAY(char, url, MAX_URL); | |
401 | char *buf; | |
4cd746ee | 402 | const char *type = mimeGetContentType(icon); |
b6a2f15e | 403 | HttpReply *reply; |
ccf44862 | 404 | http_version_t version; |
4cd746ee | 405 | if (type == NULL) |
406 | fatal("Unknown icon format while reading mime.conf\n"); | |
1da5651f | 407 | buf = internalLocalUri("/squid-internal-static/icons/", icon); |
408 | xstrncpy(url, buf, MAX_URL); | |
08e5d64f | 409 | if (storeGetPublic(url, METHOD_GET)) |
f9e5a344 | 410 | return; |
411 | snprintf(path, MAXPATHLEN, "%s/%s", Config.icons.directory, icon); | |
c4aefe96 | 412 | fd = file_open(path, O_RDONLY | O_BINARY); |
f9e5a344 | 413 | if (fd < 0) { |
414 | debug(25, 0) ("mimeLoadIconFile: %s: %s\n", path, xstrerror()); | |
415 | return; | |
416 | } | |
417 | if (fstat(fd, &sb) < 0) { | |
418 | debug(50, 0) ("mimeLoadIconFile: FD %d: fstat: %s\n", fd, xstrerror()); | |
419 | return; | |
420 | } | |
92695e5e | 421 | flags = null_request_flags; |
422 | flags.cachable = 1; | |
f9e5a344 | 423 | e = storeCreateEntry(url, |
424 | url, | |
c998d7c8 | 425 | flags, |
f9e5a344 | 426 | METHOD_GET); |
427 | assert(e != NULL); | |
4d19abd1 | 428 | storeSetPublicKey(e); |
a9f31179 | 429 | storeBuffer(e); |
f9e5a344 | 430 | e->mem_obj->request = requestLink(urlParse(METHOD_GET, url)); |
b6a2f15e | 431 | httpReplyReset(reply = e->mem_obj->reply); |
bffee5af | 432 | httpBuildVersion(&version, 1, 0); |
ccf44862 | 433 | httpReplySetHeaders(reply, version, HTTP_OK, NULL, |
b6a2f15e | 434 | type, (int) sb.st_size, sb.st_mtime, -1); |
435 | reply->cache_control = httpHdrCcCreate(); | |
436 | httpHdrCcSetMaxAge(reply->cache_control, 86400); | |
437 | httpHeaderPutCc(&reply->header, reply->cache_control); | |
438 | httpReplySwapOut(reply, e); | |
22c59550 | 439 | reply->hdr_sz = e->mem_obj->inmem_hi; /* yuk */ |
cb69b4c7 | 440 | /* read the file into the buffer and append it to store */ |
7021844c | 441 | buf = memAllocate(MEM_4K_BUF); |
1f7c9178 | 442 | while ((n = FD_READ_METHOD(fd, buf, 4096)) > 0) |
f9e5a344 | 443 | storeAppend(e, buf, n); |
444 | file_close(fd); | |
a9f31179 | 445 | EBIT_SET(e->flags, ENTRY_SPECIAL); |
446 | storeBufferFlush(e); | |
f9e5a344 | 447 | storeComplete(e); |
448 | storeTimestampsSet(e); | |
f53b06f9 | 449 | debug(25, 3) ("Loaded icon %s\n", url); |
78f1250a | 450 | storeUnlockObject(e); |
db1cd23c | 451 | memFree(buf, MEM_4K_BUF); |
365cb147 | 452 | } |