Docs ModMimeMagic ยป lighttpd-1.4.26-mod_mimemagic.patch
./src/Makefile.am 2010-04-07 11:41:52.000000000 +0800 | ||
---|---|---|
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 \
|
./src/Makefile.in 2010-04-07 11:43:59.000000000 +0800 | ||
---|---|---|
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)
|
||
... | ... | |
$(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) \
|
||
... | ... | |
$(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)
|
||
... | ... | |
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
|
||
... | ... | |
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 \
|
||
... | ... | |
$(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)
|
||
... | ... | |
@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@
|
./src/mod_mimemagic.c 2010-04-07 11:41:21.000000000 +0800 | ||
---|---|---|
/* 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 <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 <ctype.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#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;
|
||
}
|