GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: main.c,v 1.119 2017/01/24 23:47:34 beck Exp $ */ |
||
2 |
/* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */ |
||
3 |
|||
4 |
/* |
||
5 |
* Copyright (C) 1997 and 1998 WIDE Project. |
||
6 |
* All rights reserved. |
||
7 |
* |
||
8 |
* Redistribution and use in source and binary forms, with or without |
||
9 |
* modification, are permitted provided that the following conditions |
||
10 |
* are met: |
||
11 |
* 1. Redistributions of source code must retain the above copyright |
||
12 |
* notice, this list of conditions and the following disclaimer. |
||
13 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
14 |
* notice, this list of conditions and the following disclaimer in the |
||
15 |
* documentation and/or other materials provided with the distribution. |
||
16 |
* 3. Neither the name of the project nor the names of its contributors |
||
17 |
* may be used to endorse or promote products derived from this software |
||
18 |
* without specific prior written permission. |
||
19 |
* |
||
20 |
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
||
21 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
22 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
23 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
||
24 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
25 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
26 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
27 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
28 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
29 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
30 |
* SUCH DAMAGE. |
||
31 |
*/ |
||
32 |
|||
33 |
/* |
||
34 |
* Copyright (c) 1985, 1989, 1993, 1994 |
||
35 |
* The Regents of the University of California. All rights reserved. |
||
36 |
* |
||
37 |
* Redistribution and use in source and binary forms, with or without |
||
38 |
* modification, are permitted provided that the following conditions |
||
39 |
* are met: |
||
40 |
* 1. Redistributions of source code must retain the above copyright |
||
41 |
* notice, this list of conditions and the following disclaimer. |
||
42 |
* 2. Redistributions in binary form must reproduce the above copyright |
||
43 |
* notice, this list of conditions and the following disclaimer in the |
||
44 |
* documentation and/or other materials provided with the distribution. |
||
45 |
* 3. Neither the name of the University nor the names of its contributors |
||
46 |
* may be used to endorse or promote products derived from this software |
||
47 |
* without specific prior written permission. |
||
48 |
* |
||
49 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
||
50 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
51 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
52 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
||
53 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||
54 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||
55 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||
56 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||
57 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||
58 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||
59 |
* SUCH DAMAGE. |
||
60 |
*/ |
||
61 |
|||
62 |
/* |
||
63 |
* FTP User Program -- Command Interface. |
||
64 |
*/ |
||
65 |
#include <sys/types.h> |
||
66 |
#include <sys/socket.h> |
||
67 |
|||
68 |
#include <ctype.h> |
||
69 |
#include <err.h> |
||
70 |
#include <netdb.h> |
||
71 |
#include <pwd.h> |
||
72 |
#include <stdio.h> |
||
73 |
#include <errno.h> |
||
74 |
#include <stdlib.h> |
||
75 |
#include <string.h> |
||
76 |
#include <unistd.h> |
||
77 |
|||
78 |
#include <tls.h> |
||
79 |
|||
80 |
#include "cmds.h" |
||
81 |
#include "ftp_var.h" |
||
82 |
|||
83 |
int connect_timeout; |
||
84 |
|||
85 |
#ifndef NOSSL |
||
86 |
char * const ssl_verify_opts[] = { |
||
87 |
#define SSL_CAFILE 0 |
||
88 |
"cafile", |
||
89 |
#define SSL_CAPATH 1 |
||
90 |
"capath", |
||
91 |
#define SSL_CIPHERS 2 |
||
92 |
"ciphers", |
||
93 |
#define SSL_DONTVERIFY 3 |
||
94 |
"dont", |
||
95 |
#define SSL_DOVERIFY 4 |
||
96 |
"do", |
||
97 |
#define SSL_VERIFYDEPTH 5 |
||
98 |
"depth", |
||
99 |
#define SSL_MUSTSTAPLE 6 |
||
100 |
"muststaple", |
||
101 |
#define SSL_NOVERIFYTIME 7 |
||
102 |
"noverifytime", |
||
103 |
NULL |
||
104 |
}; |
||
105 |
|||
106 |
struct tls_config *tls_config; |
||
107 |
|||
108 |
static void |
||
109 |
process_ssl_options(char *cp) |
||
110 |
{ |
||
111 |
const char *errstr; |
||
112 |
long long depth; |
||
113 |
char *str; |
||
114 |
|||
115 |
while (*cp) { |
||
116 |
switch (getsubopt(&cp, ssl_verify_opts, &str)) { |
||
117 |
case SSL_CAFILE: |
||
118 |
if (str == NULL) |
||
119 |
errx(1, "missing CA file"); |
||
120 |
if (tls_config_set_ca_file(tls_config, str) != 0) |
||
121 |
errx(1, "tls ca file failed: %s", |
||
122 |
tls_config_error(tls_config)); |
||
123 |
break; |
||
124 |
case SSL_CAPATH: |
||
125 |
if (str == NULL) |
||
126 |
errx(1, "missing CA directory path"); |
||
127 |
if (tls_config_set_ca_path(tls_config, str) != 0) |
||
128 |
errx(1, "tls ca path failed: %s", |
||
129 |
tls_config_error(tls_config)); |
||
130 |
break; |
||
131 |
case SSL_CIPHERS: |
||
132 |
if (str == NULL) |
||
133 |
errx(1, "missing cipher list"); |
||
134 |
if (tls_config_set_ciphers(tls_config, str) != 0) |
||
135 |
errx(1, "tls ciphers failed: %s", |
||
136 |
tls_config_error(tls_config)); |
||
137 |
break; |
||
138 |
case SSL_DONTVERIFY: |
||
139 |
tls_config_insecure_noverifycert(tls_config); |
||
140 |
tls_config_insecure_noverifyname(tls_config); |
||
141 |
break; |
||
142 |
case SSL_DOVERIFY: |
||
143 |
tls_config_verify(tls_config); |
||
144 |
break; |
||
145 |
case SSL_VERIFYDEPTH: |
||
146 |
if (str == NULL) |
||
147 |
errx(1, "missing depth"); |
||
148 |
depth = strtonum(str, 0, INT_MAX, &errstr); |
||
149 |
if (errstr) |
||
150 |
errx(1, "certificate validation depth is %s", |
||
151 |
errstr); |
||
152 |
tls_config_set_verify_depth(tls_config, (int)depth); |
||
153 |
break; |
||
154 |
case SSL_MUSTSTAPLE: |
||
155 |
tls_config_ocsp_require_stapling(tls_config); |
||
156 |
break; |
||
157 |
case SSL_NOVERIFYTIME: |
||
158 |
tls_config_insecure_noverifytime(tls_config); |
||
159 |
break; |
||
160 |
default: |
||
161 |
errx(1, "unknown -S suboption `%s'", |
||
162 |
suboptarg ? suboptarg : ""); |
||
163 |
/* NOTREACHED */ |
||
164 |
} |
||
165 |
} |
||
166 |
} |
||
167 |
#endif /* !NOSSL */ |
||
168 |
|||
169 |
int family = PF_UNSPEC; |
||
170 |
int pipeout; |
||
171 |
|||
172 |
int |
||
173 |
main(volatile int argc, char *argv[]) |
||
174 |
{ |
||
175 |
int ch, rval; |
||
176 |
#ifndef SMALL |
||
177 |
int top; |
||
178 |
#endif |
||
179 |
struct passwd *pw = NULL; |
||
180 |
15 |
char *cp, homedir[PATH_MAX]; |
|
181 |
char *outfile = NULL; |
||
182 |
15 |
const char *errstr; |
|
183 |
int dumb_terminal = 0; |
||
184 |
|||
185 |
15 |
ftpport = "ftp"; |
|
186 |
15 |
httpport = "http"; |
|
187 |
#ifndef NOSSL |
||
188 |
15 |
httpsport = "https"; |
|
189 |
#endif /* !NOSSL */ |
||
190 |
15 |
gateport = getenv("FTPSERVERPORT"); |
|
191 |
✗✓✗✗ |
15 |
if (gateport == NULL || *gateport == '\0') |
192 |
15 |
gateport = "ftpgate"; |
|
193 |
15 |
doglob = 1; |
|
194 |
15 |
interactive = 1; |
|
195 |
15 |
autologin = 1; |
|
196 |
15 |
passivemode = 1; |
|
197 |
15 |
activefallback = 1; |
|
198 |
15 |
preserve = 1; |
|
199 |
15 |
verbose = 0; |
|
200 |
15 |
progress = 0; |
|
201 |
15 |
gatemode = 0; |
|
202 |
#ifndef NOSSL |
||
203 |
15 |
cookiefile = NULL; |
|
204 |
#endif /* NOSSL */ |
||
205 |
#ifndef SMALL |
||
206 |
15 |
editing = 0; |
|
207 |
15 |
el = NULL; |
|
208 |
15 |
hist = NULL; |
|
209 |
15 |
resume = 0; |
|
210 |
15 |
srcaddr = NULL; |
|
211 |
15 |
marg_sl = sl_init(); |
|
212 |
#endif /* !SMALL */ |
||
213 |
15 |
mark = HASHBYTES; |
|
214 |
15 |
epsv4 = 1; |
|
215 |
15 |
epsv4bad = 0; |
|
216 |
|||
217 |
/* Set default operation mode based on FTPMODE environment variable */ |
||
218 |
✗✓✗✗ |
15 |
if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') { |
219 |
if (strcmp(cp, "passive") == 0) { |
||
220 |
passivemode = 1; |
||
221 |
activefallback = 0; |
||
222 |
} else if (strcmp(cp, "active") == 0) { |
||
223 |
passivemode = 0; |
||
224 |
activefallback = 0; |
||
225 |
} else if (strcmp(cp, "gate") == 0) { |
||
226 |
gatemode = 1; |
||
227 |
} else if (strcmp(cp, "auto") == 0) { |
||
228 |
passivemode = 1; |
||
229 |
activefallback = 1; |
||
230 |
} else |
||
231 |
warnx("unknown FTPMODE: %s. Using defaults", cp); |
||
232 |
} |
||
233 |
|||
234 |
✗✓ | 15 |
if (strcmp(__progname, "gate-ftp") == 0) |
235 |
gatemode = 1; |
||
236 |
15 |
gateserver = getenv("FTPSERVER"); |
|
237 |
15 |
if (gateserver == NULL) |
|
238 |
15 |
gateserver = ""; |
|
239 |
✗✓ | 15 |
if (gatemode) { |
240 |
if (*gateserver == '\0') { |
||
241 |
warnx( |
||
242 |
"Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp"); |
||
243 |
gatemode = 0; |
||
244 |
} |
||
245 |
} |
||
246 |
|||
247 |
15 |
cp = getenv("TERM"); |
|
248 |
✓✗✓✗ ✓✗ |
45 |
dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") || |
249 |
✓✗ | 45 |
!strcmp(cp, "emacs") || !strcmp(cp, "su")); |
250 |
✓✗ | 45 |
fromatty = isatty(fileno(stdin)); |
251 |
✓✗ | 15 |
if (fromatty) { |
252 |
15 |
verbose = 1; /* verbose if from a tty */ |
|
253 |
#ifndef SMALL |
||
254 |
✓✗ | 15 |
if (!dumb_terminal) |
255 |
15 |
editing = 1; /* editing mode on if tty is usable */ |
|
256 |
#endif /* !SMALL */ |
||
257 |
} |
||
258 |
|||
259 |
15 |
ttyout = stdout; |
|
260 |
✓✗✗✓ ✗✗ |
45 |
if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc()) |
261 |
progress = 1; /* progress bar on if tty is usable */ |
||
262 |
|||
263 |
#ifndef NOSSL |
||
264 |
15 |
cookiefile = getenv("http_cookies"); |
|
265 |
✗✓ | 15 |
if (tls_init() != 0) |
266 |
errx(1, "tls init failed"); |
||
267 |
✓✗ | 15 |
if (tls_config == NULL) { |
268 |
15 |
tls_config = tls_config_new(); |
|
269 |
✗✓ | 15 |
if (tls_config == NULL) |
270 |
errx(1, "tls config failed"); |
||
271 |
✗✓ | 30 |
if (tls_config_set_protocols(tls_config, |
272 |
15 |
TLS_PROTOCOLS_ALL) != 0) |
|
273 |
errx(1, "tls set protocols failed: %s", |
||
274 |
tls_config_error(tls_config)); |
||
275 |
✗✓ | 15 |
if (tls_config_set_ciphers(tls_config, "legacy") != 0) |
276 |
errx(1, "tls set ciphers failed: %s", |
||
277 |
tls_config_error(tls_config)); |
||
278 |
} |
||
279 |
#endif /* !SMALL */ |
||
280 |
|||
281 |
15 |
httpuseragent = NULL; |
|
282 |
|||
283 |
✓✓ | 135 |
while ((ch = getopt(argc, argv, |
284 |
60 |
"46AaCc:dD:Eegik:Mmno:pP:r:S:s:tU:vVw:")) != -1) { |
|
285 |
✓✗✗✗ ✗✗✗✗ ✗✗✗✗ ✗✗✗✗ ✓✗✗✗ ✗✗✗✗ ✓✗✗✗ |
45 |
switch (ch) { |
286 |
case '4': |
||
287 |
15 |
family = PF_INET; |
|
288 |
15 |
break; |
|
289 |
case '6': |
||
290 |
family = PF_INET6; |
||
291 |
break; |
||
292 |
case 'A': |
||
293 |
activefallback = 0; |
||
294 |
passivemode = 0; |
||
295 |
break; |
||
296 |
|||
297 |
case 'a': |
||
298 |
anonftp = 1; |
||
299 |
break; |
||
300 |
|||
301 |
case 'C': |
||
302 |
#ifndef SMALL |
||
303 |
resume = 1; |
||
304 |
#endif /* !SMALL */ |
||
305 |
break; |
||
306 |
|||
307 |
case 'c': |
||
308 |
#ifndef SMALL |
||
309 |
cookiefile = optarg; |
||
310 |
#endif /* !SMALL */ |
||
311 |
break; |
||
312 |
|||
313 |
case 'D': |
||
314 |
action = optarg; |
||
315 |
break; |
||
316 |
case 'd': |
||
317 |
#ifndef SMALL |
||
318 |
options |= SO_DEBUG; |
||
319 |
debug++; |
||
320 |
#endif /* !SMALL */ |
||
321 |
break; |
||
322 |
|||
323 |
case 'E': |
||
324 |
epsv4 = 0; |
||
325 |
break; |
||
326 |
|||
327 |
case 'e': |
||
328 |
#ifndef SMALL |
||
329 |
editing = 0; |
||
330 |
#endif /* !SMALL */ |
||
331 |
break; |
||
332 |
|||
333 |
case 'g': |
||
334 |
doglob = 0; |
||
335 |
break; |
||
336 |
|||
337 |
case 'i': |
||
338 |
interactive = 0; |
||
339 |
break; |
||
340 |
|||
341 |
case 'k': |
||
342 |
keep_alive_timeout = strtonum(optarg, 0, INT_MAX, |
||
343 |
&errstr); |
||
344 |
if (errstr != NULL) { |
||
345 |
warnx("keep alive amount is %s: %s", errstr, |
||
346 |
optarg); |
||
347 |
usage(); |
||
348 |
} |
||
349 |
break; |
||
350 |
case 'M': |
||
351 |
progress = 0; |
||
352 |
break; |
||
353 |
case 'm': |
||
354 |
progress = -1; |
||
355 |
break; |
||
356 |
|||
357 |
case 'n': |
||
358 |
autologin = 0; |
||
359 |
break; |
||
360 |
|||
361 |
case 'o': |
||
362 |
15 |
outfile = optarg; |
|
363 |
✗✓ | 15 |
if (*outfile == '\0') { |
364 |
pipeout = 0; |
||
365 |
outfile = NULL; |
||
366 |
ttyout = stdout; |
||
367 |
} else { |
||
368 |
15 |
pipeout = strcmp(outfile, "-") == 0; |
|
369 |
15 |
ttyout = pipeout ? stderr : stdout; |
|
370 |
} |
||
371 |
15 |
break; |
|
372 |
|||
373 |
case 'p': |
||
374 |
passivemode = 1; |
||
375 |
activefallback = 0; |
||
376 |
break; |
||
377 |
|||
378 |
case 'P': |
||
379 |
ftpport = optarg; |
||
380 |
break; |
||
381 |
|||
382 |
case 'r': |
||
383 |
retry_connect = strtonum(optarg, 0, INT_MAX, &errstr); |
||
384 |
if (errstr != NULL) { |
||
385 |
warnx("retry amount is %s: %s", errstr, |
||
386 |
optarg); |
||
387 |
usage(); |
||
388 |
} |
||
389 |
break; |
||
390 |
|||
391 |
case 'S': |
||
392 |
#ifndef NOSSL |
||
393 |
process_ssl_options(optarg); |
||
394 |
#endif /* !NOSSL */ |
||
395 |
break; |
||
396 |
|||
397 |
case 's': |
||
398 |
#ifndef SMALL |
||
399 |
srcaddr = optarg; |
||
400 |
#endif /* !SMALL */ |
||
401 |
break; |
||
402 |
|||
403 |
case 't': |
||
404 |
trace = 1; |
||
405 |
break; |
||
406 |
|||
407 |
#ifndef SMALL |
||
408 |
case 'U': |
||
409 |
free (httpuseragent); |
||
410 |
if (strcspn(optarg, "\r\n") != strlen(optarg)) |
||
411 |
errx(1, "Invalid User-Agent: %s.", optarg); |
||
412 |
if (asprintf(&httpuseragent, "User-Agent: %s", |
||
413 |
optarg) == -1) |
||
414 |
errx(1, "Can't allocate memory for HTTP(S) " |
||
415 |
"User-Agent"); |
||
416 |
break; |
||
417 |
#endif /* !SMALL */ |
||
418 |
|||
419 |
case 'v': |
||
420 |
15 |
verbose = 1; |
|
421 |
15 |
break; |
|
422 |
|||
423 |
case 'V': |
||
424 |
verbose = 0; |
||
425 |
break; |
||
426 |
|||
427 |
case 'w': |
||
428 |
connect_timeout = strtonum(optarg, 0, 200, &errstr); |
||
429 |
if (errstr) |
||
430 |
errx(1, "-w: %s", errstr); |
||
431 |
break; |
||
432 |
default: |
||
433 |
usage(); |
||
434 |
} |
||
435 |
} |
||
436 |
15 |
argc -= optind; |
|
437 |
15 |
argv += optind; |
|
438 |
|||
439 |
#ifndef NOSSL |
||
440 |
15 |
cookie_load(); |
|
441 |
#endif /* !NOSSL */ |
||
442 |
✓✗ | 15 |
if (httpuseragent == NULL) |
443 |
15 |
httpuseragent = HTTP_USER_AGENT; |
|
444 |
|||
445 |
15 |
cpend = 0; /* no pending replies */ |
|
446 |
15 |
proxy = 0; /* proxy not active */ |
|
447 |
15 |
crflag = 1; /* strip c.r. on ascii gets */ |
|
448 |
15 |
sendport = -1; /* not using ports */ |
|
449 |
/* |
||
450 |
* Set up the home directory in case we're globbing. |
||
451 |
*/ |
||
452 |
15 |
cp = getlogin(); |
|
453 |
✓✗ | 15 |
if (cp != NULL) { |
454 |
15 |
pw = getpwnam(cp); |
|
455 |
15 |
} |
|
456 |
✗✓ | 15 |
if (pw == NULL) |
457 |
pw = getpwuid(getuid()); |
||
458 |
✓✗ | 15 |
if (pw != NULL) { |
459 |
15 |
(void)strlcpy(homedir, pw->pw_dir, sizeof homedir); |
|
460 |
15 |
home = homedir; |
|
461 |
15 |
} |
|
462 |
|||
463 |
15 |
setttywidth(0); |
|
464 |
15 |
(void)signal(SIGWINCH, setttywidth); |
|
465 |
|||
466 |
✓✗ | 15 |
if (argc > 0) { |
467 |
✓✗ | 15 |
if (isurl(argv[0])) { |
468 |
✗✓ | 15 |
if (pipeout) { |
469 |
#ifndef SMALL |
||
470 |
if (pledge("stdio rpath wpath flock dns tty inet proc exec fattr", |
||
471 |
NULL) == -1) |
||
472 |
err(1, "pledge"); |
||
473 |
#else |
||
474 |
if (pledge("stdio wpath rpath flock dns tty inet fattr", |
||
475 |
NULL) == -1) |
||
476 |
err(1, "pledge"); |
||
477 |
#endif |
||
478 |
} else { |
||
479 |
#ifndef SMALL |
||
480 |
✗✓ | 30 |
if (pledge("stdio wpath rpath flock wpath cpath dns tty inet proc exec fattr", |
481 |
15 |
NULL) == -1) |
|
482 |
err(1, "pledge"); |
||
483 |
#else |
||
484 |
if (pledge("stdio wpath flock rpath wpath cpath dns tty inet fattr", |
||
485 |
NULL) == -1) |
||
486 |
err(1, "pledge"); |
||
487 |
#endif |
||
488 |
} |
||
489 |
|||
490 |
15 |
rval = auto_fetch(argc, argv, outfile); |
|
491 |
✓✗ | 15 |
if (rval >= 0) /* -1 == connected and cd-ed */ |
492 |
exit(rval); |
||
493 |
} else { |
||
494 |
#ifndef SMALL |
||
495 |
char *xargv[5]; |
||
496 |
|||
497 |
if (setjmp(toplevel)) |
||
498 |
exit(0); |
||
499 |
(void)signal(SIGINT, (sig_t)intr); |
||
500 |
(void)signal(SIGPIPE, (sig_t)lostpeer); |
||
501 |
xargv[0] = __progname; |
||
502 |
xargv[1] = argv[0]; |
||
503 |
xargv[2] = argv[1]; |
||
504 |
xargv[3] = argv[2]; |
||
505 |
xargv[4] = NULL; |
||
506 |
do { |
||
507 |
setpeer(argc+1, xargv); |
||
508 |
if (!retry_connect) |
||
509 |
break; |
||
510 |
if (!connected) { |
||
511 |
macnum = 0; |
||
512 |
fputs("Retrying...\n", ttyout); |
||
513 |
sleep(retry_connect); |
||
514 |
} |
||
515 |
} while (!connected); |
||
516 |
retry_connect = 0; /* connected, stop hiding msgs */ |
||
517 |
#endif /* !SMALL */ |
||
518 |
} |
||
519 |
} |
||
520 |
#ifndef SMALL |
||
521 |
controlediting(); |
||
522 |
top = setjmp(toplevel) == 0; |
||
523 |
if (top) { |
||
524 |
(void)signal(SIGINT, (sig_t)intr); |
||
525 |
(void)signal(SIGPIPE, (sig_t)lostpeer); |
||
526 |
} |
||
527 |
for (;;) { |
||
528 |
cmdscanner(top); |
||
529 |
top = 1; |
||
530 |
} |
||
531 |
#else /* !SMALL */ |
||
532 |
usage(); |
||
533 |
#endif /* !SMALL */ |
||
534 |
} |
||
535 |
|||
536 |
void |
||
537 |
intr(void) |
||
538 |
{ |
||
539 |
int save_errno = errno; |
||
540 |
|||
541 |
write(fileno(ttyout), "\n\r", 2); |
||
542 |
alarmtimer(0); |
||
543 |
|||
544 |
errno = save_errno; |
||
545 |
longjmp(toplevel, 1); |
||
546 |
} |
||
547 |
|||
548 |
void |
||
549 |
lostpeer(void) |
||
550 |
{ |
||
551 |
int save_errno = errno; |
||
552 |
|||
553 |
alarmtimer(0); |
||
554 |
if (connected) { |
||
555 |
if (cout != NULL) { |
||
556 |
(void)shutdown(fileno(cout), SHUT_RDWR); |
||
557 |
(void)fclose(cout); |
||
558 |
cout = NULL; |
||
559 |
} |
||
560 |
if (data >= 0) { |
||
561 |
(void)shutdown(data, SHUT_RDWR); |
||
562 |
(void)close(data); |
||
563 |
data = -1; |
||
564 |
} |
||
565 |
connected = 0; |
||
566 |
} |
||
567 |
pswitch(1); |
||
568 |
if (connected) { |
||
569 |
if (cout != NULL) { |
||
570 |
(void)shutdown(fileno(cout), SHUT_RDWR); |
||
571 |
(void)fclose(cout); |
||
572 |
cout = NULL; |
||
573 |
} |
||
574 |
connected = 0; |
||
575 |
} |
||
576 |
proxflag = 0; |
||
577 |
pswitch(0); |
||
578 |
errno = save_errno; |
||
579 |
} |
||
580 |
|||
581 |
#ifndef SMALL |
||
582 |
/* |
||
583 |
* Generate a prompt |
||
584 |
*/ |
||
585 |
char * |
||
586 |
prompt(void) |
||
587 |
{ |
||
588 |
return ("ftp> "); |
||
589 |
} |
||
590 |
|||
591 |
/* |
||
592 |
* Command parser. |
||
593 |
*/ |
||
594 |
void |
||
595 |
cmdscanner(int top) |
||
596 |
{ |
||
597 |
struct cmd *c; |
||
598 |
int num; |
||
599 |
HistEvent hev; |
||
600 |
|||
601 |
if (!top && !editing) |
||
602 |
(void)putc('\n', ttyout); |
||
603 |
for (;;) { |
||
604 |
if (!editing) { |
||
605 |
if (fromatty) { |
||
606 |
fputs(prompt(), ttyout); |
||
607 |
(void)fflush(ttyout); |
||
608 |
} |
||
609 |
if (fgets(line, sizeof(line), stdin) == NULL) |
||
610 |
quit(0, 0); |
||
611 |
num = strlen(line); |
||
612 |
if (num == 0) |
||
613 |
break; |
||
614 |
if (line[--num] == '\n') { |
||
615 |
if (num == 0) |
||
616 |
break; |
||
617 |
line[num] = '\0'; |
||
618 |
} else if (num == sizeof(line) - 2) { |
||
619 |
fputs("sorry, input line too long.\n", ttyout); |
||
620 |
while ((num = getchar()) != '\n' && num != EOF) |
||
621 |
/* void */; |
||
622 |
break; |
||
623 |
} /* else it was a line without a newline */ |
||
624 |
} else { |
||
625 |
const char *buf; |
||
626 |
cursor_pos = NULL; |
||
627 |
|||
628 |
if ((buf = el_gets(el, &num)) == NULL || num == 0) { |
||
629 |
putc('\n', ttyout); |
||
630 |
fflush(ttyout); |
||
631 |
quit(0, 0); |
||
632 |
} |
||
633 |
if (buf[--num] == '\n') { |
||
634 |
if (num == 0) |
||
635 |
break; |
||
636 |
} |
||
637 |
if (num >= sizeof(line)) { |
||
638 |
fputs("sorry, input line too long.\n", ttyout); |
||
639 |
break; |
||
640 |
} |
||
641 |
memcpy(line, buf, (size_t)num); |
||
642 |
line[num] = '\0'; |
||
643 |
history(hist, &hev, H_ENTER, buf); |
||
644 |
} |
||
645 |
|||
646 |
makeargv(); |
||
647 |
if (margc == 0) |
||
648 |
continue; |
||
649 |
c = getcmd(margv[0]); |
||
650 |
if (c == (struct cmd *)-1) { |
||
651 |
fputs("?Ambiguous command.\n", ttyout); |
||
652 |
continue; |
||
653 |
} |
||
654 |
if (c == 0) { |
||
655 |
/* |
||
656 |
* Give editline(3) a shot at unknown commands. |
||
657 |
* XXX - bogus commands with a colon in |
||
658 |
* them will not elicit an error. |
||
659 |
*/ |
||
660 |
if (editing && |
||
661 |
el_parse(el, margc, (const char **)margv) != 0) |
||
662 |
fputs("?Invalid command.\n", ttyout); |
||
663 |
continue; |
||
664 |
} |
||
665 |
if (c->c_conn && !connected) { |
||
666 |
fputs("Not connected.\n", ttyout); |
||
667 |
continue; |
||
668 |
} |
||
669 |
confirmrest = 0; |
||
670 |
(*c->c_handler)(margc, margv); |
||
671 |
if (bell && c->c_bell) |
||
672 |
(void)putc('\007', ttyout); |
||
673 |
if (c->c_handler != help) |
||
674 |
break; |
||
675 |
} |
||
676 |
(void)signal(SIGINT, (sig_t)intr); |
||
677 |
(void)signal(SIGPIPE, (sig_t)lostpeer); |
||
678 |
} |
||
679 |
|||
680 |
struct cmd * |
||
681 |
getcmd(const char *name) |
||
682 |
{ |
||
683 |
const char *p, *q; |
||
684 |
struct cmd *c, *found; |
||
685 |
int nmatches, longest; |
||
686 |
|||
687 |
if (name == NULL) |
||
688 |
return (0); |
||
689 |
|||
690 |
longest = 0; |
||
691 |
nmatches = 0; |
||
692 |
found = 0; |
||
693 |
for (c = cmdtab; (p = c->c_name) != NULL; c++) { |
||
694 |
for (q = name; *q == *p++; q++) |
||
695 |
if (*q == 0) /* exact match? */ |
||
696 |
return (c); |
||
697 |
if (!*q) { /* the name was a prefix */ |
||
698 |
if (q - name > longest) { |
||
699 |
longest = q - name; |
||
700 |
nmatches = 1; |
||
701 |
found = c; |
||
702 |
} else if (q - name == longest) |
||
703 |
nmatches++; |
||
704 |
} |
||
705 |
} |
||
706 |
if (nmatches > 1) |
||
707 |
return ((struct cmd *)-1); |
||
708 |
return (found); |
||
709 |
} |
||
710 |
|||
711 |
/* |
||
712 |
* Slice a string up into argc/argv. |
||
713 |
*/ |
||
714 |
|||
715 |
int slrflag; |
||
716 |
|||
717 |
void |
||
718 |
makeargv(void) |
||
719 |
{ |
||
720 |
char *argp; |
||
721 |
|||
722 |
stringbase = line; /* scan from first of buffer */ |
||
723 |
argbase = argbuf; /* store from first of buffer */ |
||
724 |
slrflag = 0; |
||
725 |
marg_sl->sl_cur = 0; /* reset to start of marg_sl */ |
||
726 |
for (margc = 0; ; margc++) { |
||
727 |
argp = slurpstring(); |
||
728 |
sl_add(marg_sl, argp); |
||
729 |
if (argp == NULL) |
||
730 |
break; |
||
731 |
} |
||
732 |
if (cursor_pos == line) { |
||
733 |
cursor_argc = 0; |
||
734 |
cursor_argo = 0; |
||
735 |
} else if (cursor_pos != NULL) { |
||
736 |
cursor_argc = margc; |
||
737 |
cursor_argo = strlen(margv[margc-1]); |
||
738 |
} |
||
739 |
} |
||
740 |
|||
741 |
#define INC_CHKCURSOR(x) { (x)++ ; \ |
||
742 |
if (x == cursor_pos) { \ |
||
743 |
cursor_argc = margc; \ |
||
744 |
cursor_argo = ap-argbase; \ |
||
745 |
cursor_pos = NULL; \ |
||
746 |
} } |
||
747 |
|||
748 |
/* |
||
749 |
* Parse string into argbuf; |
||
750 |
* implemented with FSM to |
||
751 |
* handle quoting and strings |
||
752 |
*/ |
||
753 |
char * |
||
754 |
slurpstring(void) |
||
755 |
{ |
||
756 |
int got_one = 0; |
||
757 |
char *sb = stringbase; |
||
758 |
char *ap = argbase; |
||
759 |
char *tmp = argbase; /* will return this if token found */ |
||
760 |
|||
761 |
if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ |
||
762 |
switch (slrflag) { /* and $ as token for macro invoke */ |
||
763 |
case 0: |
||
764 |
slrflag++; |
||
765 |
INC_CHKCURSOR(stringbase); |
||
766 |
return ((*sb == '!') ? "!" : "$"); |
||
767 |
/* NOTREACHED */ |
||
768 |
case 1: |
||
769 |
slrflag++; |
||
770 |
altarg = stringbase; |
||
771 |
break; |
||
772 |
default: |
||
773 |
break; |
||
774 |
} |
||
775 |
} |
||
776 |
|||
777 |
S0: |
||
778 |
switch (*sb) { |
||
779 |
|||
780 |
case '\0': |
||
781 |
goto OUT; |
||
782 |
|||
783 |
case ' ': |
||
784 |
case '\t': |
||
785 |
INC_CHKCURSOR(sb); |
||
786 |
goto S0; |
||
787 |
|||
788 |
default: |
||
789 |
switch (slrflag) { |
||
790 |
case 0: |
||
791 |
slrflag++; |
||
792 |
break; |
||
793 |
case 1: |
||
794 |
slrflag++; |
||
795 |
altarg = sb; |
||
796 |
break; |
||
797 |
default: |
||
798 |
break; |
||
799 |
} |
||
800 |
goto S1; |
||
801 |
} |
||
802 |
|||
803 |
S1: |
||
804 |
switch (*sb) { |
||
805 |
|||
806 |
case ' ': |
||
807 |
case '\t': |
||
808 |
case '\0': |
||
809 |
goto OUT; /* end of token */ |
||
810 |
|||
811 |
case '\\': |
||
812 |
INC_CHKCURSOR(sb); |
||
813 |
goto S2; /* slurp next character */ |
||
814 |
|||
815 |
case '"': |
||
816 |
INC_CHKCURSOR(sb); |
||
817 |
goto S3; /* slurp quoted string */ |
||
818 |
|||
819 |
default: |
||
820 |
*ap = *sb; /* add character to token */ |
||
821 |
ap++; |
||
822 |
INC_CHKCURSOR(sb); |
||
823 |
got_one = 1; |
||
824 |
goto S1; |
||
825 |
} |
||
826 |
|||
827 |
S2: |
||
828 |
switch (*sb) { |
||
829 |
|||
830 |
case '\0': |
||
831 |
goto OUT; |
||
832 |
|||
833 |
default: |
||
834 |
*ap = *sb; |
||
835 |
ap++; |
||
836 |
INC_CHKCURSOR(sb); |
||
837 |
got_one = 1; |
||
838 |
goto S1; |
||
839 |
} |
||
840 |
|||
841 |
S3: |
||
842 |
switch (*sb) { |
||
843 |
|||
844 |
case '\0': |
||
845 |
goto OUT; |
||
846 |
|||
847 |
case '"': |
||
848 |
INC_CHKCURSOR(sb); |
||
849 |
goto S1; |
||
850 |
|||
851 |
default: |
||
852 |
*ap = *sb; |
||
853 |
ap++; |
||
854 |
INC_CHKCURSOR(sb); |
||
855 |
got_one = 1; |
||
856 |
goto S3; |
||
857 |
} |
||
858 |
|||
859 |
OUT: |
||
860 |
if (got_one) |
||
861 |
*ap++ = '\0'; |
||
862 |
argbase = ap; /* update storage pointer */ |
||
863 |
stringbase = sb; /* update scan pointer */ |
||
864 |
if (got_one) { |
||
865 |
return (tmp); |
||
866 |
} |
||
867 |
switch (slrflag) { |
||
868 |
case 0: |
||
869 |
slrflag++; |
||
870 |
break; |
||
871 |
case 1: |
||
872 |
slrflag++; |
||
873 |
altarg = (char *) 0; |
||
874 |
break; |
||
875 |
default: |
||
876 |
break; |
||
877 |
} |
||
878 |
return (NULL); |
||
879 |
} |
||
880 |
|||
881 |
/* |
||
882 |
* Help command. |
||
883 |
* Call each command handler with argc == 0 and argv[0] == name. |
||
884 |
*/ |
||
885 |
void |
||
886 |
help(int argc, char *argv[]) |
||
887 |
{ |
||
888 |
struct cmd *c; |
||
889 |
|||
890 |
if (argc == 1) { |
||
891 |
StringList *buf; |
||
892 |
|||
893 |
buf = sl_init(); |
||
894 |
fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n", |
||
895 |
proxy ? "Proxy c" : "C"); |
||
896 |
for (c = cmdtab; c < &cmdtab[NCMDS]; c++) |
||
897 |
if (c->c_name && (!proxy || c->c_proxy)) |
||
898 |
sl_add(buf, c->c_name); |
||
899 |
list_vertical(buf); |
||
900 |
sl_free(buf, 0); |
||
901 |
return; |
||
902 |
} |
||
903 |
|||
904 |
#define HELPINDENT ((int) sizeof("disconnect")) |
||
905 |
|||
906 |
while (--argc > 0) { |
||
907 |
char *arg; |
||
908 |
|||
909 |
arg = *++argv; |
||
910 |
c = getcmd(arg); |
||
911 |
if (c == (struct cmd *)-1) |
||
912 |
fprintf(ttyout, "?Ambiguous help command %s\n", arg); |
||
913 |
else if (c == NULL) |
||
914 |
fprintf(ttyout, "?Invalid help command %s\n", arg); |
||
915 |
else |
||
916 |
fprintf(ttyout, "%-*s\t%s\n", HELPINDENT, |
||
917 |
c->c_name, c->c_help); |
||
918 |
} |
||
919 |
} |
||
920 |
#endif /* !SMALL */ |
||
921 |
|||
922 |
__dead void |
||
923 |
usage(void) |
||
924 |
{ |
||
925 |
fprintf(stderr, "usage: " |
||
926 |
#ifndef SMALL |
||
927 |
"ftp [-46AadEegiMmnptVv] [-D title] [-k seconds] [-P port] " |
||
928 |
"[-r seconds]\n" |
||
929 |
" [-s srcaddr] [host [port]]\n" |
||
930 |
" ftp [-C] [-o output] [-s srcaddr]\n" |
||
931 |
" ftp://[user:password@]host[:port]/file[/] ...\n" |
||
932 |
" ftp [-C] [-c cookie] [-o output] [-S ssl_options] " |
||
933 |
"[-s srcaddr]\n" |
||
934 |
" [-U useragent] [-w seconds] " |
||
935 |
"http[s]://[user:password@]host[:port]/file ...\n" |
||
936 |
" ftp [-C] [-o output] [-s srcaddr] file:file ...\n" |
||
937 |
" ftp [-C] [-o output] [-s srcaddr] host:/file[/] ...\n" |
||
938 |
#else /* !SMALL */ |
||
939 |
"ftp [-o output] " |
||
940 |
"ftp://[user:password@]host[:port]/file[/] ...\n" |
||
941 |
#ifndef NOSSL |
||
942 |
" ftp [-o output] [-S ssl_options] [-w seconds] " |
||
943 |
"http[s]://[user:password@]host[:port]/file ...\n" |
||
944 |
#else |
||
945 |
" ftp [-o output] [-w seconds] http://host[:port]/file ...\n" |
||
946 |
#endif /* NOSSL */ |
||
947 |
" ftp [-o output] file:file ...\n" |
||
948 |
" ftp [-o output] host:/file[/] ...\n" |
||
949 |
#endif /* !SMALL */ |
||
950 |
); |
||
951 |
exit(1); |
||
952 |
} |
Generated by: GCOVR (Version 3.3) |