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