summaryrefslogtreecommitdiffstats
path: root/src/HTAlert.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/HTAlert.c')
-rw-r--r--src/HTAlert.c1201
1 files changed, 1201 insertions, 0 deletions
diff --git a/src/HTAlert.c b/src/HTAlert.c
new file mode 100644
index 0000000..81594cf
--- /dev/null
+++ b/src/HTAlert.c
@@ -0,0 +1,1201 @@
+/*
+ * $LynxId: HTAlert.c,v 1.103 2017/07/02 19:54:30 tom Exp $
+ *
+ * Displaying messages and getting input for Lynx Browser
+ * ==========================================================
+ *
+ * REPLACE THIS MODULE with a GUI version in a GUI environment!
+ *
+ * History:
+ * Jun 92 Created May 1992 By C.T. Barker
+ * Feb 93 Simplified, portablised TBL
+ *
+ */
+
+#include <HTUtils.h>
+#include <HTAlert.h>
+#include <LYGlobalDefs.h>
+#include <LYCurses.h>
+#include <LYStrings.h>
+#include <LYUtils.h>
+#include <LYClean.h>
+#include <GridText.h>
+#include <LYCookie.h>
+#include <LYHistory.h> /* store statusline messages */
+
+#include <LYLeaks.h>
+
+#include <HTParse.h>
+
+#undef timezone /* U/Win defines this in time.h, hides implementation detail */
+
+#if defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H)
+#include <sys/timeb.h>
+#endif
+
+/*
+ * 'napms()' is preferable to 'sleep()' in any case because it does not
+ * interfere with output, but also because it can be less than a second.
+ */
+#ifdef HAVE_NAPMS
+#define LYSleep(n) napms(n)
+#else
+#define LYSleep(n) sleep((unsigned)n)
+#endif
+
+/* Issue a message about a problem. HTAlert()
+ * --------------------------------
+ */
+void HTAlert(const char *Msg)
+{
+ CTRACE((tfp, "\nAlert!: %s\n\n", Msg));
+ CTRACE_FLUSH(tfp);
+ _user_message(ALERT_FORMAT, Msg);
+ LYstore_message2(ALERT_FORMAT, Msg);
+
+ if (dump_output_immediately && dump_to_stderr) {
+ fflush(stdout);
+ fprintf(stderr, ALERT_FORMAT, Msg);
+ fputc('\n', stderr);
+ fflush(stderr);
+ }
+
+ LYSleepAlert();
+}
+
+void HTAlwaysAlert(const char *extra_prefix,
+ const char *Msg)
+{
+ if (!dump_output_immediately && LYCursesON) {
+ HTAlert(Msg);
+ } else {
+ if (extra_prefix) {
+ fprintf(((TRACE) ? stdout : stderr),
+ "%s %s!\n",
+ extra_prefix, Msg);
+ fflush(stdout);
+ LYstore_message2(ALERT_FORMAT, Msg);
+ LYSleepAlert();
+ } else {
+ fprintf(((TRACE) ? stdout : stderr), ALERT_FORMAT, NonNull(Msg));
+ fflush(stdout);
+ LYstore_message2(ALERT_FORMAT, Msg);
+ LYSleepAlert();
+ fprintf(((TRACE) ? stdout : stderr), "\n");
+ }
+ CTRACE((tfp, "\nAlert!: %s\n\n", Msg));
+ CTRACE_FLUSH(tfp);
+ }
+}
+
+/* Issue an informational message. HTInfoMsg()
+ * --------------------------------
+ */
+void HTInfoMsg(const char *Msg)
+{
+ _statusline(Msg);
+ if (non_empty(Msg)) {
+ CTRACE((tfp, "Info message: %s\n", Msg));
+ LYstore_message(Msg);
+ LYSleepInfo();
+ }
+}
+
+void HTInfoMsg2(const char *Msg2, const char *Arg)
+{
+ _user_message(Msg2, Arg);
+ if (non_empty(Msg2)) {
+ CTRACE((tfp, "Info message: "));
+ CTRACE((tfp, Msg2, Arg));
+ CTRACE((tfp, "\n"));
+ LYstore_message2(Msg2, Arg);
+ LYSleepInfo();
+ }
+}
+
+/* Issue an important message. HTUserMsg()
+ * --------------------------------
+ */
+void HTUserMsg(const char *Msg)
+{
+ _statusline(Msg);
+ if (non_empty(Msg)) {
+ CTRACE((tfp, "User message: %s\n", Msg));
+ LYstore_message(Msg);
+#if !(defined(USE_SLANG) || defined(WIDEC_CURSES))
+ if (IS_CJK_TTY) {
+ clearok(curscr, TRUE);
+ LYrefresh();
+ }
+#endif
+ LYSleepMsg();
+ }
+}
+
+void HTUserMsg2(const char *Msg2, const char *Arg)
+{
+ _user_message(Msg2, Arg);
+ if (non_empty(Msg2)) {
+ CTRACE((tfp, "User message: "));
+ CTRACE((tfp, Msg2, Arg));
+ CTRACE((tfp, "\n"));
+ LYstore_message2(Msg2, Arg);
+ LYSleepMsg();
+ }
+}
+
+/* Issue a progress message. HTProgress()
+ * -------------------------
+ */
+void HTProgress(const char *Msg)
+{
+ statusline(Msg);
+ LYstore_message(Msg);
+ CTRACE((tfp, "%s\n", Msg));
+ LYSleepDelay();
+}
+
+const char *HTProgressUnits(int rate)
+{
+ static const char *bunits = 0;
+ static const char *kbunits = 0;
+
+ if (!bunits) {
+ bunits = gettext("bytes");
+ kbunits = gettext(LYTransferName);
+ }
+ return ((rate == rateKB)
+#ifdef USE_READPROGRESS
+ || (rate == rateEtaKB)
+ || (rate == rateEtaKB2)
+#endif
+ )? kbunits : bunits;
+}
+
+static const char *sprint_bytes(char *s, off_t n, const char *was_units)
+{
+ static off_t kb_units = 1024;
+ const char *u = HTProgressUnits(LYTransferRate);
+
+ if (isRateInKB(LYTransferRate)) {
+ if (n >= 10 * kb_units) {
+ sprintf(s, "%" PRI_off_t, CAST_off_t (n / kb_units));
+ } else if (n > 999) { /* Avoid switching between 1016b/s and 1K/s */
+ sprintf(s, "%.2g", ((double) n) / (double) kb_units);
+ } else {
+ sprintf(s, "%" PRI_off_t, CAST_off_t (n));
+
+ u = HTProgressUnits(rateBYTES);
+ }
+ } else {
+ sprintf(s, "%" PRI_off_t, CAST_off_t (n));
+ }
+
+ if (!was_units || was_units != u)
+ sprintf(s + strlen(s), " %s", u);
+ return u;
+}
+
+#ifdef USE_READPROGRESS
+#define TIME_HMS_LENGTH (36)
+static char *sprint_tbuf(char *s, long t)
+{
+ const char *format = ((LYTransferRate == rateEtaBYTES2 ||
+ LYTransferRate == rateEtaKB2)
+ ? "% 2ld%c"
+ : "%ld%c");
+ char *base = s;
+
+ if (t < 0) {
+ strcpy(s, "forever");
+ } else {
+ if (t > (3600 * 24)) {
+ sprintf(s, format, t / (3600 * 24), 'd');
+ s += strlen(s);
+ t %= (3600 * 24);
+ }
+ if (t > 3600) {
+ sprintf(s, format, t / 3600, 'h');
+ s += strlen(s);
+ t %= 3600;
+ }
+ if (t > 60) {
+ sprintf(s, format, t / 60, 'm');
+ s += strlen(s);
+ t %= 60;
+ }
+ if (s == base) {
+ sprintf(s, "% 2ld sec", t);
+ } else if (t != 0) {
+ sprintf(s, format, t, 's');
+ }
+ }
+ return base;
+}
+#endif /* USE_READPROGRESS */
+
+/* Issue a read-progress message. HTReadProgress()
+ * ------------------------------
+ */
+void HTReadProgress(off_t bytes, off_t total)
+{
+ static off_t bytes_last, total_last;
+ static off_t transfer_rate = 0;
+ static char *line = NULL;
+ char bytesp[80], totalp[80], transferp[80];
+ int renew = 0;
+ const char *was_units;
+
+#ifdef HAVE_GETTIMEOFDAY
+ struct timeval tv;
+ double now;
+ static double first, last, last_active;
+
+ gettimeofday(&tv, (struct timezone *) 0);
+ now = (double) tv.tv_sec + (double) tv.tv_usec / 1000000.;
+#else
+#if defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H)
+ static double now, first, last, last_active;
+ struct timeb tb;
+
+ ftime(&tb);
+ now = tb.time + (double) tb.millitm / 1000;
+#else
+ time_t now = time((time_t *) 0); /* once per second */
+ static time_t first, last, last_active;
+#endif
+#endif
+
+ if (!LYShowTransferRate)
+ LYTransferRate = rateOFF;
+
+ if (bytes == 0) {
+ first = last = last_active = now;
+ bytes_last = bytes;
+ } else if (bytes < 0) { /* stalled */
+ bytes = bytes_last;
+ total = total_last;
+ }
+
+ /* 1 sec delay for transfer_rate calculation without g-t-o-d */
+ if ((bytes > 0) &&
+ (now > first)) {
+ if (transfer_rate <= 0) { /* the very first time */
+ transfer_rate = (off_t) ((double) (bytes) / (now - first));
+ /* bytes/sec */
+ }
+ total_last = total;
+
+ /*
+ * Optimal refresh time: every 0.2 sec
+ */
+#if defined(HAVE_GETTIMEOFDAY) || (defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H))
+ if (now >= last + 0.2)
+ renew = 1;
+#else
+ /*
+ * Use interpolation. (The transfer rate may be not constant
+ * when we have partial content in a proxy. We adjust transfer_rate
+ * once a second to minimize interpolation error below.)
+ */
+ if ((now != last) || ((bytes - bytes_last) > (transfer_rate / 5))) {
+ renew = 1;
+ bytes_last += (transfer_rate / 5); /* until we got next second */
+ }
+#endif
+ if (renew) {
+ if (now > last) {
+ last = now;
+ if (bytes_last != bytes)
+ last_active = now;
+ bytes_last = bytes;
+ transfer_rate = (off_t) ((double) bytes / (now - first)); /* more accurate value */
+ }
+
+ if (total > 0)
+ was_units = sprint_bytes(totalp, total, 0);
+ else
+ was_units = 0;
+ sprint_bytes(bytesp, bytes, was_units);
+
+ switch ((TransferRate) LYTransferRate) {
+#ifdef USE_PROGRESSBAR
+ case rateBAR:
+ /*
+ * If we know the total size of the file, we can compute
+ * a percentage, and show a corresponding progress bar.
+ */
+ HTSprintf0(&line, gettext("Read %s of data"), bytesp);
+
+ if (total > 0) {
+ float percent = (float) bytes / (float) total;
+ int meter = (int) (((float) LYcolLimit * percent) - 5);
+
+ CTRACE((tfp, "rateBAR: bytes: %" PRI_off_t ", total: "
+ "%" PRI_off_t "\n",
+ CAST_off_t (bytes),
+ CAST_off_t (total)));
+ CTRACE((tfp, "meter = %d\n", meter));
+
+ HTSprintf0(&line, "%d%% ", (int) (percent * 100));
+ while (meter-- > 0)
+ StrAllocCat(line, "I");
+
+ CTRACE((tfp, "%s\n", line));
+ CTRACE_FLUSH(tfp);
+ }
+ break;
+#endif
+ default:
+ if (total > 0) {
+ HTSprintf0(&line, gettext("Read %s of %s of data"),
+ bytesp, totalp);
+ } else {
+ HTSprintf0(&line, gettext("Read %s of data"), bytesp);
+ }
+
+ if (LYTransferRate != rateOFF
+ && transfer_rate > 0) {
+ sprint_bytes(transferp, transfer_rate, 0);
+ HTSprintf(&line, gettext(", %s/sec"), transferp);
+ }
+ break;
+ }
+
+#ifdef USE_READPROGRESS
+ if (LYTransferRate == rateEtaBYTES
+ || LYTransferRate == rateEtaKB
+ || LYTransferRate == rateEtaBYTES2
+ || LYTransferRate == rateEtaKB2) {
+ char tbuf[TIME_HMS_LENGTH];
+
+ if (now - last_active >= 5)
+ HTSprintf(&line,
+ gettext(" (stalled for %s)"),
+ sprint_tbuf(tbuf, (long) (now - last_active)));
+ if (total > 0 && transfer_rate)
+ HTSprintf(&line,
+ gettext(", ETA %s"),
+ sprint_tbuf(tbuf, (long) ((total - bytes) / transfer_rate)));
+ }
+#endif
+
+ switch ((TransferRate) LYTransferRate) {
+#ifdef USE_PROGRESSBAR
+ case rateBAR:
+ /*
+ * If we were not able to show a progress bar, just show
+ * a "." for progress.
+ */
+ if (total <= 0)
+ StrAllocCat(line, ".");
+ break;
+#endif
+ default:
+ StrAllocCat(line, ".");
+ break;
+ }
+
+ if (total < -1)
+ StrAllocCat(line, gettext(" (Press 'z' to abort)"));
+
+ /* do not store the message for history page. */
+ statusline(line);
+ CTRACE((tfp, "%s\n", line));
+ }
+ }
+#ifdef LY_FIND_LEAKS
+ FREE(line);
+#endif
+}
+
+static BOOL conf_cancelled = NO; /* used by HTConfirm only - kw */
+
+BOOL HTLastConfirmCancelled(void)
+{
+ if (conf_cancelled) {
+ conf_cancelled = NO; /* reset */
+ return (YES);
+ } else {
+ return (NO);
+ }
+}
+
+/*
+ * Prompt for yes/no response, but let a configuration variable override
+ * the prompt entirely.
+ */
+int HTForcedPrompt(int option, const char *msg, int dft)
+{
+ int result = FALSE;
+ const char *show = NULL;
+ char *msg2 = NULL;
+
+ if (option == FORCE_PROMPT_DFT) {
+ result = HTConfirmDefault(msg, dft);
+ } else {
+ if (option == FORCE_PROMPT_YES) {
+ show = gettext("yes");
+ result = YES;
+ } else if (option == FORCE_PROMPT_NO) {
+ show = gettext("no");
+ result = NO;
+ } else {
+ return HTConfirmDefault(msg, dft); /* bug... */
+ }
+ HTSprintf(&msg2, "%s %s", msg, show);
+ HTUserMsg(msg2);
+ free(msg2);
+ }
+ return result;
+}
+
+#define DFT_CONFIRM ~(YES|NO)
+
+/* Seek confirmation with default answer. HTConfirmDefault()
+ * --------------------------------------
+ */
+int HTConfirmDefault(const char *Msg, int Dft)
+{
+/* Meta-note: don't move the following note from its place right
+ in front of the first gettext(). As it is now, it should
+ automatically appear in generated lynx.pot files. - kw
+ */
+
+/* NOTE TO TRANSLATORS: If you provide a translation for "yes", lynx
+ * will take the first byte of the translation as a positive response
+ * to Yes/No questions. If you provide a translation for "no", lynx
+ * will take the first byte of the translation as a negative response
+ * to Yes/No questions. For both, lynx will also try to show the
+ * first byte in the prompt as a character, instead of (y) or (n),
+ * respectively. This will not work right for multibyte charsets!
+ * Don't translate "yes" and "no" for CJK character sets (or translate
+ * them to "yes" and "no"). For a translation using UTF-8, don't
+ * translate if the translation would begin with anything but a 7-bit
+ * (US_ASCII) character. That also means do not translate if the
+ * translation would begin with anything but a 7-bit character, if
+ * you use a single-byte character encoding (a charset like ISO-8859-n)
+ * but anticipate that the message catalog may be used re-encoded in
+ * UTF-8 form.
+ * For translations using other character sets, you may also wish to
+ * leave "yes" and "no" untranslated, if using (y) and (n) is the
+ * preferred behavior.
+ * Lynx will also accept y Y n N as responses unless there is a conflict
+ * with the first letter of the "yes" or "no" translation.
+ */
+ const char *msg_yes = gettext("yes");
+ const char *msg_no = gettext("no");
+ int result = -1;
+
+ /* If they're not really distinct in the first letter, revert to English */
+ if (TOUPPER(*msg_yes) == TOUPPER(*msg_no)) {
+ msg_yes = "yes";
+ msg_no = "no";
+ }
+
+ conf_cancelled = NO;
+ if (dump_output_immediately) { /* Non-interactive, can't respond */
+ if (Dft == DFT_CONFIRM) {
+ CTRACE((tfp, "Confirm: %s (%c/%c) ", Msg, *msg_yes, *msg_no));
+ } else {
+ CTRACE((tfp, "Confirm: %s (%c) ", Msg, (Dft == YES) ? *msg_yes : *msg_no));
+ }
+ CTRACE((tfp, "- NO, not interactive.\n"));
+ result = NO;
+ } else {
+ char *msg = NULL;
+ char fallback_y = 'y'; /* English letter response as fallback */
+ char fallback_n = 'n'; /* English letter response as fallback */
+
+ if (fallback_y == *msg_yes || fallback_y == *msg_no)
+ fallback_y = '\0'; /* conflict or duplication, don't use */
+ if (fallback_n == *msg_yes || fallback_n == *msg_no)
+ fallback_n = '\0'; /* conflict or duplication, don't use */
+
+ if (Dft == DFT_CONFIRM)
+ HTSprintf0(&msg, "%s (%c/%c) ", Msg, *msg_yes, *msg_no);
+ else
+ HTSprintf0(&msg, "%s (%c) ", Msg, (Dft == YES) ? *msg_yes : *msg_no);
+ if (LYTraceLogFP) {
+ CTRACE((tfp, "Confirm: %s", msg));
+ }
+ _statusline(msg);
+ FREE(msg);
+
+ while (result < 0) {
+ int c = LYgetch_single();
+
+#ifdef VMS
+ if (HadVMSInterrupt) {
+ HadVMSInterrupt = FALSE;
+ c = TOUPPER(*msg_no);
+ }
+#endif /* VMS */
+ if (c == TOUPPER(*msg_yes)) {
+ result = YES;
+ } else if (c == TOUPPER(*msg_no)) {
+ result = NO;
+ } else if (fallback_y && c == fallback_y) {
+ result = YES;
+ } else if (fallback_n && c == fallback_n) {
+ result = NO;
+ } else if (LYCharIsINTERRUPT(c)) { /* remember we had ^G or ^C */
+ conf_cancelled = YES;
+ result = NO;
+ } else if (Dft != DFT_CONFIRM) {
+ result = Dft;
+ break;
+ }
+ }
+ CTRACE((tfp, "- %s%s.\n",
+ (result != NO) ? "YES" : "NO",
+ conf_cancelled ? ", cancelled" : ""));
+ }
+ return (result);
+}
+
+/* Seek confirmation. HTConfirm()
+ * ------------------
+ */
+BOOL HTConfirm(const char *Msg)
+{
+ return (BOOL) HTConfirmDefault(Msg, DFT_CONFIRM);
+}
+
+/*
+ * Ask a post resubmission prompt with some indication of what would
+ * be resubmitted, useful especially for going backward in history.
+ * Try to use parts of the address or, if given, the title, depending
+ * on how much fits on the statusline.
+ * if_imgmap and if_file indicate how to handle an address that is
+ * a "LYNXIMGMAP:", or a "file:" URL (presumably the List Page file),
+ * respectively: 0: auto-deny, 1: auto-confirm, 2: prompt.
+ * - kw
+ */
+
+BOOL confirm_post_resub(const char *address,
+ const char *title,
+ int if_imgmap,
+ int if_file)
+{
+ size_t len1;
+ const char *msg = CONFIRM_POST_RESUBMISSION_TO;
+ char buf[240];
+ char *temp = NULL;
+ BOOL res;
+ size_t maxlen = (size_t) (LYcolLimit - 5);
+
+ if (!address) {
+ return (NO);
+ } else if (isLYNXIMGMAP(address)) {
+ if (if_imgmap <= 0)
+ return (NO);
+ else if (if_imgmap == 1)
+ return (YES);
+ else
+ msg = CONFIRM_POST_LIST_RELOAD;
+ } else if (isFILE_URL(address)) {
+ if (if_file <= 0)
+ return (NO);
+ else if (if_file == 1)
+ return (YES);
+ else
+ msg = CONFIRM_POST_LIST_RELOAD;
+ } else if (dump_output_immediately) {
+ return (NO);
+ }
+ if (maxlen >= sizeof(buf))
+ maxlen = sizeof(buf) - 1;
+ if ((len1 = strlen(msg)) +
+ strlen(address) <= maxlen) {
+ sprintf(buf, msg, address);
+ return HTConfirm(buf);
+ }
+ if (len1 + strlen(temp = HTParse(address, "",
+ PARSE_ACCESS + PARSE_HOST + PARSE_PATH
+ + PARSE_PUNCTUATION)) <= maxlen) {
+ sprintf(buf, msg, temp);
+ res = HTConfirm(buf);
+ FREE(temp);
+ return (res);
+ }
+ FREE(temp);
+ if (title && (len1 + strlen(title) <= maxlen)) {
+ sprintf(buf, msg, title);
+ return HTConfirm(buf);
+ }
+ if (len1 + strlen(temp = HTParse(address, "",
+ PARSE_ACCESS + PARSE_HOST
+ + PARSE_PUNCTUATION)) <= maxlen) {
+ sprintf(buf, msg, temp);
+ res = HTConfirm(buf);
+ FREE(temp);
+ return (res);
+ }
+ FREE(temp);
+ if ((temp = HTParse(address, "", PARSE_HOST)) && *temp &&
+ len1 + strlen(temp) <= maxlen) {
+ sprintf(buf, msg, temp);
+ res = HTConfirm(buf);
+ FREE(temp);
+ return (res);
+ }
+ FREE(temp);
+ return HTConfirm(CONFIRM_POST_RESUBMISSION);
+}
+
+/* Prompt for answer and get text back. HTPrompt()
+ * ------------------------------------
+ */
+char *HTPrompt(const char *Msg, const char *deflt)
+{
+ char *rep = NULL;
+ bstring *data = NULL;
+
+ _statusline(Msg);
+ BStrCopy0(data, deflt ? deflt : "");
+
+ if (!dump_output_immediately)
+ (void) LYgetBString(&data, FALSE, 0, NORECALL);
+
+ StrAllocCopy(rep, data->str);
+
+ BStrFree(data);
+ return rep;
+}
+
+/*
+ * Prompt for password without echoing the reply. HTPromptPassword()
+ * ----------------------------------------------
+ */
+char *HTPromptPassword(const char *Msg, const char *given)
+{
+ char *result = NULL;
+ bstring *data = NULL;
+
+ if (isEmpty(given))
+ given = "";
+ if (!dump_output_immediately) {
+ _statusline(Msg ? Msg : PASSWORD_PROMPT);
+ BStrCopy0(data, given);
+ (void) LYgetBString(&data, TRUE, 0, NORECALL);
+ StrAllocCopy(result, data->str);
+ BStrFree(data);
+ } else {
+ printf("\n%s\n", PASSWORD_REQUIRED);
+ StrAllocCopy(result, given);
+ }
+ return result;
+}
+
+/* Prompt both username and password. HTPromptUsernameAndPassword()
+ * ----------------------------------
+ *
+ * On entry,
+ * Msg is the prompting message.
+ * *username and
+ * *password are char pointers which contain default
+ * or zero-length strings; they are changed
+ * to point to result strings.
+ * IsProxy should be TRUE if this is for
+ * proxy authentication.
+ *
+ * If *username is not NULL, it is taken
+ * to point to a default value.
+ * Initial value of *password is
+ * completely discarded.
+ *
+ * On exit,
+ * *username and *password point to newly allocated
+ * strings -- original strings pointed to by them
+ * are NOT freed.
+ *
+ */
+void HTPromptUsernameAndPassword(const char *Msg,
+ char **username,
+ char **password,
+ int IsProxy)
+{
+ if ((IsProxy == FALSE &&
+ authentication_info[0] && authentication_info[1]) ||
+ (IsProxy == TRUE &&
+ proxyauth_info[0] && proxyauth_info[1])) {
+ /*
+ * The -auth or -pauth parameter gave us both the username
+ * and password to use for the first realm or proxy server,
+ * respectively, so just use them without any prompting. - FM
+ */
+ StrAllocCopy(*username, (IsProxy ?
+ proxyauth_info[0] : authentication_info[0]));
+ if (IsProxy) {
+ FREE(proxyauth_info[0]);
+ } else {
+ FREE(authentication_info[0]);
+ }
+ StrAllocCopy(*password, (IsProxy ?
+ proxyauth_info[1] : authentication_info[1]));
+ if (IsProxy) {
+ FREE(proxyauth_info[1]);
+ } else {
+ FREE(authentication_info[1]);
+ }
+ } else if (dump_output_immediately) {
+ /*
+ * We are not interactive and don't have both the
+ * username and password from the command line,
+ * but might have one or the other. - FM
+ */
+ if ((IsProxy == FALSE && authentication_info[0]) ||
+ (IsProxy == TRUE && proxyauth_info[0])) {
+ /*
+ * Use the command line username. - FM
+ */
+ StrAllocCopy(*username, (IsProxy ?
+ proxyauth_info[0] : authentication_info[0]));
+ if (IsProxy) {
+ FREE(proxyauth_info[0]);
+ } else {
+ FREE(authentication_info[0]);
+ }
+ } else if (isEmpty(*username)) {
+ /*
+ * Default to "WWWuser". - FM
+ */
+ StrAllocCopy(*username, "WWWuser");
+ }
+ if ((IsProxy == FALSE && authentication_info[1]) ||
+ (IsProxy == TRUE && proxyauth_info[1])) {
+ /*
+ * Use the command line password. - FM
+ */
+ StrAllocCopy(*password, (IsProxy ?
+ proxyauth_info[1] : authentication_info[1]));
+ if (IsProxy) {
+ FREE(proxyauth_info[1]);
+ } else {
+ FREE(authentication_info[1]);
+ }
+ } else if (isEmpty(*password)) {
+ /*
+ * Default to a zero-length string. - FM
+ */
+ StrAllocCopy(*password, "");
+ }
+ printf("\n%s\n", USERNAME_PASSWORD_REQUIRED);
+
+ } else {
+ /*
+ * We are interactive and don't have both the
+ * username and password from the command line,
+ * but might have one or the other. - FM
+ */
+ if ((IsProxy == FALSE && authentication_info[0]) ||
+ (IsProxy == TRUE && proxyauth_info[0])) {
+ /*
+ * Offer the command line username in the
+ * prompt for the first realm. - FM
+ */
+ StrAllocCopy(*username, (IsProxy ?
+ proxyauth_info[0] : authentication_info[0]));
+ if (IsProxy) {
+ FREE(proxyauth_info[0]);
+ } else {
+ FREE(authentication_info[0]);
+ }
+ }
+ /*
+ * Prompt for confirmation or entry of the username. - FM
+ */
+ if (Msg != NULL) {
+ *username = HTPrompt(Msg, *username);
+ } else {
+ *username = HTPrompt(USERNAME_PROMPT, *username);
+ }
+ if ((IsProxy == FALSE && authentication_info[1]) ||
+ (IsProxy == TRUE && proxyauth_info[1])) {
+ /*
+ * Use the command line password for the first realm. - FM
+ */
+ StrAllocCopy(*password, (IsProxy ?
+ proxyauth_info[1] : authentication_info[1]));
+ if (IsProxy) {
+ FREE(proxyauth_info[1]);
+ } else {
+ FREE(authentication_info[1]);
+ }
+ } else if (non_empty(*username)) {
+ *password = HTPromptPassword(PASSWORD_PROMPT, *password);
+ } else {
+ /*
+ * Return a zero-length password. - FM
+ */
+ StrAllocCopy(*password, "");
+ }
+ }
+}
+
+/* Confirm a cookie operation. HTConfirmCookie()
+ * ---------------------------
+ *
+ * On entry,
+ * server is the server sending the Set-Cookie.
+ * domain is the domain of the cookie.
+ * path is the path of the cookie.
+ * name is the name of the cookie.
+ * value is the value of the cookie.
+ *
+ * On exit,
+ * Returns FALSE on cancel,
+ * TRUE if the cookie should be set.
+ */
+BOOL HTConfirmCookie(domain_entry * de, const char *server,
+ const char *name,
+ const char *value)
+{
+ int ch;
+ const char *prompt = ADVANCED_COOKIE_CONFIRMATION;
+
+ if (de == NULL)
+ return FALSE;
+
+ /* If the user has specified a list of domains to allow or deny
+ * from the config file, then they'll already have de->bv set to
+ * ACCEPT_ALWAYS or REJECT_ALWAYS so we can relax and let the
+ * default cookie handling code cope with this fine.
+ */
+
+ /*
+ * If the user has specified a constant action, don't prompt at all.
+ */
+ if (de->bv == ACCEPT_ALWAYS)
+ return TRUE;
+ if (de->bv == REJECT_ALWAYS)
+ return FALSE;
+
+ if (dump_output_immediately) {
+ /*
+ * Non-interactive, can't respond. Use the LYSetCookies value
+ * based on its compilation or configuration setting, or on the
+ * command line toggle. - FM
+ */
+ return LYSetCookies;
+ }
+
+ /*
+ * Estimate how much of the cookie we can show.
+ */
+ if (!LYAcceptAllCookies) {
+ int namelen, valuelen, space_free, percentage;
+ char *message = 0;
+
+ space_free = (LYcolLimit
+ - (LYstrCells(prompt)
+ - 10) /* %s and %.*s and %.*s chars */
+ -(int) strlen(server));
+ if (space_free < 0)
+ space_free = 0;
+ namelen = (int) strlen(name);
+ valuelen = (int) strlen(value);
+ if ((namelen + valuelen) > space_free) {
+ /*
+ * Argh... there isn't enough space on our single line for
+ * the whole cookie. Reduce them both by a percentage.
+ * This should be smarter.
+ */
+ percentage = (100 * space_free) / (namelen + valuelen);
+ namelen = (percentage * namelen) / 100;
+ valuelen = (percentage * valuelen) / 100;
+ }
+ HTSprintf(&message, prompt, server, namelen, name, valuelen, value);
+ _statusline(message);
+ FREE(message);
+ }
+ for (;;) {
+ if (LYAcceptAllCookies) {
+ ch = 'A';
+ } else {
+ ch = LYgetch_single();
+#if defined(LOCALE) && defined(HAVE_GETTEXT)
+ {
+#define L_PAREN '('
+#define R_PAREN ')'
+ /*
+ * Special-purpose workaround for gettext support (we should do
+ * this in a more general way) -TD
+ *
+ * NOTE TO TRANSLATORS: If the prompt has been rendered into
+ * another language, and if yes/no are distinct, assume the
+ * translator can make an ordered list in parentheses with one
+ * capital letter for each as we assumed in HTConfirmDefault().
+ * The list has to be in the same order as in the original message,
+ * and the four capital letters chosen to not match those in the
+ * original unless they have the same position.
+ *
+ * Example:
+ * (Y/N/Always/neVer) - English (original)
+ * (O/N/Toujours/Jamais) - French
+ */
+ char *p = gettext("Y/N/A/V"); /* placeholder for comment */
+ const char *s = "YNAV\007\003"; /* see ADVANCED_COOKIE_CONFIRMATION */
+
+ if (StrChr(s, ch) == 0
+ && isalpha(ch)
+ && (p = strrchr(prompt, L_PAREN)) != 0) {
+
+ CTRACE((tfp, "Looking for %c in %s\n", ch, p));
+ while (*p != R_PAREN && *p != 0 && isalpha(UCH(*s))) {
+ if (isalpha(UCH(*p)) && (*p == TOUPPER(*p))) {
+ CTRACE((tfp, "...testing %c/%c\n", *p, *s));
+ if (*p == ch) {
+ ch = *s;
+ break;
+ }
+ ++s;
+ }
+ ++p;
+ }
+ }
+ }
+#endif
+ }
+#ifdef VMS
+ if (HadVMSInterrupt) {
+ HadVMSInterrupt = FALSE;
+ ch = 'N';
+ }
+#endif /* VMS */
+ switch (ch) {
+ case 'A':
+ /*
+ * Set to accept all cookies for this domain.
+ */
+ de->bv = ACCEPT_ALWAYS;
+ HTUserMsg2(ALWAYS_ALLOWING_COOKIES, de->domain);
+ return TRUE;
+
+ case 'N':
+ /*
+ * Reject the cookie.
+ */
+ reject:
+ HTUserMsg(REJECTING_COOKIE);
+ return FALSE;
+
+ case 'V':
+ /*
+ * Set to reject all cookies from this domain.
+ */
+ de->bv = REJECT_ALWAYS;
+ HTUserMsg2(NEVER_ALLOWING_COOKIES, de->domain);
+ return FALSE;
+
+ case 'Y':
+ /*
+ * Accept the cookie.
+ */
+ HTInfoMsg(ALLOWING_COOKIE);
+ return TRUE;
+
+ default:
+ if (LYCharIsINTERRUPT(ch))
+ goto reject;
+ continue;
+ }
+ }
+}
+
+/* Confirm redirection of POST. HTConfirmPostRedirect()
+ * ----------------------------
+ *
+ * On entry,
+ * Redirecting_url is the Location.
+ * server_status is the server status code.
+ *
+ * On exit,
+ * Returns 0 on cancel,
+ * 1 for redirect of POST with content,
+ * 303 for redirect as GET without content
+ */
+int HTConfirmPostRedirect(const char *Redirecting_url, int server_status)
+{
+ int result = -1;
+ char *show_POST_url = NULL;
+ char *StatusInfo = 0;
+ char *url = 0;
+ int on_screen = 0; /* 0 - show menu
+
+ * 1 - show url
+ * 2 - menu is already on screen */
+
+ if (server_status == 303 ||
+ server_status == 302) {
+ /*
+ * HTTP.c should not have called us for either of
+ * these because we're treating 302 as historical,
+ * so just return 303. - FM
+ */
+ return 303;
+ }
+
+ if (dump_output_immediately) {
+ if (server_status == 301) {
+ /*
+ * Treat 301 as historical, i.e., like 303 (GET
+ * without content), when not interactive. - FM
+ */
+ return 303;
+ } else {
+ /*
+ * Treat anything else (e.g., 305, 306 or 307) as too
+ * dangerous to redirect without confirmation, and thus
+ * cancel when not interactive. - FM
+ */
+ return 0;
+ }
+ }
+
+ if (user_mode == NOVICE_MODE) {
+ on_screen = 2;
+ LYmove(LYlines - 2, 0);
+ HTSprintf0(&StatusInfo, SERVER_ASKED_FOR_REDIRECTION, server_status);
+ LYaddstr(StatusInfo);
+ LYclrtoeol();
+ LYmove(LYlines - 1, 0);
+ HTSprintf0(&url, "URL: %.*s",
+ (LYcols < 250 ? LYcolLimit - 5 : 250), Redirecting_url);
+ LYaddstr(url);
+ LYclrtoeol();
+ if (server_status == 301) {
+ _statusline(PROCEED_GET_CANCEL);
+ } else {
+ _statusline(PROCEED_OR_CANCEL);
+ }
+ } else {
+ HTSprintf0(&StatusInfo, "%d %.*s",
+ server_status,
+ 251,
+ ((server_status == 301) ?
+ ADVANCED_POST_GET_REDIRECT :
+ ADVANCED_POST_REDIRECT));
+ StrAllocCopy(show_POST_url, LOCATION_HEADER);
+ StrAllocCat(show_POST_url, Redirecting_url);
+ }
+ while (result < 0) {
+ int c;
+
+ switch (on_screen) {
+ case 0:
+ _statusline(StatusInfo);
+ break;
+ case 1:
+ _statusline(show_POST_url);
+ }
+ c = LYgetch_single();
+ switch (c) {
+ case 'P':
+ /*
+ * Proceed with 301 or 307 redirect of POST
+ * with same method and POST content. - FM
+ */
+ FREE(show_POST_url);
+ result = 1;
+ break;
+
+ case 7:
+ case 'C':
+ /*
+ * Cancel request.
+ */
+ FREE(show_POST_url);
+ result = 0;
+ break;
+
+ case 'U':
+ /*
+ * Show URL for intermediate or advanced mode.
+ */
+ if (user_mode != NOVICE_MODE) {
+ if (on_screen == 1) {
+ on_screen = 0;
+ } else {
+ on_screen = 1;
+ }
+ }
+ break;
+
+ case 'G':
+ if (server_status == 301) {
+ /*
+ * Treat as 303 (GET without content).
+ */
+ FREE(show_POST_url);
+ result = 303;
+ break;
+ }
+ /* FALLTHRU */
+
+ default:
+ /*
+ * Get another character.
+ */
+ if (on_screen == 1) {
+ on_screen = 0;
+ } else {
+ on_screen = 2;
+ }
+ }
+ }
+ FREE(StatusInfo);
+ FREE(url);
+ return (result);
+}
+
+#define okToSleep() (!crawl && !traversal && LYCursesON && !no_pause)
+
+/*
+ * Sleep for the given message class's time.
+ */
+void LYSleepAlert(void)
+{
+ if (okToSleep())
+ LYSleep(AlertSecs);
+}
+
+void LYSleepDelay(void)
+{
+ if (okToSleep())
+ LYSleep(DelaySecs);
+}
+
+void LYSleepInfo(void)
+{
+ if (okToSleep())
+ LYSleep(InfoSecs);
+}
+
+void LYSleepMsg(void)
+{
+ if (okToSleep())
+ LYSleep(MessageSecs);
+}
+
+#ifdef USE_CMD_LOGGING
+void LYSleepReplay(void)
+{
+ if (okToSleep())
+ LYSleep(ReplaySecs);
+}
+#endif /* USE_CMD_LOGGING */
+
+/*
+ * LYstrerror emulates the ANSI strerror() function.
+ */
+#ifndef LYStrerror
+char *LYStrerror(int code)
+{
+ static char temp[80];
+
+ sprintf(temp, "System errno is %d.\r\n", code);
+ return temp;
+}
+#endif /* HAVE_STRERROR */