Project

General

Profile

lighttpd-1.4.26-mod_mimemagic.patch

mod_mimemagic patch for lighttpd 1.4.26 - qhy, 2010-04-07 05:53

View differences:

./src/Makefile.am 2010-04-07 11:41:52.000000000 +0800
264 264
mod_accesslog_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
265 265
mod_accesslog_la_LIBADD = $(common_libadd)
266 266

  
267
lib_LTLIBRARIES += mod_mimemagic.la
268
mod_mimemagic_la_SOURCES = mod_mimemagic.c
269
mod_mimemagic_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
270
mod_mimemagic_la_LIBADD = $(common_libadd)
267 271

  
268 272
hdr = server.h buffer.h network.h log.h keyvalue.h \
269 273
      response.h request.h fastcgi.h chunk.h \
./src/Makefile.in 2010-04-07 11:43:59.000000000 +0800
361 361
mod_webdav_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
362 362
	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(mod_webdav_la_CFLAGS) \
363 363
	$(CFLAGS) $(mod_webdav_la_LDFLAGS) $(LDFLAGS) -o $@
364
mod_mimemagic_la_DEPENDENCIES = $(am__DEPENDENCIES_2)
365
am_mod_mimemagic_la_OBJECTS = mod_mimemagic.lo
366
mod_mimemagic_la_OBJECTS = $(am_mod_mimemagic_la_OBJECTS)
367
mod_mimemagic_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
368
	$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
369
	$(AM_CFLAGS) $(CFLAGS) $(mod_mimemagic_la_LDFLAGS) $(LDFLAGS) \
370
	-o $@
364 371
PROGRAMS = $(noinst_PROGRAMS) $(sbin_PROGRAMS)
365 372
am_lemon_OBJECTS = lemon.$(OBJEXT)
366 373
lemon_OBJECTS = $(am_lemon_OBJECTS)
......
460 467
	$(mod_ssi_la_SOURCES) $(mod_staticfile_la_SOURCES) \
461 468
	$(mod_status_la_SOURCES) $(mod_trigger_b4_dl_la_SOURCES) \
462 469
	$(mod_userdir_la_SOURCES) $(mod_usertrack_la_SOURCES) \
463
	$(mod_webdav_la_SOURCES) $(lemon_SOURCES) $(lighttpd_SOURCES) \
470
	$(mod_webdav_la_SOURCES) $(mod_mimemagic_la_SOURCES) \
471
       	$(lemon_SOURCES) $(lighttpd_SOURCES) \
464 472
	$(lighttpd_angel_SOURCES) $(proc_open_SOURCES)
465 473
DIST_SOURCES = $(am__liblightcomp_la_SOURCES_DIST) \
466 474
	$(mod_access_la_SOURCES) $(mod_accesslog_la_SOURCES) \
......
479 487
	$(mod_staticfile_la_SOURCES) $(mod_status_la_SOURCES) \
480 488
	$(mod_trigger_b4_dl_la_SOURCES) $(mod_userdir_la_SOURCES) \
481 489
	$(mod_usertrack_la_SOURCES) $(mod_webdav_la_SOURCES) \
490
	$(mod_mimemagic_la_SOURCES) \
482 491
	$(lemon_SOURCES) $(am__lighttpd_SOURCES_DIST) \
483 492
	$(lighttpd_angel_SOURCES) $(proc_open_SOURCES)
484 493
HEADERS = $(noinst_HEADERS)
......
669 678
	mod_ssi.la mod_secdownload.la mod_expire.la mod_evhost.la \
670 679
	mod_simple_vhost.la mod_fastcgi.la mod_extforward.la \
671 680
	mod_access.la mod_compress.la mod_auth.la mod_rewrite.la \
672
	mod_redirect.la mod_status.la mod_accesslog.la
681
	mod_redirect.la mod_status.la mod_accesslog.la mod_mimemagic.la
673 682
@NO_RDYNAMIC_TRUE@liblightcomp_la_SOURCES = $(common_src)
674 683
@NO_RDYNAMIC_TRUE@liblightcomp_la_CFLAGS = $(AM_CFLAGS)
675 684
@NO_RDYNAMIC_TRUE@liblightcomp_la_LDFLAGS = -avoid-version -no-undefined
......
776 785
mod_accesslog_la_SOURCES = mod_accesslog.c
777 786
mod_accesslog_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
778 787
mod_accesslog_la_LIBADD = $(common_libadd)
788
mod_mimemagic_la_SOURCES = mod_mimemagic.c
789
mod_mimemagic_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
790
mod_mimemagic_la_LIBADD = $(common_libadd)
779 791
hdr = server.h buffer.h network.h log.h keyvalue.h \
780 792
      response.h request.h fastcgi.h chunk.h \
781 793
      settings.h http_chunk.h http_auth_digest.h \
......
912 924
	$(AM_V_CCLD)$(mod_indexfile_la_LINK) -rpath $(libdir) $(mod_indexfile_la_OBJECTS) $(mod_indexfile_la_LIBADD) $(LIBS)
913 925
mod_magnet.la: $(mod_magnet_la_OBJECTS) $(mod_magnet_la_DEPENDENCIES) 
914 926
	$(AM_V_CCLD)$(mod_magnet_la_LINK) -rpath $(libdir) $(mod_magnet_la_OBJECTS) $(mod_magnet_la_LIBADD) $(LIBS)
927
mod_mimemagic.la: $(mod_mimemagic_la_OBJECTS) $(mod_mimemagic_la_DEPENDENCIES) 
928
	$(AM_V_CCLD)$(mod_mimemagic_la_LINK) -rpath $(libdir) $(mod_mimemagic_la_OBJECTS) $(mod_mimemagic_la_LIBADD) $(LIBS)
915 929
mod_mysql_vhost.la: $(mod_mysql_vhost_la_OBJECTS) $(mod_mysql_vhost_la_DEPENDENCIES) 
916 930
	$(AM_V_CCLD)$(mod_mysql_vhost_la_LINK) -rpath $(libdir) $(mod_mysql_vhost_la_OBJECTS) $(mod_mysql_vhost_la_LIBADD) $(LIBS)
917 931
mod_proxy.la: $(mod_proxy_la_OBJECTS) $(mod_proxy_la_DEPENDENCIES) 
......
1108 1122
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_indexfile.Plo@am__quote@
1109 1123
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_magnet_la-mod_magnet.Plo@am__quote@
1110 1124
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_magnet_la-mod_magnet_cache.Plo@am__quote@
1125
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_mimemagic.Plo@am__quote@
1111 1126
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_mysql_vhost_la-mod_mysql_vhost.Plo@am__quote@
1112 1127
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_proxy.Plo@am__quote@
1113 1128
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_redirect.Plo@am__quote@
./src/mod_mimemagic.c 2010-04-07 11:41:21.000000000 +0800
1
/* based on mod_mime_magic.c of apache server 2.0 */
2

  
3
/*
4
 *
5
Copyright (c) 2010 QUE Hongyu
6

  
7
Redistribution and use in source and binary forms, with or without
8
modification, are permitted provided that the following conditions
9
are met:
10
1. Redistributions of source code must retain the above copyright
11
   notice, this list of conditions and the following disclaimer.
12
2. Redistributions in binary form must reproduce the above copyright
13
   notice, this list of conditions and the following disclaimer in the
14
   documentation and/or other materials provided with the distribution.
15

  
16
THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
SUCH DAMAGE.
27

  
28
 */
29

  
30
/* Licensed to the Apache Software Foundation (ASF) under one or more
31
 * contributor license agreements.  See the NOTICE file distributed with
32
 * this work for additional information regarding copyright ownership.
33
 * The ASF licenses this file to You under the Apache License, Version 2.0
34
 * (the "License"); you may not use this file except in compliance with
35
 * the License.  You may obtain a copy of the License at
36
 *
37
 *     http://www.apache.org/licenses/LICENSE-2.0
38
 *
39
 * Unless required by applicable law or agreed to in writing, software
40
 * distributed under the License is distributed on an "AS IS" BASIS,
41
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42
 * See the License for the specific language governing permissions and
43
 * limitations under the License.
44
 */
45

  
46
/*
47
 * mod_mime_magic: MIME type lookup via file magic numbers
48
 * Copyright (c) 1996-1997 Cisco Systems, Inc.
49
 *
50
 * This software was submitted by Cisco Systems to the Apache Software Foundation in July
51
 * 1997.  Future revisions and derivatives of this source code must
52
 * acknowledge Cisco Systems as the original contributor of this module.
53
 * All other licensing and usage conditions are those of the Apache Software Foundation.
54
 *
55
 * Some of this code is derived from the free version of the file command
56
 * originally posted to comp.sources.unix.  Copyright info for that program
57
 * is included below as required.
58
 * ---------------------------------------------------------------------------
59
 * - Copyright (c) Ian F. Darwin, 1987. Written by Ian F. Darwin.
60
 *
61
 * This software is not subject to any license of the American Telephone and
62
 * Telegraph Company or of the Regents of the University of California.
63
 *
64
 * Permission is granted to anyone to use this software for any purpose on any
65
 * computer system, and to alter it and redistribute it freely, subject to
66
 * the following restrictions:
67
 *
68
 * 1. The author is not responsible for the consequences of use of this
69
 * software, no matter how awful, even if they arise from flaws in it.
70
 *
71
 * 2. The origin of this software must not be misrepresented, either by
72
 * explicit claim or by omission.  Since few users ever read sources, credits
73
 * must appear in the documentation.
74
 *
75
 * 3. Altered versions must be plainly marked as such, and must not be
76
 * misrepresented as being the original software.  Since few users ever read
77
 * sources, credits must appear in the documentation.
78
 *
79
 * 4. This notice may not be removed or altered.
80
 * -------------------------------------------------------------------------
81
 *
82
 * For compliance with Mr Darwin's terms: this has been very significantly
83
 * modified from the free "file" command.
84
 * - all-in-one file for compilation convenience when moving from one
85
 *   version of Apache to the next.
86
 * - Memory allocation is done through the Apache API's apr_pool_t structure.
87
 * - All functions have had necessary Apache API request or server
88
 *   structures passed to them where necessary to call other Apache API
89
 *   routines.  (i.e. usually for logging, files, or memory allocation in
90
 *   itself or a called function.)
91
 * - struct magic has been converted from an array to a single-ended linked
92
 *   list because it only grows one record at a time, it's only accessed
93
 *   sequentially, and the Apache API has no equivalent of realloc().
94
 * - Functions have been changed to get their parameters from the server
95
 *   configuration instead of globals.  (It should be reentrant now but has
96
 *   not been tested in a threaded environment.)
97
 * - Places where it used to print results to stdout now saves them in a
98
 *   list where they're used to set the MIME type in the Apache request
99
 *   record.
100
 * - Command-line flags have been removed since they will never be used here.
101
 *
102
 * Ian Kluft <ikluft@cisco.com>
103
 * Engineering Information Framework
104
 * Central Engineering
105
 * Cisco Systems, Inc.
106
 * San Jose, CA, USA
107
 *
108
 * Initial installation          July/August 1996
109
 * Misc bug fixes                May 1997
110
 * Submission to Apache Software Foundation    July 1997
111
 *
112
 */
113

  
114
#include <ctype.h>
115
#include <stdlib.h>
116
#include <stdio.h>
117
#include <string.h>
118
#include <errno.h>
119

  
120
#ifdef HAVE_CONFIG_H
121
#include "config.h"
122
#endif
123

  
124
#include "base.h"
125
#include "log.h"
126
#include "buffer.h"
127
#include "plugin.h"
128
#include "stat_cache.h"
129
#include "response.h"
130

  
131
#define CONFIG_MIMEMAGIC_FILE "mimemagic.file"
132
#define CONFIG_MIMEMAGIC_OVERRIDE_GLOBAL_MIMETYPE "mimemagic.override-global-mimetype"
133

  
134
#define NBYTE           4
135
#define NSHORT          5
136
#define NLONG           4
137
#define NSTRING         6
138
#define NDATE           4
139
#define NBESHORT        7
140
#define NBELONG         6
141
#define NBEDATE         6
142
#define NLESHORT        7
143
#define NLELONG         6
144
#define NLEDATE         6
145

  
146
/* limits how much work we do to figure out text files */
147
#define HOWMANY 1024
148
#define MAXDESC 50   /* max leng of text description */
149
#define MAXstring 64    /* max leng of "string" types */
150

  
151
struct magic
152
{
153
	struct magic *next;     /* link to next entry */
154
	int lineno;             /* line number from magic file */
155
	
156
	short flag;
157
#define INDIR  1            /* if '>(...)' appears,  */
158
#define UNSIGNED 2          /* comparison is unsigned */
159
	short cont_level;       /* level of ">" */
160
	
161
	struct
162
	{
163
		char type;          /* byte short long */
164
		long offset;        /* offset from indirection */
165
	} in;
166
	
167
	long offset;            /* offset to magic number */
168
	unsigned char reln;     /* relation (0=eq, '>'=gt, etc) */
169
	char type;              /* int, short, long or string. */
170
	char vallen;            /* length of string value, if any */
171
#define BYTE      1
172
#define SHORT     2
173
#define LONG      4
174
#define STRING    5
175
#define DATE      6
176
#define BESHORT   7
177
#define BELONG    8
178
#define BEDATE    9
179
#define LESHORT  10
180
#define LELONG   11
181
#define LEDATE   12
182
	
183
	union VALUETYPE
184
	{
185
		unsigned char b;
186
		unsigned short h;
187
		unsigned long l;
188
		char s[MAXstring];
189
		unsigned char hs[2];   /* 2 bytes of a fixed-endian "short" */
190
		unsigned char hl[4];   /* 2 bytes of a fixed-endian "long" */
191
	} value;                   /* either number or string */
192
	
193
	unsigned long mask;        /* mask before comparison with value */
194
	char nospflag;             /* supress space character */
195
	
196
	/* NOTE: this string is suspected of overrunning - find it! */
197
	char desc[MAXDESC];        /* description */
198
};
199

  
200
typedef struct
201
{
202
	buffer *magic_file;
203
	struct magic *magics;
204
	int override_global_mimetype;
205
} plugin_config;
206

  
207
typedef struct {
208
	PLUGIN_DATA;
209

  
210
	plugin_config **config_storage;
211

  
212
	plugin_config conf;
213
} plugin_data;
214

  
215
/* init the plugin data */
216
static void *
217
mod_mimemagic_init(void)
218
{
219
	plugin_data *p;
220
	p = calloc(1, sizeof(*p));
221

  
222
	return p;
223
}
224

  
225
static handler_t
226
mod_mimemagic_free(server *srv, void *p_d)
227
{
228
	plugin_data *p = p_d;
229
	size_t i;
230
	struct magic *m;
231

  
232
	UNUSED(srv);
233

  
234
	if (!p) return HANDLER_GO_ON;
235

  
236
	if (p->config_storage) {
237

  
238
		for (i = 0; i < srv->config_context->used; i++) {
239
			plugin_config *s = p->config_storage[i];
240

  
241
			if (!s) continue;
242

  
243
			buffer_free(s->magic_file);
244
			m = s->magics;
245

  
246
			while(m) {
247
				s->magics = m->next;
248
				free(m);
249
				m = s->magics;
250
			}
251

  
252
			free(s);
253
		}
254
		free(p->config_storage);
255
	}
256

  
257
	free(p);
258

  
259
	return HANDLER_GO_ON;
260
}
261

  
262
#define EATAB { while (isspace(*l))  ++l;}
263

  
264
/* Single hex char to int; -1 if not a hex char. */
265
static int
266
hextoint(int c)
267
{
268
	if (isdigit(c))
269
	    return c - '0';
270
	if ((c >= 'a') && (c <= 'f'))
271
	    return c + 10 - 'a';
272
	if ((c >= 'A') && (c <= 'F'))
273
	    return c + 10 - 'A';
274
	return -1;
275
}
276

  
277
/*
278
 * extend the sign bit if the comparison is to be signed
279
 */
280
static unsigned long
281
magic_signextend(struct magic *m, unsigned long v)
282
{
283
	if (!(m->flag & UNSIGNED)) {
284
		switch (m->type) {
285
			/*
286
			 * Do not remove the casts below.  They are vital. When later
287
			 * compared with the data, the sign extension must have happened.
288
			 */
289
		case BYTE:
290
			v = (char) v;
291
			break;
292
		case SHORT:
293
		case BESHORT:
294
		case LESHORT:
295
			v = (short) v;
296
			break;
297
		case DATE:
298
		case BEDATE:
299
		case LEDATE:
300
		case LONG:
301
		case BELONG:
302
		case LELONG:
303
			v = (long) v;
304
			break;
305
		case STRING:
306
			break;
307
		default:
308
			return -1;
309
		}
310
	}
311
	return v;
312
}
313

  
314
/*
315
 * Convert a string containing C character escapes.  Stop at an unescaped
316
 * space or tab. Copy the converted version to "p", returning its length in
317
 * *slen. Return updated scan pointer as function result.
318
 */
319
static char *
320
magic_getstr(register char *s, register char *p, int plen, int *slen)
321
{
322
	char *origp = p;
323
	char *pmax = p + plen - 1;
324
	register int c;
325
	register int val;
326

  
327
	while ((c = *s++) != '\0') {
328
		if (isspace(c))
329
			break;
330
		if (p >= pmax)
331
			break;
332

  
333
		if (c == '\\') {
334
			switch (c = *s++) {
335
			case '\0':
336
				goto out;
337

  
338
			default:
339
				*p++ = (char) c;
340
				break;
341

  
342
			case 'n':
343
				*p++ = '\n';
344
				break;
345

  
346
			case 'r':
347
				*p++ = '\r';
348
				break;
349

  
350
			case 'b':
351
				*p++ = '\b';
352
				break;
353

  
354
			case 't':
355
				*p++ = '\t';
356
				break;
357

  
358
			case 'f':
359
				*p++ = '\f';
360
				break;
361

  
362
			case 'v':
363
				*p++ = '\v';
364
				break;
365

  
366
				/* \ and up to 3 octal digits */
367
			case '0':
368
			case '1':
369
			case '2':
370
			case '3':
371
			case '4':
372
			case '5':
373
			case '6':
374
			case '7':
375
				val = c - '0';
376
				c = *s++;  /* try for 2 */
377
				if (c >= '0' && c <= '7') {
378
					val = (val << 3) | (c - '0');
379
					c = *s++;  /* try for 3 */
380
					if (c >= '0' && c <= '7')
381
						val = (val << 3) | (c - '0');
382
					else
383
						--s;
384
				} else {
385
					--s;
386
				}
387
				*p++ = (char) val;
388
				break;
389

  
390
				/* \x and up to 3 hex digits */
391
			case 'x':
392
				val = 'x';			/* Default if no digits */
393
				c = hextoint(*s++);   /* Get next char */
394
				if (c >= 0) {
395
					val = c;
396
					c = hextoint(*s++);
397
					if (c >= 0) {
398
						val = (val << 4) + c;
399
						c = hextoint(*s++);
400
						if (c >= 0) {
401
							val = (val << 4) + c;
402
						} else {
403
							--s;
404
						}
405
					} else {
406
						--s;
407
					}
408
				} else {
409
					--s;
410
				}
411
				*p++ = (char) val;
412
				break;
413
			}
414
		} else {
415
			*p++ = (char) c;
416
		}
417
	}
418
  out:
419
	*p = '\0';
420
	*slen = p - origp;
421
	return s;
422
}
423

  
424
/*
425
 * Read a numeric value from a pointer, into the value union of a magic
426
 * pointer, according to the magic type.  Update the string pointer to point
427
 * just after the number read.  Return 0 for success, non-zero for failure.
428
 */
429
static int
430
magic_getvalue(struct magic *m, char **p)
431
{
432
	int slen;
433

  
434
	if (m->type == STRING) {
435
		*p = magic_getstr(*p, m->value.s, sizeof(m->value.s), &slen);
436
		m->vallen = slen;
437
	} else if (m->reln != 'x') {
438
		m->value.l = magic_signextend(m, strtol(*p, p, 0));
439
	}
440
	return 0;
441
}
442

  
443
/*
444
 * parse one line from magic file, put into magic[index++] if valid
445
 */
446
static struct magic *
447
magic_parse(char *l, int lineno)
448
{
449
	struct magic *m;
450
	char *t, *s;
451

  
452
	/* allocate magic structure entry */
453
	m = (struct magic *) calloc(1, sizeof(struct magic));
454
	if (m == NULL) return NULL;
455

  
456
	/* append to linked list */
457
	m->next = NULL;
458

  
459
	/* set values in magic structure */
460
	m->flag = 0;
461
	m->cont_level = 0;
462
	m->lineno = lineno;
463

  
464
	while (*l == '>') {
465
		++l;  /* step over */
466
		m->cont_level++;
467
	}
468

  
469
	if (m->cont_level != 0 && *l == '(') {
470
		++l;  /* step over */
471
		m->flag |= INDIR;
472
	}
473

  
474
	/* get offset, then skip over it */
475
	m->offset = (int) strtol(l, &t, 0);
476
	if (l == t) {
477
		/*
478
	    ap_log_error(APLOG_MARK, APLOG_ERR, 0, serv,
479
	                MODNAME ": offset %s invalid", l);
480
			*/
481
	}
482
	l = t;
483

  
484
	if (m->flag & INDIR) {
485
		m->in.type = LONG;
486
		m->in.offset = 0;
487
		
488
		/*
489
		 * read [.lbs][+-]nnnnn)
490
		 */
491
		if (*l == '.') {
492
			switch (*++l) {
493
			case 'l':
494
				m->in.type = LONG;
495
				break;
496
			case 's':
497
				m->in.type = SHORT;
498
				break;
499
			case 'b':
500
				m->in.type = BYTE;
501
				break;
502
			default:
503
				break;
504
			}
505
			l++;
506
		}
507
		s = l;
508
		if (*l == '+' || *l == '-')
509
			l++;
510
		if (isdigit((unsigned char) *l)) {
511
			m->in.offset = strtol(l, &t, 0);
512
			if (*s == '-')
513
				m->in.offset = -m->in.offset;
514
		} else {
515
			t = l;
516
		}
517

  
518
		l = t;
519
	}
520

  
521
	while (isdigit((unsigned char) *l))
522
		++l;
523
	EATAB;
524

  
525
	if (*l == 'u') {
526
		++l;
527
		m->flag |= UNSIGNED;
528
	}
529

  
530
	/* get type, skip it */
531
	if (strncmp(l, "byte", NBYTE) == 0) {
532
		m->type = BYTE;
533
		l += NBYTE;
534
	} else if (strncmp(l, "short", NSHORT) == 0) {
535
		m->type = SHORT;
536
		l += NSHORT;
537
	} else if (strncmp(l, "long", NLONG) == 0) {
538
		m->type = LONG;
539
		l += NLONG;
540
	} else if (strncmp(l, "string", NSTRING) == 0) {
541
		m->type = STRING;
542
		l += NSTRING;
543
	} else if (strncmp(l, "date", NDATE) == 0) {
544
		m->type = DATE;
545
		l += NDATE;
546
	} else if (strncmp(l, "beshort", NBESHORT) == 0) {
547
		m->type = BESHORT;
548
		l += NBESHORT;
549
	} else if (strncmp(l, "belong", NBELONG) == 0) {
550
		m->type = BELONG;
551
		l += NBELONG;
552
	} else if (strncmp(l, "bedate", NBEDATE) == 0) {
553
		m->type = BEDATE;
554
		l += NBEDATE;
555
	} else if (strncmp(l, "leshort", NLESHORT) == 0) {
556
		m->type = LESHORT;
557
		l += NLESHORT;
558
	} else if (strncmp(l, "lelong", NLELONG) == 0) {
559
		m->type = LELONG;
560
		l += NLELONG;
561
	} else if (strncmp(l, "ledate", NLEDATE) == 0) {
562
		m->type = LEDATE;
563
		l += NLEDATE;
564
	} else {
565
		free(m);
566
		return NULL;
567
	}
568

  
569
	/* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
570
	if (*l == '&') {
571
		++l;
572
		m->mask = magic_signextend(m, strtol(l, &l, 0));
573
	}
574
	else
575
		m->mask = ~0L;
576
	EATAB;
577

  
578
	switch (*l) {
579
	case '>':
580
	case '<':
581
		/* Old-style anding: "0 byte &0x80 dynamically linked" */
582
	case '&':
583
	case '^':
584
	case '=':
585
		m->reln = *l;
586
		++l;
587
		break;
588
	case '!':
589
		if (m->type != STRING) {
590
			m->reln = *l;
591
			++l;
592
			break;
593
		}
594
		/* FALL THROUGH */
595
	default:
596
		if (*l == 'x' && isspace(l[1])) {
597
			m->reln = *l;
598
			++l;
599
			goto GetDesc;  /* Bill The Cat */
600
		}
601
		m->reln = '=';
602
		break;
603
	}
604
	EATAB;
605

  
606
	if (magic_getvalue(m, &l)) {
607
		free(m);
608
		return NULL;
609
	}
610
	/*
611
	 * now get last part - the description
612
	 */
613
  GetDesc:
614
	EATAB;
615
	if (l[0] == '\b') {
616
		++l;
617
		m->nospflag = 1;
618
	} else if ((l[0] == '\\') && (l[1] == 'b')) {
619
		++l;
620
		++l;
621
		m->nospflag = 1;
622
	} else {
623
		m->nospflag = 0;
624
	}
625
	strncpy(m->desc, l, sizeof(m->desc) - 1);
626
	m->desc[sizeof(m->desc) - 1] = '\0';
627

  
628
	return m;
629
}
630

  
631
/*
632
 * apprentice - load configuration from the magic file r
633
 *  API request record
634
 */
635
static struct magic *
636
magic_apprentice(const char *file)
637
{
638
	char line[BUFSIZ + 1];
639
	int errs = 0;
640
	int lineno;
641
	struct magic *tail = NULL, *m, *root = NULL;
642
	FILE *f;
643

  
644
	if (file == NULL || file[0] == '\0') return NULL;
645

  
646
	f = fopen(file, "rb");
647
	if (f == NULL) return NULL;
648

  
649
	/* parse it */
650
	for (lineno = 1; fgets(line, BUFSIZ, f) != NULL; lineno++) {
651
		int ws_offset;
652
		char *last = line + strlen(line) - 1; /* guaranteed that len >= 1 since an
653
	                                           * "empty" line contains a '\n'
654
	                                           */
655

  
656
		/* delete newline and any other trailing whitespace */
657
		while (last >= line
658
			   && isspace(*last)) {
659
			*last = '\0';
660
			--last;
661
		}
662

  
663
		/* skip leading whitespace */
664
		ws_offset = 0;
665
		while (line[ws_offset] && isspace(line[ws_offset])) {
666
			ws_offset++;
667
		}
668

  
669
		/* skip blank lines */
670
		if (line[ws_offset] == 0) {
671
			continue;
672
		}
673

  
674
		/* comment, do not parse */
675
		if (line[ws_offset] == '#')
676
			continue;
677

  
678
		/* parse it */
679
		m = magic_parse(line + ws_offset, lineno);
680
		if (m == NULL)
681
			++errs;
682
		else {
683
			m->next = NULL;
684
			if (root == NULL) {
685
				root = tail = m;
686
			} else {
687
				tail->next = m;
688
				tail = m;
689
			}
690
		}
691
	}
692

  
693
	fclose(f);
694

  
695
	return root;
696
}
697

  
698
/*
699
 * Convert the byte order of the data we are looking at
700
 */
701
static int
702
magic_mconvert(union VALUETYPE *p, struct magic *m)
703
{
704
	char *rt;
705

  
706
	switch (m->type) {
707
	case BYTE:
708
	case SHORT:
709
	case LONG:
710
	case DATE:
711
		return 1;
712
	case STRING:
713
		/* Null terminate and eat the return */
714
		p->s[sizeof(p->s) - 1] = '\0';
715
		if ((rt = strchr(p->s, '\n')) != NULL)
716
			*rt = '\0';
717
		return 1;
718
	case BESHORT:
719
		p->h = (short) ((p->hs[0] << 8) | (p->hs[1]));
720
		return 1;
721
	case BELONG:
722
	case BEDATE:
723
		p->l = (long)
724
			((p->hl[0] << 24) | (p->hl[1] << 16) | (p->hl[2] << 8) | (p->hl[3]));
725
		return 1;
726
	case LESHORT:
727
		p->h = (short) ((p->hs[1] << 8) | (p->hs[0]));
728
		return 1;
729
	case LELONG:
730
	case LEDATE:
731
		p->l = (long)
732
			((p->hl[3] << 24) | (p->hl[2] << 16) | (p->hl[1] << 8) | (p->hl[0]));
733
		return 1;
734
	default:
735
		return 0;
736
	}
737
}
738

  
739
/* return 1 if found */
740
static int
741
magic_mget(union VALUETYPE *p, unsigned char *s, struct magic *m, uint32_t nbytes)
742
{
743
	long offset = m->offset;
744

  
745
	if (offset + sizeof(union VALUETYPE) > nbytes) return 0;
746

  
747
	memcpy(p, s + offset, sizeof(union VALUETYPE));
748

  
749
	if (!magic_mconvert(p, m)) return 0;
750

  
751
	if (m->flag & INDIR) {
752
		switch (m->in.type) {
753
		case BYTE:
754
			offset = p->b + m->in.offset;
755
			break;
756
		case SHORT:
757
			offset = p->h + m->in.offset;
758
			break;
759
		case LONG:
760
			offset = p->l + m->in.offset;
761
			break;
762
		}
763

  
764
		if (offset + sizeof(union VALUETYPE) > nbytes)
765
					  return 0;
766

  
767
		memcpy(p, s + offset, sizeof(union VALUETYPE));
768

  
769
		if (!magic_mconvert(p, m))
770
			return 0;
771
	}
772
	return 1;
773
}
774

  
775
/* return 1 if matched */
776
static int
777
magic_mcheck(union VALUETYPE *p, struct magic *m)
778
{
779
	register unsigned long l = m->value.l;
780
	register unsigned long v;
781
	int matched;
782

  
783
	if ((m->value.s[0] == 'x') && (m->value.s[1] == '\0')) {
784
		return 1;
785
	}
786

  
787
	switch (m->type) {
788
	case BYTE:
789
		v = p->b;
790
		break;
791

  
792
	case SHORT:
793
	case BESHORT:
794
	case LESHORT:
795
		v = p->h;
796
		break;
797

  
798
	case LONG:
799
	case BELONG:
800
	case LELONG:
801
	case DATE:
802
	case BEDATE:
803
	case LEDATE:
804
		v = p->l;
805
		break;
806

  
807
	case STRING:
808
		l = 0;
809
		/*
810
		 * What we want here is: v = strncmp(m->value.s, p->s, m->vallen);
811
		 * but ignoring any nulls.  bcmp doesn't give -/+/0 and isn't
812
		 * universally available anyway.
813
		 */
814
		v = 0;
815
		{
816
			register unsigned char *a = (unsigned char *) m->value.s;
817
			register unsigned char *b = (unsigned char *) p->s;
818
			register int len = m->vallen;
819

  
820
			while (--len >= 0)
821
				if ((v = *b++ - *a++) != 0)
822
					break;
823
		}
824
		break;
825
	default:
826
		/*  bogosity, pretend that it just wasn't a match */
827
		return 0;
828
	}
829

  
830
	v = magic_signextend(m, v) & m->mask;
831

  
832
	switch (m->reln) {
833
	case 'x':
834
		matched = 1;
835
		break;
836

  
837
	case '!':
838
		matched = v != l;
839
		break;
840

  
841
	case '=':
842
		matched = v == l;
843
		break;
844

  
845
	case '>':
846
		if (m->flag & UNSIGNED) {
847
			matched = v > l;
848
		} else {
849
			matched = (long) v > (long) l;
850
		}
851
		break;
852

  
853
	case '<':
854
		if (m->flag & UNSIGNED) {
855
			matched = v < l;
856
		} else {
857
			matched = (long) v < (long) l;
858
		}
859
		break;
860

  
861
	case '&':
862
		matched = (v & l) == l;
863
		break;
864

  
865
	case '^':
866
		matched = (v & l) != l;
867
		break;
868

  
869
	default:
870
		/* bogosity, pretend it didn't match */
871
		matched = 0;
872
		break;
873
	}
874

  
875
	return matched;
876
}
877

  
878
/*
879
 * Go through the whole list, stopping if you find a match.  Process all the
880
 * continuations of that match before returning.
881
 *
882
 * We support multi-level continuations:
883
 *
884
 * At any time when processing a successful top-level match, there is a current
885
 * continuation level; it represents the level of the last successfully
886
 * matched continuation.
887
 *
888
 * Continuations above that level are skipped as, if we see one, it means that
889
 * the continuation that controls them - i.e, the lower-level continuation
890
 * preceding them - failed to match.
891
 *
892
 * Continuations below that level are processed as, if we see one, it means
893
 * we've finished processing or skipping higher-level continuations under the
894
 * control of a successful or unsuccessful lower-level continuation, and are
895
 * now seeing the next lower-level continuation and should process it.  The
896
 * current continuation level reverts to the level of the one we're seeing.
897
 *
898
 * Continuations at the current level are processed as, if we see one, there's
899
 * no lower-level continuation that may have failed.
900
 *
901
 * If a continuation matches, we bump the current continuation level so that
902
 * higher-level continuations are processed.
903
 */
904
static int
905
magic_match(unsigned char *s, int nbytes, struct magic *root, char *result)
906
{
907
	int cont_level = 0;
908
	int need_separator = 0, r = 0;
909
	union VALUETYPE p;
910
	struct magic *m;
911

  
912
	if (s == NULL || root == NULL || result == NULL) return 0;
913

  
914
	for (m = root; m; m = m->next) {
915
		/* check if main entry matches */
916
		if (!magic_mget(&p, s, m, nbytes) ||
917
			!magic_mcheck(&p, m)) {
918
			struct magic *m_cont;
919

  
920
			/*
921
			 * main entry didn't match, flush its continuations
922
			 */
923
			if (!m->next || (m->next->cont_level == 0)) {
924
				continue;
925
			}
926

  
927
			m_cont = m->next;
928
			while (m_cont && (m_cont->cont_level != 0)) {
929
				/*
930
				 * this trick allows us to keep *m in sync when the continue
931
				 * advances the pointer
932
				 */
933
				m = m_cont;
934
				m_cont = m_cont->next;
935
			}
936
			continue;
937
		}
938

  
939
		/* if we get here, the main entry rule was a match */
940
		/* this will be the last run through the loop */
941

  
942
		/* print the match */
943
		// magic_mprint(r, &p, m);
944

  
945
		/*
946
		 * If we printed something, we'll need to print a blank before we
947
		 * print something else.
948
		 */
949
		if (m->desc[0]) {
950
			need_separator = 1;
951
			strcpy(result, m->desc);
952
		}
953
		/* and any continuations that match */
954
		cont_level++;
955

  
956
		m = m->next;
957
		while (m && (m->cont_level != 0)) {
958
			if (cont_level >= m->cont_level) {
959
				if (cont_level > m->cont_level) {
960
					/*
961
					 * We're at the end of the level "cont_level"
962
					 * continuations.
963
					 */
964
					cont_level = m->cont_level;
965
				}
966

  
967
				if (magic_mget(&p, s, m, nbytes) &&
968
					magic_mcheck(&p, m)) {
969
					/*
970
					 * This continuation matched. Print its message, with a
971
					 * blank before it if the previous item printed and this
972
					 * item isn't empty.
973
					 */
974
					/* space if previous printed */
975
					if (need_separator
976
						&& (m->nospflag == 0)
977
						&& (m->desc[0] != '\0')
978
						) {
979
						strcat(result, " ");
980
						need_separator = 0;
981
					}
982

  
983
					strcat(result, m->desc);
984

  
985
					if (m->desc[0])
986
						need_separator = 1;
987

  
988
					/*
989
					 * If we see any continuations at a higher level, process
990
					 * them.
991
					 */
992
					cont_level++;
993
				}
994
			}
995

  
996
			/* move to next continuation record */
997
			m = m->next;
998
		}
999
		/* found match */
1000
		r = strlen(result);
1001
		return r;
1002
	}
1003
	return 0;  /* no match at all */
1004
}
1005

  
1006
static handler_t
1007
mod_mimemagic_set_defaults(server *srv, void *p_d)
1008
{
1009
	plugin_data *p = p_d;
1010
	size_t i = 0;
1011

  
1012
	config_values_t cv[] = {
1013
		{ CONFIG_MIMEMAGIC_FILE, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
1014
		{ CONFIG_MIMEMAGIC_OVERRIDE_GLOBAL_MIMETYPE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
1015
		{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
1016
	};
1017

  
1018

  
1019
	if (!p) return HANDLER_ERROR;
1020

  
1021
	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
1022

  
1023
	for (i = 0; i < srv->config_context->used; i++) {
1024
		plugin_config *s;
1025
		array *ca;
1026

  
1027
		s = calloc(1, sizeof(plugin_config));
1028
		s->magic_file = buffer_init();
1029
		s->magics = NULL;
1030
		s->override_global_mimetype = 0;
1031

  
1032
		cv[0].destination = s->magic_file;
1033
		cv[1].destination = &(s->override_global_mimetype);
1034

  
1035
		p->config_storage[i] = s;
1036
		ca = ((data_config *)srv->config_context->data[i])->value;
1037

  
1038
		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
1039
			return HANDLER_ERROR;
1040
		}
1041

  
1042
		if (s->magic_file->used) {
1043
			/* magic file is set */
1044
			s->magics = magic_apprentice(s->magic_file->ptr);
1045
			if (s->magics == NULL) {
1046
				log_error_write(srv, __FILE__, __LINE__, "sbs",
1047
				       	"parse magic file", s->magic_file, "failed");
1048
			}
1049
		}
1050

  
1051
	}
1052

  
1053
	return HANDLER_GO_ON;
1054
}
1055

  
1056
#define PATCH_OPTION(x) p->conf.x = s->x;
1057

  
1058
static int
1059
mod_mimemagic_patch_connection(server *srv, connection *con, plugin_data *p)
1060
{
1061
	size_t i, j;
1062
	plugin_config *s = p->config_storage[0];
1063

  
1064
	PATCH_OPTION(magics);
1065
	PATCH_OPTION(override_global_mimetype);
1066

  
1067
	/* skip the first, the global context */
1068
	for (i = 1; i < srv->config_context->used; i++) {
1069
		data_config *dc = (data_config *)srv->config_context->data[i];
1070
		s = p->config_storage[i];
1071

  
1072
		/* condition didn't match */
1073
		if (!config_check_cond(srv, con, dc)) continue;
1074

  
1075
		/* merge config */
1076
		for (j = 0; j < dc->value->used; j++) {
1077
			data_unset *du = dc->value->data[j];
1078

  
1079
			if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_MIMEMAGIC_FILE))) {
1080
				PATCH_OPTION(magics);
1081
			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_MIMEMAGIC_OVERRIDE_GLOBAL_MIMETYPE))) {
1082
				PATCH_OPTION(override_global_mimetype);
1083
			}
1084
		}
1085
	}
1086

  
1087
	return 0;
1088
}
1089

  
1090
handler_t
1091
mod_mimemagic_subrequest(server *srv, connection *con, void *p_d)
1092
{
1093
	plugin_data *p = p_d;
1094
	stat_cache_entry *sce = NULL;
1095
	FILE *fp = NULL;
1096
	char result[1024];
1097
	unsigned char buf[HOWMANY + 1];  /* one extra for terminating '\0' */
1098
	uint32_t nbytes = 0; /* number of bytes read from a datafile */
1099
	int r;
1100

  
1101
	/* someone else has done a decision for us */
1102
	if (con->http_status != 0) return HANDLER_GO_ON;
1103
	if (con->uri.path->used == 0) return HANDLER_GO_ON;
1104
	if (con->physical.path->used == 0) return HANDLER_GO_ON;
1105

  
1106
	/* someone else has handled this request */
1107
	if (con->mode != DIRECT) return HANDLER_GO_ON;
1108
	if (con->file_finished) return HANDLER_GO_ON;
1109

  
1110
	/* we only handle GET, POST and HEAD */
1111
	switch(con->request.http_method) {
1112
	case HTTP_METHOD_GET:
1113
	case HTTP_METHOD_POST:
1114
	case HTTP_METHOD_HEAD:
1115
		break;
1116
	default:
1117
		return HANDLER_GO_ON;
1118
	}
1119

  
1120
	mod_mimemagic_patch_connection(srv, con, p);
1121

  
1122
	if (p->conf.magics == NULL) return HANDLER_GO_ON;
1123

  
1124
	if (con->conf.log_request_handling)
1125
		log_error_write(srv, __FILE__, __LINE__,  "s",  "-- handling in mod_mimemagic_subrequest");
1126

  
1127
	if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
1128
		/* file doesn't exist */
1129
		return HANDLER_GO_ON;
1130
	}
1131

  
1132
	/* we only handline regular files */
1133
#ifdef HAVE_LSTAT
1134
	if ((sce->is_symlink == 1) && !con->conf.follow_symlink) {
1135
		return HANDLER_GO_ON;
1136
	}
1137
#endif
1138
	if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
1139

  
1140
	/* set response content-type, if not set already */
1141
	if ((p->conf.override_global_mimetype || buffer_is_empty(sce->content_type)) &&
1142
		(NULL == array_get_element(con->response.headers, "Content-Type"))) {
1143
		fp = fopen(con->physical.path->ptr, "rb");
1144
		if (fp == NULL) return HANDLER_GO_ON;
1145

  
1146
		/*
1147
		 * try looking at the first HOWMANY bytes
1148
		 */
1149
		nbytes = sizeof(buf) - 1;
1150
		r = fread(buf, 1, nbytes, fp);
1151
		fclose(fp);
1152
		if (r > 0) {
1153
			buf[r++] = '\0';
1154
			result[0] = '\0';
1155
			r = magic_match(buf, r, p->conf.magics, result);
1156
			if (r > 0) response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), result, r);
1157
		}
1158
	}
1159

  
1160
	return HANDLER_GO_ON;
1161
}
1162

  
1163
int
1164
mod_mimemagic_plugin_init(plugin *p)
1165
{
1166
	p->version = LIGHTTPD_VERSION_ID;
1167
	p->name = buffer_init_string("mimemagic");
1168

  
1169
	p->init = mod_mimemagic_init;
1170
	p->handle_subrequest_start = mod_mimemagic_subrequest;
1171
	p->set_defaults = mod_mimemagic_set_defaults;
1172
	p->cleanup = mod_mimemagic_free;
1173

  
1174
	p->data = NULL;
1175

  
1176
	return 0;
1177
}