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