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