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