mime.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 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 "base/RegexPattern.h"
13 #include "debug/Messages.h"
14 #include "fde.h"
15 #include "fs_io.h"
16 #include "globals.h"
17 #include "HttpHdrCc.h"
18 #include "HttpReply.h"
19 #include "HttpRequest.h"
20 #include "internal.h"
21 #include "MemBuf.h"
22 #include "MemObject.h"
23 #include "mime.h"
24 #include "SquidConfig.h"
25 #include "Store.h"
26 #include "StoreClient.h"
27 
28 #include <array>
29 
30 #if HAVE_REGEX_H
31 #include <regex.h>
32 #endif
33 
34 #if HAVE_SYS_STAT_H
35 #include <sys/stat.h>
36 #endif
37 
38 /* forward declarations */
39 static void mimeFreeMemory(void);
40 static const SBuf mimeGetIcon(const char *fn);
41 
42 class MimeIcon : public StoreClient
43 {
45 
46 public:
47  explicit MimeIcon(const char *aName);
48  ~MimeIcon() override;
49  void setName(char const *);
50  SBuf getName() const;
51  void load();
52 
53  /* StoreClient API */
54  LogTags *loggingTags() const override { return nullptr; } // no access logging/ACLs
55  void fillChecklist(ACLFilledChecklist &) const override;
56 
57 private:
59  char *url_;
60 };
61 
62 class MimeEntry
63 {
65 
66 public:
67  explicit MimeEntry(const SBuf &aPattern,
68  const char *aContentType,
69  const char *aContentEncoding, const char *aTransferMode,
70  bool optionViewEnable, bool optionDownloadEnable,
71  const char *anIconName);
72  ~MimeEntry();
73 
75  const char *content_type;
76  const char *content_encoding;
82 };
83 
84 static MimeEntry *MimeTable = nullptr;
86 
87 static MimeEntry *
88 mimeGetEntry(const char *fn, int skip_encodings)
89 {
90  MimeEntry *m;
91  char *t;
92  char *name = xstrdup(fn);
93 
94  do {
95  t = nullptr;
96 
97  for (m = MimeTable; m; m = m->next) {
98  if (m->pattern.match(name))
99  break;
100  }
101 
102  if (!skip_encodings)
103  (void) 0;
104  else if (m == nullptr)
105  (void) 0;
106  else if (strcmp(m->content_type, dash_str))
107  (void) 0;
108  else if (!strcmp(m->content_encoding, dash_str))
109  (void) 0;
110  else {
111  /* Assume we matched /\.\w$/ and cut off the last extension */
112  if ((t = strrchr(name, '.'))) {
113  *t = '\0';
114  } else {
115  /* What? A encoding without a extension? */
116  m = nullptr;
117  }
118  }
119  } while (t);
120 
121  xfree(name);
122  return m;
123 }
124 
125 MimeIcon::MimeIcon(const char *aName) :
126  url_(nullptr)
127 {
128  setName(aName);
129 }
130 
132 {
133  xfree(url_);
134 }
135 
136 void
137 MimeIcon::setName(char const *aString)
138 {
139  xfree(url_);
140  icon_ = aString;
141  url_ = xstrdup(internalLocalUri("/squid-internal-static/icons/", icon_));
142 }
143 
144 SBuf
146 {
147  return icon_;
148 }
149 
150 const SBuf
151 mimeGetIcon(const char *fn)
152 {
153  MimeEntry *m = mimeGetEntry(fn, 1);
154 
155  if (!m || !m->theIcon.getName().cmp(dash_str))
156  return SBuf();
157 
158  return m->theIcon.getName();
159 }
160 
161 const char *
162 mimeGetIconURL(const char *fn)
163 {
164  SBuf icon(mimeGetIcon(fn));
165 
166  if (icon.isEmpty())
167  return null_string;
168 
170  static SBuf mb;
171  mb.clear();
172  mb.append("/squid-internal-static/icons/");
173  mb.append(icon);
174  return mb.c_str();
175  } else {
176  return internalLocalUri("/squid-internal-static/icons/", icon);
177  }
178 }
179 
180 const char *
181 mimeGetContentType(const char *fn)
182 {
183  MimeEntry *m = mimeGetEntry(fn, 1);
184 
185  if (m == nullptr)
186  return nullptr;
187 
188  if (!strcmp(m->content_type, dash_str))
189  return nullptr;
190 
191  return m->content_type;
192 }
193 
194 const char *
195 mimeGetContentEncoding(const char *fn)
196 {
197  MimeEntry *m = mimeGetEntry(fn, 0);
198 
199  if (m == nullptr)
200  return nullptr;
201 
202  if (!strcmp(m->content_encoding, dash_str))
203  return nullptr;
204 
205  return m->content_encoding;
206 }
207 
208 char
209 mimeGetTransferMode(const char *fn)
210 {
211  MimeEntry *m = mimeGetEntry(fn, 0);
212  return m ? m->transfer_mode : 'I';
213 }
214 
215 bool
216 mimeGetDownloadOption(const char *fn)
217 {
218  MimeEntry *m = mimeGetEntry(fn, 1);
219  return m ? m->download_option : 0;
220 }
221 
222 bool
223 mimeGetViewOption(const char *fn)
224 {
225  MimeEntry *m = mimeGetEntry(fn, 0);
226  return m != nullptr ? m->view_option : false;
227 }
228 
229 /* Initializes/reloads the mime table
230  * Note: Due to Solaris STDIO problems the caller should NOT
231  * call mimeFreeMemory on reconfigure. This way, if STDIO
232  * fails we at least have the old copy loaded.
233  */
234 void
235 mimeInit(char *filename)
236 {
237  FILE *fp;
238  char buf[BUFSIZ];
239  char chopbuf[BUFSIZ];
240  char *t;
241  char *icon;
242  char *type;
243  char *encoding;
244  char *mode;
245  char *option;
246  int view_option;
247  int download_option;
248  MimeEntry *m;
249 
250  if (filename == nullptr)
251  return;
252 
253  if ((fp = fopen(filename, "r")) == nullptr) {
254  int xerrno = errno;
255  debugs(25, DBG_IMPORTANT, "mimeInit: " << filename << ": " << xstrerr(xerrno));
256  return;
257  }
258 
259 #if _SQUID_WINDOWS_
260  setmode(fileno(fp), O_TEXT);
261 #endif
262 
263  mimeFreeMemory();
264 
265  while (fgets(buf, BUFSIZ, fp)) {
266 
267  try {
268 
269  if ((t = strchr(buf, '#')))
270  *t = '\0';
271 
272  if ((t = strchr(buf, '\r')))
273  *t = '\0';
274 
275  if ((t = strchr(buf, '\n')))
276  *t = '\0';
277 
278  if (buf[0] == '\0')
279  continue;
280 
281  xstrncpy(chopbuf, buf, BUFSIZ);
282 
283  const auto pattern = SBuf(strtok(chopbuf, w_space));
284  if (pattern.isEmpty()) {
285  debugs(25, DBG_IMPORTANT, "ERROR: mimeInit: parse failure: '" << buf << "'");
286  continue;
287  }
288 
289  if ((type = strtok(nullptr, w_space)) == nullptr) {
290  debugs(25, DBG_IMPORTANT, "ERROR: mimeInit: parse failure: '" << buf << "'");
291  continue;
292  }
293 
294  if ((icon = strtok(nullptr, w_space)) == nullptr) {
295  debugs(25, DBG_IMPORTANT, "ERROR: mimeInit: parse failure: '" << buf << "'");
296  continue;
297  }
298 
299  if ((encoding = strtok(nullptr, w_space)) == nullptr) {
300  debugs(25, DBG_IMPORTANT, "ERROR: mimeInit: parse failure: '" << buf << "'");
301  continue;
302  }
303 
304  if ((mode = strtok(nullptr, w_space)) == nullptr) {
305  debugs(25, DBG_IMPORTANT, "ERROR: mimeInit: parse failure: '" << buf << "'");
306  continue;
307  }
308 
309  download_option = 0;
310  view_option = 0;
311 
312  while ((option = strtok(nullptr, w_space)) != nullptr) {
313  if (!strcmp(option, "+download"))
314  download_option = 1;
315  else if (!strcmp(option, "+view"))
316  view_option = 1;
317  else
318  debugs(25, DBG_IMPORTANT, "ERROR: mimeInit: unknown option: '" << buf << "' (" << option << ")");
319  }
320 
321  m = new MimeEntry(SBuf(pattern),type,encoding,mode,view_option,download_option,icon);
322 
323  *MimeTableTail = m;
324 
325  MimeTableTail = &m->next;
326 
327  debugs(25, 5, "mimeInit: added '" << buf << "'");
328 
329  } catch(...) {
330  debugs(25, DBG_IMPORTANT, "ERROR: " << CurrentException);
331  continue;
332  }
333  }
334 
335  fclose(fp);
336 
337  for (m = MimeTable; m != nullptr; m = m->next)
338  m->theIcon.load();
339  debugs(25, Important(28), "Finished loading MIME types and icons.");
340 }
341 
342 void
344 {
345  MimeEntry *m;
346 
347  while ((m = MimeTable)) {
348  MimeTable = m->next;
349  delete m;
350  }
351 
353 }
354 
355 void
357 {
358  const char *type = mimeGetContentType(icon_.c_str());
359 
360  if (type == nullptr)
361  fatal("Unknown icon format while reading mime.conf\n");
362 
363  if (const auto e = storeGetPublic(url_, Http::METHOD_GET)) {
364  // do not overwrite an already stored icon
365  e->abandon(__func__);
366  return;
367  }
368 
369  // XXX: if a 204 is cached due to earlier load 'failure' we should try to reload.
370 
371  // default is a 200 object with image data.
372  // set to the backup value of 204 on image loading errors
374 
375  static char path[MAXPATHLEN];
376  *path = 0;
377  if (snprintf(path, sizeof(path)-1, "%s/" SQUIDSBUFPH, Config.icons.directory, SQUIDSBUFPRINT(icon_)) < 0) {
378  debugs(25, DBG_CRITICAL, "ERROR: icon file '" << Config.icons.directory << "/" << icon_ << "' path is longer than " << MAXPATHLEN << " bytes");
379  status = Http::scNoContent;
380  }
381 
382  int fd = -1;
383  errno = 0;
384  if (status == Http::scOkay && (fd = file_open(path, O_RDONLY | O_BINARY)) < 0) {
385  int xerrno = errno;
386  debugs(25, DBG_CRITICAL, "ERROR: opening icon file " << path << ": " << xstrerr(xerrno));
387  status = Http::scNoContent;
388  }
389 
390  struct stat sb;
391  errno = 0;
392  if (status == Http::scOkay && fstat(fd, &sb) < 0) {
393  int xerrno = errno;
394  debugs(25, DBG_CRITICAL, "ERROR: opening icon file " << path << " FD " << fd << ", fstat error " << xstrerr(xerrno));
395  file_close(fd);
396  status = Http::scNoContent;
397  }
398 
400  e->lock("MimeIcon::created");
402  const auto madePublic = e->setPublicKey();
403  assert(madePublic); // nothing can block ENTRY_SPECIAL from becoming public
404 
405  /* fill `e` with a canned 2xx response object */
406 
407  const auto mx = MasterXaction::MakePortless<XactionInitiator::initIcon>();
409  if (!r)
410  fatalf("mimeLoadIcon: cannot parse internal URL: %s", url_);
411 
412  e->buffer();
413 
414  e->mem_obj->request = r;
415 
416  HttpReplyPointer reply(new HttpReply);
417 
418  if (status == Http::scNoContent)
419  reply->setHeaders(status, nullptr, nullptr, 0, -1, -1);
420  else
421  reply->setHeaders(status, nullptr, mimeGetContentType(icon_.c_str()), sb.st_size, sb.st_mtime, -1);
422  reply->cache_control = new HttpHdrCc();
423  reply->cache_control->maxAge(86400);
424  reply->header.putCc(*reply->cache_control);
425  e->replaceHttpReply(reply.getRaw());
426 
427  if (status == Http::scOkay) {
428  /* read the file into the buffer and append it to store */
429  int n;
430  std::array<char, 4096> buf;
431  while ((n = FD_READ_METHOD(fd, buf.data(), buf.size())) > 0)
432  e->append(buf.data(), n);
433 
434  file_close(fd);
435  }
436 
437  e->flush();
438  e->complete();
439  e->timestampsSet();
440  // MimeIcons are only loaded once, prevent accidental destruction
441  // e->unlock("MimeIcon::created");
442  debugs(25, 3, "Loaded icon " << url_);
443 }
444 
445 void
447 {
448  // Unreachable: We never mayInitiateCollapsing() or startCollapsingOn().
449  assert(false);
450 }
451 
453 {
456 }
457 
458 MimeEntry::MimeEntry(const SBuf &aPattern,
459  const char *aContentType, const char *aContentEncoding,
460  const char *aTransferMode, bool optionViewEnable,
461  bool optionDownloadEnable, const char *anIconName) :
462  pattern(aPattern, REG_EXTENDED|REG_NOSUB|REG_ICASE),
463  content_type(xstrdup(aContentType)),
464  content_encoding(xstrdup(aContentEncoding)),
465  view_option(optionViewEnable),
466  download_option(optionDownloadEnable),
467  theIcon(anIconName), next(nullptr)
468 {
469  if (!strcasecmp(aTransferMode, "ascii"))
470  transfer_mode = 'A';
471  else if (!strcasecmp(aTransferMode, "text"))
472  transfer_mode = 'A';
473  else
474  transfer_mode = 'I';
475 }
476 
void fatal(const char *message)
Definition: fatal.cc:28
const char * xstrerr(int error)
Definition: xstrerror.cc:83
MEMPROXY_CLASS(MimeIcon)
#define DBG_CRITICAL
Definition: Stream.h:37
#define BUFSIZ
Definition: defines.h:20
LogTags * loggingTags() const override
Definition: mime.cc:54
void fillChecklist(ACLFilledChecklist &) const override
configure the given checklist (to reflect the current transaction state)
Definition: mime.cc:446
HttpHeader header
Definition: Message.h:74
MimeIcon theIcon
Definition: mime.cc:80
#define EBIT_SET(flag, bit)
Definition: defines.h:65
bool isEmpty() const
Definition: SBuf.h:435
MemObject * mem_obj
Definition: Store.h:220
static const SBuf mimeGetIcon(const char *fn)
Definition: mime.cc:151
void append(char const *, int) override
Appends a c-string to existing packed data.
Definition: store.cc:803
#define O_TEXT
Definition: defines.h:131
void lock(const char *context)
Definition: store.cc:445
SBuf icon_
Definition: mime.cc:58
char * url_
Definition: mime.cc:59
Definition: SBuf.h:93
#define xstrdup
bool setPublicKey(const KeyScope keyScope=ksDefault)
Definition: store.cc:575
C * getRaw() const
Definition: RefCount.h:89
const char * content_type
Definition: mime.cc:75
bool match(const char *str) const
Definition: RegexPattern.h:40
uint16_t flags
Definition: Store.h:231
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37
void putCc(const HttpHdrCc &cc)
Definition: HttpHeader.cc:1011
MimeEntry * next
Definition: mime.cc:81
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1705
StatusCode
Definition: StatusCode.h:20
void file_close(int fd)
Definition: fs_io.cc:93
#define w_space
void clear()
Definition: SBuf.cc:175
static MimeEntry * mimeGetEntry(const char *fn, int skip_encodings)
Definition: mime.cc:88
static void mimeFreeMemory(void)
Definition: mime.cc:343
Definition: mime.cc:42
a storeGetPublic*() caller
Definition: StoreClient.h:40
#define O_BINARY
Definition: defines.h:134
void maxAge(int32_t v)
Definition: HttpHdrCc.h:123
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
void mimeInit(char *filename)
Definition: mime.cc:235
void load()
Definition: mime.cc:356
RegexPattern pattern
Definition: mime.cc:74
static MimeEntry * MimeTable
Definition: mime.cc:84
~MimeIcon() override
Definition: mime.cc:131
int use_short_names
Definition: SquidConfig.h:427
const char * dash_str
#define assert(EX)
Definition: assert.h:17
HttpHdrCc * cache_control
Definition: Message.h:76
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
void buffer() override
Definition: store.cc:1601
std::ostream & CurrentException(std::ostream &os)
prints active (i.e., thrown but not yet handled) exception
void flush() override
Definition: store.cc:1612
const char * c_str()
Definition: SBuf.cc:516
const char * null_string
MimeEntry(const SBuf &aPattern, const char *aContentType, const char *aContentEncoding, const char *aTransferMode, bool optionViewEnable, bool optionDownloadEnable, const char *anIconName)
Definition: mime.cc:458
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
#define xfree
bool mimeGetViewOption(const char *fn)
Definition: mime.cc:223
int FD_READ_METHOD(int fd, char *buf, int len)
Definition: fde.h:194
void complete()
Definition: store.cc:1031
MEMPROXY_CLASS(MimeEntry)
SBuf getName() const
Definition: mime.cc:145
const char * mimeGetContentType(const char *fn)
Definition: mime.cc:181
int cmp(const SBuf &S, const size_type n) const
shorthand version for compare()
Definition: SBuf.h:279
bool timestampsSet()
Definition: store.cc:1387
@ scNoContent
Definition: StatusCode.h:31
MimeIcon(const char *aName)
Definition: mime.cc:125
~MimeEntry()
Definition: mime.cc:452
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:528
const char * mimeGetContentEncoding(const char *fn)
Definition: mime.cc:195
#define Important(id)
Definition: Messages.h:93
@ ENTRY_SPECIAL
Definition: enums.h:79
#define DBG_IMPORTANT
Definition: Stream.h:38
StoreEntry * storeGetPublic(const char *uri, const HttpRequestMethod &method)
Definition: store.cc:504
void setName(char const *)
Definition: mime.cc:137
const char * content_encoding
Definition: mime.cc:76
struct SquidConfig::@100 icons
StoreEntry * storeCreatePureEntry(const char *url, const char *log_url, const HttpRequestMethod &method)
Definition: store.cc:741
static MimeEntry ** MimeTableTail
Definition: mime.cc:85
bool view_option
Definition: mime.cc:78
const char * mimeGetIconURL(const char *fn)
Definition: mime.cc:162
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:170
#define MAXPATHLEN
Definition: stdio.h:62
int file_open(const char *path, int mode)
Definition: fs_io.cc:65
@ scOkay
Definition: StatusCode.h:27
bool mimeGetDownloadOption(const char *fn)
Definition: mime.cc:216
@ METHOD_GET
Definition: MethodType.h:25
char * internalLocalUri(const char *dir, const SBuf &name)
Definition: internal.cc:139
bool download_option
Definition: mime.cc:79
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
HttpRequestPointer request
Definition: MemObject.h:205
char transfer_mode
Definition: mime.cc:77
#define SQUIDSBUFPH
Definition: SBuf.h:31
char mimeGetTransferMode(const char *fn)
Definition: mime.cc:209
char * directory
Definition: SquidConfig.h:426
class SquidConfig Config
Definition: SquidConfig.cc:12

 

Introduction

Documentation

Support

Miscellaneous