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