Annotation of libwww/Library/src/HTMIME.c, revision 2.32
2.15 frystyk 1: /* HTMIME.c
2: ** MIME MESSAGE PARSE
3: **
2.22 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.15 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
2.1 timbl 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
2.13 duns 15: ** 8 Jul 94 FM Insulate free() from _free structure element.
2.18 frystyk 16: ** 14 Mar 95 HFN Now using anchor for storing data. No more `\n',
17: ** static buffers etc.
2.1 timbl 18: */
2.17 frystyk 19:
20: /* Library include files */
21: #include "tcp.h"
22: #include "HTUtils.h"
23: #include "HTString.h"
2.9 luotonen 24: #include "HTFormat.h"
2.27 frystyk 25: #include "HTCache.h"
26: #include "HTAlert.h"
2.32 ! frystyk 27: #include "HTAnchor.h"
2.18 frystyk 28: #include "HTChunk.h"
2.26 frystyk 29: #include "HTMethod.h"
2.24 frystyk 30: #include "HTSocket.h"
2.17 frystyk 31: #include "HTFWrite.h"
2.32 ! frystyk 32: #include "HTNetMan.h"
2.31 frystyk 33: #include "HTReqMan.h"
2.14 frystyk 34: #include "HTMIME.h" /* Implemented here */
2.1 timbl 35:
36: /* MIME Object
37: ** -----------
38: */
39: typedef enum _MIME_state {
2.23 frystyk 40: BEGINNING_OF_LINE=0,
2.18 frystyk 41: CHECK, /* check against check_pointer */
42: UNKNOWN, /* Unknown header */
43: JUNK_LINE, /* Ignore rest of header */
44:
2.32 ! frystyk 45: CON, /* Intermediate states */
! 46: CONTENT,
2.18 frystyk 47: FIRSTLETTER_D,
48: FIRSTLETTER_L,
49: CONTENTLETTER_L,
50: CONTENTLETTER_T,
51:
52: ALLOW, /* Headers supported */
53: AUTHENTICATE,
2.32 ! frystyk 54: CONNECTION,
2.18 frystyk 55: CONTENT_ENCODING,
56: CONTENT_LANGUAGE,
57: CONTENT_LENGTH,
2.14 frystyk 58: CONTENT_TRANSFER_ENCODING,
59: CONTENT_TYPE,
2.32 ! frystyk 60: KEEP_ALIVE,
2.23 frystyk 61: MIME_DATE,
2.18 frystyk 62: DERIVED_FROM,
63: EXPIRES,
64: LAST_MODIFIED,
65: LINK,
2.14 frystyk 66: LOCATION,
2.18 frystyk 67: PUBLIC_METHODS,
68: RETRY_AFTER,
69: TITLE,
70: URI_HEADER,
71: VERSION
2.1 timbl 72: } MIME_state;
73:
74: struct _HTStream {
2.18 frystyk 75: CONST HTStreamClass * isa;
76: HTRequest * request;
2.32 ! frystyk 77: HTNet * net;
! 78: HTParentAnchor * anchor;
2.18 frystyk 79: HTStream * target;
80: HTFormat target_format;
81: HTChunk * buffer;
82: HTSocketEOL EOLstate;
83: BOOL transparent;
2.1 timbl 84: };
85:
2.32 ! frystyk 86: PRIVATE HTMIMEHandler *HTMIMEUnknown = NULL; /* Unknown header handler */
! 87:
! 88: /* ------------------------------------------------------------------------- */
! 89: /* HANDLING UNKNOWN HEADERS */
! 90: /* ------------------------------------------------------------------------- */
! 91:
! 92: /* Register a unknown header handler
! 93: ** ---------------------------------
! 94: ** The application can register a handler that gets called when an
! 95: ** unknown header is found in the header
! 96: */
! 97: PUBLIC BOOL HTMIME_register (HTMIMEHandler * cbf)
! 98: {
! 99: return (HTMIMEUnknown = cbf) ? YES : NO;
! 100: }
! 101:
! 102: PUBLIC BOOL HTMIME_unRegister (void)
! 103: {
! 104: HTMIMEUnknown = NULL;
! 105: return YES;
! 106: }
! 107:
! 108: /* ------------------------------------------------------------------------- */
! 109: /* MIME PARSER */
2.18 frystyk 110: /* ------------------------------------------------------------------------- */
2.1 timbl 111:
2.18 frystyk 112: /*
2.1 timbl 113: ** This is a FSM parser which is tolerant as it can be of all
114: ** syntax errors. It ignores field names it does not understand,
115: ** and resynchronises on line beginnings.
116: */
2.27 frystyk 117: PRIVATE int parseheader ARGS3(HTStream *, me, HTRequest *, request,
118: HTParentAnchor *, anchor)
2.18 frystyk 119: {
120: MIME_state state = BEGINNING_OF_LINE;
121: MIME_state ok_state; /* got this state if match */
122: char *ptr = me->buffer->data-1; /* We dont change the data in length */
123: char *stop = ptr+me->buffer->size; /* When to stop */
124: char *header = ptr; /* For diagnostics */
125: CONST char * check_pointer; /* checking input */
126: char *value;
2.27 frystyk 127:
128: /* In case we get an empty header consisting of a CRLF, we fall thru */
2.18 frystyk 129: while (ptr < stop) {
130: switch (state) {
131: case BEGINNING_OF_LINE:
132: header = ++ptr;
133: switch (TOLOWER(*ptr)) {
134: case 'a':
135: check_pointer = "llow";
136: ok_state = ALLOW;
137: state = CHECK;
138: break;
139:
140: case 'c':
2.32 ! frystyk 141: check_pointer = "on";
! 142: ok_state = CON;
2.18 frystyk 143: state = CHECK;
144: break;
145:
146: case 'd':
147: state = FIRSTLETTER_D;
148: break;
149:
150: case 'e':
151: check_pointer = "xpires";
152: ok_state = EXPIRES;
153: state = CHECK;
154: break;
155:
2.32 ! frystyk 156: case 'k':
! 157: state = KEEP_ALIVE;
! 158: break;
! 159:
2.18 frystyk 160: case 'l':
161: state = FIRSTLETTER_L;
162: break;
163:
164: case 'm':
165: check_pointer = "ime-version";
166: ok_state = JUNK_LINE; /* We don't use this but recognize it */
167: state = CHECK;
168: break;
169:
170: case 'p':
171: break;
172:
173: case 'r':
174: check_pointer = "etry-after";
175: ok_state = RETRY_AFTER;
176: state = CHECK;
177: break;
178:
179: case 's':
180: check_pointer = "erver";
181: ok_state = JUNK_LINE; /* We don't use this but recognize it */
182: state = CHECK;
183: break;
2.1 timbl 184:
2.18 frystyk 185: case 't':
186: check_pointer = "itle";
187: ok_state = TITLE;
188: state = CHECK;
189: break;
190:
191: case 'u':
192: check_pointer = "ri";
193: ok_state = URI_HEADER;
194: state = CHECK;
195: break;
196:
197: case 'v':
198: check_pointer = "ersion";
199: ok_state = VERSION;
200: state = CHECK;
201: break;
202:
203: case 'w':
204: check_pointer = "ww-authenticate";
205: ok_state = AUTHENTICATE;
206: state = CHECK;
207: break;
2.1 timbl 208:
2.18 frystyk 209: default:
210: state = UNKNOWN;
211: break;
212: }
213: ptr++;
2.1 timbl 214: break;
215:
2.18 frystyk 216: case FIRSTLETTER_D:
217: switch (TOLOWER(*ptr)) {
218: case 'a':
219: check_pointer = "te";
2.23 frystyk 220: ok_state = MIME_DATE;
2.18 frystyk 221: state = CHECK;
222: break;
223:
224: case 'e':
225: check_pointer = "rived-from";
226: ok_state = DERIVED_FROM;
227: state = CHECK;
228: break;
229:
230: default:
231: state = UNKNOWN;
232: break;
233: }
234: ptr++;
235: break;
236:
237: case FIRSTLETTER_L:
238: switch (TOLOWER(*ptr)) {
239: case 'a':
240: check_pointer = "st-modified";
241: ok_state = LAST_MODIFIED;
242: state = CHECK;
243: break;
244:
245: case 'i':
246: check_pointer = "nk";
247: ok_state = LINK;
248: state = CHECK;
249: break;
250:
251: case 'o':
252: check_pointer = "cation";
253: ok_state = LOCATION;
254: state = CHECK;
255: break;
256:
257: default:
258: state = UNKNOWN;
259: break;
260: }
261: ptr++;
262: break;
263:
2.32 ! frystyk 264: case CON:
! 265: switch (TOLOWER(*ptr)) {
! 266: case 'n':
! 267: check_pointer = "ection";
! 268: ok_state = CONNECTION;
! 269: state = CHECK;
! 270: break;
! 271:
! 272: case 't':
! 273: check_pointer = "ent-";
! 274: ok_state = CONTENT;
! 275: state = CHECK;
! 276: break;
! 277:
! 278: default:
! 279: state = UNKNOWN;
! 280: break;
! 281: }
! 282: ptr++;
! 283: break;
! 284:
2.18 frystyk 285: case CONTENT:
286: switch (TOLOWER(*ptr)) {
287: case 'e':
288: check_pointer = "ncoding";
289: ok_state = CONTENT_ENCODING;
290: state = CHECK;
291: break;
292:
293: case 'l':
294: state = CONTENTLETTER_L;
295: break;
296:
297: case 't':
298: state = CONTENTLETTER_T;
299: break;
300:
301: default:
302: state = UNKNOWN;
303: break;
304: }
305: ptr++;
2.1 timbl 306: break;
2.14 frystyk 307:
2.18 frystyk 308: case CONTENTLETTER_L:
309: switch (TOLOWER(*ptr)) {
310: case 'a':
311: check_pointer = "nguage";
312: ok_state = CONTENT_LANGUAGE;
313: state = CHECK;
314: break;
315:
316: case 'e':
317: check_pointer = "ngth";
318: ok_state = CONTENT_LENGTH;
319: state = CHECK;
320: break;
321:
322: default:
323: state = UNKNOWN;
324: break;
325: }
326: ptr++;
2.14 frystyk 327: break;
328:
2.18 frystyk 329: case CONTENTLETTER_T:
330: switch (TOLOWER(*ptr)) {
331: case 'r':
332: check_pointer = "ansfer-encoding";
333: ok_state = CONTENT_TRANSFER_ENCODING;
334: state = CHECK;
335: break;
336:
337: case 'y':
338: check_pointer = "pe";
339: ok_state = CONTENT_TYPE;
340: state = CHECK;
341: break;
342:
343: default:
344: state = UNKNOWN;
345: break;
346: }
347: ptr++;
2.14 frystyk 348: break;
349:
2.18 frystyk 350: case CHECK: /* Check against string */
351: while (TOLOWER(*ptr) == *(check_pointer)++) ptr++;
352: if (!*--check_pointer) {
353: state = ok_state;
354: while (*ptr && (WHITE(*ptr) || *ptr==':')) /* Spool to value */
355: ptr++;
356: } else
357: state = UNKNOWN;
2.14 frystyk 358: break;
359:
2.18 frystyk 360: case ALLOW:
2.20 frystyk 361: while ((value = HTNextField(&ptr)) != NULL) {
362: HTMethod new_method;
2.26 frystyk 363: /* We treat them as case-insensitive! */
2.20 frystyk 364: if ((new_method = HTMethod_enum(value)) != METHOD_INVALID)
365: anchor->methods += new_method;
2.1 timbl 366: }
2.18 frystyk 367: if (STREAM_TRACE)
368: fprintf(TDEST, "MIMEParser.. Methods allowed: %d\n",
369: anchor->methods);
370: state = JUNK_LINE;
2.1 timbl 371: break;
2.18 frystyk 372:
373: case AUTHENTICATE:
374: if ((value = HTNextField(&ptr)) != NULL) {
375: StrAllocCopy(request->WWWAAScheme, value);
2.20 frystyk 376:
377: /* The parsing is done in HTSSUtils.c for the moment */
378: if (*ptr) StrAllocCopy(request->WWWAARealm, ptr);
2.1 timbl 379: }
2.18 frystyk 380: state = JUNK_LINE;
381: break;
382:
2.32 ! frystyk 383: case CONNECTION:
! 384: if ((value = HTNextField(&ptr)) != NULL) {
! 385: if (!strcasecomp(value, "keep-alive")) {
! 386: if (STREAM_TRACE)
! 387: fprintf(TDEST,"MIMEParser.. Persistent Connection!\n");
! 388: HTDNS_setSocket(me->net->dns, me->net->sockfd);
! 389: }
! 390: }
! 391: state = JUNK_LINE;
! 392: break;
! 393:
2.18 frystyk 394: case CONTENT_ENCODING:
395: if ((value = HTNextField(&ptr)) != NULL) {
396: char *lc = value;
2.20 frystyk 397: while ((*lc = TOLOWER(*lc))) lc++;
2.18 frystyk 398: anchor->content_encoding = HTAtom_for(value);
399: }
400: state = JUNK_LINE;
401: break;
402:
2.21 frystyk 403: case CONTENT_LANGUAGE: /* @@@ SHOULD BE A LIST @@@ */
404: if ((value = HTNextField(&ptr)) != NULL) {
405: char *lc = value;
406: while ((*lc = TOLOWER(*lc))) lc++;
407: anchor->content_language = HTAtom_for(value);
408: }
409: state = JUNK_LINE;
2.18 frystyk 410: break;
411:
412: case CONTENT_LENGTH:
413: if ((value = HTNextField(&ptr)) != NULL)
414: anchor->content_length = atol(value);
415: state = JUNK_LINE;
416: break;
417:
418: case CONTENT_TRANSFER_ENCODING:
419: if ((value = HTNextField(&ptr)) != NULL) {
420: char *lc = value;
2.20 frystyk 421: while ((*lc = TOLOWER(*lc))) lc++;
2.18 frystyk 422: anchor->cte = HTAtom_for(value);
423: }
424: state = JUNK_LINE;
425: break;
426:
427: case CONTENT_TYPE:
428: if ((value = HTNextField(&ptr)) != NULL) {
429: char *lc = value;
430: while ((*lc = TOLOWER(*lc))) lc++;
431: anchor->content_type = HTAtom_for(value);
2.20 frystyk 432: while ((value = HTNextField(&ptr)) != NULL) { /* Charset */
433: if (!strcasecomp(value, "charset")) {
434: if ((value = HTNextField(&ptr)) != NULL) {
435: lc = value;
436: while ((*lc = TOLOWER(*lc))) lc++;
437: anchor->charset = HTAtom_for(value);
438: }
439: } else if (!strcasecomp(value, "level")) { /* Level */
440: if ((value = HTNextField(&ptr)) != NULL) {
441: lc = value;
442: while ((*lc = TOLOWER(*lc))) lc++;
443: anchor->level = HTAtom_for(value);
444: }
445: }
446: }
2.1 timbl 447: }
2.20 frystyk 448: state = JUNK_LINE;
2.18 frystyk 449: break;
450:
2.32 ! frystyk 451: case KEEP_ALIVE:
! 452: while ((value = HTNextField(&ptr)) != NULL) {
! 453: if (!strcasecomp(value, "timeout")) {
! 454: if ((value = HTNextField(&ptr)) != NULL) {
! 455: int delta = atoi(value);
! 456: if (STREAM_TRACE)
! 457: fprintf(TDEST, "MIMEParser.. Socket expires: %d\n",
! 458: delta);
! 459: HTDNS_setSockExpires(me->net->dns, time(NULL)+delta);
! 460: }
! 461: }
! 462: }
! 463: state = JUNK_LINE;
! 464: break;
! 465:
2.23 frystyk 466: case MIME_DATE:
2.18 frystyk 467: anchor->date = HTParseTime(ptr);
468: state = JUNK_LINE;
469: break;
470:
471: case DERIVED_FROM:
472: if ((value = HTNextField(&ptr)) != NULL)
473: StrAllocCopy(anchor->derived_from, value);
474: state = JUNK_LINE;
475: break;
476:
477: case EXPIRES:
478: anchor->expires = HTParseTime(ptr);
479: state = JUNK_LINE;
480: break;
481:
482: case LAST_MODIFIED:
483: anchor->last_modified = HTParseTime(ptr);
484: state = JUNK_LINE;
485: break;
486:
487: case LINK:
2.20 frystyk 488: state = UNKNOWN; /* @@@@@@@@@@@ */
2.18 frystyk 489: break;
490:
491: case LOCATION:
2.31 frystyk 492: #if 0
493: /*
494: ** Doesn't work as a redirection header might contain a '='
495: ** Thanks to mitch@tam.net (Mitch DeShields)
496: */
2.18 frystyk 497: if ((value = HTNextField(&ptr)) != NULL)
498: StrAllocCopy(request->redirect, value);
2.31 frystyk 499: #endif
500: StrAllocCopy(request->redirect, ptr);
2.18 frystyk 501: state = JUNK_LINE;
502: break;
503:
504: case PUBLIC_METHODS:
2.20 frystyk 505: state = UNKNOWN; /* @@@@@@@@@@@ */
2.18 frystyk 506: break;
507:
508: case RETRY_AFTER:
2.19 frystyk 509: request->retry_after = HTParseTime(ptr);
510: state = JUNK_LINE;
2.18 frystyk 511: break;
512:
513: case TITLE: /* Can't reuse buffer as HTML version might differ */
514: if ((value = HTNextField(&ptr)) != NULL)
515: StrAllocCopy(anchor->title, value);
516: state = JUNK_LINE;
517: break;
518:
519: case URI_HEADER:
520: state = LOCATION; /* @@@ Need extended parsing */
521: break;
522:
523: case VERSION:
524: if ((value = HTNextField(&ptr)) != NULL)
525: StrAllocCopy(anchor->version, value);
526: state = JUNK_LINE;
527: break;
528:
529: case UNKNOWN:
530: if (STREAM_TRACE)
531: fprintf(TDEST,"MIMEParser.. Unknown header: `%s\'\n", header);
2.32 ! frystyk 532: if (HTMIMEUnknown && HTMIMEUnknown(request, header))
! 533: HTAnchor_addExtra(anchor, header);
2.18 frystyk 534:
535: /* Fall through */
536:
537: case JUNK_LINE:
538: while (*ptr) ptr++;
539: state = BEGINNING_OF_LINE;
540: break;
2.1 timbl 541: }
2.18 frystyk 542: }
543:
2.27 frystyk 544: /*
545: ** If coming from cache then check if the document has expired. We can
546: ** either ignore this or attempt a reload
547: */
548: {
549: char *msg;
2.31 frystyk 550: HTExpiresMode expire_mode = HTCache_expiresMode(&msg);
2.27 frystyk 551: if (expire_mode != HT_EXPIRES_IGNORE) {
552: time_t cur = time(NULL);
553: if (anchor->expires>0 && cur>0 && anchor->expires<cur) {
554: if (expire_mode == HT_EXPIRES_NOTIFY)
2.28 frystyk 555: HTAlert(request, msg);
2.31 frystyk 556: else if (HTRequest_retry(request)) {
2.27 frystyk 557: if (PROT_TRACE)
558: fprintf(TDEST, "MIMEParser.. Expired - auto reload\n");
559: if (anchor->cacheHit) {
2.31 frystyk 560: HTRequest_addRqHd(request, HT_IMS);
561: HTRequest_setReloadMode(request, HT_FORCE_RELOAD);
2.27 frystyk 562: anchor->cacheHit = NO; /* Don't want to loop */
563: }
564: return HT_RELOAD;
565: }
566: }
567: }
568: }
569:
2.30 frystyk 570: if (anchor->content_type != WWW_UNKNOWN) {
2.18 frystyk 571: if (STREAM_TRACE)
2.30 frystyk 572: fprintf(TDEST, "MIMEParser.. Media type %s is converted to %s\n",
573: HTAtom_name(anchor->content_type),
574: HTAtom_name(me->target_format));
575: if ((me->target=HTStreamStack(anchor->content_type, me->target_format,
576: me->target, request, YES)) == NULL) {
577: if (STREAM_TRACE)
578: fprintf(TDEST, "MIMEParser.. Can't convert media type\n");
579: me->target = HTBlackHole();
580: }
2.18 frystyk 581: }
582: anchor->header_parsed = YES;
2.27 frystyk 583: me->transparent = YES; /* Pump rest of data right through */
584: return HT_OK;
2.1 timbl 585: }
586:
587:
2.18 frystyk 588: /*
589: ** Header is terminated by CRCR, LFLF, CRLFLF, CRLFCRLF
590: ** Folding is either of CF LWS, LF LWS, CRLF LWS
591: */
592: PRIVATE int HTMIME_put_block ARGS3(HTStream *, me, CONST char *, b, int, l)
593: {
2.32 ! frystyk 594: int status = HT_OK;
2.18 frystyk 595: while (!me->transparent && l-- > 0) {
596: if (me->EOLstate == EOL_FCR) {
2.27 frystyk 597: if (*b == CR) { /* End of header */
2.32 ! frystyk 598: int status = parseheader(me, me->request, me->anchor);
! 599: me->net->bytes_read = l;
2.27 frystyk 600: if (status != HT_OK)
601: return status;
602: } else if (*b == LF) /* CRLF */
2.18 frystyk 603: me->EOLstate = EOL_FLF;
604: else if (WHITE(*b)) { /* Folding: CR SP */
605: me->EOLstate = EOL_BEGIN;
606: HTChunkPutc(me->buffer, ' ');
607: } else { /* New line */
608: me->EOLstate = EOL_BEGIN;
609: HTChunkPutc(me->buffer, '\0');
610: HTChunkPutc(me->buffer, *b);
611: }
612: } else if (me->EOLstate == EOL_FLF) {
613: if (*b == CR) /* LF CR or CR LF CR */
614: me->EOLstate = EOL_SCR;
2.27 frystyk 615: else if (*b == LF) { /* End of header */
2.32 ! frystyk 616: int status = parseheader(me, me->request, me->anchor);
! 617: me->net->bytes_read = l;
2.27 frystyk 618: if (status != HT_OK)
619: return status;
620: } else if (WHITE(*b)) { /* Folding: LF SP or CR LF SP */
2.18 frystyk 621: me->EOLstate = EOL_BEGIN;
622: HTChunkPutc(me->buffer, ' ');
623: } else { /* New line */
624: me->EOLstate = EOL_BEGIN;
625: HTChunkPutc(me->buffer, '\0');
626: HTChunkPutc(me->buffer, *b);
627: }
628: } else if (me->EOLstate == EOL_SCR) {
2.27 frystyk 629: if (*b==CR || *b==LF) { /* End of header */
2.32 ! frystyk 630: int status = parseheader(me, me->request, me->anchor);
! 631: me->net->bytes_read = l;
2.27 frystyk 632: if (status != HT_OK)
633: return status;
634: } else if (WHITE(*b)) { /* Folding: LF CR SP or CR LF CR SP */
2.18 frystyk 635: me->EOLstate = EOL_BEGIN;
636: HTChunkPutc(me->buffer, ' ');
637: } else { /* New line */
638: me->EOLstate = EOL_BEGIN;
639: HTChunkPutc(me->buffer, '\0');
640: HTChunkPutc(me->buffer, *b);
641: }
642: } else if (*b == CR) {
643: me->EOLstate = EOL_FCR;
644: } else if (*b == LF) {
645: me->EOLstate = EOL_FLF; /* Line found */
646: } else
647: HTChunkPutc(me->buffer, *b);
648: b++;
649: }
2.30 frystyk 650: #if 0
2.26 frystyk 651: if (me->target) { /* Is the stream set up? */
652: if (l > 0) /* Anything left? */
653: return (*me->target->isa->put_block)(me->target, b, l);
654: return HT_OK;
655: }
656: return HT_WOULD_BLOCK;
2.30 frystyk 657: #endif
2.32 ! frystyk 658:
! 659: /*
! 660: ** Put the rest down the stream without touching the data but make sure
! 661: ** that we get the correct content length of data
! 662: */
! 663: if (me->target && l > 0) {
! 664: int status = (*me->target->isa->put_block)(me->target, b, l);
! 665: if (status==HT_OK && me->net->bytes_read >=me->anchor->content_length){
! 666: HTDNS_clearActive(me->net->dns);
! 667: return HT_LOADED;
! 668: }
! 669: }
! 670: return status;
2.18 frystyk 671: }
672:
673:
674: /* Character handling
675: ** ------------------
676: */
2.21 frystyk 677: PRIVATE int HTMIME_put_character ARGS2(HTStream *, me, char, c)
2.18 frystyk 678: {
679: return HTMIME_put_block(me, &c, 1);
680: }
681:
2.1 timbl 682:
683: /* String handling
684: ** ---------------
685: */
2.18 frystyk 686: PRIVATE int HTMIME_put_string ARGS2(HTStream *, me, CONST char *, s)
2.1 timbl 687: {
2.18 frystyk 688: return HTMIME_put_block(me, s, (int) strlen(s));
2.1 timbl 689: }
690:
691:
2.18 frystyk 692: /* Flush an stream object
693: ** ---------------------
2.1 timbl 694: */
2.18 frystyk 695: PRIVATE int HTMIME_flush ARGS1(HTStream *, me)
2.1 timbl 696: {
2.18 frystyk 697: return (*me->target->isa->flush)(me->target);
2.1 timbl 698: }
699:
2.18 frystyk 700: /* Free a stream object
701: ** --------------------
2.1 timbl 702: */
2.14 frystyk 703: PRIVATE int HTMIME_free ARGS1(HTStream *, me)
2.1 timbl 704: {
2.18 frystyk 705: int status = HT_OK;
2.25 frystyk 706: if (me->target) {
707: if ((status = (*me->target->isa->_free)(me->target))==HT_WOULD_BLOCK)
708: return HT_WOULD_BLOCK;
709: }
2.26 frystyk 710: if (PROT_TRACE)
711: fprintf(TDEST, "MIME........ FREEING....\n");
2.19 frystyk 712: HTChunkFree(me->buffer);
2.1 timbl 713: free(me);
2.18 frystyk 714: return status;
2.1 timbl 715: }
716:
717: /* End writing
718: */
2.14 frystyk 719: PRIVATE int HTMIME_abort ARGS2(HTStream *, me, HTError, e)
2.1 timbl 720: {
2.18 frystyk 721: int status = HT_ERROR;
722: if (me->target)
723: status = (*me->target->isa->abort)(me->target, e);
2.26 frystyk 724: if (PROT_TRACE)
725: fprintf(TDEST, "MIME........ ABORTING...\n");
726: HTChunkFree(me->buffer);
2.6 timbl 727: free(me);
2.18 frystyk 728: return status;
2.1 timbl 729: }
730:
731:
732:
733: /* Structured Object Class
734: ** -----------------------
735: */
2.6 timbl 736: PRIVATE CONST HTStreamClass HTMIME =
2.1 timbl 737: {
738: "MIMEParser",
2.18 frystyk 739: HTMIME_flush,
2.1 timbl 740: HTMIME_free,
2.6 timbl 741: HTMIME_abort,
742: HTMIME_put_character,
743: HTMIME_put_string,
2.18 frystyk 744: HTMIME_put_block
2.1 timbl 745: };
746:
747:
748: /* Subclass-specific Methods
749: ** -------------------------
750: */
2.7 timbl 751: PUBLIC HTStream* HTMIMEConvert ARGS5(
752: HTRequest *, request,
753: void *, param,
754: HTFormat, input_format,
755: HTFormat, output_format,
756: HTStream *, output_stream)
2.1 timbl 757: {
758: HTStream* me;
2.18 frystyk 759: if ((me=(HTStream *) calloc(1, sizeof(* me))) == NULL)
760: outofmem(__FILE__, "HTMIMEConvert");
2.1 timbl 761: me->isa = &HTMIME;
2.18 frystyk 762: me->request = request;
2.32 ! frystyk 763: me->anchor = request->anchor;
! 764: me->net = request->net;
2.18 frystyk 765: me->target = output_stream;
766: me->target_format = output_format;
767: me->buffer = HTChunkCreate(512);
768: me->EOLstate = EOL_BEGIN;
2.1 timbl 769: return me;
770: }
2.32 ! frystyk 771:
Webmaster