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