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