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