mod_mime.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008
  1. /* Licensed to the Apache Software Foundation (ASF) under one or more
  2. * contributor license agreements. See the NOTICE file distributed with
  3. * this work for additional information regarding copyright ownership.
  4. * The ASF licenses this file to You under the Apache License, Version 2.0
  5. * (the "License"); you may not use this file except in compliance with
  6. * the License. You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * http_mime.c: Sends/gets MIME headers for requests
  18. *
  19. * Rob McCool
  20. *
  21. */
  22. #include "apr.h"
  23. #include "apr_strings.h"
  24. #include "apr_lib.h"
  25. #include "apr_hash.h"
  26. #define APR_WANT_STRFUNC
  27. #include "apr_want.h"
  28. #include "ap_config.h"
  29. #include "httpd.h"
  30. #include "http_config.h"
  31. #include "http_log.h"
  32. #include "http_request.h"
  33. #include "http_protocol.h"
  34. /* XXXX - fix me / EBCDIC
  35. * there was a cludge here which would use its
  36. * own version apr_isascii(). Indicating that
  37. * on some platforms that might be needed.
  38. *
  39. * #define OS_ASC(c) (c) -- for mere mortals
  40. * or
  41. * #define OS_ASC(c) (ebcdic2ascii[c]) -- for dino's
  42. *
  43. * #define apr_isascii(c) ((OS_ASC(c) & 0x80) == 0)
  44. */
  45. /* XXXXX - fix me - See note with NOT_PROXY
  46. */
  47. typedef struct attrib_info {
  48. char *name;
  49. int offset;
  50. } attrib_info;
  51. /* Information to which an extension can be mapped
  52. */
  53. typedef struct extension_info {
  54. char *forced_type; /* Additional AddTyped stuff */
  55. char *encoding_type; /* Added with AddEncoding... */
  56. char *language_type; /* Added with AddLanguage... */
  57. char *handler; /* Added with AddHandler... */
  58. char *charset_type; /* Added with AddCharset... */
  59. char *input_filters; /* Added with AddInputFilter... */
  60. char *output_filters; /* Added with AddOutputFilter... */
  61. } extension_info;
  62. #define MULTIMATCH_UNSET 0
  63. #define MULTIMATCH_ANY 1
  64. #define MULTIMATCH_NEGOTIATED 2
  65. #define MULTIMATCH_HANDLERS 4
  66. #define MULTIMATCH_FILTERS 8
  67. typedef struct {
  68. apr_hash_t *extension_mappings; /* Map from extension name to
  69. * extension_info structure */
  70. apr_array_header_t *remove_mappings; /* A simple list, walked once */
  71. char *default_language; /* Language if no AddLanguage ext found */
  72. int multimatch; /* Extensions to include in multiview matching
  73. * for filenames, e.g. Filters and Handlers
  74. */
  75. int use_path_info; /* If set to 0, only use filename.
  76. * If set to 1, append PATH_INFO to filename for
  77. * lookups.
  78. * If set to 2, this value is unset and is
  79. * effectively 0.
  80. */
  81. } mime_dir_config;
  82. typedef struct param_s {
  83. char *attr;
  84. char *val;
  85. struct param_s *next;
  86. } param;
  87. typedef struct {
  88. const char *type;
  89. apr_size_t type_len;
  90. const char *subtype;
  91. apr_size_t subtype_len;
  92. param *param;
  93. } content_type;
  94. static char tspecial[] = {
  95. '(', ')', '<', '>', '@', ',', ';', ':',
  96. '\\', '"', '/', '[', ']', '?', '=',
  97. '\0'
  98. };
  99. module AP_MODULE_DECLARE_DATA mime_module;
  100. static void *create_mime_dir_config(apr_pool_t *p, char *dummy)
  101. {
  102. mime_dir_config *new = apr_palloc(p, sizeof(mime_dir_config));
  103. new->extension_mappings = NULL;
  104. new->remove_mappings = NULL;
  105. new->default_language = NULL;
  106. new->multimatch = MULTIMATCH_UNSET;
  107. new->use_path_info = 2;
  108. return new;
  109. }
  110. /*
  111. * Overlay one hash table of extension_mappings onto another
  112. */
  113. static void *overlay_extension_mappings(apr_pool_t *p,
  114. const void *key,
  115. apr_ssize_t klen,
  116. const void *overlay_val,
  117. const void *base_val,
  118. const void *data)
  119. {
  120. const extension_info *overlay_info = (const extension_info *)overlay_val;
  121. const extension_info *base_info = (const extension_info *)base_val;
  122. extension_info *new_info = apr_pmemdup(p, base_info, sizeof(extension_info));
  123. if (overlay_info->forced_type) {
  124. new_info->forced_type = overlay_info->forced_type;
  125. }
  126. if (overlay_info->encoding_type) {
  127. new_info->encoding_type = overlay_info->encoding_type;
  128. }
  129. if (overlay_info->language_type) {
  130. new_info->language_type = overlay_info->language_type;
  131. }
  132. if (overlay_info->handler) {
  133. new_info->handler = overlay_info->handler;
  134. }
  135. if (overlay_info->charset_type) {
  136. new_info->charset_type = overlay_info->charset_type;
  137. }
  138. if (overlay_info->input_filters) {
  139. new_info->input_filters = overlay_info->input_filters;
  140. }
  141. if (overlay_info->output_filters) {
  142. new_info->output_filters = overlay_info->output_filters;
  143. }
  144. return new_info;
  145. }
  146. /* Member is the offset within an extension_info of the pointer to reset
  147. */
  148. static void remove_items(apr_pool_t *p, apr_array_header_t *remove,
  149. apr_hash_t *mappings)
  150. {
  151. attrib_info *suffix = (attrib_info *) remove->elts;
  152. int i;
  153. for (i = 0; i < remove->nelts; i++) {
  154. extension_info *exinfo = apr_hash_get(mappings,
  155. suffix[i].name,
  156. APR_HASH_KEY_STRING);
  157. if (exinfo && *(const char**)((char *)exinfo + suffix[i].offset)) {
  158. extension_info *copyinfo = exinfo;
  159. exinfo = (extension_info*)apr_palloc(p, sizeof(*exinfo));
  160. apr_hash_set(mappings, suffix[i].name,
  161. APR_HASH_KEY_STRING, exinfo);
  162. memcpy(exinfo, copyinfo, sizeof(*exinfo));
  163. *(const char**)((char *)exinfo + suffix[i].offset) = NULL;
  164. }
  165. }
  166. }
  167. static void *merge_mime_dir_configs(apr_pool_t *p, void *basev, void *addv)
  168. {
  169. mime_dir_config *base = (mime_dir_config *)basev;
  170. mime_dir_config *add = (mime_dir_config *)addv;
  171. mime_dir_config *new = apr_palloc(p, sizeof(mime_dir_config));
  172. if (base->extension_mappings && add->extension_mappings) {
  173. new->extension_mappings = apr_hash_merge(p, add->extension_mappings,
  174. base->extension_mappings,
  175. overlay_extension_mappings,
  176. NULL);
  177. }
  178. else {
  179. if (base->extension_mappings == NULL) {
  180. new->extension_mappings = add->extension_mappings;
  181. }
  182. else {
  183. new->extension_mappings = base->extension_mappings;
  184. }
  185. /* We may not be merging the tables, but if we potentially will change
  186. * an exinfo member, then we are about to trounce it anyways.
  187. * We must have a copy for safety.
  188. */
  189. if (new->extension_mappings && add->remove_mappings) {
  190. new->extension_mappings =
  191. apr_hash_copy(p, new->extension_mappings);
  192. }
  193. }
  194. if (new->extension_mappings) {
  195. if (add->remove_mappings)
  196. remove_items(p, add->remove_mappings, new->extension_mappings);
  197. }
  198. new->remove_mappings = NULL;
  199. new->default_language = add->default_language ?
  200. add->default_language : base->default_language;
  201. new->multimatch = (add->multimatch != MULTIMATCH_UNSET) ?
  202. add->multimatch : base->multimatch;
  203. if ((add->use_path_info & 2) == 0) {
  204. new->use_path_info = add->use_path_info;
  205. }
  206. else {
  207. new->use_path_info = base->use_path_info;
  208. }
  209. return new;
  210. }
  211. static const char *add_extension_info(cmd_parms *cmd, void *m_,
  212. const char *value_, const char* ext)
  213. {
  214. mime_dir_config *m=m_;
  215. extension_info *exinfo;
  216. int offset = (int) (long) cmd->info;
  217. char *key = apr_pstrdup(cmd->temp_pool, ext);
  218. char *value = apr_pstrdup(cmd->pool, value_);
  219. ap_str_tolower(value);
  220. ap_str_tolower(key);
  221. if (*key == '.') {
  222. ++key;
  223. }
  224. if (!m->extension_mappings) {
  225. m->extension_mappings = apr_hash_make(cmd->pool);
  226. exinfo = NULL;
  227. }
  228. else {
  229. exinfo = (extension_info*)apr_hash_get(m->extension_mappings, key,
  230. APR_HASH_KEY_STRING);
  231. }
  232. if (!exinfo) {
  233. exinfo = apr_pcalloc(cmd->pool, sizeof(extension_info));
  234. key = apr_pstrdup(cmd->pool, key);
  235. apr_hash_set(m->extension_mappings, key, APR_HASH_KEY_STRING, exinfo);
  236. }
  237. *(const char**)((char *)exinfo + offset) = value;
  238. return NULL;
  239. }
  240. /*
  241. * As RemoveType should also override the info from TypesConfig, we add an
  242. * empty string as type instead of actually removing the type.
  243. */
  244. static const char *remove_extension_type(cmd_parms *cmd, void *m_,
  245. const char *ext)
  246. {
  247. return add_extension_info(cmd, m_, "", ext);
  248. }
  249. /*
  250. * Note handler names are un-added with each per_dir_config merge.
  251. * This keeps the association from being inherited, but not
  252. * from being re-added at a subordinate level.
  253. */
  254. static const char *remove_extension_info(cmd_parms *cmd, void *m_,
  255. const char *ext)
  256. {
  257. mime_dir_config *m = (mime_dir_config *) m_;
  258. attrib_info *suffix;
  259. if (*ext == '.') {
  260. ++ext;
  261. }
  262. if (!m->remove_mappings) {
  263. m->remove_mappings = apr_array_make(cmd->pool, 4, sizeof(*suffix));
  264. }
  265. suffix = (attrib_info *)apr_array_push(m->remove_mappings);
  266. suffix->name = apr_pstrdup(cmd->pool, ext);
  267. ap_str_tolower(suffix->name);
  268. suffix->offset = (int) (long) cmd->info;
  269. return NULL;
  270. }
  271. /* The sole bit of server configuration that the MIME module has is
  272. * the name of its config file, so...
  273. */
  274. static const char *set_types_config(cmd_parms *cmd, void *dummy,
  275. const char *arg)
  276. {
  277. ap_set_module_config(cmd->server->module_config, &mime_module,
  278. (void *)arg);
  279. return NULL;
  280. }
  281. static const char *multiviews_match(cmd_parms *cmd, void *m_,
  282. const char *include)
  283. {
  284. mime_dir_config *m = (mime_dir_config *) m_;
  285. const char *errmsg;
  286. errmsg = ap_check_cmd_context(cmd, NOT_IN_LOCATION);
  287. if (errmsg != NULL) {
  288. return errmsg;
  289. }
  290. if (strcasecmp(include, "Any") == 0) {
  291. if (m->multimatch && (m->multimatch & ~MULTIMATCH_ANY)) {
  292. return "Any is incompatible with NegotiatedOnly, "
  293. "Filters and Handlers";
  294. }
  295. m->multimatch |= MULTIMATCH_ANY;
  296. }
  297. else if (strcasecmp(include, "NegotiatedOnly") == 0) {
  298. if (m->multimatch && (m->multimatch & ~MULTIMATCH_NEGOTIATED)) {
  299. return "NegotiatedOnly is incompatible with Any, "
  300. "Filters and Handlers";
  301. }
  302. m->multimatch |= MULTIMATCH_NEGOTIATED;
  303. }
  304. else if (strcasecmp(include, "Filters") == 0) {
  305. if (m->multimatch && (m->multimatch & (MULTIMATCH_NEGOTIATED
  306. | MULTIMATCH_ANY))) {
  307. return "Filters is incompatible with Any and NegotiatedOnly";
  308. }
  309. m->multimatch |= MULTIMATCH_FILTERS;
  310. }
  311. else if (strcasecmp(include, "Handlers") == 0) {
  312. if (m->multimatch && (m->multimatch & (MULTIMATCH_NEGOTIATED
  313. | MULTIMATCH_ANY))) {
  314. return "Handlers is incompatible with Any and NegotiatedOnly";
  315. }
  316. m->multimatch |= MULTIMATCH_HANDLERS;
  317. }
  318. else {
  319. return apr_psprintf(cmd->pool, "Unrecognized option '%s'", include);
  320. }
  321. return NULL;
  322. }
  323. static const command_rec mime_cmds[] =
  324. {
  325. AP_INIT_ITERATE2("AddCharset", add_extension_info,
  326. (void *)APR_OFFSETOF(extension_info, charset_type), OR_FILEINFO,
  327. "a charset (e.g., iso-2022-jp), followed by one or more "
  328. "file extensions"),
  329. AP_INIT_ITERATE2("AddEncoding", add_extension_info,
  330. (void *)APR_OFFSETOF(extension_info, encoding_type), OR_FILEINFO,
  331. "an encoding (e.g., gzip), followed by one or more file extensions"),
  332. AP_INIT_ITERATE2("AddHandler", add_extension_info,
  333. (void *)APR_OFFSETOF(extension_info, handler), OR_FILEINFO,
  334. "a handler name followed by one or more file extensions"),
  335. AP_INIT_ITERATE2("AddInputFilter", add_extension_info,
  336. (void *)APR_OFFSETOF(extension_info, input_filters), OR_FILEINFO,
  337. "input filter name (or ; delimited names) followed by one or "
  338. "more file extensions"),
  339. AP_INIT_ITERATE2("AddLanguage", add_extension_info,
  340. (void *)APR_OFFSETOF(extension_info, language_type), OR_FILEINFO,
  341. "a language (e.g., fr), followed by one or more file extensions"),
  342. AP_INIT_ITERATE2("AddOutputFilter", add_extension_info,
  343. (void *)APR_OFFSETOF(extension_info, output_filters), OR_FILEINFO,
  344. "output filter name (or ; delimited names) followed by one or "
  345. "more file extensions"),
  346. AP_INIT_ITERATE2("AddType", add_extension_info,
  347. (void *)APR_OFFSETOF(extension_info, forced_type), OR_FILEINFO,
  348. "a mime type followed by one or more file extensions"),
  349. AP_INIT_TAKE1("DefaultLanguage", ap_set_string_slot,
  350. (void*)APR_OFFSETOF(mime_dir_config, default_language), OR_FILEINFO,
  351. "language to use for documents with no other language file extension"),
  352. AP_INIT_ITERATE("MultiviewsMatch", multiviews_match, NULL, OR_FILEINFO,
  353. "NegotiatedOnly (default), Handlers and/or Filters, or Any"),
  354. AP_INIT_ITERATE("RemoveCharset", remove_extension_info,
  355. (void *)APR_OFFSETOF(extension_info, charset_type), OR_FILEINFO,
  356. "one or more file extensions"),
  357. AP_INIT_ITERATE("RemoveEncoding", remove_extension_info,
  358. (void *)APR_OFFSETOF(extension_info, encoding_type), OR_FILEINFO,
  359. "one or more file extensions"),
  360. AP_INIT_ITERATE("RemoveHandler", remove_extension_info,
  361. (void *)APR_OFFSETOF(extension_info, handler), OR_FILEINFO,
  362. "one or more file extensions"),
  363. AP_INIT_ITERATE("RemoveInputFilter", remove_extension_info,
  364. (void *)APR_OFFSETOF(extension_info, input_filters), OR_FILEINFO,
  365. "one or more file extensions"),
  366. AP_INIT_ITERATE("RemoveLanguage", remove_extension_info,
  367. (void *)APR_OFFSETOF(extension_info, language_type), OR_FILEINFO,
  368. "one or more file extensions"),
  369. AP_INIT_ITERATE("RemoveOutputFilter", remove_extension_info,
  370. (void *)APR_OFFSETOF(extension_info, output_filters), OR_FILEINFO,
  371. "one or more file extensions"),
  372. AP_INIT_ITERATE("RemoveType", remove_extension_type,
  373. (void *)APR_OFFSETOF(extension_info, forced_type), OR_FILEINFO,
  374. "one or more file extensions"),
  375. AP_INIT_TAKE1("TypesConfig", set_types_config, NULL, RSRC_CONF,
  376. "the MIME types config file"),
  377. AP_INIT_FLAG("ModMimeUsePathInfo", ap_set_flag_slot,
  378. (void *)APR_OFFSETOF(mime_dir_config, use_path_info), ACCESS_CONF,
  379. "Set to 'yes' to allow mod_mime to use path info for type checking"),
  380. {NULL}
  381. };
  382. static apr_hash_t *mime_type_extensions;
  383. static int mime_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
  384. {
  385. ap_configfile_t *f;
  386. char l[MAX_STRING_LEN];
  387. const char *types_confname = ap_get_module_config(s->module_config,
  388. &mime_module);
  389. apr_status_t status;
  390. if (!types_confname) {
  391. types_confname = AP_TYPES_CONFIG_FILE;
  392. }
  393. types_confname = ap_server_root_relative(p, types_confname);
  394. if (!types_confname) {
  395. ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s,
  396. "Invalid mime types config path %s",
  397. (const char *)ap_get_module_config(s->module_config,
  398. &mime_module));
  399. return HTTP_INTERNAL_SERVER_ERROR;
  400. }
  401. if ((status = ap_pcfg_openfile(&f, ptemp, types_confname))
  402. != APR_SUCCESS) {
  403. ap_log_error(APLOG_MARK, APLOG_ERR, status, s,
  404. "could not open mime types config file %s.",
  405. types_confname);
  406. return HTTP_INTERNAL_SERVER_ERROR;
  407. }
  408. mime_type_extensions = apr_hash_make(p);
  409. while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
  410. const char *ll = l, *ct;
  411. if (l[0] == '#') {
  412. continue;
  413. }
  414. ct = ap_getword_conf(p, &ll);
  415. while (ll[0]) {
  416. char *ext = ap_getword_conf(p, &ll);
  417. ap_str_tolower(ext);
  418. apr_hash_set(mime_type_extensions, ext, APR_HASH_KEY_STRING, ct);
  419. }
  420. }
  421. ap_cfg_closefile(f);
  422. return OK;
  423. }
  424. static const char *zap_sp(const char *s)
  425. {
  426. if (s == NULL) {
  427. return (NULL);
  428. }
  429. if (*s == '\0') {
  430. return (s);
  431. }
  432. /* skip prefixed white space */
  433. for (; *s == ' ' || *s == '\t' || *s == '\n'; s++)
  434. ;
  435. return (s);
  436. }
  437. static char *zap_sp_and_dup(apr_pool_t *p, const char *start,
  438. const char *end, apr_size_t *len)
  439. {
  440. while ((start < end) && apr_isspace(*start)) {
  441. start++;
  442. }
  443. while ((end > start) && apr_isspace(*(end - 1))) {
  444. end--;
  445. }
  446. if (len) {
  447. *len = end - start;
  448. }
  449. return apr_pstrmemdup(p, start, end - start);
  450. }
  451. static int is_token(char c)
  452. {
  453. int res;
  454. res = (apr_isascii(c) && apr_isgraph(c)
  455. && (strchr(tspecial, c) == NULL)) ? 1 : -1;
  456. return res;
  457. }
  458. static int is_qtext(char c)
  459. {
  460. int res;
  461. res = (apr_isascii(c) && (c != '"') && (c != '\\') && (c != '\n'))
  462. ? 1 : -1;
  463. return res;
  464. }
  465. static int is_quoted_pair(const char *s)
  466. {
  467. int res = -1;
  468. int c;
  469. if (((s + 1) != NULL) && (*s == '\\')) {
  470. c = (int) *(s + 1);
  471. if (apr_isascii(c)) {
  472. res = 1;
  473. }
  474. }
  475. return (res);
  476. }
  477. static content_type *analyze_ct(request_rec *r, const char *s)
  478. {
  479. const char *cp, *mp;
  480. char *attribute, *value;
  481. int quoted = 0;
  482. server_rec * ss = r->server;
  483. apr_pool_t * p = r->pool;
  484. content_type *ctp;
  485. param *pp, *npp;
  486. /* initialize ctp */
  487. ctp = (content_type *)apr_palloc(p, sizeof(content_type));
  488. ctp->type = NULL;
  489. ctp->subtype = NULL;
  490. ctp->param = NULL;
  491. mp = s;
  492. /* getting a type */
  493. cp = mp;
  494. while (apr_isspace(*cp)) {
  495. cp++;
  496. }
  497. if (!*cp) {
  498. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  499. "mod_mime: analyze_ct: cannot get media type from '%s'",
  500. (const char *) mp);
  501. return (NULL);
  502. }
  503. ctp->type = cp;
  504. do {
  505. cp++;
  506. } while (*cp && (*cp != '/') && !apr_isspace(*cp) && (*cp != ';'));
  507. if (!*cp || (*cp == ';')) {
  508. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  509. "Cannot get media type from '%s'",
  510. (const char *) mp);
  511. return (NULL);
  512. }
  513. while (apr_isspace(*cp)) {
  514. cp++;
  515. }
  516. if (*cp != '/') {
  517. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  518. "mod_mime: analyze_ct: cannot get media type from '%s'",
  519. (const char *) mp);
  520. return (NULL);
  521. }
  522. ctp->type_len = cp - ctp->type;
  523. cp++; /* skip the '/' */
  524. /* getting a subtype */
  525. while (apr_isspace(*cp)) {
  526. cp++;
  527. }
  528. if (!*cp) {
  529. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  530. "Cannot get media subtype.");
  531. return (NULL);
  532. }
  533. ctp->subtype = cp;
  534. do {
  535. cp++;
  536. } while (*cp && !apr_isspace(*cp) && (*cp != ';'));
  537. ctp->subtype_len = cp - ctp->subtype;
  538. while (apr_isspace(*cp)) {
  539. cp++;
  540. }
  541. if (*cp == '\0') {
  542. return (ctp);
  543. }
  544. /* getting parameters */
  545. cp++; /* skip the ';' */
  546. cp = zap_sp(cp);
  547. if (cp == NULL || *cp == '\0') {
  548. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  549. "Cannot get media parameter.");
  550. return (NULL);
  551. }
  552. mp = cp;
  553. attribute = NULL;
  554. value = NULL;
  555. while (cp != NULL && *cp != '\0') {
  556. if (attribute == NULL) {
  557. if (is_token(*cp) > 0) {
  558. cp++;
  559. continue;
  560. }
  561. else if (*cp == ' ' || *cp == '\t' || *cp == '\n') {
  562. cp++;
  563. continue;
  564. }
  565. else if (*cp == '=') {
  566. attribute = zap_sp_and_dup(p, mp, cp, NULL);
  567. if (attribute == NULL || *attribute == '\0') {
  568. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  569. "Cannot get media parameter.");
  570. return (NULL);
  571. }
  572. cp++;
  573. cp = zap_sp(cp);
  574. if (cp == NULL || *cp == '\0') {
  575. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  576. "Cannot get media parameter.");
  577. return (NULL);
  578. }
  579. mp = cp;
  580. continue;
  581. }
  582. else {
  583. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  584. "Cannot get media parameter.");
  585. return (NULL);
  586. }
  587. }
  588. else {
  589. if (mp == cp) {
  590. if (*cp == '"') {
  591. quoted = 1;
  592. cp++;
  593. }
  594. else {
  595. quoted = 0;
  596. }
  597. }
  598. if (quoted > 0) {
  599. while (quoted && *cp != '\0') {
  600. if (is_qtext(*cp) > 0) {
  601. cp++;
  602. }
  603. else if (is_quoted_pair(cp) > 0) {
  604. cp += 2;
  605. }
  606. else if (*cp == '"') {
  607. cp++;
  608. while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
  609. cp++;
  610. }
  611. if (*cp != ';' && *cp != '\0') {
  612. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  613. "Cannot get media parameter.");
  614. return(NULL);
  615. }
  616. quoted = 0;
  617. }
  618. else {
  619. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  620. "Cannot get media parameter.");
  621. return (NULL);
  622. }
  623. }
  624. }
  625. else {
  626. while (1) {
  627. if (is_token(*cp) > 0) {
  628. cp++;
  629. }
  630. else if (*cp == '\0' || *cp == ';') {
  631. break;
  632. }
  633. else {
  634. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  635. "Cannot get media parameter.");
  636. return (NULL);
  637. }
  638. }
  639. }
  640. value = zap_sp_and_dup(p, mp, cp, NULL);
  641. if (value == NULL || *value == '\0') {
  642. ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ss,
  643. "Cannot get media parameter.");
  644. return (NULL);
  645. }
  646. pp = apr_palloc(p, sizeof(param));
  647. pp->attr = attribute;
  648. pp->val = value;
  649. pp->next = NULL;
  650. if (ctp->param == NULL) {
  651. ctp->param = pp;
  652. }
  653. else {
  654. npp = ctp->param;
  655. while (npp->next) {
  656. npp = npp->next;
  657. }
  658. npp->next = pp;
  659. }
  660. quoted = 0;
  661. attribute = NULL;
  662. value = NULL;
  663. if (*cp == '\0') {
  664. break;
  665. }
  666. cp++;
  667. mp = cp;
  668. }
  669. }
  670. return (ctp);
  671. }
  672. /*
  673. * find_ct is the hook routine for determining content-type and other
  674. * MIME-related metadata. It assumes that r->filename has already been
  675. * set and stat has been called for r->finfo. It also assumes that the
  676. * non-path base file name is not the empty string unless it is a dir.
  677. */
  678. static int find_ct(request_rec *r)
  679. {
  680. mime_dir_config *conf;
  681. apr_array_header_t *exception_list;
  682. char *ext;
  683. const char *fn, *type, *charset = NULL, *resource_name;
  684. int found_metadata = 0;
  685. if (r->finfo.filetype == APR_DIR) {
  686. ap_set_content_type(r, DIR_MAGIC_TYPE);
  687. return OK;
  688. }
  689. if (!r->filename) {
  690. return DECLINED;
  691. }
  692. conf = (mime_dir_config *)ap_get_module_config(r->per_dir_config,
  693. &mime_module);
  694. exception_list = apr_array_make(r->pool, 2, sizeof(char *));
  695. /* If use_path_info is explicitly set to on (value & 1 == 1), append. */
  696. if (conf->use_path_info & 1) {
  697. resource_name = apr_pstrcat(r->pool, r->filename, r->path_info, NULL);
  698. }
  699. else {
  700. resource_name = r->filename;
  701. }
  702. /* Always drop the path leading up to the file name.
  703. */
  704. if ((fn = ap_strrchr_c(resource_name, '/')) == NULL) {
  705. fn = resource_name;
  706. }
  707. else {
  708. ++fn;
  709. }
  710. /* The exception list keeps track of those filename components that
  711. * are not associated with extensions indicating metadata.
  712. * The base name is always the first exception (i.e., "txt.html" has
  713. * a basename of "txt" even though it might look like an extension).
  714. */
  715. ext = ap_getword(r->pool, &fn, '.');
  716. *((const char **)apr_array_push(exception_list)) = ext;
  717. /* Parse filename extensions which can be in any order
  718. */
  719. while (*fn && (ext = ap_getword(r->pool, &fn, '.'))) {
  720. const extension_info *exinfo = NULL;
  721. int found;
  722. if (*ext == '\0') { /* ignore empty extensions "bad..html" */
  723. continue;
  724. }
  725. found = 0;
  726. ap_str_tolower(ext);
  727. if (conf->extension_mappings != NULL) {
  728. exinfo = (extension_info*)apr_hash_get(conf->extension_mappings,
  729. ext, APR_HASH_KEY_STRING);
  730. }
  731. if (exinfo == NULL || !exinfo->forced_type) {
  732. if ((type = apr_hash_get(mime_type_extensions, ext,
  733. APR_HASH_KEY_STRING)) != NULL) {
  734. ap_set_content_type(r, (char*) type);
  735. found = 1;
  736. }
  737. }
  738. if (exinfo != NULL) {
  739. /* empty string is treated as special case for RemoveType */
  740. if (exinfo->forced_type && *exinfo->forced_type) {
  741. ap_set_content_type(r, exinfo->forced_type);
  742. found = 1;
  743. }
  744. if (exinfo->charset_type) {
  745. charset = exinfo->charset_type;
  746. found = 1;
  747. }
  748. if (exinfo->language_type) {
  749. if (!r->content_languages) {
  750. r->content_languages = apr_array_make(r->pool, 2,
  751. sizeof(char *));
  752. }
  753. *((const char **)apr_array_push(r->content_languages))
  754. = exinfo->language_type;
  755. found = 1;
  756. }
  757. if (exinfo->encoding_type) {
  758. if (!r->content_encoding) {
  759. r->content_encoding = exinfo->encoding_type;
  760. }
  761. else {
  762. /* XXX should eliminate duplicate entities
  763. *
  764. * ah no. Order is important and double encoding is neither
  765. * forbidden nor impossible. -- nd
  766. */
  767. r->content_encoding = apr_pstrcat(r->pool,
  768. r->content_encoding,
  769. ", ",
  770. exinfo->encoding_type,
  771. NULL);
  772. }
  773. found = 1;
  774. }
  775. /* The following extensions are not 'Found'. That is, they don't
  776. * make any contribution to metadata negotation, so they must have
  777. * been explicitly requested by name.
  778. */
  779. if (exinfo->handler && r->proxyreq == PROXYREQ_NONE) {
  780. r->handler = exinfo->handler;
  781. if (conf->multimatch & MULTIMATCH_HANDLERS) {
  782. found = 1;
  783. }
  784. }
  785. /* XXX Two significant problems; 1, we don't check to see if we are
  786. * setting redundant filters. 2, we insert these in the types config
  787. * hook, which may be too early (dunno.)
  788. */
  789. if (exinfo->input_filters && r->proxyreq == PROXYREQ_NONE) {
  790. const char *filter, *filters = exinfo->input_filters;
  791. while (*filters
  792. && (filter = ap_getword(r->pool, &filters, ';'))) {
  793. ap_add_input_filter(filter, NULL, r, r->connection);
  794. }
  795. if (conf->multimatch & MULTIMATCH_FILTERS) {
  796. found = 1;
  797. }
  798. }
  799. if (exinfo->output_filters && r->proxyreq == PROXYREQ_NONE) {
  800. const char *filter, *filters = exinfo->output_filters;
  801. while (*filters
  802. && (filter = ap_getword(r->pool, &filters, ';'))) {
  803. ap_add_output_filter(filter, NULL, r, r->connection);
  804. }
  805. if (conf->multimatch & MULTIMATCH_FILTERS) {
  806. found = 1;
  807. }
  808. }
  809. }
  810. if (found || (conf->multimatch & MULTIMATCH_ANY)) {
  811. found_metadata = 1;
  812. }
  813. else {
  814. *((const char **) apr_array_push(exception_list)) = ext;
  815. }
  816. }
  817. /*
  818. * Need to set a notes entry on r for unrecognized elements.
  819. * Somebody better claim them! If we did absolutely nothing,
  820. * skip the notes to alert mod_negotiation we are clueless.
  821. */
  822. if (found_metadata) {
  823. apr_table_setn(r->notes, "ap-mime-exceptions-list",
  824. (void *)exception_list);
  825. }
  826. if (r->content_type) {
  827. content_type *ctp;
  828. int override = 0;
  829. if ((ctp = analyze_ct(r, r->content_type))) {
  830. param *pp = ctp->param;
  831. char *base_content_type = apr_palloc(r->pool, ctp->type_len +
  832. ctp->subtype_len +
  833. sizeof("/"));
  834. char *tmp = base_content_type;
  835. memcpy(tmp, ctp->type, ctp->type_len);
  836. tmp += ctp->type_len;
  837. *tmp++ = '/';
  838. memcpy(tmp, ctp->subtype, ctp->subtype_len);
  839. tmp += ctp->subtype_len;
  840. *tmp = 0;
  841. ap_set_content_type(r, base_content_type);
  842. while (pp != NULL) {
  843. if (charset && !strcmp(pp->attr, "charset")) {
  844. if (!override) {
  845. ap_set_content_type(r,
  846. apr_pstrcat(r->pool,
  847. r->content_type,
  848. "; charset=",
  849. charset,
  850. NULL));
  851. override = 1;
  852. }
  853. }
  854. else {
  855. ap_set_content_type(r,
  856. apr_pstrcat(r->pool,
  857. r->content_type,
  858. "; ", pp->attr,
  859. "=", pp->val,
  860. NULL));
  861. }
  862. pp = pp->next;
  863. }
  864. if (charset && !override) {
  865. ap_set_content_type(r, apr_pstrcat(r->pool, r->content_type,
  866. "; charset=", charset,
  867. NULL));
  868. }
  869. }
  870. }
  871. /* Set default language, if none was specified by the extensions
  872. * and we have a DefaultLanguage setting in force
  873. */
  874. if (!r->content_languages && conf->default_language) {
  875. const char **new;
  876. if (!r->content_languages) {
  877. r->content_languages = apr_array_make(r->pool, 2, sizeof(char *));
  878. }
  879. new = (const char **)apr_array_push(r->content_languages);
  880. *new = conf->default_language;
  881. }
  882. if (!r->content_type) {
  883. return DECLINED;
  884. }
  885. return OK;
  886. }
  887. static void register_hooks(apr_pool_t *p)
  888. {
  889. ap_hook_post_config(mime_post_config,NULL,NULL,APR_HOOK_MIDDLE);
  890. ap_hook_type_checker(find_ct,NULL,NULL,APR_HOOK_MIDDLE);
  891. /*
  892. * this hook seems redundant ... is there any reason a type checker isn't
  893. * allowed to do this already? I'd think that fixups in general would be
  894. * the last opportunity to get the filters right.
  895. * ap_hook_insert_filter(mime_insert_filters,NULL,NULL,APR_HOOK_MIDDLE);
  896. */
  897. }
  898. module AP_MODULE_DECLARE_DATA mime_module = {
  899. STANDARD20_MODULE_STUFF,
  900. create_mime_dir_config, /* create per-directory config structure */
  901. merge_mime_dir_configs, /* merge per-directory config structures */
  902. NULL, /* create per-server config structure */
  903. NULL, /* merge per-server config structures */
  904. mime_cmds, /* command apr_table_t */
  905. register_hooks /* register hooks */
  906. };