1 /*
2  * $LynxId: HTInit.c,v 1.84 2013/05/05 19:49:29 tom Exp $
3  *
4  *		Configuration-specific Initialization		HTInit.c
5  *		----------------------------------------
6  */
7 
8 /*	Define a basic set of suffixes and presentations
9  *	------------------------------------------------
10  */
11 
12 #include <HTUtils.h>
13 
14 /* Implements:
15 */
16 #include <HTInit.h>
17 
18 #include <HTML.h>
19 #include <HTPlain.h>
20 #include <HTMLGen.h>
21 #include <HTFile.h>
22 #include <HTFormat.h>
23 #include <HTMIME.h>
24 #include <HTWSRC.h>
25 
26 #include <HTSaveToFile.h>	/* LJM */
27 #include <LYStrings.h>
28 #include <LYUtils.h>
29 #include <LYGlobalDefs.h>
30 
31 #include <LYexit.h>
32 #include <LYLeaks.h>
33 
34 #define CTrace(p) CTRACE2(TRACE_CFG, p)
35 
36 static int HTLoadTypesConfigFile(char *fn, AcceptMedia media);
37 static int HTLoadExtensionsConfigFile(char *fn);
38 
39 #define SET_SUFFIX1(suffix, description, type) \
40 	HTSetSuffix(suffix, description, type, 1.0)
41 
42 #define SET_SUFFIX5(suffix, mimetype, type, description) \
43        HTSetSuffix5(suffix, mimetype, type, description, 1.0)
44 
45 #define SET_PRESENT(mimetype, command, quality, delay) \
46   HTSetPresentation(mimetype, command, 0, quality, delay, 0.0, 0L, media)
47 
48 #define SET_EXTERNL(rep_in, rep_out, command, quality) \
49     HTSetConversion(rep_in, rep_out, command, quality, 3.0, 0.0, 0L, mediaEXT)
50 
51 #define SET_INTERNL(rep_in, rep_out, command, quality) \
52     HTSetConversion(rep_in, rep_out, command, quality, 0.0, 0.0, 0L, mediaINT)
53 
HTFormatInit(void)54 void HTFormatInit(void)
55 {
56     AcceptMedia media = mediaEXT;
57 
58     CTrace((tfp, "HTFormatInit\n"));
59 #ifdef NeXT
60     SET_PRESENT("application/postscript", "open %s", 1.0, 2.0);
61     SET_PRESENT("image/x-tiff", "open %s", 2.0, 2.0);
62     SET_PRESENT("image/tiff", "open %s", 1.0, 2.0);
63     SET_PRESENT("audio/basic", "open %s", 1.0, 2.0);
64     SET_PRESENT("*", "open %s", 1.0, 0.0);
65 #else
66     if (LYgetXDisplay() != 0) {	/* Must have X11 */
67 	SET_PRESENT("application/postscript", "ghostview %s&", 1.0, 3.0);
68 	if (non_empty(XLoadImageCommand)) {
69 	    /* *INDENT-OFF* */
70 	    SET_PRESENT("image/gif",	   XLoadImageCommand, 1.0, 3.0);
71 	    SET_PRESENT("image/x-xbm",	   XLoadImageCommand, 1.0, 3.0);
72 	    SET_PRESENT("image/x-xbitmap", XLoadImageCommand, 1.0, 3.0);
73 	    SET_PRESENT("image/x-png",	   XLoadImageCommand, 2.0, 3.0);
74 	    SET_PRESENT("image/png",	   XLoadImageCommand, 1.0, 3.0);
75 	    SET_PRESENT("image/x-rgb",	   XLoadImageCommand, 1.0, 3.0);
76 	    SET_PRESENT("image/x-tiff",	   XLoadImageCommand, 2.0, 3.0);
77 	    SET_PRESENT("image/tiff",	   XLoadImageCommand, 1.0, 3.0);
78 	    SET_PRESENT("image/jpeg",	   XLoadImageCommand, 1.0, 3.0);
79 	    /* *INDENT-ON* */
80 
81 	}
82 	SET_PRESENT("video/mpeg", "mpeg_play %s &", 1.0, 3.0);
83 
84     }
85 #endif
86 
87 #ifdef EXEC_SCRIPTS
88     /* set quality to 999.0 for protected exec applications */
89 #ifndef VMS
90     SET_PRESENT("application/x-csh", "csh %s", 999.0, 3.0);
91     SET_PRESENT("application/x-sh", "sh %s", 999.0, 3.0);
92     SET_PRESENT("application/x-ksh", "ksh %s", 999.0, 3.0);
93 #else
94     SET_PRESENT("application/x-VMS_script", "@%s", 999.0, 3.0);
95 #endif /* not VMS */
96 #endif /* EXEC_SCRIPTS */
97 
98     /*
99      * Add our header handlers.
100      */
101     SET_INTERNL("message/x-http-redirection", "*", HTMIMERedirect, 2.0);
102     SET_INTERNL("message/x-http-redirection", "www/present", HTMIMERedirect, 2.0);
103     SET_INTERNL("message/x-http-redirection", "www/debug", HTMIMERedirect, 1.0);
104     SET_INTERNL("www/mime", "www/present", HTMIMEConvert, 1.0);
105     SET_INTERNL("www/mime", "www/download", HTMIMEConvert, 1.0);
106     SET_INTERNL("www/mime", "www/source", HTMIMEConvert, 1.0);
107     SET_INTERNL("www/mime", "www/dump", HTMIMEConvert, 1.0);
108 
109     /*
110      * Add our compressed file handlers.
111      */
112     SET_INTERNL("www/compressed", "www/download", HTCompressed, 1.0);
113     SET_INTERNL("www/compressed", "www/present", HTCompressed, 1.0);
114     SET_INTERNL("www/compressed", "www/source", HTCompressed, 1.0);
115     SET_INTERNL("www/compressed", "www/dump", HTCompressed, 1.0);
116 
117     /*
118      * Added the following to support some content types beginning to surface.
119      */
120     SET_INTERNL("application/html", "text/x-c", HTMLToC, 0.5);
121     SET_INTERNL("application/html", "text/plain", HTMLToPlain, 0.5);
122     SET_INTERNL("text/css", "text/plain", HTMLToPlain, 0.5);
123     SET_INTERNL("application/html", "www/present", HTMLPresent, 2.0);
124     SET_INTERNL("application/xhtml+xml", "www/present", HTMLPresent, 2.0);
125     SET_INTERNL("application/xml", "www/present", HTMLPresent, 2.0);
126     SET_INTERNL("application/html", "www/source", HTPlainPresent, 1.0);
127     SET_INTERNL("application/x-wais-source", "www/source", HTPlainPresent, 1.0);
128     SET_INTERNL("application/x-wais-source", "www/present", HTWSRCConvert, 2.0);
129     SET_INTERNL("application/x-wais-source", "www/download", HTWSRCConvert, 1.0);
130     SET_INTERNL("application/x-wais-source", "www/dump", HTWSRCConvert, 1.0);
131 
132     /*
133      * Save all unknown mime types to disk.
134      */
135     SET_EXTERNL("www/source", "www/present", HTSaveToFile, 1.0);
136     SET_EXTERNL("www/source", "www/source", HTSaveToFile, 1.0);
137     SET_EXTERNL("www/source", "www/download", HTSaveToFile, 1.0);
138     SET_EXTERNL("www/source", "*", HTSaveToFile, 1.0);
139 
140     /*
141      * Output all www/dump presentations to stdout.
142      */
143     SET_EXTERNL("www/source", "www/dump", HTDumpToStdout, 1.0);
144 
145     /*
146      * Now add our basic conversions.
147      */
148     SET_INTERNL("text/x-sgml", "www/source", HTPlainPresent, 1.0);
149     SET_INTERNL("text/x-sgml", "www/present", HTMLPresent, 2.0);
150     SET_INTERNL("text/sgml", "www/source", HTPlainPresent, 1.0);
151     SET_INTERNL("text/sgml", "www/present", HTMLPresent, 1.0);
152     SET_INTERNL("text/css", "www/present", HTPlainPresent, 1.0);
153     SET_INTERNL("text/plain", "www/present", HTPlainPresent, 1.0);
154     SET_INTERNL("text/plain", "www/source", HTPlainPresent, 1.0);
155     SET_INTERNL("text/html", "www/source", HTPlainPresent, 1.0);
156     SET_INTERNL("text/html", "text/x-c", HTMLToC, 0.5);
157     SET_INTERNL("text/html", "text/plain", HTMLToPlain, 0.5);
158     SET_INTERNL("text/html", "www/present", HTMLPresent, 1.0);
159     SET_INTERNL("text/xml", "www/present", HTMLPresent, 2.0);
160 
161     if (LYisAbsPath(global_type_map)) {
162 	/* These should override the default types as necessary.  */
163 	HTLoadTypesConfigFile(global_type_map, mediaSYS);
164     }
165 
166     /*
167      * Load the local maps.
168      */
169     if (IsOurFile(LYAbsOrHomePath(&personal_type_map))
170 	&& LYCanReadFile(personal_type_map)) {
171 	/* These should override everything else. */
172 	HTLoadTypesConfigFile(personal_type_map, mediaUSR);
173     }
174 
175     /*
176      * Put text/html and text/plain at beginning of list.  - kw
177      */
178     HTReorderPresentation(WWW_PLAINTEXT, WWW_PRESENT);
179     HTReorderPresentation(WWW_HTML, WWW_PRESENT);
180 
181     /*
182      * Analyze the list, and set 'get_accept' for those whose representations
183      * are not redundant.
184      */
185     HTFilterPresentations();
186 }
187 
HTPreparsedFormatInit(void)188 void HTPreparsedFormatInit(void)
189 {
190     CTrace((tfp, "HTPreparsedFormatInit\n"));
191     if (LYPreparsedSource) {
192 	SET_INTERNL("text/html", "www/source", HTMLParsedPresent, 1.0);
193 	SET_INTERNL("text/html", "www/dump", HTMLParsedPresent, 1.0);
194     }
195 }
196 
197 /* Some of the following is taken from: */
198 
199 /*
200 Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
201 
202 Permission to use, copy, modify, and distribute this material
203 for any purpose and without fee is hereby granted, provided
204 that the above copyright notice and this permission notice
205 appear in all copies, and that the name of Bellcore not be
206 used in advertising or publicity pertaining to this
207 material without the specific, prior written permission
208 of an authorized representative of Bellcore.  BELLCORE
209 MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
210 OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",
211 WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
212 */
213 /******************************************************
214     Metamail -- A tool to help diverse mail readers
215                 cope with diverse multimedia mail formats.
216 
217     Author:  Nathaniel S. Borenstein, Bellcore
218 
219  ******************************************************* */
220 
221 struct MailcapEntry {
222     char *contenttype;
223     char *command;
224     char *testcommand;
225     int needsterminal;
226     int copiousoutput;
227     int needtofree;
228     char *label;
229     char *printcommand;
230     char *nametemplate;
231     float quality;
232     long int maxbytes;
233 };
234 
235 static int ExitWithError(const char *txt);
236 static int PassesTest(struct MailcapEntry *mc);
237 
GetCommand(char * s,char ** t)238 static char *GetCommand(char *s, char **t)
239 {
240     char *s2;
241     int quoted = 0;
242 
243     s = LYSkipBlanks(s);
244     /* marca -- added + 1 for error case -- oct 24, 1993. */
245     s2 = typeMallocn(char, strlen(s) * 2 + 1);	/* absolute max, if all % signs */
246 
247     if (!s2)
248 	ExitWithError(MEMORY_EXHAUSTED_ABORT);
249 
250     assert(s2 != NULL);
251 
252     *t = s2;
253     while (non_empty(s)) {
254 	if (quoted) {
255 	    if (*s == '%')
256 		*s2++ = '%';	/* Quote through next level, ugh! */
257 
258 	    *s2++ = *s++;
259 	    quoted = 0;
260 	} else {
261 	    if (*s == ';') {
262 		*s2 = '\0';
263 		return (++s);
264 	    }
265 	    if (*s == ESCAPE) {
266 		quoted = 1;
267 		++s;
268 	    } else {
269 		*s2++ = *s++;
270 	    }
271 	}
272     }
273     *s2 = '\0';
274     return (NULL);
275 }
276 
277 /* no leading or trailing space, all lower case */
Cleanse(char * s)278 static char *Cleanse(char *s)
279 {
280     LYTrimLeading(s);
281     LYTrimTrailing(s);
282     LYLowerCase(s);
283     return (s);
284 }
285 
286 /* remove unnecessary (unquoted) blanks in a shell command */
TrimCommand(char * command)287 static void TrimCommand(char *command)
288 {
289     LYTrimTrailing(command);
290 #ifdef UNIX
291     {
292 	char *s = command;
293 	char *d = command;
294 	int ch;
295 	int c0 = ' ';
296 	BOOL escape = FALSE;
297 	BOOL dquote = FALSE;
298 	BOOL squote = FALSE;
299 
300 	while ((ch = *s++) != '\0') {
301 	    if (escape) {
302 		escape = FALSE;
303 	    } else if (squote) {
304 		if (ch == SQUOTE)
305 		    squote = FALSE;
306 	    } else if (dquote) {
307 		switch (ch) {
308 		case DQUOTE:
309 		    dquote = FALSE;
310 		    break;
311 		case ESCAPE:
312 		    escape = TRUE;
313 		    break;
314 		}
315 	    } else {
316 		switch (ch) {
317 		case DQUOTE:
318 		    dquote = TRUE;
319 		    break;
320 		case SQUOTE:
321 		    squote = TRUE;
322 		    break;
323 		}
324 	    }
325 	    if (!escape && !dquote && !squote) {
326 		if (ch == '\t')
327 		    ch = ' ';
328 		if (ch == ' ') {
329 		    if (c0 == ' ')
330 			continue;
331 		}
332 	    }
333 	    *d++ = (char) ch;
334 	    c0 = ch;
335 	}
336 	*d = '\0';
337     }
338 #endif
339 }
340 
ProcessMailcapEntry(FILE * fp,struct MailcapEntry * mc,AcceptMedia media)341 static int ProcessMailcapEntry(FILE *fp, struct MailcapEntry *mc, AcceptMedia media)
342 {
343     size_t rawentryalloc = 2000, len, need;
344     char *rawentry, *s, *t;
345     char *LineBuf = NULL;
346 
347     rawentry = (char *) malloc(rawentryalloc);
348     if (!rawentry)
349 	ExitWithError(MEMORY_EXHAUSTED_ABORT);
350 
351     assert(rawentry != NULL);
352 
353     *rawentry = '\0';
354     while (LYSafeGets(&LineBuf, fp) != 0) {
355 	LYTrimNewline(LineBuf);
356 	if (LineBuf[0] == '#' || LineBuf[0] == '\0')
357 	    continue;
358 	len = strlen(LineBuf);
359 	need = len + strlen(rawentry) + 1;
360 	if (need > rawentryalloc) {
361 	    rawentryalloc += (2000 + need);
362 	    rawentry = typeRealloc(char, rawentry, rawentryalloc);
363 
364 	    if (!rawentry)
365 		ExitWithError(MEMORY_EXHAUSTED_ABORT);
366 
367 	    assert(rawentry != NULL);
368 	}
369 	if (len > 0 && LineBuf[len - 1] == ESCAPE) {
370 	    LineBuf[len - 1] = '\0';
371 	    strcat(rawentry, LineBuf);
372 	} else {
373 	    strcat(rawentry, LineBuf);
374 	    break;
375 	}
376     }
377     FREE(LineBuf);
378 
379     t = s = LYSkipBlanks(rawentry);
380     if (!*s) {
381 	/* totally blank entry -- quietly ignore */
382 	FREE(rawentry);
383 	return (0);
384     }
385     s = strchr(rawentry, ';');
386     if (s == NULL) {
387 	CTrace((tfp,
388 		"ProcessMailcapEntry: Ignoring invalid mailcap entry: %s\n",
389 		rawentry));
390 	FREE(rawentry);
391 	return (0);
392     }
393     *s++ = '\0';
394     if (!strncasecomp(t, "text/html", 9) ||
395 	!strncasecomp(t, "text/plain", 10)) {
396 	--s;
397 	*s = ';';
398 	CTrace((tfp, "ProcessMailcapEntry: Ignoring mailcap entry: %s\n",
399 		rawentry));
400 	FREE(rawentry);
401 	return (0);
402     }
403     LYRemoveBlanks(rawentry);
404     LYLowerCase(rawentry);
405 
406     mc->needsterminal = 0;
407     mc->copiousoutput = 0;
408     mc->needtofree = 1;
409     mc->testcommand = NULL;
410     mc->label = NULL;
411     mc->printcommand = NULL;
412     mc->contenttype = NULL;
413     StrAllocCopy(mc->contenttype, rawentry);
414     mc->quality = (float) 1.0;
415     mc->maxbytes = 0;
416     t = GetCommand(s, &mc->command);
417     if (!t) {
418 	goto assign_presentation;
419     }
420     s = LYSkipBlanks(t);
421     while (s) {
422 	char *arg, *eq, *mallocd_string;
423 
424 	t = GetCommand(s, &mallocd_string);
425 	arg = mallocd_string;
426 	eq = strchr(arg, '=');
427 	if (eq) {
428 	    *eq++ = '\0';
429 	    eq = LYSkipBlanks(eq);
430 	}
431 	if (non_empty(arg)) {
432 	    arg = Cleanse(arg);
433 	    if (!strcmp(arg, "needsterminal")) {
434 		mc->needsterminal = 1;
435 	    } else if (!strcmp(arg, "copiousoutput")) {
436 		mc->copiousoutput = 1;
437 	    } else if (eq && !strcmp(arg, "test")) {
438 		mc->testcommand = NULL;
439 		StrAllocCopy(mc->testcommand, eq);
440 		TrimCommand(mc->testcommand);
441 		CTrace((tfp, "ProcessMailcapEntry: Found testcommand:%s\n",
442 			mc->testcommand));
443 	    } else if (eq && !strcmp(arg, "description")) {
444 		mc->label = eq;	/* ignored */
445 	    } else if (eq && !strcmp(arg, "label")) {
446 		mc->label = eq;	/* ignored: bogus old name for description */
447 	    } else if (eq && !strcmp(arg, "print")) {
448 		mc->printcommand = eq;	/* ignored */
449 	    } else if (eq && !strcmp(arg, "textualnewlines")) {
450 		/* no support for now.  What does this do anyways? */
451 		/* ExceptionalNewline(mc->contenttype, atoi(eq)); */
452 	    } else if (eq && !strcmp(arg, "q")) {
453 		mc->quality = (float) atof(eq);
454 		if (mc->quality > 0.000 && mc->quality < 0.001)
455 		    mc->quality = (float) 0.001;
456 	    } else if (eq && !strcmp(arg, "mxb")) {
457 		mc->maxbytes = atol(eq);
458 		if (mc->maxbytes < 0)
459 		    mc->maxbytes = 0;
460 	    } else if (strcmp(arg, "notes")) {	/* IGNORE notes field */
461 		if (*arg)
462 		    CTrace((tfp,
463 			    "ProcessMailcapEntry: Ignoring mailcap flag '%s'.\n",
464 			    arg));
465 	    }
466 
467 	}
468 	FREE(mallocd_string);
469 	s = t;
470     }
471 
472   assign_presentation:
473     FREE(rawentry);
474 
475     if (PassesTest(mc)) {
476 	CTrace((tfp, "ProcessMailcapEntry Setting up conversion %s : %s\n",
477 		mc->contenttype, mc->command));
478 	HTSetPresentation(mc->contenttype,
479 			  mc->command,
480 			  mc->testcommand,
481 			  mc->quality,
482 			  3.0, 0.0, mc->maxbytes, media);
483     }
484     FREE(mc->command);
485     FREE(mc->testcommand);
486     FREE(mc->contenttype);
487 
488     return (1);
489 }
490 
491 #define L_CURL '{'
492 #define R_CURL '}'
493 
LYSkipQuoted(const char * s)494 static const char *LYSkipQuoted(const char *s)
495 {
496     int escaped = 0;
497 
498     ++s;			/* skip first quote */
499     while (*s != 0) {
500 	if (escaped) {
501 	    escaped = 0;
502 	} else if (*s == ESCAPE) {
503 	    escaped = 1;
504 	} else if (*s == DQUOTE) {
505 	    ++s;
506 	    break;
507 	}
508 	++s;
509     }
510     return s;
511 }
512 
513 /*
514  * Note: the tspecials[] here are those defined for Content-Type header, so
515  * this function is not really general-purpose.
516  */
LYSkipToken(const char * s)517 static const char *LYSkipToken(const char *s)
518 {
519     static const char tspecials[] = "\"()<>@,;:\\/[]?.=";
520 
521     while (*s != '\0' && !WHITE(*s) && strchr(tspecials, *s) == 0) {
522 	++s;
523     }
524     return s;
525 }
526 
LYSkipValue(const char * s)527 static const char *LYSkipValue(const char *s)
528 {
529     if (*s == DQUOTE)
530 	s = LYSkipQuoted(s);
531     else
532 	s = LYSkipToken(s);
533     return s;
534 }
535 
536 /*
537  * Copy the value from the source, dequoting if needed.
538  */
LYCopyValue(const char * s)539 static char *LYCopyValue(const char *s)
540 {
541     const char *t;
542     char *result = 0;
543     int j, k;
544 
545     if (*s == DQUOTE) {
546 	t = LYSkipQuoted(s);
547 	StrAllocCopy(result, s + 1);
548 	result[t - s - 2] = '\0';
549 	for (j = k = 0;; ++j, ++k) {
550 	    if (result[j] == ESCAPE) {
551 		++j;
552 	    }
553 	    if ((result[k] = result[j]) == '\0')
554 		break;
555 	}
556     } else {
557 	t = LYSkipToken(s);
558 	StrAllocCopy(result, s);
559 	result[t - s] = '\0';
560     }
561     return result;
562 }
563 
564 /*
565  * The "Content-Type:" field, contains zero or more parameters after a ';'.
566  * Return the value of the named parameter, or null.
567  */
LYGetContentType(const char * name,const char * params)568 static char *LYGetContentType(const char *name,
569 			      const char *params)
570 {
571     char *result = 0;
572 
573     if (params != 0) {
574 	if (name != 0) {
575 	    size_t length = strlen(name);
576 	    const char *test = strchr(params, ';');	/* skip type/subtype */
577 	    const char *next;
578 
579 	    while (test != 0) {
580 		BOOL found = FALSE;
581 
582 		++test;		/* skip the ';' */
583 		test = LYSkipCBlanks(test);
584 		next = LYSkipToken(test);
585 		if ((next - test) == (int) length
586 		    && !StrNCmp(test, name, length)) {
587 		    found = TRUE;
588 		}
589 		test = LYSkipCBlanks(next);
590 		if (*test == '=') {
591 		    ++test;
592 		    test = LYSkipCBlanks(test);
593 		    if (found) {
594 			result = LYCopyValue(test);
595 			break;
596 		    } else {
597 			test = LYSkipValue(test);
598 		    }
599 		    test = LYSkipCBlanks(test);
600 		}
601 		if (*test != ';') {
602 		    break;	/* we're lost */
603 		}
604 	    }
605 	} else {		/* return the content-type */
606 	    StrAllocCopy(result, params);
607 	    *LYSkipNonBlanks(result) = '\0';
608 	}
609     }
610     return result;
611 }
612 
613 /*
614  * Check if the command uses a "%s" substitution.  We need to know this, to
615  * decide when to create temporary files, etc.
616  */
LYMailcapUsesPctS(const char * controlstring)617 BOOL LYMailcapUsesPctS(const char *controlstring)
618 {
619     BOOL result = FALSE;
620     const char *from;
621     const char *next;
622     int prefixed = 0;
623     int escaped = 0;
624 
625     for (from = controlstring; *from != '\0'; from++) {
626 	if (escaped) {
627 	    escaped = 0;
628 	} else if (*from == ESCAPE) {
629 	    escaped = 1;
630 	} else if (prefixed) {
631 	    prefixed = 0;
632 	    switch (*from) {
633 	    case '%':		/* not defined */
634 	    case 'n':
635 	    case 'F':
636 	    case 't':
637 		break;
638 	    case 's':
639 		result = TRUE;
640 		break;
641 	    case L_CURL:
642 		next = strchr(from, R_CURL);
643 		if (next != 0) {
644 		    from = next;
645 		    break;
646 		}
647 		/* FALLTHRU */
648 	    default:
649 		break;
650 	    }
651 	} else if (*from == '%') {
652 	    prefixed = 1;
653 	}
654     }
655     return result;
656 }
657 
658 /*
659  * Build the command string for testing or executing a mailcap entry.
660  * If a substitution from the Content-Type header is requested but no
661  * parameters are available, return -1, otherwise 0.
662  *
663  * This does not support multipart %n or %F (does this apply to lynx?)
664  */
BuildCommand(HTChunk * cmd,const char * controlstring,const char * TmpFileName,const char * params)665 static int BuildCommand(HTChunk *cmd,
666 			const char *controlstring,
667 			const char *TmpFileName,
668 			const char *params)
669 {
670     int result = 0;
671     size_t TmpFileLen = strlen(TmpFileName);
672     const char *from;
673     const char *next;
674     char *name, *value;
675     int prefixed = 0;
676     int escaped = 0;
677 
678     for (from = controlstring; *from != '\0'; from++) {
679 	if (escaped) {
680 	    escaped = 0;
681 	    HTChunkPutc(cmd, UCH(*from));
682 	} else if (*from == ESCAPE) {
683 	    escaped = 1;
684 	} else if (prefixed) {
685 	    prefixed = 0;
686 	    switch (*from) {
687 	    case '%':		/* not defined */
688 		HTChunkPutc(cmd, UCH(*from));
689 		break;
690 	    case 'n':
691 		/* FALLTHRU */
692 	    case 'F':
693 		CTrace((tfp, "BuildCommand: Bad mailcap \"test\" clause: %s\n",
694 			controlstring));
695 		break;
696 	    case 't':
697 		if ((value = LYGetContentType(NULL, params)) != 0) {
698 		    HTChunkPuts(cmd, value);
699 		    FREE(value);
700 		}
701 		break;
702 	    case 's':
703 		if (TmpFileLen) {
704 		    HTChunkPuts(cmd, TmpFileName);
705 		}
706 		break;
707 	    case L_CURL:
708 		next = strchr(from, R_CURL);
709 		if (next != 0) {
710 		    if (params != 0) {
711 			++from;
712 			name = 0;
713 			HTSprintf0(&name, "%.*s", (int) (next - from), from);
714 			if ((value = LYGetContentType(name, params)) != 0) {
715 			    HTChunkPuts(cmd, value);
716 			    FREE(value);
717 			} else if (name) {
718 			    if (!strcmp(name, "charset")) {
719 				HTChunkPuts(cmd, "ISO-8859-1");
720 			    } else {
721 				CTrace((tfp, "BuildCommand no value for %s\n", name));
722 			    }
723 			}
724 			FREE(name);
725 		    } else {
726 			result = -1;
727 		    }
728 		    from = next;
729 		    break;
730 		}
731 		/* FALLTHRU */
732 	    default:
733 		CTrace((tfp,
734 			"BuildCommand: Ignoring unrecognized format code in mailcap file '%%%c'.\n",
735 			*from));
736 		break;
737 	    }
738 	} else if (*from == '%') {
739 	    prefixed = 1;
740 	} else {
741 	    HTChunkPutc(cmd, UCH(*from));
742 	}
743     }
744     HTChunkTerminate(cmd);
745     return result;
746 }
747 
748 /*
749  * Build the mailcap test-command and execute it.  This is only invoked when
750  * we cannot tell just by looking at the command if it would succeed.
751  *
752  * Returns 0 for success, -1 for error and 1 for deferred.
753  */
LYTestMailcapCommand(const char * testcommand,const char * params)754 int LYTestMailcapCommand(const char *testcommand,
755 			 const char *params)
756 {
757     int result;
758     char TmpFileName[LY_MAXPATH];
759     HTChunk *expanded = 0;
760 
761     if (LYMailcapUsesPctS(testcommand)) {
762 	if (LYOpenTemp(TmpFileName, HTML_SUFFIX, "w") == 0)
763 	    ExitWithError(CANNOT_OPEN_TEMP);
764 	LYCloseTemp(TmpFileName);
765     } else {
766 	/* We normally don't need a temp file name - kw */
767 	TmpFileName[0] = '\0';
768     }
769     expanded = HTChunkCreate(1024);
770     if (BuildCommand(expanded, testcommand, TmpFileName, params) != 0) {
771 	result = 1;
772 	CTrace((tfp, "PassesTest: Deferring test command: %s\n", expanded->data));
773     } else {
774 	CTrace((tfp, "PassesTest: Executing test command: %s\n", expanded->data));
775 	if ((result = LYSystem(expanded->data)) != 0) {
776 	    result = -1;
777 	    CTrace((tfp, "PassesTest: Test failed!\n"));
778 	} else {
779 	    CTrace((tfp, "PassesTest: Test passed!\n"));
780 	}
781     }
782 
783     HTChunkFree(expanded);
784     (void) LYRemoveTemp(TmpFileName);
785 
786     return result;
787 }
788 
LYMakeMailcapCommand(const char * command,const char * params,const char * filename)789 char *LYMakeMailcapCommand(const char *command,
790 			   const char *params,
791 			   const char *filename)
792 {
793     HTChunk *expanded = 0;
794     char *result = 0;
795 
796     expanded = HTChunkCreate(1024);
797     BuildCommand(expanded, command, filename, params);
798     StrAllocCopy(result, expanded->data);
799     HTChunkFree(expanded);
800     return result;
801 }
802 
803 #define RTR_forget      0
804 #define RTR_lookup      1
805 #define RTR_add         2
806 
RememberTestResult(int mode,char * cmd,int result)807 static int RememberTestResult(int mode, char *cmd, int result)
808 {
809     struct cmdlist_s {
810 	char *cmd;
811 	int result;
812 	struct cmdlist_s *next;
813     };
814     static struct cmdlist_s *cmdlist = NULL;
815     struct cmdlist_s *cur;
816 
817     switch (mode) {
818     case RTR_forget:
819 	while (cmdlist) {
820 	    cur = cmdlist->next;
821 	    FREE(cmdlist->cmd);
822 	    FREE(cmdlist);
823 	    cmdlist = cur;
824 	}
825 	break;
826     case RTR_lookup:
827 	for (cur = cmdlist; cur; cur = cur->next)
828 	    if (!strcmp(cmd, cur->cmd))
829 		return cur->result;
830 	return -1;
831     case RTR_add:
832 	cur = typecalloc(struct cmdlist_s);
833 
834 	if (cur == NULL)
835 	    outofmem(__FILE__, "RememberTestResult");
836 
837 	assert(cur != NULL);
838 
839 	cur->next = cmdlist;
840 	StrAllocCopy(cur->cmd, cmd);
841 	cur->result = result;
842 	cmdlist = cur;
843 	break;
844     }
845     return 0;
846 }
847 
848 /* FIXME: this sometimes used caseless comparison, e.g., strcasecomp */
849 #define SameCommand(tst,ref) !strcmp(tst,ref)
850 
PassesTest(struct MailcapEntry * mc)851 static int PassesTest(struct MailcapEntry *mc)
852 {
853     int result;
854 
855     /*
856      *  Make sure we have a command
857      */
858     if (!mc->testcommand)
859 	return (1);
860 
861     /*
862      *  Save overhead of system() calls by faking these. - FM
863      */
864     if (SameCommand(mc->testcommand, "test \"$DISPLAY\"") ||
865 	SameCommand(mc->testcommand, "test \"$DISPLAY\" != \"\"") ||
866 	SameCommand(mc->testcommand, "test -n \"$DISPLAY\"")) {
867 	FREE(mc->testcommand);
868 	CTrace((tfp, "PassesTest: Testing for XWINDOWS environment.\n"));
869 	if (LYgetXDisplay() != NULL) {
870 	    CTrace((tfp, "PassesTest: Test passed!\n"));
871 	    return (0 == 0);
872 	} else {
873 	    CTrace((tfp, "PassesTest: Test failed!\n"));
874 	    return (-1 == 0);
875 	}
876     }
877     if (SameCommand(mc->testcommand, "test -z \"$DISPLAY\"")) {
878 	FREE(mc->testcommand);
879 	CTrace((tfp, "PassesTest: Testing for NON_XWINDOWS environment.\n"));
880 	if (LYgetXDisplay() == NULL) {
881 	    CTrace((tfp, "PassesTest: Test passed!\n"));
882 	    return (0 == 0);
883 	} else {
884 	    CTrace((tfp, "PassesTest: Test failed!\n"));
885 	    return (-1 == 0);
886 	}
887     }
888 
889     /*
890      *  Why do anything but return success for this one! - FM
891      */
892     if (SameCommand(mc->testcommand, "test -n \"$LYNX_VERSION\"")) {
893 	FREE(mc->testcommand);
894 	CTrace((tfp, "PassesTest: Testing for LYNX environment.\n"));
895 	CTrace((tfp, "PassesTest: Test passed!\n"));
896 	return (0 == 0);
897     } else
898 	/*
899 	 *  ... or failure for this one! - FM
900 	 */
901     if (SameCommand(mc->testcommand, "test -z \"$LYNX_VERSION\"")) {
902 	FREE(mc->testcommand);
903 	CTrace((tfp, "PassesTest: Testing for non-LYNX environment.\n"));
904 	CTrace((tfp, "PassesTest: Test failed!\n"));
905 	return (-1 == 0);
906     }
907 
908     result = RememberTestResult(RTR_lookup, mc->testcommand, 0);
909     if (result == -1) {
910 	result = LYTestMailcapCommand(mc->testcommand, NULL);
911 	RememberTestResult(RTR_add, mc->testcommand, result ? 1 : 0);
912     }
913 
914     /*
915      *  Free the test command as well since
916      *  we wont be needing it anymore.
917      */
918     if (result != 1)
919 	FREE(mc->testcommand);
920 
921     if (result < 0) {
922 	CTrace((tfp, "PassesTest: Test failed!\n"));
923     } else if (result == 0) {
924 	CTrace((tfp, "PassesTest: Test passed!\n"));
925     }
926 
927     return (result >= 0);
928 }
929 
ProcessMailcapFile(char * file,AcceptMedia media)930 static int ProcessMailcapFile(char *file, AcceptMedia media)
931 {
932     struct MailcapEntry mc;
933     FILE *fp;
934 
935     CTrace((tfp, "ProcessMailcapFile: Loading file '%s'.\n",
936 	    file));
937     if ((fp = fopen(file, TXT_R)) == NULL) {
938 	CTrace((tfp, "ProcessMailcapFile: Could not open '%s'.\n",
939 		file));
940 	return (-1 == 0);
941     }
942 
943     while (fp && !feof(fp)) {
944 	ProcessMailcapEntry(fp, &mc, media);
945     }
946     LYCloseInput(fp);
947     RememberTestResult(RTR_forget, NULL, 0);
948     return (0 == 0);
949 }
950 
ExitWithError(const char * txt)951 static int ExitWithError(const char *txt)
952 {
953     if (txt)
954 	fprintf(tfp, "Lynx: %s\n", txt);
955     exit_immediately(EXIT_FAILURE);
956     return (-1);
957 }
958 
959 /* Reverse the entries from each mailcap after it has been read, so that
960  * earlier entries have precedence.  Set to 0 to get traditional lynx
961  * behavior, which means that the last match wins. - kw */
962 static int reverse_mailcap = 1;
963 
HTLoadTypesConfigFile(char * fn,AcceptMedia media)964 static int HTLoadTypesConfigFile(char *fn, AcceptMedia media)
965 {
966     int result = 0;
967     HTList *saved = HTPresentations;
968 
969     if (reverse_mailcap) {	/* temporarily hide existing list */
970 	HTPresentations = NULL;
971     }
972 
973     result = ProcessMailcapFile(fn, media);
974 
975     if (reverse_mailcap) {
976 	if (result && HTPresentations) {
977 	    HTList_reverse(HTPresentations);
978 	    HTList_appendList(HTPresentations, saved);
979 	    FREE(saved);
980 	} else {
981 	    HTPresentations = saved;
982 	}
983     }
984     return result;
985 }
986 
987 /* ------------------------------------------------------------------------ */
988 /* ------------------------------------------------------------------------ */
989 /* ------------------------------------------------------------------------ */
990 
991 /*	Define a basic set of suffixes
992  *	------------------------------
993  *
994  *	The LAST suffix for a type is that used for temporary files
995  *	of that type.
996  *	The quality is an apriori bias as to whether the file should be
997  *	used.  Not that different suffixes can be used to represent files
998  *	which are of the same format but are originals or regenerated,
999  *	with different values.
1000  */
1001 /*
1002  * Additional notes:  the encoding parameter may be taken into account when
1003  * looking for a match; for that purpose "7bit", "8bit", and "binary" are
1004  * equivalent.
1005  *
1006  * Use of mixed case and of pseudo MIME types with embedded spaces should be
1007  * avoided.  It was once necessary for getting the fancy strings into type
1008  * labels in FTP directory listings, but that can now be done with the
1009  * description field (using HTSetSuffix5).  AFAIK the only effect of such
1010  * "fancy" (and mostly invalid) types that cannot be reproduced by using a
1011  * description fields is some statusline messages in SaveToFile (HTFWriter.c).
1012  * And showing the user an invalid MIME type as the 'Content-type:' is not such
1013  * a hot idea anyway, IMO.  Still, if you want it, it is still possible (even
1014  * in lynx.cfg now), but use of it in the defaults below has been reduced.
1015  *
1016  * Case variations rely on peculiar behavior of HTAtom.c for matching.  They
1017  * lead to surprising behavior, Lynx retains the case of a string in the form
1018  * first encountered after starting up.  So while later suffix rules generally
1019  * override or modify earlier ones, the case used for a MIME time is determined
1020  * by the first suffix rule (or other occurrence).  Matching in HTAtom_for is
1021  * effectively case insensitive, except for the first character of the string
1022  * which is treated as case-sensitive by the hash function there; best not to
1023  * rely on that, rather convert MIME types to lowercase on input as is already
1024  * done in most places (And HTAtom could become consistently case-sensitive, as
1025  * in newer W3C libwww).
1026  *  - kw 1999-10-12
1027  */
HTFileInit(void)1028 void HTFileInit(void)
1029 {
1030 #ifdef BUILTIN_SUFFIX_MAPS
1031     if (LYUseBuiltinSuffixes) {
1032 	CTrace((tfp, "HTFileInit: Loading default (HTInit) extension maps.\n"));
1033 
1034 	/* default suffix interpretation */
1035 	SET_SUFFIX1("*", "text/plain", "8bit");
1036 	SET_SUFFIX1("*.*", "text/plain", "8bit");
1037 
1038 #ifdef EXEC_SCRIPTS
1039 	/*
1040 	 * define these extensions for exec scripts.
1041 	 */
1042 #ifndef VMS
1043 	/* for csh exec links */
1044 	HTSetSuffix(".csh", "application/x-csh", "8bit", 0.8);
1045 	HTSetSuffix(".sh", "application/x-sh", "8bit", 0.8);
1046 	HTSetSuffix(".ksh", "application/x-ksh", "8bit", 0.8);
1047 #else
1048 	HTSetSuffix(".com", "application/x-VMS_script", "8bit", 0.8);
1049 #endif /* !VMS */
1050 #endif /* EXEC_SCRIPTS */
1051 
1052 	/*
1053 	 * Some of the old incarnation of the mappings is preserved and can be had
1054 	 * by defining TRADITIONAL_SUFFIXES.  This is for some cases where I felt
1055 	 * the old rules might be preferred by someone, for some reason.  It's not
1056 	 * done consistently.  A lot more of this stuff could probably be changed
1057 	 * too or omitted, now that nearly the equivalent functionality is
1058 	 * available in lynx.cfg.  - kw 1999-10-12
1059 	 */
1060 	/* *INDENT-OFF* */
1061 	SET_SUFFIX1(".saveme",	"application/x-Binary",		"binary");
1062 	SET_SUFFIX1(".dump",	"application/x-Binary",		"binary");
1063 	SET_SUFFIX1(".bin",	"application/x-Binary",		"binary");
1064 
1065 	SET_SUFFIX1(".arc",	"application/x-Compressed",	"binary");
1066 
1067 	SET_SUFFIX1(".alpha-exe", "application/x-Executable",	"binary");
1068 	SET_SUFFIX1(".alpha_exe", "application/x-Executable",	"binary");
1069 	SET_SUFFIX1(".AXP-exe", "application/x-Executable",	"binary");
1070 	SET_SUFFIX1(".AXP_exe", "application/x-Executable",	"binary");
1071 	SET_SUFFIX1(".VAX-exe", "application/x-Executable",	"binary");
1072 	SET_SUFFIX1(".VAX_exe", "application/x-Executable",	"binary");
1073 	SET_SUFFIX5(".exe",	"application/octet-stream",	"binary", "Executable");
1074 
1075 #ifdef TRADITIONAL_SUFFIXES
1076 	SET_SUFFIX1(".exe.Z",	"application/x-Comp. Executable", "binary");
1077 	SET_SUFFIX1(".Z",	"application/UNIX Compressed",	"binary");
1078 	SET_SUFFIX1(".tar_Z",	"application/UNIX Compr. Tar",	"binary");
1079 	SET_SUFFIX1(".tar.Z",	"application/UNIX Compr. Tar",	"binary");
1080 #else
1081 	SET_SUFFIX5(".Z",	"application/x-compress",	"binary", "UNIX Compressed");
1082 	SET_SUFFIX5(".Z",	NULL,				"compress", "UNIX Compressed");
1083 	SET_SUFFIX5(".exe.Z",	"application/octet-stream",	"compress", "Executable");
1084 	SET_SUFFIX5(".tar_Z",	"application/x-tar",		"compress", "UNIX Compr. Tar");
1085 	SET_SUFFIX5(".tar.Z",	"application/x-tar",		"compress", "UNIX Compr. Tar");
1086 #endif
1087 
1088 #ifdef TRADITIONAL_SUFFIXES
1089 	SET_SUFFIX1("-gz",	"application/GNU Compressed",	"binary");
1090 	SET_SUFFIX1("_gz",	"application/GNU Compressed",	"binary");
1091 	SET_SUFFIX1(".gz",	"application/GNU Compressed",	"binary");
1092 
1093 	SET_SUFFIX5(".tar.gz",	"application/x-tar",		"binary", "GNU Compr. Tar");
1094 	SET_SUFFIX5(".tgz",	"application/x-tar",		"gzip", "GNU Compr. Tar");
1095 #else
1096 	SET_SUFFIX5("-gz",	"application/x-gzip",		"binary", "GNU Compressed");
1097 	SET_SUFFIX5("_gz",	"application/x-gzip",		"binary", "GNU Compressed");
1098 	SET_SUFFIX5(".gz",	"application/x-gzip",		"binary", "GNU Compressed");
1099 	SET_SUFFIX5("-gz",	NULL,				"gzip", "GNU Compressed");
1100 	SET_SUFFIX5("_gz",	NULL,				"gzip", "GNU Compressed");
1101 	SET_SUFFIX5(".gz",	NULL,				"gzip", "GNU Compressed");
1102 
1103 	SET_SUFFIX5(".tar.gz",	"application/x-tar",		"gzip", "GNU Compr. Tar");
1104 	SET_SUFFIX5(".tgz",	"application/x-tar",		"gzip", "GNU Compr. Tar");
1105 #endif
1106 
1107 #ifdef TRADITIONAL_SUFFIXES
1108 	SET_SUFFIX1(".src",	"application/x-WAIS-source",	"8bit");
1109 	SET_SUFFIX1(".wsrc",	"application/x-WAIS-source",	"8bit");
1110 #else
1111 	SET_SUFFIX5(".wsrc",	"application/x-wais-source",	"8bit", "WAIS-source");
1112 #endif
1113 
1114 	SET_SUFFIX5(".zip",	"application/zip",		"binary", "Zip File");
1115 
1116 	SET_SUFFIX1(".zz",	"application/x-deflate",	"binary");
1117 	SET_SUFFIX1(".zz",	"application/deflate",		"binary");
1118 
1119 	SET_SUFFIX1(".bz2",	"application/x-bzip2",		"binary");
1120 	SET_SUFFIX1(".bz2",	"application/bzip2",		"binary");
1121 
1122 #ifdef TRADITIONAL_SUFFIXES
1123 	SET_SUFFIX1(".uu",	"application/x-UUencoded",	"8bit");
1124 
1125 	SET_SUFFIX1(".hqx",	"application/x-Binhex",		"8bit");
1126 
1127 	SET_SUFFIX1(".o",	"application/x-Prog. Object",	"binary");
1128 	SET_SUFFIX1(".a",	"application/x-Prog. Library",	"binary");
1129 #else
1130 	SET_SUFFIX5(".uu",	"application/x-uuencoded",	"7bit", "UUencoded");
1131 
1132 	SET_SUFFIX5(".hqx",	"application/mac-binhex40",	"8bit", "Mac BinHex");
1133 
1134 	HTSetSuffix5(".o",	"application/octet-stream",	"binary", "Prog. Object", 0.5);
1135 	HTSetSuffix5(".a",	"application/octet-stream",	"binary", "Prog. Library", 0.5);
1136 	HTSetSuffix5(".so",	"application/octet-stream",	"binary", "Shared Lib", 0.5);
1137 #endif
1138 
1139 	SET_SUFFIX5(".oda",	"application/oda",		"binary", "ODA");
1140 
1141 	SET_SUFFIX5(".pdf",	"application/pdf",		"binary", "PDF");
1142 
1143 	SET_SUFFIX5(".eps",	"application/postscript",	"8bit", "Postscript");
1144 	SET_SUFFIX5(".ai",	"application/postscript",	"8bit", "Postscript");
1145 	SET_SUFFIX5(".ps",	"application/postscript",	"8bit", "Postscript");
1146 
1147 	SET_SUFFIX5(".rtf",	"application/rtf",		"8bit", "RTF");
1148 
1149 	SET_SUFFIX5(".dvi",	"application/x-dvi",		"8bit", "DVI");
1150 
1151 	SET_SUFFIX5(".hdf",	"application/x-hdf",		"8bit", "HDF");
1152 
1153 	SET_SUFFIX1(".cdf",	"application/x-netcdf",		"8bit");
1154 	SET_SUFFIX1(".nc",	"application/x-netcdf",		"8bit");
1155 
1156 #ifdef TRADITIONAL_SUFFIXES
1157 	SET_SUFFIX1(".latex",	"application/x-Latex",		"8bit");
1158 	SET_SUFFIX1(".tex",	"application/x-Tex",		"8bit");
1159 	SET_SUFFIX1(".texinfo", "application/x-Texinfo",	"8bit");
1160 	SET_SUFFIX1(".texi",	"application/x-Texinfo",	"8bit");
1161 #else
1162 	SET_SUFFIX5(".latex",	"application/x-latex",		"8bit", "LaTeX");
1163 	SET_SUFFIX5(".tex",	"text/x-tex",			"8bit", "TeX");
1164 	SET_SUFFIX5(".texinfo", "application/x-texinfo",	"8bit", "Texinfo");
1165 	SET_SUFFIX5(".texi",	"application/x-texinfo",	"8bit", "Texinfo");
1166 #endif
1167 
1168 #ifdef TRADITIONAL_SUFFIXES
1169 	SET_SUFFIX1(".t",	"application/x-Troff",		"8bit");
1170 	SET_SUFFIX1(".tr",	"application/x-Troff",		"8bit");
1171 	SET_SUFFIX1(".roff",	"application/x-Troff",		"8bit");
1172 
1173 	SET_SUFFIX1(".man",	"application/x-Troff-man",	"8bit");
1174 	SET_SUFFIX1(".me",	"application/x-Troff-me",	"8bit");
1175 	SET_SUFFIX1(".ms",	"application/x-Troff-ms",	"8bit");
1176 #else
1177 	SET_SUFFIX5(".t",	"application/x-troff",		"8bit", "Troff");
1178 	SET_SUFFIX5(".tr",	"application/x-troff",		"8bit", "Troff");
1179 	SET_SUFFIX5(".roff",	"application/x-troff",		"8bit", "Troff");
1180 
1181 	SET_SUFFIX5(".man",	"application/x-troff-man",	"8bit", "Man Page");
1182 	SET_SUFFIX5(".me",	"application/x-troff-me",	"8bit", "Troff me");
1183 	SET_SUFFIX5(".ms",	"application/x-troff-ms",	"8bit", "Troff ms");
1184 #endif
1185 
1186 	SET_SUFFIX1(".zoo",	"application/x-Zoo File",	"binary");
1187 
1188 #if defined(TRADITIONAL_SUFFIXES) || defined(VMS)
1189 	SET_SUFFIX1(".bak",	"application/x-VMS BAK File",	"binary");
1190 	SET_SUFFIX1(".bkp",	"application/x-VMS BAK File",	"binary");
1191 	SET_SUFFIX1(".bck",	"application/x-VMS BAK File",	"binary");
1192 
1193 	SET_SUFFIX5(".bkp_gz",	"application/octet-stream",	"gzip", "GNU BAK File");
1194 	SET_SUFFIX5(".bkp-gz",	"application/octet-stream",	"gzip", "GNU BAK File");
1195 	SET_SUFFIX5(".bck_gz",	"application/octet-stream",	"gzip", "GNU BAK File");
1196 	SET_SUFFIX5(".bck-gz",	"application/octet-stream",	"gzip", "GNU BAK File");
1197 
1198 	SET_SUFFIX5(".bkp-Z",	"application/octet-stream",	"compress", "Comp. BAK File");
1199 	SET_SUFFIX5(".bkp_Z",	"application/octet-stream",	"compress", "Comp. BAK File");
1200 	SET_SUFFIX5(".bck-Z",	"application/octet-stream",	"compress", "Comp. BAK File");
1201 	SET_SUFFIX5(".bck_Z",	"application/octet-stream",	"compress", "Comp. BAK File");
1202 #else
1203 	HTSetSuffix5(".bak",	NULL,				"binary", "Backup", 0.5);
1204 	SET_SUFFIX5(".bkp",	"application/octet-stream",	"binary", "VMS BAK File");
1205 	SET_SUFFIX5(".bck",	"application/octet-stream",	"binary", "VMS BAK File");
1206 #endif
1207 
1208 #if defined(TRADITIONAL_SUFFIXES) || defined(VMS)
1209 	SET_SUFFIX1(".hlb",	"application/x-VMS Help Libr.", "binary");
1210 	SET_SUFFIX1(".olb",	"application/x-VMS Obj. Libr.", "binary");
1211 	SET_SUFFIX1(".tlb",	"application/x-VMS Text Libr.", "binary");
1212 	SET_SUFFIX1(".obj",	"application/x-VMS Prog. Obj.", "binary");
1213 	SET_SUFFIX1(".decw$book", "application/x-DEC BookReader", "binary");
1214 	SET_SUFFIX1(".mem",	"application/x-RUNOFF-MANUAL", "8bit");
1215 #else
1216 	SET_SUFFIX5(".hlb",	"application/octet-stream",	"binary", "VMS Help Libr.");
1217 	SET_SUFFIX5(".olb",	"application/octet-stream",	"binary", "VMS Obj. Libr.");
1218 	SET_SUFFIX5(".tlb",	"application/octet-stream",	"binary", "VMS Text Libr.");
1219 	SET_SUFFIX5(".obj",	"application/octet-stream",	"binary", "Prog. Object");
1220 	SET_SUFFIX5(".decw$book", "application/octet-stream",	"binary", "DEC BookReader");
1221 	SET_SUFFIX5(".mem",	"text/x-runoff-manual",		"8bit", "RUNOFF-MANUAL");
1222 #endif
1223 
1224 	SET_SUFFIX1(".vsd",	"application/visio",		"binary");
1225 
1226 	SET_SUFFIX5(".lha",	"application/x-lha",		"binary", "lha File");
1227 	SET_SUFFIX5(".lzh",	"application/x-lzh",		"binary", "lzh File");
1228 	SET_SUFFIX5(".sea",	"application/x-sea",		"binary", "sea File");
1229 #ifdef TRADITIONAL_SUFFIXES
1230 	SET_SUFFIX5(".sit",	"application/x-sit",		"binary", "sit File");
1231 #else
1232 	SET_SUFFIX5(".sit",	"application/x-stuffit",	"binary", "StuffIt");
1233 #endif
1234 	SET_SUFFIX5(".dms",	"application/x-dms",		"binary", "dms File");
1235 	SET_SUFFIX5(".iff",	"application/x-iff",		"binary", "iff File");
1236 
1237 	SET_SUFFIX1(".bcpio",	"application/x-bcpio",		"binary");
1238 	SET_SUFFIX1(".cpio",	"application/x-cpio",		"binary");
1239 
1240 #ifdef TRADITIONAL_SUFFIXES
1241 	SET_SUFFIX1(".gtar",	"application/x-gtar",		"binary");
1242 #endif
1243 
1244 	SET_SUFFIX1(".shar",	"application/x-shar",		"8bit");
1245 	SET_SUFFIX1(".share",	"application/x-share",		"8bit");
1246 
1247 #ifdef TRADITIONAL_SUFFIXES
1248 	SET_SUFFIX1(".sh",	"application/x-sh",		"8bit"); /* xtra */
1249 #endif
1250 
1251 	SET_SUFFIX1(".sv4cpio", "application/x-sv4cpio",	"binary");
1252 	SET_SUFFIX1(".sv4crc",	"application/x-sv4crc",		"binary");
1253 
1254 	SET_SUFFIX5(".tar",	"application/x-tar",		"binary", "Tar File");
1255 	SET_SUFFIX1(".ustar",	"application/x-ustar",		"binary");
1256 
1257 	SET_SUFFIX1(".snd",	"audio/basic",			"binary");
1258 	SET_SUFFIX1(".au",	"audio/basic",			"binary");
1259 
1260 	SET_SUFFIX1(".aifc",	"audio/x-aiff",			"binary");
1261 	SET_SUFFIX1(".aif",	"audio/x-aiff",			"binary");
1262 	SET_SUFFIX1(".aiff",	"audio/x-aiff",			"binary");
1263 	SET_SUFFIX1(".wav",	"audio/x-wav",			"binary");
1264 	SET_SUFFIX1(".midi",	"audio/midi",			"binary");
1265 	SET_SUFFIX1(".mod",	"audio/mod",			"binary");
1266 
1267 	SET_SUFFIX1(".gif",	"image/gif",			"binary");
1268 	SET_SUFFIX1(".ief",	"image/ief",			"binary");
1269 	SET_SUFFIX1(".jfif",	"image/jpeg",			"binary"); /* xtra */
1270 	SET_SUFFIX1(".jfif-tbnl", "image/jpeg",			"binary"); /* xtra */
1271 	SET_SUFFIX1(".jpe",	"image/jpeg",			"binary");
1272 	SET_SUFFIX1(".jpg",	"image/jpeg",			"binary");
1273 	SET_SUFFIX1(".jpeg",	"image/jpeg",			"binary");
1274 	SET_SUFFIX1(".tif",	"image/tiff",			"binary");
1275 	SET_SUFFIX1(".tiff",	"image/tiff",			"binary");
1276 	SET_SUFFIX1(".ham",	"image/ham",			"binary");
1277 	SET_SUFFIX1(".ras",	"image/x-cmu-rast",		"binary");
1278 	SET_SUFFIX1(".pnm",	"image/x-portable-anymap",	"binary");
1279 	SET_SUFFIX1(".pbm",	"image/x-portable-bitmap",	"binary");
1280 	SET_SUFFIX1(".pgm",	"image/x-portable-graymap",	"binary");
1281 	SET_SUFFIX1(".ppm",	"image/x-portable-pixmap",	"binary");
1282 	SET_SUFFIX1(".png",	"image/png",			"binary");
1283 	SET_SUFFIX1(".rgb",	"image/x-rgb",			"binary");
1284 	SET_SUFFIX1(".xbm",	"image/x-xbitmap",		"binary");
1285 	SET_SUFFIX1(".xpm",	"image/x-xpixmap",		"binary");
1286 	SET_SUFFIX1(".xwd",	"image/x-xwindowdump",		"binary");
1287 
1288 	SET_SUFFIX1(".rtx",	"text/richtext",		"8bit");
1289 	SET_SUFFIX1(".tsv",	"text/tab-separated-values",	"8bit");
1290 	SET_SUFFIX1(".etx",	"text/x-setext",		"8bit");
1291 
1292 	SET_SUFFIX1(".mpg",	"video/mpeg",			"binary");
1293 	SET_SUFFIX1(".mpe",	"video/mpeg",			"binary");
1294 	SET_SUFFIX1(".mpeg",	"video/mpeg",			"binary");
1295 	SET_SUFFIX1(".mov",	"video/quicktime",		"binary");
1296 	SET_SUFFIX1(".qt",	"video/quicktime",		"binary");
1297 	SET_SUFFIX1(".avi",	"video/x-msvideo",		"binary");
1298 	SET_SUFFIX1(".movie",	"video/x-sgi-movie",		"binary");
1299 	SET_SUFFIX1(".mv",	"video/x-sgi-movie",		"binary");
1300 
1301 	SET_SUFFIX1(".mime",	"message/rfc822",		"8bit");
1302 
1303 	SET_SUFFIX1(".c",	"text/plain",			"8bit");
1304 	SET_SUFFIX1(".cc",	"text/plain",			"8bit");
1305 	SET_SUFFIX1(".c++",	"text/plain",			"8bit");
1306 	SET_SUFFIX1(".css",	"text/plain",			"8bit");
1307 	SET_SUFFIX1(".h",	"text/plain",			"8bit");
1308 	SET_SUFFIX1(".pl",	"text/plain",			"8bit");
1309 	SET_SUFFIX1(".text",	"text/plain",			"8bit");
1310 	SET_SUFFIX1(".txt",	"text/plain",			"8bit");
1311 
1312 	SET_SUFFIX1(".php",	"text/html",			"8bit");
1313 	SET_SUFFIX1(".php3",	"text/html",			"8bit");
1314 	SET_SUFFIX1(".html3",	"text/html",			"8bit");
1315 	SET_SUFFIX1(".ht3",	"text/html",			"8bit");
1316 	SET_SUFFIX1(".phtml",	"text/html",			"8bit");
1317 	SET_SUFFIX1(".shtml",	"text/html",			"8bit");
1318 	SET_SUFFIX1(".sht",	"text/html",			"8bit");
1319 	SET_SUFFIX1(".htmlx",	"text/html",			"8bit");
1320 	SET_SUFFIX1(".htm",	"text/html",			"8bit");
1321 	SET_SUFFIX1(".html",	"text/html",			"8bit");
1322 	/* *INDENT-ON* */
1323 
1324     } else {			/* LYSuffixRules */
1325 	/*
1326 	 * Note that even .html -> text/html, .htm -> text/html are omitted if
1327 	 * default maps are compiled in but then skipped because of a
1328 	 * configuration file directive.  Whoever changes the config file in
1329 	 * this way can easily also add the SUFFIX rules there.  - kw
1330 	 */
1331 	CTrace((tfp,
1332 		"HTFileInit: Skipping all default (HTInit) extension maps!\n"));
1333     }				/* LYSuffixRules */
1334 
1335 #else /* BUILTIN_SUFFIX_MAPS */
1336 
1337     CTrace((tfp,
1338 	    "HTFileInit: Default (HTInit) extension maps not compiled in.\n"));
1339     /*
1340      * The following two are still used if BUILTIN_SUFFIX_MAPS was undefined.
1341      * Without one of them, lynx would always need to have a mapping specified
1342      * in a lynx.cfg or mime.types file to be usable for local HTML files at
1343      * all.  That includes many of the generated user interface pages.  - kw
1344      */
1345     SET_SUFFIX1(".htm", "text/html", "8bit");
1346     SET_SUFFIX1(".html", "text/html", "8bit");
1347 #endif /* BUILTIN_SUFFIX_MAPS */
1348 
1349     if (LYisAbsPath(global_extension_map)) {
1350 	/* These should override the default extensions as necessary. */
1351 	HTLoadExtensionsConfigFile(global_extension_map);
1352     }
1353 
1354     /*
1355      * Load the local maps.
1356      */
1357     if (IsOurFile(LYAbsOrHomePath(&personal_extension_map))
1358 	&& LYCanReadFile(personal_extension_map)) {
1359 	/* These should override everything else. */
1360 	HTLoadExtensionsConfigFile(personal_extension_map);
1361     }
1362 }
1363 
1364 /* -------------------- Extension config file reading --------------------- */
1365 
1366 /*
1367  *  The following is lifted from NCSA httpd 1.0a1, by Rob McCool;
1368  *  NCSA httpd is in the public domain, as is this code.
1369  *
1370  *  Modified Oct 97 - KW
1371  */
1372 
1373 #define MAX_STRING_LEN 256
1374 
HTGetLine(char * s,int n,FILE * f)1375 static int HTGetLine(char *s, int n, FILE *f)
1376 {
1377     register int i = 0, r;
1378 
1379     if (!f)
1380 	return (1);
1381 
1382     while (1) {
1383 	r = fgetc(f);
1384 	s[i] = (char) r;
1385 
1386 	if (s[i] == CR) {
1387 	    r = fgetc(f);
1388 	    if (r == LF)
1389 		s[i] = (char) r;
1390 	    else if (r != EOF)
1391 		ungetc(r, f);
1392 	}
1393 
1394 	if ((r == EOF) || (s[i] == LF) || (s[i] == CR) || (i == (n - 1))) {
1395 	    s[i] = '\0';
1396 	    return (feof(f) ? 1 : 0);
1397 	}
1398 	++i;
1399     }
1400 }
1401 
HTGetWord(char * word,char * line,int stop,int stop2)1402 static void HTGetWord(char *word, char *line, int stop, int stop2)
1403 {
1404     int x = 0, y;
1405 
1406     for (x = 0; (line[x]
1407 		 && UCH(line[x]) != UCH(stop)
1408 		 && UCH(line[x]) != UCH(stop2)); x++) {
1409 	word[x] = line[x];
1410     }
1411 
1412     word[x] = '\0';
1413     if (line[x])
1414 	++x;
1415     y = 0;
1416 
1417     while ((line[y++] = line[x++])) {
1418 	;
1419     }
1420 
1421     return;
1422 }
1423 
HTLoadExtensionsConfigFile(char * fn)1424 static int HTLoadExtensionsConfigFile(char *fn)
1425 {
1426     char line[MAX_STRING_LEN];
1427     char word[MAX_STRING_LEN];
1428     char *ct;
1429     FILE *f;
1430     int count = 0;
1431 
1432     CTrace((tfp, "HTLoadExtensionsConfigFile: Loading file '%s'.\n", fn));
1433 
1434     if ((f = fopen(fn, TXT_R)) == NULL) {
1435 	CTrace((tfp, "HTLoadExtensionsConfigFile: Could not open '%s'.\n", fn));
1436 	return count;
1437     }
1438 
1439     while (!(HTGetLine(line, (int) sizeof(line), f))) {
1440 	HTGetWord(word, line, ' ', '\t');
1441 	if (line[0] == '\0' || word[0] == '#')
1442 	    continue;
1443 	ct = NULL;
1444 	StrAllocCopy(ct, word);
1445 	LYLowerCase(ct);
1446 
1447 	while (line[0]) {
1448 	    HTGetWord(word, line, ' ', '\t');
1449 	    if (word[0] && (word[0] != ' ')) {
1450 		char *ext = NULL;
1451 
1452 		HTSprintf0(&ext, ".%s", word);
1453 		LYLowerCase(ext);
1454 
1455 		CTrace((tfp, "setting suffix '%s' to '%s'.\n", ext, ct));
1456 
1457 		if (strstr(ct, "tex") != NULL ||
1458 		    strstr(ct, "postscript") != NULL ||
1459 		    strstr(ct, "sh") != NULL ||
1460 		    strstr(ct, "troff") != NULL ||
1461 		    strstr(ct, "rtf") != NULL)
1462 		    SET_SUFFIX1(ext, ct, "8bit");
1463 		else
1464 		    SET_SUFFIX1(ext, ct, "binary");
1465 		count++;
1466 
1467 		FREE(ext);
1468 	    }
1469 	}
1470 	FREE(ct);
1471     }
1472     LYCloseInput(f);
1473 
1474     return count;
1475 }
1476