1 /*
2 * $LynxId: HTFormat.c,v 1.80 2013/05/05 20:19:02 tom Exp $
3 *
4 * Manage different file formats HTFormat.c
5 * =============================
6 *
7 * Bugs:
8 * Not reentrant.
9 *
10 * Assumes the incoming stream is ASCII, rather than a local file
11 * format, and so ALWAYS converts from ASCII on non-ASCII machines.
12 * Therefore, non-ASCII machines can't read local files.
13 *
14 */
15
16 #define HTSTREAM_INTERNAL 1
17
18 #include <HTUtils.h>
19
20 /* Implements:
21 */
22 #include <HTFormat.h>
23
24 static float HTMaxSecs = 1e10; /* No effective limit */
25
26 #ifdef UNIX
27 #ifdef NeXT
28 #define PRESENT_POSTSCRIPT "open %s; /bin/rm -f %s\n"
29 #else
30 #define PRESENT_POSTSCRIPT "(ghostview %s ; /bin/rm -f %s)&\n"
31 /* Full pathname would be better! */
32 #endif /* NeXT */
33 #endif /* UNIX */
34
35 #include <HTML.h>
36 #include <HTMLDTD.h>
37 #include <HText.h>
38 #include <HTAlert.h>
39 #include <HTList.h>
40 #include <HTInit.h>
41 #include <HTTCP.h>
42 #include <HTTP.h>
43 /* Streams and structured streams which we use:
44 */
45 #include <HTFWriter.h>
46 #include <HTPlain.h>
47 #include <SGML.h>
48 #include <HTMLGen.h>
49
50 #include <LYexit.h>
51 #include <LYUtils.h>
52 #include <GridText.h>
53 #include <LYGlobalDefs.h>
54 #include <LYLeaks.h>
55
56 #ifdef DISP_PARTIAL
57 #include <LYMainLoop.h>
58 #endif
59
60 BOOL HTOutputSource = NO; /* Flag: shortcut parser to stdout */
61
62 /* this version used by the NetToText stream */
63 struct _HTStream {
64 const HTStreamClass *isa;
65 BOOL had_cr;
66 HTStream *sink;
67 };
68
69 /* Presentation methods
70 * --------------------
71 */
72 HTList *HTPresentations = NULL;
73 HTPresentation *default_presentation = NULL;
74
75 /*
76 * To free off the presentation list.
77 */
78 #ifdef LY_FIND_LEAKS
79 static void HTFreePresentations(void);
80 #endif
81
82 /* Define a presentation system command for a content-type
83 * -------------------------------------------------------
84 */
HTSetPresentation(const char * representation,const char * command,const char * testcommand,double quality,double secs,double secs_per_byte,long int maxbytes,AcceptMedia media)85 void HTSetPresentation(const char *representation,
86 const char *command,
87 const char *testcommand,
88 double quality,
89 double secs,
90 double secs_per_byte,
91 long int maxbytes,
92 AcceptMedia media)
93 {
94 HTPresentation *pres = typecalloc(HTPresentation);
95
96 if (pres == NULL)
97 outofmem(__FILE__, "HTSetPresentation");
98
99 assert(pres != NULL);
100 assert(representation != NULL);
101
102 CTRACE2(TRACE_CFG,
103 (tfp,
104 "HTSetPresentation rep=%s, command=%s, test=%s, qual=%f\n",
105 NonNull(representation),
106 NonNull(command),
107 NonNull(testcommand),
108 quality));
109
110 pres->rep = HTAtom_for(representation);
111 pres->rep_out = WWW_PRESENT; /* Fixed for now ... :-) */
112 pres->converter = HTSaveAndExecute; /* Fixed for now ... */
113 pres->quality = (float) quality;
114 pres->secs = (float) secs;
115 pres->secs_per_byte = (float) secs_per_byte;
116 pres->maxbytes = maxbytes;
117 pres->get_accept = 0;
118 pres->accept_opt = media;
119
120 pres->command = NULL;
121 StrAllocCopy(pres->command, command);
122
123 pres->testcommand = NULL;
124 StrAllocCopy(pres->testcommand, testcommand);
125
126 /*
127 * Memory leak fixed.
128 * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe
129 */
130 if (!HTPresentations) {
131 HTPresentations = HTList_new();
132 #ifdef LY_FIND_LEAKS
133 atexit(HTFreePresentations);
134 #endif
135 }
136
137 if (strcmp(representation, "*") == 0) {
138 FREE(default_presentation);
139 default_presentation = pres;
140 } else {
141 HTList_addObject(HTPresentations, pres);
142 }
143 }
144
145 /* Define a built-in function for a content-type
146 * ---------------------------------------------
147 */
HTSetConversion(const char * representation_in,const char * representation_out,HTConverter * converter,double quality,double secs,double secs_per_byte,long int maxbytes,AcceptMedia media)148 void HTSetConversion(const char *representation_in,
149 const char *representation_out,
150 HTConverter *converter,
151 double quality,
152 double secs,
153 double secs_per_byte,
154 long int maxbytes,
155 AcceptMedia media)
156 {
157 HTPresentation *pres = typecalloc(HTPresentation);
158
159 if (pres == NULL)
160 outofmem(__FILE__, "HTSetConversion");
161
162 assert(pres != NULL);
163
164 CTRACE2(TRACE_CFG,
165 (tfp,
166 "HTSetConversion rep_in=%s, rep_out=%s, qual=%f\n",
167 NonNull(representation_in),
168 NonNull(representation_out),
169 quality));
170
171 pres->rep = HTAtom_for(representation_in);
172 pres->rep_out = HTAtom_for(representation_out);
173 pres->converter = converter;
174 pres->command = NULL;
175 pres->testcommand = NULL;
176 pres->quality = (float) quality;
177 pres->secs = (float) secs;
178 pres->secs_per_byte = (float) secs_per_byte;
179 pres->maxbytes = maxbytes;
180 pres->get_accept = TRUE;
181 pres->accept_opt = media;
182
183 /*
184 * Memory Leak fixed.
185 * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe
186 */
187 if (!HTPresentations) {
188 HTPresentations = HTList_new();
189 #ifdef LY_FIND_LEAKS
190 atexit(HTFreePresentations);
191 #endif
192 }
193
194 HTList_addObject(HTPresentations, pres);
195 }
196
197 #ifdef LY_FIND_LEAKS
198 /*
199 * Purpose: Free the presentation list.
200 * Arguments: void
201 * Return Value: void
202 * Remarks/Portability/Dependencies/Restrictions:
203 * Made to clean up Lynx' bad leakage.
204 * Revision History:
205 * 05-28-94 created Lynx 2-3-1 Garrett Arch Blythe
206 */
HTFreePresentations(void)207 static void HTFreePresentations(void)
208 {
209 HTPresentation *pres = NULL;
210
211 /*
212 * Loop through the list.
213 */
214 while (!HTList_isEmpty(HTPresentations)) {
215 /*
216 * Free off each item. May also need to free off its items, but not
217 * sure as of yet.
218 */
219 pres = (HTPresentation *) HTList_removeLastObject(HTPresentations);
220 FREE(pres->command);
221 FREE(pres->testcommand);
222 FREE(pres);
223 }
224 /*
225 * Free the list itself.
226 */
227 HTList_delete(HTPresentations);
228 HTPresentations = NULL;
229 }
230 #endif /* LY_FIND_LEAKS */
231
232 /* File buffering
233 * --------------
234 *
235 * The input file is read using the macro which can read from
236 * a socket or a file.
237 * The input buffer size, if large will give greater efficiency and
238 * release the server faster, and if small will save space on PCs etc.
239 */
240 #define INPUT_BUFFER_SIZE 4096 /* Tradeoff */
241 static char input_buffer[INPUT_BUFFER_SIZE];
242 static char *input_pointer;
243 static char *input_limit;
244 static int input_file_number;
245
246 /* Set up the buffering
247 *
248 * These routines are public because they are in fact needed by
249 * many parsers, and on PCs and Macs we should not duplicate
250 * the static buffer area.
251 */
HTInitInput(int file_number)252 void HTInitInput(int file_number)
253 {
254 input_file_number = file_number;
255 input_pointer = input_limit = input_buffer;
256 }
257
258 int interrupted_in_htgetcharacter = 0;
HTGetCharacter(void)259 int HTGetCharacter(void)
260 {
261 char ch;
262
263 interrupted_in_htgetcharacter = 0;
264 do {
265 if (input_pointer >= input_limit) {
266 int status = NETREAD(input_file_number,
267 input_buffer, INPUT_BUFFER_SIZE);
268
269 if (status <= 0) {
270 if (status == 0)
271 return EOF;
272 if (status == HT_INTERRUPTED) {
273 CTRACE((tfp, "HTFormat: Interrupted in HTGetCharacter\n"));
274 interrupted_in_htgetcharacter = 1;
275 return EOF;
276 }
277 CTRACE((tfp, "HTFormat: File read error %d\n", status));
278 return EOF; /* -1 is returned by UCX
279 at end of HTTP link */
280 }
281 input_pointer = input_buffer;
282 input_limit = input_buffer + status;
283 }
284 ch = *input_pointer++;
285 } while (ch == (char) 13); /* Ignore ASCII carriage return */
286
287 return FROMASCII(UCH(ch));
288 }
289
290 #ifdef USE_SSL
HTGetSSLCharacter(void * handle)291 int HTGetSSLCharacter(void *handle)
292 {
293 char ch;
294
295 interrupted_in_htgetcharacter = 0;
296 if (!handle)
297 return (char) EOF;
298 do {
299 if (input_pointer >= input_limit) {
300 int status = SSL_read((SSL *) handle,
301 input_buffer, INPUT_BUFFER_SIZE);
302
303 if (status <= 0) {
304 if (status == 0)
305 return (char) EOF;
306 if (status == HT_INTERRUPTED) {
307 CTRACE((tfp,
308 "HTFormat: Interrupted in HTGetSSLCharacter\n"));
309 interrupted_in_htgetcharacter = 1;
310 return (char) EOF;
311 }
312 CTRACE((tfp, "HTFormat: SSL_read error %d\n", status));
313 return (char) EOF; /* -1 is returned by UCX
314 at end of HTTP link */
315 }
316 input_pointer = input_buffer;
317 input_limit = input_buffer + status;
318 }
319 ch = *input_pointer++;
320 } while (ch == (char) 13); /* Ignore ASCII carriage return */
321
322 return FROMASCII(ch);
323 }
324 #endif /* USE_SSL */
325
326 /* Match maintype to any MIME type starting with maintype, for example:
327 * image/gif should match image
328 */
half_match(char * trial_type,char * target)329 static int half_match(char *trial_type, char *target)
330 {
331 char *cp = strchr(trial_type, '/');
332
333 /* if no '/' or no '*' */
334 if (!cp || *(cp + 1) != '*')
335 return 0;
336
337 CTRACE((tfp, "HTFormat: comparing %s and %s for half match\n",
338 trial_type, target));
339
340 /* main type matches */
341 if (!StrNCmp(trial_type, target, ((cp - trial_type) - 1)))
342 return 1;
343
344 return 0;
345 }
346
347 /*
348 * Evaluate a deferred mailcap test command, i.e.,. one that substitutes the
349 * document's charset or other values in %{name} format.
350 */
failsMailcap(HTPresentation * pres,HTParentAnchor * anchor)351 static BOOL failsMailcap(HTPresentation *pres, HTParentAnchor *anchor)
352 {
353 if (pres->testcommand != 0) {
354 if (LYTestMailcapCommand(pres->testcommand,
355 anchor->content_type_params) != 0)
356 return TRUE;
357 }
358 return FALSE;
359 }
360
361 #define WWW_WILDCARD_REP_OUT HTAtom_for("*")
362
363 /* Look up a presentation
364 * ----------------------
365 *
366 * If fill_in is NULL, only look for an exact match.
367 * If a wildcard match is made, *fill_in is used to store
368 * a possibly modified presentation, and a pointer to it is
369 * returned. For an exact match, a pointer to the presentation
370 * in the HTPresentations list is returned. Returns NULL if
371 * nothing found. - kw
372 *
373 */
HTFindPresentation(HTFormat rep_in,HTFormat rep_out,HTPresentation * fill_in,HTParentAnchor * anchor)374 static HTPresentation *HTFindPresentation(HTFormat rep_in,
375 HTFormat rep_out,
376 HTPresentation *fill_in,
377 HTParentAnchor *anchor)
378 {
379 HTAtom *wildcard = NULL; /* = HTAtom_for("*"); lookup when needed - kw */
380 int n;
381 int i;
382 HTPresentation *pres;
383 HTPresentation *match;
384 HTPresentation *strong_wildcard_match = 0;
385 HTPresentation *weak_wildcard_match = 0;
386 HTPresentation *last_default_match = 0;
387 HTPresentation *strong_subtype_wildcard_match = 0;
388
389 CTRACE((tfp, "HTFormat: Looking up presentation for %s to %s\n",
390 HTAtom_name(rep_in), HTAtom_name(rep_out)));
391
392 n = HTList_count(HTPresentations);
393 for (i = 0; i < n; i++) {
394 pres = (HTPresentation *) HTList_objectAt(HTPresentations, i);
395 if (pres->rep == rep_in) {
396 if (pres->rep_out == rep_out) {
397 if (failsMailcap(pres, anchor))
398 continue;
399 CTRACE((tfp, "FindPresentation: found exact match: %s -> %s\n",
400 HTAtom_name(pres->rep),
401 HTAtom_name(pres->rep_out)));
402 return pres;
403
404 } else if (!fill_in) {
405 continue;
406 } else {
407 if (!wildcard)
408 wildcard = WWW_WILDCARD_REP_OUT;
409 if (pres->rep_out == wildcard) {
410 if (failsMailcap(pres, anchor))
411 continue;
412 if (!strong_wildcard_match)
413 strong_wildcard_match = pres;
414 /* otherwise use the first one */
415 CTRACE((tfp,
416 "StreamStack: found strong wildcard match: %s -> %s\n",
417 HTAtom_name(pres->rep),
418 HTAtom_name(pres->rep_out)));
419 }
420 }
421
422 } else if (!fill_in) {
423 continue;
424
425 } else if (half_match(HTAtom_name(pres->rep),
426 HTAtom_name(rep_in))) {
427 if (pres->rep_out == rep_out) {
428 if (failsMailcap(pres, anchor))
429 continue;
430 if (!strong_subtype_wildcard_match)
431 strong_subtype_wildcard_match = pres;
432 /* otherwise use the first one */
433 CTRACE((tfp,
434 "StreamStack: found strong subtype wildcard match: %s -> %s\n",
435 HTAtom_name(pres->rep),
436 HTAtom_name(pres->rep_out)));
437 }
438 }
439
440 if (pres->rep == WWW_SOURCE) {
441 if (pres->rep_out == rep_out) {
442 if (failsMailcap(pres, anchor))
443 continue;
444 if (!weak_wildcard_match)
445 weak_wildcard_match = pres;
446 /* otherwise use the first one */
447 CTRACE((tfp,
448 "StreamStack: found weak wildcard match: %s\n",
449 HTAtom_name(pres->rep_out)));
450
451 } else if (!last_default_match) {
452 if (!wildcard)
453 wildcard = WWW_WILDCARD_REP_OUT;
454 if (pres->rep_out == wildcard) {
455 if (failsMailcap(pres, anchor))
456 continue;
457 last_default_match = pres;
458 /* otherwise use the first one */
459 }
460 }
461 }
462 }
463
464 match = (strong_subtype_wildcard_match
465 ? strong_subtype_wildcard_match
466 : (strong_wildcard_match
467 ? strong_wildcard_match
468 : (weak_wildcard_match
469 ? weak_wildcard_match
470 : last_default_match)));
471
472 if (match) {
473 *fill_in = *match; /* Specific instance */
474 fill_in->rep = rep_in; /* yuk */
475 fill_in->rep_out = rep_out; /* yuk */
476 return fill_in;
477 }
478
479 return NULL;
480 }
481
482 /* Create a filter stack
483 * ---------------------
484 *
485 * If a wildcard match is made, a temporary HTPresentation
486 * structure is made to hold the destination format while the
487 * new stack is generated. This is just to pass the out format to
488 * MIME so far. Storing the format of a stream in the stream might
489 * be a lot neater.
490 *
491 */
HTStreamStack(HTFormat rep_in,HTFormat rep_out,HTStream * sink,HTParentAnchor * anchor)492 HTStream *HTStreamStack(HTFormat rep_in,
493 HTFormat rep_out,
494 HTStream *sink,
495 HTParentAnchor *anchor)
496 {
497 HTPresentation temp;
498 HTPresentation *match;
499 HTStream *result;
500
501 CTRACE((tfp, "StreamStack: Constructing stream stack for %s to %s (%s)\n",
502 HTAtom_name(rep_in),
503 HTAtom_name(rep_out),
504 NONNULL(anchor->content_type_params)));
505
506 if (rep_out == rep_in) {
507 result = sink;
508
509 } else if ((match = HTFindPresentation(rep_in, rep_out, &temp, anchor))) {
510 if (match == &temp) {
511 CTRACE((tfp, "StreamStack: Using %s\n", HTAtom_name(temp.rep_out)));
512 } else {
513 CTRACE((tfp, "StreamStack: found exact match: %s -> %s\n",
514 HTAtom_name(match->rep),
515 HTAtom_name(match->rep_out)));
516 }
517 result = (*match->converter) (match, anchor, sink);
518 } else {
519 result = NULL;
520 }
521 if (TRACE) {
522 if (result && result->isa && result->isa->name) {
523 CTRACE((tfp, "StreamStack: Returning \"%s\"\n", result->isa->name));
524 } else if (result) {
525 CTRACE((tfp, "StreamStack: Returning *unknown* stream!\n"));
526 } else {
527 CTRACE((tfp, "StreamStack: Returning NULL!\n"));
528 CTRACE_FLUSH(tfp); /* a crash may be imminent... - kw */
529 }
530 }
531 return result;
532 }
533
534 /* Put a presentation near start of list
535 * -------------------------------------
536 *
537 * Look up a presentation (exact match only) and, if found, reorder
538 * it to the start of the HTPresentations list. - kw
539 */
HTReorderPresentation(HTFormat rep_in,HTFormat rep_out)540 void HTReorderPresentation(HTFormat rep_in,
541 HTFormat rep_out)
542 {
543 HTPresentation *match;
544
545 if ((match = HTFindPresentation(rep_in, rep_out, NULL, NULL))) {
546 HTList_removeObject(HTPresentations, match);
547 HTList_addObject(HTPresentations, match);
548 }
549 }
550
551 /*
552 * Setup 'get_accept' flag to denote presentations that are not redundant,
553 * and will be listed in "Accept:" header.
554 */
HTFilterPresentations(void)555 void HTFilterPresentations(void)
556 {
557 int i, j;
558 int n = HTList_count(HTPresentations);
559 HTPresentation *p, *q;
560 BOOL matched;
561 char *s, *t;
562
563 CTRACE((tfp, "HTFilterPresentations (AcceptMedia %#x)\n", LYAcceptMedia));
564 for (i = 0; i < n; i++) {
565 p = (HTPresentation *) HTList_objectAt(HTPresentations, i);
566 s = HTAtom_name(p->rep);
567
568 p->get_accept = FALSE;
569 if ((LYAcceptMedia & p->accept_opt) != 0
570 && p->rep_out == WWW_PRESENT
571 && p->rep != WWW_SOURCE
572 && strcasecomp(s, "www/mime")
573 && strcasecomp(s, "www/compressed")
574 && p->quality <= 1.0 && p->quality >= 0.0) {
575 matched = TRUE;
576 for (j = 0; j < i; j++) {
577 q = (HTPresentation *) HTList_objectAt(HTPresentations, j);
578 t = HTAtom_name(q->rep);
579
580 if (!strcasecomp(s, t)) {
581 matched = FALSE;
582 CTRACE((tfp, " match %s %s\n", s, t));
583 break;
584 }
585 }
586 p->get_accept = matched;
587 }
588 }
589 }
590
591 /* Find the cost of a filter stack
592 * -------------------------------
593 *
594 * Must return the cost of the same stack which StreamStack would set up.
595 *
596 * On entry,
597 * length The size of the data to be converted
598 */
HTStackValue(HTFormat rep_in,HTFormat rep_out,double initial_value,long int length)599 float HTStackValue(HTFormat rep_in,
600 HTFormat rep_out,
601 double initial_value,
602 long int length)
603 {
604 HTAtom *wildcard = WWW_WILDCARD_REP_OUT;
605
606 CTRACE((tfp, "HTFormat: Evaluating stream stack for %s worth %.3f to %s\n",
607 HTAtom_name(rep_in), initial_value, HTAtom_name(rep_out)));
608
609 if (rep_out == WWW_SOURCE || rep_out == rep_in)
610 return 0.0;
611
612 {
613 int n = HTList_count(HTPresentations);
614 int i;
615 HTPresentation *pres;
616
617 for (i = 0; i < n; i++) {
618 pres = (HTPresentation *) HTList_objectAt(HTPresentations, i);
619 if (pres->rep == rep_in &&
620 (pres->rep_out == rep_out || pres->rep_out == wildcard)) {
621 float value = (float) (initial_value * pres->quality);
622
623 if (HTMaxSecs > 0.0)
624 value = (value
625 - ((float) length * pres->secs_per_byte
626 + pres->secs)
627 / HTMaxSecs);
628 return value;
629 }
630 }
631 }
632
633 return (float) -1e30; /* Really bad */
634
635 }
636
637 /* Display the page while transfer in progress
638 * -------------------------------------------
639 *
640 * Repaint the page only when necessary.
641 * This is a traverse call for HText_pageDisplay() - it works!.
642 *
643 */
HTDisplayPartial(void)644 void HTDisplayPartial(void)
645 {
646 #ifdef DISP_PARTIAL
647 if (display_partial) {
648 /*
649 * HText_getNumOfLines() = "current" number of complete lines received
650 * NumOfLines_partial = number of lines at the moment of last repaint.
651 * (we update NumOfLines_partial only when we repaint the display.)
652 *
653 * display_partial could only be enabled in HText_new() so a new
654 * HTMainText object available - all HText_ functions use it, lines
655 * counter HText_getNumOfLines() in particular.
656 *
657 * Otherwise HTMainText holds info from the previous document and we
658 * may repaint it instead of the new one: prev doc scrolled to the
659 * first line (=Newline_partial) is not good looking :-) 23 Aug 1998
660 * Leonid Pauzner
661 *
662 * So repaint the page only when necessary:
663 */
664 int Newline_partial = LYGetNewline();
665
666 if (((Newline_partial + display_lines) - 1 > NumOfLines_partial)
667 /* current page not complete... */
668 && (partial_threshold > 0 ?
669 ((Newline_partial + partial_threshold) - 1 <=
670 HText_getNumOfLines()) :
671 ((Newline_partial + display_lines) - 1 <= HText_getNumOfLines()))
672 /*
673 * Originally we rendered by increments of 2 lines,
674 * but that got annoying on slow network connections.
675 * Then we switched to full-pages. Now it's configurable.
676 * If partial_threshold <= 0, then it's a full page
677 */
678 ) {
679 if (LYMainLoop_pageDisplay(Newline_partial))
680 NumOfLines_partial = HText_getNumOfLines();
681 }
682 }
683 #else /* nothing */
684 #endif /* DISP_PARTIAL */
685 }
686
687 /* Put this as early as possible, OK just after HTDisplayPartial() */
HTFinishDisplayPartial(void)688 void HTFinishDisplayPartial(void)
689 {
690 #ifdef DISP_PARTIAL
691 /*
692 * End of incremental rendering stage here.
693 */
694 display_partial = FALSE;
695 #endif /* DISP_PARTIAL */
696 }
697
698 /* Push data from a socket down a stream
699 * -------------------------------------
700 *
701 * This routine is responsible for creating and PRESENTING any
702 * graphic (or other) objects described by the file.
703 *
704 * The file number given is assumed to be a TELNET stream, i.e., containing
705 * CRLF at the end of lines which need to be stripped to LF for unix
706 * when the format is textual.
707 *
708 * State of socket and target stream on entry:
709 * socket (file_number) assumed open,
710 * target (sink) assumed valid.
711 *
712 * Return values:
713 * HT_INTERRUPTED Interruption or error after some data received.
714 * -2 Unexpected disconnect before any data received.
715 * -1 Interruption or error before any data received, or
716 * (UNIX) other read error before any data received, or
717 * download cancelled.
718 * HT_LOADED Normal close of socket (end of file indication
719 * received), or
720 * unexpected disconnect after some data received, or
721 * other read error after some data received, or
722 * (not UNIX) other read error before any data received.
723 *
724 * State of socket and target stream on return depends on return value:
725 * HT_INTERRUPTED socket still open, target aborted.
726 * -2 socket still open, target stream still valid.
727 * -1 socket still open, target aborted.
728 * otherwise socket closed, target stream still valid.
729 */
HTCopy(HTParentAnchor * anchor,int file_number,void * handle GCC_UNUSED,HTStream * sink)730 int HTCopy(HTParentAnchor *anchor,
731 int file_number,
732 void *handle GCC_UNUSED,
733 HTStream *sink)
734 {
735 HTStreamClass targetClass;
736 BOOL suppress_readprogress = NO;
737 off_t limit = anchor ? anchor->content_length : 0;
738 off_t bytes = anchor ? anchor->actual_length : 0;
739 off_t total;
740 int rv = 0;
741
742 /* Push the data down the stream
743 */
744 targetClass = *(sink->isa); /* Copy pointers to procedures */
745
746 /*
747 * Push binary from socket down sink
748 *
749 * This operation could be put into a main event loop
750 */
751 HTReadProgress(bytes, (off_t) 0);
752 for (;;) {
753 int status;
754
755 if (LYCancelDownload) {
756 LYCancelDownload = FALSE;
757 (*targetClass._abort) (sink, NULL);
758 rv = -1;
759 goto finished;
760 }
761
762 if (HTCheckForInterrupt()) {
763 _HTProgress(TRANSFER_INTERRUPTED);
764 (*targetClass._abort) (sink, NULL);
765 if (bytes)
766 rv = HT_INTERRUPTED;
767 else
768 rv = -1;
769 goto finished;
770 }
771 #ifdef USE_SSL
772 if (handle)
773 status = SSL_read((SSL *) handle, input_buffer, INPUT_BUFFER_SIZE);
774 else
775 status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
776 #else
777 status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
778 #endif /* USE_SSL */
779
780 if (status <= 0) {
781 if (status == 0) {
782 break;
783 } else if (status == HT_INTERRUPTED) {
784 _HTProgress(TRANSFER_INTERRUPTED);
785 (*targetClass._abort) (sink, NULL);
786 if (bytes)
787 rv = HT_INTERRUPTED;
788 else
789 rv = -1;
790 goto finished;
791 } else if (SOCKET_ERRNO == ENOTCONN ||
792 #ifdef _WINDOWS /* 1997/11/10 (Mon) 16:57:18 */
793 SOCKET_ERRNO == ETIMEDOUT ||
794 #endif
795 SOCKET_ERRNO == ECONNRESET ||
796 SOCKET_ERRNO == EPIPE) {
797 /*
798 * Arrrrgh, HTTP 0/1 compatibility problem, maybe.
799 */
800 if (bytes <= 0) {
801 /*
802 * Don't have any data, so let the calling function decide
803 * what to do about it. - FM
804 */
805 rv = -2;
806 goto finished;
807 } else {
808 #ifdef UNIX
809 /*
810 * Treat what we've received already as the complete
811 * transmission, but not without giving the user an alert.
812 * I don't know about all the different TCP stacks for VMS
813 * etc., so this is currently only for UNIX. - kw
814 */
815 HTInetStatus("NETREAD");
816 HTAlert("Unexpected server disconnect.");
817 CTRACE((tfp,
818 "HTCopy: Unexpected server disconnect. Treating as completed.\n"));
819 #else /* !UNIX */
820 /*
821 * Treat what we've gotten already as the complete
822 * transmission. - FM
823 */
824 CTRACE((tfp,
825 "HTCopy: Unexpected server disconnect. Treating as completed.\n"));
826 status = 0;
827 #endif /* UNIX */
828 }
829 #ifdef UNIX
830 } else { /* status < 0 and other errno */
831 /*
832 * Treat what we've received already as the complete
833 * transmission, but not without giving the user an alert. I
834 * don't know about all the different TCP stacks for VMS etc.,
835 * so this is currently only for UNIX. - kw
836 */
837 HTInetStatus("NETREAD");
838 HTAlert("Unexpected read error.");
839 if (bytes) {
840 (void) NETCLOSE(file_number);
841 rv = HT_LOADED;
842 } else {
843 (*targetClass._abort) (sink, NULL);
844 rv = -1;
845 }
846 goto finished;
847 #endif
848 }
849 break;
850 }
851
852 /*
853 * Suppress ReadProgress messages when collecting a redirection
854 * message, at least initially (unless/until anchor->content_type gets
855 * changed, probably by the MIME message parser). That way messages
856 * put up by the HTTP module or elsewhere can linger in the statusline
857 * for a while. - kw
858 */
859 suppress_readprogress = (BOOL) (anchor && anchor->content_type &&
860 !strcmp(anchor->content_type,
861 "message/x-http-redirection"));
862 #ifdef NOT_ASCII
863 {
864 char *p;
865
866 for (p = input_buffer; p < input_buffer + status; p++) {
867 *p = FROMASCII(*p);
868 }
869 }
870 #endif /* NOT_ASCII */
871
872 total = bytes + status;
873 if (limit == 0 || (total < limit)) {
874 (*targetClass.put_block) (sink, input_buffer, status);
875 } else if (bytes < limit) {
876 (*targetClass.put_block) (sink, input_buffer, (int) (limit - bytes));
877 }
878 bytes = total;
879 if (!suppress_readprogress)
880 HTReadProgress(bytes, limit);
881 HTDisplayPartial();
882
883 } /* next bufferload */
884 if (anchor != 0) {
885 CTRACE((tfp, "HTCopy copied %"
886 PRI_off_t " actual, %"
887 PRI_off_t " limit\n", bytes, limit));
888 anchor->actual_length = bytes;
889 }
890
891 _HTProgress(TRANSFER_COMPLETE);
892 (void) NETCLOSE(file_number);
893 rv = HT_LOADED;
894
895 finished:
896 HTFinishDisplayPartial();
897 return (rv);
898 }
899
900 /* Push data from a file pointer down a stream
901 * -------------------------------------
902 *
903 * This routine is responsible for creating and PRESENTING any
904 * graphic (or other) objects described by the file.
905 *
906 *
907 * State of file and target stream on entry:
908 * FILE* (fp) assumed open,
909 * target (sink) assumed valid.
910 *
911 * Return values:
912 * HT_INTERRUPTED Interruption after some data read.
913 * HT_PARTIAL_CONTENT Error after some data read.
914 * -1 Error before any data read.
915 * HT_LOADED Normal end of file indication on reading.
916 *
917 * State of file and target stream on return:
918 * always fp still open, target stream still valid.
919 */
HTFileCopy(FILE * fp,HTStream * sink)920 int HTFileCopy(FILE *fp, HTStream *sink)
921 {
922 HTStreamClass targetClass;
923 int status;
924 off_t bytes;
925 int rv = HT_OK;
926
927 /* Push the data down the stream
928 */
929 targetClass = *(sink->isa); /* Copy pointers to procedures */
930
931 /* Push binary from socket down sink
932 */
933 HTReadProgress(bytes = 0, (off_t) 0);
934 for (;;) {
935 status = (int) fread(input_buffer,
936 (size_t) 1,
937 (size_t) INPUT_BUFFER_SIZE, fp);
938 if (status == 0) { /* EOF or error */
939 if (ferror(fp) == 0) {
940 rv = HT_LOADED;
941 break;
942 }
943 CTRACE((tfp, "HTFormat: Read error, read returns %d\n",
944 ferror(fp)));
945 if (bytes) {
946 rv = HT_PARTIAL_CONTENT;
947 } else {
948 rv = -1;
949 }
950 break;
951 }
952
953 (*targetClass.put_block) (sink, input_buffer, status);
954 bytes += status;
955 HTReadProgress(bytes, (off_t) 0);
956 /* Suppress last screen update in partial mode - a regular update under
957 * control of mainloop() should follow anyway. - kw
958 */
959 #ifdef DISP_PARTIAL
960 if (display_partial && bytes != HTMainAnchor->content_length)
961 HTDisplayPartial();
962 #endif
963
964 if (HTCheckForInterrupt()) {
965 _HTProgress(TRANSFER_INTERRUPTED);
966 if (bytes) {
967 rv = HT_INTERRUPTED;
968 } else {
969 rv = -1;
970 }
971 break;
972 }
973 } /* next bufferload */
974
975 HTFinishDisplayPartial();
976 return rv;
977 }
978
979 #ifdef USE_SOURCE_CACHE
980 /* Push data from an HTChunk down a stream
981 * ---------------------------------------
982 *
983 * This routine is responsible for creating and PRESENTING any
984 * graphic (or other) objects described by the file.
985 *
986 * State of memory and target stream on entry:
987 * HTChunk* (chunk) and target (sink) assumed valid.
988 *
989 * Return values:
990 * HT_LOADED All data sent.
991 * HT_INTERRUPTED Interruption after some data read.
992 *
993 * State of memory and target stream on return:
994 * always chunk unchanged, target stream still valid.
995 */
HTMemCopy(HTChunk * chunk,HTStream * sink)996 int HTMemCopy(HTChunk *chunk, HTStream *sink)
997 {
998 HTStreamClass targetClass;
999 off_t bytes;
1000 int rv = HT_OK;
1001
1002 targetClass = *(sink->isa);
1003 HTReadProgress(bytes = 0, (off_t) 0);
1004 for (; chunk != NULL; chunk = chunk->next) {
1005
1006 /* Push the data down the stream a piece at a time, in case we're
1007 * running a large document on a slow machine.
1008 */
1009 (*targetClass.put_block) (sink, chunk->data, chunk->size);
1010 bytes += chunk->size;
1011
1012 HTReadProgress(bytes, (off_t) 0);
1013 HTDisplayPartial();
1014
1015 if (HTCheckForInterrupt()) {
1016 _HTProgress(TRANSFER_INTERRUPTED);
1017 if (bytes) {
1018 rv = HT_INTERRUPTED;
1019 } else {
1020 rv = -1;
1021 }
1022 break;
1023 }
1024 }
1025
1026 HTFinishDisplayPartial();
1027 return rv;
1028 }
1029 #endif
1030
1031 #ifdef USE_ZLIB
1032 /* Push data from a gzip file pointer down a stream
1033 * -------------------------------------
1034 *
1035 * This routine is responsible for creating and PRESENTING any
1036 * graphic (or other) objects described by the file.
1037 *
1038 *
1039 * State of file and target stream on entry:
1040 * gzFile (gzfp) assumed open (should have gzipped content),
1041 * target (sink) assumed valid.
1042 *
1043 * Return values:
1044 * HT_INTERRUPTED Interruption after some data read.
1045 * HT_PARTIAL_CONTENT Error after some data read.
1046 * -1 Error before any data read.
1047 * HT_LOADED Normal end of file indication on reading.
1048 *
1049 * State of file and target stream on return:
1050 * always gzfp still open, target stream still valid.
1051 */
HTGzFileCopy(gzFile gzfp,HTStream * sink)1052 static int HTGzFileCopy(gzFile gzfp, HTStream *sink)
1053 {
1054 HTStreamClass targetClass;
1055 int status;
1056 off_t bytes;
1057 int gzerrnum;
1058 int rv = HT_OK;
1059
1060 /* Push the data down the stream
1061 */
1062 targetClass = *(sink->isa); /* Copy pointers to procedures */
1063
1064 /* read and inflate gzip'd file, and push binary down sink
1065 */
1066 HTReadProgress(bytes = 0, (off_t) 0);
1067 for (;;) {
1068 status = gzread(gzfp, input_buffer, INPUT_BUFFER_SIZE);
1069 if (status <= 0) { /* EOF or error */
1070 if (status == 0) {
1071 rv = HT_LOADED;
1072 break;
1073 }
1074 CTRACE((tfp, "HTGzFileCopy: Read error, gzread returns %d\n",
1075 status));
1076 CTRACE((tfp, "gzerror : %s\n",
1077 gzerror(gzfp, &gzerrnum)));
1078 if (TRACE) {
1079 if (gzerrnum == Z_ERRNO)
1080 perror("gzerror ");
1081 }
1082 if (bytes) {
1083 rv = HT_PARTIAL_CONTENT;
1084 } else {
1085 rv = -1;
1086 }
1087 break;
1088 }
1089
1090 (*targetClass.put_block) (sink, input_buffer, status);
1091 bytes += status;
1092 HTReadProgress(bytes, (off_t) -1);
1093 HTDisplayPartial();
1094
1095 if (HTCheckForInterrupt()) {
1096 _HTProgress(TRANSFER_INTERRUPTED);
1097 rv = HT_INTERRUPTED;
1098 break;
1099 }
1100 } /* next bufferload */
1101
1102 HTFinishDisplayPartial();
1103 return rv;
1104 }
1105
1106 #ifndef HAVE_ZERROR
1107 #define zError(s) LynxZError(s)
zError(int status)1108 static const char *zError(int status)
1109 {
1110 static char result[80];
1111
1112 sprintf(result, "zlib error %d", status);
1113 return result;
1114 }
1115 #endif
1116
1117 /* Push data from a deflate file pointer down a stream
1118 * -------------------------------------
1119 *
1120 * This routine is responsible for creating and PRESENTING any
1121 * graphic (or other) objects described by the file. The code is
1122 * loosely based on the inflate.c file from w3m.
1123 *
1124 *
1125 * State of file and target stream on entry:
1126 * FILE (zzfp) assumed open (should have deflated content),
1127 * target (sink) assumed valid.
1128 *
1129 * Return values:
1130 * HT_INTERRUPTED Interruption after some data read.
1131 * HT_PARTIAL_CONTENT Error after some data read.
1132 * -1 Error before any data read.
1133 * HT_LOADED Normal end of file indication on reading.
1134 *
1135 * State of file and target stream on return:
1136 * always zzfp still open, target stream still valid.
1137 */
HTZzFileCopy(FILE * zzfp,HTStream * sink)1138 static int HTZzFileCopy(FILE *zzfp, HTStream *sink)
1139 {
1140 static char dummy_head[1 + 1] =
1141 {
1142 0x8 + 0x7 * 0x10,
1143 (((0x8 + 0x7 * 0x10) * 0x100 + 30) / 31 * 31) & 0xFF,
1144 };
1145
1146 z_stream s;
1147 HTStreamClass targetClass;
1148 off_t bytes;
1149 int rv = HT_OK;
1150 char output_buffer[INPUT_BUFFER_SIZE];
1151 int status;
1152 int flush;
1153 int retry = 0;
1154 int len = 0;
1155
1156 /* Push the data down the stream
1157 */
1158 targetClass = *(sink->isa); /* Copy pointers to procedures */
1159
1160 s.zalloc = Z_NULL;
1161 s.zfree = Z_NULL;
1162 s.opaque = Z_NULL;
1163 status = inflateInit(&s);
1164 if (status != Z_OK) {
1165 CTRACE((tfp, "HTZzFileCopy inflateInit() %s\n", zError(status)));
1166 exit_immediately(EXIT_FAILURE);
1167 }
1168 s.avail_in = 0;
1169 s.next_out = (Bytef *) output_buffer;
1170 s.avail_out = sizeof(output_buffer);
1171 flush = Z_NO_FLUSH;
1172
1173 /* read and inflate deflate'd file, and push binary down sink
1174 */
1175 HTReadProgress(bytes = 0, (off_t) 0);
1176 for (;;) {
1177 if (s.avail_in == 0) {
1178 s.next_in = (Bytef *) input_buffer;
1179 s.avail_in = (uInt) fread(input_buffer,
1180 (size_t) 1,
1181 (size_t) INPUT_BUFFER_SIZE, zzfp);
1182 len = (int) s.avail_in;
1183 }
1184 status = inflate(&s, flush);
1185 if (status == Z_STREAM_END || status == Z_BUF_ERROR) {
1186 len = (int) sizeof(output_buffer) - (int) s.avail_out;
1187 if (len > 0) {
1188 (*targetClass.put_block) (sink, output_buffer, len);
1189 bytes += len;
1190 HTReadProgress(bytes, (off_t) -1);
1191 HTDisplayPartial();
1192 }
1193 rv = HT_LOADED;
1194 break;
1195 } else if (status == Z_DATA_ERROR && !retry++) {
1196 status = inflateReset(&s);
1197 if (status != Z_OK) {
1198 CTRACE((tfp, "HTZzFileCopy inflateReset() %s\n", zError(status)));
1199 rv = -1;
1200 break;
1201 }
1202 s.next_in = (Bytef *) dummy_head;
1203 s.avail_in = sizeof(dummy_head);
1204 (void) inflate(&s, flush);
1205 s.next_in = (Bytef *) input_buffer;
1206 s.avail_in = (unsigned) len;
1207 continue;
1208 } else if (status != Z_OK) {
1209 CTRACE((tfp, "HTZzFileCopy inflate() %s\n", zError(status)));
1210 rv = bytes ? HT_PARTIAL_CONTENT : -1;
1211 break;
1212 } else if (s.avail_out == 0) {
1213 len = sizeof(output_buffer);
1214 s.next_out = (Bytef *) output_buffer;
1215 s.avail_out = sizeof(output_buffer);
1216
1217 (*targetClass.put_block) (sink, output_buffer, len);
1218 bytes += len;
1219 HTReadProgress(bytes, (off_t) -1);
1220 HTDisplayPartial();
1221
1222 if (HTCheckForInterrupt()) {
1223 _HTProgress(TRANSFER_INTERRUPTED);
1224 rv = bytes ? HT_INTERRUPTED : -1;
1225 break;
1226 }
1227 }
1228 retry = 1;
1229 } /* next bufferload */
1230
1231 inflateEnd(&s);
1232 HTFinishDisplayPartial();
1233 return rv;
1234 }
1235 #endif /* USE_ZLIB */
1236
1237 #ifdef USE_BZLIB
1238 /* Push data from a bzip file pointer down a stream
1239 * -------------------------------------
1240 *
1241 * This routine is responsible for creating and PRESENTING any
1242 * graphic (or other) objects described by the file.
1243 *
1244 *
1245 * State of file and target stream on entry:
1246 * BZFILE (bzfp) assumed open (should have bzipped content),
1247 * target (sink) assumed valid.
1248 *
1249 * Return values:
1250 * HT_INTERRUPTED Interruption after some data read.
1251 * HT_PARTIAL_CONTENT Error after some data read.
1252 * -1 Error before any data read.
1253 * HT_LOADED Normal end of file indication on reading.
1254 *
1255 * State of file and target stream on return:
1256 * always bzfp still open, target stream still valid.
1257 */
HTBzFileCopy(BZFILE * bzfp,HTStream * sink)1258 static int HTBzFileCopy(BZFILE * bzfp, HTStream *sink)
1259 {
1260 HTStreamClass targetClass;
1261 int status;
1262 off_t bytes;
1263 int bzerrnum;
1264 int rv = HT_OK;
1265
1266 /* Push the data down the stream
1267 */
1268 targetClass = *(sink->isa); /* Copy pointers to procedures */
1269
1270 /* read and inflate bzip'd file, and push binary down sink
1271 */
1272 HTReadProgress(bytes = 0, (off_t) 0);
1273 for (;;) {
1274 status = BZ2_bzread(bzfp, input_buffer, INPUT_BUFFER_SIZE);
1275 if (status <= 0) { /* EOF or error */
1276 if (status == 0) {
1277 rv = HT_LOADED;
1278 break;
1279 }
1280 CTRACE((tfp, "HTBzFileCopy: Read error, bzread returns %d\n",
1281 status));
1282 CTRACE((tfp, "bzerror : %s\n",
1283 BZ2_bzerror(bzfp, &bzerrnum)));
1284 if (bytes) {
1285 rv = HT_PARTIAL_CONTENT;
1286 } else {
1287 rv = -1;
1288 }
1289 break;
1290 }
1291
1292 (*targetClass.put_block) (sink, input_buffer, status);
1293 bytes += status;
1294 HTReadProgress(bytes, (off_t) -1);
1295 HTDisplayPartial();
1296
1297 if (HTCheckForInterrupt()) {
1298 _HTProgress(TRANSFER_INTERRUPTED);
1299 rv = HT_INTERRUPTED;
1300 break;
1301 }
1302 } /* next bufferload */
1303
1304 HTFinishDisplayPartial();
1305 return rv;
1306 }
1307 #endif /* USE_BZLIB */
1308
1309 /* Push data from a socket down a stream STRIPPING CR
1310 * --------------------------------------------------
1311 *
1312 * This routine is responsible for creating and PRESENTING any
1313 * graphic (or other) objects described by the socket.
1314 *
1315 * The file number given is assumed to be a TELNET stream ie containing
1316 * CRLF at the end of lines which need to be stripped to LF for unix
1317 * when the format is textual.
1318 *
1319 */
HTCopyNoCR(HTParentAnchor * anchor GCC_UNUSED,int file_number,HTStream * sink)1320 void HTCopyNoCR(HTParentAnchor *anchor GCC_UNUSED,
1321 int file_number,
1322 HTStream *sink)
1323 {
1324 HTStreamClass targetClass;
1325 int character;
1326
1327 /* Push the data, ignoring CRLF, down the stream
1328 */
1329 targetClass = *(sink->isa); /* Copy pointers to procedures */
1330
1331 /*
1332 * Push text from telnet socket down sink
1333 *
1334 * @@@@@ To push strings could be faster? (especially is we cheat and
1335 * don't ignore CR! :-}
1336 */
1337 HTInitInput(file_number);
1338 for (;;) {
1339 character = HTGetCharacter();
1340 if (character == EOF)
1341 break;
1342 (*targetClass.put_character) (sink, (char) character);
1343 }
1344 }
1345
1346 /* Parse a socket given format and file number
1347 *
1348 * This routine is responsible for creating and PRESENTING any
1349 * graphic (or other) objects described by the file.
1350 *
1351 * The file number given is assumed to be a TELNET stream ie containing
1352 * CRLF at the end of lines which need to be stripped to LF for unix
1353 * when the format is textual.
1354 *
1355 * State of socket and target stream on entry:
1356 * socket (file_number) assumed open,
1357 * target (sink) usually NULL (will call stream stack).
1358 *
1359 * Return values:
1360 * HT_INTERRUPTED Interruption or error after some data received.
1361 * -501 Stream stack failed (cannot present or convert).
1362 * -2 Unexpected disconnect before any data received.
1363 * -1 Stream stack failed (cannot present or convert), or
1364 * Interruption or error before any data received, or
1365 * (UNIX) other read error before any data received, or
1366 * download cancelled.
1367 * HT_LOADED Normal close of socket (end of file indication
1368 * received), or
1369 * unexpected disconnect after some data received, or
1370 * other read error after some data received, or
1371 * (not UNIX) other read error before any data received.
1372 *
1373 * State of socket and target stream on return depends on return value:
1374 * HT_INTERRUPTED socket still open, target aborted.
1375 * -501 socket still open, target stream NULL.
1376 * -2 socket still open, target freed.
1377 * -1 socket still open, target stream aborted or NULL.
1378 * otherwise socket closed, target stream freed.
1379 */
HTParseSocket(HTFormat rep_in,HTFormat format_out,HTParentAnchor * anchor,int file_number,HTStream * sink)1380 int HTParseSocket(HTFormat rep_in,
1381 HTFormat format_out,
1382 HTParentAnchor *anchor,
1383 int file_number,
1384 HTStream *sink)
1385 {
1386 HTStream *stream;
1387 HTStreamClass targetClass;
1388 int rv;
1389
1390 stream = HTStreamStack(rep_in, format_out, sink, anchor);
1391
1392 if (!stream) {
1393 char *buffer = 0;
1394
1395 if (LYCancelDownload) {
1396 LYCancelDownload = FALSE;
1397 return -1;
1398 }
1399 HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
1400 HTAtom_name(rep_in), HTAtom_name(format_out));
1401 CTRACE((tfp, "HTFormat: %s\n", buffer));
1402 rv = HTLoadError(sink, 501, buffer); /* returns -501 */
1403 FREE(buffer);
1404 } else {
1405 /*
1406 * Push the data, don't worry about CRLF we can strip them later.
1407 */
1408 targetClass = *(stream->isa); /* Copy pointers to procedures */
1409 rv = HTCopy(anchor, file_number, NULL, stream);
1410 if (rv != -1 && rv != HT_INTERRUPTED)
1411 (*targetClass._free) (stream);
1412 }
1413 return rv;
1414 /* Originally: full: HT_LOADED; partial: HT_INTERRUPTED; no bytes: -1 */
1415 }
1416
1417 /* Parse a file given format and file pointer
1418 *
1419 * This routine is responsible for creating and PRESENTING any
1420 * graphic (or other) objects described by the file.
1421 *
1422 * The file number given is assumed to be a TELNET stream ie containing
1423 * CRLF at the end of lines which need to be stripped to \n for unix
1424 * when the format is textual.
1425 *
1426 * State of file and target stream on entry:
1427 * FILE* (fp) assumed open,
1428 * target (sink) usually NULL (will call stream stack).
1429 *
1430 * Return values:
1431 * -501 Stream stack failed (cannot present or convert).
1432 * -1 Download cancelled.
1433 * HT_NO_DATA Error before any data read.
1434 * HT_PARTIAL_CONTENT Interruption or error after some data read.
1435 * HT_LOADED Normal end of file indication on reading.
1436 *
1437 * State of file and target stream on return:
1438 * always fp still open; target freed, aborted, or NULL.
1439 */
HTParseFile(HTFormat rep_in,HTFormat format_out,HTParentAnchor * anchor,FILE * fp,HTStream * sink)1440 int HTParseFile(HTFormat rep_in,
1441 HTFormat format_out,
1442 HTParentAnchor *anchor,
1443 FILE *fp,
1444 HTStream *sink)
1445 {
1446 HTStream *stream;
1447 HTStreamClass targetClass;
1448 int rv;
1449 int result;
1450
1451 if (fp == NULL) {
1452 result = HT_LOADED;
1453 } else {
1454 stream = HTStreamStack(rep_in, format_out, sink, anchor);
1455
1456 if (!stream || !stream->isa) {
1457 char *buffer = 0;
1458
1459 if (LYCancelDownload) {
1460 LYCancelDownload = FALSE;
1461 result = -1;
1462 } else {
1463 HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
1464 HTAtom_name(rep_in), HTAtom_name(format_out));
1465 CTRACE((tfp, "HTFormat(in HTParseFile): %s\n", buffer));
1466 rv = HTLoadError(sink, 501, buffer);
1467 FREE(buffer);
1468 result = rv;
1469 }
1470 } else {
1471
1472 /*
1473 * Push the data down the stream
1474 *
1475 * @@ Bug: This decision ought to be made based on "encoding"
1476 * rather than on content-type. @@@ When we handle encoding. The
1477 * current method smells anyway.
1478 */
1479 targetClass = *(stream->isa); /* Copy pointers to procedures */
1480 rv = HTFileCopy(fp, stream);
1481 if (rv == -1 || rv == HT_INTERRUPTED) {
1482 (*targetClass._abort) (stream, NULL);
1483 } else {
1484 (*targetClass._free) (stream);
1485 }
1486
1487 if (rv == -1) {
1488 result = HT_NO_DATA;
1489 } else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) {
1490 result = HT_PARTIAL_CONTENT;
1491 } else {
1492 result = HT_LOADED;
1493 }
1494 }
1495 }
1496 return result;
1497 }
1498
1499 #ifdef USE_SOURCE_CACHE
1500 /* Parse a document in memory given format and memory block pointer
1501 *
1502 * This routine is responsible for creating and PRESENTING any
1503 * graphic (or other) objects described by the file.
1504 *
1505 * State of memory and target stream on entry:
1506 * HTChunk* (chunk) assumed valid,
1507 * target (sink) usually NULL (will call stream stack).
1508 *
1509 * Return values:
1510 * -501 Stream stack failed (cannot present or convert).
1511 * HT_LOADED All data sent.
1512 *
1513 * State of memory and target stream on return:
1514 * always chunk unchanged; target freed, aborted, or NULL.
1515 */
HTParseMem(HTFormat rep_in,HTFormat format_out,HTParentAnchor * anchor,HTChunk * chunk,HTStream * sink)1516 int HTParseMem(HTFormat rep_in,
1517 HTFormat format_out,
1518 HTParentAnchor *anchor,
1519 HTChunk *chunk,
1520 HTStream *sink)
1521 {
1522 HTStream *stream;
1523 HTStreamClass targetClass;
1524 int rv;
1525 int result;
1526
1527 stream = HTStreamStack(rep_in, format_out, sink, anchor);
1528 if (!stream || !stream->isa) {
1529 char *buffer = 0;
1530
1531 HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
1532 HTAtom_name(rep_in), HTAtom_name(format_out));
1533 CTRACE((tfp, "HTFormat(in HTParseMem): %s\n", buffer));
1534 rv = HTLoadError(sink, 501, buffer);
1535 FREE(buffer);
1536 result = rv;
1537 } else {
1538
1539 /* Push the data down the stream
1540 */
1541 targetClass = *(stream->isa);
1542 (void) HTMemCopy(chunk, stream);
1543 (*targetClass._free) (stream);
1544 result = HT_LOADED;
1545 }
1546 return result;
1547 }
1548 #endif
1549
1550 #ifdef USE_ZLIB
HTCloseGzFile(gzFile gzfp)1551 static int HTCloseGzFile(gzFile gzfp)
1552 {
1553 int gzres;
1554
1555 if (gzfp == NULL)
1556 return 0;
1557 gzres = gzclose(gzfp);
1558 if (TRACE) {
1559 if (gzres == Z_ERRNO) {
1560 perror("gzclose ");
1561 } else if (gzres != Z_OK) {
1562 CTRACE((tfp, "gzclose : error number %d\n", gzres));
1563 }
1564 }
1565 return (gzres);
1566 }
1567
1568 /* HTParseGzFile
1569 *
1570 * State of file and target stream on entry:
1571 * gzFile (gzfp) assumed open,
1572 * target (sink) usually NULL (will call stream stack).
1573 *
1574 * Return values:
1575 * -501 Stream stack failed (cannot present or convert).
1576 * -1 Download cancelled.
1577 * HT_NO_DATA Error before any data read.
1578 * HT_PARTIAL_CONTENT Interruption or error after some data read.
1579 * HT_LOADED Normal end of file indication on reading.
1580 *
1581 * State of file and target stream on return:
1582 * always gzfp closed; target freed, aborted, or NULL.
1583 */
HTParseGzFile(HTFormat rep_in,HTFormat format_out,HTParentAnchor * anchor,gzFile gzfp,HTStream * sink)1584 int HTParseGzFile(HTFormat rep_in,
1585 HTFormat format_out,
1586 HTParentAnchor *anchor,
1587 gzFile gzfp,
1588 HTStream *sink)
1589 {
1590 HTStream *stream;
1591 HTStreamClass targetClass;
1592 int rv;
1593 int result;
1594
1595 stream = HTStreamStack(rep_in, format_out, sink, anchor);
1596
1597 if (!stream || !stream->isa) {
1598 char *buffer = 0;
1599
1600 HTCloseGzFile(gzfp);
1601 if (LYCancelDownload) {
1602 LYCancelDownload = FALSE;
1603 result = -1;
1604 } else {
1605 HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
1606 HTAtom_name(rep_in), HTAtom_name(format_out));
1607 CTRACE((tfp, "HTFormat(in HTParseGzFile): %s\n", buffer));
1608 rv = HTLoadError(sink, 501, buffer);
1609 FREE(buffer);
1610 result = rv;
1611 }
1612 } else {
1613
1614 /*
1615 * Push the data down the stream
1616 *
1617 * @@ Bug: This decision ought to be made based on "encoding" rather than
1618 * on content-type. @@@ When we handle encoding. The current method
1619 * smells anyway.
1620 */
1621 targetClass = *(stream->isa); /* Copy pointers to procedures */
1622 rv = HTGzFileCopy(gzfp, stream);
1623 if (rv == -1 || rv == HT_INTERRUPTED) {
1624 (*targetClass._abort) (stream, NULL);
1625 } else {
1626 (*targetClass._free) (stream);
1627 }
1628
1629 HTCloseGzFile(gzfp);
1630 if (rv == -1) {
1631 result = HT_NO_DATA;
1632 } else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) {
1633 result = HT_PARTIAL_CONTENT;
1634 } else {
1635 result = HT_LOADED;
1636 }
1637 }
1638 return result;
1639 }
1640
1641 /* HTParseZzFile
1642 *
1643 * State of file and target stream on entry:
1644 * FILE (zzfp) assumed open,
1645 * target (sink) usually NULL (will call stream stack).
1646 *
1647 * Return values:
1648 * -501 Stream stack failed (cannot present or convert).
1649 * -1 Download cancelled.
1650 * HT_NO_DATA Error before any data read.
1651 * HT_PARTIAL_CONTENT Interruption or error after some data read.
1652 * HT_LOADED Normal end of file indication on reading.
1653 *
1654 * State of file and target stream on return:
1655 * always zzfp closed; target freed, aborted, or NULL.
1656 */
HTParseZzFile(HTFormat rep_in,HTFormat format_out,HTParentAnchor * anchor,FILE * zzfp,HTStream * sink)1657 int HTParseZzFile(HTFormat rep_in,
1658 HTFormat format_out,
1659 HTParentAnchor *anchor,
1660 FILE *zzfp,
1661 HTStream *sink)
1662 {
1663 HTStream *stream;
1664 HTStreamClass targetClass;
1665 int rv;
1666 int result;
1667
1668 stream = HTStreamStack(rep_in, format_out, sink, anchor);
1669
1670 if (!stream || !stream->isa) {
1671 char *buffer = 0;
1672
1673 fclose(zzfp);
1674 if (LYCancelDownload) {
1675 LYCancelDownload = FALSE;
1676 result = -1;
1677 } else {
1678 HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
1679 HTAtom_name(rep_in), HTAtom_name(format_out));
1680 CTRACE((tfp, "HTFormat(in HTParseGzFile): %s\n", buffer));
1681 rv = HTLoadError(sink, 501, buffer);
1682 FREE(buffer);
1683 result = rv;
1684 }
1685 } else {
1686
1687 /*
1688 * Push the data down the stream
1689 *
1690 * @@ Bug: This decision ought to be made based on "encoding" rather than
1691 * on content-type. @@@ When we handle encoding. The current method
1692 * smells anyway.
1693 */
1694 targetClass = *(stream->isa); /* Copy pointers to procedures */
1695 rv = HTZzFileCopy(zzfp, stream);
1696 if (rv == -1 || rv == HT_INTERRUPTED) {
1697 (*targetClass._abort) (stream, NULL);
1698 } else {
1699 (*targetClass._free) (stream);
1700 }
1701
1702 fclose(zzfp);
1703 if (rv == -1) {
1704 result = HT_NO_DATA;
1705 } else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) {
1706 result = HT_PARTIAL_CONTENT;
1707 } else {
1708 result = HT_LOADED;
1709 }
1710 }
1711 return result;
1712 }
1713 #endif /* USE_ZLIB */
1714
1715 #ifdef USE_BZLIB
HTCloseBzFile(BZFILE * bzfp)1716 static void HTCloseBzFile(BZFILE * bzfp)
1717 {
1718 if (bzfp)
1719 BZ2_bzclose(bzfp);
1720 }
1721
1722 /* HTParseBzFile
1723 *
1724 * State of file and target stream on entry:
1725 * bzFile (bzfp) assumed open,
1726 * target (sink) usually NULL (will call stream stack).
1727 *
1728 * Return values:
1729 * -501 Stream stack failed (cannot present or convert).
1730 * -1 Download cancelled.
1731 * HT_NO_DATA Error before any data read.
1732 * HT_PARTIAL_CONTENT Interruption or error after some data read.
1733 * HT_LOADED Normal end of file indication on reading.
1734 *
1735 * State of file and target stream on return:
1736 * always bzfp closed; target freed, aborted, or NULL.
1737 */
HTParseBzFile(HTFormat rep_in,HTFormat format_out,HTParentAnchor * anchor,BZFILE * bzfp,HTStream * sink)1738 int HTParseBzFile(HTFormat rep_in,
1739 HTFormat format_out,
1740 HTParentAnchor *anchor,
1741 BZFILE * bzfp,
1742 HTStream *sink)
1743 {
1744 HTStream *stream;
1745 HTStreamClass targetClass;
1746 int rv;
1747 int result;
1748
1749 stream = HTStreamStack(rep_in, format_out, sink, anchor);
1750
1751 if (!stream || !stream->isa) {
1752 char *buffer = 0;
1753
1754 HTCloseBzFile(bzfp);
1755 if (LYCancelDownload) {
1756 LYCancelDownload = FALSE;
1757 result = -1;
1758 } else {
1759 HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
1760 HTAtom_name(rep_in), HTAtom_name(format_out));
1761 CTRACE((tfp, "HTFormat(in HTParseBzFile): %s\n", buffer));
1762 rv = HTLoadError(sink, 501, buffer);
1763 FREE(buffer);
1764 result = rv;
1765 }
1766 } else {
1767
1768 /*
1769 * Push the data down the stream
1770 *
1771 * @@ Bug: This decision ought to be made based on "encoding" rather than
1772 * on content-type. @@@ When we handle encoding. The current method
1773 * smells anyway.
1774 */
1775 targetClass = *(stream->isa); /* Copy pointers to procedures */
1776 rv = HTBzFileCopy(bzfp, stream);
1777 if (rv == -1 || rv == HT_INTERRUPTED) {
1778 (*targetClass._abort) (stream, NULL);
1779 } else {
1780 (*targetClass._free) (stream);
1781 }
1782
1783 HTCloseBzFile(bzfp);
1784 if (rv == -1) {
1785 result = HT_NO_DATA;
1786 } else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) {
1787 result = HT_PARTIAL_CONTENT;
1788 } else {
1789 result = HT_LOADED;
1790 }
1791 }
1792 return result;
1793 }
1794 #endif /* USE_BZLIB */
1795
1796 /* Converter stream: Network Telnet to internal character text
1797 * -----------------------------------------------------------
1798 *
1799 * The input is assumed to be in ASCII, with lines delimited
1800 * by (13,10) pairs, These pairs are converted into (CR,LF)
1801 * pairs in the local representation. The (CR,LF) sequence
1802 * when found is changed to a '\n' character, the internal
1803 * C representation of a new line.
1804 */
1805
NetToText_put_character(HTStream * me,int net_char)1806 static void NetToText_put_character(HTStream *me, int net_char)
1807 {
1808 char c = (char) FROMASCII(net_char);
1809
1810 if (me->had_cr) {
1811 if (c == LF) {
1812 me->sink->isa->put_character(me->sink, '\n'); /* Newline */
1813 me->had_cr = NO;
1814 return;
1815 } else {
1816 me->sink->isa->put_character(me->sink, CR); /* leftover */
1817 }
1818 }
1819 me->had_cr = (BOOL) (c == CR);
1820 if (!me->had_cr)
1821 me->sink->isa->put_character(me->sink, c); /* normal */
1822 }
1823
NetToText_put_string(HTStream * me,const char * s)1824 static void NetToText_put_string(HTStream *me, const char *s)
1825 {
1826 const char *p;
1827
1828 for (p = s; *p; p++)
1829 NetToText_put_character(me, *p);
1830 }
1831
NetToText_put_block(HTStream * me,const char * s,int l)1832 static void NetToText_put_block(HTStream *me, const char *s, int l)
1833 {
1834 const char *p;
1835
1836 for (p = s; p < (s + l); p++)
1837 NetToText_put_character(me, *p);
1838 }
1839
NetToText_free(HTStream * me)1840 static void NetToText_free(HTStream *me)
1841 {
1842 (me->sink->isa->_free) (me->sink); /* Close rest of pipe */
1843 FREE(me);
1844 }
1845
NetToText_abort(HTStream * me,HTError e)1846 static void NetToText_abort(HTStream *me, HTError e)
1847 {
1848 me->sink->isa->_abort(me->sink, e); /* Abort rest of pipe */
1849 FREE(me);
1850 }
1851
1852 /* The class structure
1853 */
1854 static HTStreamClass NetToTextClass =
1855 {
1856 "NetToText",
1857 NetToText_free,
1858 NetToText_abort,
1859 NetToText_put_character,
1860 NetToText_put_string,
1861 NetToText_put_block
1862 };
1863
1864 /* The creation method
1865 */
HTNetToText(HTStream * sink)1866 HTStream *HTNetToText(HTStream *sink)
1867 {
1868 HTStream *me = typecalloc(HTStream);
1869
1870 if (me == NULL)
1871 outofmem(__FILE__, "NetToText");
1872
1873 assert(me != NULL);
1874
1875 me->isa = &NetToTextClass;
1876
1877 me->had_cr = NO;
1878 me->sink = sink;
1879 return me;
1880 }
1881
1882 static HTStream HTBaseStreamInstance; /* Made static */
1883
1884 /*
1885 * ERROR STREAM
1886 * ------------
1887 * There is only one error stream shared by anyone who wants a
1888 * generic error returned from all stream methods.
1889 */
HTErrorStream_put_character(HTStream * me GCC_UNUSED,int c GCC_UNUSED)1890 static void HTErrorStream_put_character(HTStream *me GCC_UNUSED, int c GCC_UNUSED)
1891 {
1892 LYCancelDownload = TRUE;
1893 }
1894
HTErrorStream_put_string(HTStream * me GCC_UNUSED,const char * s)1895 static void HTErrorStream_put_string(HTStream *me GCC_UNUSED, const char *s)
1896 {
1897 if (s && *s)
1898 LYCancelDownload = TRUE;
1899 }
1900
HTErrorStream_write(HTStream * me GCC_UNUSED,const char * s,int l)1901 static void HTErrorStream_write(HTStream *me GCC_UNUSED, const char *s, int l)
1902 {
1903 if (l && s)
1904 LYCancelDownload = TRUE;
1905 }
1906
HTErrorStream_free(HTStream * me GCC_UNUSED)1907 static void HTErrorStream_free(HTStream *me GCC_UNUSED)
1908 {
1909 return;
1910 }
1911
HTErrorStream_abort(HTStream * me GCC_UNUSED,HTError e GCC_UNUSED)1912 static void HTErrorStream_abort(HTStream *me GCC_UNUSED, HTError e GCC_UNUSED)
1913 {
1914 return;
1915 }
1916
1917 static const HTStreamClass HTErrorStreamClass =
1918 {
1919 "ErrorStream",
1920 HTErrorStream_free,
1921 HTErrorStream_abort,
1922 HTErrorStream_put_character,
1923 HTErrorStream_put_string,
1924 HTErrorStream_write
1925 };
1926
HTErrorStream(void)1927 HTStream *HTErrorStream(void)
1928 {
1929 CTRACE((tfp, "ErrorStream. Created\n"));
1930 HTBaseStreamInstance.isa = &HTErrorStreamClass; /* The rest is random */
1931 return &HTBaseStreamInstance;
1932 }
1933