Annotation of src/usr.bin/ftp/util.c, Revision 1.73
1.73 ! mmcc 1: /* $OpenBSD: util.c,v 1.72 2015/03/17 19:31:30 millert 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.32 millert 393: if ((cp = getenv("TMPDIR")) == NULL || *cp == '\0')
1.3 millert 394: cp = _PATH_TMP;
395: len = strlen(cp);
396: if (len + sizeof(TMPFILE) + (cp[len-1] != '/') > sizeof(temp)) {
397: warnx("unable to create temporary file: %s",
398: strerror(ENAMETOOLONG));
399: return (NULL);
400: }
401:
1.33 deraadt 402: (void)strlcpy(temp, cp, sizeof temp);
1.3 millert 403: if (temp[len-1] != '/')
404: temp[len++] = '/';
1.33 deraadt 405: (void)strlcpy(&temp[len], TMPFILE, sizeof temp - len);
1.40 deraadt 406: if ((fd = mkstemp(temp)) < 0) {
1.58 sthen 407: warn("unable to create temporary file: %s", temp);
1.40 deraadt 408: return (NULL);
409: }
410: close(fd);
1.4 millert 411: oldverbose = verbose;
412: verbose = (errbuf != NULL) ? -1 : 0;
413: oldhash = hash;
414: hash = 0;
1.40 deraadt 415: if (doswitch)
416: pswitch(!proxy);
1.43 ray 417: for (lmode = "w"; *++argv != NULL; lmode = "a")
1.54 martynas 418: recvrequest(type ? "LIST" : "NLST", temp, *argv, lmode,
419: 0, 0);
1.4 millert 420: if ((code / 100) != COMPLETE) {
421: if (errbuf != NULL)
422: *errbuf = reply_string;
423: }
424: if (doswitch)
425: pswitch(!proxy);
1.40 deraadt 426: verbose = oldverbose;
1.4 millert 427: hash = oldhash;
1.54 martynas 428: *ftemp = fopen(temp, "r");
1.40 deraadt 429: (void)unlink(temp);
1.54 martynas 430: if (*ftemp == NULL) {
1.4 millert 431: if (errbuf == NULL)
1.7 deraadt 432: fputs("can't find list of remote files, oops.\n",
433: ttyout);
1.4 millert 434: else
435: *errbuf =
436: "can't find list of remote files, oops.";
1.40 deraadt 437: return (NULL);
438: }
439: }
1.54 martynas 440: again:
441: if (fgets(buf, sizeof(buf), *ftemp) == NULL) {
442: (void)fclose(*ftemp);
443: *ftemp = NULL;
1.40 deraadt 444: return (NULL);
445: }
1.47 gilles 446:
447: buf[strcspn(buf, "\n")] = '\0';
1.54 martynas 448: bufp = buf;
449:
450: #ifndef SMALL
451: if (type) {
452: parse_list(&bufp, type);
1.56 martynas 453: if (!bufp ||
454: (bufp[0] == '.' && /* LIST defaults to -a on some ftp */
455: (bufp[1] == '\0' || /* servers. Ignore '.' and '..'. */
456: (bufp[1] == '.' && bufp[2] == '\0'))))
1.54 martynas 457: goto again;
458: }
459: #endif /* !SMALL */
460:
461: return (bufp);
462: }
463:
464: /*
465: * wrapper for remglob2
466: */
467: char *
468: remglob(char *argv[], int doswitch, char **errbuf)
469: {
470: static FILE *ftemp = NULL;
1.47 gilles 471:
1.54 martynas 472: return remglob2(argv, doswitch, errbuf, &ftemp, NULL);
1.1 millert 473: }
474:
1.62 martynas 475: #ifndef SMALL
1.1 millert 476: int
1.54 martynas 477: confirm(const char *cmd, const char *file)
1.1 millert 478: {
1.43 ray 479: char str[BUFSIZ];
1.1 millert 480:
1.54 martynas 481: if (file && (confirmrest || !interactive))
1.1 millert 482: return (1);
1.16 deraadt 483: top:
1.54 martynas 484: if (file)
485: fprintf(ttyout, "%s %s? ", cmd, file);
486: else
487: fprintf(ttyout, "Continue with %s? ", cmd);
1.7 deraadt 488: (void)fflush(ttyout);
1.51 martynas 489: if (fgets(str, sizeof(str), stdin) == NULL)
490: goto quit;
1.73 ! mmcc 491: switch (tolower((unsigned char)*str)) {
1.51 martynas 492: case '?':
493: fprintf(ttyout,
494: "? help\n"
495: "a answer yes to all\n"
496: "n answer no\n"
497: "p turn off prompt mode\n"
498: "q answer no to all\n"
499: "y answer yes\n");
500: goto top;
501: case 'a':
502: confirmrest = 1;
503: fprintf(ttyout, "Prompting off for duration of %s.\n",
504: cmd);
505: break;
1.1 millert 506: case 'n':
507: return (0);
508: case 'p':
509: interactive = 0;
1.7 deraadt 510: fputs("Interactive mode: off.\n", ttyout);
1.1 millert 511: break;
1.51 martynas 512: case 'q':
513: quit:
514: mflag = 0;
515: clearerr(stdin);
516: return (0);
1.12 jkatz 517: case 'y':
518: return(1);
519: break;
520: default:
1.51 martynas 521: fprintf(ttyout, "?, a, n, p, q, y "
522: "are the only acceptable commands!\n");
1.16 deraadt 523: goto top;
1.1 millert 524: break;
525: }
526: return (1);
527: }
1.62 martynas 528: #endif /* !SMALL */
1.1 millert 529:
530: /*
531: * Glob a local file name specification with
532: * the expectation of a single return value.
533: * Can't control multiple values being expanded
534: * from the expression, we return only the first.
535: */
536: int
1.38 deraadt 537: globulize(char **cpp)
1.1 millert 538: {
539: glob_t gl;
540: int flags;
541:
542: if (!doglob)
543: return (1);
544:
545: flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
546: memset(&gl, 0, sizeof(gl));
547: if (glob(*cpp, flags, NULL, &gl) ||
548: gl.gl_pathc == 0) {
549: warnx("%s: not found", *cpp);
550: globfree(&gl);
551: return (0);
552: }
1.13 millert 553: /* XXX: caller should check if *cpp changed, and
554: * free(*cpp) if that is the case
555: */
556: *cpp = strdup(gl.gl_pathv[0]);
1.30 deraadt 557: if (*cpp == NULL)
558: err(1, NULL);
1.1 millert 559: globfree(&gl);
560: return (1);
561: }
562:
563: /*
564: * determine size of remote file
565: */
566: off_t
1.38 deraadt 567: remotesize(const char *file, int noisy)
1.1 millert 568: {
569: int overbose;
570: off_t size;
571:
572: overbose = verbose;
573: size = -1;
1.54 martynas 574: #ifndef SMALL
575: if (!debug)
576: #endif /* !SMALL */
1.1 millert 577: verbose = -1;
1.11 millert 578: if (command("SIZE %s", file) == COMPLETE) {
579: char *cp, *ep;
580:
581: cp = strchr(reply_string, ' ');
582: if (cp != NULL) {
583: cp++;
584: size = strtoq(cp, &ep, 10);
1.73 ! mmcc 585: if (*ep != '\0' && !isspace((unsigned char)*ep))
1.11 millert 586: size = -1;
587: }
1.54 martynas 588: } else if (noisy
589: #ifndef SMALL
590: && !debug
591: #endif /* !SMALL */
592: ) {
1.7 deraadt 593: fputs(reply_string, ttyout);
1.11 millert 594: fputc('\n', ttyout);
1.7 deraadt 595: }
1.1 millert 596: verbose = overbose;
597: return (size);
598: }
599:
600: /*
601: * determine last modification time (in GMT) of remote file
602: */
603: time_t
1.38 deraadt 604: remotemodtime(const char *file, int noisy)
1.1 millert 605: {
606: int overbose;
607: time_t rtime;
1.15 millert 608: int ocode;
1.1 millert 609:
610: overbose = verbose;
1.15 millert 611: ocode = code;
1.1 millert 612: rtime = -1;
1.54 martynas 613: #ifndef SMALL
614: if (!debug)
615: #endif /* !SMALL */
1.1 millert 616: verbose = -1;
617: if (command("MDTM %s", file) == COMPLETE) {
618: struct tm timebuf;
619: int yy, mo, day, hour, min, sec;
1.23 espie 620: /*
621: * time-val = 14DIGIT [ "." 1*DIGIT ]
622: * YYYYMMDDHHMMSS[.sss]
623: * mdtm-response = "213" SP time-val CRLF / error-response
624: */
625: /* TODO: parse .sss as well, use timespecs. */
626: char *timestr = reply_string;
627:
628: /* Repair `19%02d' bug on server side */
1.73 ! mmcc 629: while (!isspace((unsigned char)*timestr))
1.23 espie 630: timestr++;
1.73 ! mmcc 631: while (isspace((unsigned char)*timestr))
1.23 espie 632: timestr++;
633: if (strncmp(timestr, "191", 3) == 0) {
634: fprintf(ttyout,
635: "Y2K warning! Fixed incorrect time-val received from server.\n");
636: timestr[0] = ' ';
637: timestr[1] = '2';
638: timestr[2] = '0';
639: }
1.1 millert 640: sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
641: &day, &hour, &min, &sec);
642: memset(&timebuf, 0, sizeof(timebuf));
643: timebuf.tm_sec = sec;
644: timebuf.tm_min = min;
645: timebuf.tm_hour = hour;
646: timebuf.tm_mday = day;
647: timebuf.tm_mon = mo - 1;
1.72 millert 648: timebuf.tm_year = yy - 1900;
1.1 millert 649: timebuf.tm_isdst = -1;
650: rtime = mktime(&timebuf);
1.54 martynas 651: if (rtime == -1 && (noisy
652: #ifndef SMALL
653: || debug
654: #endif /* !SMALL */
655: ))
1.7 deraadt 656: fprintf(ttyout, "Can't convert %s to a time.\n", reply_string);
1.1 millert 657: else
658: rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */
1.54 martynas 659: } else if (noisy
660: #ifndef SMALL
661: && !debug
662: #endif /* !SMALL */
663: ) {
1.7 deraadt 664: fputs(reply_string, ttyout);
1.11 millert 665: fputc('\n', ttyout);
1.7 deraadt 666: }
1.1 millert 667: verbose = overbose;
1.15 millert 668: if (rtime == -1)
669: code = ocode;
1.1 millert 670: return (rtime);
671: }
1.41 otto 672:
673: /*
674: * Ensure file is in or under dir.
675: * Returns 1 if so, 0 if not (or an error occurred).
676: */
677: int
678: fileindir(const char *file, const char *dir)
679: {
1.68 deraadt 680: char parentdirbuf[PATH_MAX], *parentdir;
681: char realdir[PATH_MAX];
1.41 otto 682: size_t dirlen;
683:
684: /* determine parent directory of file */
685: (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf));
686: parentdir = dirname(parentdirbuf);
687: if (strcmp(parentdir, ".") == 0)
688: return 1; /* current directory is ok */
689:
690: /* find the directory */
691: if (realpath(parentdir, realdir) == NULL) {
692: warn("Unable to determine real path of `%s'", parentdir);
693: return 0;
694: }
695: if (realdir[0] != '/') /* relative result is ok */
696: return 1;
697:
698: dirlen = strlen(dir);
699: if (strncmp(realdir, dir, dirlen) == 0 &&
700: (realdir[dirlen] == '/' || realdir[dirlen] == '\0'))
701: return 1;
702: return 0;
703: }
704:
1.1 millert 705:
1.10 millert 706: /*
707: * Returns true if this is the controlling/foreground process, else false.
708: */
709: int
1.38 deraadt 710: foregroundproc(void)
1.10 millert 711: {
712: static pid_t pgrp = -1;
713: int ctty_pgrp;
714:
715: if (pgrp == -1)
716: pgrp = getpgrp();
717:
718: return((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 &&
719: ctty_pgrp == pgrp));
720: }
721:
1.39 deraadt 722: /* ARGSUSED */
1.20 millert 723: static void
1.39 deraadt 724: updateprogressmeter(int signo)
1.1 millert 725: {
1.19 deraadt 726: int save_errno = errno;
1.1 millert 727:
1.20 millert 728: /* update progressmeter if foreground process or in -m mode */
729: if (foregroundproc() || progress == -1)
1.59 martynas 730: progressmeter(0, NULL);
1.19 deraadt 731: errno = save_errno;
1.1 millert 732: }
733:
734: /*
735: * Display a transfer progress bar if progress is non-zero.
736: * SIGALRM is hijacked for use by this function.
737: * - Before the transfer, set filesize to size of file (or -1 if unknown),
738: * and call with flag = -1. This starts the once per second timer,
739: * and a call to updateprogressmeter() upon SIGALRM.
740: * - During the transfer, updateprogressmeter will call progressmeter
741: * with flag = 0
742: * - After the transfer, call with flag = 1
743: */
744: static struct timeval start;
745:
1.65 deraadt 746: char *action;
747:
1.1 millert 748: void
1.59 martynas 749: progressmeter(int flag, const char *filename)
1.1 millert 750: {
751: /*
752: * List of order of magnitude prefixes.
753: * The last is `P', as 2^64 = 16384 Petabytes
754: */
755: static const char prefixes[] = " KMGTP";
756:
757: static struct timeval lastupdate;
758: static off_t lastsize;
1.59 martynas 759: static char *title = NULL;
1.1 millert 760: struct timeval now, td, wait;
761: off_t cursize, abbrevsize;
762: double elapsed;
1.57 deraadt 763: int ratio, barlength, i, remaining, overhead = 30;
1.29 deraadt 764: char buf[512];
1.1 millert 765:
766: if (flag == -1) {
1.2 millert 767: (void)gettimeofday(&start, (struct timezone *)0);
1.1 millert 768: lastupdate = start;
769: lastsize = restart_point;
770: }
1.2 millert 771: (void)gettimeofday(&now, (struct timezone *)0);
1.24 deraadt 772: if (!progress || filesize < 0)
1.1 millert 773: return;
774: cursize = bytes + restart_point;
775:
1.24 deraadt 776: if (filesize)
777: ratio = cursize * 100 / filesize;
778: else
779: ratio = 100;
1.68 deraadt 780: ratio = MAXIMUM(ratio, 0);
781: ratio = MINIMUM(ratio, 100);
1.59 martynas 782: if (!verbose && flag == -1) {
783: filename = basename(filename);
784: if (filename != NULL)
785: title = strdup(filename);
786: }
1.65 deraadt 787:
788: buf[0] = 0;
789: if (!verbose && action != NULL) {
790: int l = strlen(action);
791: char *dotdot = "";
792:
793: if (l < 7)
794: l = 7;
795: else if (l > 12) {
796: l = 12;
797: dotdot = "...";
798: overhead += 3;
799: }
800: snprintf(buf, sizeof(buf), "\r%-*.*s%s ", l, l, action,
801: dotdot);
802: overhead += l + 1;
803: } else
1.66 dcoppa 804: snprintf(buf, sizeof(buf), "\r");
1.65 deraadt 805:
1.59 martynas 806: if (!verbose && title != NULL) {
1.57 deraadt 807: int l = strlen(title);
808: char *dotdot = "";
809:
810: if (l < 12)
811: l = 12;
812: else if (l > 25) {
813: l = 22;
814: dotdot = "...";
815: overhead += 3;
816: }
1.65 deraadt 817: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
818: "%-*.*s%s %3d%% ", l, l, title,
1.57 deraadt 819: dotdot, ratio);
820: overhead += l + 1;
1.59 martynas 821: } else
1.57 deraadt 822: snprintf(buf, sizeof(buf), "\r%3d%% ", ratio);
1.1 millert 823:
1.57 deraadt 824: barlength = ttywidth - overhead;
1.1 millert 825: if (barlength > 0) {
826: i = barlength * ratio / 100;
827: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
828: "|%.*s%*s|", i,
1.29 deraadt 829: "*******************************************************"
830: "*******************************************************"
831: "*******************************************************"
832: "*******************************************************"
833: "*******************************************************"
834: "*******************************************************"
835: "*******************************************************",
1.1 millert 836: barlength - i, "");
837: }
838:
839: i = 0;
840: abbrevsize = cursize;
1.64 deraadt 841: while (abbrevsize >= 100000 && i < sizeof(prefixes)-1) {
1.1 millert 842: i++;
843: abbrevsize >>= 10;
844: }
845: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
1.26 deraadt 846: " %5lld %c%c ", (long long)abbrevsize, prefixes[i],
1.1 millert 847: prefixes[i] == ' ' ? ' ' : 'B');
848:
849: timersub(&now, &lastupdate, &wait);
850: if (cursize > lastsize) {
851: lastupdate = now;
852: lastsize = cursize;
853: if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */
854: start.tv_sec += wait.tv_sec;
855: start.tv_usec += wait.tv_usec;
856: }
857: wait.tv_sec = 0;
858: }
859:
860: timersub(&now, &start, &td);
861: elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
862:
1.24 deraadt 863: if (flag == 1) {
864: i = (int)elapsed / 3600;
865: if (i)
866: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
867: "%2d:", i);
868: else
869: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
870: " ");
871: i = (int)elapsed % 3600;
872: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
873: "%02d:%02d ", i / 60, i % 60);
874: } else if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
1.1 millert 875: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
876: " --:-- ETA");
877: } else if (wait.tv_sec >= STALLTIME) {
878: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
879: " - stalled -");
880: } else {
881: remaining = (int)((filesize - restart_point) /
882: (bytes / elapsed) - elapsed);
883: i = remaining / 3600;
884: if (i)
885: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
886: "%2d:", i);
887: else
888: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
889: " ");
890: i = remaining % 3600;
891: snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
892: "%02d:%02d ETA", i / 60, i % 60);
893: }
1.7 deraadt 894: (void)write(fileno(ttyout), buf, strlen(buf));
1.1 millert 895:
896: if (flag == -1) {
1.2 millert 897: (void)signal(SIGALRM, updateprogressmeter);
1.1 millert 898: alarmtimer(1); /* set alarm timer for 1 Hz */
899: } else if (flag == 1) {
900: alarmtimer(0);
1.7 deraadt 901: (void)putc('\n', ttyout);
1.59 martynas 902: if (title != NULL) {
903: free(title);
904: title = NULL;
905: }
1.1 millert 906: }
1.7 deraadt 907: fflush(ttyout);
1.1 millert 908: }
909:
910: /*
911: * Display transfer statistics.
912: * Requires start to be initialised by progressmeter(-1),
913: * direction to be defined by xfer routines, and filesize and bytes
914: * to be updated by xfer routines
915: * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR
1.7 deraadt 916: * instead of TTYOUT.
1.1 millert 917: */
918: void
1.38 deraadt 919: ptransfer(int siginfo)
1.1 millert 920: {
921: struct timeval now, td;
922: double elapsed;
923: off_t bs;
924: int meg, remaining, hh;
925: char buf[100];
926:
927: if (!verbose && !siginfo)
928: return;
929:
1.2 millert 930: (void)gettimeofday(&now, (struct timezone *)0);
1.1 millert 931: timersub(&now, &start, &td);
932: elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
933: bs = bytes / (elapsed == 0.0 ? 1 : elapsed);
934: meg = 0;
935: if (bs > (1024 * 1024))
936: meg = 1;
1.67 deraadt 937:
938: /* XXX floating point printf in signal handler */
1.1 millert 939: (void)snprintf(buf, sizeof(buf),
1.26 deraadt 940: "%lld byte%s %s in %.2f seconds (%.2f %sB/s)\n",
941: (long long)bytes, bytes == 1 ? "" : "s", direction, elapsed,
1.1 millert 942: bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K");
1.67 deraadt 943:
944: if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 &&
945: bytes + restart_point <= filesize) {
1.1 millert 946: remaining = (int)((filesize - restart_point) /
1.67 deraadt 947: (bytes / elapsed) - elapsed);
1.1 millert 948: hh = remaining / 3600;
949: remaining %= 3600;
1.67 deraadt 950:
951: /* "buf+len(buf) -1" to overwrite \n */
1.1 millert 952: snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf),
953: " ETA: %02d:%02d:%02d\n", hh, remaining / 60,
954: remaining % 60);
955: }
1.7 deraadt 956: (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, strlen(buf));
1.1 millert 957: }
958:
959: /*
960: * List words in stringlist, vertically arranged
961: */
1.62 martynas 962: #ifndef SMALL
1.1 millert 963: void
1.38 deraadt 964: list_vertical(StringList *sl)
1.1 millert 965: {
966: int i, j, w;
1.39 deraadt 967: int columns, width, lines;
1.1 millert 968: char *p;
969:
1.39 deraadt 970: width = 0;
1.1 millert 971:
972: for (i = 0 ; i < sl->sl_cur ; i++) {
973: w = strlen(sl->sl_str[i]);
974: if (w > width)
975: width = w;
976: }
977: width = (width + 8) &~ 7;
978:
979: columns = ttywidth / width;
980: if (columns == 0)
981: columns = 1;
982: lines = (sl->sl_cur + columns - 1) / columns;
983: for (i = 0; i < lines; i++) {
984: for (j = 0; j < columns; j++) {
985: p = sl->sl_str[j * lines + i];
986: if (p)
1.7 deraadt 987: fputs(p, ttyout);
1.1 millert 988: if (j * lines + i + lines >= sl->sl_cur) {
1.7 deraadt 989: putc('\n', ttyout);
1.1 millert 990: break;
991: }
992: w = strlen(p);
993: while (w < width) {
994: w = (w + 8) &~ 7;
1.7 deraadt 995: (void)putc('\t', ttyout);
1.1 millert 996: }
997: }
998: }
999: }
1.62 martynas 1000: #endif /* !SMALL */
1.1 millert 1001:
1002: /*
1003: * Update the global ttywidth value, using TIOCGWINSZ.
1004: */
1.39 deraadt 1005: /* ARGSUSED */
1.1 millert 1006: void
1.39 deraadt 1007: setttywidth(int signo)
1.1 millert 1008: {
1.19 deraadt 1009: int save_errno = errno;
1.1 millert 1010: struct winsize winsize;
1011:
1.7 deraadt 1012: if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1)
1.21 deraadt 1013: ttywidth = winsize.ws_col ? winsize.ws_col : 80;
1.1 millert 1014: else
1015: ttywidth = 80;
1.19 deraadt 1016: errno = save_errno;
1.1 millert 1017: }
1018:
1019: /*
1020: * Set the SIGALRM interval timer for wait seconds, 0 to disable.
1021: */
1022: void
1.38 deraadt 1023: alarmtimer(int wait)
1.1 millert 1024: {
1.67 deraadt 1025: int save_errno = errno;
1.1 millert 1026: struct itimerval itv;
1027:
1028: itv.it_value.tv_sec = wait;
1029: itv.it_value.tv_usec = 0;
1030: itv.it_interval = itv.it_value;
1031: setitimer(ITIMER_REAL, &itv, NULL);
1.67 deraadt 1032: errno = save_errno;
1.1 millert 1033: }
1.5 millert 1034:
1035: /*
1036: * Setup or cleanup EditLine structures
1037: */
1038: #ifndef SMALL
1039: void
1.38 deraadt 1040: controlediting(void)
1.5 millert 1041: {
1.36 otto 1042: HistEvent hev;
1043:
1.5 millert 1044: if (editing && el == NULL && hist == NULL) {
1.36 otto 1045: el = el_init(__progname, stdin, ttyout, stderr); /* init editline */
1.5 millert 1046: hist = history_init(); /* init the builtin history */
1.36 otto 1047: history(hist, &hev, H_SETSIZE, 100); /* remember 100 events */
1.5 millert 1048: el_set(el, EL_HIST, history, hist); /* use history */
1049:
1050: el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
1051: el_set(el, EL_PROMPT, prompt); /* set the prompt function */
1052:
1053: /* add local file completion, bind to TAB */
1054: el_set(el, EL_ADDFN, "ftp-complete",
1055: "Context sensitive argument completion",
1056: complete);
1057: el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
1058:
1059: el_source(el, NULL); /* read ~/.editrc */
1060: el_set(el, EL_SIGNAL, 1);
1061: } else if (!editing) {
1062: if (hist) {
1063: history_end(hist);
1064: hist = NULL;
1065: }
1066: if (el) {
1067: el_end(el);
1068: el = NULL;
1069: }
1070: }
1071: }
1072: #endif /* !SMALL */
1.62 martynas 1073: