Annotation of src/usr.bin/ftp/util.c, Revision 1.74
1.74 ! deraadt 1: /* $OpenBSD: util.c,v 1.73 2015/10/18 03:04:11 mmcc Exp $ */
1.13 millert 2: /* $NetBSD: util.c,v 1.12 1997/08/18 10:20:27 lukem Exp $ */
1.1 millert 3:
1.34 millert 4: /*-
5: * Copyright (c) 1997-1999 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by Luke Mewburn.
10: *
11: * This code is derived from software contributed to The NetBSD Foundation
12: * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
13: * NASA Ames Research Center.
14: *
15: * Redistribution and use in source and binary forms, with or without
16: * modification, are permitted provided that the following conditions
17: * are met:
18: * 1. Redistributions of source code must retain the above copyright
19: * notice, this list of conditions and the following disclaimer.
20: * 2. Redistributions in binary form must reproduce the above copyright
21: * notice, this list of conditions and the following disclaimer in the
22: * documentation and/or other materials provided with the distribution.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34: * POSSIBILITY OF SUCH DAMAGE.
35: */
36:
1.1 millert 37: /*
38: * Copyright (c) 1985, 1989, 1993, 1994
39: * The Regents of the University of California. All rights reserved.
40: *
41: * Redistribution and use in source and binary forms, with or without
42: * modification, are permitted provided that the following conditions
43: * are met:
44: * 1. Redistributions of source code must retain the above copyright
45: * notice, this list of conditions and the following disclaimer.
46: * 2. Redistributions in binary form must reproduce the above copyright
47: * notice, this list of conditions and the following disclaimer in the
48: * documentation and/or other materials provided with the distribution.
1.35 millert 49: * 3. Neither the name of the University nor the names of its contributors
1.1 millert 50: * may be used to endorse or promote products derived from this software
51: * without specific prior written permission.
52: *
53: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63: * SUCH DAMAGE.
64: */
65:
66: /*
67: * FTP User Program -- Misc support routines
68: */
69: #include <sys/ioctl.h>
70: #include <sys/time.h>
71: #include <arpa/ftp.h>
72:
73: #include <ctype.h>
74: #include <err.h>
1.3 millert 75: #include <errno.h>
1.1 millert 76: #include <fcntl.h>
1.41 otto 77: #include <libgen.h>
1.1 millert 78: #include <glob.h>
1.6 millert 79: #include <pwd.h>
1.3 millert 80: #include <signal.h>
1.1 millert 81: #include <stdio.h>
1.2 millert 82: #include <stdlib.h>
1.1 millert 83: #include <string.h>
84: #include <time.h>
85: #include <unistd.h>
86:
87: #include "ftp_var.h"
88: #include "pathnames.h"
89:
1.68 deraadt 90: #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
91: #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
92:
1.27 millert 93: static void updateprogressmeter(int);
1.20 millert 94:
1.1 millert 95: /*
96: * Connect to peer server and
97: * auto-login, if possible.
98: */
99: void
1.38 deraadt 100: setpeer(int argc, char *argv[])
1.1 millert 101: {
1.40 deraadt 102: char *host, *port;
1.1 millert 103:
104: if (connected) {
1.7 deraadt 105: fprintf(ttyout, "Already connected to %s, use close first.\n",
1.2 millert 106: hostname);
1.1 millert 107: code = -1;
108: return;
109: }
1.62 martynas 110: #ifndef SMALL
1.1 millert 111: if (argc < 2)
1.2 millert 112: (void)another(&argc, &argv, "to");
1.1 millert 113: if (argc < 2 || argc > 3) {
1.55 sobrado 114: fprintf(ttyout, "usage: %s host [port]\n", argv[0]);
1.1 millert 115: code = -1;
116: return;
117: }
1.62 martynas 118: #endif /* !SMALL */
1.13 millert 119: if (gatemode)
120: port = gateport;
121: else
122: port = ftpport;
1.22 itojun 123: if (argc > 2)
124: port = argv[2];
1.13 millert 125:
126: if (gatemode) {
127: if (gateserver == NULL || *gateserver == '\0')
128: errx(1, "gateserver not defined (shouldn't happen)");
129: host = hookup(gateserver, port);
130: } else
131: host = hookup(argv[1], port);
132:
1.1 millert 133: if (host) {
134: int overbose;
135:
1.13 millert 136: if (gatemode) {
137: if (command("PASSERVE %s", argv[1]) != COMPLETE)
138: return;
139: if (verbose)
140: fprintf(ttyout,
141: "Connected via pass-through server %s\n",
142: gateserver);
143: }
144:
1.1 millert 145: connected = 1;
146: /*
147: * Set up defaults for FTP.
148: */
1.33 deraadt 149: (void)strlcpy(formname, "non-print", sizeof formname);
150: form = FORM_N;
151: (void)strlcpy(modename, "stream", sizeof modename);
152: mode = MODE_S;
153: (void)strlcpy(structname, "file", sizeof structname);
154: stru = STRU_F;
155: (void)strlcpy(bytename, "8", sizeof bytename);
156: bytesize = 8;
157:
1.25 millert 158: /*
159: * Set type to 0 (not specified by user),
160: * meaning binary by default, but don't bother
161: * telling server. We can use binary
162: * for text files unless changed by the user.
163: */
1.33 deraadt 164: (void)strlcpy(typename, "binary", sizeof typename);
1.25 millert 165: curtype = TYPE_A;
166: type = 0;
1.1 millert 167: if (autologin)
1.31 fgsch 168: (void)ftp_login(argv[1], NULL, NULL);
1.1 millert 169:
170: overbose = verbose;
1.54 martynas 171: #ifndef SMALL
172: if (!debug)
173: #endif /* !SMALL */
1.1 millert 174: verbose = -1;
175: if (command("SYST") == COMPLETE && overbose) {
176: char *cp, c;
177: c = 0;
1.4 millert 178: cp = strchr(reply_string + 4, ' ');
1.1 millert 179: if (cp == NULL)
1.4 millert 180: cp = strchr(reply_string + 4, '\r');
1.1 millert 181: if (cp) {
182: if (cp[-1] == '.')
183: cp--;
184: c = *cp;
185: *cp = '\0';
186: }
187:
1.7 deraadt 188: fprintf(ttyout, "Remote system type is %s.\n", reply_string + 4);
1.1 millert 189: if (cp)
190: *cp = c;
191: }
192: if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
193: if (proxy)
194: unix_proxy = 1;
195: else
196: unix_server = 1;
197: if (overbose)
1.7 deraadt 198: fprintf(ttyout, "Using %s mode to transfer files.\n",
199: typename);
1.1 millert 200: } else {
201: if (proxy)
202: unix_proxy = 0;
203: else
204: unix_server = 0;
205: }
206: verbose = overbose;
207: }
1.6 millert 208: }
209:
210: /*
211: * login to remote host, using given username & password if supplied
212: */
213: int
1.38 deraadt 214: ftp_login(const char *host, char *user, char *pass)
1.6 millert 215: {
1.68 deraadt 216: char tmp[80], *acctname = NULL, host_name[HOST_NAME_MAX+1];
217: char anonpass[LOGIN_NAME_MAX + 1 + HOST_NAME_MAX+1]; /* "user@hostname" */
1.40 deraadt 218: int n, aflag = 0, retry = 0;
1.14 millert 219: struct passwd *pw;
1.6 millert 220:
1.46 pyr 221: #ifndef SMALL
1.6 millert 222: if (user == NULL) {
1.43 ray 223: if (ruserpass(host, &user, &pass, &acctname) < 0) {
1.6 millert 224: code = -1;
225: return (0);
226: }
227: }
1.52 martynas 228: #endif /* !SMALL */
1.6 millert 229:
230: /*
231: * Set up arguments for an anonymous FTP session, if necessary.
232: */
233: if ((user == NULL || pass == NULL) && anonftp) {
234: memset(anonpass, 0, sizeof(anonpass));
1.43 ray 235: memset(host_name, 0, sizeof(host_name));
1.6 millert 236:
237: /*
238: * Set up anonymous login password.
239: */
1.14 millert 240: if ((user = getlogin()) == NULL) {
241: if ((pw = getpwuid(getuid())) == NULL)
242: user = "anonymous";
243: else
244: user = pw->pw_name;
245: }
1.43 ray 246: gethostname(host_name, sizeof(host_name));
1.6 millert 247: #ifndef DONT_CHEAT_ANONPASS
248: /*
249: * Every anonymous FTP server I've encountered
250: * will accept the string "username@", and will
251: * append the hostname itself. We do this by default
252: * since many servers are picky about not having
253: * a FQDN in the anonymous password. - thorpej@netbsd.org
254: */
255: snprintf(anonpass, sizeof(anonpass) - 1, "%s@",
256: user);
257: #else
258: snprintf(anonpass, sizeof(anonpass) - 1, "%s@%s",
259: user, hp->h_name);
260: #endif
261: pass = anonpass;
1.11 millert 262: user = "anonymous"; /* as per RFC 1635 */
1.8 jkatz 263: }
264:
265: tryagain:
1.9 millert 266: if (retry)
1.11 millert 267: user = "ftp"; /* some servers only allow "ftp" */
1.8 jkatz 268:
1.6 millert 269: while (user == NULL) {
270: char *myname = getlogin();
271:
1.14 millert 272: if (myname == NULL && (pw = getpwuid(getuid())) != NULL)
273: myname = pw->pw_name;
1.6 millert 274: if (myname)
1.7 deraadt 275: fprintf(ttyout, "Name (%s:%s): ", host, myname);
1.6 millert 276: else
1.7 deraadt 277: fprintf(ttyout, "Name (%s): ", host);
1.44 ray 278: user = myname;
1.45 ray 279: if (fgets(tmp, sizeof(tmp), stdin) != NULL) {
1.47 gilles 280: tmp[strcspn(tmp, "\n")] = '\0';
1.44 ray 281: if (tmp[0] != '\0')
282: user = tmp;
283: }
1.49 martynas 284: else
285: exit(0);
1.6 millert 286: }
287: n = command("USER %s", user);
288: if (n == CONTINUE) {
289: if (pass == NULL)
290: pass = getpass("Password:");
291: n = command("PASS %s", pass);
292: }
293: if (n == CONTINUE) {
294: aflag++;
1.43 ray 295: if (acctname == NULL)
296: acctname = getpass("Account:");
297: n = command("ACCT %s", acctname);
1.6 millert 298: }
299: if ((n != COMPLETE) ||
1.43 ray 300: (!aflag && acctname != NULL && command("ACCT %s", acctname) != COMPLETE)) {
1.63 deraadt 301: warnx("Login %s failed.", user);
1.9 millert 302: if (retry || !anonftp)
1.8 jkatz 303: return (0);
1.9 millert 304: else
305: retry = 1;
1.8 jkatz 306: goto tryagain;
1.6 millert 307: }
308: if (proxy)
309: return (1);
310: connected = -1;
1.61 martynas 311: #ifndef SMALL
1.6 millert 312: for (n = 0; n < macnum; ++n) {
313: if (!strcmp("init", macros[n].mac_name)) {
1.33 deraadt 314: (void)strlcpy(line, "$init", sizeof line);
1.6 millert 315: makeargv();
316: domacro(margc, margv);
317: break;
318: }
319: }
1.61 martynas 320: #endif /* SMALL */
1.6 millert 321: return (1);
1.1 millert 322: }
323:
324: /*
1.4 millert 325: * `another' gets another argument, and stores the new argc and argv.
1.1 millert 326: * It reverts to the top level (via main.c's intr()) on EOF/error.
327: *
328: * Returns false if no new arguments have been added.
329: */
1.62 martynas 330: #ifndef SMALL
1.1 millert 331: int
1.38 deraadt 332: another(int *pargc, char ***pargv, const char *prompt)
1.1 millert 333: {
334: int len = strlen(line), ret;
335:
336: if (len >= sizeof(line) - 3) {
1.7 deraadt 337: fputs("sorry, arguments too long.\n", ttyout);
1.1 millert 338: intr();
339: }
1.7 deraadt 340: fprintf(ttyout, "(%s) ", prompt);
1.1 millert 341: line[len++] = ' ';
1.49 martynas 342: if (fgets(&line[len], (int)(sizeof(line) - len), stdin) == NULL) {
343: clearerr(stdin);
1.1 millert 344: intr();
1.49 martynas 345: }
1.1 millert 346: len += strlen(&line[len]);
347: if (len > 0 && line[len - 1] == '\n')
348: line[len - 1] = '\0';
349: makeargv();
350: ret = margc > *pargc;
351: *pargc = margc;
352: *pargv = margv;
353: return (ret);
354: }
1.62 martynas 355: #endif /* !SMALL */
1.1 millert 356:
1.4 millert 357: /*
358: * glob files given in argv[] from the remote server.
359: * if errbuf isn't NULL, store error messages there instead
360: * of writing to the screen.
1.54 martynas 361: * if type isn't NULL, use LIST instead of NLST, and store filetype.
362: * 'd' means directory, 's' means symbolic link, '-' means plain
363: * file.
1.4 millert 364: */
1.1 millert 365: char *
1.54 martynas 366: remglob2(char *argv[], int doswitch, char **errbuf, FILE **ftemp, char *type)
1.1 millert 367: {
1.68 deraadt 368: char temp[PATH_MAX], *bufp, *cp, *lmode;
369: static char buf[PATH_MAX], **args;
1.40 deraadt 370: int oldverbose, oldhash, fd;
371:
372: if (!mflag) {
373: if (!doglob)
374: args = NULL;
375: else {
1.54 martynas 376: if (*ftemp) {
377: (void)fclose(*ftemp);
378: *ftemp = NULL;
1.40 deraadt 379: }
380: }
381: return (NULL);
382: }
383: if (!doglob) {
384: if (args == NULL)
385: args = argv;
386: if ((cp = *++args) == NULL)
387: args = NULL;
388: return (cp);
389: }
1.54 martynas 390: if (*ftemp == NULL) {
1.3 millert 391: int len;
392:
1.74 ! deraadt 393: cp = _PATH_TMP;
1.3 millert 394: len = strlen(cp);
395: if (len + sizeof(TMPFILE) + (cp[len-1] != '/') > sizeof(temp)) {
396: warnx("unable to create temporary file: %s",
397: strerror(ENAMETOOLONG));
398: return (NULL);
399: }
400:
1.33 deraadt 401: (void)strlcpy(temp, cp, sizeof temp);
1.3 millert 402: if (temp[len-1] != '/')
403: temp[len++] = '/';
1.33 deraadt 404: (void)strlcpy(&temp[len], TMPFILE, sizeof temp - len);
1.40 deraadt 405: if ((fd = mkstemp(temp)) < 0) {
1.58 sthen 406: warn("unable to create temporary file: %s", temp);
1.40 deraadt 407: return (NULL);
408: }
409: close(fd);
1.4 millert 410: oldverbose = verbose;
411: verbose = (errbuf != NULL) ? -1 : 0;
412: oldhash = hash;
413: hash = 0;
1.40 deraadt 414: if (doswitch)
415: pswitch(!proxy);
1.43 ray 416: for (lmode = "w"; *++argv != NULL; lmode = "a")
1.54 martynas 417: recvrequest(type ? "LIST" : "NLST", temp, *argv, lmode,
418: 0, 0);
1.4 millert 419: if ((code / 100) != COMPLETE) {
420: if (errbuf != NULL)
421: *errbuf = reply_string;
422: }
423: if (doswitch)
424: pswitch(!proxy);
1.40 deraadt 425: verbose = oldverbose;
1.4 millert 426: hash = oldhash;
1.54 martynas 427: *ftemp = fopen(temp, "r");
1.40 deraadt 428: (void)unlink(temp);
1.54 martynas 429: if (*ftemp == NULL) {
1.4 millert 430: if (errbuf == NULL)
1.7 deraadt 431: fputs("can't find list of remote files, oops.\n",
432: ttyout);
1.4 millert 433: else
434: *errbuf =
435: "can't find list of remote files, oops.";
1.40 deraadt 436: return (NULL);
437: }
438: }
1.54 martynas 439: again:
440: if (fgets(buf, sizeof(buf), *ftemp) == NULL) {
441: (void)fclose(*ftemp);
442: *ftemp = NULL;
1.40 deraadt 443: return (NULL);
444: }
1.47 gilles 445:
446: buf[strcspn(buf, "\n")] = '\0';
1.54 martynas 447: bufp = buf;
448:
449: #ifndef SMALL
450: if (type) {
451: parse_list(&bufp, type);
1.56 martynas 452: if (!bufp ||
453: (bufp[0] == '.' && /* LIST defaults to -a on some ftp */
454: (bufp[1] == '\0' || /* servers. Ignore '.' and '..'. */
455: (bufp[1] == '.' && bufp[2] == '\0'))))
1.54 martynas 456: goto again;
457: }
458: #endif /* !SMALL */
459:
460: return (bufp);
461: }
462:
463: /*
464: * wrapper for remglob2
465: */
466: char *
467: remglob(char *argv[], int doswitch, char **errbuf)
468: {
469: static FILE *ftemp = NULL;
1.47 gilles 470:
1.54 martynas 471: return remglob2(argv, doswitch, errbuf, &ftemp, NULL);
1.1 millert 472: }
473:
1.62 martynas 474: #ifndef SMALL
1.1 millert 475: int
1.54 martynas 476: confirm(const char *cmd, const char *file)
1.1 millert 477: {
1.43 ray 478: char str[BUFSIZ];
1.1 millert 479:
1.54 martynas 480: if (file && (confirmrest || !interactive))
1.1 millert 481: return (1);
1.16 deraadt 482: top:
1.54 martynas 483: if (file)
484: fprintf(ttyout, "%s %s? ", cmd, file);
485: else
486: fprintf(ttyout, "Continue with %s? ", cmd);
1.7 deraadt 487: (void)fflush(ttyout);
1.51 martynas 488: if (fgets(str, sizeof(str), stdin) == NULL)
489: goto quit;
1.73 mmcc 490: switch (tolower((unsigned char)*str)) {
1.51 martynas 491: case '?':
492: fprintf(ttyout,
493: "? help\n"
494: "a answer yes to all\n"
495: "n answer no\n"
496: "p turn off prompt mode\n"
497: "q answer no to all\n"
498: "y answer yes\n");
499: goto top;
500: case 'a':
501: confirmrest = 1;
502: fprintf(ttyout, "Prompting off for duration of %s.\n",
503: cmd);
504: break;
1.1 millert 505: case 'n':
506: return (0);
507: case 'p':
508: interactive = 0;
1.7 deraadt 509: fputs("Interactive mode: off.\n", ttyout);
1.1 millert 510: break;
1.51 martynas 511: case 'q':
512: quit:
513: mflag = 0;
514: clearerr(stdin);
515: return (0);
1.12 jkatz 516: case 'y':
517: return(1);
518: break;
519: default:
1.51 martynas 520: fprintf(ttyout, "?, a, n, p, q, y "
521: "are the only acceptable commands!\n");
1.16 deraadt 522: goto top;
1.1 millert 523: break;
524: }
525: return (1);
526: }
1.62 martynas 527: #endif /* !SMALL */
1.1 millert 528:
529: /*
530: * Glob a local file name specification with
531: * the expectation of a single return value.
532: * Can't control multiple values being expanded
533: * from the expression, we return only the first.
534: */
535: int
1.38 deraadt 536: globulize(char **cpp)
1.1 millert 537: {
538: glob_t gl;
539: int flags;
540:
541: if (!doglob)
542: return (1);
543:
544: flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
545: memset(&gl, 0, sizeof(gl));
546: if (glob(*cpp, flags, NULL, &gl) ||
547: gl.gl_pathc == 0) {
548: warnx("%s: not found", *cpp);
549: globfree(&gl);
550: return (0);
551: }
1.13 millert 552: /* XXX: caller should check if *cpp changed, and
553: * free(*cpp) if that is the case
554: */
555: *cpp = strdup(gl.gl_pathv[0]);
1.30 deraadt 556: if (*cpp == NULL)
557: err(1, NULL);
1.1 millert 558: globfree(&gl);
559: return (1);
560: }
561:
562: /*
563: * determine size of remote file
564: */
565: off_t
1.38 deraadt 566: remotesize(const char *file, int noisy)
1.1 millert 567: {
568: int overbose;
569: off_t size;
570:
571: overbose = verbose;
572: size = -1;
1.54 martynas 573: #ifndef SMALL
574: if (!debug)
575: #endif /* !SMALL */
1.1 millert 576: verbose = -1;
1.11 millert 577: if (command("SIZE %s", file) == COMPLETE) {
578: char *cp, *ep;
579:
580: cp = strchr(reply_string, ' ');
581: if (cp != NULL) {
582: cp++;
583: size = strtoq(cp, &ep, 10);
1.73 mmcc 584: if (*ep != '\0' && !isspace((unsigned char)*ep))
1.11 millert 585: size = -1;
586: }
1.54 martynas 587: } else if (noisy
588: #ifndef SMALL
589: && !debug
590: #endif /* !SMALL */
591: ) {
1.7 deraadt 592: fputs(reply_string, ttyout);
1.11 millert 593: fputc('\n', ttyout);
1.7 deraadt 594: }
1.1 millert 595: verbose = overbose;
596: return (size);
597: }
598:
599: /*
600: * determine last modification time (in GMT) of remote file
601: */
602: time_t
1.38 deraadt 603: remotemodtime(const char *file, int noisy)
1.1 millert 604: {
605: int overbose;
606: time_t rtime;
1.15 millert 607: int ocode;
1.1 millert 608:
609: overbose = verbose;
1.15 millert 610: ocode = code;
1.1 millert 611: rtime = -1;
1.54 martynas 612: #ifndef SMALL
613: if (!debug)
614: #endif /* !SMALL */
1.1 millert 615: verbose = -1;
616: if (command("MDTM %s", file) == COMPLETE) {
617: struct tm timebuf;
618: int yy, mo, day, hour, min, sec;
1.23 espie 619: /*
620: * time-val = 14DIGIT [ "." 1*DIGIT ]
621: * YYYYMMDDHHMMSS[.sss]
622: * mdtm-response = "213" SP time-val CRLF / error-response
623: */
624: /* TODO: parse .sss as well, use timespecs. */
625: char *timestr = reply_string;
626:
627: /* Repair `19%02d' bug on server side */
1.73 mmcc 628: while (!isspace((unsigned char)*timestr))
1.23 espie 629: timestr++;
1.73 mmcc 630: while (isspace((unsigned char)*timestr))
1.23 espie 631: timestr++;
632: if (strncmp(timestr, "191", 3) == 0) {
633: fprintf(ttyout,
634: "Y2K warning! Fixed incorrect time-val received from server.\n");
635: timestr[0] = ' ';
636: timestr[1] = '2';
637: timestr[2] = '0';
638: }
1.1 millert 639: sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
640: &day, &hour, &min, &sec);
641: memset(&timebuf, 0, sizeof(timebuf));
642: timebuf.tm_sec = sec;
643: timebuf.tm_min = min;
644: timebuf.tm_hour = hour;
645: timebuf.tm_mday = day;
646: timebuf.tm_mon = mo - 1;
1.72 millert 647: timebuf.tm_year = yy - 1900;
1.1 millert 648: timebuf.tm_isdst = -1;
649: rtime = mktime(&timebuf);
1.54 martynas 650: if (rtime == -1 && (noisy
651: #ifndef SMALL
652: || debug
653: #endif /* !SMALL */
654: ))
1.7 deraadt 655: fprintf(ttyout, "Can't convert %s to a time.\n", reply_string);
1.1 millert 656: else
657: rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
1.54 martynas 658: } else if (noisy
659: #ifndef SMALL
660: && !debug
661: #endif /* !SMALL */
662: ) {
1.7 deraadt 663: fputs(reply_string, ttyout);
1.11 millert 664: fputc('\n', ttyout);
1.7 deraadt 665: }
1.1 millert 666: verbose = overbose;
1.15 millert 667: if (rtime == -1)
668: code = ocode;
1.1 millert 669: return (rtime);
670: }
1.41 otto 671:
672: /*
673: * Ensure file is in or under dir.
674: * Returns 1 if so, 0 if not (or an error occurred).
675: */
676: int
677: fileindir(const char *file, const char *dir)
678: {
1.68 deraadt 679: char parentdirbuf[PATH_MAX], *parentdir;
680: char realdir[PATH_MAX];
1.41 otto 681: size_t dirlen;
682:
683: /* determine parent directory of file */
684: (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf));
685: parentdir = dirname(parentdirbuf);
686: if (strcmp(parentdir, ".") == 0)
687: return 1; /* current directory is ok */
688:
689: /* find the directory */
690: if (realpath(parentdir, realdir) == NULL) {
691: warn("Unable to determine real path of `%s'", parentdir);
692: return 0;
693: }
694: if (realdir[0] != '/') /* relative result is ok */
695: return 1;
696:
697: dirlen = strlen(dir);
698: if (strncmp(realdir, dir, dirlen) == 0 &&
699: (realdir[dirlen] == '/' || realdir[dirlen] == '\0'))
700: return 1;
701: return 0;
702: }
703:
1.1 millert 704:
1.10 millert 705: /*
706: * Returns true if this is the controlling/foreground process, else false.
707: */
708: int
1.38 deraadt 709: foregroundproc(void)
1.10 millert 710: {
711: static pid_t pgrp = -1;
712: int ctty_pgrp;
713:
714: if (pgrp == -1)
715: pgrp = getpgrp();
716:
717: return((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 &&
718: ctty_pgrp == pgrp));
719: }
720:
1.39 deraadt 721: /* ARGSUSED */
1.20 millert 722: static void
1.39 deraadt 723: updateprogressmeter(int signo)
1.1 millert 724: {
1.19 deraadt 725: int save_errno = errno;
1.1 millert 726:
1.20 millert 727: /* update progressmeter if foreground process or in -m mode */
728: if (foregroundproc() || progress == -1)
1.59 martynas 729: progressmeter(0, NULL);
1.19 deraadt 730: errno = save_errno;
1.1 millert 731: }
732:
733: /*
734: * Display a transfer progress bar if progress is non-zero.
735: * SIGALRM is hijacked for use by this function.
736: * - Before the transfer, set filesize to size of file (or -1 if unknown),
737: * and call with flag = -1. This starts the once per second timer,
738: * and a call to updateprogressmeter() upon SIGALRM.
739: * - During the transfer, updateprogressmeter will call progressmeter
740: * with flag = 0
741: * - After the transfer, call with flag = 1
742: */
743: static struct timeval start;
744:
1.65 deraadt 745: char *action;
746:
1.1 millert 747: void
1.59 martynas 748: progressmeter(int flag, const char *filename)
1.1 millert 749: {
750: /*
751: * List of order of magnitude prefixes.
752: * The last is `P', as 2^64 = 16384 Petabytes
753: */
754: static const char prefixes[] = " KMGTP";
755:
756: static struct timeval lastupdate;
757: static off_t lastsize;
1.59 martynas 758: static char *title = NULL;
1.1 millert 759: struct timeval now, td, wait;
760: off_t cursize, abbrevsize;
761: double elapsed;
1.57 deraadt 762: int ratio, barlength, i, remaining, overhead = 30;
1.29 deraadt 763: char buf[512];
1.1 millert 764:
765: if (flag == -1) {
1.2 millert 766: (void)gettimeofday(&start, (struct timezone *)0);
1.1 millert 767: lastupdate = start;
768: lastsize = restart_point;
769: }
1.2 millert 770: (void)gettimeofday(&now, (struct timezone *)0);
1.24 deraadt 771: if (!progress || filesize < 0)
1.1 millert 772: return;
773: cursize = bytes + restart_point;
774:
1.24 deraadt 775: if (filesize)
776: ratio = cursize * 100 / filesize;
777: else
778: ratio = 100;
1.68 deraadt 779: ratio = MAXIMUM(ratio, 0);
780: ratio = MINIMUM(ratio, 100);
1.59 martynas 781: if (!verbose && flag == -1) {
782: filename = basename(filename);
783: if (filename != NULL)
784: title = strdup(filename);
785: }
1.65 deraadt 786:
787: buf[0] = 0;
788: if (!verbose && action != NULL) {
789: int l = strlen(action);
790: char *dotdot = "";
791:
792: if (l < 7)
793: l = 7;
794: else if (l > 12) {
795: l = 12;
796: dotdot = "...";
797: overhead += 3;
798: }
799: snprintf(buf, sizeof(buf), "\r%-*.*s%s ", l, l, action,
800: dotdot);
801: overhead += l + 1;
802: } else
1.66 dcoppa 803: snprintf(buf, sizeof(buf), "\r");
1.65 deraadt 804:
1.59 martynas 805: if (!verbose && title != NULL) {
1.57 deraadt 806: int l = strlen(title);
807: char *dotdot = "";
808:
809: if (l < 12)
810: l = 12;
811: else if (l > 25) {
812: l = 22;
813: dotdot = "...";
814: overhead += 3;
815: }
1.65 deraadt 816: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
817: "%-*.*s%s %3d%% ", l, l, title,
1.57 deraadt 818: dotdot, ratio);
819: overhead += l + 1;
1.59 martynas 820: } else
1.57 deraadt 821: snprintf(buf, sizeof(buf), "\r%3d%% ", ratio);
1.1 millert 822:
1.57 deraadt 823: barlength = ttywidth - overhead;
1.1 millert 824: if (barlength > 0) {
825: i = barlength * ratio / 100;
826: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
827: "|%.*s%*s|", i,
1.29 deraadt 828: "*******************************************************"
829: "*******************************************************"
830: "*******************************************************"
831: "*******************************************************"
832: "*******************************************************"
833: "*******************************************************"
834: "*******************************************************",
1.1 millert 835: barlength - i, "");
836: }
837:
838: i = 0;
839: abbrevsize = cursize;
1.64 deraadt 840: while (abbrevsize >= 100000 && i < sizeof(prefixes)-1) {
1.1 millert 841: i++;
842: abbrevsize >>= 10;
843: }
844: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
1.26 deraadt 845: " %5lld %c%c ", (long long)abbrevsize, prefixes[i],
1.1 millert 846: prefixes[i] == ' ' ? ' ' : 'B');
847:
848: timersub(&now, &lastupdate, &wait);
849: if (cursize > lastsize) {
850: lastupdate = now;
851: lastsize = cursize;
852: if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */
853: start.tv_sec += wait.tv_sec;
854: start.tv_usec += wait.tv_usec;
855: }
856: wait.tv_sec = 0;
857: }
858:
859: timersub(&now, &start, &td);
860: elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
861:
1.24 deraadt 862: if (flag == 1) {
863: i = (int)elapsed / 3600;
864: if (i)
865: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
866: "%2d:", i);
867: else
868: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
869: " ");
870: i = (int)elapsed % 3600;
871: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
872: "%02d:%02d ", i / 60, i % 60);
873: } else if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
1.1 millert 874: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
875: " --:-- ETA");
876: } else if (wait.tv_sec >= STALLTIME) {
877: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
878: " - stalled -");
879: } else {
880: remaining = (int)((filesize - restart_point) /
881: (bytes / elapsed) - elapsed);
882: i = remaining / 3600;
883: if (i)
884: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
885: "%2d:", i);
886: else
887: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
888: " ");
889: i = remaining % 3600;
890: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
891: "%02d:%02d ETA", i / 60, i % 60);
892: }
1.7 deraadt 893: (void)write(fileno(ttyout), buf, strlen(buf));
1.1 millert 894:
895: if (flag == -1) {
1.2 millert 896: (void)signal(SIGALRM, updateprogressmeter);
1.1 millert 897: alarmtimer(1); /* set alarm timer for 1 Hz */
898: } else if (flag == 1) {
899: alarmtimer(0);
1.7 deraadt 900: (void)putc('\n', ttyout);
1.59 martynas 901: if (title != NULL) {
902: free(title);
903: title = NULL;
904: }
1.1 millert 905: }
1.7 deraadt 906: fflush(ttyout);
1.1 millert 907: }
908:
909: /*
910: * Display transfer statistics.
911: * Requires start to be initialised by progressmeter(-1),
912: * direction to be defined by xfer routines, and filesize and bytes
913: * to be updated by xfer routines
914: * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR
1.7 deraadt 915: * instead of TTYOUT.
1.1 millert 916: */
917: void
1.38 deraadt 918: ptransfer(int siginfo)
1.1 millert 919: {
920: struct timeval now, td;
921: double elapsed;
922: off_t bs;
923: int meg, remaining, hh;
924: char buf[100];
925:
926: if (!verbose && !siginfo)
927: return;
928:
1.2 millert 929: (void)gettimeofday(&now, (struct timezone *)0);
1.1 millert 930: timersub(&now, &start, &td);
931: elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
932: bs = bytes / (elapsed == 0.0 ? 1 : elapsed);
933: meg = 0;
934: if (bs > (1024 * 1024))
935: meg = 1;
1.67 deraadt 936:
937: /* XXX floating point printf in signal handler */
1.1 millert 938: (void)snprintf(buf, sizeof(buf),
1.26 deraadt 939: "%lld byte%s %s in %.2f seconds (%.2f %sB/s)\n",
940: (long long)bytes, bytes == 1 ? "" : "s", direction, elapsed,
1.1 millert 941: bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K");
1.67 deraadt 942:
943: if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 &&
944: bytes + restart_point <= filesize) {
1.1 millert 945: remaining = (int)((filesize - restart_point) /
1.67 deraadt 946: (bytes / elapsed) - elapsed);
1.1 millert 947: hh = remaining / 3600;
948: remaining %= 3600;
1.67 deraadt 949:
950: /* "buf+len(buf) -1" to overwrite \n */
1.1 millert 951: snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf),
952: " ETA: %02d:%02d:%02d\n", hh, remaining / 60,
953: remaining % 60);
954: }
1.7 deraadt 955: (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, strlen(buf));
1.1 millert 956: }
957:
958: /*
959: * List words in stringlist, vertically arranged
960: */
1.62 martynas 961: #ifndef SMALL
1.1 millert 962: void
1.38 deraadt 963: list_vertical(StringList *sl)
1.1 millert 964: {
965: int i, j, w;
1.39 deraadt 966: int columns, width, lines;
1.1 millert 967: char *p;
968:
1.39 deraadt 969: width = 0;
1.1 millert 970:
971: for (i = 0 ; i < sl->sl_cur ; i++) {
972: w = strlen(sl->sl_str[i]);
973: if (w > width)
974: width = w;
975: }
976: width = (width + 8) &~ 7;
977:
978: columns = ttywidth / width;
979: if (columns == 0)
980: columns = 1;
981: lines = (sl->sl_cur + columns - 1) / columns;
982: for (i = 0; i < lines; i++) {
983: for (j = 0; j < columns; j++) {
984: p = sl->sl_str[j * lines + i];
985: if (p)
1.7 deraadt 986: fputs(p, ttyout);
1.1 millert 987: if (j * lines + i + lines >= sl->sl_cur) {
1.7 deraadt 988: putc('\n', ttyout);
1.1 millert 989: break;
990: }
991: w = strlen(p);
992: while (w < width) {
993: w = (w + 8) &~ 7;
1.7 deraadt 994: (void)putc('\t', ttyout);
1.1 millert 995: }
996: }
997: }
998: }
1.62 martynas 999: #endif /* !SMALL */
1.1 millert 1000:
1001: /*
1002: * Update the global ttywidth value, using TIOCGWINSZ.
1003: */
1.39 deraadt 1004: /* ARGSUSED */
1.1 millert 1005: void
1.39 deraadt 1006: setttywidth(int signo)
1.1 millert 1007: {
1.19 deraadt 1008: int save_errno = errno;
1.1 millert 1009: struct winsize winsize;
1010:
1.7 deraadt 1011: if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1)
1.21 deraadt 1012: ttywidth = winsize.ws_col ? winsize.ws_col : 80;
1.1 millert 1013: else
1014: ttywidth = 80;
1.19 deraadt 1015: errno = save_errno;
1.1 millert 1016: }
1017:
1018: /*
1019: * Set the SIGALRM interval timer for wait seconds, 0 to disable.
1020: */
1021: void
1.38 deraadt 1022: alarmtimer(int wait)
1.1 millert 1023: {
1.67 deraadt 1024: int save_errno = errno;
1.1 millert 1025: struct itimerval itv;
1026:
1027: itv.it_value.tv_sec = wait;
1028: itv.it_value.tv_usec = 0;
1029: itv.it_interval = itv.it_value;
1030: setitimer(ITIMER_REAL, &itv, NULL);
1.67 deraadt 1031: errno = save_errno;
1.1 millert 1032: }
1.5 millert 1033:
1034: /*
1035: * Setup or cleanup EditLine structures
1036: */
1037: #ifndef SMALL
1038: void
1.38 deraadt 1039: controlediting(void)
1.5 millert 1040: {
1.36 otto 1041: HistEvent hev;
1042:
1.5 millert 1043: if (editing && el == NULL && hist == NULL) {
1.36 otto 1044: el = el_init(__progname, stdin, ttyout, stderr); /* init editline */
1.5 millert 1045: hist = history_init(); /* init the builtin history */
1.36 otto 1046: history(hist, &hev, H_SETSIZE, 100); /* remember 100 events */
1.5 millert 1047: el_set(el, EL_HIST, history, hist); /* use history */
1048:
1049: el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
1050: el_set(el, EL_PROMPT, prompt); /* set the prompt function */
1051:
1052: /* add local file completion, bind to TAB */
1053: el_set(el, EL_ADDFN, "ftp-complete",
1054: "Context sensitive argument completion",
1055: complete);
1056: el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1057:
1058: el_source(el, NULL); /* read ~/.editrc */
1059: el_set(el, EL_SIGNAL, 1);
1060: } else if (!editing) {
1061: if (hist) {
1062: history_end(hist);
1063: hist = NULL;
1064: }
1065: if (el) {
1066: el_end(el);
1067: el = NULL;
1068: }
1069: }
1070: }
1071: #endif /* !SMALL */
1.62 martynas 1072: