1 /*
2  * $LynxId: HTAlert.c,v 1.99 2013/05/03 08:41:08 tom Exp $
3  *
4  *	Displaying messages and getting input for Lynx Browser
5  *	==========================================================
6  *
7  *	REPLACE THIS MODULE with a GUI version in a GUI environment!
8  *
9  * History:
10  *	   Jun 92 Created May 1992 By C.T. Barker
11  *	   Feb 93 Simplified, portablised TBL
12  *
13  */
14 
15 #include <HTUtils.h>
16 #include <HTAlert.h>
17 #include <LYGlobalDefs.h>
18 #include <LYCurses.h>
19 #include <LYStrings.h>
20 #include <LYUtils.h>
21 #include <LYClean.h>
22 #include <GridText.h>
23 #include <LYCookie.h>
24 #include <LYHistory.h>		/* store statusline messages */
25 
26 #include <LYLeaks.h>
27 
28 #include <HTParse.h>
29 
30 #undef timezone			/* U/Win defines this in time.h, hides implementation detail */
31 
32 #if defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H)
33 #include <sys/timeb.h>
34 #endif
35 
36 /*
37  * 'napms()' is preferable to 'sleep()' in any case because it does not
38  * interfere with output, but also because it can be less than a second.
39  */
40 #ifdef HAVE_NAPMS
41 #define LYSleep(n) napms(n)
42 #else
43 #define LYSleep(n) sleep((unsigned)n)
44 #endif
45 
46 /*	Issue a message about a problem.		HTAlert()
47  *	--------------------------------
48  */
HTAlert(const char * Msg)49 void HTAlert(const char *Msg)
50 {
51     CTRACE((tfp, "\nAlert!: %s\n\n", Msg));
52     CTRACE_FLUSH(tfp);
53     _user_message(ALERT_FORMAT, Msg);
54     LYstore_message2(ALERT_FORMAT, Msg);
55 
56     if (dump_output_immediately && dump_to_stderr) {
57 	fflush(stdout);
58 	fprintf(stderr, ALERT_FORMAT, Msg);
59 	fputc('\n', stderr);
60 	fflush(stderr);
61     }
62 
63     LYSleepAlert();
64 }
65 
HTAlwaysAlert(const char * extra_prefix,const char * Msg)66 void HTAlwaysAlert(const char *extra_prefix,
67 		   const char *Msg)
68 {
69     if (!dump_output_immediately && LYCursesON) {
70 	HTAlert(Msg);
71     } else {
72 	if (extra_prefix) {
73 	    fprintf(((TRACE) ? stdout : stderr),
74 		    "%s %s!\n",
75 		    extra_prefix, Msg);
76 	    fflush(stdout);
77 	    LYstore_message2(ALERT_FORMAT, Msg);
78 	    LYSleepAlert();
79 	} else {
80 	    fprintf(((TRACE) ? stdout : stderr), ALERT_FORMAT, NonNull(Msg));
81 	    fflush(stdout);
82 	    LYstore_message2(ALERT_FORMAT, Msg);
83 	    LYSleepAlert();
84 	    fprintf(((TRACE) ? stdout : stderr), "\n");
85 	}
86 	CTRACE((tfp, "\nAlert!: %s\n\n", Msg));
87 	CTRACE_FLUSH(tfp);
88     }
89 }
90 
91 /*	Issue an informational message.			HTInfoMsg()
92  *	--------------------------------
93  */
HTInfoMsg(const char * Msg)94 void HTInfoMsg(const char *Msg)
95 {
96     _statusline(Msg);
97     if (non_empty(Msg)) {
98 	CTRACE((tfp, "Info message: %s\n", Msg));
99 	LYstore_message(Msg);
100 	LYSleepInfo();
101     }
102 }
103 
HTInfoMsg2(const char * Msg2,const char * Arg)104 void HTInfoMsg2(const char *Msg2, const char *Arg)
105 {
106     _user_message(Msg2, Arg);
107     if (non_empty(Msg2)) {
108 	CTRACE((tfp, "Info message: "));
109 	CTRACE((tfp, Msg2, Arg));
110 	CTRACE((tfp, "\n"));
111 	LYstore_message2(Msg2, Arg);
112 	LYSleepInfo();
113     }
114 }
115 
116 /*	Issue an important message.			HTUserMsg()
117  *	--------------------------------
118  */
HTUserMsg(const char * Msg)119 void HTUserMsg(const char *Msg)
120 {
121     _statusline(Msg);
122     if (non_empty(Msg)) {
123 	CTRACE((tfp, "User message: %s\n", Msg));
124 	LYstore_message(Msg);
125 #if !(defined(USE_SLANG) || defined(WIDEC_CURSES))
126 	if (IS_CJK_TTY) {
127 	    clearok(curscr, TRUE);
128 	    LYrefresh();
129 	}
130 #endif
131 	LYSleepMsg();
132     }
133 }
134 
HTUserMsg2(const char * Msg2,const char * Arg)135 void HTUserMsg2(const char *Msg2, const char *Arg)
136 {
137     _user_message(Msg2, Arg);
138     if (non_empty(Msg2)) {
139 	CTRACE((tfp, "User message: "));
140 	CTRACE((tfp, Msg2, Arg));
141 	CTRACE((tfp, "\n"));
142 	LYstore_message2(Msg2, Arg);
143 	LYSleepMsg();
144     }
145 }
146 
147 /*	Issue a progress message.			HTProgress()
148  *	-------------------------
149  */
HTProgress(const char * Msg)150 void HTProgress(const char *Msg)
151 {
152     statusline(Msg);
153     LYstore_message(Msg);
154     CTRACE((tfp, "%s\n", Msg));
155     LYSleepDelay();
156 }
157 
HTProgressUnits(int rate)158 const char *HTProgressUnits(int rate)
159 {
160     static const char *bunits = 0;
161     static const char *kbunits = 0;
162 
163     if (!bunits) {
164 	bunits = gettext("bytes");
165 	kbunits = gettext(LYTransferName);
166     }
167     return ((rate == rateKB)
168 #ifdef USE_READPROGRESS
169 	    || (rate == rateEtaKB)
170 	    || (rate == rateEtaKB2)
171 #endif
172 	)? kbunits : bunits;
173 }
174 
sprint_bytes(char * s,off_t n,const char * was_units)175 static const char *sprint_bytes(char *s, off_t n, const char *was_units)
176 {
177     static off_t kb_units = 1024;
178     const char *u = HTProgressUnits(LYTransferRate);
179 
180     if (isRateInKB(LYTransferRate)) {
181 	if (n >= 10 * kb_units) {
182 	    sprintf(s, "%" PRI_off_t, CAST_off_t (n / kb_units));
183 	} else if (n > 999) {	/* Avoid switching between 1016b/s and 1K/s */
184 	    sprintf(s, "%.2g", ((double) n) / (double) kb_units);
185 	} else {
186 	    sprintf(s, "%" PRI_off_t, CAST_off_t (n));
187 
188 	    u = HTProgressUnits(rateBYTES);
189 	}
190     } else {
191 	sprintf(s, "%" PRI_off_t, CAST_off_t (n));
192     }
193 
194     if (!was_units || was_units != u)
195 	sprintf(s + strlen(s), " %s", u);
196     return u;
197 }
198 
199 #ifdef USE_READPROGRESS
200 #define TIME_HMS_LENGTH (36)
sprint_tbuf(char * s,long t)201 static char *sprint_tbuf(char *s, long t)
202 {
203     const char *format = ((LYTransferRate == rateEtaBYTES2 ||
204 			   LYTransferRate == rateEtaKB2)
205 			  ? "% 2ld%c"
206 			  : "%ld%c");
207     char *base = s;
208 
209     if (t < 0) {
210 	strcpy(s, "forever");
211     } else {
212 	if (t > (3600 * 24)) {
213 	    sprintf(s, format, t / (3600 * 24), 'd');
214 	    s += strlen(s);
215 	    t %= (3600 * 24);
216 	}
217 	if (t > 3600) {
218 	    sprintf(s, format, t / 3600, 'h');
219 	    s += strlen(s);
220 	    t %= 3600;
221 	}
222 	if (t > 60) {
223 	    sprintf(s, format, t / 60, 'm');
224 	    s += strlen(s);
225 	    t %= 60;
226 	}
227 	if (s == base) {
228 	    sprintf(s, "% 2ld sec", t);
229 	} else if (t != 0) {
230 	    sprintf(s, format, t, 's');
231 	}
232     }
233     return base;
234 }
235 #endif /* USE_READPROGRESS */
236 
237 /*	Issue a read-progress message.			HTReadProgress()
238  *	------------------------------
239  */
HTReadProgress(off_t bytes,off_t total)240 void HTReadProgress(off_t bytes, off_t total)
241 {
242     static off_t bytes_last, total_last;
243     static off_t transfer_rate = 0;
244     static char *line = NULL;
245     char bytesp[80], totalp[80], transferp[80];
246     int renew = 0;
247     const char *was_units;
248 
249 #ifdef HAVE_GETTIMEOFDAY
250     struct timeval tv;
251     double now;
252     static double first, last, last_active;
253 
254     gettimeofday(&tv, (struct timezone *) 0);
255     now = (double) tv.tv_sec + (double) tv.tv_usec / 1000000.;
256 #else
257 #if defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H)
258     static double now, first, last, last_active;
259     struct timeb tb;
260 
261     ftime(&tb);
262     now = tb.time + (double) tb.millitm / 1000;
263 #else
264     time_t now = time((time_t *) 0);	/* once per second */
265     static time_t first, last, last_active;
266 #endif
267 #endif
268 
269     if (!LYShowTransferRate)
270 	LYTransferRate = rateOFF;
271 
272     if (bytes == 0) {
273 	first = last = last_active = now;
274 	bytes_last = bytes;
275     } else if (bytes < 0) {	/* stalled */
276 	bytes = bytes_last;
277 	total = total_last;
278     }
279 
280     /* 1 sec delay for transfer_rate calculation without g-t-o-d */
281     if ((bytes > 0) &&
282 	(now > first)) {
283 	if (transfer_rate <= 0) {	/* the very first time */
284 	    transfer_rate = (off_t) ((double) (bytes) / (now - first));
285 	    /* bytes/sec */
286 	}
287 	total_last = total;
288 
289 	/*
290 	 * Optimal refresh time:  every 0.2 sec
291 	 */
292 #if defined(HAVE_GETTIMEOFDAY) || (defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H))
293 	if (now >= last + 0.2)
294 	    renew = 1;
295 #else
296 	/*
297 	 * Use interpolation.  (The transfer rate may be not constant
298 	 * when we have partial content in a proxy.  We adjust transfer_rate
299 	 * once a second to minimize interpolation error below.)
300 	 */
301 	if ((now != last) || ((bytes - bytes_last) > (transfer_rate / 5))) {
302 	    renew = 1;
303 	    bytes_last += (transfer_rate / 5);	/* until we got next second */
304 	}
305 #endif
306 	if (renew) {
307 	    if (now > last) {
308 		last = now;
309 		if (bytes_last != bytes)
310 		    last_active = now;
311 		bytes_last = bytes;
312 		transfer_rate = (off_t) ((double) bytes / (now - first));	/* more accurate value */
313 	    }
314 
315 	    if (total > 0)
316 		was_units = sprint_bytes(totalp, total, 0);
317 	    else
318 		was_units = 0;
319 	    sprint_bytes(bytesp, bytes, was_units);
320 
321 	    switch ((TransferRate) LYTransferRate) {
322 #ifdef USE_PROGRESSBAR
323 	    case rateBAR:
324 		/*
325 		 * If we know the total size of the file, we can compute
326 		 * a percentage, and show a corresponding progress bar.
327 		 */
328 		HTSprintf0(&line, gettext("Read %s of data"), bytesp);
329 
330 		if (total > 0) {
331 		    float percent = (float) bytes / (float) total;
332 		    int meter = (int) (((float) LYcolLimit * percent) - 5);
333 
334 		    CTRACE((tfp, "rateBAR: bytes: %" PRI_off_t ", total: "
335 			    "%" PRI_off_t "\n",
336 			    CAST_off_t (bytes),
337 			    CAST_off_t (total)));
338 		    CTRACE((tfp, "meter = %d\n", meter));
339 
340 		    HTSprintf0(&line, "%d%% ", (int) (percent * 100));
341 		    while (meter-- > 0)
342 			StrAllocCat(line, "I");
343 
344 		    CTRACE((tfp, "%s\n", line));
345 		    CTRACE_FLUSH(tfp);
346 		}
347 		break;
348 #endif
349 	    default:
350 		if (total > 0) {
351 		    HTSprintf0(&line, gettext("Read %s of %s of data"),
352 			       bytesp, totalp);
353 		} else {
354 		    HTSprintf0(&line, gettext("Read %s of data"), bytesp);
355 		}
356 
357 		if (LYTransferRate != rateOFF
358 		    && transfer_rate > 0) {
359 		    sprint_bytes(transferp, transfer_rate, 0);
360 		    HTSprintf(&line, gettext(", %s/sec"), transferp);
361 		}
362 		break;
363 	    }
364 
365 #ifdef USE_READPROGRESS
366 	    if (LYTransferRate == rateEtaBYTES
367 		|| LYTransferRate == rateEtaKB
368 		|| LYTransferRate == rateEtaBYTES2
369 		|| LYTransferRate == rateEtaKB2) {
370 		char tbuf[TIME_HMS_LENGTH];
371 
372 		if (now - last_active >= 5)
373 		    HTSprintf(&line,
374 			      gettext(" (stalled for %s)"),
375 			      sprint_tbuf(tbuf, (long) (now - last_active)));
376 		if (total > 0 && transfer_rate)
377 		    HTSprintf(&line,
378 			      gettext(", ETA %s"),
379 			      sprint_tbuf(tbuf, (long) ((total - bytes) / transfer_rate)));
380 	    }
381 #endif
382 
383 	    switch ((TransferRate) LYTransferRate) {
384 #ifdef USE_PROGRESSBAR
385 	    case rateBAR:
386 		/*
387 		 * If we were not able to show a progress bar, just show
388 		 * a "." for progress.
389 		 */
390 		if (total <= 0)
391 		    StrAllocCat(line, ".");
392 		break;
393 #endif
394 	    default:
395 		StrAllocCat(line, ".");
396 		break;
397 	    }
398 
399 	    if (total < -1)
400 		StrAllocCat(line, gettext(" (Press 'z' to abort)"));
401 
402 	    /* do not store the message for history page. */
403 	    statusline(line);
404 	    CTRACE((tfp, "%s\n", line));
405 	}
406     }
407 #ifdef LY_FIND_LEAKS
408     FREE(line);
409 #endif
410 }
411 
412 static BOOL conf_cancelled = NO;	/* used by HTConfirm only - kw */
413 
HTLastConfirmCancelled(void)414 BOOL HTLastConfirmCancelled(void)
415 {
416     if (conf_cancelled) {
417 	conf_cancelled = NO;	/* reset */
418 	return (YES);
419     } else {
420 	return (NO);
421     }
422 }
423 
424 /*
425  * Prompt for yes/no response, but let a configuration variable override
426  * the prompt entirely.
427  */
HTForcedPrompt(int option,const char * msg,int dft)428 int HTForcedPrompt(int option, const char *msg, int dft)
429 {
430     int result = FALSE;
431     const char *show = NULL;
432     char *msg2 = NULL;
433 
434     if (option == FORCE_PROMPT_DFT) {
435 	result = HTConfirmDefault(msg, dft);
436     } else {
437 	if (option == FORCE_PROMPT_YES) {
438 	    show = gettext("yes");
439 	    result = YES;
440 	} else if (option == FORCE_PROMPT_NO) {
441 	    show = gettext("no");
442 	    result = NO;
443 	} else {
444 	    return HTConfirmDefault(msg, dft);	/* bug... */
445 	}
446 	HTSprintf(&msg2, "%s %s", msg, show);
447 	HTUserMsg(msg2);
448 	free(msg2);
449     }
450     return result;
451 }
452 
453 #define DFT_CONFIRM ~(YES|NO)
454 
455 /*	Seek confirmation with default answer.		HTConfirmDefault()
456  *	--------------------------------------
457  */
HTConfirmDefault(const char * Msg,int Dft)458 int HTConfirmDefault(const char *Msg, int Dft)
459 {
460 /* Meta-note: don't move the following note from its place right
461    in front of the first gettext().  As it is now, it should
462    automatically appear in generated lynx.pot files. - kw
463  */
464 
465 /* NOTE TO TRANSLATORS:  If you provide a translation for "yes", lynx
466  * will take the first byte of the translation as a positive response
467  * to Yes/No questions.  If you provide a translation for "no", lynx
468  * will take the first byte of the translation as a negative response
469  * to Yes/No questions.  For both, lynx will also try to show the
470  * first byte in the prompt as a character, instead of (y) or (n),
471  * respectively.  This will not work right for multibyte charsets!
472  * Don't translate "yes" and "no" for CJK character sets (or translate
473  * them to "yes" and "no").  For a translation using UTF-8, don't
474  * translate if the translation would begin with anything but a 7-bit
475  * (US_ASCII) character.  That also means do not translate if the
476  * translation would begin with anything but a 7-bit character, if
477  * you use a single-byte character encoding (a charset like ISO-8859-n)
478  * but anticipate that the message catalog may be used re-encoded in
479  * UTF-8 form.
480  * For translations using other character sets, you may also wish to
481  * leave "yes" and "no" untranslated, if using (y) and (n) is the
482  * preferred behavior.
483  * Lynx will also accept y Y n N as responses unless there is a conflict
484  * with the first letter of the "yes" or "no" translation.
485  */
486     const char *msg_yes = gettext("yes");
487     const char *msg_no = gettext("no");
488     int result = -1;
489 
490     /* If they're not really distinct in the first letter, revert to English */
491     if (TOUPPER(*msg_yes) == TOUPPER(*msg_no)) {
492 	msg_yes = "yes";
493 	msg_no = "no";
494     }
495 
496     conf_cancelled = NO;
497     if (dump_output_immediately) {	/* Non-interactive, can't respond */
498 	if (Dft == DFT_CONFIRM) {
499 	    CTRACE((tfp, "Confirm: %s (%c/%c) ", Msg, *msg_yes, *msg_no));
500 	} else {
501 	    CTRACE((tfp, "Confirm: %s (%c) ", Msg, (Dft == YES) ? *msg_yes : *msg_no));
502 	}
503 	CTRACE((tfp, "- NO, not interactive.\n"));
504 	result = NO;
505     } else {
506 	char *msg = NULL;
507 	char fallback_y = 'y';	/* English letter response as fallback */
508 	char fallback_n = 'n';	/* English letter response as fallback */
509 
510 	if (fallback_y == *msg_yes || fallback_y == *msg_no)
511 	    fallback_y = '\0';	/* conflict or duplication, don't use */
512 	if (fallback_n == *msg_yes || fallback_n == *msg_no)
513 	    fallback_n = '\0';	/* conflict or duplication, don't use */
514 
515 	if (Dft == DFT_CONFIRM)
516 	    HTSprintf0(&msg, "%s (%c/%c) ", Msg, *msg_yes, *msg_no);
517 	else
518 	    HTSprintf0(&msg, "%s (%c) ", Msg, (Dft == YES) ? *msg_yes : *msg_no);
519 	if (LYTraceLogFP) {
520 	    CTRACE((tfp, "Confirm: %s", msg));
521 	}
522 	_statusline(msg);
523 	FREE(msg);
524 
525 	while (result < 0) {
526 	    int c = LYgetch_single();
527 
528 #ifdef VMS
529 	    if (HadVMSInterrupt) {
530 		HadVMSInterrupt = FALSE;
531 		c = TOUPPER(*msg_no);
532 	    }
533 #endif /* VMS */
534 	    if (c == TOUPPER(*msg_yes)) {
535 		result = YES;
536 	    } else if (c == TOUPPER(*msg_no)) {
537 		result = NO;
538 	    } else if (fallback_y && c == fallback_y) {
539 		result = YES;
540 	    } else if (fallback_n && c == fallback_n) {
541 		result = NO;
542 	    } else if (LYCharIsINTERRUPT(c)) {	/* remember we had ^G or ^C */
543 		conf_cancelled = YES;
544 		result = NO;
545 	    } else if (Dft != DFT_CONFIRM) {
546 		result = Dft;
547 		break;
548 	    }
549 	}
550 	CTRACE((tfp, "- %s%s.\n",
551 		(result != NO) ? "YES" : "NO",
552 		conf_cancelled ? ", cancelled" : ""));
553     }
554     return (result);
555 }
556 
557 /*	Seek confirmation.				HTConfirm()
558  *	------------------
559  */
HTConfirm(const char * Msg)560 BOOL HTConfirm(const char *Msg)
561 {
562     return (BOOL) HTConfirmDefault(Msg, DFT_CONFIRM);
563 }
564 
565 /*
566  * Ask a post resubmission prompt with some indication of what would
567  * be resubmitted, useful especially for going backward in history.
568  * Try to use parts of the address or, if given, the title, depending
569  * on how much fits on the statusline.
570  * if_imgmap and if_file indicate how to handle an address that is
571  * a "LYNXIMGMAP:", or a "file:" URL (presumably the List Page file),
572  * respectively:  0:  auto-deny, 1:  auto-confirm, 2:  prompt.
573  * - kw
574  */
575 
confirm_post_resub(const char * address,const char * title,int if_imgmap,int if_file)576 BOOL confirm_post_resub(const char *address,
577 			const char *title,
578 			int if_imgmap,
579 			int if_file)
580 {
581     size_t len1;
582     const char *msg = CONFIRM_POST_RESUBMISSION_TO;
583     char buf[240];
584     char *temp = NULL;
585     BOOL res;
586     size_t maxlen = (size_t) (LYcolLimit - 5);
587 
588     if (!address) {
589 	return (NO);
590     } else if (isLYNXIMGMAP(address)) {
591 	if (if_imgmap <= 0)
592 	    return (NO);
593 	else if (if_imgmap == 1)
594 	    return (YES);
595 	else
596 	    msg = CONFIRM_POST_LIST_RELOAD;
597     } else if (isFILE_URL(address)) {
598 	if (if_file <= 0)
599 	    return (NO);
600 	else if (if_file == 1)
601 	    return (YES);
602 	else
603 	    msg = CONFIRM_POST_LIST_RELOAD;
604     } else if (dump_output_immediately) {
605 	return (NO);
606     }
607     if (maxlen >= sizeof(buf))
608 	maxlen = sizeof(buf) - 1;
609     if ((len1 = strlen(msg)) +
610 	strlen(address) <= maxlen) {
611 	sprintf(buf, msg, address);
612 	return HTConfirm(buf);
613     }
614     if (len1 + strlen(temp = HTParse(address, "",
615 				     PARSE_ACCESS + PARSE_HOST + PARSE_PATH
616 				     + PARSE_PUNCTUATION)) <= maxlen) {
617 	sprintf(buf, msg, temp);
618 	res = HTConfirm(buf);
619 	FREE(temp);
620 	return (res);
621     }
622     FREE(temp);
623     if (title && (len1 + strlen(title) <= maxlen)) {
624 	sprintf(buf, msg, title);
625 	return HTConfirm(buf);
626     }
627     if (len1 + strlen(temp = HTParse(address, "",
628 				     PARSE_ACCESS + PARSE_HOST
629 				     + PARSE_PUNCTUATION)) <= maxlen) {
630 	sprintf(buf, msg, temp);
631 	res = HTConfirm(buf);
632 	FREE(temp);
633 	return (res);
634     }
635     FREE(temp);
636     if ((temp = HTParse(address, "", PARSE_HOST)) && *temp &&
637 	len1 + strlen(temp) <= maxlen) {
638 	sprintf(buf, msg, temp);
639 	res = HTConfirm(buf);
640 	FREE(temp);
641 	return (res);
642     }
643     FREE(temp);
644     return HTConfirm(CONFIRM_POST_RESUBMISSION);
645 }
646 
647 /*	Prompt for answer and get text back.		HTPrompt()
648  *	------------------------------------
649  */
HTPrompt(const char * Msg,const char * deflt)650 char *HTPrompt(const char *Msg, const char *deflt)
651 {
652     char *rep = NULL;
653     bstring *data = NULL;
654 
655     _statusline(Msg);
656     BStrCopy0(data, deflt ? deflt : "");
657 
658     if (!dump_output_immediately)
659 	(void) LYgetBString(&data, VISIBLE, 0, NORECALL);
660 
661     StrAllocCopy(rep, data->str);
662 
663     BStrFree(data);
664     return rep;
665 }
666 
667 /*
668  *	Prompt for password without echoing the reply.	HTPromptPassword()
669  *	----------------------------------------------
670  */
HTPromptPassword(const char * Msg)671 char *HTPromptPassword(const char *Msg)
672 {
673     char *result = NULL;
674     bstring *data = NULL;
675 
676     if (!dump_output_immediately) {
677 	_statusline(Msg ? Msg : PASSWORD_PROMPT);
678 	BStrCopy0(data, "");
679 	(void) LYgetBString(&data, HIDDEN, 0, NORECALL);
680 	StrAllocCopy(result, data->str);
681 	BStrFree(data);
682     } else {
683 	printf("\n%s\n", PASSWORD_REQUIRED);
684 	StrAllocCopy(result, "");
685     }
686     return result;
687 }
688 
689 /*	Prompt both username and password.	 HTPromptUsernameAndPassword()
690  *	----------------------------------
691  *
692  *  On entry,
693  *	Msg		is the prompting message.
694  *	*username and
695  *	*password	are char pointers which contain default
696  *			or zero-length strings; they are changed
697  *			to point to result strings.
698  *	IsProxy 	should be TRUE if this is for
699  *			proxy authentication.
700  *
701  *			If *username is not NULL, it is taken
702  *			to point to a default value.
703  *			Initial value of *password is
704  *			completely discarded.
705  *
706  *  On exit,
707  *	*username and *password point to newly allocated
708  *	strings -- original strings pointed to by them
709  *	are NOT freed.
710  *
711  */
HTPromptUsernameAndPassword(const char * Msg,char ** username,char ** password,int IsProxy)712 void HTPromptUsernameAndPassword(const char *Msg,
713 				 char **username,
714 				 char **password,
715 				 int IsProxy)
716 {
717     if ((IsProxy == FALSE &&
718 	 authentication_info[0] && authentication_info[1]) ||
719 	(IsProxy == TRUE &&
720 	 proxyauth_info[0] && proxyauth_info[1])) {
721 	/*
722 	 * The -auth or -pauth parameter gave us both the username
723 	 * and password to use for the first realm or proxy server,
724 	 * respectively, so just use them without any prompting.  - FM
725 	 */
726 	StrAllocCopy(*username, (IsProxy ?
727 				 proxyauth_info[0] : authentication_info[0]));
728 	if (IsProxy) {
729 	    FREE(proxyauth_info[0]);
730 	} else {
731 	    FREE(authentication_info[0]);
732 	}
733 	StrAllocCopy(*password, (IsProxy ?
734 				 proxyauth_info[1] : authentication_info[1]));
735 	if (IsProxy) {
736 	    FREE(proxyauth_info[1]);
737 	} else {
738 	    FREE(authentication_info[1]);
739 	}
740     } else if (dump_output_immediately) {
741 	/*
742 	 * We are not interactive and don't have both the
743 	 * username and password from the command line,
744 	 * but might have one or the other.  - FM
745 	 */
746 	if ((IsProxy == FALSE && authentication_info[0]) ||
747 	    (IsProxy == TRUE && proxyauth_info[0])) {
748 	    /*
749 	     * Use the command line username.  - FM
750 	     */
751 	    StrAllocCopy(*username, (IsProxy ?
752 				     proxyauth_info[0] : authentication_info[0]));
753 	    if (IsProxy) {
754 		FREE(proxyauth_info[0]);
755 	    } else {
756 		FREE(authentication_info[0]);
757 	    }
758 	} else {
759 	    /*
760 	     * Default to "WWWuser".  - FM
761 	     */
762 	    StrAllocCopy(*username, "WWWuser");
763 	}
764 	if ((IsProxy == FALSE && authentication_info[1]) ||
765 	    (IsProxy == TRUE && proxyauth_info[1])) {
766 	    /*
767 	     * Use the command line password.  - FM
768 	     */
769 	    StrAllocCopy(*password, (IsProxy ?
770 				     proxyauth_info[1] : authentication_info[1]));
771 	    if (IsProxy) {
772 		FREE(proxyauth_info[1]);
773 	    } else {
774 		FREE(authentication_info[1]);
775 	    }
776 	} else {
777 	    /*
778 	     * Default to a zero-length string.  - FM
779 	     */
780 	    StrAllocCopy(*password, "");
781 	}
782 	printf("\n%s\n", USERNAME_PASSWORD_REQUIRED);
783 
784     } else {
785 	/*
786 	 * We are interactive and don't have both the
787 	 * username and password from the command line,
788 	 * but might have one or the other.  - FM
789 	 */
790 	if ((IsProxy == FALSE && authentication_info[0]) ||
791 	    (IsProxy == TRUE && proxyauth_info[0])) {
792 	    /*
793 	     * Offer the command line username in the
794 	     * prompt for the first realm.  - FM
795 	     */
796 	    StrAllocCopy(*username, (IsProxy ?
797 				     proxyauth_info[0] : authentication_info[0]));
798 	    if (IsProxy) {
799 		FREE(proxyauth_info[0]);
800 	    } else {
801 		FREE(authentication_info[0]);
802 	    }
803 	}
804 	/*
805 	 * Prompt for confirmation or entry of the username.  - FM
806 	 */
807 	if (Msg != NULL) {
808 	    *username = HTPrompt(Msg, *username);
809 	} else {
810 	    *username = HTPrompt(USERNAME_PROMPT, *username);
811 	}
812 	if ((IsProxy == FALSE && authentication_info[1]) ||
813 	    (IsProxy == TRUE && proxyauth_info[1])) {
814 	    /*
815 	     * Use the command line password for the first realm.  - FM
816 	     */
817 	    StrAllocCopy(*password, (IsProxy ?
818 				     proxyauth_info[1] : authentication_info[1]));
819 	    if (IsProxy) {
820 		FREE(proxyauth_info[1]);
821 	    } else {
822 		FREE(authentication_info[1]);
823 	    }
824 	} else if (non_empty(*username)) {
825 	    /*
826 	     * We have a non-zero length username,
827 	     * so prompt for the password.  - FM
828 	     */
829 	    *password = HTPromptPassword(PASSWORD_PROMPT);
830 	} else {
831 	    /*
832 	     * Return a zero-length password.  - FM
833 	     */
834 	    StrAllocCopy(*password, "");
835 	}
836     }
837 }
838 
839 /*	Confirm a cookie operation.			HTConfirmCookie()
840  *	---------------------------
841  *
842  *  On entry,
843  *	server			is the server sending the Set-Cookie.
844  *	domain			is the domain of the cookie.
845  *	path			is the path of the cookie.
846  *	name			is the name of the cookie.
847  *	value			is the value of the cookie.
848  *
849  *  On exit,
850  *	Returns FALSE on cancel,
851  *		TRUE if the cookie should be set.
852  */
HTConfirmCookie(domain_entry * de,const char * server,const char * name,const char * value)853 BOOL HTConfirmCookie(domain_entry * de, const char *server,
854 		     const char *name,
855 		     const char *value)
856 {
857     int ch;
858     const char *prompt = ADVANCED_COOKIE_CONFIRMATION;
859 
860     if (de == NULL)
861 	return FALSE;
862 
863     /* If the user has specified a list of domains to allow or deny
864      * from the config file, then they'll already have de->bv set to
865      * ACCEPT_ALWAYS or REJECT_ALWAYS so we can relax and let the
866      * default cookie handling code cope with this fine.
867      */
868 
869     /*
870      * If the user has specified a constant action, don't prompt at all.
871      */
872     if (de->bv == ACCEPT_ALWAYS)
873 	return TRUE;
874     if (de->bv == REJECT_ALWAYS)
875 	return FALSE;
876 
877     if (dump_output_immediately) {
878 	/*
879 	 * Non-interactive, can't respond.  Use the LYSetCookies value
880 	 * based on its compilation or configuration setting, or on the
881 	 * command line toggle.  - FM
882 	 */
883 	return LYSetCookies;
884     }
885 
886     /*
887      * Estimate how much of the cookie we can show.
888      */
889     if (!LYAcceptAllCookies) {
890 	int namelen, valuelen, space_free, percentage;
891 	char *message = 0;
892 
893 	space_free = (LYcolLimit
894 		      - (LYstrCells(prompt)
895 			 - 10)	/* %s and %.*s and %.*s chars */
896 		      -(int) strlen(server));
897 	if (space_free < 0)
898 	    space_free = 0;
899 	namelen = (int) strlen(name);
900 	valuelen = (int) strlen(value);
901 	if ((namelen + valuelen) > space_free) {
902 	    /*
903 	     * Argh...  there isn't enough space on our single line for
904 	     * the whole cookie.  Reduce them both by a percentage.
905 	     * This should be smarter.
906 	     */
907 	    percentage = (100 * space_free) / (namelen + valuelen);
908 	    namelen = (percentage * namelen) / 100;
909 	    valuelen = (percentage * valuelen) / 100;
910 	}
911 	HTSprintf(&message, prompt, server, namelen, name, valuelen, value);
912 	_statusline(message);
913 	FREE(message);
914     }
915     for (;;) {
916 	if (LYAcceptAllCookies) {
917 	    ch = 'A';
918 	} else {
919 	    ch = LYgetch_single();
920 #if defined(LOCALE) && defined(HAVE_GETTEXT)
921 	    {
922 #define L_PAREN '('
923 #define R_PAREN ')'
924 		/*
925 		 * Special-purpose workaround for gettext support (we should do
926 		 * this in a more general way) -TD
927 		 *
928 		 * NOTE TO TRANSLATORS:  If the prompt has been rendered into
929 		 * another language, and if yes/no are distinct, assume the
930 		 * translator can make an ordered list in parentheses with one
931 		 * capital letter for each as we assumed in HTConfirmDefault().
932 		 * The list has to be in the same order as in the original message,
933 		 * and the four capital letters chosen to not match those in the
934 		 * original unless they have the same position.
935 		 *
936 		 * Example:
937 		 * (Y/N/Always/neVer)              - English (original)
938 		 * (O/N/Toujours/Jamais)           - French
939 		 */
940 		char *p = gettext("Y/N/A/V");	/* placeholder for comment */
941 		const char *s = "YNAV\007\003";		/* see ADVANCED_COOKIE_CONFIRMATION */
942 
943 		if (strchr(s, ch) == 0
944 		    && isalpha(ch)
945 		    && (p = strrchr(prompt, L_PAREN)) != 0) {
946 
947 		    CTRACE((tfp, "Looking for %c in %s\n", ch, p));
948 		    while (*p != R_PAREN && *p != 0 && isalpha(UCH(*s))) {
949 			if (isalpha(UCH(*p)) && (*p == TOUPPER(*p))) {
950 			    CTRACE((tfp, "...testing %c/%c\n", *p, *s));
951 			    if (*p == ch) {
952 				ch = *s;
953 				break;
954 			    }
955 			    ++s;
956 			}
957 			++p;
958 		    }
959 		}
960 	    }
961 #endif
962 	}
963 #ifdef VMS
964 	if (HadVMSInterrupt) {
965 	    HadVMSInterrupt = FALSE;
966 	    ch = 'N';
967 	}
968 #endif /* VMS */
969 	switch (ch) {
970 	case 'A':
971 	    /*
972 	     * Set to accept all cookies for this domain.
973 	     */
974 	    de->bv = ACCEPT_ALWAYS;
975 	    HTUserMsg2(ALWAYS_ALLOWING_COOKIES, de->domain);
976 	    return TRUE;
977 
978 	case 'N':
979 	    /*
980 	     * Reject the cookie.
981 	     */
982 	  reject:
983 	    HTUserMsg(REJECTING_COOKIE);
984 	    return FALSE;
985 
986 	case 'V':
987 	    /*
988 	     * Set to reject all cookies from this domain.
989 	     */
990 	    de->bv = REJECT_ALWAYS;
991 	    HTUserMsg2(NEVER_ALLOWING_COOKIES, de->domain);
992 	    return FALSE;
993 
994 	case 'Y':
995 	    /*
996 	     * Accept the cookie.
997 	     */
998 	    HTInfoMsg(ALLOWING_COOKIE);
999 	    return TRUE;
1000 
1001 	default:
1002 	    if (LYCharIsINTERRUPT(ch))
1003 		goto reject;
1004 	    continue;
1005 	}
1006     }
1007 }
1008 
1009 /*	Confirm redirection of POST.		HTConfirmPostRedirect()
1010  *	----------------------------
1011  *
1012  *  On entry,
1013  *	Redirecting_url 	    is the Location.
1014  *	server_status		    is the server status code.
1015  *
1016  *  On exit,
1017  *	Returns 0 on cancel,
1018  *	  1 for redirect of POST with content,
1019  *	303 for redirect as GET without content
1020  */
HTConfirmPostRedirect(const char * Redirecting_url,int server_status)1021 int HTConfirmPostRedirect(const char *Redirecting_url, int server_status)
1022 {
1023     int result = -1;
1024     char *show_POST_url = NULL;
1025     char *StatusInfo = 0;
1026     char *url = 0;
1027     int on_screen = 0;		/* 0 - show menu
1028 
1029 				 * 1 - show url
1030 				 * 2 - menu is already on screen */
1031 
1032     if (server_status == 303 ||
1033 	server_status == 302) {
1034 	/*
1035 	 * HTTP.c should not have called us for either of
1036 	 * these because we're treating 302 as historical,
1037 	 * so just return 303.  - FM
1038 	 */
1039 	return 303;
1040     }
1041 
1042     if (dump_output_immediately) {
1043 	if (server_status == 301) {
1044 	    /*
1045 	     * Treat 301 as historical, i.e., like 303 (GET
1046 	     * without content), when not interactive.  - FM
1047 	     */
1048 	    return 303;
1049 	} else {
1050 	    /*
1051 	     * Treat anything else (e.g., 305, 306 or 307) as too
1052 	     * dangerous to redirect without confirmation, and thus
1053 	     * cancel when not interactive.  - FM
1054 	     */
1055 	    return 0;
1056 	}
1057     }
1058 
1059     if (user_mode == NOVICE_MODE) {
1060 	on_screen = 2;
1061 	LYmove(LYlines - 2, 0);
1062 	HTSprintf0(&StatusInfo, SERVER_ASKED_FOR_REDIRECTION, server_status);
1063 	LYaddstr(StatusInfo);
1064 	LYclrtoeol();
1065 	LYmove(LYlines - 1, 0);
1066 	HTSprintf0(&url, "URL: %.*s",
1067 		   (LYcols < 250 ? LYcolLimit - 5 : 250), Redirecting_url);
1068 	LYaddstr(url);
1069 	LYclrtoeol();
1070 	if (server_status == 301) {
1071 	    _statusline(PROCEED_GET_CANCEL);
1072 	} else {
1073 	    _statusline(PROCEED_OR_CANCEL);
1074 	}
1075     } else {
1076 	HTSprintf0(&StatusInfo, "%d %.*s",
1077 		   server_status,
1078 		   251,
1079 		   ((server_status == 301) ?
1080 		    ADVANCED_POST_GET_REDIRECT :
1081 		    ADVANCED_POST_REDIRECT));
1082 	StrAllocCopy(show_POST_url, LOCATION_HEADER);
1083 	StrAllocCat(show_POST_url, Redirecting_url);
1084     }
1085     while (result < 0) {
1086 	int c;
1087 
1088 	switch (on_screen) {
1089 	case 0:
1090 	    _statusline(StatusInfo);
1091 	    break;
1092 	case 1:
1093 	    _statusline(show_POST_url);
1094 	}
1095 	c = LYgetch_single();
1096 	switch (c) {
1097 	case 'P':
1098 	    /*
1099 	     * Proceed with 301 or 307 redirect of POST
1100 	     * with same method and POST content.  - FM
1101 	     */
1102 	    FREE(show_POST_url);
1103 	    result = 1;
1104 	    break;
1105 
1106 	case 7:
1107 	case 'C':
1108 	    /*
1109 	     * Cancel request.
1110 	     */
1111 	    FREE(show_POST_url);
1112 	    result = 0;
1113 	    break;
1114 
1115 	case 'U':
1116 	    /*
1117 	     * Show URL for intermediate or advanced mode.
1118 	     */
1119 	    if (user_mode != NOVICE_MODE) {
1120 		if (on_screen == 1) {
1121 		    on_screen = 0;
1122 		} else {
1123 		    on_screen = 1;
1124 		}
1125 	    }
1126 	    break;
1127 
1128 	case 'G':
1129 	    if (server_status == 301) {
1130 		/*
1131 		 * Treat as 303 (GET without content).
1132 		 */
1133 		FREE(show_POST_url);
1134 		result = 303;
1135 		break;
1136 	    }
1137 	    /* fall through to default */
1138 
1139 	default:
1140 	    /*
1141 	     * Get another character.
1142 	     */
1143 	    if (on_screen == 1) {
1144 		on_screen = 0;
1145 	    } else {
1146 		on_screen = 2;
1147 	    }
1148 	}
1149     }
1150     FREE(StatusInfo);
1151     FREE(url);
1152     return (result);
1153 }
1154 
1155 #define okToSleep() (!crawl && !traversal && LYCursesON && !no_pause)
1156 
1157 /*
1158  * Sleep for the given message class' time.
1159  */
LYSleepAlert(void)1160 void LYSleepAlert(void)
1161 {
1162     if (okToSleep())
1163 	LYSleep(AlertSecs);
1164 }
1165 
LYSleepDelay(void)1166 void LYSleepDelay(void)
1167 {
1168     if (okToSleep())
1169 	LYSleep(DelaySecs);
1170 }
1171 
LYSleepInfo(void)1172 void LYSleepInfo(void)
1173 {
1174     if (okToSleep())
1175 	LYSleep(InfoSecs);
1176 }
1177 
LYSleepMsg(void)1178 void LYSleepMsg(void)
1179 {
1180     if (okToSleep())
1181 	LYSleep(MessageSecs);
1182 }
1183 
1184 #ifdef USE_CMD_LOGGING
LYSleepReplay(void)1185 void LYSleepReplay(void)
1186 {
1187     if (okToSleep())
1188 	LYSleep(ReplaySecs);
1189 }
1190 #endif /* USE_CMD_LOGGING */
1191 
1192 /*
1193  * LYstrerror emulates the ANSI strerror() function.
1194  */
1195 #ifndef LYStrerror
LYStrerror(int code)1196 char *LYStrerror(int code)
1197 {
1198     static char temp[80];
1199 
1200     sprintf(temp, "System errno is %d.\r\n", code);
1201     return temp;
1202 }
1203 #endif /* HAVE_STRERROR */
1204