diff -Nurap ../lighttpd-1.4.26/src/Makefile.am ./src/Makefile.am --- ../lighttpd-1.4.26/src/Makefile.am 2010-02-02 07:28:20.000000000 +0800 +++ ./src/Makefile.am 2010-04-07 11:41:52.000000000 +0800 @@ -264,6 +264,10 @@ mod_accesslog_la_SOURCES = mod_accesslog mod_accesslog_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_accesslog_la_LIBADD = $(common_libadd) +lib_LTLIBRARIES += mod_mimemagic.la +mod_mimemagic_la_SOURCES = mod_mimemagic.c +mod_mimemagic_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +mod_mimemagic_la_LIBADD = $(common_libadd) hdr = server.h buffer.h network.h log.h keyvalue.h \ response.h request.h fastcgi.h chunk.h \ diff -Nurap ../lighttpd-1.4.26/src/Makefile.in ./src/Makefile.in --- ../lighttpd-1.4.26/src/Makefile.in 2010-02-08 04:05:44.000000000 +0800 +++ ./src/Makefile.in 2010-04-07 11:43:59.000000000 +0800 @@ -361,6 +361,13 @@ mod_webdav_la_OBJECTS = $(am_mod_webdav_ mod_webdav_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(mod_webdav_la_CFLAGS) \ $(CFLAGS) $(mod_webdav_la_LDFLAGS) $(LDFLAGS) -o $@ +mod_mimemagic_la_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_mod_mimemagic_la_OBJECTS = mod_mimemagic.lo +mod_mimemagic_la_OBJECTS = $(am_mod_mimemagic_la_OBJECTS) +mod_mimemagic_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(mod_mimemagic_la_LDFLAGS) $(LDFLAGS) \ + -o $@ PROGRAMS = $(noinst_PROGRAMS) $(sbin_PROGRAMS) am_lemon_OBJECTS = lemon.$(OBJEXT) lemon_OBJECTS = $(am_lemon_OBJECTS) @@ -460,7 +467,8 @@ SOURCES = $(liblightcomp_la_SOURCES) $(m $(mod_ssi_la_SOURCES) $(mod_staticfile_la_SOURCES) \ $(mod_status_la_SOURCES) $(mod_trigger_b4_dl_la_SOURCES) \ $(mod_userdir_la_SOURCES) $(mod_usertrack_la_SOURCES) \ - $(mod_webdav_la_SOURCES) $(lemon_SOURCES) $(lighttpd_SOURCES) \ + $(mod_webdav_la_SOURCES) $(mod_mimemagic_la_SOURCES) \ + $(lemon_SOURCES) $(lighttpd_SOURCES) \ $(lighttpd_angel_SOURCES) $(proc_open_SOURCES) DIST_SOURCES = $(am__liblightcomp_la_SOURCES_DIST) \ $(mod_access_la_SOURCES) $(mod_accesslog_la_SOURCES) \ @@ -479,6 +487,7 @@ DIST_SOURCES = $(am__liblightcomp_la_SOU $(mod_staticfile_la_SOURCES) $(mod_status_la_SOURCES) \ $(mod_trigger_b4_dl_la_SOURCES) $(mod_userdir_la_SOURCES) \ $(mod_usertrack_la_SOURCES) $(mod_webdav_la_SOURCES) \ + $(mod_mimemagic_la_SOURCES) \ $(lemon_SOURCES) $(am__lighttpd_SOURCES_DIST) \ $(lighttpd_angel_SOURCES) $(proc_open_SOURCES) HEADERS = $(noinst_HEADERS) @@ -669,7 +678,7 @@ lib_LTLIBRARIES = $(am__append_1) mod_fl mod_ssi.la mod_secdownload.la mod_expire.la mod_evhost.la \ mod_simple_vhost.la mod_fastcgi.la mod_extforward.la \ mod_access.la mod_compress.la mod_auth.la mod_rewrite.la \ - mod_redirect.la mod_status.la mod_accesslog.la + mod_redirect.la mod_status.la mod_accesslog.la mod_mimemagic.la @NO_RDYNAMIC_TRUE@liblightcomp_la_SOURCES = $(common_src) @NO_RDYNAMIC_TRUE@liblightcomp_la_CFLAGS = $(AM_CFLAGS) @NO_RDYNAMIC_TRUE@liblightcomp_la_LDFLAGS = -avoid-version -no-undefined @@ -776,6 +785,9 @@ mod_status_la_LIBADD = $(common_libadd) mod_accesslog_la_SOURCES = mod_accesslog.c mod_accesslog_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined mod_accesslog_la_LIBADD = $(common_libadd) +mod_mimemagic_la_SOURCES = mod_mimemagic.c +mod_mimemagic_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined +mod_mimemagic_la_LIBADD = $(common_libadd) hdr = server.h buffer.h network.h log.h keyvalue.h \ response.h request.h fastcgi.h chunk.h \ settings.h http_chunk.h http_auth_digest.h \ @@ -912,6 +924,8 @@ mod_indexfile.la: $(mod_indexfile_la_OBJ $(AM_V_CCLD)$(mod_indexfile_la_LINK) -rpath $(libdir) $(mod_indexfile_la_OBJECTS) $(mod_indexfile_la_LIBADD) $(LIBS) mod_magnet.la: $(mod_magnet_la_OBJECTS) $(mod_magnet_la_DEPENDENCIES) $(AM_V_CCLD)$(mod_magnet_la_LINK) -rpath $(libdir) $(mod_magnet_la_OBJECTS) $(mod_magnet_la_LIBADD) $(LIBS) +mod_mimemagic.la: $(mod_mimemagic_la_OBJECTS) $(mod_mimemagic_la_DEPENDENCIES) + $(AM_V_CCLD)$(mod_mimemagic_la_LINK) -rpath $(libdir) $(mod_mimemagic_la_OBJECTS) $(mod_mimemagic_la_LIBADD) $(LIBS) mod_mysql_vhost.la: $(mod_mysql_vhost_la_OBJECTS) $(mod_mysql_vhost_la_DEPENDENCIES) $(AM_V_CCLD)$(mod_mysql_vhost_la_LINK) -rpath $(libdir) $(mod_mysql_vhost_la_OBJECTS) $(mod_mysql_vhost_la_LIBADD) $(LIBS) mod_proxy.la: $(mod_proxy_la_OBJECTS) $(mod_proxy_la_DEPENDENCIES) @@ -1108,6 +1122,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_indexfile.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_magnet_la-mod_magnet.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_magnet_la-mod_magnet_cache.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_mimemagic.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_proxy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_redirect.Plo@am__quote@ diff -Nurap ../lighttpd-1.4.26/src/mod_mimemagic.c ./src/mod_mimemagic.c --- ../lighttpd-1.4.26/src/mod_mimemagic.c 1970-01-01 08:00:00.000000000 +0800 +++ ./src/mod_mimemagic.c 2010-04-07 11:41:21.000000000 +0800 @@ -0,0 +1,1177 @@ +/* based on mod_mime_magic.c of apache server 2.0 */ + +/* + * +Copyright (c) 2010 QUE Hongyu + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + */ + +/* 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 + * 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 +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "base.h" +#include "log.h" +#include "buffer.h" +#include "plugin.h" +#include "stat_cache.h" +#include "response.h" + +#define CONFIG_MIMEMAGIC_FILE "mimemagic.file" +#define CONFIG_MIMEMAGIC_OVERRIDE_GLOBAL_MIMETYPE "mimemagic.override-global-mimetype" + +#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 + +/* limits how much work we do to figure out text files */ +#define 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 */ +}; + +typedef struct +{ + buffer *magic_file; + struct magic *magics; + int override_global_mimetype; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + + plugin_config **config_storage; + + plugin_config conf; +} plugin_data; + +/* init the plugin data */ +static void * +mod_mimemagic_init(void) +{ + plugin_data *p; + p = calloc(1, sizeof(*p)); + + return p; +} + +static handler_t +mod_mimemagic_free(server *srv, void *p_d) +{ + plugin_data *p = p_d; + size_t i; + struct magic *m; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (!s) continue; + + buffer_free(s->magic_file); + m = s->magics; + + while(m) { + s->magics = m->next; + free(m); + m = s->magics; + } + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +#define EATAB { while (isspace(*l)) ++l;} + +/* Single hex char to int; -1 if not a hex char. */ +static int +hextoint(int c) +{ + if (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; +} + +/* + * extend the sign bit if the comparison is to be signed + */ +static unsigned long +magic_signextend(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: + return -1; + } + } + return v; +} + +/* + * 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 * +magic_getstr(register char *s, register char *p, int plen, int *slen) +{ + char *origp = p; + char *pmax = p + plen - 1; + register int c; + register int val; + + while ((c = *s++) != '\0') { + if (isspace(c)) + break; + if (p >= pmax) + 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; +} + +/* + * 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 +magic_getvalue(struct magic *m, char **p) +{ + int slen; + + if (m->type == STRING) { + *p = magic_getstr(*p, m->value.s, sizeof(m->value.s), &slen); + m->vallen = slen; + } else if (m->reln != 'x') { + m->value.l = magic_signextend(m, strtol(*p, p, 0)); + } + return 0; +} + +/* + * parse one line from magic file, put into magic[index++] if valid + */ +static struct magic * +magic_parse(char *l, int lineno) +{ + struct magic *m; + char *t, *s; + + /* allocate magic structure entry */ + m = (struct magic *) calloc(1, sizeof(struct magic)); + if (m == NULL) return NULL; + + /* append to linked list */ + m->next = NULL; + + /* 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: + break; + } + l++; + } + s = l; + if (*l == '+' || *l == '-') + l++; + if (isdigit((unsigned char) *l)) { + m->in.offset = strtol(l, &t, 0); + if (*s == '-') + m->in.offset = -m->in.offset; + } else { + t = l; + } + + l = t; + } + + while (isdigit((unsigned char) *l)) + ++l; + EATAB; + + 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 { + free(m); + return NULL; + } + + /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */ + if (*l == '&') { + ++l; + m->mask = magic_signextend(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' && isspace(l[1])) { + m->reln = *l; + ++l; + goto GetDesc; /* Bill The Cat */ + } + m->reln = '='; + break; + } + EATAB; + + if (magic_getvalue(m, &l)) { + free(m); + return NULL; + } + /* + * 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'; + + return m; +} + +/* + * apprentice - load configuration from the magic file r + * API request record + */ +static struct magic * +magic_apprentice(const char *file) +{ + char line[BUFSIZ + 1]; + int errs = 0; + int lineno; + struct magic *tail = NULL, *m, *root = NULL; + FILE *f; + + if (file == NULL || file[0] == '\0') return NULL; + + f = fopen(file, "rb"); + if (f == NULL) return NULL; + + /* parse it */ + for (lineno = 1; fgets(line, BUFSIZ, f) != NULL; 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 + && isspace(*last)) { + *last = '\0'; + --last; + } + + /* skip leading whitespace */ + ws_offset = 0; + while (line[ws_offset] && isspace(line[ws_offset])) { + ws_offset++; + } + + /* skip blank lines */ + if (line[ws_offset] == 0) { + continue; + } + + /* comment, do not parse */ + if (line[ws_offset] == '#') + continue; + + /* parse it */ + m = magic_parse(line + ws_offset, lineno); + if (m == NULL) + ++errs; + else { + m->next = NULL; + if (root == NULL) { + root = tail = m; + } else { + tail->next = m; + tail = m; + } + } + } + + fclose(f); + + return root; +} + +/* + * Convert the byte order of the data we are looking at + */ +static int +magic_mconvert(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: + return 0; + } +} + +/* return 1 if found */ +static int +magic_mget(union VALUETYPE *p, unsigned char *s, struct magic *m, uint32_t nbytes) +{ + long offset = m->offset; + + if (offset + sizeof(union VALUETYPE) > nbytes) return 0; + + memcpy(p, s + offset, sizeof(union VALUETYPE)); + + if (!magic_mconvert(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 (!magic_mconvert(p, m)) + return 0; + } + return 1; +} + +/* return 1 if matched */ +static int +magic_mcheck(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')) { + 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 */ + return 0; + } + + v = magic_signextend(m, v) & m->mask; + + switch (m->reln) { + case 'x': + matched = 1; + break; + + case '!': + matched = v != l; + break; + + case '=': + matched = v == l; + break; + + case '>': + if (m->flag & UNSIGNED) { + matched = v > l; + } else { + matched = (long) v > (long) l; + } + break; + + case '<': + if (m->flag & UNSIGNED) { + matched = v < l; + } else { + matched = (long) v < (long) l; + } + break; + + case '&': + matched = (v & l) == l; + break; + + case '^': + matched = (v & l) != l; + break; + + default: + /* bogosity, pretend it didn't match */ + matched = 0; + break; + } + + return matched; +} + +/* + * 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 +magic_match(unsigned char *s, int nbytes, struct magic *root, char *result) +{ + int cont_level = 0; + int need_separator = 0, r = 0; + union VALUETYPE p; + struct magic *m; + + if (s == NULL || root == NULL || result == NULL) return 0; + + for (m = root; m; m = m->next) { + /* check if main entry matches */ + if (!magic_mget(&p, s, m, nbytes) || + !magic_mcheck(&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)) { + /* + * 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 */ + + /* print the match */ + // magic_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; + strcpy(result, m->desc); + } + /* and any continuations that match */ + cont_level++; + + m = m->next; + while (m && (m->cont_level != 0)) { + 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 (magic_mget(&p, s, m, nbytes) && + magic_mcheck(&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') + ) { + strcat(result, " "); + need_separator = 0; + } + + strcat(result, m->desc); + + 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; + } + /* found match */ + r = strlen(result); + return r; + } + return 0; /* no match at all */ +} + +static handler_t +mod_mimemagic_set_defaults(server *srv, void *p_d) +{ + plugin_data *p = p_d; + size_t i = 0; + + config_values_t cv[] = { + { CONFIG_MIMEMAGIC_FILE, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { CONFIG_MIMEMAGIC_OVERRIDE_GLOBAL_MIMETYPE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + + if (!p) return HANDLER_ERROR; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s; + array *ca; + + s = calloc(1, sizeof(plugin_config)); + s->magic_file = buffer_init(); + s->magics = NULL; + s->override_global_mimetype = 0; + + cv[0].destination = s->magic_file; + cv[1].destination = &(s->override_global_mimetype); + + p->config_storage[i] = s; + ca = ((data_config *)srv->config_context->data[i])->value; + + if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) { + return HANDLER_ERROR; + } + + if (s->magic_file->used) { + /* magic file is set */ + s->magics = magic_apprentice(s->magic_file->ptr); + if (s->magics == NULL) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "parse magic file", s->magic_file, "failed"); + } + } + + } + + return HANDLER_GO_ON; +} + +#define PATCH_OPTION(x) p->conf.x = s->x; + +static int +mod_mimemagic_patch_connection(server *srv, connection *con, plugin_data *p) +{ + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH_OPTION(magics); + PATCH_OPTION(override_global_mimetype); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_MIMEMAGIC_FILE))) { + PATCH_OPTION(magics); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_MIMEMAGIC_OVERRIDE_GLOBAL_MIMETYPE))) { + PATCH_OPTION(override_global_mimetype); + } + } + } + + return 0; +} + +handler_t +mod_mimemagic_subrequest(server *srv, connection *con, void *p_d) +{ + plugin_data *p = p_d; + stat_cache_entry *sce = NULL; + FILE *fp = NULL; + char result[1024]; + unsigned char buf[HOWMANY + 1]; /* one extra for terminating '\0' */ + uint32_t nbytes = 0; /* number of bytes read from a datafile */ + int r; + + /* someone else has done a decision for us */ + if (con->http_status != 0) return HANDLER_GO_ON; + if (con->uri.path->used == 0) return HANDLER_GO_ON; + if (con->physical.path->used == 0) return HANDLER_GO_ON; + + /* someone else has handled this request */ + if (con->mode != DIRECT) return HANDLER_GO_ON; + if (con->file_finished) return HANDLER_GO_ON; + + /* we only handle GET, POST and HEAD */ + switch(con->request.http_method) { + case HTTP_METHOD_GET: + case HTTP_METHOD_POST: + case HTTP_METHOD_HEAD: + break; + default: + return HANDLER_GO_ON; + } + + mod_mimemagic_patch_connection(srv, con, p); + + if (p->conf.magics == NULL) return HANDLER_GO_ON; + + if (con->conf.log_request_handling) + log_error_write(srv, __FILE__, __LINE__, "s", "-- handling in mod_mimemagic_subrequest"); + + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { + /* file doesn't exist */ + return HANDLER_GO_ON; + } + + /* we only handline regular files */ +#ifdef HAVE_LSTAT + if ((sce->is_symlink == 1) && !con->conf.follow_symlink) { + return HANDLER_GO_ON; + } +#endif + if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON; + + /* set response content-type, if not set already */ + if ((p->conf.override_global_mimetype || buffer_is_empty(sce->content_type)) && + (NULL == array_get_element(con->response.headers, "Content-Type"))) { + fp = fopen(con->physical.path->ptr, "rb"); + if (fp == NULL) return HANDLER_GO_ON; + + /* + * try looking at the first HOWMANY bytes + */ + nbytes = sizeof(buf) - 1; + r = fread(buf, 1, nbytes, fp); + fclose(fp); + if (r > 0) { + buf[r++] = '\0'; + result[0] = '\0'; + r = magic_match(buf, r, p->conf.magics, result); + if (r > 0) response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), result, r); + } + } + + return HANDLER_GO_ON; +} + +int +mod_mimemagic_plugin_init(plugin *p) +{ + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("mimemagic"); + + p->init = mod_mimemagic_init; + p->handle_subrequest_start = mod_mimemagic_subrequest; + p->set_defaults = mod_mimemagic_set_defaults; + p->cleanup = mod_mimemagic_free; + + p->data = NULL; + + return 0; +}