diff options
author | hongbotian <hongbo.tianhongbo@huawei.com> | 2015-11-30 03:10:21 -0500 |
---|---|---|
committer | hongbotian <hongbo.tianhongbo@huawei.com> | 2015-11-30 03:10:21 -0500 |
commit | c0b7206652b2852bc574694e7ba07ba1c2acdc00 (patch) | |
tree | 5cb95cb0e19e03610525903df46279df2c3b7eb1 /rubbos/app/httpd-2.0.64/modules/metadata/mod_mime_magic.c | |
parent | b6d3d6e668b793220f2d3af1bc3e828553dc3fe6 (diff) |
delete app
Change-Id: Id4c572809969ebe89e946e88063eaed262cff3f2
Signed-off-by: hongbotian <hongbo.tianhongbo@huawei.com>
Diffstat (limited to 'rubbos/app/httpd-2.0.64/modules/metadata/mod_mime_magic.c')
-rw-r--r-- | rubbos/app/httpd-2.0.64/modules/metadata/mod_mime_magic.c | 2477 |
1 files changed, 0 insertions, 2477 deletions
diff --git a/rubbos/app/httpd-2.0.64/modules/metadata/mod_mime_magic.c b/rubbos/app/httpd-2.0.64/modules/metadata/mod_mime_magic.c deleted file mode 100644 index 329d3898..00000000 --- a/rubbos/app/httpd-2.0.64/modules/metadata/mod_mime_magic.c +++ /dev/null @@ -1,2477 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * mod_mime_magic: MIME type lookup via file magic numbers - * Copyright (c) 1996-1997 Cisco Systems, Inc. - * - * This software was submitted by Cisco Systems to the Apache Software Foundation in July - * 1997. Future revisions and derivatives of this source code must - * acknowledge Cisco Systems as the original contributor of this module. - * All other licensing and usage conditions are those of the Apache Software Foundation. - * - * Some of this code is derived from the free version of the file command - * originally posted to comp.sources.unix. Copyright info for that program - * is included below as required. - * --------------------------------------------------------------------------- - * - Copyright (c) Ian F. Darwin, 1987. Written by Ian F. Darwin. - * - * This software is not subject to any license of the American Telephone and - * Telegraph Company or of the Regents of the University of California. - * - * Permission is granted to anyone to use this software for any purpose on any - * computer system, and to alter it and redistribute it freely, subject to - * the following restrictions: - * - * 1. The author is not responsible for the consequences of use of this - * software, no matter how awful, even if they arise from flaws in it. - * - * 2. The origin of this software must not be misrepresented, either by - * explicit claim or by omission. Since few users ever read sources, credits - * must appear in the documentation. - * - * 3. Altered versions must be plainly marked as such, and must not be - * misrepresented as being the original software. Since few users ever read - * sources, credits must appear in the documentation. - * - * 4. This notice may not be removed or altered. - * ------------------------------------------------------------------------- - * - * For compliance with Mr Darwin's terms: this has been very significantly - * modified from the free "file" command. - * - all-in-one file for compilation convenience when moving from one - * version of Apache to the next. - * - Memory allocation is done through the Apache API's apr_pool_t structure. - * - All functions have had necessary Apache API request or server - * structures passed to them where necessary to call other Apache API - * routines. (i.e. usually for logging, files, or memory allocation in - * itself or a called function.) - * - struct magic has been converted from an array to a single-ended linked - * list because it only grows one record at a time, it's only accessed - * sequentially, and the Apache API has no equivalent of realloc(). - * - Functions have been changed to get their parameters from the server - * configuration instead of globals. (It should be reentrant now but has - * not been tested in a threaded environment.) - * - Places where it used to print results to stdout now saves them in a - * list where they're used to set the MIME type in the Apache request - * record. - * - Command-line flags have been removed since they will never be used here. - * - * Ian Kluft <ikluft@cisco.com> - * Engineering Information Framework - * Central Engineering - * Cisco Systems, Inc. - * San Jose, CA, USA - * - * Initial installation July/August 1996 - * Misc bug fixes May 1997 - * Submission to Apache Software Foundation July 1997 - * - */ - -#include "apr.h" -#include "apr_strings.h" -#include "apr_lib.h" -#define APR_WANT_STRFUNC -#include "apr_want.h" - -#if APR_HAVE_UNISTD_H -#include <unistd.h> -#endif - -#include "ap_config.h" -#include "httpd.h" -#include "http_config.h" -#include "http_request.h" -#include "http_core.h" -#include "http_log.h" -#include "http_protocol.h" -#include "util_script.h" - -/* ### this isn't set by configure? does anybody set this? */ -#ifdef HAVE_UTIME_H -#include <utime.h> -#endif - -/* - * data structures and related constants - */ - -#define MODNAME "mod_mime_magic" -#define MIME_MAGIC_DEBUG 0 - -#define MIME_BINARY_UNKNOWN "application/octet-stream" -#define MIME_TEXT_UNKNOWN "text/plain" - -#define MAXMIMESTRING 256 - -/* HOWMANY must be at least 4096 to make gzip -dcq work */ -#define HOWMANY 4096 -/* SMALL_HOWMANY limits how much work we do to figure out text files */ -#define SMALL_HOWMANY 1024 -#define MAXDESC 50 /* max leng of text description */ -#define MAXstring 64 /* max leng of "string" types */ - -struct magic { - struct magic *next; /* link to next entry */ - int lineno; /* line number from magic file */ - - short flag; -#define INDIR 1 /* if '>(...)' appears, */ -#define UNSIGNED 2 /* comparison is unsigned */ - short cont_level; /* level of ">" */ - struct { - char type; /* byte short long */ - long offset; /* offset from indirection */ - } in; - long offset; /* offset to magic number */ - unsigned char reln; /* relation (0=eq, '>'=gt, etc) */ - char type; /* int, short, long or string. */ - char vallen; /* length of string value, if any */ -#define BYTE 1 -#define SHORT 2 -#define LONG 4 -#define STRING 5 -#define DATE 6 -#define BESHORT 7 -#define BELONG 8 -#define BEDATE 9 -#define LESHORT 10 -#define LELONG 11 -#define LEDATE 12 - union VALUETYPE { - unsigned char b; - unsigned short h; - unsigned long l; - char s[MAXstring]; - unsigned char hs[2]; /* 2 bytes of a fixed-endian "short" */ - unsigned char hl[4]; /* 2 bytes of a fixed-endian "long" */ - } value; /* either number or string */ - unsigned long mask; /* mask before comparison with value */ - char nospflag; /* supress space character */ - - /* NOTE: this string is suspected of overrunning - find it! */ - char desc[MAXDESC]; /* description */ -}; - -/* - * data structures for tar file recognition - * -------------------------------------------------------------------------- - * Header file for public domain tar (tape archive) program. - * - * @(#)tar.h 1.20 86/10/29 Public Domain. Created 25 August 1985 by John - * Gilmore, ihnp4!hoptoad!gnu. - * - * Header block on tape. - * - * I'm going to use traditional DP naming conventions here. A "block" is a big - * chunk of stuff that we do I/O on. A "record" is a piece of info that we - * care about. Typically many "record"s fit into a "block". - */ -#define RECORDSIZE 512 -#define NAMSIZ 100 -#define TUNMLEN 32 -#define TGNMLEN 32 - -union record { - char charptr[RECORDSIZE]; - struct header { - char name[NAMSIZ]; - char mode[8]; - char uid[8]; - char gid[8]; - char size[12]; - char mtime[12]; - char chksum[8]; - char linkflag; - char linkname[NAMSIZ]; - char magic[8]; - char uname[TUNMLEN]; - char gname[TGNMLEN]; - char devmajor[8]; - char devminor[8]; - } header; -}; - -/* The magic field is filled with this if uname and gname are valid. */ -#define TMAGIC "ustar " /* 7 chars and a null */ - -/* - * file-function prototypes - */ -static int ascmagic(request_rec *, unsigned char *, apr_size_t); -static int is_tar(unsigned char *, apr_size_t); -static int softmagic(request_rec *, unsigned char *, apr_size_t); -static int tryit(request_rec *, unsigned char *, apr_size_t, int); -static int zmagic(request_rec *, unsigned char *, apr_size_t); - -static int getvalue(server_rec *, struct magic *, char **); -static int hextoint(int); -static char *getstr(server_rec *, char *, char *, int, int *); -static int parse(server_rec *, apr_pool_t *p, char *, int); - -static int match(request_rec *, unsigned char *, apr_size_t); -static int mget(request_rec *, union VALUETYPE *, unsigned char *, - struct magic *, apr_size_t); -static int mcheck(request_rec *, union VALUETYPE *, struct magic *); -static void mprint(request_rec *, union VALUETYPE *, struct magic *); - -static int uncompress(request_rec *, int, - unsigned char **, apr_size_t); -static long from_oct(int, char *); -static int fsmagic(request_rec *r, const char *fn); - -/* - * includes for ASCII substring recognition formerly "names.h" in file - * command - * - * Original notes: names and types used by ascmagic in file(1). These tokens are - * here because they can appear anywhere in the first HOWMANY bytes, while - * tokens in /etc/magic must appear at fixed offsets into the file. Don't - * make HOWMANY too high unless you have a very fast CPU. - */ - -/* these types are used to index the apr_table_t 'types': keep em in sync! */ -/* HTML inserted in first because this is a web server module now */ -#define L_HTML 0 /* HTML */ -#define L_C 1 /* first and foremost on UNIX */ -#define L_FORT 2 /* the oldest one */ -#define L_MAKE 3 /* Makefiles */ -#define L_PLI 4 /* PL/1 */ -#define L_MACH 5 /* some kinda assembler */ -#define L_ENG 6 /* English */ -#define L_PAS 7 /* Pascal */ -#define L_MAIL 8 /* Electronic mail */ -#define L_NEWS 9 /* Usenet Netnews */ - -static char *types[] = -{ - "text/html", /* HTML */ - "text/plain", /* "c program text", */ - "text/plain", /* "fortran program text", */ - "text/plain", /* "make commands text", */ - "text/plain", /* "pl/1 program text", */ - "text/plain", /* "assembler program text", */ - "text/plain", /* "English text", */ - "text/plain", /* "pascal program text", */ - "message/rfc822", /* "mail text", */ - "message/news", /* "news text", */ - "application/binary", /* "can't happen error on names.h/types", */ - 0 -}; - -static struct names { - char *name; - short type; -} names[] = { - - /* These must be sorted by eye for optimal hit rate */ - /* Add to this list only after substantial meditation */ - { - "<html>", L_HTML - }, - { - "<HTML>", L_HTML - }, - { - "<head>", L_HTML - }, - { - "<HEAD>", L_HTML - }, - { - "<title>", L_HTML - }, - { - "<TITLE>", L_HTML - }, - { - "<h1>", L_HTML - }, - { - "<H1>", L_HTML - }, - { - "<!--", L_HTML - }, - { - "<!DOCTYPE HTML", L_HTML - }, - { - "/*", L_C - }, /* must precede "The", "the", etc. */ - { - "#include", L_C - }, - { - "char", L_C - }, - { - "The", L_ENG - }, - { - "the", L_ENG - }, - { - "double", L_C - }, - { - "extern", L_C - }, - { - "float", L_C - }, - { - "real", L_C - }, - { - "struct", L_C - }, - { - "union", L_C - }, - { - "CFLAGS", L_MAKE - }, - { - "LDFLAGS", L_MAKE - }, - { - "all:", L_MAKE - }, - { - ".PRECIOUS", L_MAKE - }, - /* - * Too many files of text have these words in them. Find another way to - * recognize Fortrash. - */ -#ifdef NOTDEF - { - "subroutine", L_FORT - }, - { - "function", L_FORT - }, - { - "block", L_FORT - }, - { - "common", L_FORT - }, - { - "dimension", L_FORT - }, - { - "integer", L_FORT - }, - { - "data", L_FORT - }, -#endif /* NOTDEF */ - { - ".ascii", L_MACH - }, - { - ".asciiz", L_MACH - }, - { - ".byte", L_MACH - }, - { - ".even", L_MACH - }, - { - ".globl", L_MACH - }, - { - "clr", L_MACH - }, - { - "(input,", L_PAS - }, - { - "dcl", L_PLI - }, - { - "Received:", L_MAIL - }, - { - ">From", L_MAIL - }, - { - "Return-Path:", L_MAIL - }, - { - "Cc:", L_MAIL - }, - { - "Newsgroups:", L_NEWS - }, - { - "Path:", L_NEWS - }, - { - "Organization:", L_NEWS - }, - { - NULL, 0 - } -}; - -#define NNAMES ((sizeof(names)/sizeof(struct names)) - 1) - -/* - * Result String List (RSL) - * - * The file(1) command prints its output. Instead, we store the various - * "printed" strings in a list (allocating memory as we go) and concatenate - * them at the end when we finally know how much space they'll need. - */ - -typedef struct magic_rsl_s { - char *str; /* string, possibly a fragment */ - struct magic_rsl_s *next; /* pointer to next fragment */ -} magic_rsl; - -/* - * Apache module configuration structures - */ - -/* per-server info */ -typedef struct { - const char *magicfile; /* where magic be found */ - struct magic *magic; /* head of magic config list */ - struct magic *last; -} magic_server_config_rec; - -/* per-request info */ -typedef struct { - magic_rsl *head; /* result string list */ - magic_rsl *tail; - unsigned suf_recursion; /* recursion depth in suffix check */ -} magic_req_rec; - -/* - * configuration functions - called by Apache API routines - */ - -module AP_MODULE_DECLARE_DATA mime_magic_module; - -static void *create_magic_server_config(apr_pool_t *p, server_rec *d) -{ - /* allocate the config - use pcalloc because it needs to be zeroed */ - return apr_pcalloc(p, sizeof(magic_server_config_rec)); -} - -static void *merge_magic_server_config(apr_pool_t *p, void *basev, void *addv) -{ - magic_server_config_rec *base = (magic_server_config_rec *) basev; - magic_server_config_rec *add = (magic_server_config_rec *) addv; - magic_server_config_rec *new = (magic_server_config_rec *) - apr_palloc(p, sizeof(magic_server_config_rec)); - - new->magicfile = add->magicfile ? add->magicfile : base->magicfile; - new->magic = NULL; - new->last = NULL; - return new; -} - -static const char *set_magicfile(cmd_parms *cmd, void *dummy, const char *arg) -{ - magic_server_config_rec *conf = (magic_server_config_rec *) - ap_get_module_config(cmd->server->module_config, - &mime_magic_module); - - if (!conf) { - return MODNAME ": server structure not allocated"; - } - conf->magicfile = arg; - return NULL; -} - -/* - * configuration file commands - exported to Apache API - */ - -static const command_rec mime_magic_cmds[] = -{ - AP_INIT_TAKE1("MimeMagicFile", set_magicfile, NULL, RSRC_CONF, - "Path to MIME Magic file (in file(1) format)"), - {NULL} -}; - -/* - * RSL (result string list) processing routines - * - * These collect strings that would have been printed in fragments by file(1) - * into a list of magic_rsl structures with the strings. When complete, - * they're concatenated together to become the MIME content and encoding - * types. - * - * return value conventions for these functions: functions which return int: - * failure = -1, other = result functions which return pointers: failure = 0, - * other = result - */ - -/* allocate a per-request structure and put it in the request record */ -static magic_req_rec *magic_set_config(request_rec *r) -{ - magic_req_rec *req_dat = (magic_req_rec *) apr_palloc(r->pool, - sizeof(magic_req_rec)); - - req_dat->head = req_dat->tail = (magic_rsl *) NULL; - ap_set_module_config(r->request_config, &mime_magic_module, req_dat); - return req_dat; -} - -/* add a string to the result string list for this request */ -/* it is the responsibility of the caller to allocate "str" */ -static int magic_rsl_add(request_rec *r, char *str) -{ - magic_req_rec *req_dat = (magic_req_rec *) - ap_get_module_config(r->request_config, &mime_magic_module); - magic_rsl *rsl; - - /* make sure we have a list to put it in */ - if (!req_dat) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, r, - MODNAME ": request config should not be NULL"); - if (!(req_dat = magic_set_config(r))) { - /* failure */ - return -1; - } - } - - /* allocate the list entry */ - rsl = (magic_rsl *) apr_palloc(r->pool, sizeof(magic_rsl)); - - /* fill it */ - rsl->str = str; - rsl->next = (magic_rsl *) NULL; - - /* append to the list */ - if (req_dat->head && req_dat->tail) { - req_dat->tail->next = rsl; - req_dat->tail = rsl; - } - else { - req_dat->head = req_dat->tail = rsl; - } - - /* success */ - return 0; -} - -/* RSL hook for puts-type functions */ -static int magic_rsl_puts(request_rec *r, char *str) -{ - return magic_rsl_add(r, str); -} - -/* RSL hook for printf-type functions */ -static int magic_rsl_printf(request_rec *r, char *str,...) -{ - va_list ap; - - char buf[MAXMIMESTRING]; - - /* assemble the string into the buffer */ - va_start(ap, str); - apr_vsnprintf(buf, sizeof(buf), str, ap); - va_end(ap); - - /* add the buffer to the list */ - return magic_rsl_add(r, apr_pstrdup(r->pool, buf)); -} - -/* RSL hook for putchar-type functions */ -static int magic_rsl_putchar(request_rec *r, char c) -{ - char str[2]; - - /* high overhead for 1 char - just hope they don't do this much */ - str[0] = c; - str[1] = '\0'; - return magic_rsl_add(r, str); -} - -/* allocate and copy a contiguous string from a result string list */ -static char *rsl_strdup(request_rec *r, int start_frag, int start_pos, int len) -{ - char *result; /* return value */ - int cur_frag, /* current fragment number/counter */ - cur_pos, /* current position within fragment */ - res_pos; /* position in result string */ - magic_rsl *frag; /* list-traversal pointer */ - magic_req_rec *req_dat = (magic_req_rec *) - ap_get_module_config(r->request_config, &mime_magic_module); - - /* allocate the result string */ - result = (char *) apr_palloc(r->pool, len + 1); - - /* loop through and collect the string */ - res_pos = 0; - for (frag = req_dat->head, cur_frag = 0; - frag->next; - frag = frag->next, cur_frag++) { - /* loop to the first fragment */ - if (cur_frag < start_frag) - continue; - - /* loop through and collect chars */ - for (cur_pos = (cur_frag == start_frag) ? start_pos : 0; - frag->str[cur_pos]; - cur_pos++) { - if (cur_frag >= start_frag - && cur_pos >= start_pos - && res_pos <= len) { - result[res_pos++] = frag->str[cur_pos]; - if (res_pos > len) { - break; - } - } - } - } - - /* clean up and return */ - result[res_pos] = 0; -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": rsl_strdup() %d chars: %s", res_pos - 1, result); -#endif - return result; -} - -/* states for the state-machine algorithm in magic_rsl_to_request() */ -typedef enum { - rsl_leading_space, rsl_type, rsl_subtype, rsl_separator, rsl_encoding -} rsl_states; - -/* process the RSL and set the MIME info in the request record */ -static int magic_rsl_to_request(request_rec *r) -{ - int cur_frag, /* current fragment number/counter */ - cur_pos, /* current position within fragment */ - type_frag, /* content type starting point: fragment */ - type_pos, /* content type starting point: position */ - type_len, /* content type length */ - encoding_frag, /* content encoding starting point: fragment */ - encoding_pos, /* content encoding starting point: position */ - encoding_len; /* content encoding length */ - - magic_rsl *frag; /* list-traversal pointer */ - rsl_states state; - - magic_req_rec *req_dat = (magic_req_rec *) - ap_get_module_config(r->request_config, &mime_magic_module); - - /* check if we have a result */ - if (!req_dat || !req_dat->head) { - /* empty - no match, we defer to other Apache modules */ - return DECLINED; - } - - /* start searching for the type and encoding */ - state = rsl_leading_space; - type_frag = type_pos = type_len = 0; - encoding_frag = encoding_pos = encoding_len = 0; - for (frag = req_dat->head, cur_frag = 0; - frag && frag->next; - frag = frag->next, cur_frag++) { - /* loop through the characters in the fragment */ - for (cur_pos = 0; frag->str[cur_pos]; cur_pos++) { - if (apr_isspace(frag->str[cur_pos])) { - /* process whitespace actions for each state */ - if (state == rsl_leading_space) { - /* eat whitespace in this state */ - continue; - } - else if (state == rsl_type) { - /* whitespace: type has no slash! */ - return DECLINED; - } - else if (state == rsl_subtype) { - /* whitespace: end of MIME type */ - state++; - continue; - } - else if (state == rsl_separator) { - /* eat whitespace in this state */ - continue; - } - else if (state == rsl_encoding) { - /* whitespace: end of MIME encoding */ - /* we're done */ - frag = req_dat->tail; - break; - } - else { - /* should not be possible */ - /* abandon malfunctioning module */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - MODNAME ": bad state %d (ws)", state); - return DECLINED; - } - /* NOTREACHED */ - } - else if (state == rsl_type && - frag->str[cur_pos] == '/') { - /* copy the char and go to rsl_subtype state */ - type_len++; - state++; - } - else { - /* process non-space actions for each state */ - if (state == rsl_leading_space) { - /* non-space: begin MIME type */ - state++; - type_frag = cur_frag; - type_pos = cur_pos; - type_len = 1; - continue; - } - else if (state == rsl_type || - state == rsl_subtype) { - /* non-space: adds to type */ - type_len++; - continue; - } - else if (state == rsl_separator) { - /* non-space: begin MIME encoding */ - state++; - encoding_frag = cur_frag; - encoding_pos = cur_pos; - encoding_len = 1; - continue; - } - else if (state == rsl_encoding) { - /* non-space: adds to encoding */ - encoding_len++; - continue; - } - else { - /* should not be possible */ - /* abandon malfunctioning module */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - MODNAME ": bad state %d (ns)", state); - return DECLINED; - } - /* NOTREACHED */ - } - /* NOTREACHED */ - } - } - - /* if we ended prior to state rsl_subtype, we had incomplete info */ - if (state != rsl_subtype && state != rsl_separator && - state != rsl_encoding) { - /* defer to other modules */ - return DECLINED; - } - - /* save the info in the request record */ - if (state == rsl_subtype || state == rsl_encoding || - state == rsl_encoding) { - char *tmp; - tmp = rsl_strdup(r, type_frag, type_pos, type_len); - /* XXX: this could be done at config time I'm sure... but I'm - * confused by all this magic_rsl stuff. -djg */ - ap_content_type_tolower(tmp); - ap_set_content_type(r, tmp); - } - if (state == rsl_encoding) { - char *tmp; - tmp = rsl_strdup(r, encoding_frag, - encoding_pos, encoding_len); - /* XXX: this could be done at config time I'm sure... but I'm - * confused by all this magic_rsl stuff. -djg */ - ap_str_tolower(tmp); - r->content_encoding = tmp; - } - - /* detect memory allocation or other errors */ - if (!r->content_type || - (state == rsl_encoding && !r->content_encoding)) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - MODNAME ": unexpected state %d; could be caused by bad " - "data in magic file", - state); - return HTTP_INTERNAL_SERVER_ERROR; - } - - /* success! */ - return OK; -} - -/* - * magic_process - process input file r Apache API request record - * (formerly called "process" in file command, prefix added for clarity) Opens - * the file and reads a fixed-size buffer to begin processing the contents. - */ -static int magic_process(request_rec *r) -{ - apr_file_t *fd = NULL; - unsigned char buf[HOWMANY + 1]; /* one extra for terminating '\0' */ - apr_size_t nbytes = 0; /* number of bytes read from a datafile */ - int result; - - /* - * first try judging the file based on its filesystem status - */ - switch ((result = fsmagic(r, r->filename))) { - case DONE: - magic_rsl_putchar(r, '\n'); - return OK; - case OK: - break; - default: - /* fatal error, bail out */ - return result; - } - - if (apr_file_open(&fd, r->filename, APR_READ, APR_OS_DEFAULT, r->pool) != APR_SUCCESS) { - /* We can't open it, but we were able to stat it. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - MODNAME ": can't read `%s'", r->filename); - /* let some other handler decide what the problem is */ - return DECLINED; - } - - /* - * try looking at the first HOWMANY bytes - */ - nbytes = sizeof(buf) - 1; - if ((result = apr_file_read(fd, (char *) buf, &nbytes)) != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, result, r, - MODNAME ": read failed: %s", r->filename); - return HTTP_INTERNAL_SERVER_ERROR; - } - - if (nbytes == 0) { - return DECLINED; - } - else { - buf[nbytes++] = '\0'; /* null-terminate it */ - result = tryit(r, buf, nbytes, 1); - if (result != OK) { - return result; - } - } - - (void) apr_file_close(fd); - (void) magic_rsl_putchar(r, '\n'); - - return OK; -} - - -static int tryit(request_rec *r, unsigned char *buf, apr_size_t nb, - int checkzmagic) -{ - /* - * Try compression stuff - */ - if (checkzmagic == 1) { - if (zmagic(r, buf, nb) == 1) - return OK; - } - - /* - * try tests in /etc/magic (or surrogate magic file) - */ - if (softmagic(r, buf, nb) == 1) - return OK; - - /* - * try known keywords, check for ascii-ness too. - */ - if (ascmagic(r, buf, nb) == 1) - return OK; - - /* - * abandon hope, all ye who remain here - */ - return DECLINED; -} - -#define EATAB {while (apr_isspace(*l)) ++l;} - -/* - * apprentice - load configuration from the magic file r - * API request record - */ -static int apprentice(server_rec *s, apr_pool_t *p) -{ - apr_file_t *f = NULL; - apr_status_t result; - char line[BUFSIZ + 1]; - int errs = 0; - int lineno; -#if MIME_MAGIC_DEBUG - int rule = 0; - struct magic *m, *prevm; -#endif - magic_server_config_rec *conf = (magic_server_config_rec *) - ap_get_module_config(s->module_config, &mime_magic_module); - const char *fname = ap_server_root_relative(p, conf->magicfile); - - if (!fname) { - ap_log_error(APLOG_MARK, APLOG_ERR, APR_EBADPATH, s, - MODNAME ": Invalid magic file path %s", conf->magicfile); - return -1; - } - if ((result = apr_file_open(&f, fname, APR_READ | APR_BUFFERED, - APR_OS_DEFAULT, p) != APR_SUCCESS)) { - ap_log_error(APLOG_MARK, APLOG_ERR, result, s, - MODNAME ": can't read magic file %s", fname); - return -1; - } - - /* set up the magic list (empty) */ - conf->magic = conf->last = NULL; - - /* parse it */ - for (lineno = 1; apr_file_gets(line, BUFSIZ, f) == APR_SUCCESS; lineno++) { - int ws_offset; - char *last = line + strlen(line) - 1; /* guaranteed that len >= 1 since an - * "empty" line contains a '\n' - */ - - /* delete newline and any other trailing whitespace */ - while (last >= line - && apr_isspace(*last)) { - *last = '\0'; - --last; - } - - /* skip leading whitespace */ - ws_offset = 0; - while (line[ws_offset] && apr_isspace(line[ws_offset])) { - ws_offset++; - } - - /* skip blank lines */ - if (line[ws_offset] == 0) { - continue; - } - - /* comment, do not parse */ - if (line[ws_offset] == '#') - continue; - -#if MIME_MAGIC_DEBUG - /* if we get here, we're going to use it so count it */ - rule++; -#endif - - /* parse it */ - if (parse(s, p, line + ws_offset, lineno) != 0) - ++errs; - } - - (void) apr_file_close(f); - -#if MIME_MAGIC_DEBUG - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, - MODNAME ": apprentice conf=%x file=%s m=%s m->next=%s last=%s", - conf, - conf->magicfile ? conf->magicfile : "NULL", - conf->magic ? "set" : "NULL", - (conf->magic && conf->magic->next) ? "set" : "NULL", - conf->last ? "set" : "NULL"); - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, - MODNAME ": apprentice read %d lines, %d rules, %d errors", - lineno, rule, errs); -#endif - -#if MIME_MAGIC_DEBUG - prevm = 0; - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, - MODNAME ": apprentice test"); - for (m = conf->magic; m; m = m->next) { - if (apr_isprint((((unsigned long) m) >> 24) & 255) && - apr_isprint((((unsigned long) m) >> 16) & 255) && - apr_isprint((((unsigned long) m) >> 8) & 255) && - apr_isprint(((unsigned long) m) & 255)) { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, - MODNAME ": apprentice: POINTER CLOBBERED! " - "m=\"%c%c%c%c\" line=%d", - (((unsigned long) m) >> 24) & 255, - (((unsigned long) m) >> 16) & 255, - (((unsigned long) m) >> 8) & 255, - ((unsigned long) m) & 255, - prevm ? prevm->lineno : -1); - break; - } - prevm = m; - } -#endif - - return (errs ? -1 : 0); -} - -/* - * extend the sign bit if the comparison is to be signed - */ -static unsigned long signextend(server_rec *s, struct magic *m, unsigned long v) -{ - if (!(m->flag & UNSIGNED)) - switch (m->type) { - /* - * Do not remove the casts below. They are vital. When later - * compared with the data, the sign extension must have happened. - */ - case BYTE: - v = (char) v; - break; - case SHORT: - case BESHORT: - case LESHORT: - v = (short) v; - break; - case DATE: - case BEDATE: - case LEDATE: - case LONG: - case BELONG: - case LELONG: - v = (long) v; - break; - case STRING: - break; - default: - ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - MODNAME ": can't happen: m->type=%d", m->type); - return -1; - } - return v; -} - -/* - * parse one line from magic file, put into magic[index++] if valid - */ -static int parse(server_rec *serv, apr_pool_t *p, char *l, int lineno) -{ - struct magic *m; - char *t, *s; - magic_server_config_rec *conf = (magic_server_config_rec *) - ap_get_module_config(serv->module_config, &mime_magic_module); - - /* allocate magic structure entry */ - m = (struct magic *) apr_pcalloc(p, sizeof(struct magic)); - - /* append to linked list */ - m->next = NULL; - if (!conf->magic || !conf->last) { - conf->magic = conf->last = m; - } - else { - conf->last->next = m; - conf->last = m; - } - - /* set values in magic structure */ - m->flag = 0; - m->cont_level = 0; - m->lineno = lineno; - - while (*l == '>') { - ++l; /* step over */ - m->cont_level++; - } - - if (m->cont_level != 0 && *l == '(') { - ++l; /* step over */ - m->flag |= INDIR; - } - - /* get offset, then skip over it */ - m->offset = (int) strtol(l, &t, 0); - if (l == t) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, serv, - MODNAME ": offset %s invalid", l); - } - l = t; - - if (m->flag & INDIR) { - m->in.type = LONG; - m->in.offset = 0; - /* - * read [.lbs][+-]nnnnn) - */ - if (*l == '.') { - switch (*++l) { - case 'l': - m->in.type = LONG; - break; - case 's': - m->in.type = SHORT; - break; - case 'b': - m->in.type = BYTE; - break; - default: - ap_log_error(APLOG_MARK, APLOG_ERR, 0, serv, - MODNAME ": indirect offset type %c invalid", *l); - break; - } - l++; - } - s = l; - if (*l == '+' || *l == '-') - l++; - if (apr_isdigit((unsigned char) *l)) { - m->in.offset = strtol(l, &t, 0); - if (*s == '-') - m->in.offset = -m->in.offset; - } - else - t = l; - if (*t++ != ')') { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, serv, - MODNAME ": missing ')' in indirect offset"); - } - l = t; - } - - - while (apr_isdigit((unsigned char) *l)) - ++l; - EATAB; - -#define NBYTE 4 -#define NSHORT 5 -#define NLONG 4 -#define NSTRING 6 -#define NDATE 4 -#define NBESHORT 7 -#define NBELONG 6 -#define NBEDATE 6 -#define NLESHORT 7 -#define NLELONG 6 -#define NLEDATE 6 - - if (*l == 'u') { - ++l; - m->flag |= UNSIGNED; - } - - /* get type, skip it */ - if (strncmp(l, "byte", NBYTE) == 0) { - m->type = BYTE; - l += NBYTE; - } - else if (strncmp(l, "short", NSHORT) == 0) { - m->type = SHORT; - l += NSHORT; - } - else if (strncmp(l, "long", NLONG) == 0) { - m->type = LONG; - l += NLONG; - } - else if (strncmp(l, "string", NSTRING) == 0) { - m->type = STRING; - l += NSTRING; - } - else if (strncmp(l, "date", NDATE) == 0) { - m->type = DATE; - l += NDATE; - } - else if (strncmp(l, "beshort", NBESHORT) == 0) { - m->type = BESHORT; - l += NBESHORT; - } - else if (strncmp(l, "belong", NBELONG) == 0) { - m->type = BELONG; - l += NBELONG; - } - else if (strncmp(l, "bedate", NBEDATE) == 0) { - m->type = BEDATE; - l += NBEDATE; - } - else if (strncmp(l, "leshort", NLESHORT) == 0) { - m->type = LESHORT; - l += NLESHORT; - } - else if (strncmp(l, "lelong", NLELONG) == 0) { - m->type = LELONG; - l += NLELONG; - } - else if (strncmp(l, "ledate", NLEDATE) == 0) { - m->type = LEDATE; - l += NLEDATE; - } - else { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, serv, - MODNAME ": type %s invalid", l); - return -1; - } - /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */ - if (*l == '&') { - ++l; - m->mask = signextend(serv, m, strtol(l, &l, 0)); - } - else - m->mask = ~0L; - EATAB; - - switch (*l) { - case '>': - case '<': - /* Old-style anding: "0 byte &0x80 dynamically linked" */ - case '&': - case '^': - case '=': - m->reln = *l; - ++l; - break; - case '!': - if (m->type != STRING) { - m->reln = *l; - ++l; - break; - } - /* FALL THROUGH */ - default: - if (*l == 'x' && apr_isspace(l[1])) { - m->reln = *l; - ++l; - goto GetDesc; /* Bill The Cat */ - } - m->reln = '='; - break; - } - EATAB; - - if (getvalue(serv, m, &l)) - return -1; - /* - * now get last part - the description - */ - GetDesc: - EATAB; - if (l[0] == '\b') { - ++l; - m->nospflag = 1; - } - else if ((l[0] == '\\') && (l[1] == 'b')) { - ++l; - ++l; - m->nospflag = 1; - } - else - m->nospflag = 0; - strncpy(m->desc, l, sizeof(m->desc) - 1); - m->desc[sizeof(m->desc) - 1] = '\0'; - -#if MIME_MAGIC_DEBUG - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, serv, - MODNAME ": parse line=%d m=%x next=%x cont=%d desc=%s", - lineno, m, m->next, m->cont_level, m->desc); -#endif /* MIME_MAGIC_DEBUG */ - - return 0; -} - -/* - * Read a numeric value from a pointer, into the value union of a magic - * pointer, according to the magic type. Update the string pointer to point - * just after the number read. Return 0 for success, non-zero for failure. - */ -static int getvalue(server_rec *s, struct magic *m, char **p) -{ - int slen; - - if (m->type == STRING) { - *p = getstr(s, *p, m->value.s, sizeof(m->value.s), &slen); - m->vallen = slen; - } - else if (m->reln != 'x') - m->value.l = signextend(s, m, strtol(*p, p, 0)); - return 0; -} - -/* - * Convert a string containing C character escapes. Stop at an unescaped - * space or tab. Copy the converted version to "p", returning its length in - * *slen. Return updated scan pointer as function result. - */ -static char *getstr(server_rec *serv, register char *s, register char *p, - int plen, int *slen) -{ - char *origs = s, *origp = p; - char *pmax = p + plen - 1; - register int c; - register int val; - - while ((c = *s++) != '\0') { - if (apr_isspace(c)) - break; - if (p >= pmax) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, serv, - MODNAME ": string too long: %s", origs); - break; - } - if (c == '\\') { - switch (c = *s++) { - - case '\0': - goto out; - - default: - *p++ = (char) c; - break; - - case 'n': - *p++ = '\n'; - break; - - case 'r': - *p++ = '\r'; - break; - - case 'b': - *p++ = '\b'; - break; - - case 't': - *p++ = '\t'; - break; - - case 'f': - *p++ = '\f'; - break; - - case 'v': - *p++ = '\v'; - break; - - /* \ and up to 3 octal digits */ - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - val = c - '0'; - c = *s++; /* try for 2 */ - if (c >= '0' && c <= '7') { - val = (val << 3) | (c - '0'); - c = *s++; /* try for 3 */ - if (c >= '0' && c <= '7') - val = (val << 3) | (c - '0'); - else - --s; - } - else - --s; - *p++ = (char) val; - break; - - /* \x and up to 3 hex digits */ - case 'x': - val = 'x'; /* Default if no digits */ - c = hextoint(*s++); /* Get next char */ - if (c >= 0) { - val = c; - c = hextoint(*s++); - if (c >= 0) { - val = (val << 4) + c; - c = hextoint(*s++); - if (c >= 0) { - val = (val << 4) + c; - } - else - --s; - } - else - --s; - } - else - --s; - *p++ = (char) val; - break; - } - } - else - *p++ = (char) c; - } - out: - *p = '\0'; - *slen = p - origp; - return s; -} - - -/* Single hex char to int; -1 if not a hex char. */ -static int hextoint(int c) -{ - if (apr_isdigit(c)) - return c - '0'; - if ((c >= 'a') && (c <= 'f')) - return c + 10 - 'a'; - if ((c >= 'A') && (c <= 'F')) - return c + 10 - 'A'; - return -1; -} - - -/* - * return DONE to indicate it's been handled - * return OK to indicate it's a regular file still needing handling - * other returns indicate a failure of some sort - */ -static int fsmagic(request_rec *r, const char *fn) -{ - switch (r->finfo.filetype) { - case APR_DIR: - magic_rsl_puts(r, DIR_MAGIC_TYPE); - return DONE; - case APR_CHR: - /* - * (void) magic_rsl_printf(r,"character special (%d/%d)", - * major(sb->st_rdev), minor(sb->st_rdev)); - */ - (void) magic_rsl_puts(r, MIME_BINARY_UNKNOWN); - return DONE; - case APR_BLK: - /* - * (void) magic_rsl_printf(r,"block special (%d/%d)", - * major(sb->st_rdev), minor(sb->st_rdev)); - */ - (void) magic_rsl_puts(r, MIME_BINARY_UNKNOWN); - return DONE; - /* TODO add code to handle V7 MUX and Blit MUX files */ - case APR_PIPE: - /* - * magic_rsl_puts(r,"fifo (named pipe)"); - */ - (void) magic_rsl_puts(r, MIME_BINARY_UNKNOWN); - return DONE; - case APR_LNK: - /* We used stat(), the only possible reason for this is that the - * symlink is broken. - */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - MODNAME ": broken symlink (%s)", fn); - return HTTP_INTERNAL_SERVER_ERROR; - case APR_SOCK: - magic_rsl_puts(r, MIME_BINARY_UNKNOWN); - return DONE; - case APR_REG: - break; - default: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - MODNAME ": invalid file type %d.", r->finfo.filetype); - return HTTP_INTERNAL_SERVER_ERROR; - } - - /* - * regular file, check next possibility - */ - if (r->finfo.size == 0) { - magic_rsl_puts(r, MIME_TEXT_UNKNOWN); - return DONE; - } - return OK; -} - -/* - * softmagic - lookup one file in database (already read from /etc/magic by - * apprentice.c). Passed the name and FILE * of one file to be typed. - */ - /* ARGSUSED1 *//* nbytes passed for regularity, maybe need later */ -static int softmagic(request_rec *r, unsigned char *buf, apr_size_t nbytes) -{ - if (match(r, buf, nbytes)) - return 1; - - return 0; -} - -/* - * Go through the whole list, stopping if you find a match. Process all the - * continuations of that match before returning. - * - * We support multi-level continuations: - * - * At any time when processing a successful top-level match, there is a current - * continuation level; it represents the level of the last successfully - * matched continuation. - * - * Continuations above that level are skipped as, if we see one, it means that - * the continuation that controls them - i.e, the lower-level continuation - * preceding them - failed to match. - * - * Continuations below that level are processed as, if we see one, it means - * we've finished processing or skipping higher-level continuations under the - * control of a successful or unsuccessful lower-level continuation, and are - * now seeing the next lower-level continuation and should process it. The - * current continuation level reverts to the level of the one we're seeing. - * - * Continuations at the current level are processed as, if we see one, there's - * no lower-level continuation that may have failed. - * - * If a continuation matches, we bump the current continuation level so that - * higher-level continuations are processed. - */ -static int match(request_rec *r, unsigned char *s, apr_size_t nbytes) -{ -#if MIME_MAGIC_DEBUG - int rule_counter = 0; -#endif - int cont_level = 0; - int need_separator = 0; - union VALUETYPE p; - magic_server_config_rec *conf = (magic_server_config_rec *) - ap_get_module_config(r->server->module_config, &mime_magic_module); - struct magic *m; - -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": match conf=%x file=%s m=%s m->next=%s last=%s", - conf, - conf->magicfile ? conf->magicfile : "NULL", - conf->magic ? "set" : "NULL", - (conf->magic && conf->magic->next) ? "set" : "NULL", - conf->last ? "set" : "NULL"); -#endif - -#if MIME_MAGIC_DEBUG - for (m = conf->magic; m; m = m->next) { - if (apr_isprint((((unsigned long) m) >> 24) & 255) && - apr_isprint((((unsigned long) m) >> 16) & 255) && - apr_isprint((((unsigned long) m) >> 8) & 255) && - apr_isprint(((unsigned long) m) & 255)) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": match: POINTER CLOBBERED! " - "m=\"%c%c%c%c\"", - (((unsigned long) m) >> 24) & 255, - (((unsigned long) m) >> 16) & 255, - (((unsigned long) m) >> 8) & 255, - ((unsigned long) m) & 255); - break; - } - } -#endif - - for (m = conf->magic; m; m = m->next) { -#if MIME_MAGIC_DEBUG - rule_counter++; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": line=%d desc=%s", m->lineno, m->desc); -#endif - - /* check if main entry matches */ - if (!mget(r, &p, s, m, nbytes) || - !mcheck(r, &p, m)) { - struct magic *m_cont; - - /* - * main entry didn't match, flush its continuations - */ - if (!m->next || (m->next->cont_level == 0)) { - continue; - } - - m_cont = m->next; - while (m_cont && (m_cont->cont_level != 0)) { -#if MIME_MAGIC_DEBUG - rule_counter++; - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": line=%d mc=%x mc->next=%x cont=%d desc=%s", - m_cont->lineno, m_cont, - m_cont->next, m_cont->cont_level, - m_cont->desc); -#endif - /* - * this trick allows us to keep *m in sync when the continue - * advances the pointer - */ - m = m_cont; - m_cont = m_cont->next; - } - continue; - } - - /* if we get here, the main entry rule was a match */ - /* this will be the last run through the loop */ -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": rule matched, line=%d type=%d %s", - m->lineno, m->type, - (m->type == STRING) ? m->value.s : ""); -#endif - - /* print the match */ - mprint(r, &p, m); - - /* - * If we printed something, we'll need to print a blank before we - * print something else. - */ - if (m->desc[0]) - need_separator = 1; - /* and any continuations that match */ - cont_level++; - /* - * while (m && m->next && m->next->cont_level != 0 && ( m = m->next - * )) - */ - m = m->next; - while (m && (m->cont_level != 0)) { -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": match line=%d cont=%d type=%d %s", - m->lineno, m->cont_level, m->type, - (m->type == STRING) ? m->value.s : ""); -#endif - if (cont_level >= m->cont_level) { - if (cont_level > m->cont_level) { - /* - * We're at the end of the level "cont_level" - * continuations. - */ - cont_level = m->cont_level; - } - if (mget(r, &p, s, m, nbytes) && - mcheck(r, &p, m)) { - /* - * This continuation matched. Print its message, with a - * blank before it if the previous item printed and this - * item isn't empty. - */ - /* space if previous printed */ - if (need_separator - && (m->nospflag == 0) - && (m->desc[0] != '\0') - ) { - (void) magic_rsl_putchar(r, ' '); - need_separator = 0; - } - mprint(r, &p, m); - if (m->desc[0]) - need_separator = 1; - - /* - * If we see any continuations at a higher level, process - * them. - */ - cont_level++; - } - } - - /* move to next continuation record */ - m = m->next; - } -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": matched after %d rules", rule_counter); -#endif - return 1; /* all through */ - } -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": failed after %d rules", rule_counter); -#endif - return 0; /* no match at all */ -} - -static void mprint(request_rec *r, union VALUETYPE *p, struct magic *m) -{ - char *pp; - unsigned long v; - char time_str[APR_CTIME_LEN]; - - switch (m->type) { - case BYTE: - v = p->b; - break; - - case SHORT: - case BESHORT: - case LESHORT: - v = p->h; - break; - - case LONG: - case BELONG: - case LELONG: - v = p->l; - break; - - case STRING: - if (m->reln == '=') { - (void) magic_rsl_printf(r, m->desc, m->value.s); - } - else { - (void) magic_rsl_printf(r, m->desc, p->s); - } - return; - - case DATE: - case BEDATE: - case LEDATE: - apr_ctime(time_str, apr_time_from_sec(*(time_t *)&p->l)); - pp = time_str; - (void) magic_rsl_printf(r, m->desc, pp); - return; - default: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - MODNAME ": invalid m->type (%d) in mprint().", - m->type); - return; - } - - v = signextend(r->server, m, v) & m->mask; - (void) magic_rsl_printf(r, m->desc, (unsigned long) v); -} - -/* - * Convert the byte order of the data we are looking at - */ -static int mconvert(request_rec *r, union VALUETYPE *p, struct magic *m) -{ - char *rt; - - switch (m->type) { - case BYTE: - case SHORT: - case LONG: - case DATE: - return 1; - case STRING: - /* Null terminate and eat the return */ - p->s[sizeof(p->s) - 1] = '\0'; - if ((rt = strchr(p->s, '\n')) != NULL) - *rt = '\0'; - return 1; - case BESHORT: - p->h = (short) ((p->hs[0] << 8) | (p->hs[1])); - return 1; - case BELONG: - case BEDATE: - p->l = (long) - ((p->hl[0] << 24) | (p->hl[1] << 16) | (p->hl[2] << 8) | (p->hl[3])); - return 1; - case LESHORT: - p->h = (short) ((p->hs[1] << 8) | (p->hs[0])); - return 1; - case LELONG: - case LEDATE: - p->l = (long) - ((p->hl[3] << 24) | (p->hl[2] << 16) | (p->hl[1] << 8) | (p->hl[0])); - return 1; - default: - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - MODNAME ": invalid type %d in mconvert().", m->type); - return 0; - } -} - - -static int mget(request_rec *r, union VALUETYPE *p, unsigned char *s, - struct magic *m, apr_size_t nbytes) -{ - long offset = m->offset; - - if (offset + sizeof(union VALUETYPE) > nbytes) - return 0; - - memcpy(p, s + offset, sizeof(union VALUETYPE)); - - if (!mconvert(r, p, m)) - return 0; - - if (m->flag & INDIR) { - - switch (m->in.type) { - case BYTE: - offset = p->b + m->in.offset; - break; - case SHORT: - offset = p->h + m->in.offset; - break; - case LONG: - offset = p->l + m->in.offset; - break; - } - - if (offset + sizeof(union VALUETYPE) > nbytes) - return 0; - - memcpy(p, s + offset, sizeof(union VALUETYPE)); - - if (!mconvert(r, p, m)) - return 0; - } - return 1; -} - -static int mcheck(request_rec *r, union VALUETYPE *p, struct magic *m) -{ - register unsigned long l = m->value.l; - register unsigned long v; - int matched; - - if ((m->value.s[0] == 'x') && (m->value.s[1] == '\0')) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - MODNAME ": BOINK"); - return 1; - } - - switch (m->type) { - case BYTE: - v = p->b; - break; - - case SHORT: - case BESHORT: - case LESHORT: - v = p->h; - break; - - case LONG: - case BELONG: - case LELONG: - case DATE: - case BEDATE: - case LEDATE: - v = p->l; - break; - - case STRING: - l = 0; - /* - * What we want here is: v = strncmp(m->value.s, p->s, m->vallen); - * but ignoring any nulls. bcmp doesn't give -/+/0 and isn't - * universally available anyway. - */ - v = 0; - { - register unsigned char *a = (unsigned char *) m->value.s; - register unsigned char *b = (unsigned char *) p->s; - register int len = m->vallen; - - while (--len >= 0) - if ((v = *b++ - *a++) != 0) - break; - } - break; - default: - /* bogosity, pretend that it just wasn't a match */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - MODNAME ": invalid type %d in mcheck().", m->type); - return 0; - } - - v = signextend(r->server, m, v) & m->mask; - - switch (m->reln) { - case 'x': -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "%lu == *any* = 1", v); -#endif - matched = 1; - break; - - case '!': - matched = v != l; -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "%lu != %lu = %d", v, l, matched); -#endif - break; - - case '=': - matched = v == l; -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "%lu == %lu = %d", v, l, matched); -#endif - break; - - case '>': - if (m->flag & UNSIGNED) { - matched = v > l; -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "%lu > %lu = %d", v, l, matched); -#endif - } - else { - matched = (long) v > (long) l; -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "%ld > %ld = %d", v, l, matched); -#endif - } - break; - - case '<': - if (m->flag & UNSIGNED) { - matched = v < l; -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "%lu < %lu = %d", v, l, matched); -#endif - } - else { - matched = (long) v < (long) l; -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "%ld < %ld = %d", v, l, matched); -#endif - } - break; - - case '&': - matched = (v & l) == l; -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "((%lx & %lx) == %lx) = %d", v, l, l, matched); -#endif - break; - - case '^': - matched = (v & l) != l; -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - "((%lx & %lx) != %lx) = %d", v, l, l, matched); -#endif - break; - - default: - /* bogosity, pretend it didn't match */ - matched = 0; - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - MODNAME ": mcheck: can't happen: invalid relation %d.", - m->reln); - break; - } - - return matched; -} - -/* an optimization over plain strcmp() */ -#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) - -static int ascmagic(request_rec *r, unsigned char *buf, apr_size_t nbytes) -{ - int has_escapes = 0; - unsigned char *s; - char nbuf[HOWMANY + 1]; /* one extra for terminating '\0' */ - char *token; - register struct names *p; - int small_nbytes; - char *strtok_state; - - /* these are easy, do them first */ - - /* - * for troff, look for . + letter + letter or .\"; this must be done to - * disambiguate tar archives' ./file and other trash from real troff - * input. - */ - if (*buf == '.') { - unsigned char *tp = buf + 1; - - while (apr_isspace(*tp)) - ++tp; /* skip leading whitespace */ - if ((apr_isalnum(*tp) || *tp == '\\') && - (apr_isalnum(*(tp + 1)) || *tp == '"')) { - magic_rsl_puts(r, "application/x-troff"); - return 1; - } - } - if ((*buf == 'c' || *buf == 'C') && apr_isspace(*(buf + 1))) { - /* Fortran */ - magic_rsl_puts(r, "text/plain"); - return 1; - } - - /* look for tokens from names.h - this is expensive!, so we'll limit - * ourselves to only SMALL_HOWMANY bytes */ - small_nbytes = (nbytes > SMALL_HOWMANY) ? SMALL_HOWMANY : nbytes; - /* make a copy of the buffer here because apr_strtok() will destroy it */ - s = (unsigned char *) memcpy(nbuf, buf, small_nbytes); - s[small_nbytes] = '\0'; - has_escapes = (memchr(s, '\033', small_nbytes) != NULL); - while ((token = apr_strtok((char *) s, " \t\n\r\f", &strtok_state)) != NULL) { - s = NULL; /* make apr_strtok() keep on tokin' */ - for (p = names; p < names + NNAMES; p++) { - if (STREQ(p->name, token)) { - magic_rsl_puts(r, types[p->type]); - if (has_escapes) - magic_rsl_puts(r, " (with escape sequences)"); - return 1; - } - } - } - - switch (is_tar(buf, nbytes)) { - case 1: - /* V7 tar archive */ - magic_rsl_puts(r, "application/x-tar"); - return 1; - case 2: - /* POSIX tar archive */ - magic_rsl_puts(r, "application/x-tar"); - return 1; - } - - /* all else fails, but it is ascii... */ - return 0; -} - - -/* - * compress routines: zmagic() - returns 0 if not recognized, uncompresses - * and prints information if recognized uncompress(s, method, old, n, newch) - * - uncompress old into new, using method, return sizeof new - */ - -static struct { - char *magic; - apr_size_t maglen; - char *argv[3]; - int silent; - char *encoding; /* MUST be lowercase */ -} compr[] = { - - /* we use gzip here rather than uncompress because we have to pass - * it a full filename -- and uncompress only considers filenames - * ending with .Z - */ - { - "\037\235", 2, { - "gzip", "-dcq", NULL - }, 0, "x-compress" - }, - { - "\037\213", 2, { - "gzip", "-dcq", NULL - }, 1, "x-gzip" - }, - /* - * XXX pcat does not work, cause I don't know how to make it read stdin, - * so we use gzip - */ - { - "\037\036", 2, { - "gzip", "-dcq", NULL - }, 0, "x-gzip" - }, -}; - -static int ncompr = sizeof(compr) / sizeof(compr[0]); - -static int zmagic(request_rec *r, unsigned char *buf, apr_size_t nbytes) -{ - unsigned char *newbuf; - int newsize; - int i; - - for (i = 0; i < ncompr; i++) { - if (nbytes < compr[i].maglen) - continue; - if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0) - break; - } - - if (i == ncompr) - return 0; - - if ((newsize = uncompress(r, i, &newbuf, nbytes)) > 0) { - if (tryit(r, newbuf, newsize, 0) != OK) { - return 0; - } - - /* set encoding type in the request record */ - r->content_encoding = compr[i].encoding; - } - return 1; -} - - -struct uncompress_parms { - request_rec *r; - int method; -}; - -static int create_uncompress_child(struct uncompress_parms *parm, apr_pool_t *cntxt, - apr_file_t **pipe_in) -{ - int rc = 1; - const char *new_argv[4]; - const char *const *env; - request_rec *r = parm->r; - apr_pool_t *child_context = cntxt; - apr_procattr_t *procattr; - apr_proc_t *procnew; - - /* XXX missing 1.3 logic: - * - * what happens when !compr[parm->method].silent? - * Should we create the err pipe, read it, and copy to the log? - */ - - env = (const char *const *)ap_create_environment(child_context, r->subprocess_env); - - if ((apr_procattr_create(&procattr, child_context) != APR_SUCCESS) || - (apr_procattr_io_set(procattr, APR_FULL_BLOCK, - APR_FULL_BLOCK, APR_NO_PIPE) != APR_SUCCESS) || - (apr_procattr_dir_set(procattr, r->filename) != APR_SUCCESS) || - (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS)) { - /* Something bad happened, tell the world. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_ENOPROC, r, - "couldn't setup child process: %s", r->filename); - } - else { - new_argv[0] = compr[parm->method].argv[0]; - new_argv[1] = compr[parm->method].argv[1]; - new_argv[2] = r->filename; - new_argv[3] = NULL; - - procnew = apr_pcalloc(child_context, sizeof(*procnew)); - rc = apr_proc_create(procnew, compr[parm->method].argv[0], - new_argv, env, procattr, child_context); - - if (rc != APR_SUCCESS) { - /* Bad things happened. Everyone should have cleaned up. */ - ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_ENOPROC, r, - MODNAME ": could not execute `%s'.", - compr[parm->method].argv[0]); - } - else { - apr_pool_note_subprocess(child_context, procnew, APR_KILL_AFTER_TIMEOUT); - *pipe_in = procnew->out; - } - } - - return (rc); -} - -static int uncompress(request_rec *r, int method, - unsigned char **newch, apr_size_t n) -{ - struct uncompress_parms parm; - apr_file_t *pipe_out = NULL; - apr_pool_t *sub_context; - apr_status_t rv; - - parm.r = r; - parm.method = method; - - /* We make a sub_pool so that we can collect our child early, otherwise - * there are cases (i.e. generating directory indicies with mod_autoindex) - * where we would end up with LOTS of zombies. - */ - if (apr_pool_create(&sub_context, r->pool) != APR_SUCCESS) - return -1; - - if ((rv = create_uncompress_child(&parm, sub_context, &pipe_out)) != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, - MODNAME ": couldn't spawn uncompress process: %s", r->uri); - return -1; - } - - *newch = (unsigned char *) apr_palloc(r->pool, n); - rv = apr_file_read(pipe_out, *newch, &n); - if (n == 0) { - apr_pool_destroy(sub_context); - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, - MODNAME ": read failed %s", r->filename); - return -1; - } - apr_pool_destroy(sub_context); - return n; -} - -/* - * is_tar() -- figure out whether file is a tar archive. - * - * Stolen (by author of file utility) from the public domain tar program: Public - * Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu). - * - * @(#)list.c 1.18 9/23/86 Public Domain - gnu $Id: mod_mime_magic.c,v 1.7 - * 1997/06/24 00:41:02 ikluft Exp ikluft $ - * - * Comments changed and some code/comments reformatted for file command by Ian - * Darwin. - */ - -#define isodigit(c) (((unsigned char)(c) >= '0') && ((unsigned char)(c) <= '7')) - -/* - * Return 0 if the checksum is bad (i.e., probably not a tar archive), 1 for - * old UNIX tar file, 2 for Unix Std (POSIX) tar file. - */ - -static int is_tar(unsigned char *buf, apr_size_t nbytes) -{ - register union record *header = (union record *) buf; - register int i; - register long sum, recsum; - register char *p; - - if (nbytes < sizeof(union record)) - return 0; - - recsum = from_oct(8, header->header.chksum); - - sum = 0; - p = header->charptr; - for (i = sizeof(union record); --i >= 0;) { - /* - * We can't use unsigned char here because of old compilers, e.g. V7. - */ - sum += 0xFF & *p++; - } - - /* Adjust checksum to count the "chksum" field as blanks. */ - for (i = sizeof(header->header.chksum); --i >= 0;) - sum -= 0xFF & header->header.chksum[i]; - sum += ' ' * sizeof header->header.chksum; - - if (sum != recsum) - return 0; /* Not a tar archive */ - - if (0 == strcmp(header->header.magic, TMAGIC)) - return 2; /* Unix Standard tar archive */ - - return 1; /* Old fashioned tar archive */ -} - - -/* - * Quick and dirty octal conversion. - * - * Result is -1 if the field is invalid (all blank, or nonoctal). - */ -static long from_oct(int digs, char *where) -{ - register long value; - - while (apr_isspace(*where)) { /* Skip spaces */ - where++; - if (--digs <= 0) - return -1; /* All blank field */ - } - value = 0; - while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */ - value = (value << 3) | (*where++ - '0'); - --digs; - } - - if (digs > 0 && *where && !apr_isspace(*where)) - return -1; /* Ended on non-space/nul */ - - return value; -} - -/* - * Check for file-revision suffix - * - * This is for an obscure document control system used on an intranet. - * The web representation of each file's revision has an @1, @2, etc - * appended with the revision number. This needs to be stripped off to - * find the file suffix, which can be recognized by sending the name back - * through a sub-request. The base file name (without the @num suffix) - * must exist because its type will be used as the result. - */ -static int revision_suffix(request_rec *r) -{ - int suffix_pos, result; - char *sub_filename; - request_rec *sub; - -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": revision_suffix checking %s", r->filename); -#endif /* MIME_MAGIC_DEBUG */ - - /* check for recognized revision suffix */ - suffix_pos = strlen(r->filename) - 1; - if (!apr_isdigit(r->filename[suffix_pos])) { - return 0; - } - while (suffix_pos >= 0 && apr_isdigit(r->filename[suffix_pos])) - suffix_pos--; - if (suffix_pos < 0 || r->filename[suffix_pos] != '@') { - return 0; - } - - /* perform sub-request for the file name without the suffix */ - result = 0; - sub_filename = apr_pstrndup(r->pool, r->filename, suffix_pos); -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": subrequest lookup for %s", sub_filename); -#endif /* MIME_MAGIC_DEBUG */ - sub = ap_sub_req_lookup_file(sub_filename, r, NULL); - - /* extract content type/encoding/language from sub-request */ - if (sub->content_type) { - ap_set_content_type(r, apr_pstrdup(r->pool, sub->content_type)); -#if MIME_MAGIC_DEBUG - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, - MODNAME ": subrequest %s got %s", - sub_filename, r->content_type); -#endif /* MIME_MAGIC_DEBUG */ - if (sub->content_encoding) - r->content_encoding = - apr_pstrdup(r->pool, sub->content_encoding); - if (sub->content_languages) { - int n; - r->content_languages = apr_array_copy(r->pool, - sub->content_languages); - for (n = 0; n < r->content_languages->nelts; ++n) { - char **lang = ((char **)r->content_languages->elts) + n; - *lang = apr_pstrdup(r->pool, *lang); - } - } - result = 1; - } - - /* clean up */ - ap_destroy_sub_req(sub); - - return result; -} - -/* - * initialize the module - */ -static int magic_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_server) -{ - int result; - magic_server_config_rec *conf; - magic_server_config_rec *main_conf; - server_rec *s; -#if MIME_MAGIC_DEBUG - struct magic *m, *prevm; -#endif /* MIME_MAGIC_DEBUG */ - - main_conf = ap_get_module_config(main_server->module_config, &mime_magic_module); - for (s = main_server; s; s = s->next) { - conf = ap_get_module_config(s->module_config, &mime_magic_module); - if (conf->magicfile == NULL && s != main_server) { - /* inherits from the parent */ - *conf = *main_conf; - } - else if (conf->magicfile) { - result = apprentice(s, p); - if (result == -1) - return OK; -#if MIME_MAGIC_DEBUG - prevm = 0; - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, - MODNAME ": magic_init 1 test"); - for (m = conf->magic; m; m = m->next) { - if (apr_isprint((((unsigned long) m) >> 24) & 255) && - apr_isprint((((unsigned long) m) >> 16) & 255) && - apr_isprint((((unsigned long) m) >> 8) & 255) && - apr_isprint(((unsigned long) m) & 255)) { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, - MODNAME ": magic_init 1: POINTER CLOBBERED! " - "m=\"%c%c%c%c\" line=%d", - (((unsigned long) m) >> 24) & 255, - (((unsigned long) m) >> 16) & 255, - (((unsigned long) m) >> 8) & 255, - ((unsigned long) m) & 255, - prevm ? prevm->lineno : -1); - break; - } - prevm = m; - } -#endif - } - } - return OK; -} - -/* - * Find the Content-Type from any resource this module has available - */ - -static int magic_find_ct(request_rec *r) -{ - int result; - magic_server_config_rec *conf; - - /* the file has to exist */ - if (r->finfo.filetype == 0 || !r->filename) { - return DECLINED; - } - - /* was someone else already here? */ - if (r->content_type) { - return DECLINED; - } - - conf = ap_get_module_config(r->server->module_config, &mime_magic_module); - if (!conf || !conf->magic) { - return DECLINED; - } - - /* initialize per-request info */ - if (!magic_set_config(r)) { - return HTTP_INTERNAL_SERVER_ERROR; - } - - /* try excluding file-revision suffixes */ - if (revision_suffix(r) != 1) { - /* process it based on the file contents */ - if ((result = magic_process(r)) != OK) { - return result; - } - } - - /* if we have any results, put them in the request structure */ - return magic_rsl_to_request(r); -} - -static void register_hooks(apr_pool_t *p) -{ - static const char * const aszPre[]={ "mod_mime.c", NULL }; - - /* mod_mime_magic should be run after mod_mime, if at all. */ - - ap_hook_type_checker(magic_find_ct, aszPre, NULL, APR_HOOK_MIDDLE); - ap_hook_post_config(magic_init, NULL, NULL, APR_HOOK_FIRST); -} - -/* - * Apache API module interface - */ - -module AP_MODULE_DECLARE_DATA mime_magic_module = -{ - STANDARD20_MODULE_STUFF, - NULL, /* dir config creator */ - NULL, /* dir merger --- default is to override */ - create_magic_server_config, /* server config */ - merge_magic_server_config, /* merge server config */ - mime_magic_cmds, /* command apr_table_t */ - register_hooks /* register hooks */ -}; - - |