]> granicus.if.org Git - apache/blob - modules/metadata/mod_mime_magic.c
*) continued header revamping
[apache] / modules / metadata / mod_mime_magic.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 The Apache Software Foundation.  All rights
5  * reserved.
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  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /*
60  * mod_mime_magic: MIME type lookup via file magic numbers
61  * Copyright (c) 1996-1997 Cisco Systems, Inc.
62  *
63  * This software was submitted by Cisco Systems to the Apache Software Foundation in July
64  * 1997.  Future revisions and derivatives of this source code must
65  * acknowledge Cisco Systems as the original contributor of this module.
66  * All other licensing and usage conditions are those of the Apache Software Foundation.
67  *
68  * Some of this code is derived from the free version of the file command
69  * originally posted to comp.sources.unix.  Copyright info for that program
70  * is included below as required.
71  * ---------------------------------------------------------------------------
72  * - Copyright (c) Ian F. Darwin, 1987. Written by Ian F. Darwin.
73  *
74  * This software is not subject to any license of the American Telephone and
75  * Telegraph Company or of the Regents of the University of California.
76  *
77  * Permission is granted to anyone to use this software for any purpose on any
78  * computer system, and to alter it and redistribute it freely, subject to
79  * the following restrictions:
80  *
81  * 1. The author is not responsible for the consequences of use of this
82  * software, no matter how awful, even if they arise from flaws in it.
83  *
84  * 2. The origin of this software must not be misrepresented, either by
85  * explicit claim or by omission.  Since few users ever read sources, credits
86  * must appear in the documentation.
87  *
88  * 3. Altered versions must be plainly marked as such, and must not be
89  * misrepresented as being the original software.  Since few users ever read
90  * sources, credits must appear in the documentation.
91  *
92  * 4. This notice may not be removed or altered.
93  * -------------------------------------------------------------------------
94  *
95  * For compliance with Mr Darwin's terms: this has been very significantly
96  * modified from the free "file" command.
97  * - all-in-one file for compilation convenience when moving from one
98  *   version of Apache to the next.
99  * - Memory allocation is done through the Apache API's apr_pool_t structure.
100  * - All functions have had necessary Apache API request or server
101  *   structures passed to them where necessary to call other Apache API
102  *   routines.  (i.e. usually for logging, files, or memory allocation in
103  *   itself or a called function.)
104  * - struct magic has been converted from an array to a single-ended linked
105  *   list because it only grows one record at a time, it's only accessed
106  *   sequentially, and the Apache API has no equivalent of realloc().
107  * - Functions have been changed to get their parameters from the server
108  *   configuration instead of globals.  (It should be reentrant now but has
109  *   not been tested in a threaded environment.)
110  * - Places where it used to print results to stdout now saves them in a
111  *   list where they're used to set the MIME type in the Apache request
112  *   record.
113  * - Command-line flags have been removed since they will never be used here.
114  *
115  * Ian Kluft <ikluft@cisco.com>
116  * Engineering Information Framework
117  * Central Engineering
118  * Cisco Systems, Inc.
119  * San Jose, CA, USA
120  *
121  * Initial installation          July/August 1996
122  * Misc bug fixes                May 1997
123  * Submission to Apache Software Foundation    July 1997
124  *
125  */
126
127 #include "apr.h"
128 #include "apr_strings.h"
129
130 #if APR_HAVE_UNISTD_H
131 #include <unistd.h>
132 #endif
133
134 #include "ap_config.h"
135 #include "httpd.h"
136 #include "http_config.h"
137 #include "http_request.h"
138 #include "http_core.h"
139 #include "http_log.h"
140 #include "http_protocol.h"
141 #include "util_script.h"
142
143 /* ### this isn't set by configure? does anybody set this? */
144 #ifdef HAVE_UTIME_H
145 #include <utime.h>
146 #endif
147
148 /*
149  * data structures and related constants
150  */
151
152 #define MODNAME        "mod_mime_magic"
153 #define MIME_MAGIC_DEBUG        0
154
155 #define MIME_BINARY_UNKNOWN    "application/octet-stream"
156 #define MIME_TEXT_UNKNOWN    "text/plain"
157
158 #define MAXMIMESTRING        256
159
160 /* HOWMANY must be at least 4096 to make gzip -dcq work */
161 #define HOWMANY 4096
162 /* SMALL_HOWMANY limits how much work we do to figure out text files */
163 #define SMALL_HOWMANY 1024
164 #define MAXDESC    50           /* max leng of text description */
165 #define MAXstring 64            /* max leng of "string" types */
166
167 struct magic {
168     struct magic *next;         /* link to next entry */
169     int lineno;                 /* line number from magic file */
170
171     short flag;
172 #define INDIR    1              /* if '>(...)' appears,  */
173 #define    UNSIGNED 2           /* comparison is unsigned */
174     short cont_level;           /* level of ">" */
175     struct {
176         char type;              /* byte short long */
177         long offset;            /* offset from indirection */
178     } in;
179     long offset;                /* offset to magic number */
180     unsigned char reln;         /* relation (0=eq, '>'=gt, etc) */
181     char type;                  /* int, short, long or string. */
182     char vallen;                /* length of string value, if any */
183 #define BYTE    1
184 #define SHORT    2
185 #define LONG    4
186 #define STRING    5
187 #define DATE    6
188 #define BESHORT    7
189 #define BELONG    8
190 #define BEDATE    9
191 #define LESHORT    10
192 #define LELONG    11
193 #define LEDATE    12
194     union VALUETYPE {
195         unsigned char b;
196         unsigned short h;
197         unsigned long l;
198         char s[MAXstring];
199         unsigned char hs[2];    /* 2 bytes of a fixed-endian "short" */
200         unsigned char hl[4];    /* 2 bytes of a fixed-endian "long" */
201     } value;                    /* either number or string */
202     unsigned long mask;         /* mask before comparison with value */
203     char nospflag;              /* supress space character */
204
205     /* NOTE: this string is suspected of overrunning - find it! */
206     char desc[MAXDESC];         /* description */
207 };
208
209 /*
210  * data structures for tar file recognition
211  * --------------------------------------------------------------------------
212  * Header file for public domain tar (tape archive) program.
213  *
214  * @(#)tar.h 1.20 86/10/29    Public Domain. Created 25 August 1985 by John
215  * Gilmore, ihnp4!hoptoad!gnu.
216  *
217  * Header block on tape.
218  *
219  * I'm going to use traditional DP naming conventions here. A "block" is a big
220  * chunk of stuff that we do I/O on. A "record" is a piece of info that we
221  * care about. Typically many "record"s fit into a "block".
222  */
223 #define RECORDSIZE    512
224 #define NAMSIZ    100
225 #define TUNMLEN    32
226 #define TGNMLEN    32
227
228 union record {
229     char charptr[RECORDSIZE];
230     struct header {
231         char name[NAMSIZ];
232         char mode[8];
233         char uid[8];
234         char gid[8];
235         char size[12];
236         char mtime[12];
237         char chksum[8];
238         char linkflag;
239         char linkname[NAMSIZ];
240         char magic[8];
241         char uname[TUNMLEN];
242         char gname[TGNMLEN];
243         char devmajor[8];
244         char devminor[8];
245     } header;
246 };
247
248 /* The magic field is filled with this if uname and gname are valid. */
249 #define    TMAGIC        "ustar  "      /* 7 chars and a null */
250
251 /*
252  * file-function prototypes
253  */
254 static int ascmagic(request_rec *, unsigned char *, apr_size_t);
255 static int is_tar(unsigned char *, apr_size_t);
256 static int softmagic(request_rec *, unsigned char *, apr_size_t);
257 static void tryit(request_rec *, unsigned char *, apr_size_t, int);
258 static int zmagic(request_rec *, unsigned char *, apr_size_t);
259
260 static int getvalue(server_rec *, struct magic *, char **);
261 static int hextoint(int);
262 static char *getstr(server_rec *, char *, char *, int, int *);
263 static int parse(server_rec *, apr_pool_t *p, char *, int);
264
265 static int match(request_rec *, unsigned char *, apr_size_t);
266 static int mget(request_rec *, union VALUETYPE *, unsigned char *,
267                 struct magic *, apr_size_t);
268 static int mcheck(request_rec *, union VALUETYPE *, struct magic *);
269 static void mprint(request_rec *, union VALUETYPE *, struct magic *);
270
271 static int uncompress(request_rec *, int, 
272                       unsigned char **, apr_size_t);
273 static long from_oct(int, char *);
274 static int fsmagic(request_rec *r, const char *fn);
275
276 /*
277  * includes for ASCII substring recognition formerly "names.h" in file
278  * command
279  *
280  * Original notes: names and types used by ascmagic in file(1). These tokens are
281  * here because they can appear anywhere in the first HOWMANY bytes, while
282  * tokens in /etc/magic must appear at fixed offsets into the file. Don't
283  * make HOWMANY too high unless you have a very fast CPU.
284  */
285
286 /* these types are used to index the apr_table_t 'types': keep em in sync! */
287 /* HTML inserted in first because this is a web server module now */
288 #define L_HTML    0             /* HTML */
289 #define L_C       1             /* first and foremost on UNIX */
290 #define L_FORT    2             /* the oldest one */
291 #define L_MAKE    3             /* Makefiles */
292 #define L_PLI     4             /* PL/1 */
293 #define L_MACH    5             /* some kinda assembler */
294 #define L_ENG     6             /* English */
295 #define L_PAS     7             /* Pascal */
296 #define L_MAIL    8             /* Electronic mail */
297 #define L_NEWS    9             /* Usenet Netnews */
298
299 static char *types[] =
300 {
301     "text/html",                /* HTML */
302     "text/plain",               /* "c program text", */
303     "text/plain",               /* "fortran program text", */
304     "text/plain",               /* "make commands text", */
305     "text/plain",               /* "pl/1 program text", */
306     "text/plain",               /* "assembler program text", */
307     "text/plain",               /* "English text", */
308     "text/plain",               /* "pascal program text", */
309     "message/rfc822",           /* "mail text", */
310     "message/news",             /* "news text", */
311     "application/binary",       /* "can't happen error on names.h/types", */
312     0
313 };
314
315 static struct names {
316     char *name;
317     short type;
318 } names[] = {
319
320     /* These must be sorted by eye for optimal hit rate */
321     /* Add to this list only after substantial meditation */
322     {
323         "<html>", L_HTML
324     },
325     {
326         "<HTML>", L_HTML
327     },
328     {
329         "<head>", L_HTML
330     },
331     {
332         "<HEAD>", L_HTML
333     },
334     {
335         "<title>", L_HTML
336     },
337     {
338         "<TITLE>", L_HTML
339     },
340     {
341         "<h1>", L_HTML
342     },
343     {
344         "<H1>", L_HTML
345     },
346     {
347         "<!--", L_HTML
348     },
349     {
350         "<!DOCTYPE HTML", L_HTML
351     },
352     {
353         "/*", L_C
354     },                          /* must precede "The", "the", etc. */
355     {
356         "#include", L_C
357     },
358     {
359         "char", L_C
360     },
361     {
362         "The", L_ENG
363     },
364     {
365         "the", L_ENG
366     },
367     {
368         "double", L_C
369     },
370     {
371         "extern", L_C
372     },
373     {
374         "float", L_C
375     },
376     {
377         "real", L_C
378     },
379     {
380         "struct", L_C
381     },
382     {
383         "union", L_C
384     },
385     {
386         "CFLAGS", L_MAKE
387     },
388     {
389         "LDFLAGS", L_MAKE
390     },
391     {
392         "all:", L_MAKE
393     },
394     {
395         ".PRECIOUS", L_MAKE
396     },
397     /*
398      * Too many files of text have these words in them.  Find another way to
399      * recognize Fortrash.
400      */
401 #ifdef    NOTDEF
402     {
403         "subroutine", L_FORT
404     },
405     {
406         "function", L_FORT
407     },
408     {
409         "block", L_FORT
410     },
411     {
412         "common", L_FORT
413     },
414     {
415         "dimension", L_FORT
416     },
417     {
418         "integer", L_FORT
419     },
420     {
421         "data", L_FORT
422     },
423 #endif /* NOTDEF */
424     {
425         ".ascii", L_MACH
426     },
427     {
428         ".asciiz", L_MACH
429     },
430     {
431         ".byte", L_MACH
432     },
433     {
434         ".even", L_MACH
435     },
436     {
437         ".globl", L_MACH
438     },
439     {
440         "clr", L_MACH
441     },
442     {
443         "(input,", L_PAS
444     },
445     {
446         "dcl", L_PLI
447     },
448     {
449         "Received:", L_MAIL
450     },
451     {
452         ">From", L_MAIL
453     },
454     {
455         "Return-Path:", L_MAIL
456     },
457     {
458         "Cc:", L_MAIL
459     },
460     {
461         "Newsgroups:", L_NEWS
462     },
463     {
464         "Path:", L_NEWS
465     },
466     {
467         "Organization:", L_NEWS
468     },
469     {
470         NULL, 0
471     }
472 };
473
474 #define NNAMES ((sizeof(names)/sizeof(struct names)) - 1)
475
476 /*
477  * Result String List (RSL)
478  *
479  * The file(1) command prints its output.  Instead, we store the various
480  * "printed" strings in a list (allocating memory as we go) and concatenate
481  * them at the end when we finally know how much space they'll need.
482  */
483
484 typedef struct magic_rsl_s {
485     char *str;                  /* string, possibly a fragment */
486     struct magic_rsl_s *next;   /* pointer to next fragment */
487 } magic_rsl;
488
489 /*
490  * Apache module configuration structures
491  */
492
493 /* per-server info */
494 typedef struct {
495     const char *magicfile;              /* where magic be found */
496     struct magic *magic;        /* head of magic config list */
497     struct magic *last;
498 } magic_server_config_rec;
499
500 /* per-request info */
501 typedef struct {
502     magic_rsl *head;            /* result string list */
503     magic_rsl *tail;
504     unsigned suf_recursion;     /* recursion depth in suffix check */
505 } magic_req_rec;
506
507 /*
508  * configuration functions - called by Apache API routines
509  */
510
511 module mime_magic_module;
512
513 static void *create_magic_server_config(apr_pool_t *p, server_rec *d)
514 {
515     /* allocate the config - use pcalloc because it needs to be zeroed */
516     return apr_pcalloc(p, sizeof(magic_server_config_rec));
517 }
518
519 static void *merge_magic_server_config(apr_pool_t *p, void *basev, void *addv)
520 {
521     magic_server_config_rec *base = (magic_server_config_rec *) basev;
522     magic_server_config_rec *add = (magic_server_config_rec *) addv;
523     magic_server_config_rec *new = (magic_server_config_rec *)
524                             apr_palloc(p, sizeof(magic_server_config_rec));
525
526     new->magicfile = add->magicfile ? add->magicfile : base->magicfile;
527     new->magic = NULL;
528     new->last = NULL;
529     return new;
530 }
531
532 static const char *set_magicfile(cmd_parms *cmd, void *dummy, const char *arg)
533 {
534     magic_server_config_rec *conf = (magic_server_config_rec *)
535     ap_get_module_config(cmd->server->module_config,
536                       &mime_magic_module);
537
538     if (!conf) {
539         return MODNAME ": server structure not allocated";
540     }
541     conf->magicfile = arg;
542     return NULL;
543 }
544
545 /*
546  * configuration file commands - exported to Apache API
547  */
548
549 static const command_rec mime_magic_cmds[] =
550 {
551     AP_INIT_TAKE1("MimeMagicFile", set_magicfile, NULL, RSRC_CONF,
552      "Path to MIME Magic file (in file(1) format)"),
553     {NULL}
554 };
555
556 /*
557  * RSL (result string list) processing routines
558  *
559  * These collect strings that would have been printed in fragments by file(1)
560  * into a list of magic_rsl structures with the strings. When complete,
561  * they're concatenated together to become the MIME content and encoding
562  * types.
563  *
564  * return value conventions for these functions: functions which return int:
565  * failure = -1, other = result functions which return pointers: failure = 0,
566  * other = result
567  */
568
569 /* allocate a per-request structure and put it in the request record */
570 static magic_req_rec *magic_set_config(request_rec *r)
571 {
572     magic_req_rec *req_dat = (magic_req_rec *) apr_palloc(r->pool,
573                                                       sizeof(magic_req_rec));
574
575     req_dat->head = req_dat->tail = (magic_rsl *) NULL;
576     ap_set_module_config(r->request_config, &mime_magic_module, req_dat);
577     return req_dat;
578 }
579
580 /* add a string to the result string list for this request */
581 /* it is the responsibility of the caller to allocate "str" */
582 static int magic_rsl_add(request_rec *r, char *str)
583 {
584     magic_req_rec *req_dat = (magic_req_rec *)
585                     ap_get_module_config(r->request_config, &mime_magic_module);
586     magic_rsl *rsl;
587
588     /* make sure we have a list to put it in */
589     if (!req_dat) {
590         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, APR_EINVAL, r,
591                     MODNAME ": request config should not be NULL");
592         if (!(req_dat = magic_set_config(r))) {
593             /* failure */
594             return -1;
595         }
596     }
597
598     /* allocate the list entry */
599     rsl = (magic_rsl *) apr_palloc(r->pool, sizeof(magic_rsl));
600
601     /* fill it */
602     rsl->str = str;
603     rsl->next = (magic_rsl *) NULL;
604
605     /* append to the list */
606     if (req_dat->head && req_dat->tail) {
607         req_dat->tail->next = rsl;
608         req_dat->tail = rsl;
609     }
610     else {
611         req_dat->head = req_dat->tail = rsl;
612     }
613
614     /* success */
615     return 0;
616 }
617
618 /* RSL hook for puts-type functions */
619 static int magic_rsl_puts(request_rec *r, char *str)
620 {
621     return magic_rsl_add(r, str);
622 }
623
624 /* RSL hook for printf-type functions */
625 static int magic_rsl_printf(request_rec *r, char *str,...)
626 {
627     va_list ap;
628
629     char buf[MAXMIMESTRING];
630
631     /* assemble the string into the buffer */
632     va_start(ap, str);
633     apr_vsnprintf(buf, sizeof(buf), str, ap);
634     va_end(ap);
635
636     /* add the buffer to the list */
637     return magic_rsl_add(r, strdup(buf));
638 }
639
640 /* RSL hook for putchar-type functions */
641 static int magic_rsl_putchar(request_rec *r, char c)
642 {
643     char str[2];
644
645     /* high overhead for 1 char - just hope they don't do this much */
646     str[0] = c;
647     str[1] = '\0';
648     return magic_rsl_add(r, str);
649 }
650
651 /* allocate and copy a contiguous string from a result string list */
652 static char *rsl_strdup(request_rec *r, int start_frag, int start_pos, int len)
653 {
654     char *result;               /* return value */
655     int cur_frag,               /* current fragment number/counter */
656         cur_pos,                /* current position within fragment */
657         res_pos;                /* position in result string */
658     magic_rsl *frag;            /* list-traversal pointer */
659     magic_req_rec *req_dat = (magic_req_rec *)
660                     ap_get_module_config(r->request_config, &mime_magic_module);
661
662     /* allocate the result string */
663     result = (char *) apr_palloc(r->pool, len + 1);
664
665     /* loop through and collect the string */
666     res_pos = 0;
667     for (frag = req_dat->head, cur_frag = 0;
668          frag->next;
669          frag = frag->next, cur_frag++) {
670         /* loop to the first fragment */
671         if (cur_frag < start_frag)
672             continue;
673
674         /* loop through and collect chars */
675         for (cur_pos = (cur_frag == start_frag) ? start_pos : 0;
676              frag->str[cur_pos];
677              cur_pos++) {
678             if (cur_frag >= start_frag
679                 && cur_pos >= start_pos
680                 && res_pos <= len) {
681                 result[res_pos++] = frag->str[cur_pos];
682                 if (res_pos > len) {
683                     break;
684                 }
685             }
686         }
687     }
688
689     /* clean up and return */
690     result[res_pos] = 0;
691 #if MIME_MAGIC_DEBUG
692     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
693              MODNAME ": rsl_strdup() %d chars: %s", res_pos - 1, result);
694 #endif
695     return result;
696 }
697
698 /* states for the state-machine algorithm in magic_rsl_to_request() */
699 typedef enum {
700     rsl_leading_space, rsl_type, rsl_subtype, rsl_separator, rsl_encoding
701 } rsl_states;
702
703 /* process the RSL and set the MIME info in the request record */
704 static int magic_rsl_to_request(request_rec *r)
705 {
706     int cur_frag,               /* current fragment number/counter */
707         cur_pos,                /* current position within fragment */
708         type_frag,              /* content type starting point: fragment */
709         type_pos,               /* content type starting point: position */
710         type_len,               /* content type length */
711         encoding_frag,          /* content encoding starting point: fragment */
712         encoding_pos,           /* content encoding starting point: position */
713         encoding_len;           /* content encoding length */
714
715     magic_rsl *frag;            /* list-traversal pointer */
716     rsl_states state;
717
718     magic_req_rec *req_dat = (magic_req_rec *)
719                     ap_get_module_config(r->request_config, &mime_magic_module);
720
721     /* check if we have a result */
722     if (!req_dat || !req_dat->head) {
723         /* empty - no match, we defer to other Apache modules */
724         return DECLINED;
725     }
726
727     /* start searching for the type and encoding */
728     state = rsl_leading_space;
729     type_frag = type_pos = type_len = 0;
730     encoding_frag = encoding_pos = encoding_len = 0;
731     for (frag = req_dat->head, cur_frag = 0;
732          frag && frag->next;
733          frag = frag->next, cur_frag++) {
734         /* loop through the characters in the fragment */
735         for (cur_pos = 0; frag->str[cur_pos]; cur_pos++) {
736             if (apr_isspace(frag->str[cur_pos])) {
737                 /* process whitespace actions for each state */
738                 if (state == rsl_leading_space) {
739                     /* eat whitespace in this state */
740                     continue;
741                 }
742                 else if (state == rsl_type) {
743                     /* whitespace: type has no slash! */
744                     return DECLINED;
745                 }
746                 else if (state == rsl_subtype) {
747                     /* whitespace: end of MIME type */
748                     state++;
749                     continue;
750                 }
751                 else if (state == rsl_separator) {
752                     /* eat whitespace in this state */
753                     continue;
754                 }
755                 else if (state == rsl_encoding) {
756                     /* whitespace: end of MIME encoding */
757                     /* we're done */
758                     frag = req_dat->tail;
759                     break;
760                 }
761                 else {
762                     /* should not be possible */
763                     /* abandon malfunctioning module */
764                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r,
765                                 MODNAME ": bad state %d (ws)", state);
766                     return DECLINED;
767                 }
768                 /* NOTREACHED */
769             }
770             else if (state == rsl_type &&
771                      frag->str[cur_pos] == '/') {
772                 /* copy the char and go to rsl_subtype state */
773                 type_len++;
774                 state++;
775             }
776             else {
777                 /* process non-space actions for each state */
778                 if (state == rsl_leading_space) {
779                     /* non-space: begin MIME type */
780                     state++;
781                     type_frag = cur_frag;
782                     type_pos = cur_pos;
783                     type_len = 1;
784                     continue;
785                 }
786                 else if (state == rsl_type ||
787                          state == rsl_subtype) {
788                     /* non-space: adds to type */
789                     type_len++;
790                     continue;
791                 }
792                 else if (state == rsl_separator) {
793                     /* non-space: begin MIME encoding */
794                     state++;
795                     encoding_frag = cur_frag;
796                     encoding_pos = cur_pos;
797                     encoding_len = 1;
798                     continue;
799                 }
800                 else if (state == rsl_encoding) {
801                     /* non-space: adds to encoding */
802                     encoding_len++;
803                     continue;
804                 }
805                 else {
806                     /* should not be possible */
807                     /* abandon malfunctioning module */
808                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r,
809                                 MODNAME ": bad state %d (ns)", state);
810                     return DECLINED;
811                 }
812                 /* NOTREACHED */
813             }
814             /* NOTREACHED */
815         }
816     }
817
818     /* if we ended prior to state rsl_subtype, we had incomplete info */
819     if (state != rsl_subtype && state != rsl_separator &&
820         state != rsl_encoding) {
821         /* defer to other modules */
822         return DECLINED;
823     }
824
825     /* save the info in the request record */
826     if (state == rsl_subtype || state == rsl_encoding ||
827         state == rsl_encoding) {
828         char *tmp;
829         tmp = rsl_strdup(r, type_frag, type_pos, type_len);
830         /* XXX: this could be done at config time I'm sure... but I'm
831          * confused by all this magic_rsl stuff. -djg */
832         ap_content_type_tolower(tmp);
833         r->content_type = tmp;
834     }
835     if (state == rsl_encoding) {
836         char *tmp;
837         tmp = rsl_strdup(r, encoding_frag,
838                                          encoding_pos, encoding_len);
839         /* XXX: this could be done at config time I'm sure... but I'm
840          * confused by all this magic_rsl stuff. -djg */
841         ap_str_tolower(tmp);
842         r->content_encoding = tmp;
843     }
844
845     /* detect memory allocation errors */
846     if (!r->content_type ||
847         (state == rsl_encoding && !r->content_encoding)) {
848         return HTTP_INTERNAL_SERVER_ERROR;
849     }
850
851     /* success! */
852     return OK;
853 }
854
855 /*
856  * magic_process - process input file r        Apache API request record
857  * (formerly called "process" in file command, prefix added for clarity) Opens
858  * the file and reads a fixed-size buffer to begin processing the contents.
859  */
860 static int magic_process(request_rec *r)
861 {
862     apr_file_t *fd = NULL;
863     unsigned char buf[HOWMANY + 1];     /* one extra for terminating '\0' */
864     apr_size_t nbytes = 0;              /* number of bytes read from a datafile */
865     int result;
866
867     /*
868      * first try judging the file based on its filesystem status
869      */
870     switch ((result = fsmagic(r, r->filename))) {
871     case DONE:
872         magic_rsl_putchar(r, '\n');
873         return OK;
874     case OK:
875         break;
876     default:
877         /* fatal error, bail out */
878         return result;
879     }
880
881     if (apr_file_open(&fd, r->filename, APR_READ, APR_OS_DEFAULT, r->pool) != APR_SUCCESS) {
882         /* We can't open it, but we were able to stat it. */
883         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
884                     MODNAME ": can't read `%s'", r->filename);
885         /* let some other handler decide what the problem is */
886         return DECLINED;
887     }
888
889     /*
890      * try looking at the first HOWMANY bytes
891      */
892     nbytes = sizeof(buf) - 1;
893     if ((result = apr_file_read(fd, (char *) buf, &nbytes)) != APR_SUCCESS) {
894         ap_log_rerror(APLOG_MARK, APLOG_ERR, result, r,
895                     MODNAME ": read failed: %s", r->filename);
896         return HTTP_INTERNAL_SERVER_ERROR;
897     }
898
899     if (nbytes == 0)
900         magic_rsl_puts(r, MIME_TEXT_UNKNOWN);
901     else {
902         buf[nbytes++] = '\0';   /* null-terminate it */
903         tryit(r, buf, nbytes, 1); 
904     }
905
906     (void) apr_file_close(fd);
907     (void) magic_rsl_putchar(r, '\n');
908
909     return OK;
910 }
911
912
913 static void tryit(request_rec *r, unsigned char *buf, apr_size_t nb, int checkzmagic)
914 {
915     /*
916      * Try compression stuff
917      */
918         if (checkzmagic == 1) {  
919                         if (zmagic(r, buf, nb) == 1)
920                         return;
921         }
922
923     /*
924      * try tests in /etc/magic (or surrogate magic file)
925      */
926     if (softmagic(r, buf, nb) == 1)
927         return;
928
929     /*
930      * try known keywords, check for ascii-ness too.
931      */
932     if (ascmagic(r, buf, nb) == 1)
933         return;
934
935     /*
936      * abandon hope, all ye who remain here
937      */
938     magic_rsl_puts(r, MIME_BINARY_UNKNOWN);
939 }
940
941 #define    EATAB {while (apr_isspace((unsigned char) *l))  ++l;}
942
943 /*
944  * apprentice - load configuration from the magic file r
945  *  API request record
946  */
947 static int apprentice(server_rec *s, apr_pool_t *p)
948 {
949     apr_file_t *f = NULL;
950     apr_status_t result;
951     char line[BUFSIZ + 1];
952     int errs = 0;
953     int lineno;
954 #if MIME_MAGIC_DEBUG
955     int rule = 0;
956     struct magic *m, *prevm;
957 #endif
958     magic_server_config_rec *conf = (magic_server_config_rec *)
959                     ap_get_module_config(s->module_config, &mime_magic_module);
960
961     const char *fname = ap_server_root_relative(p, conf->magicfile);
962     result = apr_file_open(&f, fname, APR_READ | APR_BUFFERED, APR_OS_DEFAULT, p);
963     if (result != APR_SUCCESS) {
964         ap_log_error(APLOG_MARK, APLOG_ERR, result, s,
965                     MODNAME ": can't read magic file %s", fname);
966         return -1;
967     }
968
969     /* set up the magic list (empty) */
970     conf->magic = conf->last = NULL;
971
972     /* parse it */
973     for (lineno = 1; apr_file_gets(line, BUFSIZ, f) == APR_SUCCESS; lineno++) {
974         int ws_offset;
975
976         /* delete newline */
977         if (line[0]) {
978             line[strlen(line) - 1] = '\0';
979         }
980
981         /* skip leading whitespace */
982         ws_offset = 0;
983         while (line[ws_offset] && apr_isspace(line[ws_offset])) {
984             ws_offset++;
985         }
986
987         /* skip blank lines */
988         if (line[ws_offset] == 0) {
989             continue;
990         }
991
992         /* comment, do not parse */
993         if (line[ws_offset] == '#')
994             continue;
995
996 #if MIME_MAGIC_DEBUG
997         /* if we get here, we're going to use it so count it */
998         rule++;
999 #endif
1000
1001         /* parse it */
1002         if (parse(s, p, line + ws_offset, lineno) != 0)
1003             ++errs;
1004     }
1005
1006     (void) apr_file_close(f);
1007
1008 #if MIME_MAGIC_DEBUG
1009     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, s,
1010                 MODNAME ": apprentice conf=%x file=%s m=%s m->next=%s last=%s",
1011                 conf,
1012                 conf->magicfile ? conf->magicfile : "NULL",
1013                 conf->magic ? "set" : "NULL",
1014                 (conf->magic && conf->magic->next) ? "set" : "NULL",
1015                 conf->last ? "set" : "NULL");
1016     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, s,
1017                 MODNAME ": apprentice read %d lines, %d rules, %d errors",
1018                 lineno, rule, errs);
1019 #endif
1020
1021 #if MIME_MAGIC_DEBUG
1022     prevm = 0;
1023     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, s,
1024                 MODNAME ": apprentice test");
1025     for (m = conf->magic; m; m = m->next) {
1026         if (apr_isprint((((unsigned long) m) >> 24) & 255) &&
1027             apr_isprint((((unsigned long) m) >> 16) & 255) &&
1028             apr_isprint((((unsigned long) m) >> 8) & 255) &&
1029             apr_isprint(((unsigned long) m) & 255)) {
1030             ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, s,
1031                         MODNAME ": apprentice: POINTER CLOBBERED! "
1032                         "m=\"%c%c%c%c\" line=%d",
1033                         (((unsigned long) m) >> 24) & 255,
1034                         (((unsigned long) m) >> 16) & 255,
1035                         (((unsigned long) m) >> 8) & 255,
1036                         ((unsigned long) m) & 255,
1037                         prevm ? prevm->lineno : -1);
1038             break;
1039         }
1040         prevm = m;
1041     }
1042 #endif
1043
1044     return (errs ? -1 : 0);
1045 }
1046
1047 /*
1048  * extend the sign bit if the comparison is to be signed
1049  */
1050 static unsigned long signextend(server_rec *s, struct magic *m, unsigned long v)
1051 {
1052     if (!(m->flag & UNSIGNED))
1053         switch (m->type) {
1054             /*
1055              * Do not remove the casts below.  They are vital. When later
1056              * compared with the data, the sign extension must have happened.
1057              */
1058         case BYTE:
1059             v = (char) v;
1060             break;
1061         case SHORT:
1062         case BESHORT:
1063         case LESHORT:
1064             v = (short) v;
1065             break;
1066         case DATE:
1067         case BEDATE:
1068         case LEDATE:
1069         case LONG:
1070         case BELONG:
1071         case LELONG:
1072             v = (long) v;
1073             break;
1074         case STRING:
1075             break;
1076         default:
1077             ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, s,
1078                         MODNAME ": can't happen: m->type=%d", m->type);
1079             return -1;
1080         }
1081     return v;
1082 }
1083
1084 /*
1085  * parse one line from magic file, put into magic[index++] if valid
1086  */
1087 static int parse(server_rec *serv, apr_pool_t *p, char *l, int lineno)
1088 {
1089     struct magic *m;
1090     char *t, *s;
1091     magic_server_config_rec *conf = (magic_server_config_rec *)
1092                     ap_get_module_config(serv->module_config, &mime_magic_module);
1093
1094     /* allocate magic structure entry */
1095     m = (struct magic *) apr_pcalloc(p, sizeof(struct magic));
1096
1097     /* append to linked list */
1098     m->next = NULL;
1099     if (!conf->magic || !conf->last) {
1100         conf->magic = conf->last = m;
1101     }
1102     else {
1103         conf->last->next = m;
1104         conf->last = m;
1105     }
1106
1107     /* set values in magic structure */
1108     m->flag = 0;
1109     m->cont_level = 0;
1110     m->lineno = lineno;
1111
1112     while (*l == '>') {
1113         ++l;                    /* step over */
1114         m->cont_level++;
1115     }
1116
1117     if (m->cont_level != 0 && *l == '(') {
1118         ++l;                    /* step over */
1119         m->flag |= INDIR;
1120     }
1121
1122     /* get offset, then skip over it */
1123     m->offset = (int) strtol(l, &t, 0);
1124     if (l == t) {
1125         ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, serv,
1126                     MODNAME ": offset %s invalid", l);
1127     }
1128     l = t;
1129
1130     if (m->flag & INDIR) {
1131         m->in.type = LONG;
1132         m->in.offset = 0;
1133         /*
1134          * read [.lbs][+-]nnnnn)
1135          */
1136         if (*l == '.') {
1137             switch (*++l) {
1138             case 'l':
1139                 m->in.type = LONG;
1140                 break;
1141             case 's':
1142                 m->in.type = SHORT;
1143                 break;
1144             case 'b':
1145                 m->in.type = BYTE;
1146                 break;
1147             default:
1148                 ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, serv,
1149                         MODNAME ": indirect offset type %c invalid", *l);
1150                 break;
1151             }
1152             l++;
1153         }
1154         s = l;
1155         if (*l == '+' || *l == '-')
1156             l++;
1157         if (apr_isdigit((unsigned char) *l)) {
1158             m->in.offset = strtol(l, &t, 0);
1159             if (*s == '-')
1160                 m->in.offset = -m->in.offset;
1161         }
1162         else
1163             t = l;
1164         if (*t++ != ')') {
1165             ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, serv,
1166                         MODNAME ": missing ')' in indirect offset");
1167         }
1168         l = t;
1169     }
1170
1171
1172     while (apr_isdigit((unsigned char) *l))
1173         ++l;
1174     EATAB;
1175
1176 #define NBYTE           4
1177 #define NSHORT          5
1178 #define NLONG           4
1179 #define NSTRING         6
1180 #define NDATE           4
1181 #define NBESHORT        7
1182 #define NBELONG         6
1183 #define NBEDATE         6
1184 #define NLESHORT        7
1185 #define NLELONG         6
1186 #define NLEDATE         6
1187
1188     if (*l == 'u') {
1189         ++l;
1190         m->flag |= UNSIGNED;
1191     }
1192
1193     /* get type, skip it */
1194     if (strncmp(l, "byte", NBYTE) == 0) {
1195         m->type = BYTE;
1196         l += NBYTE;
1197     }
1198     else if (strncmp(l, "short", NSHORT) == 0) {
1199         m->type = SHORT;
1200         l += NSHORT;
1201     }
1202     else if (strncmp(l, "long", NLONG) == 0) {
1203         m->type = LONG;
1204         l += NLONG;
1205     }
1206     else if (strncmp(l, "string", NSTRING) == 0) {
1207         m->type = STRING;
1208         l += NSTRING;
1209     }
1210     else if (strncmp(l, "date", NDATE) == 0) {
1211         m->type = DATE;
1212         l += NDATE;
1213     }
1214     else if (strncmp(l, "beshort", NBESHORT) == 0) {
1215         m->type = BESHORT;
1216         l += NBESHORT;
1217     }
1218     else if (strncmp(l, "belong", NBELONG) == 0) {
1219         m->type = BELONG;
1220         l += NBELONG;
1221     }
1222     else if (strncmp(l, "bedate", NBEDATE) == 0) {
1223         m->type = BEDATE;
1224         l += NBEDATE;
1225     }
1226     else if (strncmp(l, "leshort", NLESHORT) == 0) {
1227         m->type = LESHORT;
1228         l += NLESHORT;
1229     }
1230     else if (strncmp(l, "lelong", NLELONG) == 0) {
1231         m->type = LELONG;
1232         l += NLELONG;
1233     }
1234     else if (strncmp(l, "ledate", NLEDATE) == 0) {
1235         m->type = LEDATE;
1236         l += NLEDATE;
1237     }
1238     else {
1239         ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, serv,
1240                     MODNAME ": type %s invalid", l);
1241         return -1;
1242     }
1243     /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
1244     if (*l == '&') {
1245         ++l;
1246         m->mask = signextend(serv, m, strtol(l, &l, 0));
1247     }
1248     else
1249         m->mask = ~0L;
1250     EATAB;
1251
1252     switch (*l) {
1253     case '>':
1254     case '<':
1255         /* Old-style anding: "0 byte &0x80 dynamically linked" */
1256     case '&':
1257     case '^':
1258     case '=':
1259         m->reln = *l;
1260         ++l;
1261         break;
1262     case '!':
1263         if (m->type != STRING) {
1264             m->reln = *l;
1265             ++l;
1266             break;
1267         }
1268         /* FALL THROUGH */
1269     default:
1270         if (*l == 'x' && apr_isspace((unsigned char) l[1])) {
1271             m->reln = *l;
1272             ++l;
1273             goto GetDesc;       /* Bill The Cat */
1274         }
1275         m->reln = '=';
1276         break;
1277     }
1278     EATAB;
1279
1280     if (getvalue(serv, m, &l))
1281         return -1;
1282     /*
1283      * now get last part - the description
1284      */
1285   GetDesc:
1286     EATAB;
1287     if (l[0] == '\b') {
1288         ++l;
1289         m->nospflag = 1;
1290     }
1291     else if ((l[0] == '\\') && (l[1] == 'b')) {
1292         ++l;
1293         ++l;
1294         m->nospflag = 1;
1295     }
1296     else
1297         m->nospflag = 0;
1298     strncpy(m->desc, l, sizeof(m->desc) - 1);
1299     m->desc[sizeof(m->desc) - 1] = '\0';
1300
1301 #if MIME_MAGIC_DEBUG
1302     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, serv,
1303                 MODNAME ": parse line=%d m=%x next=%x cont=%d desc=%s",
1304                 lineno, m, m->next, m->cont_level, m->desc);
1305 #endif /* MIME_MAGIC_DEBUG */
1306
1307     return 0;
1308 }
1309
1310 /*
1311  * Read a numeric value from a pointer, into the value union of a magic
1312  * pointer, according to the magic type.  Update the string pointer to point
1313  * just after the number read.  Return 0 for success, non-zero for failure.
1314  */
1315 static int getvalue(server_rec *s, struct magic *m, char **p)
1316 {
1317     int slen;
1318
1319     if (m->type == STRING) {
1320         *p = getstr(s, *p, m->value.s, sizeof(m->value.s), &slen);
1321         m->vallen = slen;
1322     }
1323     else if (m->reln != 'x')
1324         m->value.l = signextend(s, m, strtol(*p, p, 0));
1325     return 0;
1326 }
1327
1328 /*
1329  * Convert a string containing C character escapes.  Stop at an unescaped
1330  * space or tab. Copy the converted version to "p", returning its length in
1331  * *slen. Return updated scan pointer as function result.
1332  */
1333 static char *getstr(server_rec *serv, register char *s, register char *p,
1334                     int plen, int *slen)
1335 {
1336     char *origs = s, *origp = p;
1337     char *pmax = p + plen - 1;
1338     register int c;
1339     register int val;
1340
1341     while ((c = *s++) != '\0') {
1342         if (apr_isspace((unsigned char) c))
1343             break;
1344         if (p >= pmax) {
1345             ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, serv,
1346                         MODNAME ": string too long: %s", origs);
1347             break;
1348         }
1349         if (c == '\\') {
1350             switch (c = *s++) {
1351
1352             case '\0':
1353                 goto out;
1354
1355             default:
1356                 *p++ = (char) c;
1357                 break;
1358
1359             case 'n':
1360                 *p++ = '\n';
1361                 break;
1362
1363             case 'r':
1364                 *p++ = '\r';
1365                 break;
1366
1367             case 'b':
1368                 *p++ = '\b';
1369                 break;
1370
1371             case 't':
1372                 *p++ = '\t';
1373                 break;
1374
1375             case 'f':
1376                 *p++ = '\f';
1377                 break;
1378
1379             case 'v':
1380                 *p++ = '\v';
1381                 break;
1382
1383                 /* \ and up to 3 octal digits */
1384             case '0':
1385             case '1':
1386             case '2':
1387             case '3':
1388             case '4':
1389             case '5':
1390             case '6':
1391             case '7':
1392                 val = c - '0';
1393                 c = *s++;       /* try for 2 */
1394                 if (c >= '0' && c <= '7') {
1395                     val = (val << 3) | (c - '0');
1396                     c = *s++;   /* try for 3 */
1397                     if (c >= '0' && c <= '7')
1398                         val = (val << 3) | (c - '0');
1399                     else
1400                         --s;
1401                 }
1402                 else
1403                     --s;
1404                 *p++ = (char) val;
1405                 break;
1406
1407                 /* \x and up to 3 hex digits */
1408             case 'x':
1409                 val = 'x';      /* Default if no digits */
1410                 c = hextoint(*s++);     /* Get next char */
1411                 if (c >= 0) {
1412                     val = c;
1413                     c = hextoint(*s++);
1414                     if (c >= 0) {
1415                         val = (val << 4) + c;
1416                         c = hextoint(*s++);
1417                         if (c >= 0) {
1418                             val = (val << 4) + c;
1419                         }
1420                         else
1421                             --s;
1422                     }
1423                     else
1424                         --s;
1425                 }
1426                 else
1427                     --s;
1428                 *p++ = (char) val;
1429                 break;
1430             }
1431         }
1432         else
1433             *p++ = (char) c;
1434     }
1435   out:
1436     *p = '\0';
1437     *slen = p - origp;
1438     return s;
1439 }
1440
1441
1442 /* Single hex char to int; -1 if not a hex char. */
1443 static int hextoint(int c)
1444 {
1445     if (apr_isdigit((unsigned char) c))
1446         return c - '0';
1447     if ((c >= 'a') && (c <= 'f'))
1448         return c + 10 - 'a';
1449     if ((c >= 'A') && (c <= 'F'))
1450         return c + 10 - 'A';
1451     return -1;
1452 }
1453
1454
1455 /*
1456  * return DONE to indicate it's been handled
1457  * return OK to indicate it's a regular file still needing handling
1458  * other returns indicate a failure of some sort
1459  */
1460 static int fsmagic(request_rec *r, const char *fn)
1461 {
1462     switch (r->finfo.filetype) {
1463     case APR_DIR:
1464         magic_rsl_puts(r, DIR_MAGIC_TYPE);
1465         return DONE;
1466     case APR_CHR:
1467         /*
1468          * (void) magic_rsl_printf(r,"character special (%d/%d)",
1469          * major(sb->st_rdev), minor(sb->st_rdev));
1470          */
1471         (void) magic_rsl_puts(r, MIME_BINARY_UNKNOWN);
1472         return DONE;
1473     case APR_BLK:
1474         /*
1475          * (void) magic_rsl_printf(r,"block special (%d/%d)",
1476          * major(sb->st_rdev), minor(sb->st_rdev));
1477          */
1478         (void) magic_rsl_puts(r, MIME_BINARY_UNKNOWN);
1479         return DONE;
1480         /* TODO add code to handle V7 MUX and Blit MUX files */
1481     case APR_PIPE:
1482         /*
1483          * magic_rsl_puts(r,"fifo (named pipe)");
1484          */
1485         (void) magic_rsl_puts(r, MIME_BINARY_UNKNOWN);
1486         return DONE;
1487     case APR_LNK:
1488         /* We used stat(), the only possible reason for this is that the
1489          * symlink is broken.
1490          */
1491         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r,
1492                     MODNAME ": broken symlink (%s)", fn);
1493         return HTTP_INTERNAL_SERVER_ERROR;
1494     case APR_SOCK:
1495         magic_rsl_puts(r, MIME_BINARY_UNKNOWN);
1496         return DONE;
1497     case APR_REG:
1498         break;
1499     default:
1500         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r,
1501                       MODNAME ": invalid file type %d.", r->finfo.filetype);
1502         return HTTP_INTERNAL_SERVER_ERROR;
1503     }
1504
1505     /*
1506      * regular file, check next possibility
1507      */
1508     if (r->finfo.size == 0) {
1509         magic_rsl_puts(r, MIME_TEXT_UNKNOWN);
1510         return DONE;
1511     }
1512     return OK;
1513 }
1514
1515 /*
1516  * softmagic - lookup one file in database (already read from /etc/magic by
1517  * apprentice.c). Passed the name and FILE * of one file to be typed.
1518  */
1519                 /* ARGSUSED1 *//* nbytes passed for regularity, maybe need later */
1520 static int softmagic(request_rec *r, unsigned char *buf, apr_size_t nbytes)
1521 {
1522     if (match(r, buf, nbytes))
1523         return 1;
1524
1525     return 0;
1526 }
1527
1528 /*
1529  * Go through the whole list, stopping if you find a match.  Process all the
1530  * continuations of that match before returning.
1531  *
1532  * We support multi-level continuations:
1533  *
1534  * At any time when processing a successful top-level match, there is a current
1535  * continuation level; it represents the level of the last successfully
1536  * matched continuation.
1537  *
1538  * Continuations above that level are skipped as, if we see one, it means that
1539  * the continuation that controls them - i.e, the lower-level continuation
1540  * preceding them - failed to match.
1541  *
1542  * Continuations below that level are processed as, if we see one, it means
1543  * we've finished processing or skipping higher-level continuations under the
1544  * control of a successful or unsuccessful lower-level continuation, and are
1545  * now seeing the next lower-level continuation and should process it.  The
1546  * current continuation level reverts to the level of the one we're seeing.
1547  *
1548  * Continuations at the current level are processed as, if we see one, there's
1549  * no lower-level continuation that may have failed.
1550  *
1551  * If a continuation matches, we bump the current continuation level so that
1552  * higher-level continuations are processed.
1553  */
1554 static int match(request_rec *r, unsigned char *s, apr_size_t nbytes)
1555 {
1556 #if MIME_MAGIC_DEBUG
1557     int rule_counter = 0;
1558 #endif
1559     int cont_level = 0;
1560     int need_separator = 0;
1561     union VALUETYPE p;
1562     magic_server_config_rec *conf = (magic_server_config_rec *)
1563                 ap_get_module_config(r->server->module_config, &mime_magic_module);
1564     struct magic *m;
1565
1566 #if MIME_MAGIC_DEBUG
1567     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1568                 MODNAME ": match conf=%x file=%s m=%s m->next=%s last=%s",
1569                 conf,
1570                 conf->magicfile ? conf->magicfile : "NULL",
1571                 conf->magic ? "set" : "NULL",
1572                 (conf->magic && conf->magic->next) ? "set" : "NULL",
1573                 conf->last ? "set" : "NULL");
1574 #endif
1575
1576 #if MIME_MAGIC_DEBUG
1577     for (m = conf->magic; m; m = m->next) {
1578         if (apr_isprint((((unsigned long) m) >> 24) & 255) &&
1579             apr_isprint((((unsigned long) m) >> 16) & 255) &&
1580             apr_isprint((((unsigned long) m) >> 8) & 255) &&
1581             apr_isprint(((unsigned long) m) & 255)) {
1582             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1583                         MODNAME ": match: POINTER CLOBBERED! "
1584                         "m=\"%c%c%c%c\"",
1585                         (((unsigned long) m) >> 24) & 255,
1586                         (((unsigned long) m) >> 16) & 255,
1587                         (((unsigned long) m) >> 8) & 255,
1588                         ((unsigned long) m) & 255);
1589             break;
1590         }
1591     }
1592 #endif
1593
1594     for (m = conf->magic; m; m = m->next) {
1595 #if MIME_MAGIC_DEBUG
1596         rule_counter++;
1597         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1598                     MODNAME ": line=%d desc=%s", m->lineno, m->desc);
1599 #endif
1600
1601         /* check if main entry matches */
1602         if (!mget(r, &p, s, m, nbytes) ||
1603             !mcheck(r, &p, m)) {
1604             struct magic *m_cont;
1605
1606             /*
1607              * main entry didn't match, flush its continuations
1608              */
1609             if (!m->next || (m->next->cont_level == 0)) {
1610                 continue;
1611             }
1612
1613             m_cont = m->next;
1614             while (m_cont && (m_cont->cont_level != 0)) {
1615 #if MIME_MAGIC_DEBUG
1616                 rule_counter++;
1617                 ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1618                         MODNAME ": line=%d mc=%x mc->next=%x cont=%d desc=%s",
1619                             m_cont->lineno, m_cont,
1620                             m_cont->next, m_cont->cont_level,
1621                             m_cont->desc);
1622 #endif
1623                 /*
1624                  * this trick allows us to keep *m in sync when the continue
1625                  * advances the pointer
1626                  */
1627                 m = m_cont;
1628                 m_cont = m_cont->next;
1629             }
1630             continue;
1631         }
1632
1633         /* if we get here, the main entry rule was a match */
1634         /* this will be the last run through the loop */
1635 #if MIME_MAGIC_DEBUG
1636         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1637                     MODNAME ": rule matched, line=%d type=%d %s",
1638                     m->lineno, m->type,
1639                     (m->type == STRING) ? m->value.s : "");
1640 #endif
1641
1642         /* print the match */
1643         mprint(r, &p, m);
1644
1645         /*
1646          * If we printed something, we'll need to print a blank before we
1647          * print something else.
1648          */
1649         if (m->desc[0])
1650             need_separator = 1;
1651         /* and any continuations that match */
1652         cont_level++;
1653         /*
1654          * while (m && m->next && m->next->cont_level != 0 && ( m = m->next
1655          * ))
1656          */
1657         m = m->next;
1658         while (m && (m->cont_level != 0)) {
1659 #if MIME_MAGIC_DEBUG
1660             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1661                         MODNAME ": match line=%d cont=%d type=%d %s",
1662                         m->lineno, m->cont_level, m->type,
1663                         (m->type == STRING) ? m->value.s : "");
1664 #endif
1665             if (cont_level >= m->cont_level) {
1666                 if (cont_level > m->cont_level) {
1667                     /*
1668                      * We're at the end of the level "cont_level"
1669                      * continuations.
1670                      */
1671                     cont_level = m->cont_level;
1672                 }
1673                 if (mget(r, &p, s, m, nbytes) &&
1674                     mcheck(r, &p, m)) {
1675                     /*
1676                      * This continuation matched. Print its message, with a
1677                      * blank before it if the previous item printed and this
1678                      * item isn't empty.
1679                      */
1680                     /* space if previous printed */
1681                     if (need_separator
1682                         && (m->nospflag == 0)
1683                         && (m->desc[0] != '\0')
1684                         ) {
1685                         (void) magic_rsl_putchar(r, ' ');
1686                         need_separator = 0;
1687                     }
1688                     mprint(r, &p, m);
1689                     if (m->desc[0])
1690                         need_separator = 1;
1691
1692                     /*
1693                      * If we see any continuations at a higher level, process
1694                      * them.
1695                      */
1696                     cont_level++;
1697                 }
1698             }
1699
1700             /* move to next continuation record */
1701             m = m->next;
1702         }
1703 #if MIME_MAGIC_DEBUG
1704         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1705                     MODNAME ": matched after %d rules", rule_counter);
1706 #endif
1707         return 1;               /* all through */
1708     }
1709 #if MIME_MAGIC_DEBUG
1710     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1711                 MODNAME ": failed after %d rules", rule_counter);
1712 #endif
1713     return 0;                   /* no match at all */
1714 }
1715
1716 static void mprint(request_rec *r, union VALUETYPE *p, struct magic *m)
1717 {
1718     char *pp, *rt;
1719     unsigned long v;
1720
1721     switch (m->type) {
1722     case BYTE:
1723         v = p->b;
1724         break;
1725
1726     case SHORT:
1727     case BESHORT:
1728     case LESHORT:
1729         v = p->h;
1730         break;
1731
1732     case LONG:
1733     case BELONG:
1734     case LELONG:
1735         v = p->l;
1736         break;
1737
1738     case STRING:
1739         if (m->reln == '=') {
1740             (void) magic_rsl_printf(r, m->desc, m->value.s);
1741         }
1742         else {
1743             (void) magic_rsl_printf(r, m->desc, p->s);
1744         }
1745         return;
1746
1747     case DATE:
1748     case BEDATE:
1749     case LEDATE:
1750         /* XXX: not multithread safe */
1751         pp = ctime((time_t *) & p->l);
1752         if ((rt = strchr(pp, '\n')) != NULL)
1753             *rt = '\0';
1754         (void) magic_rsl_printf(r, m->desc, pp);
1755         return;
1756     default:
1757         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r,
1758                     MODNAME ": invalid m->type (%d) in mprint().",
1759                     m->type);
1760         return;
1761     }
1762
1763     v = signextend(r->server, m, v) & m->mask;
1764     (void) magic_rsl_printf(r, m->desc, (unsigned long) v);
1765 }
1766
1767 /*
1768  * Convert the byte order of the data we are looking at
1769  */
1770 static int mconvert(request_rec *r, union VALUETYPE *p, struct magic *m)
1771 {
1772     char *rt;
1773
1774     switch (m->type) {
1775     case BYTE:
1776     case SHORT:
1777     case LONG:
1778     case DATE:
1779         return 1;
1780     case STRING:
1781         /* Null terminate and eat the return */
1782         p->s[sizeof(p->s) - 1] = '\0';
1783         if ((rt = strchr(p->s, '\n')) != NULL)
1784             *rt = '\0';
1785         return 1;
1786     case BESHORT:
1787         p->h = (short) ((p->hs[0] << 8) | (p->hs[1]));
1788         return 1;
1789     case BELONG:
1790     case BEDATE:
1791         p->l = (long)
1792             ((p->hl[0] << 24) | (p->hl[1] << 16) | (p->hl[2] << 8) | (p->hl[3]));
1793         return 1;
1794     case LESHORT:
1795         p->h = (short) ((p->hs[1] << 8) | (p->hs[0]));
1796         return 1;
1797     case LELONG:
1798     case LEDATE:
1799         p->l = (long)
1800             ((p->hl[3] << 24) | (p->hl[2] << 16) | (p->hl[1] << 8) | (p->hl[0]));
1801         return 1;
1802     default:
1803         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r,
1804                     MODNAME ": invalid type %d in mconvert().", m->type);
1805         return 0;
1806     }
1807 }
1808
1809
1810 static int mget(request_rec *r, union VALUETYPE *p, unsigned char *s,
1811                 struct magic *m, apr_size_t nbytes)
1812 {
1813     long offset = m->offset;
1814
1815     if (offset + sizeof(union VALUETYPE) > nbytes)
1816                   return 0;
1817
1818     memcpy(p, s + offset, sizeof(union VALUETYPE));
1819
1820     if (!mconvert(r, p, m))
1821         return 0;
1822
1823     if (m->flag & INDIR) {
1824
1825         switch (m->in.type) {
1826         case BYTE:
1827             offset = p->b + m->in.offset;
1828             break;
1829         case SHORT:
1830             offset = p->h + m->in.offset;
1831             break;
1832         case LONG:
1833             offset = p->l + m->in.offset;
1834             break;
1835         }
1836
1837         if (offset + sizeof(union VALUETYPE) > nbytes)
1838                       return 0;
1839
1840         memcpy(p, s + offset, sizeof(union VALUETYPE));
1841
1842         if (!mconvert(r, p, m))
1843             return 0;
1844     }
1845     return 1;
1846 }
1847
1848 static int mcheck(request_rec *r, union VALUETYPE *p, struct magic *m)
1849 {
1850     register unsigned long l = m->value.l;
1851     register unsigned long v;
1852     int matched;
1853
1854     if ((m->value.s[0] == 'x') && (m->value.s[1] == '\0')) {
1855         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r,
1856                     MODNAME ": BOINK");
1857         return 1;
1858     }
1859
1860     switch (m->type) {
1861     case BYTE:
1862         v = p->b;
1863         break;
1864
1865     case SHORT:
1866     case BESHORT:
1867     case LESHORT:
1868         v = p->h;
1869         break;
1870
1871     case LONG:
1872     case BELONG:
1873     case LELONG:
1874     case DATE:
1875     case BEDATE:
1876     case LEDATE:
1877         v = p->l;
1878         break;
1879
1880     case STRING:
1881         l = 0;
1882         /*
1883          * What we want here is: v = strncmp(m->value.s, p->s, m->vallen);
1884          * but ignoring any nulls.  bcmp doesn't give -/+/0 and isn't
1885          * universally available anyway.
1886          */
1887         v = 0;
1888         {
1889             register unsigned char *a = (unsigned char *) m->value.s;
1890             register unsigned char *b = (unsigned char *) p->s;
1891             register int len = m->vallen;
1892
1893             while (--len >= 0)
1894                 if ((v = *b++ - *a++) != 0)
1895                     break;
1896         }
1897         break;
1898     default:
1899         /*  bogosity, pretend that it just wasn't a match */
1900         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r,
1901                     MODNAME ": invalid type %d in mcheck().", m->type);
1902         return 0;
1903     }
1904
1905     v = signextend(r->server, m, v) & m->mask;
1906
1907     switch (m->reln) {
1908     case 'x':
1909 #if MIME_MAGIC_DEBUG
1910         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1911                     "%lu == *any* = 1", v);
1912 #endif
1913         matched = 1;
1914         break;
1915
1916     case '!':
1917         matched = v != l;
1918 #if MIME_MAGIC_DEBUG
1919         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1920                     "%lu != %lu = %d", v, l, matched);
1921 #endif
1922         break;
1923
1924     case '=':
1925         matched = v == l;
1926 #if MIME_MAGIC_DEBUG
1927         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1928                     "%lu == %lu = %d", v, l, matched);
1929 #endif
1930         break;
1931
1932     case '>':
1933         if (m->flag & UNSIGNED) {
1934             matched = v > l;
1935 #if MIME_MAGIC_DEBUG
1936             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1937                         "%lu > %lu = %d", v, l, matched);
1938 #endif
1939         }
1940         else {
1941             matched = (long) v > (long) l;
1942 #if MIME_MAGIC_DEBUG
1943             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1944                         "%ld > %ld = %d", v, l, matched);
1945 #endif
1946         }
1947         break;
1948
1949     case '<':
1950         if (m->flag & UNSIGNED) {
1951             matched = v < l;
1952 #if MIME_MAGIC_DEBUG
1953             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1954                         "%lu < %lu = %d", v, l, matched);
1955 #endif
1956         }
1957         else {
1958             matched = (long) v < (long) l;
1959 #if MIME_MAGIC_DEBUG
1960             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1961                         "%ld < %ld = %d", v, l, matched);
1962 #endif
1963         }
1964         break;
1965
1966     case '&':
1967         matched = (v & l) == l;
1968 #if MIME_MAGIC_DEBUG
1969         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1970                     "((%lx & %lx) == %lx) = %d", v, l, l, matched);
1971 #endif
1972         break;
1973
1974     case '^':
1975         matched = (v & l) != l;
1976 #if MIME_MAGIC_DEBUG
1977         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
1978                     "((%lx & %lx) != %lx) = %d", v, l, l, matched);
1979 #endif
1980         break;
1981
1982     default:
1983         /* bogosity, pretend it didn't match */
1984         matched = 0;
1985         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r,
1986                     MODNAME ": mcheck: can't happen: invalid relation %d.",
1987                     m->reln);
1988         break;
1989     }
1990
1991     return matched;
1992 }
1993
1994 /* an optimization over plain strcmp() */
1995 #define    STREQ(a, b)    (*(a) == *(b) && strcmp((a), (b)) == 0)
1996
1997 static int ascmagic(request_rec *r, unsigned char *buf, apr_size_t nbytes)
1998 {
1999     int has_escapes = 0;
2000     unsigned char *s;
2001     char nbuf[HOWMANY + 1];     /* one extra for terminating '\0' */
2002     char *token;
2003     register struct names *p;
2004     int small_nbytes;
2005
2006     /* these are easy, do them first */
2007
2008     /*
2009      * for troff, look for . + letter + letter or .\"; this must be done to
2010      * disambiguate tar archives' ./file and other trash from real troff
2011      * input.
2012      */
2013     if (*buf == '.') {
2014         unsigned char *tp = buf + 1;
2015
2016         while (apr_isspace(*tp))
2017             ++tp;               /* skip leading whitespace */
2018         if ((apr_isalnum(*tp) || *tp == '\\') &&
2019              (apr_isalnum(*(tp + 1)) || *tp == '"')) {
2020             magic_rsl_puts(r, "application/x-troff");
2021             return 1;
2022         }
2023     }
2024     if ((*buf == 'c' || *buf == 'C') && apr_isspace(*(buf + 1))) {
2025         /* Fortran */
2026         magic_rsl_puts(r, "text/plain");
2027         return 1;
2028     }
2029
2030     /* look for tokens from names.h - this is expensive!, so we'll limit
2031      * ourselves to only SMALL_HOWMANY bytes */
2032     small_nbytes = (nbytes > SMALL_HOWMANY) ? SMALL_HOWMANY : nbytes;
2033     /* make a copy of the buffer here because strtok() will destroy it */
2034     s = (unsigned char *) memcpy(nbuf, buf, small_nbytes);
2035     s[small_nbytes] = '\0';
2036     has_escapes = (memchr(s, '\033', small_nbytes) != NULL);
2037     /* XXX: not multithread safe */
2038     while ((token = strtok((char *) s, " \t\n\r\f")) != NULL) {
2039         s = NULL;               /* make strtok() keep on tokin' */
2040         for (p = names; p < names + NNAMES; p++) {
2041             if (STREQ(p->name, token)) {
2042                 magic_rsl_puts(r, types[p->type]);
2043                 if (has_escapes)
2044                     magic_rsl_puts(r, " (with escape sequences)");
2045                 return 1;
2046             }
2047         }
2048     }
2049
2050     switch (is_tar(buf, nbytes)) {
2051     case 1:
2052         /* V7 tar archive */
2053         magic_rsl_puts(r, "application/x-tar");
2054         return 1;
2055     case 2:
2056         /* POSIX tar archive */
2057         magic_rsl_puts(r, "application/x-tar");
2058         return 1;
2059     }
2060
2061     /* all else fails, but it is ascii... */
2062     if (has_escapes) {
2063         /* text with escape sequences */
2064         /* we leave this open for further differentiation later */
2065         magic_rsl_puts(r, "text/plain");
2066     }
2067     else {
2068         /* plain text */
2069         magic_rsl_puts(r, "text/plain");
2070     }
2071     return 1;
2072 }
2073
2074
2075 /*
2076  * compress routines: zmagic() - returns 0 if not recognized, uncompresses
2077  * and prints information if recognized uncompress(s, method, old, n, newch)
2078  * - uncompress old into new, using method, return sizeof new
2079  */
2080
2081 static struct {
2082     char *magic;
2083     int maglen;
2084     char *argv[3];
2085     int silent;
2086     char *encoding;     /* MUST be lowercase */
2087 } compr[] = {
2088
2089     /* we use gzip here rather than uncompress because we have to pass
2090      * it a full filename -- and uncompress only considers filenames
2091      * ending with .Z
2092      */
2093     {
2094         "\037\235", 2, {
2095             "gzip", "-dcq", NULL
2096         }, 0, "x-compress"
2097     },
2098     {
2099         "\037\213", 2, {
2100             "gzip", "-dcq", NULL
2101         }, 1, "x-gzip"
2102     },
2103     /*
2104      * XXX pcat does not work, cause I don't know how to make it read stdin,
2105      * so we use gzip
2106      */
2107     {
2108         "\037\036", 2, {
2109             "gzip", "-dcq", NULL
2110         }, 0, "x-gzip"
2111     },
2112 };
2113
2114 static int ncompr = sizeof(compr) / sizeof(compr[0]);
2115
2116 static int zmagic(request_rec *r, unsigned char *buf, apr_size_t nbytes)
2117 {
2118     unsigned char *newbuf;
2119     int newsize;
2120     int i;
2121
2122     for (i = 0; i < ncompr; i++) {
2123         if (nbytes < compr[i].maglen)
2124             continue;
2125         if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0)
2126             break;
2127     }
2128
2129     if (i == ncompr)
2130         return 0;
2131
2132     if ((newsize = uncompress(r, i, &newbuf, nbytes)) > 0) {
2133         tryit(r, newbuf, newsize, 0);
2134
2135         /* set encoding type in the request record */
2136         r->content_encoding = compr[i].encoding;
2137     }
2138     return 1;
2139 }
2140
2141
2142 struct uncompress_parms {
2143     request_rec *r;
2144     int method;
2145 };
2146
2147 static int uncompress_child(struct uncompress_parms *parm, apr_pool_t *cntxt,
2148                             apr_file_t **pipe_in)
2149 {
2150     int rc = 1;
2151     const char *new_argv[4];
2152     const char *const *env;
2153     request_rec *r = parm->r;
2154     apr_pool_t *child_context = cntxt;
2155     apr_procattr_t *procattr;
2156     apr_proc_t *procnew;
2157
2158     env = (const char *const *)ap_create_environment(child_context, r->subprocess_env);
2159
2160     if ((apr_procattr_create(&procattr, child_context) != APR_SUCCESS) ||
2161         (apr_procattr_io_set(procattr, APR_FULL_BLOCK, 
2162                            APR_FULL_BLOCK, APR_NO_PIPE)   != APR_SUCCESS) ||
2163         (apr_procattr_dir_set(procattr, r->filename)        != APR_SUCCESS) ||
2164         (apr_procattr_cmdtype_set(procattr, APR_PROGRAM)    != APR_SUCCESS)) {
2165         /* Something bad happened, tell the world. */
2166         ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_ENOPROC, r,
2167                "couldn't setup child process: %s", r->filename);
2168     }
2169     else {
2170         new_argv[0] = compr[parm->method].argv[0];
2171         new_argv[1] = compr[parm->method].argv[1];
2172         new_argv[2] = r->filename;
2173         new_argv[3] = NULL;
2174
2175         if (compr[parm->method].silent) {
2176             close(STDERR_FILENO);
2177         }
2178
2179         procnew = apr_pcalloc(child_context, sizeof(*procnew));
2180         rc = apr_proc_create(procnew, compr[parm->method].argv[0],
2181                                new_argv, env, procattr, child_context);
2182
2183         if (rc != APR_SUCCESS) {
2184             /* Bad things happened. Everyone should have cleaned up. */
2185             ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_ENOPROC, r,
2186                           MODNAME ": could not execute `%s'.",
2187                           compr[parm->method].argv[0]);
2188         }
2189         else {
2190             apr_pool_note_subprocess(child_context, procnew, kill_after_timeout);
2191             *pipe_in = procnew->out;
2192         }
2193     }
2194
2195     return (rc);
2196 }
2197
2198 static int uncompress(request_rec *r, int method, 
2199                       unsigned char **newch, apr_size_t n)
2200 {
2201     struct uncompress_parms parm;
2202     apr_file_t *pipe_out = NULL;
2203     apr_pool_t *sub_context;
2204     apr_status_t rv;
2205
2206     parm.r = r;
2207     parm.method = method;
2208
2209     /* We make a sub_pool so that we can collect our child early, otherwise
2210      * there are cases (i.e. generating directory indicies with mod_autoindex)
2211      * where we would end up with LOTS of zombies.
2212      */
2213     if (apr_pool_create(&sub_context, r->pool) != APR_SUCCESS)
2214         return -1;
2215
2216     if ((rv = uncompress_child(&parm, sub_context, &pipe_out)) != APR_SUCCESS) {
2217         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
2218                     MODNAME ": couldn't spawn uncompress process: %s", r->uri);
2219         return -1;
2220     }
2221
2222     *newch = (unsigned char *) apr_palloc(r->pool, n);
2223     rv = apr_file_read(pipe_out, *newch, &n);
2224     if (n == 0) {
2225         apr_pool_destroy(sub_context);
2226         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
2227             MODNAME ": read failed %s", r->filename);
2228         return -1;
2229     }
2230     apr_pool_destroy(sub_context);
2231     return n;
2232 }
2233
2234 /*
2235  * is_tar() -- figure out whether file is a tar archive.
2236  *
2237  * Stolen (by author of file utility) from the public domain tar program: Public
2238  * Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu).
2239  *
2240  * @(#)list.c 1.18 9/23/86 Public Domain - gnu $Id: mod_mime_magic.c,v 1.7
2241  * 1997/06/24 00:41:02 ikluft Exp ikluft $
2242  *
2243  * Comments changed and some code/comments reformatted for file command by Ian
2244  * Darwin.
2245  */
2246
2247 #define    isodigit(c)    ( ((c) >= '0') && ((c) <= '7') )
2248
2249 /*
2250  * Return 0 if the checksum is bad (i.e., probably not a tar archive), 1 for
2251  * old UNIX tar file, 2 for Unix Std (POSIX) tar file.
2252  */
2253
2254 static int is_tar(unsigned char *buf, apr_size_t nbytes)
2255 {
2256     register union record *header = (union record *) buf;
2257     register int i;
2258     register long sum, recsum;
2259     register char *p;
2260
2261     if (nbytes < sizeof(union record))
2262                return 0;
2263
2264     recsum = from_oct(8, header->header.chksum);
2265
2266     sum = 0;
2267     p = header->charptr;
2268     for (i = sizeof(union record); --i >= 0;) {
2269         /*
2270          * We can't use unsigned char here because of old compilers, e.g. V7.
2271          */
2272         sum += 0xFF & *p++;
2273     }
2274
2275     /* Adjust checksum to count the "chksum" field as blanks. */
2276     for (i = sizeof(header->header.chksum); --i >= 0;)
2277         sum -= 0xFF & header->header.chksum[i];
2278     sum += ' ' * sizeof header->header.chksum;
2279
2280     if (sum != recsum)
2281         return 0;               /* Not a tar archive */
2282
2283     if (0 == strcmp(header->header.magic, TMAGIC))
2284         return 2;               /* Unix Standard tar archive */
2285
2286     return 1;                   /* Old fashioned tar archive */
2287 }
2288
2289
2290 /*
2291  * Quick and dirty octal conversion.
2292  *
2293  * Result is -1 if the field is invalid (all blank, or nonoctal).
2294  */
2295 static long from_oct(int digs, char *where)
2296 {
2297     register long value;
2298
2299     while (apr_isspace(*where)) {       /* Skip spaces */
2300         where++;
2301         if (--digs <= 0)
2302             return -1;          /* All blank field */
2303     }
2304     value = 0;
2305     while (digs > 0 && isodigit(*where)) {      /* Scan til nonoctal */
2306         value = (value << 3) | (*where++ - '0');
2307         --digs;
2308     }
2309
2310     if (digs > 0 && *where && !apr_isspace(*where))
2311         return -1;              /* Ended on non-space/nul */
2312
2313     return value;
2314 }
2315
2316 /*
2317  * Check for file-revision suffix
2318  *
2319  * This is for an obscure document control system used on an intranet.
2320  * The web representation of each file's revision has an @1, @2, etc
2321  * appended with the revision number.  This needs to be stripped off to
2322  * find the file suffix, which can be recognized by sending the name back
2323  * through a sub-request.  The base file name (without the @num suffix)
2324  * must exist because its type will be used as the result.
2325  */
2326 static int revision_suffix(request_rec *r)
2327 {
2328     int suffix_pos, result;
2329     char *sub_filename;
2330     request_rec *sub;
2331
2332 #if MIME_MAGIC_DEBUG
2333     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
2334                 MODNAME ": revision_suffix checking %s", r->filename);
2335 #endif /* MIME_MAGIC_DEBUG */
2336
2337     /* check for recognized revision suffix */
2338     suffix_pos = strlen(r->filename) - 1;
2339     if (!apr_isdigit(r->filename[suffix_pos])) {
2340         return 0;
2341     }
2342     while (suffix_pos >= 0 && apr_isdigit(r->filename[suffix_pos]))
2343         suffix_pos--;
2344     if (suffix_pos < 0 || r->filename[suffix_pos] != '@') {
2345         return 0;
2346     }
2347
2348     /* perform sub-request for the file name without the suffix */
2349     result = 0;
2350     sub_filename = apr_pstrndup(r->pool, r->filename, suffix_pos);
2351 #if MIME_MAGIC_DEBUG
2352     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
2353                 MODNAME ": subrequest lookup for %s", sub_filename);
2354 #endif /* MIME_MAGIC_DEBUG */
2355     sub = ap_sub_req_lookup_file(sub_filename, r, NULL);
2356
2357     /* extract content type/encoding/language from sub-request */
2358     if (sub->content_type) {
2359         r->content_type = apr_pstrdup(r->pool, sub->content_type);
2360 #if MIME_MAGIC_DEBUG
2361         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r,
2362                     MODNAME ": subrequest %s got %s",
2363                     sub_filename, r->content_type);
2364 #endif /* MIME_MAGIC_DEBUG */
2365         if (sub->content_encoding)
2366             r->content_encoding =
2367                 apr_pstrdup(r->pool, sub->content_encoding);
2368         if (sub->content_language)
2369             r->content_language =
2370                 apr_pstrdup(r->pool, sub->content_language);
2371         result = 1;
2372     }
2373
2374     /* clean up */
2375     ap_destroy_sub_req(sub);
2376
2377     return result;
2378 }
2379
2380 /*
2381  * initialize the module
2382  */
2383 static void magic_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_server)
2384 {
2385     int result;
2386     magic_server_config_rec *conf;
2387     magic_server_config_rec *main_conf;
2388     server_rec *s;
2389 #if MIME_MAGIC_DEBUG
2390     struct magic *m, *prevm;
2391 #endif /* MIME_MAGIC_DEBUG */
2392
2393     main_conf = ap_get_module_config(main_server->module_config, &mime_magic_module);
2394     for (s = main_server; s; s = s->next) {
2395         conf = ap_get_module_config(s->module_config, &mime_magic_module);
2396         if (conf->magicfile == NULL && s != main_server) {
2397             /* inherits from the parent */
2398             *conf = *main_conf;
2399         }
2400         else if (conf->magicfile) {
2401             result = apprentice(s, p);
2402             if (result == -1)
2403                 return;
2404 #if MIME_MAGIC_DEBUG
2405             prevm = 0;
2406             ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, s,
2407                         MODNAME ": magic_init 1 test");
2408             for (m = conf->magic; m; m = m->next) {
2409                 if (apr_isprint((((unsigned long) m) >> 24) & 255) &&
2410                     apr_isprint((((unsigned long) m) >> 16) & 255) &&
2411                     apr_isprint((((unsigned long) m) >> 8) & 255) &&
2412                     apr_isprint(((unsigned long) m) & 255)) {
2413                     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, s,
2414                                 MODNAME ": magic_init 1: POINTER CLOBBERED! "
2415                                 "m=\"%c%c%c%c\" line=%d",
2416                                 (((unsigned long) m) >> 24) & 255,
2417                                 (((unsigned long) m) >> 16) & 255,
2418                                 (((unsigned long) m) >> 8) & 255,
2419                                 ((unsigned long) m) & 255,
2420                                 prevm ? prevm->lineno : -1);
2421                     break;
2422                 }
2423                 prevm = m;
2424             }
2425 #endif
2426         }
2427     }
2428 }
2429
2430 /*
2431  * Find the Content-Type from any resource this module has available
2432  */
2433
2434 static int magic_find_ct(request_rec *r)
2435 {
2436     int result;
2437     magic_server_config_rec *conf;
2438
2439     /* the file has to exist */
2440     if (r->finfo.filetype == 0 || !r->filename) {
2441         return DECLINED;
2442     }
2443
2444     /* was someone else already here? */
2445     if (r->content_type) {
2446         return DECLINED;
2447     }
2448
2449     conf = ap_get_module_config(r->server->module_config, &mime_magic_module);
2450     if (!conf || !conf->magic) {
2451         return DECLINED;
2452     }
2453
2454     /* initialize per-request info */
2455     if (!magic_set_config(r)) {
2456         return HTTP_INTERNAL_SERVER_ERROR;
2457     }
2458
2459     /* try excluding file-revision suffixes */
2460     if (revision_suffix(r) != 1) {
2461         /* process it based on the file contents */
2462         if ((result = magic_process(r)) != OK) {
2463             return result;
2464         }
2465     }
2466
2467     /* if we have any results, put them in the request structure */
2468     return magic_rsl_to_request(r);
2469 }
2470
2471 static void register_hooks(apr_pool_t *p)
2472 {
2473     static const char * const aszPre[]={ "mod_mime.c", NULL };
2474
2475     /* mod_mime_magic should be run after mod_mime, if at all. */
2476
2477     ap_hook_type_checker(magic_find_ct, aszPre, NULL, APR_HOOK_MIDDLE);
2478     ap_hook_post_config(magic_init, NULL, NULL, APR_HOOK_FIRST);
2479 }
2480
2481 /*
2482  * Apache API module interface
2483  */
2484
2485 module mime_magic_module =
2486 {
2487     STANDARD20_MODULE_STUFF,
2488     NULL,                      /* dir config creator */
2489     NULL,                      /* dir merger --- default is to override */
2490     create_magic_server_config,        /* server config */
2491     merge_magic_server_config, /* merge server config */
2492     mime_magic_cmds,           /* command apr_table_t */
2493     register_hooks              /* register hooks */
2494 };
2495
2496