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