1 /*
2  * $LynxId: HTMIME.c,v 1.87 2013/05/05 20:07:25 tom Exp $
3  *
4  *			MIME Message Parse			HTMIME.c
5  *			==================
6  *
7  *	This is RFC 1341-specific code.
8  *	The input stream pushed into this parser is assumed to be
9  *	stripped on CRs, ie lines end with LF, not CR LF.
10  *	(It is easy to change this except for the body part where
11  *	conversion can be slow.)
12  *
13  * History:
14  *	   Feb 92	Written Tim Berners-Lee, CERN
15  *
16  */
17 
18 #define HTSTREAM_INTERNAL 1
19 
20 #include <HTUtils.h>
21 #include <HTMIME.h>		/* Implemented here */
22 #include <HTTP.h>		/* for redirecting_url */
23 #include <HTAlert.h>
24 #include <HTFile.h>
25 #include <HTCJK.h>
26 #include <UCMap.h>
27 #include <UCDefs.h>
28 #include <UCAux.h>
29 
30 #include <LYCookie.h>
31 #include <LYCharSets.h>
32 #include <LYCharUtils.h>
33 #include <LYStrings.h>
34 #include <LYUtils.h>
35 #include <LYLeaks.h>
36 
37 /*		MIME Object
38  *		-----------
39  */
40 
41 typedef enum {
42     MIME_TRANSPARENT,		/* put straight through to target ASAP! */
43     /* states for "Transfer-Encoding: chunked" */
44     MIME_CHUNKED,
45     mcCHUNKED_COUNT_DIGIT,
46     mcCHUNKED_COUNT_CR,
47     mcCHUNKED_COUNT_LF,
48     mcCHUNKED_EXTENSION,
49     mcCHUNKED_DATA,
50     mcCHUNKED_DATA_CR,
51     mcCHUNKED_DATA_LF,
52     /* character state-machine */
53     miBEGINNING_OF_LINE,	/* first character and not a continuation */
54     miA,
55     miACCEPT_RANGES,
56     miAGE,
57     miAL,
58     miALLOW,
59     miALTERNATES,
60     miC,
61     miCACHE_CONTROL,
62     miCO,
63     miCOOKIE,
64     miCON,
65     miCONNECTION,
66     miCONTENT_,
67     miCONTENT_BASE,
68     miCONTENT_DISPOSITION,
69     miCONTENT_ENCODING,
70     miCONTENT_FEATURES,
71     miCONTENT_L,
72     miCONTENT_LANGUAGE,
73     miCONTENT_LENGTH,
74     miCONTENT_LOCATION,
75     miCONTENT_MD5,
76     miCONTENT_RANGE,
77     miCONTENT_T,
78     miCONTENT_TRANSFER_ENCODING,
79     miCONTENT_TYPE,
80     miDATE,
81     miE,
82     miETAG,
83     miEXPIRES,
84     miKEEP_ALIVE,
85     miL,
86     miLAST_MODIFIED,
87     miLINK,
88     miLOCATION,
89     miP,
90     miPR,
91     miPRAGMA,
92     miPROXY_AUTHENTICATE,
93     miPUBLIC,
94     miR,
95     miRE,
96     miREFRESH,
97     miRETRY_AFTER,
98     miS,
99     miSAFE,
100     miSE,
101     miSERVER,
102     miSET_COOKIE,
103     miSET_COOKIE1,
104     miSET_COOKIE2,
105     miT,
106     miTITLE,
107     miTRANSFER_ENCODING,
108     miU,
109     miUPGRADE,
110     miURI,
111     miV,
112     miVARY,
113     miVIA,
114     miW,
115     miWARNING,
116     miWWW_AUTHENTICATE,
117     miSKIP_GET_VALUE,		/* Skip space then get value */
118     miGET_VALUE,		/* Get value till white space */
119     miJUNK_LINE,		/* Ignore the rest of this folded line */
120     miNEWLINE,			/* Just found a LF .. maybe continuation */
121     miCHECK,			/* check against check_pointer */
122     MIME_NET_ASCII,		/* Translate from net ascii */
123     MIME_IGNORE			/* Ignore entire file */
124     /* TRANSPARENT and IGNORE are defined as stg else in _WINDOWS */
125 } MIME_state;
126 
127 #define VALUE_SIZE 5120		/* @@@@@@@ Arbitrary? */
128 struct _HTStream {
129     const HTStreamClass *isa;
130 
131     BOOL net_ascii;		/* Is input net ascii? */
132     MIME_state state;		/* current state */
133     MIME_state if_ok;		/* got this state if match */
134     MIME_state field;		/* remember which field */
135     MIME_state fold_state;	/* state on a fold */
136     BOOL head_only;		/* only parsing header */
137     BOOL pickup_redirection;	/* parsing for location */
138     BOOL no_streamstack;	/* use sink directly */
139     const char *check_pointer;	/* checking input */
140 
141     char *value_pointer;	/* storing values */
142     char value[VALUE_SIZE];
143 
144     HTParentAnchor *anchor;	/* Given on creation */
145     HTStream *sink;		/* Given on creation */
146 
147     char *boundary;		/* For multipart */
148     char *set_cookie;		/* Set-Cookie */
149     char *set_cookie2;		/* Set-Cookie2 */
150     char *location;		/* Location */
151 
152     char *refresh_url;		/* "Refresh:" URL */
153 
154     HTFormat c_t_encoding;	/* Content-Transfer-Encoding */
155     char *compression_encoding;
156 
157     BOOL chunked_encoding;	/* Transfer-Encoding: chunked */
158     long chunked_size;		/* ...counter for "chunked" */
159 
160     HTFormat format;		/* Content-Type */
161     HTStream *target;		/* While writing out */
162     HTStreamClass targetClass;
163 
164     HTAtom *targetRep;		/* Converting into? */
165 };
166 
167 /*
168  *  This function is for trimming off any paired
169  *  open- and close-double quotes from header values.
170  *  It does not parse the string for embedded quotes,
171  *  and will not modify the string unless both the
172  *  first and last characters are double-quotes. - FM
173  */
HTMIME_TrimDoubleQuotes(char * value)174 void HTMIME_TrimDoubleQuotes(char *value)
175 {
176     size_t i;
177     char *cp = value;
178 
179     if (isEmpty(cp) || *cp != '"')
180 	return;
181 
182     i = strlen(cp);
183     if (cp[(i - 1)] != '"')
184 	return;
185     else
186 	cp[(i - 1)] = '\0';
187 
188     for (i = 0; value[i]; i++)
189 	value[i] = cp[(i + 1)];
190 }
191 
192 /*
193  * Check if the token from "Content-Encoding" corresponds to a compression
194  * type.
195  */
content_is_compressed(HTStream * me)196 static BOOL content_is_compressed(HTStream *me)
197 {
198     char *encoding = me->anchor->content_encoding;
199     BOOL result = (BOOL) (HTEncodingToCompressType(encoding) != cftNone);
200 
201     CTRACE((tfp, "content is%s compressed\n", result ? "" : " NOT"));
202     return result;
203 }
204 
205 /*
206  * Strip quotes from a refresh-URL.
207  */
dequote(char * url)208 static void dequote(char *url)
209 {
210     size_t len;
211 
212     len = strlen(url);
213     if (*url == '\'' && len > 1 && url[len - 1] == url[0]) {
214 	url[len - 1] = '\0';
215 	while ((url[0] = url[1]) != '\0') {
216 	    ++url;
217 	}
218     }
219 }
220 
221 /*
222  * Strip off any compression-suffix from the address and check if the result
223  * looks like one of the presentable suffixes.  If so, return the corresponding
224  * MIME type.
225  */
UncompressedContentType(HTStream * me,CompressFileType method)226 static const char *UncompressedContentType(HTStream *me, CompressFileType method)
227 {
228     const char *result = 0;
229     char *address = me->anchor->address;
230     const char *expected = HTCompressTypeToSuffix(method);
231     const char *actual = strrchr(address, '.');
232 
233     /*
234      * We have to ensure the suffix is consistent, to use HTFileFormat().
235      */
236     if (actual != 0 && !strcasecomp(actual, expected)) {
237 	HTFormat format;
238 	HTAtom *pencoding = 0;
239 	const char *description = 0;
240 
241 	format = HTFileFormat(address, &pencoding, &description);
242 	result = HTAtom_name(format);
243     }
244 
245     return result;
246 }
247 
pumpData(HTStream * me)248 static int pumpData(HTStream *me)
249 {
250     CompressFileType method;
251     const char *new_encoding;
252     const char *new_content;
253 
254     CTRACE((tfp, "Begin pumpData\n"));
255     /*
256      * If the content-type says it is compressed, and there is no
257      * content-encoding, check further and see if the address (omitting the
258      * suffix for a compressed type) looks like a type we can present.  If so,
259      * rearrange things so we'll present the StreamStack code with the
260      * presentable type, already marked as compressed.
261      */
262     CTRACE((tfp, "...address{%s}\n", me->anchor->address));
263     method = HTContentTypeToCompressType(me->anchor->content_type_params);
264     if ((method != cftNone)
265 	&& isEmpty(me->anchor->content_encoding)
266 	&& (new_content = UncompressedContentType(me, method)) != 0) {
267 
268 	new_encoding = HTCompressTypeToEncoding(method);
269 	CTRACE((tfp, "reinterpreting as content-type:%s, encoding:%s\n",
270 		new_content, new_encoding));
271 
272 	StrAllocCopy(me->anchor->content_encoding, new_encoding);
273 	FREE(me->compression_encoding);
274 	StrAllocCopy(me->compression_encoding, new_encoding);
275 
276 	LYStrNCpy(me->value, new_content, VALUE_SIZE - 1);
277 	StrAllocCopy(me->anchor->content_type_params, me->value);
278 	me->format = HTAtom_for(me->value);
279     }
280 
281     if (strchr(HTAtom_name(me->format), ';') != NULL) {
282 	char *cp = NULL, *cp1, *cp2, *cp3 = NULL, *cp4;
283 
284 	CTRACE((tfp, "HTMIME: Extended MIME Content-Type is %s\n",
285 		HTAtom_name(me->format)));
286 	StrAllocCopy(cp, HTAtom_name(me->format));
287 	/*
288 	 * Note that the Content-Type value was converted
289 	 * to lower case when we loaded into me->format,
290 	 * but there may have been a mixed or upper-case
291 	 * atom, so we'll force lower-casing again.  We
292 	 * also stripped spaces and double-quotes, but
293 	 * we'll make sure they're still gone from any
294 	 * charset parameter we check.  - FM
295 	 */
296 	LYLowerCase(cp);
297 	if ((cp1 = strchr(cp, ';')) != NULL) {
298 	    BOOL chartrans_ok = NO;
299 
300 	    if ((cp2 = strstr(cp1, "charset")) != NULL) {
301 		int chndl;
302 
303 		cp2 += 7;
304 		while (*cp2 == ' ' || *cp2 == '=' || *cp2 == '"')
305 		    cp2++;
306 		StrAllocCopy(cp3, cp2);		/* copy to mutilate more */
307 		for (cp4 = cp3; (*cp4 != '\0' && *cp4 != '"' &&
308 				 *cp4 != ';' && *cp4 != ':' &&
309 				 !WHITE(*cp4)); cp4++) ;	/* do nothing */
310 		*cp4 = '\0';
311 		cp4 = cp3;
312 		chndl = UCGetLYhndl_byMIME(cp3);
313 		if (UCCanTranslateFromTo(chndl,
314 					 current_char_set)) {
315 		    chartrans_ok = YES;
316 		    *cp1 = '\0';
317 		    me->format = HTAtom_for(cp);
318 		    StrAllocCopy(me->anchor->charset, cp4);
319 		    HTAnchor_setUCInfoStage(me->anchor, chndl,
320 					    UCT_STAGE_MIME,
321 					    UCT_SETBY_MIME);
322 		} else if (chndl < 0) {		/* got something but we don't
323 						   recognize it */
324 		    chndl = UCLYhndl_for_unrec;
325 		    if (chndl < 0)
326 			/*
327 			 * UCLYhndl_for_unrec not defined :-( fallback to
328 			 * UCLYhndl_for_unspec which always valid.
329 			 */
330 			chndl = UCLYhndl_for_unspec;	/* always >= 0 */
331 		    if (UCCanTranslateFromTo(chndl,
332 					     current_char_set)) {
333 			chartrans_ok = YES;
334 			*cp1 = '\0';
335 			me->format = HTAtom_for(cp);
336 			HTAnchor_setUCInfoStage(me->anchor, chndl,
337 						UCT_STAGE_MIME,
338 						UCT_SETBY_DEFAULT);
339 		    }
340 		} else {
341 		    /*
342 		     * Something like 'big5' - we cannot translate it, but
343 		     * the user may still be able to navigate the links.
344 		     */
345 		    *cp1 = '\0';
346 		    me->format = HTAtom_for(cp);
347 		    StrAllocCopy(me->anchor->charset, cp4);
348 		    HTAnchor_setUCInfoStage(me->anchor, chndl,
349 					    UCT_STAGE_MIME,
350 					    UCT_SETBY_MIME);
351 		}
352 		if (chartrans_ok) {
353 		    LYUCcharset *p_in =
354 		    HTAnchor_getUCInfoStage(me->anchor,
355 					    UCT_STAGE_MIME);
356 		    LYUCcharset *p_out =
357 		    HTAnchor_setUCInfoStage(me->anchor,
358 					    current_char_set,
359 					    UCT_STAGE_HTEXT,
360 					    UCT_SETBY_DEFAULT);
361 
362 		    if (!p_out)
363 			/*
364 			 * Try again.
365 			 */
366 			p_out =
367 			    HTAnchor_getUCInfoStage(me->anchor,
368 						    UCT_STAGE_HTEXT);
369 
370 		    if (!strcmp(p_in->MIMEname,
371 				"x-transparent")) {
372 			HTPassEightBitRaw = TRUE;
373 			HTAnchor_setUCInfoStage(me->anchor,
374 						HTAnchor_getUCLYhndl(me->anchor,
375 								     UCT_STAGE_HTEXT),
376 						UCT_STAGE_MIME,
377 						UCT_SETBY_DEFAULT);
378 		    }
379 		    if (!strcmp(p_out->MIMEname,
380 				"x-transparent")) {
381 			HTPassEightBitRaw = TRUE;
382 			HTAnchor_setUCInfoStage(me->anchor,
383 						HTAnchor_getUCLYhndl(me->anchor,
384 								     UCT_STAGE_MIME),
385 						UCT_STAGE_HTEXT,
386 						UCT_SETBY_DEFAULT);
387 		    }
388 		    if ((p_in->enc != UCT_ENC_CJK)
389 #ifdef EXP_JAPANESEUTF8_SUPPORT
390 			&& ((p_in->enc != UCT_ENC_UTF8)
391 			    || (p_out->enc != UCT_ENC_CJK))
392 #endif
393 			) {
394 			HTCJK = NOCJK;
395 			if (!(p_in->codepoints &
396 			      UCT_CP_SUBSETOF_LAT1) &&
397 			    chndl == current_char_set) {
398 			    HTPassEightBitRaw = TRUE;
399 			}
400 		    } else if (p_out->enc == UCT_ENC_CJK) {
401 			Set_HTCJK(p_in->MIMEname, p_out->MIMEname);
402 		    }
403 		} else {
404 		    /*
405 		     * Cannot translate.  If according to some heuristic the
406 		     * given charset and the current display character both are
407 		     * likely to be like ISO-8859 in structure, pretend we have
408 		     * some kind of match.
409 		     */
410 		    BOOL given_is_8859 =
411 		    (BOOL) (!StrNCmp(cp4, "iso-8859-", 9) &&
412 			    isdigit(UCH(cp4[9])));
413 		    BOOL given_is_8859like =
414 		    (BOOL) (given_is_8859 ||
415 			    !StrNCmp(cp4, "windows-", 8) ||
416 			    !StrNCmp(cp4, "cp12", 4) ||
417 			    !StrNCmp(cp4, "cp-12", 5));
418 		    BOOL given_and_display_8859like =
419 		    (BOOL) (given_is_8859like &&
420 			    (strstr(LYchar_set_names[current_char_set],
421 				    "ISO-8859") ||
422 			     strstr(LYchar_set_names[current_char_set],
423 				    "windows-")));
424 
425 		    if (given_and_display_8859like) {
426 			*cp1 = '\0';
427 			me->format = HTAtom_for(cp);
428 		    }
429 		    if (given_is_8859) {
430 			cp1 = &cp4[10];
431 			while (*cp1 &&
432 			       isdigit(UCH(*cp1)))
433 			    cp1++;
434 			*cp1 = '\0';
435 		    }
436 		    if (given_and_display_8859like) {
437 			StrAllocCopy(me->anchor->charset, cp4);
438 			HTPassEightBitRaw = TRUE;
439 		    }
440 		    HTAlert(*cp4 ? cp4 : me->anchor->charset);
441 		}
442 		FREE(cp3);
443 	    } else {
444 		/*
445 		 * No charset parameter is present.  Ignore all other
446 		 * parameters, as we do when charset is present.  - FM
447 		 */
448 		*cp1 = '\0';
449 		me->format = HTAtom_for(cp);
450 	    }
451 	}
452 	FREE(cp);
453     }
454     /*
455      * If we have an Expires header and haven't already set the no_cache
456      * element for the anchor, check if we should set it based on that header.
457      * - FM
458      */
459     if (me->anchor->no_cache == FALSE &&
460 	me->anchor->expires != NULL) {
461 	if (!strcmp(me->anchor->expires, "0")) {
462 	    /*
463 	     * The value is zero, which we treat as an absolute no-cache
464 	     * directive.  - FM
465 	     */
466 	    me->anchor->no_cache = TRUE;
467 	} else if (me->anchor->date != NULL) {
468 	    /*
469 	     * We have a Date header, so check if the value is less than or
470 	     * equal to that.  - FM
471 	     */
472 	    if (LYmktime(me->anchor->expires, TRUE) <=
473 		LYmktime(me->anchor->date, TRUE)) {
474 		me->anchor->no_cache = TRUE;
475 	    }
476 	} else if (LYmktime(me->anchor->expires, FALSE) == 0) {
477 	    /*
478 	     * We don't have a Date header, and the value is in past for us.  -
479 	     * FM
480 	     */
481 	    me->anchor->no_cache = TRUE;
482 	}
483     }
484     StrAllocCopy(me->anchor->content_type,
485 		 HTAtom_name(me->format));
486 
487     if (me->set_cookie != NULL || me->set_cookie2 != NULL) {
488 	LYSetCookie(me->set_cookie,
489 		    me->set_cookie2,
490 		    me->anchor->address);
491 	FREE(me->set_cookie);
492 	FREE(me->set_cookie2);
493     }
494     if (me->pickup_redirection) {
495 	if (me->location && *me->location) {
496 	    redirecting_url = me->location;
497 	    me->location = NULL;
498 	    if (me->targetRep != WWW_DEBUG || me->sink)
499 		me->head_only = YES;
500 
501 	} else {
502 	    permanent_redirection = FALSE;
503 	    if (me->location) {
504 		CTRACE((tfp, "HTTP: 'Location:' is zero-length!\n"));
505 		HTAlert(REDIRECTION_WITH_BAD_LOCATION);
506 	    }
507 	    CTRACE((tfp, "HTTP: Failed to pick up location.\n"));
508 	    if (me->location) {
509 		FREE(me->location);
510 	    } else {
511 		HTAlert(REDIRECTION_WITH_NO_LOCATION);
512 	    }
513 	}
514     }
515     CTRACE((tfp, "...pumpData finished reading header\n"));
516     if (me->head_only) {
517 	/* We are done! - kw */
518 	me->state = MIME_IGNORE;
519     } else {
520 
521 	if (me->no_streamstack) {
522 	    me->target = me->sink;
523 	} else {
524 	    if (!me->compression_encoding) {
525 		CTRACE((tfp,
526 			"HTMIME: MIME Content-Type is '%s', converting to '%s'\n",
527 			HTAtom_name(me->format), HTAtom_name(me->targetRep)));
528 	    } else {
529 		/*
530 		 * Change the format to that for "www/compressed" and set up a
531 		 * stream to deal with it.  - FM
532 		 */
533 		CTRACE((tfp, "HTMIME: MIME Content-Type is '%s',\n",
534 			HTAtom_name(me->format)));
535 		me->format = HTAtom_for("www/compressed");
536 		CTRACE((tfp, "        Treating as '%s'.  Converting to '%s'\n",
537 			HTAtom_name(me->format), HTAtom_name(me->targetRep)));
538 		FREE(me->compression_encoding);
539 	    }
540 	    me->target = HTStreamStack(me->format, me->targetRep,
541 				       me->sink, me->anchor);
542 	    if (!me->target) {
543 		CTRACE((tfp, "HTMIME: Can't translate! ** \n"));
544 		me->target = me->sink;	/* Cheat */
545 	    }
546 	}
547 	if (me->target) {
548 	    me->targetClass = *me->target->isa;
549 	    /*
550 	     * Pump rest of data right through, according to the transfer encoding.
551 	     */
552 	    me->state = (me->chunked_encoding
553 			 ? MIME_CHUNKED
554 			 : MIME_TRANSPARENT);
555 	} else {
556 	    me->state = MIME_IGNORE;	/* What else to do? */
557 	}
558 	if (me->refresh_url != NULL && !content_is_compressed(me)) {
559 	    char *url = NULL;
560 	    char *num = NULL;
561 	    char *txt = NULL;
562 	    const char *base = "";	/* FIXME: refresh_url may be relative to doc */
563 
564 	    LYParseRefreshURL(me->refresh_url, &num, &url);
565 	    if (url != NULL && me->format == WWW_HTML) {
566 		CTRACE((tfp,
567 			"Formatting refresh-url as first line of result\n"));
568 		HTSprintf0(&txt, gettext("Refresh: "));
569 		HTSprintf(&txt, gettext("%s seconds "), num);
570 		dequote(url);
571 		HTSprintf(&txt, "<a href=\"%s%s\">%s</a><br>", base, url, url);
572 		CTRACE((tfp, "URL %s%s\n", base, url));
573 		(me->isa->put_string) (me, txt);
574 		free(txt);
575 	    }
576 	    FREE(num);
577 	    FREE(url);
578 	}
579     }
580     CTRACE((tfp, "...end of pumpData, copied %"
581 	    PRI_off_t " vs %"
582 	    PRI_off_t "\n",
583 	    me->anchor->actual_length,
584 	    me->anchor->content_length));
585     me->anchor->actual_length = 0;
586     return HT_OK;
587 }
588 
dispatchField(HTStream * me)589 static int dispatchField(HTStream *me)
590 {
591     int i, j;
592     char *cp;
593 
594     *me->value_pointer = '\0';
595 
596     cp = me->value_pointer;
597     while ((cp > me->value) && *(--cp) == ' ')	/* S/390 -- gil -- 0146 */
598 	/*
599 	 * Trim trailing spaces.
600 	 */
601 	*cp = '\0';
602 
603     switch (me->field) {
604     case miACCEPT_RANGES:
605 	HTMIME_TrimDoubleQuotes(me->value);
606 	CTRACE((tfp, "HTMIME: PICKED UP Accept-Ranges: '%s'\n",
607 		me->value));
608 	break;
609     case miAGE:
610 	HTMIME_TrimDoubleQuotes(me->value);
611 	CTRACE((tfp, "HTMIME: PICKED UP Age: '%s'\n",
612 		me->value));
613 	break;
614     case miALLOW:
615 	HTMIME_TrimDoubleQuotes(me->value);
616 	CTRACE((tfp, "HTMIME: PICKED UP Allow: '%s'\n",
617 		me->value));
618 	break;
619     case miALTERNATES:
620 	HTMIME_TrimDoubleQuotes(me->value);
621 	CTRACE((tfp, "HTMIME: PICKED UP Alternates: '%s'\n",
622 		me->value));
623 	break;
624     case miCACHE_CONTROL:
625 	HTMIME_TrimDoubleQuotes(me->value);
626 	CTRACE((tfp, "HTMIME: PICKED UP Cache-Control: '%s'\n",
627 		me->value));
628 	if (me->value[0] == '\0')
629 	    break;
630 	/*
631 	 * Convert to lowercase and indicate in anchor.  - FM
632 	 */
633 	LYLowerCase(me->value);
634 	StrAllocCopy(me->anchor->cache_control, me->value);
635 	/*
636 	 * Check whether to set no_cache for the anchor.  - FM
637 	 */
638 	{
639 	    char *cp1, *cp0 = me->value;
640 
641 	    while ((cp1 = strstr(cp0, "no-cache")) != NULL) {
642 		cp1 += 8;
643 		while (*cp1 != '\0' && WHITE(*cp1))
644 		    cp1++;
645 		if (*cp1 == '\0' || *cp1 == ';') {
646 		    me->anchor->no_cache = TRUE;
647 		    break;
648 		}
649 		cp0 = cp1;
650 	    }
651 	    if (me->anchor->no_cache == TRUE)
652 		break;
653 	    cp0 = me->value;
654 	    while ((cp1 = strstr(cp0, "max-age")) != NULL) {
655 		cp1 += 7;
656 		while (*cp1 != '\0' && WHITE(*cp1))
657 		    cp1++;
658 		if (*cp1 == '=') {
659 		    cp1++;
660 		    while (*cp1 != '\0' && WHITE(*cp1))
661 			cp1++;
662 		    if (isdigit(UCH(*cp1))) {
663 			cp0 = cp1;
664 			while (isdigit(UCH(*cp1)))
665 			    cp1++;
666 			if (*cp0 == '0' && cp1 == (cp0 + 1)) {
667 			    me->anchor->no_cache = TRUE;
668 			    break;
669 			}
670 		    }
671 		}
672 		cp0 = cp1;
673 	    }
674 	}
675 	break;
676     case miCOOKIE:
677 	HTMIME_TrimDoubleQuotes(me->value);
678 	CTRACE((tfp, "HTMIME: PICKED UP Cookie: '%s'\n",
679 		me->value));
680 	break;
681     case miCONNECTION:
682 	HTMIME_TrimDoubleQuotes(me->value);
683 	CTRACE((tfp, "HTMIME: PICKED UP Connection: '%s'\n",
684 		me->value));
685 	break;
686     case miCONTENT_BASE:
687 	HTMIME_TrimDoubleQuotes(me->value);
688 	CTRACE((tfp, "HTMIME: PICKED UP Content-Base: '%s'\n",
689 		me->value));
690 	if (me->value[0] == '\0')
691 	    break;
692 	/*
693 	 * Indicate in anchor.  - FM
694 	 */
695 	StrAllocCopy(me->anchor->content_base, me->value);
696 	break;
697     case miCONTENT_DISPOSITION:
698 	HTMIME_TrimDoubleQuotes(me->value);
699 	CTRACE((tfp, "HTMIME: PICKED UP Content-Disposition: '%s'\n",
700 		me->value));
701 	if (me->value[0] == '\0')
702 	    break;
703 	/*
704 	 * Indicate in anchor.  - FM
705 	 */
706 	StrAllocCopy(me->anchor->content_disposition, me->value);
707 	/*
708 	 * It's not clear yet from existing RFCs and IDs whether we should be
709 	 * looking for file;, attachment;, and/or inline; before the
710 	 * filename=value, so we'll just search for "filename" followed by '='
711 	 * and just hope we get the intended value.  It is purely a suggested
712 	 * name, anyway.  - FM
713 	 */
714 	cp = me->anchor->content_disposition;
715 	while (*cp != '\0' && strncasecomp(cp, "filename", 8))
716 	    cp++;
717 	if (*cp == '\0')
718 	    break;
719 	cp += 8;
720 	while ((*cp != '\0') && (WHITE(*cp) || *cp == '='))
721 	    cp++;
722 	if (*cp == '\0')
723 	    break;
724 	while (*cp != '\0' && WHITE(*cp))
725 	    cp++;
726 	if (*cp == '\0')
727 	    break;
728 	StrAllocCopy(me->anchor->SugFname, cp);
729 	if (*me->anchor->SugFname == '"') {
730 	    if ((cp = strchr((me->anchor->SugFname + 1),
731 			     '"')) != NULL) {
732 		*(cp + 1) = '\0';
733 		HTMIME_TrimDoubleQuotes(me->anchor->SugFname);
734 	    } else {
735 		FREE(me->anchor->SugFname);
736 		break;
737 	    }
738 	}
739 	cp = me->anchor->SugFname;
740 	while (*cp != '\0' && !WHITE(*cp))
741 	    cp++;
742 	*cp = '\0';
743 	if (*me->anchor->SugFname == '\0')
744 	    FREE(me->anchor->SugFname);
745 	break;
746     case miCONTENT_ENCODING:
747 	HTMIME_TrimDoubleQuotes(me->value);
748 	CTRACE((tfp, "HTMIME: PICKED UP Content-Encoding: '%s'\n",
749 		me->value));
750 	if (me->value[0] == '\0' ||
751 	    !strcasecomp(me->value, "identity"))
752 	    break;
753 	/*
754 	 * Convert to lowercase and indicate in anchor.  - FM
755 	 */
756 	LYLowerCase(me->value);
757 	StrAllocCopy(me->anchor->content_encoding, me->value);
758 	FREE(me->compression_encoding);
759 	if (content_is_compressed(me)) {
760 	    /*
761 	     * Save it to use as a flag for setting up a "www/compressed"
762 	     * target.  - FM
763 	     */
764 	    StrAllocCopy(me->compression_encoding, me->value);
765 	} else {
766 	    /*
767 	     * Some server indicated "8bit", "7bit" or "binary"
768 	     * inappropriately.  We'll ignore it.  - FM
769 	     */
770 	    CTRACE((tfp, "                Ignoring it!\n"));
771 	}
772 	break;
773     case miCONTENT_FEATURES:
774 	HTMIME_TrimDoubleQuotes(me->value);
775 	CTRACE((tfp, "HTMIME: PICKED UP Content-Features: '%s'\n",
776 		me->value));
777 	break;
778     case miCONTENT_LANGUAGE:
779 	HTMIME_TrimDoubleQuotes(me->value);
780 	CTRACE((tfp, "HTMIME: PICKED UP Content-Language: '%s'\n",
781 		me->value));
782 	if (me->value[0] == '\0')
783 	    break;
784 	/*
785 	 * Convert to lowercase and indicate in anchor.  - FM
786 	 */
787 	LYLowerCase(me->value);
788 	StrAllocCopy(me->anchor->content_language, me->value);
789 	break;
790     case miCONTENT_LENGTH:
791 	HTMIME_TrimDoubleQuotes(me->value);
792 	CTRACE((tfp, "HTMIME: PICKED UP Content-Length: '%s'\n",
793 		me->value));
794 	if (me->value[0] == '\0')
795 	    break;
796 	/*
797 	 * Convert to integer and indicate in anchor.  - FM
798 	 */
799 	me->anchor->content_length = LYatoll(me->value);
800 	if (me->anchor->content_length < 0)
801 	    me->anchor->content_length = 0;
802 	CTRACE((tfp, "        Converted to integer: '%" PRI_off_t "'\n",
803 		me->anchor->content_length));
804 	break;
805     case miCONTENT_LOCATION:
806 	HTMIME_TrimDoubleQuotes(me->value);
807 	CTRACE((tfp, "HTMIME: PICKED UP Content-Location: '%s'\n",
808 		me->value));
809 	if (me->value[0] == '\0')
810 	    break;
811 	/*
812 	 * Indicate in anchor.  - FM
813 	 */
814 	StrAllocCopy(me->anchor->content_location, me->value);
815 	break;
816     case miCONTENT_MD5:
817 	HTMIME_TrimDoubleQuotes(me->value);
818 	CTRACE((tfp, "HTMIME: PICKED UP Content-MD5: '%s'\n",
819 		me->value));
820 	if (me->value[0] == '\0')
821 	    break;
822 	/*
823 	 * Indicate in anchor.  - FM
824 	 */
825 	StrAllocCopy(me->anchor->content_md5, me->value);
826 	break;
827     case miCONTENT_RANGE:
828 	HTMIME_TrimDoubleQuotes(me->value);
829 	CTRACE((tfp, "HTMIME: PICKED UP Content-Range: '%s'\n",
830 		me->value));
831 	break;
832     case miCONTENT_TRANSFER_ENCODING:
833 	HTMIME_TrimDoubleQuotes(me->value);
834 	CTRACE((tfp, "HTMIME: PICKED UP Content-Transfer-Encoding: '%s'\n",
835 		me->value));
836 	if (me->value[0] == '\0')
837 	    break;
838 	/*
839 	 * Force the Content-Transfer-Encoding value to all lower case.  - FM
840 	 */
841 	LYLowerCase(me->value);
842 	me->c_t_encoding = HTAtom_for(me->value);
843 	break;
844     case miCONTENT_TYPE:
845 	HTMIME_TrimDoubleQuotes(me->value);
846 	CTRACE((tfp, "HTMIME: PICKED UP Content-Type: '%s'\n",
847 		me->value));
848 	if (me->value[0] == '\0')
849 	    break;
850 	/*
851 	 * Force the Content-Type value to all lower case and strip spaces and
852 	 * double-quotes.  - FM
853 	 */
854 	for (i = 0, j = 0; me->value[i]; i++) {
855 	    if (me->value[i] != ' ' && me->value[i] != '"') {
856 		me->value[j++] = (char) TOLOWER(me->value[i]);
857 	    }
858 	}
859 	me->value[j] = '\0';
860 	me->format = HTAtom_for(me->value);
861 	StrAllocCopy(me->anchor->content_type_params, me->value);
862 	break;
863     case miDATE:
864 	HTMIME_TrimDoubleQuotes(me->value);
865 	CTRACE((tfp, "HTMIME: PICKED UP Date: '%s'\n",
866 		me->value));
867 	if (me->value[0] == '\0')
868 	    break;
869 	/*
870 	 * Indicate in anchor.  - FM
871 	 */
872 	StrAllocCopy(me->anchor->date, me->value);
873 	break;
874     case miETAG:
875 	/* Do not trim double quotes:  an entity tag consists of an opaque
876 	 * quoted string, possibly prefixed by a weakness indicator.
877 	 */
878 	CTRACE((tfp, "HTMIME: PICKED UP ETag: %s\n",
879 		me->value));
880 	if (me->value[0] == '\0')
881 	    break;
882 	/*
883 	 * Indicate in anchor.  - FM
884 	 */
885 	StrAllocCopy(me->anchor->ETag, me->value);
886 	break;
887     case miEXPIRES:
888 	HTMIME_TrimDoubleQuotes(me->value);
889 	CTRACE((tfp, "HTMIME: PICKED UP Expires: '%s'\n",
890 		me->value));
891 	if (me->value[0] == '\0')
892 	    break;
893 	/*
894 	 * Indicate in anchor.  - FM
895 	 */
896 	StrAllocCopy(me->anchor->expires, me->value);
897 	break;
898     case miKEEP_ALIVE:
899 	HTMIME_TrimDoubleQuotes(me->value);
900 	CTRACE((tfp, "HTMIME: PICKED UP Keep-Alive: '%s'\n",
901 		me->value));
902 	break;
903     case miLAST_MODIFIED:
904 	HTMIME_TrimDoubleQuotes(me->value);
905 	CTRACE((tfp, "HTMIME: PICKED UP Last-Modified: '%s'\n",
906 		me->value));
907 	if (me->value[0] == '\0')
908 	    break;
909 	/*
910 	 * Indicate in anchor.  - FM
911 	 */
912 	StrAllocCopy(me->anchor->last_modified, me->value);
913 	break;
914     case miLINK:
915 	HTMIME_TrimDoubleQuotes(me->value);
916 	CTRACE((tfp, "HTMIME: PICKED UP Link: '%s'\n",
917 		me->value));
918 	break;
919     case miLOCATION:
920 	HTMIME_TrimDoubleQuotes(me->value);
921 	CTRACE((tfp, "HTMIME: PICKED UP Location: '%s'\n",
922 		me->value));
923 	if (me->pickup_redirection && !me->location) {
924 	    StrAllocCopy(me->location, me->value);
925 	} else {
926 	    CTRACE((tfp, "HTMIME: *** Ignoring Location!\n"));
927 	}
928 	break;
929     case miPRAGMA:
930 	HTMIME_TrimDoubleQuotes(me->value);
931 	CTRACE((tfp, "HTMIME: PICKED UP Pragma: '%s'\n",
932 		me->value));
933 	if (me->value[0] == '\0')
934 	    break;
935 	/*
936 	 * Check whether to set no_cache for the anchor.  - FM
937 	 */
938 	if (!strcmp(me->value, "no-cache"))
939 	    me->anchor->no_cache = TRUE;
940 	break;
941     case miPROXY_AUTHENTICATE:
942 	HTMIME_TrimDoubleQuotes(me->value);
943 	CTRACE((tfp, "HTMIME: PICKED UP Proxy-Authenticate: '%s'\n",
944 		me->value));
945 	break;
946     case miPUBLIC:
947 	HTMIME_TrimDoubleQuotes(me->value);
948 	CTRACE((tfp, "HTMIME: PICKED UP Public: '%s'\n",
949 		me->value));
950 	break;
951     case miREFRESH:		/* nonstandard: Netscape */
952 	HTMIME_TrimDoubleQuotes(me->value);
953 	CTRACE((tfp, "HTMIME: PICKED UP Refresh: '%s'\n",
954 		me->value));
955 	StrAllocCopy(me->refresh_url, me->value);
956 	break;
957     case miRETRY_AFTER:
958 	HTMIME_TrimDoubleQuotes(me->value);
959 	CTRACE((tfp, "HTMIME: PICKED UP Retry-After: '%s'\n",
960 		me->value));
961 	break;
962     case miSAFE:
963 	HTMIME_TrimDoubleQuotes(me->value);
964 	CTRACE((tfp, "HTMIME: PICKED UP Safe: '%s'\n",
965 		me->value));
966 	if (me->value[0] == '\0')
967 	    break;
968 	/*
969 	 * Indicate in anchor if "YES" or "TRUE".  - FM
970 	 */
971 	if (!strcasecomp(me->value, "YES") ||
972 	    !strcasecomp(me->value, "TRUE")) {
973 	    me->anchor->safe = TRUE;
974 	} else if (!strcasecomp(me->value, "NO") ||
975 		   !strcasecomp(me->value, "FALSE")) {
976 	    /*
977 	     * If server explicitly tells us that it has changed its mind,
978 	     * reset flag in anchor.  - kw
979 	     */
980 	    me->anchor->safe = FALSE;
981 	}
982 	break;
983     case miSERVER:
984 	HTMIME_TrimDoubleQuotes(me->value);
985 	CTRACE((tfp, "HTMIME: PICKED UP Server: '%s'\n",
986 		me->value));
987 	if (me->value[0] == '\0')
988 	    break;
989 	/*
990 	 * Indicate in anchor.  - FM
991 	 */
992 	StrAllocCopy(me->anchor->server, me->value);
993 	break;
994     case miSET_COOKIE1:
995 	HTMIME_TrimDoubleQuotes(me->value);
996 	CTRACE((tfp, "HTMIME: PICKED UP Set-Cookie: '%s'\n",
997 		me->value));
998 	if (me->set_cookie == NULL) {
999 	    StrAllocCopy(me->set_cookie, me->value);
1000 	} else {
1001 	    StrAllocCat(me->set_cookie, ", ");
1002 	    StrAllocCat(me->set_cookie, me->value);
1003 	}
1004 	break;
1005     case miSET_COOKIE2:
1006 	HTMIME_TrimDoubleQuotes(me->value);
1007 	CTRACE((tfp, "HTMIME: PICKED UP Set-Cookie2: '%s'\n",
1008 		me->value));
1009 	if (me->set_cookie2 == NULL) {
1010 	    StrAllocCopy(me->set_cookie2, me->value);
1011 	} else {
1012 	    StrAllocCat(me->set_cookie2, ", ");
1013 	    StrAllocCat(me->set_cookie2, me->value);
1014 	}
1015 	break;
1016     case miTITLE:
1017 	HTMIME_TrimDoubleQuotes(me->value);
1018 	CTRACE((tfp, "HTMIME: PICKED UP Title: '%s'\n",
1019 		me->value));
1020 	break;
1021     case miTRANSFER_ENCODING:
1022 	HTMIME_TrimDoubleQuotes(me->value);
1023 	CTRACE((tfp, "HTMIME: PICKED UP Transfer-Encoding: '%s'\n",
1024 		me->value));
1025 	if (!strcmp(me->value, "chunked"))
1026 	    me->chunked_encoding = YES;
1027 	break;
1028     case miUPGRADE:
1029 	HTMIME_TrimDoubleQuotes(me->value);
1030 	CTRACE((tfp, "HTMIME: PICKED UP Upgrade: '%s'\n",
1031 		me->value));
1032 	break;
1033     case miURI:
1034 	HTMIME_TrimDoubleQuotes(me->value);
1035 	CTRACE((tfp, "HTMIME: PICKED UP URI: '%s'\n",
1036 		me->value));
1037 	break;
1038     case miVARY:
1039 	HTMIME_TrimDoubleQuotes(me->value);
1040 	CTRACE((tfp, "HTMIME: PICKED UP Vary: '%s'\n",
1041 		me->value));
1042 	break;
1043     case miVIA:
1044 	HTMIME_TrimDoubleQuotes(me->value);
1045 	CTRACE((tfp, "HTMIME: PICKED UP Via: '%s'\n",
1046 		me->value));
1047 	break;
1048     case miWARNING:
1049 	HTMIME_TrimDoubleQuotes(me->value);
1050 	CTRACE((tfp, "HTMIME: PICKED UP Warning: '%s'\n",
1051 		me->value));
1052 	break;
1053     case miWWW_AUTHENTICATE:
1054 	HTMIME_TrimDoubleQuotes(me->value);
1055 	CTRACE((tfp, "HTMIME: PICKED UP WWW-Authenticate: '%s'\n",
1056 		me->value));
1057 	break;
1058     default:			/* Should never get here */
1059 	return HT_ERROR;
1060     }
1061     return HT_OK;
1062 }
1063 
1064 /*_________________________________________________________________________
1065  *
1066  *			A C T I O N	R O U T I N E S
1067  */
1068 
1069 /*	Character handling
1070  *	------------------
1071  *
1072  *	This is a FSM parser. It ignores field names it does not understand.
1073  *	Folded header fields are recognized.  Lines without a fieldname at
1074  *	the beginning (that are not folded continuation lines) are ignored
1075  *	as unknown field names.  Fields with empty values are not picked up.
1076  */
HTMIME_put_character(HTStream * me,int c)1077 static void HTMIME_put_character(HTStream *me, int c)
1078 {
1079     /* MUST BE FAST */
1080     switch (me->state) {
1081       begin_transparent:
1082     case MIME_TRANSPARENT:
1083 	me->anchor->actual_length += 1;
1084 	if (me->anchor->content_length == 0 ||
1085 	    (me->anchor->content_length >= me->anchor->actual_length)) {
1086 	    (me->targetClass.put_character) (me->target, c);
1087 	} else {
1088 	    (me->targetClass.put_character) (me->target, c);
1089 	}
1090 	return;
1091 
1092 	/* RFC-2616 describes chunked transfer coding */
1093     case mcCHUNKED_DATA:
1094 	(*me->targetClass.put_character) (me->target, c);
1095 	me->chunked_size--;
1096 	if (me->chunked_size <= 0)
1097 	    me->state = mcCHUNKED_DATA_CR;
1098 	return;
1099 
1100     case mcCHUNKED_DATA_CR:
1101 	me->state = mcCHUNKED_DATA_LF;
1102 	if (c == CR) {
1103 	    return;
1104 	}
1105 	/* FALLTHRU */
1106 
1107     case mcCHUNKED_DATA_LF:
1108 	me->state = MIME_CHUNKED;
1109 	if (c == LF) {
1110 	    return;
1111 	}
1112 
1113 	CTRACE((tfp, "HTIME_put_character expected LF in chunked data\n"));
1114 	me->state = MIME_TRANSPARENT;
1115 	goto begin_transparent;
1116 
1117 	/* FALLTHRU */
1118       begin_chunked:
1119     case MIME_CHUNKED:
1120 	me->chunked_size = 0;
1121 	me->state = mcCHUNKED_COUNT_DIGIT;
1122 
1123 	/* FALLTHRU */
1124     case mcCHUNKED_COUNT_DIGIT:
1125 	if (isxdigit(UCH(c))) {
1126 	    me->chunked_size <<= 4;
1127 	    if (isdigit(UCH(c)))
1128 		me->chunked_size += UCH(c) - '0';
1129 	    else
1130 		me->chunked_size += TOUPPER(UCH(c)) - 'A' + 10;
1131 	    return;
1132 	}
1133 	if (c == ';')
1134 	    me->state = mcCHUNKED_EXTENSION;
1135 
1136 	/* FALLTHRU */
1137     case mcCHUNKED_EXTENSION:
1138 	if (c != CR && c != LF) {
1139 	    return;
1140 	}
1141 	me->state = mcCHUNKED_COUNT_CR;
1142 
1143 	/* FALLTHRU */
1144     case mcCHUNKED_COUNT_CR:
1145 	me->state = mcCHUNKED_COUNT_LF;
1146 	if (c == CR) {
1147 	    return;
1148 	}
1149 
1150 	/* FALLTHRU */
1151     case mcCHUNKED_COUNT_LF:
1152 	me->state = ((me->chunked_size != 0)
1153 		     ? mcCHUNKED_DATA
1154 		     : MIME_CHUNKED);
1155 	if (c == LF) {
1156 	    return;
1157 	}
1158 	goto begin_chunked;
1159 
1160     default:
1161 	break;
1162     }
1163 
1164     /*
1165      * This slightly simple conversion just strips CR and turns LF to newline.
1166      * On unix LF is \n but on Mac \n is CR for example.  See NetToText for an
1167      * implementation which preserves single CR or LF.
1168      */
1169     if (me->net_ascii) {
1170 	/*
1171 	 * <sigh> This is evidence that at one time, this code supported
1172 	 * local character sets other than ASCII.  But there is so much
1173 	 * code in HTTP.c that depends on line_buffers having been
1174 	 * translated to local character set that I needed to put the
1175 	 * FROMASCII translation there, leaving this translation purely
1176 	 * destructive.  -- gil
1177 	 */
1178 	/* S/390 -- gil -- 0118 */
1179 #ifndef   NOT_ASCII
1180 	c = FROMASCII(c);
1181 #endif /* NOT_ASCII */
1182 	if (c == CR)
1183 	    return;
1184 	else if (c == LF)
1185 	    c = '\n';
1186     }
1187 
1188     switch (me->state) {
1189 
1190     case MIME_IGNORE:
1191 	return;
1192 
1193     case MIME_TRANSPARENT:	/* Not reached see above */
1194     case MIME_CHUNKED:
1195     case mcCHUNKED_COUNT_DIGIT:
1196     case mcCHUNKED_COUNT_CR:
1197     case mcCHUNKED_COUNT_LF:
1198     case mcCHUNKED_EXTENSION:
1199     case mcCHUNKED_DATA:
1200     case mcCHUNKED_DATA_CR:
1201     case mcCHUNKED_DATA_LF:
1202 	return;
1203 
1204     case MIME_NET_ASCII:
1205 	(*me->targetClass.put_character) (me->target, c);	/* MUST BE FAST */
1206 	return;
1207 
1208     case miNEWLINE:
1209 	if (c != '\n' && WHITE(c)) {	/* Folded line */
1210 	    me->state = me->fold_state;		/* pop state before newline */
1211 	    if (me->state == miGET_VALUE &&
1212 		me->value_pointer && me->value_pointer != me->value &&
1213 		!WHITE(*(me->value_pointer - 1))) {
1214 		c = ' ';
1215 		goto GET_VALUE;	/* will add space to value if it fits - kw */
1216 	    }
1217 	    break;
1218 	} else if (me->fold_state == miGET_VALUE) {
1219 	    /* Got a field, and now we know it's complete - so
1220 	     * act on it. - kw */
1221 	    dispatchField(me);
1222 	}
1223 	/* FALLTHRU */
1224 
1225     case miBEGINNING_OF_LINE:
1226 	me->net_ascii = YES;
1227 	switch (c) {
1228 	case 'a':
1229 	case 'A':
1230 	    me->state = miA;
1231 	    CTRACE((tfp, "HTMIME: Got 'A' at beginning of line, state now A\n"));
1232 	    break;
1233 
1234 	case 'c':
1235 	case 'C':
1236 	    me->state = miC;
1237 	    CTRACE((tfp, "HTMIME: Got 'C' at beginning of line, state now C\n"));
1238 	    break;
1239 
1240 	case 'd':
1241 	case 'D':
1242 	    me->check_pointer = "ate:";
1243 	    me->if_ok = miDATE;
1244 	    me->state = miCHECK;
1245 	    CTRACE((tfp,
1246 		    "HTMIME: Got 'D' at beginning of line, checking for 'ate:'\n"));
1247 	    break;
1248 
1249 	case 'e':
1250 	case 'E':
1251 	    me->state = miE;
1252 	    CTRACE((tfp, "HTMIME: Got 'E' at beginning of line, state now E\n"));
1253 	    break;
1254 
1255 	case 'k':
1256 	case 'K':
1257 	    me->check_pointer = "eep-alive:";
1258 	    me->if_ok = miKEEP_ALIVE;
1259 	    me->state = miCHECK;
1260 	    CTRACE((tfp,
1261 		    "HTMIME: Got 'K' at beginning of line, checking for 'eep-alive:'\n"));
1262 	    break;
1263 
1264 	case 'l':
1265 	case 'L':
1266 	    me->state = miL;
1267 	    CTRACE((tfp, "HTMIME: Got 'L' at beginning of line, state now L\n"));
1268 	    break;
1269 
1270 	case 'p':
1271 	case 'P':
1272 	    me->state = miP;
1273 	    CTRACE((tfp, "HTMIME: Got 'P' at beginning of line, state now P\n"));
1274 	    break;
1275 
1276 	case 'r':
1277 	case 'R':
1278 	    me->state = miR;
1279 	    CTRACE((tfp, "HTMIME: Got 'R' at beginning of line, state now R\n"));
1280 	    break;
1281 
1282 	case 's':
1283 	case 'S':
1284 	    me->state = miS;
1285 	    CTRACE((tfp, "HTMIME: Got 'S' at beginning of line, state now S\n"));
1286 	    break;
1287 
1288 	case 't':
1289 	case 'T':
1290 	    me->state = miT;
1291 	    CTRACE((tfp, "HTMIME: Got 'T' at beginning of line, state now T\n"));
1292 	    break;
1293 
1294 	case 'u':
1295 	case 'U':
1296 	    me->state = miU;
1297 	    CTRACE((tfp, "HTMIME: Got 'U' at beginning of line, state now U\n"));
1298 	    break;
1299 
1300 	case 'v':
1301 	case 'V':
1302 	    me->state = miV;
1303 	    CTRACE((tfp, "HTMIME: Got 'V' at beginning of line, state now V\n"));
1304 	    break;
1305 
1306 	case 'w':
1307 	case 'W':
1308 	    me->state = miW;
1309 	    CTRACE((tfp, "HTMIME: Got 'W' at beginning of line, state now W\n"));
1310 	    break;
1311 
1312 	case '\n':		/* Blank line: End of Header! */
1313 	    {
1314 		me->net_ascii = NO;
1315 		pumpData(me);
1316 	    }
1317 	    break;
1318 
1319 	default:
1320 	    goto bad_field_name;
1321 
1322 	}			/* switch on character */
1323 	break;
1324 
1325     case miA:			/* Check for 'c','g' or 'l' */
1326 	switch (c) {
1327 	case 'c':
1328 	case 'C':
1329 	    me->check_pointer = "cept-ranges:";
1330 	    me->if_ok = miACCEPT_RANGES;
1331 	    me->state = miCHECK;
1332 	    CTRACE((tfp,
1333 		    "HTMIME: Was A, found C, checking for 'cept-ranges:'\n"));
1334 	    break;
1335 
1336 	case 'g':
1337 	case 'G':
1338 	    me->check_pointer = "e:";
1339 	    me->if_ok = miAGE;
1340 	    me->state = miCHECK;
1341 	    CTRACE((tfp, "HTMIME: Was A, found G, checking for 'e:'\n"));
1342 	    break;
1343 
1344 	case 'l':
1345 	case 'L':
1346 	    me->state = miAL;
1347 	    CTRACE((tfp, "HTMIME: Was A, found L, state now AL'\n"));
1348 	    break;
1349 
1350 	default:
1351 	    CTRACE((tfp,
1352 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1353 		    c, "'g' or 'l'"));
1354 	    goto bad_field_name;
1355 
1356 	}			/* switch on character */
1357 	break;
1358 
1359     case miAL:			/* Check for 'l' or 't' */
1360 	switch (c) {
1361 	case 'l':
1362 	case 'L':
1363 	    me->check_pointer = "ow:";
1364 	    me->if_ok = miALLOW;
1365 	    me->state = miCHECK;
1366 	    CTRACE((tfp, "HTMIME: Was AL, found L, checking for 'ow:'\n"));
1367 	    break;
1368 
1369 	case 't':
1370 	case 'T':
1371 	    me->check_pointer = "ernates:";
1372 	    me->if_ok = miALTERNATES;
1373 	    me->state = miCHECK;
1374 	    CTRACE((tfp, "HTMIME: Was AL, found T, checking for 'ernates:'\n"));
1375 	    break;
1376 
1377 	default:
1378 	    CTRACE((tfp,
1379 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1380 		    c, "'l' or 't'"));
1381 	    goto bad_field_name;
1382 
1383 	}			/* switch on character */
1384 	break;
1385 
1386     case miC:			/* Check for 'a' or 'o' */
1387 	switch (c) {
1388 	case 'a':
1389 	case 'A':
1390 	    me->check_pointer = "che-control:";
1391 	    me->if_ok = miCACHE_CONTROL;
1392 	    me->state = miCHECK;
1393 	    CTRACE((tfp,
1394 		    "HTMIME: Was C, found A, checking for 'che-control:'\n"));
1395 	    break;
1396 
1397 	case 'o':
1398 	case 'O':
1399 	    me->state = miCO;
1400 	    CTRACE((tfp, "HTMIME: Was C, found O, state now CO'\n"));
1401 	    break;
1402 
1403 	default:
1404 	    CTRACE((tfp,
1405 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1406 		    c, "'a' or 'o'"));
1407 	    goto bad_field_name;
1408 
1409 	}			/* switch on character */
1410 	break;
1411 
1412     case miCO:			/* Check for 'n' or 'o' */
1413 	switch (c) {
1414 	case 'n':
1415 	case 'N':
1416 	    me->state = miCON;
1417 	    CTRACE((tfp, "HTMIME: Was CO, found N, state now CON\n"));
1418 	    break;
1419 
1420 	case 'o':
1421 	case 'O':
1422 	    me->check_pointer = "kie:";
1423 	    me->if_ok = miCOOKIE;
1424 	    me->state = miCHECK;
1425 	    CTRACE((tfp, "HTMIME: Was CO, found O, checking for 'kie:'\n"));
1426 	    break;
1427 
1428 	default:
1429 	    CTRACE((tfp,
1430 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1431 		    c, "'n' or 'o'"));
1432 	    goto bad_field_name;
1433 
1434 	}			/* switch on character */
1435 	break;
1436 
1437     case miCON:		/* Check for 'n' or 't' */
1438 	switch (c) {
1439 	case 'n':
1440 	case 'N':
1441 	    me->check_pointer = "ection:";
1442 	    me->if_ok = miCONNECTION;
1443 	    me->state = miCHECK;
1444 	    CTRACE((tfp, "HTMIME: Was CON, found N, checking for 'ection:'\n"));
1445 	    break;
1446 
1447 	case 't':
1448 	case 'T':
1449 	    me->check_pointer = "ent-";
1450 	    me->if_ok = miCONTENT_;
1451 	    me->state = miCHECK;
1452 	    CTRACE((tfp, "HTMIME: Was CON, found T, checking for 'ent-'\n"));
1453 	    break;
1454 
1455 	default:
1456 	    CTRACE((tfp,
1457 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1458 		    c, "'n' or 't'"));
1459 	    goto bad_field_name;
1460 
1461 	}			/* switch on character */
1462 	break;
1463 
1464     case miE:			/* Check for 't' or 'x' */
1465 	switch (c) {
1466 	case 't':
1467 	case 'T':
1468 	    me->check_pointer = "ag:";
1469 	    me->if_ok = miETAG;
1470 	    me->state = miCHECK;
1471 	    CTRACE((tfp, "HTMIME: Was E, found T, checking for 'ag:'\n"));
1472 	    break;
1473 
1474 	case 'x':
1475 	case 'X':
1476 	    me->check_pointer = "pires:";
1477 	    me->if_ok = miEXPIRES;
1478 	    me->state = miCHECK;
1479 	    CTRACE((tfp, "HTMIME: Was E, found X, checking for 'pires:'\n"));
1480 	    break;
1481 
1482 	default:
1483 	    CTRACE((tfp,
1484 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1485 		    c, "'t' or 'x'"));
1486 	    goto bad_field_name;
1487 
1488 	}			/* switch on character */
1489 	break;
1490 
1491     case miL:			/* Check for 'a', 'i' or 'o' */
1492 	switch (c) {
1493 	case 'a':
1494 	case 'A':
1495 	    me->check_pointer = "st-modified:";
1496 	    me->if_ok = miLAST_MODIFIED;
1497 	    me->state = miCHECK;
1498 	    CTRACE((tfp,
1499 		    "HTMIME: Was L, found A, checking for 'st-modified:'\n"));
1500 	    break;
1501 
1502 	case 'i':
1503 	case 'I':
1504 	    me->check_pointer = "nk:";
1505 	    me->if_ok = miLINK;
1506 	    me->state = miCHECK;
1507 	    CTRACE((tfp, "HTMIME: Was L, found I, checking for 'nk:'\n"));
1508 	    break;
1509 
1510 	case 'o':
1511 	case 'O':
1512 	    me->check_pointer = "cation:";
1513 	    me->if_ok = miLOCATION;
1514 	    me->state = miCHECK;
1515 	    CTRACE((tfp, "HTMIME: Was L, found O, checking for 'cation:'\n"));
1516 	    break;
1517 
1518 	default:
1519 	    CTRACE((tfp,
1520 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1521 		    c, "'a', 'i' or 'o'"));
1522 	    goto bad_field_name;
1523 
1524 	}			/* switch on character */
1525 	break;
1526 
1527     case miP:			/* Check for 'r' or 'u' */
1528 	switch (c) {
1529 	case 'r':
1530 	case 'R':
1531 	    me->state = miPR;
1532 	    CTRACE((tfp, "HTMIME: Was P, found R, state now PR'\n"));
1533 	    break;
1534 
1535 	case 'u':
1536 	case 'U':
1537 	    me->check_pointer = "blic:";
1538 	    me->if_ok = miPUBLIC;
1539 	    me->state = miCHECK;
1540 	    CTRACE((tfp, "HTMIME: Was P, found U, checking for 'blic:'\n"));
1541 	    break;
1542 
1543 	default:
1544 	    CTRACE((tfp,
1545 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1546 		    c, "'r' or 'u'"));
1547 	    goto bad_field_name;
1548 
1549 	}			/* switch on character */
1550 	break;
1551 
1552     case miPR:			/* Check for 'a' or 'o' */
1553 	switch (c) {
1554 	case 'a':
1555 	case 'A':
1556 	    me->check_pointer = "gma:";
1557 	    me->if_ok = miPRAGMA;
1558 	    me->state = miCHECK;
1559 	    CTRACE((tfp, "HTMIME: Was PR, found A, checking for 'gma'\n"));
1560 	    break;
1561 
1562 	case 'o':
1563 	case 'O':
1564 	    me->check_pointer = "xy-authenticate:";
1565 	    me->if_ok = miPROXY_AUTHENTICATE;
1566 	    me->state = miCHECK;
1567 	    CTRACE((tfp,
1568 		    "HTMIME: Was PR, found O, checking for 'xy-authenticate'\n"));
1569 	    break;
1570 
1571 	default:
1572 	    CTRACE((tfp,
1573 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1574 		    c, "'a' or 'o'"));
1575 	    goto bad_field_name;
1576 
1577 	}			/* switch on character */
1578 	break;
1579 
1580     case miR:			/* Check for 'e' */
1581 	switch (c) {
1582 	case 'e':
1583 	case 'E':
1584 	    me->state = miRE;
1585 	    CTRACE((tfp, "HTMIME: Was R, found E\n"));
1586 	    break;
1587 	default:
1588 	    CTRACE((tfp,
1589 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1590 		    c, "'e'"));
1591 	    goto bad_field_name;
1592 
1593 	}			/* switch on character */
1594 	break;
1595 
1596     case miRE:			/* Check for 'a' or 'o' */
1597 	switch (c) {
1598 	case 'f':
1599 	case 'F':		/* nonstandard: Netscape */
1600 	    me->check_pointer = "resh:";
1601 	    me->if_ok = miREFRESH;
1602 	    me->state = miCHECK;
1603 	    CTRACE((tfp, "HTMIME: Was RE, found F, checking for '%s'\n", me->check_pointer));
1604 	    break;
1605 
1606 	case 't':
1607 	case 'T':
1608 	    me->check_pointer = "ry-after:";
1609 	    me->if_ok = miRETRY_AFTER;
1610 	    me->state = miCHECK;
1611 	    CTRACE((tfp, "HTMIME: Was RE, found T, checking for '%s'\n", me->check_pointer));
1612 	    break;
1613 
1614 	default:
1615 	    CTRACE((tfp,
1616 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1617 		    c, "'f' or 't'"));
1618 	    goto bad_field_name;
1619 
1620 	}			/* switch on character */
1621 	break;
1622 
1623     case miS:			/* Check for 'a' or 'e' */
1624 	switch (c) {
1625 	case 'a':
1626 	case 'A':
1627 	    me->check_pointer = "fe:";
1628 	    me->if_ok = miSAFE;
1629 	    me->state = miCHECK;
1630 	    CTRACE((tfp, "HTMIME: Was S, found A, checking for 'fe:'\n"));
1631 	    break;
1632 
1633 	case 'e':
1634 	case 'E':
1635 	    me->state = miSE;
1636 	    CTRACE((tfp, "HTMIME: Was S, found E, state now SE'\n"));
1637 	    break;
1638 
1639 	default:
1640 	    CTRACE((tfp,
1641 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1642 		    c, "'a' or 'e'"));
1643 	    goto bad_field_name;
1644 
1645 	}			/* switch on character */
1646 	break;
1647 
1648     case miSE:			/* Check for 'r' or 't' */
1649 	switch (c) {
1650 	case 'r':
1651 	case 'R':
1652 	    me->check_pointer = "ver:";
1653 	    me->if_ok = miSERVER;
1654 	    me->state = miCHECK;
1655 	    CTRACE((tfp, "HTMIME: Was SE, found R, checking for 'ver'\n"));
1656 	    break;
1657 
1658 	case 't':
1659 	case 'T':
1660 	    me->check_pointer = "-cookie";
1661 	    me->if_ok = miSET_COOKIE;
1662 	    me->state = miCHECK;
1663 	    CTRACE((tfp, "HTMIME: Was SE, found T, checking for '-cookie'\n"));
1664 	    break;
1665 
1666 	default:
1667 	    CTRACE((tfp,
1668 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1669 		    c, "'r' or 't'"));
1670 	    goto bad_field_name;
1671 
1672 	}			/* switch on character */
1673 	break;
1674 
1675     case miSET_COOKIE:		/* Check for ':' or '2' */
1676 	switch (c) {
1677 	case ':':
1678 	    me->field = miSET_COOKIE1;	/* remember it */
1679 	    me->state = miSKIP_GET_VALUE;
1680 	    CTRACE((tfp, "HTMIME: Was SET_COOKIE, found :, processing\n"));
1681 	    break;
1682 
1683 	case '2':
1684 	    me->check_pointer = ":";
1685 	    me->if_ok = miSET_COOKIE2;
1686 	    me->state = miCHECK;
1687 	    CTRACE((tfp, "HTMIME: Was SET_COOKIE, found 2, checking for ':'\n"));
1688 	    break;
1689 
1690 	default:
1691 	    CTRACE((tfp,
1692 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1693 		    c, "':' or '2'"));
1694 	    goto bad_field_name;
1695 
1696 	}			/* switch on character */
1697 	break;
1698 
1699     case miT:			/* Check for 'i' or 'r' */
1700 	switch (c) {
1701 	case 'i':
1702 	case 'I':
1703 	    me->check_pointer = "tle:";
1704 	    me->if_ok = miTITLE;
1705 	    me->state = miCHECK;
1706 	    CTRACE((tfp, "HTMIME: Was T, found I, checking for 'tle:'\n"));
1707 	    break;
1708 
1709 	case 'r':
1710 	case 'R':
1711 	    me->check_pointer = "ansfer-encoding:";
1712 	    me->if_ok = miTRANSFER_ENCODING;
1713 	    me->state = miCHECK;
1714 	    CTRACE((tfp,
1715 		    "HTMIME: Was T, found R, checking for 'ansfer-encoding'\n"));
1716 	    break;
1717 
1718 	default:
1719 	    CTRACE((tfp,
1720 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1721 		    c, "'i' or 'r'"));
1722 	    goto bad_field_name;
1723 
1724 	}			/* switch on character */
1725 	break;
1726 
1727     case miU:			/* Check for 'p' or 'r' */
1728 	switch (c) {
1729 	case 'p':
1730 	case 'P':
1731 	    me->check_pointer = "grade:";
1732 	    me->if_ok = miUPGRADE;
1733 	    me->state = miCHECK;
1734 	    CTRACE((tfp, "HTMIME: Was U, found P, checking for 'grade:'\n"));
1735 	    break;
1736 
1737 	case 'r':
1738 	case 'R':
1739 	    me->check_pointer = "i:";
1740 	    me->if_ok = miURI;
1741 	    me->state = miCHECK;
1742 	    CTRACE((tfp, "HTMIME: Was U, found R, checking for 'i:'\n"));
1743 	    break;
1744 
1745 	default:
1746 	    CTRACE((tfp,
1747 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1748 		    c, "'p' or 'r'"));
1749 	    goto bad_field_name;
1750 
1751 	}			/* switch on character */
1752 	break;
1753 
1754     case miV:			/* Check for 'a' or 'i' */
1755 	switch (c) {
1756 	case 'a':
1757 	case 'A':
1758 	    me->check_pointer = "ry:";
1759 	    me->if_ok = miVARY;
1760 	    me->state = miCHECK;
1761 	    CTRACE((tfp, "HTMIME: Was V, found A, checking for 'ry:'\n"));
1762 	    break;
1763 
1764 	case 'i':
1765 	case 'I':
1766 	    me->check_pointer = "a:";
1767 	    me->if_ok = miVIA;
1768 	    me->state = miCHECK;
1769 	    CTRACE((tfp, "HTMIME: Was V, found I, checking for 'a:'\n"));
1770 	    break;
1771 
1772 	default:
1773 	    CTRACE((tfp,
1774 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1775 		    c, "'a' or 'i'"));
1776 	    goto bad_field_name;
1777 
1778 	}			/* switch on character */
1779 	break;
1780 
1781     case miW:			/* Check for 'a' or 'w' */
1782 	switch (c) {
1783 	case 'a':
1784 	case 'A':
1785 	    me->check_pointer = "rning:";
1786 	    me->if_ok = miWARNING;
1787 	    me->state = miCHECK;
1788 	    CTRACE((tfp, "HTMIME: Was W, found A, checking for 'rning:'\n"));
1789 	    break;
1790 
1791 	case 'w':
1792 	case 'W':
1793 	    me->check_pointer = "w-authenticate:";
1794 	    me->if_ok = miWWW_AUTHENTICATE;
1795 	    me->state = miCHECK;
1796 	    CTRACE((tfp,
1797 		    "HTMIME: Was W, found W, checking for 'w-authenticate:'\n"));
1798 	    break;
1799 
1800 	default:
1801 	    CTRACE((tfp,
1802 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1803 		    c, "'a' or 'w'"));
1804 	    goto bad_field_name;
1805 
1806 	}			/* switch on character */
1807 	break;
1808 
1809     case miCHECK:		/* Check against string */
1810 	if (TOLOWER(c) == *(me->check_pointer)++) {
1811 	    if (!*me->check_pointer)
1812 		me->state = me->if_ok;
1813 	} else {		/* Error */
1814 	    CTRACE((tfp,
1815 		    "HTMIME: Bad character `%c' found where `%s' expected\n",
1816 		    c, me->check_pointer - 1));
1817 	    goto bad_field_name;
1818 	}
1819 	break;
1820 
1821     case miCONTENT_:
1822 	CTRACE((tfp, "HTMIME: in case CONTENT_\n"));
1823 
1824 	switch (c) {
1825 	case 'b':
1826 	case 'B':
1827 	    me->check_pointer = "ase:";
1828 	    me->if_ok = miCONTENT_BASE;
1829 	    me->state = miCHECK;
1830 	    CTRACE((tfp,
1831 		    "HTMIME: Was CONTENT_, found B, checking for 'ase:'\n"));
1832 	    break;
1833 
1834 	case 'd':
1835 	case 'D':
1836 	    me->check_pointer = "isposition:";
1837 	    me->if_ok = miCONTENT_DISPOSITION;
1838 	    me->state = miCHECK;
1839 	    CTRACE((tfp,
1840 		    "HTMIME: Was CONTENT_, found D, checking for 'isposition:'\n"));
1841 	    break;
1842 
1843 	case 'e':
1844 	case 'E':
1845 	    me->check_pointer = "ncoding:";
1846 	    me->if_ok = miCONTENT_ENCODING;
1847 	    me->state = miCHECK;
1848 	    CTRACE((tfp,
1849 		    "HTMIME: Was CONTENT_, found E, checking for 'ncoding:'\n"));
1850 	    break;
1851 
1852 	case 'f':
1853 	case 'F':
1854 	    me->check_pointer = "eatures:";
1855 	    me->if_ok = miCONTENT_FEATURES;
1856 	    me->state = miCHECK;
1857 	    CTRACE((tfp,
1858 		    "HTMIME: Was CONTENT_, found F, checking for 'eatures:'\n"));
1859 	    break;
1860 
1861 	case 'l':
1862 	case 'L':
1863 	    me->state = miCONTENT_L;
1864 	    CTRACE((tfp,
1865 		    "HTMIME: Was CONTENT_, found L, state now CONTENT_L\n"));
1866 	    break;
1867 
1868 	case 'm':
1869 	case 'M':
1870 	    me->check_pointer = "d5:";
1871 	    me->if_ok = miCONTENT_MD5;
1872 	    me->state = miCHECK;
1873 	    CTRACE((tfp, "HTMIME: Was CONTENT_, found M, checking for 'd5:'\n"));
1874 	    break;
1875 
1876 	case 'r':
1877 	case 'R':
1878 	    me->check_pointer = "ange:";
1879 	    me->if_ok = miCONTENT_RANGE;
1880 	    me->state = miCHECK;
1881 	    CTRACE((tfp,
1882 		    "HTMIME: Was CONTENT_, found R, checking for 'ange:'\n"));
1883 	    break;
1884 
1885 	case 't':
1886 	case 'T':
1887 	    me->state = miCONTENT_T;
1888 	    CTRACE((tfp,
1889 		    "HTMIME: Was CONTENT_, found T, state now CONTENT_T\n"));
1890 	    break;
1891 
1892 	default:
1893 	    CTRACE((tfp, "HTMIME: Was CONTENT_, found nothing; bleah\n"));
1894 	    goto bad_field_name;
1895 
1896 	}			/* switch on character */
1897 	break;
1898 
1899     case miCONTENT_L:
1900 	CTRACE((tfp, "HTMIME: in case CONTENT_L\n"));
1901 
1902 	switch (c) {
1903 	case 'a':
1904 	case 'A':
1905 	    me->check_pointer = "nguage:";
1906 	    me->if_ok = miCONTENT_LANGUAGE;
1907 	    me->state = miCHECK;
1908 	    CTRACE((tfp,
1909 		    "HTMIME: Was CONTENT_L, found A, checking for 'nguage:'\n"));
1910 	    break;
1911 
1912 	case 'e':
1913 	case 'E':
1914 	    me->check_pointer = "ngth:";
1915 	    me->if_ok = miCONTENT_LENGTH;
1916 	    me->state = miCHECK;
1917 	    CTRACE((tfp,
1918 		    "HTMIME: Was CONTENT_L, found E, checking for 'ngth:'\n"));
1919 	    break;
1920 
1921 	case 'o':
1922 	case 'O':
1923 	    me->check_pointer = "cation:";
1924 	    me->if_ok = miCONTENT_LOCATION;
1925 	    me->state = miCHECK;
1926 	    CTRACE((tfp,
1927 		    "HTMIME: Was CONTENT_L, found O, checking for 'cation:'\n"));
1928 	    break;
1929 
1930 	default:
1931 	    CTRACE((tfp, "HTMIME: Was CONTENT_L, found nothing; bleah\n"));
1932 	    goto bad_field_name;
1933 
1934 	}			/* switch on character */
1935 	break;
1936 
1937     case miCONTENT_T:
1938 	CTRACE((tfp, "HTMIME: in case CONTENT_T\n"));
1939 
1940 	switch (c) {
1941 	case 'r':
1942 	case 'R':
1943 	    me->check_pointer = "ansfer-encoding:";
1944 	    me->if_ok = miCONTENT_TRANSFER_ENCODING;
1945 	    me->state = miCHECK;
1946 	    CTRACE((tfp,
1947 		    "HTMIME: Was CONTENT_T, found R, checking for 'ansfer-encoding:'\n"));
1948 	    break;
1949 
1950 	case 'y':
1951 	case 'Y':
1952 	    me->check_pointer = "pe:";
1953 	    me->if_ok = miCONTENT_TYPE;
1954 	    me->state = miCHECK;
1955 	    CTRACE((tfp,
1956 		    "HTMIME: Was CONTENT_T, found Y, checking for 'pe:'\n"));
1957 	    break;
1958 
1959 	default:
1960 	    CTRACE((tfp, "HTMIME: Was CONTENT_T, found nothing; bleah\n"));
1961 	    goto bad_field_name;
1962 
1963 	}			/* switch on character */
1964 	break;
1965 
1966     case miACCEPT_RANGES:
1967     case miAGE:
1968     case miALLOW:
1969     case miALTERNATES:
1970     case miCACHE_CONTROL:
1971     case miCOOKIE:
1972     case miCONNECTION:
1973     case miCONTENT_BASE:
1974     case miCONTENT_DISPOSITION:
1975     case miCONTENT_ENCODING:
1976     case miCONTENT_FEATURES:
1977     case miCONTENT_LANGUAGE:
1978     case miCONTENT_LENGTH:
1979     case miCONTENT_LOCATION:
1980     case miCONTENT_MD5:
1981     case miCONTENT_RANGE:
1982     case miCONTENT_TRANSFER_ENCODING:
1983     case miCONTENT_TYPE:
1984     case miDATE:
1985     case miETAG:
1986     case miEXPIRES:
1987     case miKEEP_ALIVE:
1988     case miLAST_MODIFIED:
1989     case miLINK:
1990     case miLOCATION:
1991     case miPRAGMA:
1992     case miPROXY_AUTHENTICATE:
1993     case miPUBLIC:
1994     case miREFRESH:
1995     case miRETRY_AFTER:
1996     case miSAFE:
1997     case miSERVER:
1998     case miSET_COOKIE1:
1999     case miSET_COOKIE2:
2000     case miTITLE:
2001     case miTRANSFER_ENCODING:
2002     case miUPGRADE:
2003     case miURI:
2004     case miVARY:
2005     case miVIA:
2006     case miWARNING:
2007     case miWWW_AUTHENTICATE:
2008 	me->field = me->state;	/* remember it */
2009 	me->state = miSKIP_GET_VALUE;
2010 	/* Fall through! */
2011 
2012     case miSKIP_GET_VALUE:
2013 	if (c == '\n') {
2014 	    me->fold_state = me->state;
2015 	    me->state = miNEWLINE;
2016 	    break;
2017 	}
2018 	if (WHITE(c))
2019 	    /*
2020 	     * Skip white space.
2021 	     */
2022 	    break;
2023 
2024 	me->value_pointer = me->value;
2025 	me->state = miGET_VALUE;
2026 	/* Fall through to store first character */
2027 
2028     case miGET_VALUE:
2029       GET_VALUE:
2030 	if (c != '\n') {	/* Not end of line */
2031 	    if (me->value_pointer < me->value + VALUE_SIZE - 1) {
2032 		*me->value_pointer++ = (char) c;
2033 		break;
2034 	    } else {
2035 		goto value_too_long;
2036 	    }
2037 	}
2038 	/* Fall through (if end of line) */
2039 
2040     case miJUNK_LINE:
2041 	if (c == '\n') {
2042 	    me->fold_state = me->state;
2043 	    me->state = miNEWLINE;
2044 	}
2045 	break;
2046 
2047     }				/* switch on state */
2048 
2049 #ifdef EXP_HTTP_HEADERS
2050     HTChunkPutc(&me->anchor->http_headers, UCH(c));
2051     if (me->state == MIME_TRANSPARENT) {
2052 	HTChunkTerminate(&me->anchor->http_headers);
2053 	CTRACE((tfp, "Server Headers:\n%.*s\n",
2054 		me->anchor->http_headers.size,
2055 		me->anchor->http_headers.data));
2056 	CTRACE((tfp, "Server Content-Type:%s\n",
2057 		me->anchor->content_type_params));
2058     }
2059 #endif
2060     return;
2061 
2062   value_too_long:
2063     CTRACE((tfp, "HTMIME: *** Syntax error. (string too long)\n"));
2064 
2065   bad_field_name:		/* Ignore it */
2066     me->state = miJUNK_LINE;
2067 
2068 #ifdef EXP_HTTP_HEADERS
2069     HTChunkPutc(&me->anchor->http_headers, UCH(c));
2070 #endif
2071 
2072     return;
2073 
2074 }
2075 
2076 /*	String handling
2077  *	---------------
2078  *
2079  *	Strings must be smaller than this buffer size.
2080  */
HTMIME_put_string(HTStream * me,const char * s)2081 static void HTMIME_put_string(HTStream *me,
2082 			      const char *s)
2083 {
2084     const char *p;
2085 
2086     if (me->state == MIME_TRANSPARENT) {	/* Optimisation */
2087 	(*me->targetClass.put_string) (me->target, s);
2088 
2089     } else if (me->state != MIME_IGNORE) {
2090 	CTRACE((tfp, "HTMIME:  %s\n", s));
2091 
2092 	for (p = s; *p; p++)
2093 	    HTMIME_put_character(me, *p);
2094     }
2095 }
2096 
2097 /*	Buffer write.  Buffers can (and should!) be big.
2098  *	------------
2099  */
HTMIME_write(HTStream * me,const char * s,int l)2100 static void HTMIME_write(HTStream *me,
2101 			 const char *s,
2102 			 int l)
2103 {
2104     const char *p;
2105 
2106     if (me->state == MIME_TRANSPARENT) {	/* Optimisation */
2107 	(*me->targetClass.put_block) (me->target, s, l);
2108 
2109     } else {
2110 	CTRACE((tfp, "HTMIME:  %.*s\n", l, s));
2111 
2112 	for (p = s; p < s + l; p++)
2113 	    HTMIME_put_character(me, *p);
2114     }
2115 }
2116 
2117 /*	Free an HTML object
2118  *	-------------------
2119  *
2120  */
HTMIME_free(HTStream * me)2121 static void HTMIME_free(HTStream *me)
2122 {
2123     if (me) {
2124 	FREE(me->location);
2125 	FREE(me->compression_encoding);
2126 	if (me->target)
2127 	    (*me->targetClass._free) (me->target);
2128 	FREE(me);
2129     }
2130 }
2131 
2132 /*	End writing
2133 */
HTMIME_abort(HTStream * me,HTError e)2134 static void HTMIME_abort(HTStream *me,
2135 			 HTError e)
2136 {
2137     if (me) {
2138 	FREE(me->location);
2139 	FREE(me->compression_encoding);
2140 	if (me->target)
2141 	    (*me->targetClass._abort) (me->target, e);
2142 	FREE(me);
2143     }
2144 }
2145 
2146 /*	Structured Object Class
2147  *	-----------------------
2148  */
2149 static const HTStreamClass HTMIME =
2150 {
2151     "MIMEParser",
2152     HTMIME_free,
2153     HTMIME_abort,
2154     HTMIME_put_character,
2155     HTMIME_put_string,
2156     HTMIME_write
2157 };
2158 
2159 /*	Subclass-specific Methods
2160  *	-------------------------
2161  */
HTMIMEConvert(HTPresentation * pres,HTParentAnchor * anchor,HTStream * sink)2162 HTStream *HTMIMEConvert(HTPresentation *pres,
2163 			HTParentAnchor *anchor,
2164 			HTStream *sink)
2165 {
2166     HTStream *me;
2167 
2168     me = typecalloc(HTStream);
2169 
2170     if (me == NULL)
2171 	outofmem(__FILE__, "HTMIMEConvert");
2172 
2173     assert(me != NULL);
2174 
2175     me->isa = &HTMIME;
2176     me->sink = sink;
2177     me->anchor = anchor;
2178     me->anchor->safe = FALSE;
2179     me->anchor->no_cache = FALSE;
2180     FREE(me->anchor->cache_control);
2181     FREE(me->anchor->SugFname);
2182     FREE(me->anchor->charset);
2183 #ifdef EXP_HTTP_HEADERS
2184     HTChunkClear(&me->anchor->http_headers);
2185     HTChunkInit(&me->anchor->http_headers, 128);
2186 #endif
2187     FREE(me->anchor->content_type_params);
2188     FREE(me->anchor->content_language);
2189     FREE(me->anchor->content_encoding);
2190     FREE(me->anchor->content_base);
2191     FREE(me->anchor->content_disposition);
2192     FREE(me->anchor->content_location);
2193     FREE(me->anchor->content_md5);
2194     me->anchor->content_length = 0;
2195     FREE(me->anchor->date);
2196     FREE(me->anchor->expires);
2197     FREE(me->anchor->last_modified);
2198     FREE(me->anchor->ETag);
2199     FREE(me->anchor->server);
2200     me->target = NULL;
2201     me->state = miBEGINNING_OF_LINE;
2202     /*
2203      * Sadly enough, change this to always default to WWW_HTML to parse all
2204      * text as HTML for the users.
2205      * GAB 06-30-94
2206      * Thanks to Robert Rowland robert@cyclops.pei.edu
2207      *
2208      * After discussion of the correct handline, should be application/octet-
2209      * stream or unknown; causing servers to send a correct content type.
2210      *
2211      * The consequence of using WWW_UNKNOWN is that you end up downloading as a
2212      * binary file what 99.9% of the time is an HTML file, which should have
2213      * been rendered or displayed.  So sadly enough, I'm changing it back to
2214      * WWW_HTML, and it will handle the situation like Mosaic does, and as
2215      * Robert Rowland suggested, because being functionally correct 99.9% of
2216      * the time is better than being technically correct but functionally
2217      * nonsensical.  - FM
2218      */
2219     /***
2220     me->format	  =	WWW_UNKNOWN;
2221     ***/
2222     me->format = WWW_HTML;
2223     me->targetRep = pres->rep_out;
2224     me->boundary = NULL;	/* Not set yet */
2225     me->set_cookie = NULL;	/* Not set yet */
2226     me->set_cookie2 = NULL;	/* Not set yet */
2227     me->refresh_url = NULL;	/* Not set yet */
2228     me->c_t_encoding = 0;	/* Not set yet */
2229     me->compression_encoding = NULL;	/* Not set yet */
2230     me->net_ascii = NO;		/* Local character set */
2231     HTAnchor_setUCInfoStage(me->anchor, current_char_set,
2232 			    UCT_STAGE_STRUCTURED,
2233 			    UCT_SETBY_DEFAULT);
2234     HTAnchor_setUCInfoStage(me->anchor, current_char_set,
2235 			    UCT_STAGE_HTEXT,
2236 			    UCT_SETBY_DEFAULT);
2237     return me;
2238 }
2239 
HTNetMIME(HTPresentation * pres,HTParentAnchor * anchor,HTStream * sink)2240 HTStream *HTNetMIME(HTPresentation *pres,
2241 		    HTParentAnchor *anchor,
2242 		    HTStream *sink)
2243 {
2244     HTStream *me = HTMIMEConvert(pres, anchor, sink);
2245 
2246     if (!me)
2247 	return NULL;
2248 
2249     me->net_ascii = YES;
2250     return me;
2251 }
2252 
HTMIMERedirect(HTPresentation * pres,HTParentAnchor * anchor,HTStream * sink)2253 HTStream *HTMIMERedirect(HTPresentation *pres,
2254 			 HTParentAnchor *anchor,
2255 			 HTStream *sink)
2256 {
2257     HTStream *me = HTMIMEConvert(pres, anchor, sink);
2258 
2259     if (!me)
2260 	return NULL;
2261 
2262     me->pickup_redirection = YES;
2263     if (me->targetRep == WWW_DEBUG && sink)
2264 	me->no_streamstack = YES;
2265     return me;
2266 }
2267 
2268 /*		Japanese header handling functions
2269  *		==================================
2270  *
2271  *	K&Rized and added 07-Jun-96 by FM, based on:
2272  *
2273 ////////////////////////////////////////////////////////////////////////
2274  *
2275  *	ISO-2022-JP handling routines
2276  *			&
2277  *	MIME decode routines (quick hack just for ISO-2022-JP)
2278  *
2279  *		Thu Jan 25 10:11:42 JST 1996
2280  *
2281  *  Copyright (C) 1994, 1995, 1996
2282  *  Shuichi Ichikawa (ichikawa@nuee.nagoya-u.ac.jp)
2283  *
2284  *  This program is free software; you can redistribute it and/or modify
2285  *  it under the terms of the GNU General Public License as published by
2286  *  the Free Software Foundation; either versions 2, or (at your option)
2287  *  any later version.
2288  *
2289  *  This program is distributed in the hope that it will be useful
2290  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
2291  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2292  *  GNU General Public License for more details.
2293  *
2294  *  You should have received a copy of the GNU General Public License
2295  *  along with SKK, see the file COPYING.  If not, write to the Free
2296  *  Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2297  */
2298 
2299 /*
2300  *  MIME decoding routines
2301  *
2302  *	Written by S. Ichikawa,
2303  *	partially inspired by encdec.c of <jh@efd.lth.se>.
2304  *	Caller's buffers decode to no longer than the input strings.
2305  */
2306 #include <LYCharVals.h>		/* S/390 -- gil -- 0163 */
2307 
2308 static char HTmm64[] =
2309 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
2310 static char HTmmquote[] = "0123456789ABCDEF";
2311 static int HTmmcont = 0;
2312 
HTmmdec_base64(char ** t,char * s)2313 static void HTmmdec_base64(char **t,
2314 			   char *s)
2315 {
2316     int d, count, j, val;
2317     char *buf, *bp, nw[4], *p;
2318 
2319     if ((buf = typeMallocn(char, strlen(s) * 3 + 1)) == 0)
2320 	  outofmem(__FILE__, "HTmmdec_base64");
2321 
2322     assert(buf != NULL);
2323 
2324     for (bp = buf; *s; s += 4) {
2325 	val = 0;
2326 	if (s[2] == '=')
2327 	    count = 1;
2328 	else if (s[3] == '=')
2329 	    count = 2;
2330 	else
2331 	    count = 3;
2332 
2333 	for (j = 0; j <= count; j++) {
2334 	    if (!(p = strchr(HTmm64, s[j]))) {
2335 		FREE(buf);
2336 		return;
2337 	    }
2338 	    d = (int) (p - HTmm64);
2339 	    d <<= (3 - j) * 6;
2340 	    val += d;
2341 	}
2342 	for (j = 2; j >= 0; j--) {
2343 	    nw[j] = (char) (val & 255);
2344 	    val >>= 8;
2345 	}
2346 	if (count--)
2347 	    *bp++ = nw[0];
2348 	if (count--)
2349 	    *bp++ = nw[1];
2350 	if (count)
2351 	    *bp++ = nw[2];
2352     }
2353     *bp = '\0';
2354     StrAllocCopy(*t, buf);
2355     FREE(buf);
2356 }
2357 
HTmmdec_quote(char ** t,char * s)2358 static void HTmmdec_quote(char **t,
2359 			  char *s)
2360 {
2361     char *buf, cval, *bp, *p;
2362 
2363     if ((buf = typeMallocn(char, strlen(s) + 1)) == 0)
2364 	  outofmem(__FILE__, "HTmmdec_quote");
2365 
2366     assert(buf != NULL);
2367 
2368     for (bp = buf; *s;) {
2369 	if (*s == '=') {
2370 	    cval = 0;
2371 	    if (s[1] && (p = strchr(HTmmquote, s[1]))) {
2372 		cval = (char) (cval + (char) (p - HTmmquote));
2373 	    } else {
2374 		*bp++ = *s++;
2375 		continue;
2376 	    }
2377 	    if (s[2] && (p = strchr(HTmmquote, s[2]))) {
2378 		cval = (char) (cval << 4);
2379 		cval = (char) (cval + (p - HTmmquote));
2380 		*bp++ = cval;
2381 		s += 3;
2382 	    } else {
2383 		*bp++ = *s++;
2384 	    }
2385 	} else if (*s == '_') {
2386 	    *bp++ = 0x20;
2387 	    s++;
2388 	} else {
2389 	    *bp++ = *s++;
2390 	}
2391     }
2392     *bp = '\0';
2393     StrAllocCopy(*t, buf);
2394     FREE(buf);
2395 }
2396 
2397 /*
2398  *	HTmmdecode for ISO-2022-JP - FM
2399  */
HTmmdecode(char ** target,char * source)2400 void HTmmdecode(char **target,
2401 		char *source)
2402 {
2403     char *buf;
2404     char *mmbuf = NULL;
2405     char *m2buf = NULL;
2406     char *s, *t, *u;
2407     int base64, quote;
2408 
2409     if ((buf = typeMallocn(char, strlen(source) + 1)) == 0)
2410 	  outofmem(__FILE__, "HTmmdecode");
2411 
2412     assert(buf != NULL);
2413 
2414     for (s = source, u = buf; *s;) {
2415 	if (!strncasecomp(s, "=?ISO-2022-JP?B?", 16)) {
2416 	    base64 = 1;
2417 	} else {
2418 	    base64 = 0;
2419 	}
2420 	if (!strncasecomp(s, "=?ISO-2022-JP?Q?", 16)) {
2421 	    quote = 1;
2422 	} else {
2423 	    quote = 0;
2424 	}
2425 	if (base64 || quote) {
2426 	    if (HTmmcont) {
2427 		for (t = s - 1;
2428 		     t >= source && (*t == ' ' || *t == '\t'); t--) {
2429 		    u--;
2430 		}
2431 	    }
2432 	    if (mmbuf == 0)	/* allocate buffer big enough for source */
2433 		StrAllocCopy(mmbuf, source);
2434 	    for (s += 16, t = mmbuf; *s;) {
2435 		if (s[0] == '?' && s[1] == '=') {
2436 		    break;
2437 		} else {
2438 		    *t++ = *s++;
2439 		    *t = '\0';
2440 		}
2441 	    }
2442 	    if (s[0] != '?' || s[1] != '=') {
2443 		goto end;
2444 	    } else {
2445 		s += 2;
2446 		*t = '\0';
2447 	    }
2448 	    if (base64)
2449 		HTmmdec_base64(&m2buf, mmbuf);
2450 	    else
2451 		HTmmdec_quote(&m2buf, mmbuf);
2452 	    for (t = m2buf; *t;)
2453 		*u++ = *t++;
2454 	    HTmmcont = 1;
2455 	} else {
2456 	    if (*s != ' ' && *s != '\t')
2457 		HTmmcont = 0;
2458 	    *u++ = *s++;
2459 	}
2460     }
2461     *u = '\0';
2462   end:
2463     StrAllocCopy(*target, buf);
2464     FREE(m2buf);
2465     FREE(mmbuf);
2466     FREE(buf);
2467 }
2468 
2469 /*
2470  *  Insert ESC where it seems lost.
2471  *  (The author of this function "rjis" is S. Ichikawa.)
2472  */
HTrjis(char ** t,char * s)2473 int HTrjis(char **t,
2474 	   char *s)
2475 {
2476     char *p;
2477     char *buf = NULL;
2478     int kanji = 0;
2479 
2480     if (strchr(s, CH_ESC) || !strchr(s, '$')) {
2481 	if (s != *t)
2482 	    StrAllocCopy(*t, s);
2483 	return 1;
2484     }
2485 
2486     if ((buf = typeMallocn(char, strlen(s) * 2 + 1)) == 0)
2487 	  outofmem(__FILE__, "HTrjis");
2488 
2489     assert(buf != NULL);
2490 
2491     for (p = buf; *s;) {
2492 	if (!kanji && s[0] == '$' && (s[1] == '@' || s[1] == 'B')) {
2493 	    if (HTmaybekanji((int) s[2], (int) s[3])) {
2494 		kanji = 1;
2495 		*p++ = CH_ESC;
2496 		*p++ = *s++;
2497 		*p++ = *s++;
2498 		*p++ = *s++;
2499 		*p++ = *s++;
2500 		continue;
2501 	    }
2502 	    *p++ = *s++;
2503 	    continue;
2504 	}
2505 	if (kanji && s[0] == '(' && (s[1] == 'J' || s[1] == 'B')) {
2506 	    kanji = 0;
2507 	    *p++ = CH_ESC;
2508 	    *p++ = *s++;
2509 	    *p++ = *s++;
2510 	    continue;
2511 	}
2512 	*p++ = *s++;
2513     }
2514     *p = *s;			/* terminate string */
2515 
2516     StrAllocCopy(*t, buf);
2517     FREE(buf);
2518     return 0;
2519 }
2520 
2521 /*
2522  *  The following function "maybekanji" is derived from
2523  *  RJIS-1.0 by Mr. Hironobu Takahashi.
2524  *  Maybekanji() is included here under the courtesy of the author.
2525  *  The original comment of rjis.c is also included here.
2526  */
2527 /*
2528  * RJIS ( Recover JIS code from broken file )
2529  * Copyright (C) 1992 1994
2530  * Hironobu Takahashi (takahasi@tiny.or.jp)
2531  *
2532  * This program is free software; you can redistribute it and/or modify
2533  * it under the terms of the GNU General Public License as published by
2534  * the Free Software Foundation; either versions 2, or (at your option)
2535  * any later version.
2536  *
2537  * This program is distributed in the hope that it will be useful
2538  * but WITHOUT ANY WARRANTY; without even the implied warranty of
2539  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2540  * GNU General Public License for more details.
2541  *
2542  * You should have received a copy of the GNU General Public License
2543  * along with SKK, see the file COPYING.  If not, write to the Free
2544  * Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2545  */
2546 
HTmaybekanji(int c1,int c2)2547 int HTmaybekanji(int c1,
2548 		 int c2)
2549 {
2550 
2551     if ((c2 < 33) || (c2 > 126))
2552 	return 0;
2553     if ((c1 < 33) || ((40 < c1) && (c1 < 48)) || (116 < c1))
2554 	return 0;
2555     c2 -= 32;
2556     switch (c1 - 32) {
2557     case 2:
2558 	if ((14 < c2) && (c2 < 26))
2559 	    return 0;
2560 	if ((33 < c2) && (c2 < 42))
2561 	    return 0;
2562 	if ((48 < c2) && (c2 < 60))
2563 	    return 0;
2564 	if ((74 < c2) && (c2 < 82))
2565 	    return 0;
2566 	if ((89 < c2) && (c2 < 94))
2567 	    return 0;
2568 	break;
2569     case 3:
2570 	if (c2 < 16)
2571 	    return 0;
2572 	if ((25 < c2) && (c2 < 33))
2573 	    return 0;
2574 	if ((58 < c2) && (c2 < 65))
2575 	    return 0;
2576 	if (90 < c2)
2577 	    return 0;
2578 	break;
2579     case 4:
2580 	if (83 < c2)
2581 	    return 0;
2582 	break;
2583     case 5:
2584 	if (86 < c2)
2585 	    return 0;
2586 	break;
2587     case 6:
2588 	if ((24 < c2) && (c2 < 33))
2589 	    return 0;
2590 	if (56 < c2)
2591 	    return 0;
2592 	break;
2593     case 7:
2594 	if ((33 < c2) && (c2 < 49))
2595 	    return 0;
2596 	if (81 < c2)
2597 	    return 0;
2598 	break;
2599     case 8:
2600 	if (32 < c2)
2601 	    return 0;
2602 	break;
2603     case 47:
2604 	if (51 < c2)
2605 	    return 0;
2606 	break;
2607     case 84:
2608 	if (6 < c2)
2609 	    return 0;
2610 	break;
2611     }
2612     return 1;
2613 }
2614