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