]> git.ipfire.org Git - thirdparty/squid.git/blob - src/mime.cc
Merged from trunk
[thirdparty/squid.git] / src / mime.cc
1
2 /*
3 * DEBUG: section 25 MIME Parsing and Internal Icons
4 * AUTHOR: Harvest Derived
5 *
6 * SQUID Web Proxy Cache http://www.squid-cache.org/
7 * ----------------------------------------------------------
8 *
9 * Squid is the result of efforts by numerous individuals from
10 * the Internet community; see the CONTRIBUTORS file for full
11 * details. Many organizations have provided support for Squid's
12 * development; see the SPONSORS file for full details. Squid is
13 * Copyrighted (C) 2001 by the Regents of the University of
14 * California; see the COPYRIGHT file for full details. Squid
15 * incorporates software developed and/or copyrighted by other
16 * sources; see the CREDITS file for full details.
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
31 *
32 */
33
34 #include "squid.h"
35 #include "disk.h"
36 #include "fde.h"
37 #include "globals.h"
38 #include "HttpHdrCc.h"
39 #include "HttpReply.h"
40 #include "HttpRequest.h"
41 #include "internal.h"
42 #include "Mem.h"
43 #include "MemBuf.h"
44 #include "mime.h"
45 #include "MemObject.h"
46 #include "RequestFlags.h"
47 #include "SquidConfig.h"
48 #include "Store.h"
49 #include "StoreClient.h"
50
51 #if HAVE_SYS_STAT_H
52 #include <sys/stat.h>
53 #endif
54
55 #define GET_HDR_SZ 1024
56
57 /* forward declarations */
58 static void mimeFreeMemory(void);
59 static char const *mimeGetIcon(const char *fn);
60
61 class MimeIcon : public StoreClient
62 {
63
64 public:
65 MimeIcon ();
66 ~MimeIcon ();
67 void setName (char const *);
68 char const * getName () const;
69 void _free();
70 void load();
71 void created (StoreEntry *newEntry);
72
73 private:
74 char *icon;
75 char *url;
76 };
77
78 class mimeEntry
79 {
80
81 public:
82 void *operator new (size_t byteCount);
83 void operator delete (void *address);
84
85 char *pattern;
86 regex_t compiled_pattern;
87 char *icon;
88 char *content_type;
89 char *content_encoding;
90 char transfer_mode;
91
92 unsigned int view_option:1;
93 unsigned int download_option:1;
94
95 mimeEntry *next;
96 MimeIcon theIcon;
97 };
98
99 static mimeEntry *MimeTable = NULL;
100 static mimeEntry **MimeTableTail = &MimeTable;
101
102 void *
103 mimeEntry::operator new (size_t byteCount)
104 {
105 return xcalloc(1, byteCount);
106 }
107
108 void
109 mimeEntry::operator delete (void *address)
110 {
111 safe_free (address);
112 }
113
114 static mimeEntry *
115 mimeGetEntry(const char *fn, int skip_encodings)
116 {
117 mimeEntry *m;
118 char *t;
119 char *name = xstrdup(fn);
120
121 do {
122 t = NULL;
123
124 for (m = MimeTable; m; m = m->next) {
125 if (regexec(&m->compiled_pattern, name, 0, 0, 0) == 0)
126 break;
127 }
128
129 if (!skip_encodings)
130 (void) 0;
131 else if (m == NULL)
132 (void) 0;
133 else if (strcmp(m->content_type, dash_str))
134 (void) 0;
135 else if (!strcmp(m->content_encoding, dash_str))
136 (void) 0;
137 else {
138 /* Assume we matched /\.\w$/ and cut off the last extension */
139 if ((t = strrchr(name, '.'))) {
140 *t = '\0';
141 } else {
142 /* What? A encoding without a extension? */
143 m = NULL;
144 }
145 }
146 } while (t);
147
148 xfree(name);
149 return m;
150 }
151
152 MimeIcon::MimeIcon () : icon (NULL), url (NULL)
153 {}
154
155 MimeIcon::~MimeIcon ()
156 {
157 _free();
158 }
159
160 void
161 MimeIcon::setName (char const *aString)
162 {
163 safe_free (icon);
164 safe_free (url);
165 icon = xstrdup (aString);
166 url = xstrdup (internalLocalUri("/squid-internal-static/icons/", icon));
167 }
168
169 char const *
170 MimeIcon::getName () const
171 {
172 return icon;
173 }
174
175 void
176 MimeIcon::_free()
177 {
178 safe_free (icon);
179 safe_free (url);
180 }
181
182 char const *
183 mimeGetIcon(const char *fn)
184 {
185 mimeEntry *m = mimeGetEntry(fn, 1);
186
187 if (m == NULL)
188 return NULL;
189
190 if (!strcmp(m->theIcon.getName(), dash_str))
191 return NULL;
192
193 return m->theIcon.getName();
194 }
195
196 const char *
197 mimeGetIconURL(const char *fn)
198 {
199 char const *icon = mimeGetIcon(fn);
200
201 if (icon == NULL)
202 return null_string;
203
204 if (Config.icons.use_short_names) {
205 static MemBuf mb;
206 mb.reset();
207 mb.Printf("/squid-internal-static/icons/%s", icon);
208 return mb.content();
209 } else {
210 return internalLocalUri("/squid-internal-static/icons/", icon);
211 }
212 }
213
214 char *
215 mimeGetContentType(const char *fn)
216 {
217 mimeEntry *m = mimeGetEntry(fn, 1);
218
219 if (m == NULL)
220 return NULL;
221
222 if (!strcmp(m->content_type, dash_str))
223 return NULL;
224
225 return m->content_type;
226 }
227
228 char *
229 mimeGetContentEncoding(const char *fn)
230 {
231 mimeEntry *m = mimeGetEntry(fn, 0);
232
233 if (m == NULL)
234 return NULL;
235
236 if (!strcmp(m->content_encoding, dash_str))
237 return NULL;
238
239 return m->content_encoding;
240 }
241
242 char
243 mimeGetTransferMode(const char *fn)
244 {
245 mimeEntry *m = mimeGetEntry(fn, 0);
246 return m ? m->transfer_mode : 'I';
247 }
248
249 int
250 mimeGetDownloadOption(const char *fn)
251 {
252 mimeEntry *m = mimeGetEntry(fn, 1);
253 return m ? m->download_option : 0;
254 }
255
256 int
257 mimeGetViewOption(const char *fn)
258 {
259 mimeEntry *m = mimeGetEntry(fn, 0);
260 return m ? m->view_option : 0;
261 }
262
263 /* Initializes/reloads the mime table
264 * Note: Due to Solaris STDIO problems the caller should NOT
265 * call mimeFreeMemory on reconfigure. This way, if STDIO
266 * fails we at least have the old copy loaded.
267 */
268 void
269 mimeInit(char *filename)
270 {
271 FILE *fp;
272 char buf[BUFSIZ];
273 char chopbuf[BUFSIZ];
274 char *t;
275 char *pattern;
276 char *icon;
277 char *type;
278 char *encoding;
279 char *mode;
280 char *option;
281 int view_option;
282 int download_option;
283 regex_t re;
284 mimeEntry *m;
285 int re_flags = REG_EXTENDED | REG_NOSUB | REG_ICASE;
286
287 if (filename == NULL)
288 return;
289
290 if ((fp = fopen(filename, "r")) == NULL) {
291 debugs(25, DBG_IMPORTANT, "mimeInit: " << filename << ": " << xstrerror());
292 return;
293 }
294
295 #if _SQUID_WINDOWS_
296 setmode(fileno(fp), O_TEXT);
297 #endif
298
299 mimeFreeMemory();
300
301 while (fgets(buf, BUFSIZ, fp)) {
302 if ((t = strchr(buf, '#')))
303 *t = '\0';
304
305 if ((t = strchr(buf, '\r')))
306 *t = '\0';
307
308 if ((t = strchr(buf, '\n')))
309 *t = '\0';
310
311 if (buf[0] == '\0')
312 continue;
313
314 xstrncpy(chopbuf, buf, BUFSIZ);
315
316 if ((pattern = strtok(chopbuf, w_space)) == NULL) {
317 debugs(25, DBG_IMPORTANT, "mimeInit: parse error: '" << buf << "'");
318 continue;
319 }
320
321 if ((type = strtok(NULL, w_space)) == NULL) {
322 debugs(25, DBG_IMPORTANT, "mimeInit: parse error: '" << buf << "'");
323 continue;
324 }
325
326 if ((icon = strtok(NULL, w_space)) == NULL) {
327 debugs(25, DBG_IMPORTANT, "mimeInit: parse error: '" << buf << "'");
328 continue;
329 }
330
331 if ((encoding = strtok(NULL, w_space)) == NULL) {
332 debugs(25, DBG_IMPORTANT, "mimeInit: parse error: '" << buf << "'");
333 continue;
334 }
335
336 if ((mode = strtok(NULL, w_space)) == NULL) {
337 debugs(25, DBG_IMPORTANT, "mimeInit: parse error: '" << buf << "'");
338 continue;
339 }
340
341 download_option = 0;
342 view_option = 0;
343
344 while ((option = strtok(NULL, w_space)) != NULL) {
345 if (!strcmp(option, "+download"))
346 download_option = 1;
347 else if (!strcmp(option, "+view"))
348 view_option = 1;
349 else
350 debugs(25, DBG_IMPORTANT, "mimeInit: unknown option: '" << buf << "' (" << option << ")");
351 }
352
353 if (regcomp(&re, pattern, re_flags) != 0) {
354 debugs(25, DBG_IMPORTANT, "mimeInit: regcomp error: '" << buf << "'");
355 continue;
356 }
357
358 m = new mimeEntry;
359 m->pattern = xstrdup(pattern);
360 m->content_type = xstrdup(type);
361 m->theIcon.setName(icon);
362 m->content_encoding = xstrdup(encoding);
363 m->compiled_pattern = re;
364
365 if (!strcasecmp(mode, "ascii"))
366 m->transfer_mode = 'A';
367 else if (!strcasecmp(mode, "text"))
368 m->transfer_mode = 'A';
369 else
370 m->transfer_mode = 'I';
371
372 m->view_option = view_option;
373
374 m->download_option = download_option;
375
376 *MimeTableTail = m;
377
378 MimeTableTail = &m->next;
379
380 debugs(25, 5, "mimeInit: added '" << buf << "'");
381 }
382
383 fclose(fp);
384 /*
385 * Create Icon StoreEntry's
386 */
387
388 for (m = MimeTable; m != NULL; m = m->next)
389 m->theIcon.load();
390
391 debugs(25, DBG_IMPORTANT, "Loaded Icons.");
392 }
393
394 void
395 mimeFreeMemory(void)
396 {
397 mimeEntry *m;
398
399 while ((m = MimeTable)) {
400 MimeTable = m->next;
401 safe_free(m->pattern);
402 safe_free(m->content_type);
403 safe_free(m->icon);
404 safe_free(m->content_encoding);
405 regfree(&m->compiled_pattern);
406 delete m;
407 }
408
409 MimeTableTail = &MimeTable;
410 }
411
412 void
413 MimeIcon::load()
414 {
415 const char *type = mimeGetContentType(icon);
416
417 if (type == NULL)
418 fatal("Unknown icon format while reading mime.conf\n");
419
420 StoreEntry::getPublic(this, url, Http::METHOD_GET);
421 }
422
423 void
424 MimeIcon::created (StoreEntry *newEntry)
425 {
426 /* is already in the store, do nothing */
427
428 if (!newEntry->isNull())
429 return;
430
431 int fd;
432
433 int n;
434
435 RequestFlags flags;
436
437 struct stat sb;
438
439 LOCAL_ARRAY(char, path, MAXPATHLEN);
440
441 char *buf;
442
443 snprintf(path, MAXPATHLEN, "%s/%s", Config.icons.directory, icon);
444
445 fd = file_open(path, O_RDONLY | O_BINARY);
446
447 if (fd < 0) {
448 debugs(25, DBG_CRITICAL, "mimeLoadIconFile: " << path << ": " << xstrerror());
449 return;
450 }
451
452 if (fstat(fd, &sb) < 0) {
453 debugs(25, DBG_CRITICAL, "mimeLoadIconFile: FD " << fd << ": fstat: " << xstrerror());
454 file_close(fd);
455 return;
456 }
457
458 flags.cachable = true;
459 StoreEntry *e = storeCreateEntry(url,url,flags,Http::METHOD_GET);
460 assert(e != NULL);
461 EBIT_SET(e->flags, ENTRY_SPECIAL);
462 e->setPublicKey();
463 e->buffer();
464 HttpRequest *r = HttpRequest::CreateFromUrl(url);
465
466 if (NULL == r)
467 fatal("mimeLoadIcon: cannot parse internal URL");
468
469 e->mem_obj->request = HTTPMSGLOCK(r);
470
471 HttpReply *reply = new HttpReply;
472
473 reply->setHeaders(HTTP_OK, NULL, mimeGetContentType(icon), sb.st_size, sb.st_mtime, -1);
474
475 reply->cache_control = new HttpHdrCc();
476
477 reply->cache_control->maxAge(86400);
478
479 reply->header.putCc(reply->cache_control);
480
481 e->replaceHttpReply(reply);
482
483 /* read the file into the buffer and append it to store */
484 buf = (char *)memAllocate(MEM_4K_BUF);
485
486 while ((n = FD_READ_METHOD(fd, buf, 4096)) > 0)
487 e->append(buf, n);
488
489 file_close(fd);
490
491 e->flush();
492
493 e->complete();
494
495 e->timestampsSet();
496
497 debugs(25, 3, "Loaded icon " << url);
498
499 e->unlock();
500
501 memFree(buf, MEM_4K_BUF);
502 }