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