]> git.ipfire.org Git - thirdparty/squid.git/blob - src/mime.cc
add53c4a4deda84b7452f84415dac73921308fe6
[thirdparty/squid.git] / src / mime.cc
1
2 /*
3 * $Id$
4 *
5 * DEBUG: section 25 MIME Parsing
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
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.
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
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37 #include "Store.h"
38 #include "StoreClient.h"
39 #include "HttpReply.h"
40 #include "HttpRequest.h"
41 #include "MemObject.h"
42 #include "fde.h"
43 #include "MemBuf.h"
44
45 #define GET_HDR_SZ 1024
46
47 class MimeIcon : public StoreClient
48 {
49
50 public:
51 MimeIcon ();
52 void setName (char const *);
53 char const * getName () const;
54 void _free();
55 void load();
56 void created (StoreEntry *newEntry);
57
58 private:
59 char *icon;
60 char *url;
61 };
62
63 class mimeEntry
64 {
65
66 public:
67 void *operator new (size_t byteCount);
68 void operator delete (void *address);
69
70 char *pattern;
71 regex_t compiled_pattern;
72 char *icon;
73 char *content_type;
74 char *content_encoding;
75 char transfer_mode;
76
77 unsigned int view_option:1;
78 unsigned int download_option:1;
79
80 mimeEntry *next;
81 MimeIcon theIcon;
82 };
83
84 static mimeEntry *MimeTable = NULL;
85 static mimeEntry **MimeTableTail = &MimeTable;
86
87 void *
88 mimeEntry::operator new (size_t byteCount)
89 {
90 return xcalloc(1, byteCount);
91 }
92
93 void
94 mimeEntry::operator delete (void *address)
95 {
96 safe_free (address);
97 }
98
99 /* returns a pointer to a field-value of the first matching field-name */
100 char *
101 mime_get_header(const char *mime, const char *name)
102 {
103 return mime_get_header_field(mime, name, NULL);
104 }
105
106 /*
107 * returns a pointer to a field-value of the first matching field-name where
108 * field-value matches prefix if any
109 */
110 char *
111 mime_get_header_field(const char *mime, const char *name, const char *prefix)
112 {
113 LOCAL_ARRAY(char, header, GET_HDR_SZ);
114 const char *p = NULL;
115 char *q = NULL;
116 char got = 0;
117 const int namelen = name ? strlen(name) : 0;
118 const int preflen = prefix ? strlen(prefix) : 0;
119 int l;
120
121 if (NULL == mime)
122 return NULL;
123
124 assert(NULL != name);
125
126 debugs(25, 5, "mime_get_header: looking for '" << name << "'");
127
128 for (p = mime; *p; p += strcspn(p, "\n\r")) {
129 if (strcmp(p, "\r\n\r\n") == 0 || strcmp(p, "\n\n") == 0)
130 return NULL;
131
132 while (xisspace(*p))
133 p++;
134
135 if (strncasecmp(p, name, namelen))
136 continue;
137
138 if (!xisspace(p[namelen]) && p[namelen] != ':')
139 continue;
140
141 l = strcspn(p, "\n\r") + 1;
142
143 if (l > GET_HDR_SZ)
144 l = GET_HDR_SZ;
145
146 xstrncpy(header, p, l);
147
148 debugs(25, 5, "mime_get_header: checking '" << header << "'");
149
150 q = header;
151
152 q += namelen;
153
154 if (*q == ':')
155 q++, got = 1;
156
157 while (xisspace(*q))
158 q++, got = 1;
159
160 if (got && prefix) {
161 /* we could process list entries here if we had strcasestr(). */
162 /* make sure we did not match a part of another field-value */
163 got = !strncasecmp(q, prefix, preflen) && !xisalpha(q[preflen]);
164 }
165
166 if (got) {
167 debugs(25, 5, "mime_get_header: returning '" << q << "'");
168 return q;
169 }
170 }
171
172 return NULL;
173 }
174
175 size_t
176 headersEnd(const char *mime, size_t l)
177 {
178 size_t e = 0;
179 int state = 1;
180
181 PROF_start(headersEnd);
182
183 while (e < l && state < 3) {
184 switch (state) {
185
186 case 0:
187
188 if ('\n' == mime[e])
189 state = 1;
190
191 break;
192
193 case 1:
194 if ('\r' == mime[e])
195 state = 2;
196 else if ('\n' == mime[e])
197 state = 3;
198 else
199 state = 0;
200
201 break;
202
203 case 2:
204 if ('\n' == mime[e])
205 state = 3;
206 else
207 state = 0;
208
209 break;
210
211 default:
212 break;
213 }
214
215 e++;
216 }
217 PROF_stop(headersEnd);
218
219 if (3 == state)
220 return e;
221
222 return 0;
223 }
224
225 static mimeEntry *
226 mimeGetEntry(const char *fn, int skip_encodings)
227 {
228 mimeEntry *m;
229 char *t = NULL;
230 char *name = xstrdup(fn);
231
232 do {
233
234 for (m = MimeTable; m; m = m->next) {
235 if (regexec(&m->compiled_pattern, name, 0, 0, 0) == 0)
236 break;
237 }
238
239 if (!skip_encodings)
240 (void) 0;
241 else if (m == NULL)
242 (void) 0;
243 else if (strcmp(m->content_type, dash_str))
244 (void) 0;
245 else if (!strcmp(m->content_encoding, dash_str))
246 (void) 0;
247 else {
248 /* Assume we matched /\.\w$/ and cut off the last extension */
249 if ((t = strrchr(name, '.'))) {
250 *t = '\0';
251 } else {
252 /* What? A encoding without a extension? */
253 m = NULL;
254 }
255 }
256 } while (t);
257
258 xfree(name);
259 return m;
260 }
261
262 MimeIcon::MimeIcon () : icon (NULL), url (NULL)
263 {}
264
265 void
266 MimeIcon::setName (char const *aString)
267 {
268 safe_free (icon);
269 safe_free (url);
270 icon = xstrdup (aString);
271 url = xstrdup (internalLocalUri("/squid-internal-static/icons/", icon));
272 }
273
274 char const *
275 MimeIcon::getName () const
276 {
277 return icon;
278 }
279
280 void
281 MimeIcon::_free()
282 {
283 safe_free (icon);
284 safe_free (url);
285 }
286
287
288 char const *
289 mimeGetIcon(const char *fn)
290 {
291 mimeEntry *m = mimeGetEntry(fn, 1);
292
293 if (m == NULL)
294 return NULL;
295
296 if (!strcmp(m->theIcon.getName(), dash_str))
297 return NULL;
298
299 return m->theIcon.getName();
300 }
301
302 const char *
303 mimeGetIconURL(const char *fn)
304 {
305 char const *icon = mimeGetIcon(fn);
306
307 if (icon == NULL)
308 return null_string;
309
310 if (Config.icons.use_short_names) {
311 static MemBuf mb;
312 mb.reset();
313 mb.Printf("/squid-internal-static/icons/%s", icon);
314 return mb.content();
315 } else {
316 return internalLocalUri("/squid-internal-static/icons/", icon);
317 }
318 }
319
320 char *
321 mimeGetContentType(const char *fn)
322 {
323 mimeEntry *m = mimeGetEntry(fn, 1);
324
325 if (m == NULL)
326 return NULL;
327
328 if (!strcmp(m->content_type, dash_str))
329 return NULL;
330
331 return m->content_type;
332 }
333
334 char *
335 mimeGetContentEncoding(const char *fn)
336 {
337 mimeEntry *m = mimeGetEntry(fn, 0);
338
339 if (m == NULL)
340 return NULL;
341
342 if (!strcmp(m->content_encoding, dash_str))
343 return NULL;
344
345 return m->content_encoding;
346 }
347
348 char
349 mimeGetTransferMode(const char *fn)
350 {
351 mimeEntry *m = mimeGetEntry(fn, 0);
352 return m ? m->transfer_mode : 'I';
353 }
354
355 int
356 mimeGetDownloadOption(const char *fn)
357 {
358 mimeEntry *m = mimeGetEntry(fn, 1);
359 return m ? m->download_option : 0;
360 }
361
362 int
363 mimeGetViewOption(const char *fn)
364 {
365 mimeEntry *m = mimeGetEntry(fn, 0);
366 return m ? m->view_option : 0;
367 }
368
369 /* Initializes/reloads the mime table
370 * Note: Due to Solaris STDIO problems the caller should NOT
371 * call mimeFreeMemory on reconfigure. This way, if STDIO
372 * fails we at least have the old copy loaded.
373 */
374 void
375 mimeInit(char *filename)
376 {
377 FILE *fp;
378 char buf[BUFSIZ];
379 char chopbuf[BUFSIZ];
380 char *t;
381 char *pattern;
382 char *icon;
383 char *type;
384 char *encoding;
385 char *mode;
386 char *option;
387 int view_option;
388 int download_option;
389 regex_t re;
390 mimeEntry *m;
391 int re_flags = REG_EXTENDED | REG_NOSUB | REG_ICASE;
392
393 if (filename == NULL)
394 return;
395
396 if ((fp = fopen(filename, "r")) == NULL) {
397 debugs(25, 1, "mimeInit: " << filename << ": " << xstrerror());
398 return;
399 }
400
401 #ifdef _SQUID_WIN32_
402 setmode(fileno(fp), O_TEXT);
403
404 #endif
405
406 mimeFreeMemory();
407
408 while (fgets(buf, BUFSIZ, fp)) {
409 if ((t = strchr(buf, '#')))
410 *t = '\0';
411
412 if ((t = strchr(buf, '\r')))
413 *t = '\0';
414
415 if ((t = strchr(buf, '\n')))
416 *t = '\0';
417
418 if (buf[0] == '\0')
419 continue;
420
421 xstrncpy(chopbuf, buf, BUFSIZ);
422
423 if ((pattern = strtok(chopbuf, w_space)) == NULL) {
424 debugs(25, 1, "mimeInit: parse error: '" << buf << "'");
425 continue;
426 }
427
428 if ((type = strtok(NULL, w_space)) == NULL) {
429 debugs(25, 1, "mimeInit: parse error: '" << buf << "'");
430 continue;
431 }
432
433 if ((icon = strtok(NULL, w_space)) == NULL) {
434 debugs(25, 1, "mimeInit: parse error: '" << buf << "'");
435 continue;
436 }
437
438 if ((encoding = strtok(NULL, w_space)) == NULL) {
439 debugs(25, 1, "mimeInit: parse error: '" << buf << "'");
440 continue;
441 }
442
443 if ((mode = strtok(NULL, w_space)) == NULL) {
444 debugs(25, 1, "mimeInit: parse error: '" << buf << "'");
445 continue;
446 }
447
448 download_option = 0;
449 view_option = 0;
450
451 while ((option = strtok(NULL, w_space)) != NULL) {
452 if (!strcmp(option, "+download"))
453 download_option = 1;
454 else if (!strcmp(option, "+view"))
455 view_option = 1;
456 else
457 debugs(25, 1, "mimeInit: unknown option: '" << buf << "' (" << option << ")");
458 }
459
460 if (regcomp(&re, pattern, re_flags) != 0) {
461 debugs(25, 1, "mimeInit: regcomp error: '" << buf << "'");
462 continue;
463 }
464
465 m = new mimeEntry;
466 m->pattern = xstrdup(pattern);
467 m->content_type = xstrdup(type);
468 m->theIcon.setName(icon);
469 m->content_encoding = xstrdup(encoding);
470 m->compiled_pattern = re;
471
472 if (!strcasecmp(mode, "ascii"))
473 m->transfer_mode = 'A';
474 else if (!strcasecmp(mode, "text"))
475 m->transfer_mode = 'A';
476 else
477 m->transfer_mode = 'I';
478
479 m->view_option = view_option;
480
481 m->download_option = download_option;
482
483 *MimeTableTail = m;
484
485 MimeTableTail = &m->next;
486
487 debugs(25, 5, "mimeInit: added '" << buf << "'");
488 }
489
490 fclose(fp);
491 /*
492 * Create Icon StoreEntry's
493 */
494
495 for (m = MimeTable; m != NULL; m = m->next)
496 m->theIcon.load();
497
498 debugs(25, 1, "Loaded Icons.");
499 }
500
501 void
502 mimeFreeMemory(void)
503 {
504 mimeEntry *m;
505
506 while ((m = MimeTable)) {
507 MimeTable = m->next;
508 safe_free(m->pattern);
509 safe_free(m->content_type);
510 safe_free(m->icon);
511 safe_free(m->content_encoding);
512 regfree(&m->compiled_pattern);
513 delete m;
514 }
515
516 MimeTableTail = &MimeTable;
517 }
518
519 void
520 MimeIcon::load()
521 {
522 const char *type = mimeGetContentType(icon);
523
524 if (type == NULL)
525 fatal("Unknown icon format while reading mime.conf\n");
526
527 StoreEntry::getPublic(this, url, METHOD_GET);
528 }
529
530 void
531 MimeIcon::created (StoreEntry *newEntry)
532 {
533 /* is already in the store, do nothing */
534
535 if (!newEntry->isNull())
536 return;
537
538 int fd;
539
540 int n;
541
542 request_flags flags;
543
544 struct stat sb;
545
546 LOCAL_ARRAY(char, path, MAXPATHLEN);
547
548 char *buf;
549
550 snprintf(path, MAXPATHLEN, "%s/%s", Config.icons.directory, icon);
551
552 fd = file_open(path, O_RDONLY | O_BINARY);
553
554 if (fd < 0) {
555 debugs(25, 0, "mimeLoadIconFile: " << path << ": " << xstrerror());
556 return;
557 }
558
559 if (fstat(fd, &sb) < 0) {
560 debugs(25, 0, "mimeLoadIconFile: FD " << fd << ": fstat: " << xstrerror());
561 file_close(fd);
562 return;
563 }
564
565 flags.cachable = 1;
566 StoreEntry *e = storeCreateEntry(url,
567 url,
568 flags,
569 METHOD_GET);
570 assert(e != NULL);
571 EBIT_SET(e->flags, ENTRY_SPECIAL);
572 e->setPublicKey();
573 e->buffer();
574 HttpRequest *r = HttpRequest::CreateFromUrl(url);
575
576 if (NULL == r)
577 fatal("mimeLoadIcon: cannot parse internal URL");
578
579 e->mem_obj->request = HTTPMSGLOCK(r);
580
581 HttpReply *reply = new HttpReply;
582
583 HttpVersion version(1, 0);
584
585 reply->setHeaders(version, HTTP_OK, NULL,
586 mimeGetContentType(icon), sb.st_size, sb.st_mtime, -1);
587
588 reply->cache_control = httpHdrCcCreate();
589
590 httpHdrCcSetMaxAge(reply->cache_control, 86400);
591
592 reply->header.putCc(reply->cache_control);
593
594 e->replaceHttpReply(reply);
595
596 /* read the file into the buffer and append it to store */
597 buf = (char *)memAllocate(MEM_4K_BUF);
598
599 while ((n = FD_READ_METHOD(fd, buf, 4096)) > 0)
600 e->append(buf, n);
601
602 file_close(fd);
603
604 e->flush();
605
606 e->complete();
607
608 e->timestampsSet();
609
610 debugs(25, 3, "Loaded icon " << url);
611
612 e->unlock();
613
614 memFree(buf, MEM_4K_BUF);
615 }