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