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