summaryrefslogtreecommitdiffstats
path: root/src/LYExtern.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:37:15 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:37:15 +0000
commitae5d181b854d3ccb373b6bc01b4869e44ff4d87a (patch)
tree91f59efb48c56a84cc798e012fccb667b63d3fee /src/LYExtern.c
parentInitial commit. (diff)
downloadlynx-ae5d181b854d3ccb373b6bc01b4869e44ff4d87a.tar.xz
lynx-ae5d181b854d3ccb373b6bc01b4869e44ff4d87a.zip
Adding upstream version 2.9.0dev.12.upstream/2.9.0dev.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/LYExtern.c')
-rw-r--r--src/LYExtern.c443
1 files changed, 443 insertions, 0 deletions
diff --git a/src/LYExtern.c b/src/LYExtern.c
new file mode 100644
index 0000000..d36ba0b
--- /dev/null
+++ b/src/LYExtern.c
@@ -0,0 +1,443 @@
+/*
+ * $LynxId: LYExtern.c,v 1.55 2018/02/15 01:53:07 tom Exp $
+ *
+ External application support.
+ This feature allows lynx to pass a given URL to an external program.
+ It was written for three reasons.
+ 1) To overcome the deficiency of Lynx_386 not supporting ftp and news.
+ External programs can be used instead by passing the URL.
+
+ 2) To allow for background transfers in multitasking systems.
+ I use wget for http and ftp transfers via the external command.
+
+ 3) To allow for new URLs to be used through lynx.
+ URLs can be made up such as mymail: to spawn desired applications
+ via the external command.
+
+ See lynx.cfg for other info.
+*/
+
+#include <LYUtils.h>
+
+#ifdef USE_EXTERNALS
+
+#include <HTAlert.h>
+#include <LYGlobalDefs.h>
+#include <LYExtern.h>
+#include <LYLeaks.h>
+#include <LYCurses.h>
+#include <LYReadCFG.h>
+#include <LYStrings.h>
+
+#ifdef WIN_EX
+/* ASCII char -> HEX digit */
+#define ASC2HEXD(x) ((UCH(x) >= '0' && UCH(x) <= '9') ? \
+ (UCH(x) - '0') : (toupper(UCH(x)) - 'A' + 10))
+
+/* Decodes the forms %xy in a URL to the character the hexadecimal
+ code of which is xy. xy are hexadecimal digits from
+ [0123456789ABCDEF] (case-insensitive). If x or y are not hex-digits
+ or '%' is near '\0', the whole sequence is inserted literally. */
+
+static char *decode_string(char *s)
+{
+ char *save_s;
+ char *p = s;
+
+ save_s = s;
+ for (; *s; s++, p++) {
+ if (*s != '%')
+ *p = *s;
+ else {
+ /* Do nothing if at the end of the string. Or if the chars
+ are not hex-digits. */
+ if (!*(s + 1) || !*(s + 2)
+ || !(isxdigit(UCH(*(s + 1))) && isxdigit(UCH(*(s + 2))))) {
+ *p = *s;
+ continue;
+ }
+ *p = (char) ((ASC2HEXD(*(s + 1)) << 4) + ASC2HEXD(*(s + 2)));
+ s += 2;
+ }
+ }
+ *p = '\0';
+ return save_s;
+}
+#endif /* WIN_EX */
+
+#ifdef WIN_EX
+/*
+ * Delete dangerous characters as local path.
+ * We delete '<>|' and also '%"'.
+ * '%' should be deleted because it's difficut to escape for all cases.
+ * So we can't treat paths which include '%'.
+ * '"' should be deleted because it's a obstacle to quote whole path.
+ */
+static void delete_danger_characters(char *src)
+{
+ char *dst;
+
+ for (dst = src; *src != '\0'; src++) {
+ if (StrChr("<>|%\"", *src) == NULL) {
+ *dst = *src;
+ dst++;
+ }
+ }
+ *dst = '\0';
+}
+
+static char *escapeParameter(CONST char *parameter)
+{
+ size_t i;
+ size_t last = strlen(parameter);
+ size_t n = 0;
+ size_t encoded = 0;
+ size_t escaped = 0;
+ char *result;
+ char *needs_encoded = "<>|";
+ char *needs_escaped = "%";
+ char *needs_escaped_NT = "%&^";
+
+ for (i = 0; i < last; ++i) {
+ if (StrChr(needs_encoded, parameter[i]) != NULL) {
+ ++encoded;
+ }
+ if (system_is_NT) {
+ if (StrChr(needs_escaped_NT, parameter[i]) != NULL) {
+ ++escaped;
+ }
+ } else if (StrChr(needs_escaped, parameter[i]) != NULL) {
+ ++escaped;
+ }
+ }
+
+ result = (char *) malloc(last + encoded * 2 + escaped + 1);
+ if (result == NULL)
+ outofmem(__FILE__, "escapeParameter");
+
+ n = 0;
+ for (i = 0; i < last; i++) {
+ if (StrChr(needs_encoded, parameter[i]) != NULL) {
+ sprintf(result + n, "%%%02X", (unsigned char) parameter[i]);
+ n += 3;
+ continue;
+ }
+ if (system_is_NT) {
+ if (StrChr(needs_escaped_NT, parameter[i]) != NULL) {
+ result[n++] = '^';
+ result[n++] = parameter[i];
+ continue;
+ }
+ } else if (StrChr(needs_escaped, parameter[i]) != NULL) {
+ result[n++] = '%'; /* parameter[i] is '%' */
+ result[n++] = parameter[i];
+ continue;
+ }
+ result[n++] = parameter[i];
+ }
+ result[n] = '\0';
+
+ return result;
+}
+#endif /* WIN_EX */
+
+static void format(char **result,
+ char *fmt,
+ char *parm)
+{
+ *result = NULL;
+ HTAddParam(result, fmt, 1, parm);
+ HTEndParam(result, fmt, 1);
+}
+
+/*
+ * Format the given command into a buffer, returning the resulting string.
+ *
+ * It is too dangerous to leave any URL that may come along unquoted. They
+ * often contain '&', ';', and '?' chars, and who knows what else may occur.
+ * Prevent spoofing of the shell. Dunno how this needs to be modified for VMS
+ * or DOS. - kw
+ */
+static char *format_command(char *command,
+ char *param)
+{
+ char *cmdbuf = NULL;
+
+#if defined(WIN_EX)
+ char pram_string[LY_MAXPATH];
+ char *escaped = NULL;
+
+ if (strncasecomp("file://localhost/", param, 17) == 0) {
+ /* decode local path parameter for programs to be
+ able to interpret - TH */
+ LYStrNCpy(pram_string, param, sizeof(pram_string) - 1);
+ decode_string(pram_string);
+ param = pram_string;
+ } else {
+ /* encode or escape URL parameter - TH */
+ escaped = escapeParameter(param);
+ param = escaped;
+ }
+
+ if (isMAILTO_URL(param)) {
+ format(&cmdbuf, command, param + 7);
+ } else if (strncasecomp("telnet://", param, 9) == 0) {
+ char host[sizeof(pram_string)];
+ int last_pos;
+
+ LYStrNCpy(host, param + 9, sizeof(host));
+ last_pos = (int) strlen(host) - 1;
+ if (last_pos > 1 && host[last_pos] == '/')
+ host[last_pos] = '\0';
+
+ format(&cmdbuf, command, host);
+ } else if (strncasecomp("file://localhost/", param, 17) == 0) {
+ char e_buff[LY_MAXPATH], *p;
+
+ p = param + 17;
+ delete_danger_characters(p);
+ *e_buff = 0;
+ if (StrChr(p, ':') == NULL) {
+ sprintf(e_buff, "%.3s/", windows_drive);
+ }
+ strncat(e_buff, p, sizeof(e_buff) - strlen(e_buff) - 1);
+ p = strrchr(e_buff, '.');
+ if (p) {
+ trimPoundSelector(p);
+ }
+
+ /* Less ==> short filename with backslashes,
+ * less ==> long filename with forward slashes, may be quoted
+ */
+ if (ISUPPER(command[0])) {
+ char *short_name = HTDOS_short_name(e_buff);
+
+ p = quote_pathname(short_name);
+ format(&cmdbuf, command, p);
+ FREE(p);
+ } else {
+ p = quote_pathname(e_buff);
+ format(&cmdbuf, command, p);
+ FREE(p);
+ }
+ } else {
+ format(&cmdbuf, command, param);
+ }
+ FREE(escaped);
+#else
+ format(&cmdbuf, command, param);
+#endif
+ return cmdbuf;
+}
+
+/*
+ * Find the EXTERNAL command which matches the given name 'param'. If there is
+ * more than one possibility, make a popup menu of the matching commands and
+ * allow the user to select one. Return the selected command.
+ */
+static char *lookup_external(char *param,
+ int only_overriders)
+{
+ int pass, num_disabled, num_matched, num_choices, cur_choice;
+ size_t length = 0;
+ char *cmdbuf = NULL;
+ char **actions = 0;
+ char **choices = 0;
+ lynx_list_item_type *ptr = 0;
+
+ for (pass = 0; pass < 2; pass++) {
+ num_disabled = 0;
+ num_matched = 0;
+ num_choices = 0;
+ for (ptr = externals; ptr != 0; ptr = ptr->next) {
+
+ if (match_item_by_name(ptr, param, only_overriders)) {
+ ++num_matched;
+ CTRACE((tfp, "EXTERNAL: '%s' <==> '%s'\n", ptr->name, param));
+ if (no_externals && !ptr->always_enabled && !only_overriders) {
+ ++num_disabled;
+ } else {
+ if (pass == 0) {
+ length++;
+ } else if (pass != 0) {
+ cmdbuf = format_command(ptr->command, param);
+ if (length > 1) {
+ actions[num_choices] = cmdbuf;
+ choices[num_choices] =
+ format_command(ptr->menu_name, param);
+ }
+ }
+ num_choices++;
+ }
+ }
+ }
+ if (length > 1) {
+ if (pass == 0) {
+ actions = typecallocn(char *, length + 1);
+ choices = typecallocn(char *, length + 1);
+
+ if (actions == 0 || choices == 0)
+ outofmem(__FILE__, "lookup_external");
+ } else {
+ actions[num_choices] = 0;
+ choices[num_choices] = 0;
+ }
+ }
+ }
+
+ if (num_disabled != 0
+ && num_disabled == num_matched) {
+ HTUserMsg(EXTERNALS_DISABLED);
+ } else if (num_choices > 1) {
+ int old_y, old_x;
+
+ LYGetYX(old_y, old_x);
+ cur_choice = LYhandlePopupList(-1,
+ 0,
+ old_x,
+ (STRING2PTR) choices,
+ -1,
+ -1,
+ FALSE,
+ TRUE);
+ wmove(LYwin, old_y, old_x);
+ CTRACE((tfp, "selected choice %d of %d\n", cur_choice, num_choices));
+ if (cur_choice < 0) {
+ HTInfoMsg(CANCELLED);
+ cmdbuf = 0;
+ }
+ for (pass = 0; choices[pass] != 0; pass++) {
+ if (pass == cur_choice) {
+ cmdbuf = actions[pass];
+ } else {
+ FREE(actions[pass]);
+ }
+ FREE(choices[pass]);
+ }
+ }
+
+ if (actions) {
+ for (pass = 0; actions[pass] != 0; ++pass) {
+ if (actions[pass] != cmdbuf)
+ FREE(actions[pass]);
+ }
+ FREE(actions);
+ }
+
+ if (choices) {
+ for (pass = 0; choices[pass] != 0; ++pass) {
+ FREE(choices[pass]);
+ }
+ FREE(choices);
+ }
+
+ return cmdbuf;
+}
+
+BOOL run_external(char *param,
+ int only_overriders)
+{
+#ifdef WIN_EX
+ int status;
+#endif
+ int redraw_flag = TRUE;
+ char *cmdbuf = NULL;
+ BOOL found = FALSE;
+ int confirmed = TRUE;
+
+ if (externals == NULL)
+ return 0;
+
+#ifdef WIN_EX /* 1998/01/26 (Mon) 09:16:13 */
+ if (param == NULL) {
+ HTInfoMsg(gettext("External command is null"));
+ return 0;
+ }
+#endif
+
+ cmdbuf = lookup_external(param, only_overriders);
+ if (non_empty(cmdbuf)) {
+#ifdef WIN_EX /* 1997/10/17 (Fri) 14:07:50 */
+ int len;
+ char buff[LY_MAXPATH];
+
+ CTRACE((tfp, "Lynx EXTERNAL: '%s'\n", cmdbuf));
+#ifdef WIN_GUI /* 1997/11/06 (Thu) 14:17:15 */
+ confirmed = MessageBox(GetForegroundWindow(), cmdbuf,
+ "Lynx (EXTERNAL COMMAND EXEC)",
+ MB_ICONQUESTION | MB_SETFOREGROUND | MB_OKCANCEL)
+ != IDCANCEL;
+#else
+ confirmed = HTConfirm(LYElideString(cmdbuf, 40)) != NO;
+#endif
+ if (confirmed) {
+ len = (int) strlen(cmdbuf);
+ if (len > 255) {
+ sprintf(buff, "Lynx: command line too long (%d > 255)", len);
+#ifdef WIN_GUI /* 1997/11/06 (Thu) 14:17:02 */
+ MessageBox(GetForegroundWindow(), buff,
+ "Lynx (EXTERNAL COMMAND EXEC)",
+ MB_ICONEXCLAMATION | MB_SETFOREGROUND | MB_OK);
+ SetConsoleTitle("Lynx for Win32");
+#else
+ HTConfirm(LYElideString(buff, 40));
+#endif
+ confirmed = FALSE;
+ } else {
+ SetConsoleTitle(cmdbuf);
+ }
+ }
+
+ if (strncasecomp(cmdbuf, "start ", 6) == 0)
+ redraw_flag = FALSE;
+ else
+ redraw_flag = TRUE;
+#else
+ HTUserMsg(cmdbuf);
+#endif
+ found = TRUE;
+ if (confirmed) {
+ if (redraw_flag) {
+ stop_curses();
+ fflush(stdout);
+ }
+
+ /* command running. */
+#ifdef WIN_EX /* 1997/10/17 (Fri) 14:07:50 */
+#if defined(__CYGWIN__) || defined(__MINGW32__)
+ status = system(cmdbuf);
+#else
+ status = xsystem(cmdbuf);
+#endif
+ if (status != 0) {
+ sprintf(buff,
+ "EXEC code = %04x (%2d, %2d)\r\n"
+ "'%s'",
+ status, (status / 256), (status & 0xff),
+ cmdbuf);
+#ifdef SH_EX /* WIN_GUI for ERROR only */
+ MessageBox(GetForegroundWindow(), buff,
+ "Lynx (EXTERNAL COMMAND EXEC)",
+ MB_ICONSTOP | MB_SETFOREGROUND | MB_OK);
+#else
+ HTConfirm(LYElideString(buff, 40));
+#endif /* 1 */
+ }
+#else /* Not WIN_EX */
+ LYSystem(cmdbuf);
+#endif /* WIN_EX */
+
+#if defined(WIN_EX)
+ SetConsoleTitle("Lynx for Win32");
+#endif
+ if (redraw_flag) {
+ fflush(stdout);
+ start_curses();
+ }
+ }
+ }
+
+ FREE(cmdbuf);
+ return found;
+}
+#endif /* USE_EXTERNALS */