1 /* $LynxId: LYGetFile.c,v 1.89 2013/05/01 00:21:02 tom Exp $ */
2 #include <HTUtils.h>
3 #include <HTTP.h>
4 #include <HTAnchor.h> /* Anchor class */
5 #include <HTAccess.h>
6 #include <HTParse.h>
7 #include <LYCurses.h>
8 #include <GridText.h>
9 #include <LYGlobalDefs.h>
10 #include <LYUtils.h>
11 #include <LYCharSets.h>
12 #include <LYCharUtils.h>
13 #include <HTAlert.h>
14 #include <LYSignal.h>
15 #include <LYGetFile.h>
16 #include <LYPrint.h>
17 #include <LYOptions.h>
18 #include <LYStrings.h>
19 #include <LYClean.h>
20 #include <LYDownload.h>
21 #include <LYNews.h>
22 #include <LYMail.h>
23 #include <LYKeymap.h>
24 #include <LYBookmark.h>
25 #include <LYMap.h>
26 #include <LYList.h>
27 #ifdef DIRED_SUPPORT
28 #include <LYLocal.h>
29 #endif /* DIRED_SUPPORT */
30 #include <LYReadCFG.h>
31 #include <LYHistory.h>
32 #include <LYPrettySrc.h>
33
34 #include <LYexit.h>
35 #include <LYLeaks.h>
36
37 static int fix_httplike_urls(DocInfo *doc, UrlTypes type);
38
39 #ifdef VMS
40 #define STRNADDRCOMP strncasecomp
41 #else
42 #define STRNADDRCOMP strncmp
43 #endif /* !VMS */
44
45 int HTNoDataOK = 0;
46
47 /*
48 * getfile is the main mechanism to load a new document (or a previously loaded
49 * one whose rendering is cached in a HText structure) from mainloop, nearly
50 * everything goes through it.
51 * It should return one of the values
52 * NORMAL - requested document loaded successfully, usually [always?]
53 * its rendering is available as HTMainText. It can be an
54 * HTTP error message page or similar, we make no
55 * distinction here.
56 * NOT_FOUND - requested document cannot be accessed, and the reason
57 * is a real error (as may be caused by an invalid link),
58 * not just that lynx disallows access because of some
59 * permission restrictions, and we have no error page
60 * to show for it either.
61 * NULLFILE - requested document not loaded into HTMainText, either
62 * some interactive protocol was requested (like telnet),
63 * or lynx does not allow access.
64 * The distinction between NOT_FOUND and NULLFILE is not very crucial, but
65 * getting it right prevents mainloop from exiting with the wrong message if it
66 * happens for the first file, and from logging (or not logging) errors
67 * inappropriately with -traversal, and from sending bogus error mail with
68 * MAIL_SYSTEM_ERROR_LOGGING:TRUE. - kw
69 */
getfile(DocInfo * doc,int * target)70 int getfile(DocInfo *doc, int *target)
71 {
72 UrlTypes url_type = NOT_A_URL_TYPE;
73 char *pound;
74 char *cp = NULL;
75 char *temp = NULL;
76 DocAddress WWWDoc; /* a WWW absolute doc address struct */
77
78 /*
79 * Reset LYCancelDownload to prevent unwanted delayed effect. - KW
80 */
81 if (LYCancelDownload) {
82 CTRACE((tfp, "getfile: resetting LYCancelDownload to FALSE\n"));
83 LYCancelDownload = FALSE;
84 }
85
86 /*
87 * Reset fake 'Z' to prevent unwanted delayed effect. - kw
88 */
89 LYFakeZap(NO);
90
91 /*
92 * Reset redirection counter to prevent bogus TOO_MANY_REDIRECTIONS in rare
93 * situations if the previous cycle got to the limit, but did not fail for
94 * that reason because the URL of the final location was handled specially,
95 * not via HTLoadAbsolute. - kw
96 */
97 redirection_attempts = 0;
98
99 Try_Redirected_URL:
100 /*
101 * Load the WWWDoc struct in case we need to use it.
102 */
103 WWWDoc.address = doc->address;
104 WWWDoc.post_data = doc->post_data;
105 WWWDoc.post_content_type = doc->post_content_type;
106 WWWDoc.bookmark = doc->bookmark;
107 WWWDoc.isHEAD = doc->isHEAD;
108 WWWDoc.safe = doc->safe;
109
110 /*
111 * Reset HTPermitRedir, it has done its job if it was set. - kw
112 */
113 HTPermitRedir = FALSE;
114
115 /*
116 * Reset WWW_Download_File just in case.
117 */
118 FREE(WWW_Download_File);
119
120 /*
121 * Reset redirect_post_content just in case.
122 */
123 redirect_post_content = FALSE;
124
125 /*
126 * This flag is a hack to allow us to pass on the fact that 'no data' may
127 * not really be an error although HTLoadAbsolute returned NO. There
128 * should be a better way... HT_NO_DATA should always mean 'not data but
129 * not an error', and be passed on to us as that, but current usage if
130 * HT_NO_DATA vs HT_NOT_LOADED has to be reviewed everywhere. Anyway, some
131 * protocol module can set it to say 'I really mean it', we have to reset
132 * it here. - kw
133 */
134 HTNoDataOK = 0;
135
136 CTRACE((tfp, "getfile: getting %s\n\n", doc->address));
137
138 /*
139 * Protect against denial of service attacks via the port 19 CHARGEN
140 * service, and block connections to the port 25 ESMTP service. Also
141 * reject any likely spoof attempts via wrap arounds at 65536. - FM
142 */
143 if ((temp = HTParse(doc->address, "", PARSE_HOST)) != NULL &&
144 strlen(temp) > 3) {
145 char *cp1;
146
147 if ((cp1 = strchr(temp, '@')) == NULL)
148 cp1 = temp;
149 if ((cp = strrchr(cp1, ':')) != NULL) {
150 long int value;
151
152 cp++;
153 if (sscanf(cp, "%ld", &value) == 1) {
154 if (value == 19 || value == 65555) {
155 HTAlert(PORT_NINETEEN_INVALID);
156 FREE(temp);
157 return (NULLFILE);
158 } else if (value == 25 || value == 65561) {
159 HTAlert(PORT_TWENTYFIVE_INVALID);
160 FREE(temp);
161 return (NULLFILE);
162 } else if (value > 65535 || value < 0) {
163 char *msg = 0;
164
165 HTSprintf0(&msg, PORT_INVALID, (unsigned long) value);
166 HTAlert(msg);
167 FREE(msg);
168 FREE(temp);
169 return (NULLFILE);
170 }
171 } else if (isdigit(UCH(*cp))) {
172 HTAlert(URL_PORT_BAD);
173 FREE(temp);
174 return (NULLFILE);
175 }
176 }
177 }
178 cp = NULL;
179 FREE(temp);
180
181 /*
182 * Check to see if this is a universal document ID that lib WWW wants to
183 * handle.
184 *
185 * Some special URLs we handle ourselves. :)
186 */
187 if ((url_type = is_url(doc->address)) != 0) {
188 if (LYValidate && !LYPermitURL) {
189 if (!(url_type == HTTP_URL_TYPE ||
190 url_type == HTTPS_URL_TYPE ||
191 url_type == LYNXHIST_URL_TYPE ||
192 url_type == LYNXKEYMAP_URL_TYPE ||
193 url_type == LYNXIMGMAP_URL_TYPE ||
194 url_type == LYNXCOOKIE_URL_TYPE ||
195 #ifdef USE_CACHEJAR
196 url_type == LYNXCACHE_URL_TYPE ||
197 #endif
198 url_type == LYNXMESSAGES_URL_TYPE ||
199 (url_type == LYNXOPTIONS_URL_TYPE &&
200 WWWDoc.post_data) ||
201 0 == STRNADDRCOMP(WWWDoc.address, helpfilepath,
202 strlen(helpfilepath)) ||
203 (lynxlistfile != NULL &&
204 0 == STRNADDRCOMP(WWWDoc.address, lynxlistfile,
205 strlen(lynxlistfile))) ||
206 (lynxlinksfile != NULL &&
207 0 == STRNADDRCOMP(WWWDoc.address, lynxlinksfile,
208 strlen(lynxlinksfile))) ||
209 (lynxjumpfile != NULL &&
210 0 == STRNADDRCOMP(WWWDoc.address, lynxjumpfile,
211 strlen(lynxjumpfile))))) {
212 HTUserMsg(NOT_HTTP_URL_OR_ACTION);
213 return (NULLFILE);
214 }
215 }
216 if (traversal) {
217 /*
218 * Only traverse http URLs.
219 */
220 if (url_type != HTTP_URL_TYPE &&
221 url_type != LYNXIMGMAP_URL_TYPE) {
222 return (NULLFILE);
223 }
224 } else if (check_realm && !LYPermitURL && !LYJumpFileURL) {
225 if (!(0 == StrNCmp(startrealm, WWWDoc.address,
226 strlen(startrealm)) ||
227 url_type == LYNXHIST_URL_TYPE ||
228 url_type == LYNXKEYMAP_URL_TYPE ||
229 url_type == LYNXIMGMAP_URL_TYPE ||
230 url_type == LYNXCOOKIE_URL_TYPE ||
231 #ifdef USE_CACHEJAR
232 url_type == LYNXCACHE_URL_TYPE ||
233 #endif
234 url_type == LYNXPRINT_URL_TYPE ||
235 url_type == LYNXOPTIONS_URL_TYPE ||
236 url_type == LYNXCFG_URL_TYPE ||
237 url_type == LYNXCOMPILE_OPTS_URL_TYPE ||
238 url_type == LYNXMESSAGES_URL_TYPE ||
239 url_type == LYNXDOWNLOAD_URL_TYPE ||
240 url_type == MAILTO_URL_TYPE ||
241 url_type == NEWSPOST_URL_TYPE ||
242 url_type == NEWSREPLY_URL_TYPE ||
243 url_type == SNEWSPOST_URL_TYPE ||
244 url_type == SNEWSREPLY_URL_TYPE ||
245 (!LYUserSpecifiedURL &&
246 (url_type == LYNXEXEC_URL_TYPE ||
247 url_type == LYNXPROG_URL_TYPE ||
248 url_type == LYNXCGI_URL_TYPE)) ||
249 (WWWDoc.bookmark != NULL &&
250 *WWWDoc.bookmark != '\0') ||
251 0 == STRNADDRCOMP(WWWDoc.address, helpfilepath,
252 strlen(helpfilepath)) ||
253 (lynxlistfile != NULL &&
254 0 == STRNADDRCOMP(WWWDoc.address, lynxlistfile,
255 strlen(lynxlistfile))) ||
256 (lynxjumpfile != NULL &&
257 0 == STRNADDRCOMP(WWWDoc.address, lynxjumpfile,
258 strlen(lynxjumpfile))))) {
259 HTUserMsg(NOT_IN_STARTING_REALM);
260 return (NULLFILE);
261 }
262 }
263 if (WWWDoc.post_data &&
264 url_type != HTTP_URL_TYPE &&
265 url_type != HTTPS_URL_TYPE &&
266 url_type != LYNXCGI_URL_TYPE &&
267 url_type != LYNXIMGMAP_URL_TYPE &&
268 url_type != GOPHER_URL_TYPE &&
269 url_type != CSO_URL_TYPE &&
270 url_type != PROXY_URL_TYPE &&
271 url_type != LYNXOPTIONS_URL_TYPE &&
272 !(url_type == FILE_URL_TYPE &&
273 (LYIsUIPage(WWWDoc.address, UIP_LIST_PAGE) ||
274 LYIsUIPage(WWWDoc.address, UIP_ADDRLIST_PAGE)))) {
275 CTRACE((tfp, "getfile: dropping post_data!\n"));
276 HTAlert(IGNORED_POST);
277 LYFreePostData(doc);
278 WWWDoc.post_data = NULL;
279 WWWDoc.post_content_type = NULL;
280 }
281 #ifdef SYSLOG_REQUESTED_URLS
282 LYSyslog(doc->address);
283 #endif
284 if (url_type == UNKNOWN_URL_TYPE ||
285 url_type == AFS_URL_TYPE ||
286 url_type == PROSPERO_URL_TYPE) {
287 HTAlert(UNSUPPORTED_URL_SCHEME);
288 return (NULLFILE);
289
290 } else if (url_type == DATA_URL_TYPE) {
291 HTAlert(UNSUPPORTED_DATA_URL);
292 return (NULLFILE);
293
294 } else if (url_type == LYNXPRINT_URL_TYPE) {
295 return (printfile(doc));
296
297 #ifndef NO_OPTION_FORMS
298 } else if (url_type == LYNXOPTIONS_URL_TYPE) {
299 /* proceed forms-based options menu */
300 return (postoptions(doc));
301 #endif
302
303 } else if (url_type == LYNXCFG_URL_TYPE &&
304 !no_lynxcfg_info) {
305 /* @@@ maybe we should generate a specific error message
306 if attempted but restricted. - kw */
307 /* show/change/reload lynx.cfg settings */
308 return (lynx_cfg_infopage(doc));
309
310 #if defined(HAVE_CONFIG_H) && !defined(NO_CONFIG_INFO)
311 } else if (url_type == LYNXCOMPILE_OPTS_URL_TYPE &&
312 !no_compileopts_info) {
313 /* @@@ maybe we should generate a specific error message
314 if attempted but restricted or not supported. - kw */
315 /* show compile-time settings */
316 return (lynx_compile_opts(doc));
317 #endif
318
319 #ifndef DISABLE_NEWS
320 } else if (url_type == NEWSPOST_URL_TYPE ||
321 url_type == NEWSREPLY_URL_TYPE ||
322 url_type == SNEWSPOST_URL_TYPE ||
323 url_type == SNEWSREPLY_URL_TYPE) {
324
325 if (no_newspost) {
326 HTUserMsg(NEWSPOSTING_DISABLED);
327 return (NULLFILE);
328 } else if (!news_ok && (url_type == NEWSPOST_URL_TYPE ||
329 url_type == NEWSREPLY_URL_TYPE)) {
330 HTUserMsg(NEWS_DISABLED);
331 return (NULLFILE);
332 } else {
333 HTLoadAbsolute(&WWWDoc);
334 return (NULLFILE);
335 }
336 #endif
337
338 } else if (url_type == LYNXDOWNLOAD_URL_TYPE) {
339 LYDownload(doc->address);
340 #ifdef VMS
341 if (LYDidRename) {
342 /*
343 * The temporary file was saved to disk via a rename(), so we
344 * can't access the temporary file again via the download menu.
345 * Clear the flag, and return NULLFILE to pop. - FM
346 */
347 LYDidRename = FALSE;
348 return (NULLFILE);
349 }
350 #endif /* VMS */
351 return (NORMAL);
352 } else if (url_type == LYNXDIRED_URL_TYPE) {
353 #ifdef DIRED_SUPPORT
354 if (!no_dired_support) {
355 local_dired(doc);
356 WWWDoc.address = doc->address;
357 WWWDoc.post_data = doc->post_data;
358 WWWDoc.post_content_type = doc->post_content_type;
359 WWWDoc.bookmark = doc->bookmark;
360 WWWDoc.isHEAD = doc->isHEAD;
361 WWWDoc.safe = doc->safe;
362
363 if (!HTLoadAbsolute(&WWWDoc)) {
364 return (NOT_FOUND);
365 }
366 return (NORMAL);
367 }
368 #endif /* DIRED_SUPPORT */
369 HTUserMsg(DIRED_DISABLED);
370 return (NULLFILE);
371 }
372
373 if (LYNoRefererHeader == FALSE &&
374 LYNoRefererForThis == FALSE) {
375 const char *ref_url = HTLoadedDocumentURL();
376
377 if (isLYNXIMGMAP(ref_url))
378 ref_url += LEN_LYNXIMGMAP;
379 if (no_filereferer == TRUE && isFILE_URL(ref_url)) {
380 LYNoRefererForThis = TRUE;
381 }
382 if (LYNoRefererForThis == FALSE &&
383 (cp = strchr(ref_url, '?')) != NULL &&
384 strchr(cp, '=') != NULL) {
385 /*
386 * Don't send a Referer header if the URL is the reply from a
387 * form with method GET, in case the content has personal data
388 * (e.g., a password or credit card number) which would become
389 * visible in logs. - FM
390 *
391 * Changed 1999-11-01 to be controlled by REFERER_WITH_QUERY
392 * option. - kw
393 */
394 if (LYRefererWithQuery == 'S') { /* SEND */
395 StrAllocCopy(LYRequestReferer, ref_url);
396 } else if (LYRefererWithQuery == 'P') { /* PARTIAL */
397 FREE(LYRequestReferer); /* just to be sure */
398 LYRequestReferer = HTParse(ref_url, "",
399 PARSE_ACCESS
400 | PARSE_HOST
401 | PARSE_STRICTPATH
402 | PARSE_PUNCTUATION);
403 } else { /* Everything else - don't send Referer */
404 LYNoRefererForThis = TRUE;
405 }
406 cp = NULL;
407 } else if (LYNoRefererForThis == FALSE) {
408 StrAllocCopy(LYRequestReferer, ref_url);
409 }
410 } else {
411 StrAllocCopy(LYRequestReferer, HTLoadedDocumentURL());
412 }
413 if (url_type == LYNXHIST_URL_TYPE) {
414 /*
415 * 'doc' will change to the new file if we had a successful
416 * LYpop_num(), and the return value will be FALSE if we had a
417 * cancel. - FM
418 */
419 if ((historytarget(doc) == FALSE) ||
420 !doc || !doc->address) {
421 return (NOT_FOUND);
422 }
423
424 /*
425 * We changed it so reload.
426 */
427 WWWDoc.address = doc->address;
428 WWWDoc.post_data = doc->post_data;
429 WWWDoc.post_content_type = doc->post_content_type;
430 WWWDoc.bookmark = doc->bookmark;
431 WWWDoc.isHEAD = doc->isHEAD;
432 WWWDoc.safe = doc->safe;
433 if (track_internal_links && doc->internal_link && !reloading) {
434 LYinternal_flag = TRUE;
435 }
436 #ifdef DIRED_SUPPORT
437 lynx_edit_mode = FALSE;
438 #endif /* DIRED_SUPPORT */
439 if (!HTLoadAbsolute(&WWWDoc)) {
440 return (NOT_FOUND);
441 }
442 return (NORMAL);
443
444 } else if (url_type == LYNXEXEC_URL_TYPE ||
445 url_type == LYNXPROG_URL_TYPE) {
446 #ifdef EXEC_LINKS
447 if (no_exec &&
448 !exec_ok(HTLoadedDocumentURL(),
449 doc->address + 9, ALWAYS_EXEC_PATH)) {
450 HTUserMsg(EXECUTION_DISABLED);
451 } else if (no_bookmark_exec &&
452 HTLoadedDocumentBookmark()) {
453 HTUserMsg(BOOKMARK_EXEC_DISABLED);
454 } else if (local_exec || (local_exec_on_local_files &&
455 exec_ok(HTLoadedDocumentURL(),
456 doc->address + 9, EXEC_PATH))) {
457
458 char *p = NULL;
459
460 /*
461 * Bug puts slash on end if none is in the string.
462 */
463 char *last_slash = strrchr(doc->address, '/');
464
465 if (last_slash - doc->address
466 == (int) strlen(doc->address) - 1)
467 doc->address[strlen(doc->address) - 1] = '\0';
468
469 /*
470 * Convert '~' to $HOME.
471 */
472 StrAllocCopy(p, doc->address);
473 LYTildeExpand(&p, TRUE);
474
475 /*
476 * Show URL before executing it.
477 */
478 HTInfoMsg(doc->address);
479 stop_curses();
480 /*
481 * Run the command.
482 */
483 if (strstr(p, "//") == p + 9)
484 LYSystem(p + 11);
485 else
486 LYSystem(p + 9);
487 FREE(p);
488
489 if (url_type != LYNXPROG_URL_TYPE) {
490 /*
491 * Make sure user gets to see screen output.
492 */
493 #ifndef VMS
494 signal(SIGINT, SIG_IGN);
495 #endif /* !VMS */
496 printf("\n%s", RETURN_TO_LYNX);
497 fflush(stdout);
498 (void) LYgetch();
499 #ifdef VMS
500 HadVMSInterrupt = FALSE;
501 #endif /* VMS */
502 }
503 if (!dump_output_immediately) {
504 start_curses();
505 LYAddVisitedLink(doc);
506 }
507
508 } else {
509 char *buf = 0;
510
511 HTSprintf0(&buf,
512 EXECUTION_DISABLED_FOR_FILE,
513 key_for_func(LYK_OPTIONS));
514 HTAlert(buf);
515 FREE(buf);
516 }
517 #else /* no exec_links */
518 HTUserMsg(EXECUTION_NOT_COMPILED);
519 #endif /* EXEC_LINKS */
520 return (NULLFILE);
521
522 } else if (url_type == MAILTO_URL_TYPE) {
523 if (no_mail) {
524 HTUserMsg(MAIL_DISABLED);
525 } else if (!dump_output_immediately) {
526 HTParentAnchor *tmpanchor = HTAnchor_findAddress(&WWWDoc);
527 const char *title;
528 char *tmptitle = NULL;
529
530 title = "";
531 if (HTAnchor_title(tmpanchor)) {
532 title = HTAnchor_title(tmpanchor);
533 } else if (HTMainAnchor && !LYUserSpecifiedURL) {
534 title = HTAnchor_subject(HTMainAnchor);
535 if (non_empty(title)) {
536 if (strncasecomp(title, "Re:", 3)) {
537 StrAllocCopy(tmptitle, "Re: ");
538 StrAllocCat(tmptitle, title);
539 title = tmptitle;
540 }
541 } else {
542 title = "";
543 }
544 }
545 cp = strchr(doc->address, ':') + 1;
546 reply_by_mail(cp,
547 ((HTMainAnchor && !LYUserSpecifiedURL)
548 ? (char *) HTMainAnchor->address
549 : (char *) doc->address),
550 title,
551 (HTMainAnchor && !LYUserSpecifiedURL)
552 ? HTMainAnchor->message_id
553 : NULL);
554 FREE(tmptitle);
555 }
556 return (NULLFILE);
557
558 /*
559 * From here on we could have a remote host, so check if that's
560 * allowed.
561 */
562 } else if (local_host_only &&
563 url_type != LYNXKEYMAP_URL_TYPE &&
564 url_type != LYNXIMGMAP_URL_TYPE &&
565 url_type != LYNXCOOKIE_URL_TYPE &&
566 url_type != LYNXMESSAGES_URL_TYPE &&
567 #ifdef USE_CACHEJAR
568 url_type != LYNXCACHE_URL_TYPE &&
569 #endif
570 url_type != LYNXCGI_URL_TYPE &&
571 !(url_type == NEWS_URL_TYPE &&
572 StrNCmp(doc->address, "news://", 7)) &&
573 !(LYisLocalHost(doc->address) ||
574 LYisLocalAlias(doc->address))) {
575 HTUserMsg(ACCESS_ONLY_LOCALHOST);
576 return (NULLFILE);
577
578 /*
579 * Disable www telnet access if not telnet_ok.
580 */
581 } else if (url_type == TELNET_URL_TYPE ||
582 url_type == TN3270_URL_TYPE ||
583 url_type == TELNET_GOPHER_URL_TYPE) {
584 char *proxy;
585
586 if (!telnet_ok) {
587 HTUserMsg(TELNET_DISABLED);
588 return (NULLFILE);
589 } else if (no_telnet_port && strchr(doc->address + 7, ':')) {
590 HTUserMsg(TELNET_PORT_SPECS_DISABLED);
591 return (NULLFILE);
592 /*
593 * Detect weird case where interactive protocol would be
594 * proxied, and to a non-interactive protocol at that.
595 */
596 } else if ((proxy = LYGetEnv(((url_type == TN3270_URL_TYPE)
597 ? "tn3270_proxy"
598 :
599 ((url_type == TELNET_GOPHER_URL_TYPE)
600 ? "gopher_proxy"
601 : "telnet_proxy")))) != NULL &&
602 !override_proxy(doc->address) &&
603 (!isTELNET_URL(proxy) &&
604 !isTN3270_URL(proxy) &&
605 !isRLOGIN_URL(proxy))) {
606 /* Do nothing, fall through to generic code - kw */
607 } else {
608 stop_curses();
609 HTLoadAbsolute(&WWWDoc);
610 if (!dump_output_immediately) {
611 start_curses();
612 fflush(stdout);
613 LYAddVisitedLink(doc);
614 }
615 return (NULLFILE);
616 }
617
618 /*
619 * Disable www news access if not news_ok.
620 */
621 #ifndef DISABLE_NEWS
622 } else if (!news_ok && (url_type == NEWS_URL_TYPE ||
623 url_type == NNTP_URL_TYPE)) {
624 HTUserMsg(NEWS_DISABLED);
625 return (NULLFILE);
626 #endif
627
628 } else if (url_type == RLOGIN_URL_TYPE) {
629 char *proxy;
630
631 if (!rlogin_ok) {
632 HTUserMsg(RLOGIN_DISABLED);
633 return (NULLFILE);
634 /*
635 * Detect weird case where interactive protocol would be
636 * proxied, and to a non-interactive protocol at that.
637 */
638 } else if ((proxy = LYGetEnv("rlogin_proxy")) != NULL &&
639 !override_proxy(doc->address) &&
640 (!isTELNET_URL(proxy) &&
641 !isTN3270_URL(proxy) &&
642 !isRLOGIN_URL(proxy))) {
643 /* Do nothing, fall through to generic code - kw */
644 } else {
645 stop_curses();
646 HTLoadAbsolute(&WWWDoc);
647 fflush(stdout);
648 if (!dump_output_immediately) {
649 start_curses();
650 LYAddVisitedLink(doc);
651 }
652 return (NULLFILE);
653 }
654
655 /*
656 * If it's a gopher index type and there isn't a search term
657 * already attached then do this. Otherwise just load it!
658 */
659 } else if (url_type == INDEX_GOPHER_URL_TYPE &&
660 strchr(doc->address, '?') == NULL) {
661 int status;
662
663 /*
664 * Make sure we don't have a gopher+ escaped tab instead of a
665 * gopher0 question mark delimiting the search term. - FM
666 */
667 if ((cp = strstr(doc->address, "%09")) != NULL) {
668 *cp = '\0';
669 StrAllocCopy(temp, doc->address);
670 cp += 3;
671 if (*cp && StrNCmp(cp, "%09", 3)) {
672 StrAllocCat(temp, "?");
673 StrAllocCat(temp, cp);
674 if ((cp = strstr(temp, "%09")) != NULL) {
675 *cp = '\0';
676 }
677 }
678 StrAllocCopy(doc->address, temp);
679 FREE(temp);
680 goto Try_Redirected_URL;
681 }
682 /*
683 * Load it because the do_www_search routine uses the base url of
684 * the currently loaded document :(
685 */
686 if (!HTLoadAbsolute(&WWWDoc)) {
687 return (NOT_FOUND);
688 }
689 status = do_www_search(doc);
690 if (status == NULLFILE) {
691 LYpop(doc);
692 WWWDoc.address = doc->address;
693 WWWDoc.post_data = doc->post_data;
694 WWWDoc.post_content_type = doc->post_content_type;
695 WWWDoc.bookmark = doc->bookmark;
696 WWWDoc.isHEAD = doc->isHEAD;
697 WWWDoc.safe = doc->safe;
698 status = HTLoadAbsolute(&WWWDoc);
699 #ifdef DIRED_SUPPORT
700 } else {
701 lynx_edit_mode = FALSE;
702 #endif /* DIRED_SUPPORT */
703 }
704 return (status);
705 }
706
707 if (!ftp_ok
708 && (url_type == FTP_URL_TYPE
709 || url_type == NCFTP_URL_TYPE)) {
710 HTUserMsg(FTP_DISABLED);
711 return (NULLFILE);
712 } else if (url_type == HTML_GOPHER_URL_TYPE) {
713 char *tmp = NULL;
714
715 /*
716 * If tuple's Path=GET%20/... convert to an http URL.
717 */
718 if ((cp = strchr(doc->address + 9, '/')) != NULL &&
719 0 == StrNCmp(++cp, "hGET%20/", 8)) {
720 StrAllocCopy(tmp, "http://");
721 CTRACE((tfp, "getfile: URL '%s'\n",
722 doc->address));
723 *cp = '\0';
724 StrAllocCat(tmp, doc->address + 9);
725 /*
726 * If the port is defaulted, it should stay 70.
727 */
728 if (strchr(tmp + 6, ':') == NULL) {
729 StrAllocCat(tmp, "70/");
730 tmp[strlen(tmp) - 4] = ':';
731 }
732 if (strlen(cp + 7) > 1)
733 StrAllocCat(tmp, cp + 8);
734 StrAllocCopy(doc->address, tmp);
735 CTRACE((tfp, " changed to '%s'\n",
736 doc->address));
737 FREE(tmp);
738 url_type = HTTP_URL_TYPE;
739 }
740 }
741
742 if (url_type == HTTP_URL_TYPE ||
743 url_type == HTTPS_URL_TYPE ||
744 url_type == FTP_URL_TYPE ||
745 url_type == NCFTP_URL_TYPE ||
746 url_type == CSO_URL_TYPE) {
747 fix_httplike_urls(doc, url_type);
748 }
749
750 WWWDoc.address = doc->address; /* possible reload */
751 #ifdef DIRED_SUPPORT
752 lynx_edit_mode = FALSE;
753 #endif /* DIRED_SUPPORT */
754
755 #ifndef DISABLE_BIBP
756 if (url_type == BIBP_URL_TYPE) {
757 char *bibpTmp = NULL;
758
759 if (!BibP_bibhost_checked)
760 LYCheckBibHost();
761 if (BibP_bibhost_available) {
762 StrAllocCopy(bibpTmp, BibP_bibhost);
763 } else if (HTMainAnchor && HTAnchor_citehost(HTMainAnchor)) {
764 StrAllocCopy(bibpTmp, HTAnchor_citehost(HTMainAnchor));
765 } else {
766 StrAllocCopy(bibpTmp, BibP_globalserver);
767 }
768 if (HTMainAnchor && HTAnchor_citehost(HTMainAnchor)) {
769 StrAllocCat(bibpTmp, "bibp1.0/resolve?citehost=");
770 StrAllocCat(bibpTmp, HTAnchor_citehost(HTMainAnchor));
771 StrAllocCat(bibpTmp, "&usin=");
772 } else {
773 StrAllocCat(bibpTmp, "bibp1.0/resolve?usin=");
774 }
775 StrAllocCat(bibpTmp, doc->address + 5); /* USIN after bibp: */
776 StrAllocCopy(doc->address, bibpTmp);
777 WWWDoc.address = doc->address;
778 FREE(bibpTmp);
779 }
780 #endif /* !DISABLE_BIBP */
781
782 if (url_type == FILE_URL_TYPE) {
783 /*
784 * If a file URL has a '~' as the lead character of its first
785 * symbolic element, convert the '~' to Home_Dir(), then append
786 * the rest of of path, if present, skipping "user" if "~user"
787 * was entered, simplifying, and eliminating any residual
788 * relative elements. - FM
789 */
790 LYTildeExpand(&(doc->address), TRUE);
791 WWWDoc.address = doc->address;
792 }
793 CTRACE_SLEEP(MessageSecs);
794 user_message(WWW_WAIT_MESSAGE, doc->address);
795
796 if (TRACE) {
797 #ifdef USE_SLANG
798 if (LYCursesON) {
799 LYaddstr("*\n");
800 LYrefresh();
801 }
802 #endif /* USE_SLANG */
803 CTRACE((tfp, "\n"));
804 }
805
806 if (!HTLoadAbsolute(&WWWDoc)) {
807 /*
808 * Check for redirection.
809 */
810 if (use_this_url_instead != NULL) {
811 if (!is_url(use_this_url_instead)) {
812 /*
813 * The server did not return a complete URL in its
814 * Location: header, probably due to a FORM or other
815 * CGI script written by someone who doesn't know that
816 * the http protocol requires that it be a complete
817 * URL, or using a server which does not treat such a
818 * redirect string from the script as an instruction to
819 * resolve it versus the initial request, check
820 * authentication with that URL, and then act on it
821 * without returning redirection to us. We'll violate
822 * the http protocol and resolve it ourselves using the
823 * URL of the original request as the BASE, rather than
824 * doing the RIGHT thing and returning an invalid
825 * address message. - FM
826 */
827 HTUserMsg(LOCATION_NOT_ABSOLUTE);
828 temp = HTParse(use_this_url_instead,
829 WWWDoc.address,
830 PARSE_ALL);
831 if (non_empty(temp)) {
832 StrAllocCopy(use_this_url_instead, temp);
833 }
834 FREE(temp);
835 }
836 url_type = is_url(use_this_url_instead);
837 if (!HTPermitRedir &&
838 (url_type == LYNXDOWNLOAD_URL_TYPE ||
839 url_type == LYNXEXEC_URL_TYPE ||
840 url_type == LYNXPROG_URL_TYPE ||
841 #ifdef DIRED_SUPPORT
842 url_type == LYNXDIRED_URL_TYPE ||
843 #endif /* DIRED_SUPPORT */
844 url_type == LYNXPRINT_URL_TYPE ||
845 url_type == LYNXOPTIONS_URL_TYPE ||
846 url_type == LYNXCFG_URL_TYPE ||
847 url_type == LYNXCOMPILE_OPTS_URL_TYPE ||
848 url_type == LYNXHIST_URL_TYPE ||
849 url_type == LYNXCOOKIE_URL_TYPE ||
850 #ifdef USE_CACHEJAR
851 url_type == LYNXCACHE_URL_TYPE ||
852 #endif
853 url_type == LYNXMESSAGES_URL_TYPE ||
854 (LYValidate &&
855 url_type != HTTP_URL_TYPE &&
856 url_type != HTTPS_URL_TYPE) ||
857 ((no_file_url || no_goto_file) &&
858 url_type == FILE_URL_TYPE) ||
859 (no_goto_lynxcgi &&
860 url_type == LYNXCGI_URL_TYPE) ||
861 #ifndef DISABLE_BIBP
862 (no_goto_bibp &&
863 url_type == BIBP_URL_TYPE) ||
864 #endif
865 (no_goto_cso &&
866 url_type == CSO_URL_TYPE) ||
867 (no_goto_finger &&
868 url_type == FINGER_URL_TYPE) ||
869 (no_goto_ftp &&
870 (url_type == FTP_URL_TYPE ||
871 url_type == NCFTP_URL_TYPE)) ||
872 (no_goto_gopher &&
873 url_type == GOPHER_URL_TYPE) ||
874 (no_goto_http &&
875 url_type == HTTP_URL_TYPE) ||
876 (no_goto_https &&
877 url_type == HTTPS_URL_TYPE) ||
878 (no_goto_mailto &&
879 url_type == MAILTO_URL_TYPE) ||
880 #ifndef DISABLE_NEWS
881 (no_goto_news &&
882 url_type == NEWS_URL_TYPE) ||
883 (no_goto_nntp &&
884 url_type == NNTP_URL_TYPE) ||
885 #endif
886 (no_goto_rlogin &&
887 url_type == RLOGIN_URL_TYPE) ||
888 #ifndef DISABLE_NEWS
889 (no_goto_snews &&
890 url_type == SNEWS_URL_TYPE) ||
891 #endif
892 (no_goto_telnet &&
893 url_type == TELNET_URL_TYPE) ||
894 (no_goto_tn3270 &&
895 url_type == TN3270_URL_TYPE) ||
896 (no_goto_wais &&
897 url_type == WAIS_URL_TYPE))) {
898 /*
899 * Some schemes are not acceptable from server
900 * redirections. - KW & FM
901 */
902 HTAlert(ILLEGAL_REDIRECTION_URL);
903 if (LYCursesON) {
904 HTUserMsg2(WWW_ILLEGAL_URL_MESSAGE,
905 use_this_url_instead);
906 } else {
907 fprintf(stderr,
908 WWW_ILLEGAL_URL_MESSAGE,
909 use_this_url_instead);
910 }
911 FREE(use_this_url_instead);
912 return (NULLFILE);
913 }
914 if ((pound = findPoundSelector(doc->address)) != NULL
915 && findPoundSelector(use_this_url_instead) == NULL) {
916 /*
917 * Our requested URL had a fragment associated with it,
918 * and the redirection URL doesn't, so we'll append the
919 * fragment associated with the original request. If
920 * it's bogus for the redirection URL, we'll be
921 * positioned at the top of that document, so there's
922 * no harm done. - FM
923 */
924 CTRACE((tfp,
925 "getfile: Adding fragment '%s' to redirection URL.\n",
926 pound));
927 StrAllocCat(use_this_url_instead, pound);
928 doc->link = -1;
929 }
930 CTRACE_SLEEP(MessageSecs);
931 HTUserMsg2(WWW_USING_MESSAGE, use_this_url_instead);
932 CTRACE((tfp, "\n"));
933 StrAllocCopy(doc->address,
934 use_this_url_instead);
935 FREE(use_this_url_instead);
936 if (redirect_post_content == FALSE) {
937 /*
938 * Freeing the content also yields a GET request. - FM
939 */
940 LYFreePostData(doc);
941 }
942 /*
943 * Go to top to check for URLs which get special handling
944 * and/or security checks in Lynx. - FM
945 */
946 goto Try_Redirected_URL;
947 }
948 if (HTNoDataOK) {
949 return (NULLFILE);
950 } else {
951 return (NOT_FOUND);
952 }
953 } else {
954
955 lynx_mode = NORMAL_LYNX_MODE;
956
957 /*
958 * Some URLs don't actually return a document; compare
959 * doc->address with the document that is actually loaded and
960 * return NULLFILE if not loaded. If www_search_result is not -1
961 * then this is a reference to a named anchor within the same
962 * document; do NOT return NULLFILE in that case.
963 */
964
965 /*
966 * Check for a #fragment selector.
967 */
968 pound = findPoundSelector(doc->address);
969
970 /*
971 * Check to see if there is a temp file waiting for us to
972 * download.
973 */
974 if (WWW_Download_File) {
975 HTParentAnchor *tmpanchor = HTAnchor_findAddress(&WWWDoc);
976 char *fname = NULL;
977
978 /*
979 * Check for a suggested filename from the
980 * Content-Disposition header. - FM
981 */
982 if (HTAnchor_SugFname(tmpanchor) != NULL) {
983 StrAllocCopy(fname, HTAnchor_SugFname(tmpanchor));
984 } else {
985 StrAllocCopy(fname, doc->address);
986 }
987 /*
988 * Check whether this is a compressed file, which we don't
989 * uncompress for downloads, and adjust any suffix
990 * appropriately. - FM
991 */
992 HTCheckFnameForCompression(&fname, tmpanchor, FALSE);
993
994 if (LYdownload_options(&fname,
995 WWW_Download_File) < 0) {
996 FREE(fname);
997 return (NOT_FOUND);
998 }
999 LYAddVisitedLink(doc);
1000 StrAllocCopy(doc->address, fname);
1001 FREE(fname);
1002 doc->internal_link = FALSE;
1003 WWWDoc.address = doc->address;
1004 LYFreePostData(doc);
1005 WWWDoc.post_data = NULL;
1006 WWWDoc.post_content_type = NULL;
1007 WWWDoc.bookmark = doc->bookmark = FALSE;
1008 WWWDoc.isHEAD = doc->isHEAD = FALSE;
1009 WWWDoc.safe = doc->safe = FALSE;
1010 HTOutputFormat = WWW_PRESENT;
1011 if (!HTLoadAbsolute(&WWWDoc)) {
1012 return (NOT_FOUND);
1013 } else {
1014 return (NORMAL);
1015 }
1016
1017 } else if (pound == NULL &&
1018 /*
1019 * HTAnchor hash-table searches are now case-sensitive
1020 * (hopefully, without anchor deletion problems), so this
1021 * is too. - FM
1022 */
1023 (strcmp(doc->address,
1024 HTLoadedDocumentURL()) ||
1025 /*
1026 * Also check the post_data elements. - FM
1027 */
1028 !BINEQ(doc->post_data,
1029 HTLoadedDocumentPost_data()) ||
1030 /*
1031 * Also check the isHEAD element. - FM
1032 */
1033 doc->isHEAD != HTLoadedDocumentIsHEAD())) {
1034 /*
1035 * Nothing needed to be shown.
1036 */
1037 LYAddVisitedLink(doc);
1038 return (NULLFILE);
1039
1040 } else {
1041 if (pound != NULL) {
1042 if (!HTMainText) { /* this should not happen... */
1043 return (NULLFILE); /* but it can. - kw */
1044 }
1045 /*
1046 * May set www_search_result.
1047 */
1048 if (HTFindPoundSelector(pound + 1)) {
1049 *target = www_search_result;
1050 doc->link = -1;
1051 }
1052 }
1053 return (NORMAL);
1054 }
1055 }
1056 } else {
1057 CTRACE_SLEEP(MessageSecs);
1058 HTUserMsg2(WWW_BAD_ADDR_MESSAGE, doc->address);
1059 CTRACE((tfp, "\n"));
1060 return (NULLFILE);
1061 }
1062 }
1063
1064 /*
1065 * Set source mode for the next retrieval via getfile or HTreparse_document.
1066 * mode == -1: force normal presentation
1067 * mode == 1: force source presentation
1068 * mode == 0: reset to normal if it was set to source
1069 * - kw
1070 */
srcmode_for_next_retrieval(int mode)1071 void srcmode_for_next_retrieval(int mode)
1072 {
1073 if (mode < 0) {
1074 HTOutputFormat = WWW_PRESENT;
1075 #ifdef USE_PRETTYSRC
1076 psrc_view = FALSE;
1077 #endif
1078
1079 } else if (mode == 0) {
1080 if (HTOutputFormat == WWW_SOURCE)
1081 HTOutputFormat = WWW_PRESENT;
1082 #ifdef USE_PRETTYSRC
1083 else if (LYpsrc)
1084 psrc_view = FALSE;
1085 #endif
1086
1087 } else {
1088 #ifdef USE_PRETTYSRC
1089 if (LYpsrc)
1090 psrc_view = TRUE;
1091 else
1092 HTOutputFormat = WWW_SOURCE;
1093 #else
1094 HTOutputFormat = WWW_SOURCE;
1095 #endif
1096 }
1097 }
1098
1099 /*
1100 * The user wants to select a link or page by number.
1101 *
1102 * If follow_link_number returns DO_LINK_STUFF do_link will be run immediately
1103 * following its execution.
1104 *
1105 * If follow_link_number returns DO_GOTOLINK_STUFF it has updated the passed in
1106 * doc for positioning on a link.
1107 *
1108 * If follow_link_number returns DO_GOTOPAGE_STUFF it has set doc->line to the
1109 * top line of the desired page for displaying that page.
1110 *
1111 * If follow_link_number returns PRINT_ERROR an error message will be given to
1112 * the user.
1113 *
1114 * If follow_link_number returns DO_FORMS_STUFF some forms stuff will be done.
1115 * (Not yet implemented.)
1116 *
1117 * If follow_link_number returns DO_NOTHING nothing special will run after it.
1118 */
follow_link_number(int c,int cur,DocInfo * doc,int * num)1119 int follow_link_number(int c,
1120 int cur,
1121 DocInfo *doc,
1122 int *num)
1123 {
1124 bstring *temp = NULL;
1125 char *p;
1126 int rel = 0;
1127 int new_top, new_link;
1128 BOOL want_go;
1129 int curline = *num; /* passed in from mainloop() */
1130 int code;
1131
1132 CTRACE((tfp, "follow_link_number(%d,%d,...)\n", c, cur));
1133 BStrCopy0(temp, "?");
1134 temp->str[0] = (char) c;
1135 *num = -1;
1136 _statusline(FOLLOW_LINK_NUMBER);
1137
1138 /*
1139 * Get the number, possibly with a letter suffix, from the user.
1140 */
1141 if (LYgetBString(&temp, VISIBLE, sizeof(temp), NORECALL) < 0 ||
1142 isBEmpty(temp)) {
1143 HTInfoMsg(CANCELLED);
1144 return (DO_NOTHING);
1145 }
1146
1147 p = temp->str;
1148 *num = atoi(p);
1149 while (isdigit(UCH(*p)))
1150 ++p;
1151 c = *p; /* reuse c; 0 or g or p or + or - */
1152 switch (c) {
1153 case '+':
1154 case '-':
1155 /* 123+ or 123- */
1156 rel = c;
1157 c = *++p;
1158 break;
1159 default:
1160 rel = *++p;
1161 break;
1162 case 0:
1163 break;
1164 }
1165 /* don't currently check for errors typing suffix */
1166
1167 CTRACE((tfp, " temp=%s, *num=%d, rel='%c'\n", temp->str, *num, rel));
1168 /*
1169 * Check if we had a 'p' or 'P' following the number as a flag for
1170 * displaying the page with that number. - FM
1171 */
1172 if ((c == 'p' || c == 'P') && display_lines == 0) {
1173 CTRACE((tfp, " curline=%d, LYlines=%d, display too small!\n",
1174 curline, LYlines));
1175 code = PRINT_ERROR;
1176 } else if (c == 'p' || c == 'P') {
1177 int nlines = HText_getNumOfLines();
1178 int npages = (((nlines + 1) > display_lines)
1179 ? (((nlines + 1) + (display_lines - 1)) / (display_lines))
1180 : 1);
1181 int curpage = (((curline + 1) > display_lines)
1182 ? (((curline + 1) + (display_lines - 1)) / (display_lines))
1183 : 1);
1184
1185 CTRACE((tfp, " nlines=%d, npages=%d, curline=%d, curpage=%d\n",
1186 nlines, npages, curline, curpage));
1187 if (*num < 1)
1188 *num = rel ? 0 : 1;
1189 if (rel == '+')
1190 *num = curpage + *num;
1191 else if (rel == '-')
1192 *num = curpage - *num;
1193 doc->line = ((npages <= 1)
1194 ? 1
1195 : ((*num <= npages)
1196 ? (((*num - 1) * display_lines) + 1)
1197 : (((npages - 1) * display_lines) + 1)));
1198 code = DO_GOTOPAGE_STUFF;
1199 } else {
1200
1201 /*
1202 * Check if we want to make the link corresponding to the number the
1203 * current link, rather than ACTIVATE-ing it.
1204 */
1205 want_go = (BOOL) (c == 'g' || c == 'G');
1206
1207 /* If rel, add or subtract num from current link, or
1208 * nearest previous/subsequent link if current link is not on screen.
1209 */
1210 if (rel)
1211 *num = HTGetRelLinkNum(*num, rel, cur);
1212 /*
1213 * If we have a valid number, act on it.
1214 */
1215 if (*num > 0) {
1216 int info;
1217 char *text = NULL;
1218
1219 /*
1220 * Get the lname, and hightext, directly from www structures and
1221 * add it to the cur link so that we can pass it transparently on
1222 * to getfile(), and load new_top and new_link if we instead want
1223 * to make the link number current. These things are done so that
1224 * a link can be selected anywhere in the current document, whether
1225 * it is displayed on the screen or not!
1226 */
1227 info = HTGetLinkInfo(*num,
1228 want_go,
1229 &new_top,
1230 &new_link,
1231 &text,
1232 &links[cur].lname);
1233 if (text != NULL)
1234 LYSetHilite(cur, text);
1235
1236 if (info == WWW_INTERN_LINK_TYPE) {
1237 links[cur].type = WWW_INTERN_LINK_TYPE;
1238 code = DO_LINK_STUFF;
1239 } else if (info == LINK_LINE_FOUND) {
1240 doc->line = new_top + 1;
1241 doc->link = new_link;
1242 code = DO_GOTOLINK_STUFF;
1243 } else if (info) {
1244 links[cur].type = WWW_LINK_TYPE;
1245 code = DO_LINK_STUFF;
1246 } else {
1247 code = PRINT_ERROR;
1248 }
1249 } else {
1250 code = PRINT_ERROR;
1251 }
1252 }
1253 BStrFree(temp);
1254 return code;
1255 }
1256
1257 #if defined(EXEC_LINKS) || defined(LYNXCGI_LINKS)
1258
1259 struct trust {
1260 char *src;
1261 char *path;
1262 int type;
1263 struct trust *next;
1264 };
1265
1266 static struct trust *trusted_exec = 0;
1267 static struct trust *always_trusted_exec;
1268 static struct trust *trusted_cgi = 0;
1269
new_trust(const char * src,const char * path,int type)1270 static struct trust *new_trust(const char *src, const char *path, int type)
1271 {
1272 struct trust *tp;
1273
1274 tp = typecalloc(struct trust);
1275
1276 if (tp == NULL)
1277 outofmem(__FILE__, "new_trust");
1278
1279 assert(tp != NULL);
1280
1281 tp->type = type;
1282 StrAllocCopy(tp->src, src);
1283 StrAllocCopy(tp->path, path);
1284
1285 return tp;
1286 }
1287
get_trust(struct trust ** table,const char * src,int type)1288 static struct trust *get_trust(struct trust **table, const char *src, int type)
1289 {
1290 if (*table == 0) {
1291 *table = new_trust(src, "", type);
1292 }
1293 return *table;
1294 }
1295
1296 #ifdef LY_FIND_LEAKS
free_data(struct trust * cur)1297 static void free_data(struct trust *cur)
1298 {
1299 struct trust *next;
1300
1301 cur = trusted_exec;
1302 while (cur) {
1303 FREE(cur->src);
1304 FREE(cur->path);
1305 next = cur->next;
1306 FREE(cur);
1307 cur = next;
1308 }
1309 }
1310
LYTrusted_free(void)1311 static void LYTrusted_free(void)
1312 {
1313 free_data(trusted_exec);
1314 free_data(always_trusted_exec);
1315 free_data(trusted_cgi);
1316
1317 return;
1318 }
1319 #endif /* LY_FIND_LEAKS */
1320
add_trusted(char * str,int type)1321 void add_trusted(char *str,
1322 int type)
1323 {
1324 struct trust *tp;
1325 char *path;
1326 char *src = str;
1327 const char *after_tab;
1328 int Type = type;
1329 static BOOLEAN first = TRUE;
1330
1331 if (!src)
1332 return;
1333 if (first) {
1334 #ifdef LY_FIND_LEAKS
1335 atexit(LYTrusted_free);
1336 #endif
1337 first = FALSE;
1338 }
1339
1340 path = strchr(src, '\t');
1341 if (path) {
1342 *path++ = '\0';
1343 after_tab = path;
1344 } else {
1345 after_tab = "";
1346 }
1347
1348 tp = new_trust(src, after_tab, Type);
1349
1350 if (Type == EXEC_PATH) {
1351 tp->next = trusted_exec;
1352 trusted_exec = tp;
1353 } else if (Type == ALWAYS_EXEC_PATH) {
1354 tp->next = always_trusted_exec;
1355 always_trusted_exec = tp;
1356 } else if (Type == CGI_PATH) {
1357 tp->next = trusted_cgi;
1358 trusted_cgi = tp;
1359 }
1360 }
1361
1362 /*
1363 * Check to see if the supplied paths is allowed to be executed.
1364 */
exec_ok(const char * source,const char * linktext,int type)1365 BOOLEAN exec_ok(const char *source,
1366 const char *linktext,
1367 int type)
1368 {
1369 struct trust *tp;
1370 const char *cp;
1371 const char *allowed_extra_chars;
1372 int Type = type;
1373
1374 /*
1375 * Always OK if it is a jump file shortcut.
1376 */
1377 if (LYJumpFileURL)
1378 return TRUE;
1379
1380 /*
1381 * Choose the trust structure based on the type.
1382 */
1383 if (Type == EXEC_PATH) {
1384 tp = get_trust(&trusted_exec, "file://localhost/", EXEC_PATH);
1385 } else if (Type == ALWAYS_EXEC_PATH) {
1386 tp = get_trust(&always_trusted_exec, "none", ALWAYS_EXEC_PATH);
1387 } else if (Type == CGI_PATH) {
1388 tp = get_trust(&trusted_cgi, "none", CGI_PATH);
1389 } else {
1390 HTAlert(MALFORMED_EXEC_REQUEST);
1391 return FALSE;
1392 }
1393
1394 #ifdef VMS
1395 /*
1396 * Security: reject on relative path.
1397 */
1398 if ((cp = strchr(linktext, '[')) != NULL) {
1399 char *cp1;
1400
1401 if (((cp1 = strchr(cp, '-')) != NULL) &&
1402 strchr(cp1, ']') != NULL) {
1403 while (cp1[1] == '-')
1404 cp1++;
1405 if (cp1[1] == ']' ||
1406 cp1[1] == '.') {
1407 HTAlert(RELPATH_IN_EXEC_LINK);
1408 return FALSE;
1409 }
1410 }
1411 }
1412 #else
1413 /*
1414 * Security: reject on relative path.
1415 */
1416 if (strstr(linktext, "../") != NULL) {
1417 HTAlert(RELPATH_IN_EXEC_LINK);
1418 return FALSE;
1419 }
1420
1421 /*
1422 * Security: reject on strange character.
1423 */
1424 if (Type == CGI_PATH)
1425 allowed_extra_chars = " _-:./@~$&+=\t";
1426 else
1427 allowed_extra_chars = " _-:./@~$+=\t";
1428 for (cp = linktext; *cp != '\0'; cp++) {
1429 if (!isalnum(UCH(*cp)) && !strchr(allowed_extra_chars, *cp)) {
1430 char *buf = 0;
1431
1432 HTSprintf0(&buf,
1433 BADCHAR_IN_EXEC_LINK,
1434 *cp);
1435 HTAlert(buf);
1436 FREE(buf);
1437 return FALSE;
1438 }
1439 }
1440 #endif /* VMS */
1441
1442 check_tp_for_entry:
1443 while (tp) {
1444 if (tp->type == Type) {
1445 char const *command = linktext;
1446
1447 if (strstr(command, "//") == linktext) {
1448 command += 2;
1449 }
1450 CTRACE((tfp, "comparing source\n\t'%s'\n\t'%s'\n", source, tp->src));
1451 CTRACE((tfp, "comparing command\n\t'%s'\n\t'%s'\n", command, tp->path));
1452 if (STRNADDRCOMP(source, tp->src, strlen(tp->src)) == 0 &&
1453 STRNADDRCOMP(command, tp->path, strlen(tp->path)) == 0)
1454 return TRUE;
1455 }
1456 tp = tp->next;
1457 }
1458 if (Type == EXEC_PATH &&
1459 always_trusted_exec->next != 0) {
1460 Type = ALWAYS_EXEC_PATH;
1461 tp = always_trusted_exec;
1462 goto check_tp_for_entry;
1463 }
1464 if (!(no_exec && type == ALWAYS_EXEC_PATH))
1465 HTAlert(BADLOCPATH_IN_EXEC_LINK);
1466 return FALSE;
1467 }
1468 #endif /* EXEC_LINKS || LYNXCGI_LINKS */
1469
fix_httplike_urls(DocInfo * doc,UrlTypes type)1470 static int fix_httplike_urls(DocInfo *doc, UrlTypes type)
1471 {
1472 char *slash;
1473
1474 /*
1475 * If there's a fragment present, our simplistic methods won't work. - kw
1476 */
1477 if (findPoundSelector(doc->address) != NULL)
1478 return 0;
1479
1480 #ifndef DISABLE_FTP
1481 /*
1482 * If it's an ftp URL with a trailing slash, trim it off.
1483 */
1484 if (type == FTP_URL_TYPE &&
1485 LYIsHtmlSep(doc->address[strlen(doc->address) - 1])) {
1486 char *path = HTParse(doc->address, "", PARSE_PATH | PARSE_PUNCTUATION);
1487
1488 /*
1489 * If the path is a lone slash, we're done. - FM
1490 */
1491 if (path) {
1492 if (LYIsHtmlSep(path[0]) && path[1] == '\0') {
1493 FREE(path);
1494 return 0;
1495 }
1496 FREE(path);
1497 }
1498
1499 /*
1500 * If we're proxying ftp, don't trim anything. - KW
1501 */
1502 if ((LYGetEnv("ftp_proxy") != NULL) &&
1503 !override_proxy(doc->address))
1504 return 0;
1505
1506 /*
1507 * If we get to here, trim the trailing slash. - FM
1508 */
1509 CTRACE((tfp, "fix_httplike_urls: URL '%s'\n", doc->address));
1510 LYTrimHtmlSep(doc->address);
1511 CTRACE((tfp, " changed to '%s'\n", doc->address));
1512 CTRACE_SLEEP(MessageSecs);
1513 } else if (type == NCFTP_URL_TYPE) {
1514 char *path = NULL;
1515 char *first = doc->address;
1516 char *second = strchr(first, ':');
1517
1518 CTRACE((tfp, "fix_httplike_urls: URL '%s'\n", doc->address));
1519 if (second == 0)
1520 second = first + strlen(first);
1521 else
1522 *second++ = '\0';
1523 HTSprintf0(&path, "%s//%s%s", STR_FTP_URL, first, second);
1524 FREE(doc->address);
1525 doc->address = path;
1526
1527 CTRACE((tfp, " changed to '%s'\n", doc->address));
1528 CTRACE_SLEEP(MessageSecs);
1529 }
1530 #endif /* DISABLE_FTP */
1531
1532 /*
1533 * If there isn't a slash besides the two at the beginning, append one.
1534 */
1535 if ((slash = strrchr(doc->address, '/')) != NULL) {
1536 if (!LYIsHtmlSep(*(slash - 1)) || *(slash - 2) != ':') {
1537 return (0);
1538 }
1539 if (type == HTTP_URL_TYPE ||
1540 type == HTTPS_URL_TYPE) {
1541 if ((slash - 2) != strchr(doc->address, ':')) {
1542 /*
1543 * Turns out we were not looking at the right slash after all,
1544 * there must have been more than one "://" which is valid at
1545 * least for http URLs (later occurrences can be part of a
1546 * query string, for example), so leave this alone, too. - kw
1547 */
1548 return (0);
1549 }
1550 if (strchr(doc->address, '?')) {
1551 /*
1552 * If there is a question mark that appears to be part of the
1553 * hostname, don't append anything either. Leave it to HTParse
1554 * to interpret the question mark as ending the hostname. - kw
1555 */
1556 return (0);
1557 }
1558 }
1559 }
1560 CTRACE((tfp, "fix_httplike_urls: URL '%s'\n", doc->address));
1561 LYAddHtmlSep(&(doc->address));
1562 CTRACE((tfp, " changed to '%s'\n", doc->address));
1563 CTRACE_SLEEP(MessageSecs);
1564
1565 return (1);
1566 }
1567