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