about summary refs log tree commit diff stats
path: root/src/LYExtern.c
blob: 6c0da690ccb94ce88c8bae3e8f96b620670c825d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
/*
 * $LynxId: LYExtern.c,v 1.38 2007/07/24 22:50:21 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) (((x) >= '0' && (x) <= '9') ?               \
		     ((x) - '0') : (toupper(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(*(s + 1)) && isxdigit(*(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
/*
 * Quote the path to make it safe for shell command processing.
 *  We always quote it not only includes spaces in it.
 *  At least we should quote paths which include "&".
 */
char *quote_pathname(char *pathname)
{
    char *result = NULL;

    HTSprintf0(&result, "\"%s\"", pathname);
    return result;
}

/*
 *  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 (strnicmp("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 (strnicmp("telnet://", param, 9) == 0) {
	char host[sizeof(pram_string)];
	int last_pos;

	LYstrncpy(host, param + 9, sizeof(host));
	last_pos = strlen(host) - 1;
	if (last_pos > 1 && host[last_pos] == '/')
	    host[last_pos] = '\0';

	format(&cmdbuf, command, host);
    } else if (strnicmp("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,
			     BOOL only_overriders)
{
    int pass, num_disabled, num_matched, num_choices, cur_choice;
    int length = 0;
    char *cmdbuf = NULL;
    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)
			    choices[num_choices] = cmdbuf;
		    }
		    num_choices++;
		}
	    }
	}
	if (length > 1) {
	    if (pass == 0) {
		choices = typecallocn(char *, length + 1);
	    } else {
		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,
				       (const char **) choices,
				       -1,
				       -1,
				       FALSE,
				       TRUE,
				       (keypad_mode != NUMBERS_AS_ARROWS));
	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 = choices[pass];
	    } else {
		FREE(choices[pass]);
	    }
	}
	FREE(choices);
    }
    return cmdbuf;
}

BOOL run_external(char *param,
		  BOOL 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 = 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 (strnicmp(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 */