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