mod_cern_meta.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  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. * mod_cern_meta.c
  18. * version 0.1.0
  19. * status beta
  20. *
  21. * Andrew Wilson <Andrew.Wilson@cm.cf.ac.uk> 25.Jan.96
  22. *
  23. * *** IMPORTANT ***
  24. * This version of mod_cern_meta.c controls Meta File behaviour on a
  25. * per-directory basis. Previous versions of the module defined behaviour
  26. * on a per-server basis. The upshot is that you'll need to revisit your
  27. * configuration files in order to make use of the new module.
  28. * ***
  29. *
  30. * Emulate the CERN HTTPD Meta file semantics. Meta files are HTTP
  31. * headers that can be output in addition to the normal range of
  32. * headers for each file accessed. They appear rather like the Apache
  33. * .asis files, and are able to provide a crude way of influencing
  34. * the Expires: header, as well as providing other curiosities.
  35. * There are many ways to manage meta information, this one was
  36. * chosen because there is already a large number of CERN users
  37. * who can exploit this module. It should be noted that there are probably
  38. * more sensitive ways of managing the Expires: header specifically.
  39. *
  40. * The module obeys the following directives, which can appear
  41. * in the server's .conf files and in .htaccess files.
  42. *
  43. * MetaFiles <on|off>
  44. *
  45. * turns on|off meta file processing for any directory.
  46. * Default value is off
  47. *
  48. * # turn on MetaFiles in this directory
  49. * MetaFiles on
  50. *
  51. * MetaDir <directory name>
  52. *
  53. * specifies the name of the directory in which Apache can find
  54. * meta information files. The directory is usually a 'hidden'
  55. * subdirectory of the directory that contains the file being
  56. * accessed. eg:
  57. *
  58. * # .meta files are in the *same* directory as the
  59. * # file being accessed
  60. * MetaDir .
  61. *
  62. * the default is to look in a '.web' subdirectory. This is the
  63. * same as for CERN 3.+ webservers and behaviour is the same as
  64. * for the directive:
  65. *
  66. * MetaDir .web
  67. *
  68. * MetaSuffix <meta file suffix>
  69. *
  70. * specifies the file name suffix for the file containing the
  71. * meta information. eg:
  72. *
  73. * # our meta files are suffixed with '.cern_meta'
  74. * MetaSuffix .cern_meta
  75. *
  76. * the default is to look for files with the suffix '.meta'. This
  77. * behaviour is the same as for the directive:
  78. *
  79. * MetaSuffix .meta
  80. *
  81. * When accessing the file
  82. *
  83. * DOCUMENT_ROOT/somedir/index.html
  84. *
  85. * this module will look for the file
  86. *
  87. * DOCUMENT_ROOT/somedir/.web/index.html.meta
  88. *
  89. * and will use its contents to generate additional MIME header
  90. * information.
  91. *
  92. * For more information on the CERN Meta file semantics see:
  93. *
  94. * http://www.w3.org/hypertext/WWW/Daemon/User/Config/General.html#MetaDir
  95. *
  96. * Change-log:
  97. * 29.Jan.96 pfopen/pfclose instead of fopen/fclose
  98. * DECLINE when real file not found, we may be checking each
  99. * of the index.html/index.shtml/index.htm variants and don't
  100. * need to report missing ones as spurious errors.
  101. * 31.Jan.96 log_error reports about a malformed .meta file, rather
  102. * than a script error.
  103. * 20.Jun.96 MetaFiles <on|off> default off, added, so that module
  104. * can be configured per-directory. Prior to this the module
  105. * was running for each request anywhere on the server, naughty..
  106. * 29.Jun.96 All directives made per-directory.
  107. */
  108. #include "apr.h"
  109. #include "apr_strings.h"
  110. #define APR_WANT_STRFUNC
  111. #include "apr_want.h"
  112. #if APR_HAVE_SYS_TYPES_H
  113. #include <sys/types.h>
  114. #endif
  115. #include "ap_config.h"
  116. #include "httpd.h"
  117. #include "http_config.h"
  118. #include "util_script.h"
  119. #include "http_log.h"
  120. #include "http_request.h"
  121. #include "http_protocol.h"
  122. #include "apr_lib.h"
  123. #define DIR_CMD_PERMS OR_INDEXES
  124. #define DEFAULT_METADIR ".web"
  125. #define DEFAULT_METASUFFIX ".meta"
  126. #define DEFAULT_METAFILES 0
  127. module AP_MODULE_DECLARE_DATA cern_meta_module;
  128. typedef struct {
  129. const char *metadir;
  130. const char *metasuffix;
  131. int metafiles;
  132. } cern_meta_dir_config;
  133. static void *create_cern_meta_dir_config(apr_pool_t *p, char *dummy)
  134. {
  135. cern_meta_dir_config *new =
  136. (cern_meta_dir_config *) apr_palloc(p, sizeof(cern_meta_dir_config));
  137. new->metadir = NULL;
  138. new->metasuffix = NULL;
  139. new->metafiles = DEFAULT_METAFILES;
  140. return new;
  141. }
  142. static void *merge_cern_meta_dir_configs(apr_pool_t *p, void *basev, void *addv)
  143. {
  144. cern_meta_dir_config *base = (cern_meta_dir_config *) basev;
  145. cern_meta_dir_config *add = (cern_meta_dir_config *) addv;
  146. cern_meta_dir_config *new =
  147. (cern_meta_dir_config *) apr_palloc(p, sizeof(cern_meta_dir_config));
  148. new->metadir = add->metadir ? add->metadir : base->metadir;
  149. new->metasuffix = add->metasuffix ? add->metasuffix : base->metasuffix;
  150. new->metafiles = add->metafiles;
  151. return new;
  152. }
  153. static const char *set_metadir(cmd_parms *parms, void *in_dconf, const char *arg)
  154. {
  155. cern_meta_dir_config *dconf = in_dconf;
  156. dconf->metadir = arg;
  157. return NULL;
  158. }
  159. static const char *set_metasuffix(cmd_parms *parms, void *in_dconf, const char *arg)
  160. {
  161. cern_meta_dir_config *dconf = in_dconf;
  162. dconf->metasuffix = arg;
  163. return NULL;
  164. }
  165. static const char *set_metafiles(cmd_parms *parms, void *in_dconf, int arg)
  166. {
  167. cern_meta_dir_config *dconf = in_dconf;
  168. dconf->metafiles = arg;
  169. return NULL;
  170. }
  171. static const command_rec cern_meta_cmds[] =
  172. {
  173. AP_INIT_FLAG("MetaFiles", set_metafiles, NULL, DIR_CMD_PERMS,
  174. "Limited to 'on' or 'off'"),
  175. AP_INIT_TAKE1("MetaDir", set_metadir, NULL, DIR_CMD_PERMS,
  176. "the name of the directory containing meta files"),
  177. AP_INIT_TAKE1("MetaSuffix", set_metasuffix, NULL, DIR_CMD_PERMS,
  178. "the filename suffix for meta files"),
  179. {NULL}
  180. };
  181. /* XXX: this is very similar to ap_scan_script_header_err_core...
  182. * are the differences deliberate, or just a result of bit rot?
  183. */
  184. static int scan_meta_file(request_rec *r, apr_file_t *f)
  185. {
  186. char w[MAX_STRING_LEN];
  187. char *l;
  188. int p;
  189. apr_table_t *tmp_headers;
  190. tmp_headers = apr_table_make(r->pool, 5);
  191. while (apr_file_gets(w, MAX_STRING_LEN - 1, f) == APR_SUCCESS) {
  192. /* Delete terminal (CR?)LF */
  193. p = strlen(w);
  194. if (p > 0 && w[p - 1] == '\n') {
  195. if (p > 1 && w[p - 2] == '\015')
  196. w[p - 2] = '\0';
  197. else
  198. w[p - 1] = '\0';
  199. }
  200. if (w[0] == '\0') {
  201. return OK;
  202. }
  203. /* if we see a bogus header don't ignore it. Shout and scream */
  204. if (!(l = strchr(w, ':'))) {
  205. ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  206. "malformed header in meta file: %s", r->filename);
  207. return HTTP_INTERNAL_SERVER_ERROR;
  208. }
  209. *l++ = '\0';
  210. while (*l && apr_isspace(*l))
  211. ++l;
  212. if (!strcasecmp(w, "Content-type")) {
  213. char *tmp;
  214. /* Nuke trailing whitespace */
  215. char *endp = l + strlen(l) - 1;
  216. while (endp > l && apr_isspace(*endp))
  217. *endp-- = '\0';
  218. tmp = apr_pstrdup(r->pool, l);
  219. ap_content_type_tolower(tmp);
  220. ap_set_content_type(r, tmp);
  221. }
  222. else if (!strcasecmp(w, "Status")) {
  223. sscanf(l, "%d", &r->status);
  224. r->status_line = apr_pstrdup(r->pool, l);
  225. }
  226. else {
  227. apr_table_set(tmp_headers, w, l);
  228. }
  229. }
  230. apr_table_overlap(r->headers_out, tmp_headers, APR_OVERLAP_TABLES_SET);
  231. return OK;
  232. }
  233. static int add_cern_meta_data(request_rec *r)
  234. {
  235. char *metafilename;
  236. char *leading_slash;
  237. char *last_slash;
  238. char *real_file;
  239. char *scrap_book;
  240. apr_file_t *f = NULL;
  241. apr_status_t retcode;
  242. cern_meta_dir_config *dconf;
  243. int rv;
  244. request_rec *rr;
  245. dconf = ap_get_module_config(r->per_dir_config, &cern_meta_module);
  246. if (!dconf->metafiles) {
  247. return DECLINED;
  248. };
  249. /* if ./.web/$1.meta exists then output 'asis' */
  250. if (r->finfo.filetype == 0) {
  251. return DECLINED;
  252. };
  253. /* is this a directory? */
  254. if (r->finfo.filetype == APR_DIR || r->uri[strlen(r->uri) - 1] == '/') {
  255. return DECLINED;
  256. };
  257. /* what directory is this file in? */
  258. scrap_book = apr_pstrdup(r->pool, r->filename);
  259. leading_slash = strchr(scrap_book, '/');
  260. last_slash = strrchr(scrap_book, '/');
  261. if ((last_slash != NULL) && (last_slash != leading_slash)) {
  262. /* skip over last slash */
  263. real_file = last_slash;
  264. real_file++;
  265. *last_slash = '\0';
  266. }
  267. else {
  268. /* no last slash, buh?! */
  269. ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  270. "internal error in mod_cern_meta: %s", r->filename);
  271. /* should really barf, but hey, let's be friends... */
  272. return DECLINED;
  273. };
  274. metafilename = apr_pstrcat(r->pool, scrap_book, "/",
  275. dconf->metadir ? dconf->metadir : DEFAULT_METADIR,
  276. "/", real_file,
  277. dconf->metasuffix ? dconf->metasuffix : DEFAULT_METASUFFIX,
  278. NULL);
  279. /* It sucks to require this subrequest to complete, because this
  280. * means people must leave their meta files accessible to the world.
  281. * A better solution might be a "safe open" feature of pfopen to avoid
  282. * pipes, symlinks, and crap like that.
  283. *
  284. * In fact, this doesn't suck. Because <Location > blocks are never run
  285. * against sub_req_lookup_file, the meta can be somewhat protected by
  286. * either masking it with a <Location > directive or alias, or stowing
  287. * the file outside of the web document tree, while providing the
  288. * appropriate directory blocks to allow access to it as a file.
  289. */
  290. rr = ap_sub_req_lookup_file(metafilename, r, NULL);
  291. if (rr->status != HTTP_OK) {
  292. ap_destroy_sub_req(rr);
  293. return DECLINED;
  294. }
  295. ap_destroy_sub_req(rr);
  296. retcode = apr_file_open(&f, metafilename, APR_READ, APR_OS_DEFAULT, r->pool);
  297. if (retcode != APR_SUCCESS) {
  298. if (APR_STATUS_IS_ENOENT(retcode)) {
  299. return DECLINED;
  300. }
  301. ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  302. "meta file permissions deny server access: %s", metafilename);
  303. return HTTP_FORBIDDEN;
  304. };
  305. /* read the headers in */
  306. rv = scan_meta_file(r, f);
  307. apr_file_close(f);
  308. return rv;
  309. }
  310. static void register_hooks(apr_pool_t *p)
  311. {
  312. ap_hook_fixups(add_cern_meta_data,NULL,NULL,APR_HOOK_MIDDLE);
  313. }
  314. module AP_MODULE_DECLARE_DATA cern_meta_module =
  315. {
  316. STANDARD20_MODULE_STUFF,
  317. create_cern_meta_dir_config, /* dir config creater */
  318. merge_cern_meta_dir_configs, /* dir merger --- default is to override */
  319. NULL, /* server config */
  320. NULL, /* merge server configs */
  321. cern_meta_cmds, /* command apr_table_t */
  322. register_hooks /* register hooks */
  323. };