Annotation of src/usr.bin/ftp/util.c, Revision 1.145
1.145 ! lukem 1: /* $NetBSD: util.c,v 1.144 2007/12/05 03:46:34 lukem Exp $ */
1.27 thorpej 2:
3: /*-
1.145 ! lukem 4: * Copyright (c) 1997-2008 The NetBSD Foundation, Inc.
1.27 thorpej 5: * All rights reserved.
6: *
7: * This code is derived from software contributed to The NetBSD Foundation
1.72 lukem 8: * by Luke Mewburn.
9: *
10: * This code is derived from software contributed to The NetBSD Foundation
1.27 thorpej 11: * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
12: * NASA Ames Research Center.
13: *
14: * Redistribution and use in source and binary forms, with or without
15: * modification, are permitted provided that the following conditions
16: * are met:
17: * 1. Redistributions of source code must retain the above copyright
18: * notice, this list of conditions and the following disclaimer.
19: * 2. Redistributions in binary form must reproduce the above copyright
20: * notice, this list of conditions and the following disclaimer in the
21: * documentation and/or other materials provided with the distribution.
22: * 3. All advertising materials mentioning features or use of this software
23: * must display the following acknowledgement:
24: * This product includes software developed by the NetBSD
25: * Foundation, Inc. and its contributors.
26: * 4. Neither the name of The NetBSD Foundation nor the names of its
27: * contributors may be used to endorse or promote products derived
28: * from this software without specific prior written permission.
29: *
30: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
31: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
32: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
34: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40: * POSSIBILITY OF SUCH DAMAGE.
41: */
1.1 lukem 42:
43: /*
44: * Copyright (c) 1985, 1989, 1993, 1994
45: * The Regents of the University of California. All rights reserved.
46: *
47: * Redistribution and use in source and binary forms, with or without
48: * modification, are permitted provided that the following conditions
49: * are met:
50: * 1. Redistributions of source code must retain the above copyright
51: * notice, this list of conditions and the following disclaimer.
52: * 2. Redistributions in binary form must reproduce the above copyright
53: * notice, this list of conditions and the following disclaimer in the
54: * documentation and/or other materials provided with the distribution.
1.114 agc 55: * 3. Neither the name of the University nor the names of its contributors
1.1 lukem 56: * may be used to endorse or promote products derived from this software
57: * without specific prior written permission.
58: *
59: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69: * SUCH DAMAGE.
70: */
71:
1.10 lukem 72: #include <sys/cdefs.h>
1.1 lukem 73: #ifndef lint
1.145 ! lukem 74: __RCSID("$NetBSD: util.c,v 1.144 2007/12/05 03:46:34 lukem Exp $");
1.1 lukem 75: #endif /* not lint */
76:
77: /*
78: * FTP User Program -- Misc support routines
79: */
1.117 lukem 80: #include <sys/param.h>
1.27 thorpej 81: #include <sys/socket.h>
1.1 lukem 82: #include <sys/ioctl.h>
83: #include <sys/time.h>
1.78 lukem 84: #include <netinet/in.h>
1.1 lukem 85: #include <arpa/ftp.h>
86:
87: #include <ctype.h>
88: #include <err.h>
1.53 lukem 89: #include <errno.h>
1.1 lukem 90: #include <fcntl.h>
91: #include <glob.h>
1.23 christos 92: #include <signal.h>
1.127 lukem 93: #include <libgen.h>
1.10 lukem 94: #include <limits.h>
1.102 lukem 95: #include <netdb.h>
1.1 lukem 96: #include <stdio.h>
1.10 lukem 97: #include <stdlib.h>
1.1 lukem 98: #include <string.h>
1.68 lukem 99: #include <termios.h>
1.1 lukem 100: #include <time.h>
1.17 lukem 101: #include <tzfile.h>
1.1 lukem 102: #include <unistd.h>
103:
104: #include "ftp_var.h"
105:
106: /*
1.96 lukem 107: * Connect to peer server and auto-login, if possible.
1.1 lukem 108: */
109: void
1.95 lukem 110: setpeer(int argc, char *argv[])
1.1 lukem 111: {
112: char *host;
1.54 itojun 113: char *port;
1.1 lukem 114:
1.79 lukem 115: if (argc == 0)
116: goto usage;
1.1 lukem 117: if (connected) {
1.24 lukem 118: fprintf(ttyout, "Already connected to %s, use close first.\n",
1.5 lukem 119: hostname);
1.1 lukem 120: code = -1;
121: return;
122: }
123: if (argc < 2)
1.5 lukem 124: (void)another(&argc, &argv, "to");
1.1 lukem 125: if (argc < 2 || argc > 3) {
1.79 lukem 126: usage:
1.130 christos 127: UPRINTF("usage: %s host-name [port]\n", argv[0]);
1.1 lukem 128: code = -1;
129: return;
130: }
1.12 lukem 131: if (gatemode)
132: port = gateport;
133: else
134: port = ftpport;
1.54 itojun 135: if (argc > 2)
1.80 lukem 136: port = argv[2];
1.12 lukem 137:
138: if (gatemode) {
139: if (gateserver == NULL || *gateserver == '\0')
1.138 lukem 140: errx(1, "main: gateserver not defined");
1.12 lukem 141: host = hookup(gateserver, port);
142: } else
143: host = hookup(argv[1], port);
144:
1.1 lukem 145: if (host) {
1.49 lukem 146: if (gatemode && verbose) {
147: fprintf(ttyout,
148: "Connecting via pass-through server %s\n",
149: gateserver);
1.12 lukem 150: }
151:
1.1 lukem 152: connected = 1;
153: /*
154: * Set up defaults for FTP.
155: */
1.65 lukem 156: (void)strlcpy(typename, "ascii", sizeof(typename));
157: type = TYPE_A;
1.1 lukem 158: curtype = TYPE_A;
1.65 lukem 159: (void)strlcpy(formname, "non-print", sizeof(formname));
160: form = FORM_N;
161: (void)strlcpy(modename, "stream", sizeof(modename));
162: mode = MODE_S;
163: (void)strlcpy(structname, "file", sizeof(structname));
164: stru = STRU_F;
165: (void)strlcpy(bytename, "8", sizeof(bytename));
166: bytesize = 8;
1.1 lukem 167: if (autologin)
1.28 lukem 168: (void)ftp_login(argv[1], NULL, NULL);
1.96 lukem 169: }
170: }
171:
1.97 lukem 172: static void
173: parse_feat(const char *line)
174: {
175:
1.113 lukem 176: /*
177: * work-around broken ProFTPd servers that can't
1.139 lukem 178: * even obey RFC2389.
1.113 lukem 179: */
180: while (*line && isspace((int)*line))
181: line++;
182:
183: if (strcasecmp(line, "MDTM") == 0)
1.97 lukem 184: features[FEAT_MDTM] = 1;
1.113 lukem 185: else if (strncasecmp(line, "MLST", sizeof("MLST") - 1) == 0) {
1.97 lukem 186: features[FEAT_MLST] = 1;
1.113 lukem 187: } else if (strcasecmp(line, "REST STREAM") == 0)
1.97 lukem 188: features[FEAT_REST_STREAM] = 1;
1.113 lukem 189: else if (strcasecmp(line, "SIZE") == 0)
1.97 lukem 190: features[FEAT_SIZE] = 1;
1.113 lukem 191: else if (strcasecmp(line, "TVFS") == 0)
1.97 lukem 192: features[FEAT_TVFS] = 1;
193: }
194:
1.96 lukem 195: /*
1.97 lukem 196: * Determine the remote system type (SYST) and features (FEAT).
1.96 lukem 197: * Call after a successful login (i.e, connected = -1)
198: */
199: void
1.97 lukem 200: getremoteinfo(void)
1.96 lukem 201: {
1.97 lukem 202: int overbose, i;
1.1 lukem 203:
1.96 lukem 204: overbose = verbose;
1.133 christos 205: if (ftp_debug == 0)
1.96 lukem 206: verbose = -1;
1.97 lukem 207:
208: /* determine remote system type */
209: if (command("SYST") == COMPLETE) {
210: if (overbose) {
211: char *cp, c;
212:
213: c = 0;
214: cp = strchr(reply_string + 4, ' ');
215: if (cp == NULL)
216: cp = strchr(reply_string + 4, '\r');
217: if (cp) {
218: if (cp[-1] == '.')
219: cp--;
220: c = *cp;
221: *cp = '\0';
222: }
223:
224: fprintf(ttyout, "Remote system type is %s.\n",
225: reply_string + 4);
226: if (cp)
227: *cp = c;
228: }
229: if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
230: if (proxy)
231: unix_proxy = 1;
232: else
233: unix_server = 1;
234: /*
235: * Set type to 0 (not specified by user),
236: * meaning binary by default, but don't bother
237: * telling server. We can use binary
238: * for text files unless changed by the user.
239: */
240: type = 0;
241: (void)strlcpy(typename, "binary", sizeof(typename));
242: if (overbose)
243: fprintf(ttyout,
244: "Using %s mode to transfer files.\n",
245: typename);
246: } else {
247: if (proxy)
248: unix_proxy = 0;
249: else
250: unix_server = 0;
251: if (overbose &&
252: !strncmp(reply_string, "215 TOPS20", 10))
253: fputs(
1.24 lukem 254: "Remember to set tenex mode when transferring binary files from this machine.\n",
1.97 lukem 255: ttyout);
256: }
1.1 lukem 257: }
1.97 lukem 258:
259: /* determine features (if any) */
260: for (i = 0; i < FEAT_max; i++)
261: features[i] = -1;
262: reply_callback = parse_feat;
263: if (command("FEAT") == COMPLETE) {
264: for (i = 0; i < FEAT_max; i++) {
265: if (features[i] == -1)
266: features[i] = 0;
267: }
268: features[FEAT_FEAT] = 1;
269: } else
270: features[FEAT_FEAT] = 0;
1.130 christos 271: #ifndef NO_DEBUG
1.133 christos 272: if (ftp_debug) {
1.113 lukem 273: #define DEBUG_FEAT(x) fprintf(ttyout, "features[" #x "] = %d\n", features[(x)])
274: DEBUG_FEAT(FEAT_FEAT);
275: DEBUG_FEAT(FEAT_MDTM);
276: DEBUG_FEAT(FEAT_MLST);
277: DEBUG_FEAT(FEAT_REST_STREAM);
278: DEBUG_FEAT(FEAT_SIZE);
279: DEBUG_FEAT(FEAT_TVFS);
280: #undef DEBUG_FEAT
281: }
1.130 christos 282: #endif
1.97 lukem 283: reply_callback = NULL;
284:
1.96 lukem 285: verbose = overbose;
1.7 lukem 286: }
287:
288: /*
1.79 lukem 289: * Reset the various variables that indicate connection state back to
290: * disconnected settings.
291: * The caller is responsible for issuing any commands to the remote server
292: * to perform a clean shutdown before this is invoked.
293: */
294: void
1.96 lukem 295: cleanuppeer(void)
1.79 lukem 296: {
297:
298: if (cout)
299: (void)fclose(cout);
300: cout = NULL;
301: connected = 0;
1.96 lukem 302: unix_server = 0;
303: unix_proxy = 0;
1.79 lukem 304: /*
305: * determine if anonftp was specifically set with -a
306: * (1), or implicitly set by auto_fetch() (2). in the
307: * latter case, disable after the current xfer
308: */
309: if (anonftp == 2)
310: anonftp = 0;
311: data = -1;
312: epsv4bad = 0;
1.81 lukem 313: if (username)
314: free(username);
1.84 lukem 315: username = NULL;
1.79 lukem 316: if (!proxy)
317: macnum = 0;
318: }
319:
320: /*
321: * Top-level signal handler for interrupted commands.
322: */
323: void
1.116 lukem 324: intr(int signo)
1.79 lukem 325: {
326:
1.116 lukem 327: sigint_raised = 1;
1.79 lukem 328: alarmtimer(0);
329: if (fromatty)
330: write(fileno(ttyout), "\n", 1);
331: siglongjmp(toplevel, 1);
332: }
333:
334: /*
335: * Signal handler for lost connections; cleanup various elements of
336: * the connection state, and call cleanuppeer() to finish it off.
337: */
338: void
1.95 lukem 339: lostpeer(int dummy)
1.79 lukem 340: {
341: int oerrno = errno;
342:
343: alarmtimer(0);
344: if (connected) {
345: if (cout != NULL) {
346: (void)shutdown(fileno(cout), 1+1);
347: (void)fclose(cout);
348: cout = NULL;
349: }
350: if (data >= 0) {
351: (void)shutdown(data, 1+1);
352: (void)close(data);
353: data = -1;
354: }
355: connected = 0;
356: }
357: pswitch(1);
358: if (connected) {
359: if (cout != NULL) {
360: (void)shutdown(fileno(cout), 1+1);
361: (void)fclose(cout);
362: cout = NULL;
363: }
364: connected = 0;
365: }
366: proxflag = 0;
367: pswitch(0);
368: cleanuppeer();
369: errno = oerrno;
370: }
371:
372:
373: /*
1.96 lukem 374: * Login to remote host, using given username & password if supplied.
375: * Return non-zero if successful.
1.7 lukem 376: */
377: int
1.129 lukem 378: ftp_login(const char *host, const char *luser, const char *lpass)
1.7 lukem 379: {
380: char tmp[80];
1.129 lukem 381: char *user, *pass, *acct, *p;
1.136 lukem 382: char emptypass[] = "";
1.129 lukem 383: const char *errormsg;
384: int n, aflag, rval, nlen;
385:
386: aflag = rval = 0;
387: user = pass = acct = NULL;
388: if (luser)
1.132 christos 389: user = ftp_strdup(luser);
1.129 lukem 390: if (lpass)
1.132 christos 391: pass = ftp_strdup(lpass);
1.7 lukem 392:
1.130 christos 393: DPRINTF("ftp_login: user `%s' pass `%s' host `%s'\n",
1.144 lukem 394: STRorNULL(user), STRorNULL(pass), STRorNULL(host));
1.83 lukem 395:
1.7 lukem 396: /*
397: * Set up arguments for an anonymous FTP session, if necessary.
398: */
1.47 lukem 399: if (anonftp) {
1.129 lukem 400: FREEPTR(user);
1.139 lukem 401: user = ftp_strdup("anonymous"); /* as per RFC1635 */
1.129 lukem 402: FREEPTR(pass);
1.132 christos 403: pass = ftp_strdup(getoptionvalue("anonpass"));
1.7 lukem 404: }
405:
1.53 lukem 406: if (ruserpass(host, &user, &pass, &acct) < 0) {
407: code = -1;
408: goto cleanup_ftp_login;
1.47 lukem 409: }
410:
1.7 lukem 411: while (user == NULL) {
1.103 lukem 412: if (localname)
413: fprintf(ttyout, "Name (%s:%s): ", host, localname);
1.7 lukem 414: else
1.24 lukem 415: fprintf(ttyout, "Name (%s): ", host);
1.129 lukem 416: errormsg = NULL;
417: nlen = getline(stdin, tmp, sizeof(tmp), &errormsg);
418: if (nlen < 0) {
419: fprintf(ttyout, "%s; %s aborted.\n", errormsg, "login");
1.49 lukem 420: code = -1;
421: goto cleanup_ftp_login;
1.129 lukem 422: } else if (nlen == 0) {
1.132 christos 423: user = ftp_strdup(localname);
1.129 lukem 424: } else {
1.132 christos 425: user = ftp_strdup(tmp);
1.49 lukem 426: }
1.7 lukem 427: }
1.49 lukem 428:
429: if (gatemode) {
430: char *nuser;
1.125 lukem 431: size_t len;
1.49 lukem 432:
433: len = strlen(user) + 1 + strlen(host) + 1;
1.132 christos 434: nuser = ftp_malloc(len);
1.65 lukem 435: (void)strlcpy(nuser, user, len);
436: (void)strlcat(nuser, "@", len);
437: (void)strlcat(nuser, host, len);
1.129 lukem 438: FREEPTR(user);
1.49 lukem 439: user = nuser;
440: }
441:
1.7 lukem 442: n = command("USER %s", user);
443: if (n == CONTINUE) {
1.51 christos 444: if (pass == NULL) {
1.129 lukem 445: p = getpass("Password: ");
1.136 lukem 446: if (p == NULL)
447: p = emptypass;
1.132 christos 448: pass = ftp_strdup(p);
1.129 lukem 449: memset(p, 0, strlen(p));
1.51 christos 450: }
1.7 lukem 451: n = command("PASS %s", pass);
1.129 lukem 452: memset(pass, 0, strlen(pass));
1.7 lukem 453: }
454: if (n == CONTINUE) {
455: aflag++;
1.47 lukem 456: if (acct == NULL) {
1.129 lukem 457: p = getpass("Account: ");
1.136 lukem 458: if (p == NULL)
459: p = emptypass;
1.132 christos 460: acct = ftp_strdup(p);
1.129 lukem 461: memset(p, 0, strlen(p));
1.49 lukem 462: }
463: if (acct[0] == '\0') {
1.138 lukem 464: warnx("Login failed");
1.49 lukem 465: goto cleanup_ftp_login;
1.47 lukem 466: }
1.7 lukem 467: n = command("ACCT %s", acct);
1.129 lukem 468: memset(acct, 0, strlen(acct));
1.7 lukem 469: }
470: if ((n != COMPLETE) ||
471: (!aflag && acct != NULL && command("ACCT %s", acct) != COMPLETE)) {
1.138 lukem 472: warnx("Login failed");
1.47 lukem 473: goto cleanup_ftp_login;
474: }
1.48 matthias 475: rval = 1;
1.132 christos 476: username = ftp_strdup(user);
1.48 matthias 477: if (proxy)
1.47 lukem 478: goto cleanup_ftp_login;
1.48 matthias 479:
1.7 lukem 480: connected = -1;
1.97 lukem 481: getremoteinfo();
1.7 lukem 482: for (n = 0; n < macnum; ++n) {
483: if (!strcmp("init", macros[n].mac_name)) {
1.65 lukem 484: (void)strlcpy(line, "$init", sizeof(line));
1.7 lukem 485: makeargv();
486: domacro(margc, margv);
487: break;
488: }
489: }
1.117 lukem 490: updatelocalcwd();
491: updateremotecwd();
1.83 lukem 492:
1.97 lukem 493: cleanup_ftp_login:
1.129 lukem 494: FREEPTR(user);
495: if (pass != NULL)
496: memset(pass, 0, strlen(pass));
497: FREEPTR(pass);
498: if (acct != NULL)
1.135 jnemeth 499: memset(acct, 0, strlen(acct));
1.129 lukem 500: FREEPTR(acct);
1.47 lukem 501: return (rval);
1.1 lukem 502: }
503:
504: /*
1.5 lukem 505: * `another' gets another argument, and stores the new argc and argv.
1.79 lukem 506: * It reverts to the top level (via intr()) on EOF/error.
1.1 lukem 507: *
508: * Returns false if no new arguments have been added.
509: */
510: int
1.95 lukem 511: another(int *pargc, char ***pargv, const char *prompt)
1.1 lukem 512: {
1.129 lukem 513: const char *errormsg;
514: int ret, nlen;
515: size_t len;
1.1 lukem 516:
1.125 lukem 517: len = strlen(line);
1.1 lukem 518: if (len >= sizeof(line) - 3) {
1.129 lukem 519: fputs("Sorry, arguments too long.\n", ttyout);
1.75 lukem 520: intr(0);
1.1 lukem 521: }
1.24 lukem 522: fprintf(ttyout, "(%s) ", prompt);
1.1 lukem 523: line[len++] = ' ';
1.129 lukem 524: errormsg = NULL;
525: nlen = getline(stdin, line + len, sizeof(line)-len, &errormsg);
526: if (nlen < 0) {
527: fprintf(ttyout, "%s; %s aborted.\n", errormsg, "operation");
1.75 lukem 528: intr(0);
1.77 lukem 529: }
1.129 lukem 530: len += nlen;
1.1 lukem 531: makeargv();
532: ret = margc > *pargc;
533: *pargc = margc;
534: *pargv = margv;
535: return (ret);
536: }
537:
1.5 lukem 538: /*
539: * glob files given in argv[] from the remote server.
540: * if errbuf isn't NULL, store error messages there instead
541: * of writing to the screen.
542: */
1.1 lukem 543: char *
1.124 lukem 544: remglob(char *argv[], int doswitch, const char **errbuf)
1.1 lukem 545: {
1.126 lukem 546: static char buf[MAXPATHLEN];
547: static FILE *ftemp = NULL;
548: static char **args;
549: char temp[MAXPATHLEN];
550: int oldverbose, oldhash, oldprogress, fd;
1.129 lukem 551: char *cp;
552: const char *mode;
1.125 lukem 553: size_t len;
1.1 lukem 554:
1.126 lukem 555: if (!mflag || !connected) {
556: if (!doglob)
557: args = NULL;
558: else {
559: if (ftemp) {
560: (void)fclose(ftemp);
561: ftemp = NULL;
562: }
563: }
564: return (NULL);
565: }
566: if (!doglob) {
567: if (args == NULL)
568: args = argv;
569: if ((cp = *++args) == NULL)
570: args = NULL;
571: return (cp);
572: }
573: if (ftemp == NULL) {
1.75 lukem 574: len = strlcpy(temp, tmpdir, sizeof(temp));
575: if (temp[len - 1] != '/')
1.74 simonb 576: (void)strlcat(temp, "/", sizeof(temp));
577: (void)strlcat(temp, TMPFILE, sizeof(temp));
1.126 lukem 578: if ((fd = mkstemp(temp)) < 0) {
1.138 lukem 579: warn("Unable to create temporary file `%s'", temp);
1.126 lukem 580: return (NULL);
581: }
582: close(fd);
583: oldverbose = verbose;
1.5 lukem 584: verbose = (errbuf != NULL) ? -1 : 0;
1.126 lukem 585: oldhash = hash;
1.104 lukem 586: oldprogress = progress;
1.126 lukem 587: hash = 0;
1.104 lukem 588: progress = 0;
1.126 lukem 589: if (doswitch)
590: pswitch(!proxy);
591: for (mode = "w"; *++argv != NULL; mode = "a")
592: recvrequest("NLST", temp, *argv, mode, 0, 0);
1.5 lukem 593: if ((code / 100) != COMPLETE) {
594: if (errbuf != NULL)
595: *errbuf = reply_string;
596: }
1.126 lukem 597: if (doswitch)
598: pswitch(!proxy);
599: verbose = oldverbose;
1.5 lukem 600: hash = oldhash;
1.104 lukem 601: progress = oldprogress;
1.126 lukem 602: ftemp = fopen(temp, "r");
603: (void)unlink(temp);
604: if (ftemp == NULL) {
1.5 lukem 605: if (errbuf == NULL)
1.138 lukem 606: warnx("Can't find list of remote files");
1.5 lukem 607: else
608: *errbuf =
1.138 lukem 609: "Can't find list of remote files";
1.126 lukem 610: return (NULL);
611: }
612: }
613: if (fgets(buf, sizeof(buf), ftemp) == NULL) {
614: (void)fclose(ftemp);
1.5 lukem 615: ftemp = NULL;
1.126 lukem 616: return (NULL);
617: }
618: if ((cp = strchr(buf, '\n')) != NULL)
619: *cp = '\0';
620: return (buf);
1.1 lukem 621: }
622:
623: /*
1.65 lukem 624: * Glob a local file name specification with the expectation of a single
625: * return value. Can't control multiple values being expanded from the
626: * expression, we return only the first.
627: * Returns NULL on error, or a pointer to a buffer containing the filename
628: * that's the caller's responsiblity to free(3) when finished with.
1.1 lukem 629: */
1.65 lukem 630: char *
1.95 lukem 631: globulize(const char *pattern)
1.1 lukem 632: {
633: glob_t gl;
634: int flags;
1.65 lukem 635: char *p;
1.1 lukem 636:
637: if (!doglob)
1.132 christos 638: return (ftp_strdup(pattern));
1.1 lukem 639:
1.25 kleink 640: flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
1.1 lukem 641: memset(&gl, 0, sizeof(gl));
1.65 lukem 642: if (glob(pattern, flags, NULL, &gl) || gl.gl_pathc == 0) {
1.138 lukem 643: warnx("Glob pattern `%s' not found", pattern);
1.1 lukem 644: globfree(&gl);
1.65 lukem 645: return (NULL);
1.1 lukem 646: }
1.132 christos 647: p = ftp_strdup(gl.gl_pathv[0]);
1.1 lukem 648: globfree(&gl);
1.65 lukem 649: return (p);
1.1 lukem 650: }
651:
652: /*
653: * determine size of remote file
654: */
655: off_t
1.95 lukem 656: remotesize(const char *file, int noisy)
1.1 lukem 657: {
1.97 lukem 658: int overbose, r;
1.1 lukem 659: off_t size;
660:
661: overbose = verbose;
662: size = -1;
1.133 christos 663: if (ftp_debug == 0)
1.1 lukem 664: verbose = -1;
1.97 lukem 665: if (! features[FEAT_SIZE]) {
666: if (noisy)
667: fprintf(ttyout,
668: "SIZE is not supported by remote server.\n");
669: goto cleanup_remotesize;
670: }
671: r = command("SIZE %s", file);
672: if (r == COMPLETE) {
1.11 lukem 673: char *cp, *ep;
674:
675: cp = strchr(reply_string, ' ');
676: if (cp != NULL) {
677: cp++;
1.98 lukem 678: size = STRTOLL(cp, &ep, 10);
1.23 christos 679: if (*ep != '\0' && !isspace((unsigned char)*ep))
1.11 lukem 680: size = -1;
681: }
1.97 lukem 682: } else {
683: if (r == ERROR && code == 500 && features[FEAT_SIZE] == -1)
684: features[FEAT_SIZE] = 0;
1.133 christos 685: if (noisy && ftp_debug == 0) {
1.97 lukem 686: fputs(reply_string, ttyout);
687: putc('\n', ttyout);
688: }
1.24 lukem 689: }
1.97 lukem 690: cleanup_remotesize:
1.1 lukem 691: verbose = overbose;
692: return (size);
693: }
694:
695: /*
696: * determine last modification time (in GMT) of remote file
697: */
698: time_t
1.95 lukem 699: remotemodtime(const char *file, int noisy)
1.1 lukem 700: {
1.97 lukem 701: int overbose, ocode, r;
702: time_t rtime;
1.1 lukem 703:
704: overbose = verbose;
1.14 lukem 705: ocode = code;
1.1 lukem 706: rtime = -1;
1.133 christos 707: if (ftp_debug == 0)
1.1 lukem 708: verbose = -1;
1.97 lukem 709: if (! features[FEAT_MDTM]) {
710: if (noisy)
711: fprintf(ttyout,
712: "MDTM is not supported by remote server.\n");
713: goto cleanup_parse_time;
714: }
715: r = command("MDTM %s", file);
716: if (r == COMPLETE) {
1.1 lukem 717: struct tm timebuf;
1.87 lukem 718: char *timestr, *frac;
719:
720: /*
721: * time-val = 14DIGIT [ "." 1*DIGIT ]
722: * YYYYMMDDHHMMSS[.sss]
723: * mdtm-response = "213" SP time-val CRLF / error-response
724: */
725: timestr = reply_string + 4;
726:
727: /*
728: * parse fraction.
729: * XXX: ignored for now
730: */
731: frac = strchr(timestr, '\r');
732: if (frac != NULL)
733: *frac = '\0';
734: frac = strchr(timestr, '.');
735: if (frac != NULL)
736: *frac++ = '\0';
737: if (strlen(timestr) == 15 && strncmp(timestr, "191", 3) == 0) {
738: /*
1.88 lukem 739: * XXX: Workaround for lame ftpd's that return
740: * `19100' instead of `2000'
1.87 lukem 741: */
742: fprintf(ttyout,
743: "Y2K warning! Incorrect time-val `%s' received from server.\n",
744: timestr);
745: timestr++;
746: timestr[0] = '2';
747: timestr[1] = '0';
748: fprintf(ttyout, "Converted to `%s'\n", timestr);
749: }
1.140 lukem 750: memset(&timebuf, 0, sizeof(timebuf));
1.87 lukem 751: if (strlen(timestr) != 14 ||
1.140 lukem 752: (strptime(timestr, "%Y%m%d%H%M%S", &timebuf) == NULL)) {
1.87 lukem 753: bad_parse_time:
1.90 lukem 754: fprintf(ttyout, "Can't parse time `%s'.\n", timestr);
1.87 lukem 755: goto cleanup_parse_time;
756: }
1.90 lukem 757: timebuf.tm_isdst = -1;
1.61 lukem 758: rtime = timegm(&timebuf);
1.89 lukem 759: if (rtime == -1) {
1.133 christos 760: if (noisy || ftp_debug != 0)
1.89 lukem 761: goto bad_parse_time;
762: else
763: goto cleanup_parse_time;
1.137 lukem 764: } else
1.144 lukem 765: DPRINTF("remotemodtime: parsed date `%s' as " LLF
766: ", %s",
1.143 lukem 767: timestr, (LLT)rtime,
768: rfc2822time(localtime(&rtime)));
1.97 lukem 769: } else {
770: if (r == ERROR && code == 500 && features[FEAT_MDTM] == -1)
771: features[FEAT_MDTM] = 0;
1.133 christos 772: if (noisy && ftp_debug == 0) {
1.97 lukem 773: fputs(reply_string, ttyout);
774: putc('\n', ttyout);
775: }
1.24 lukem 776: }
1.87 lukem 777: cleanup_parse_time:
1.1 lukem 778: verbose = overbose;
1.14 lukem 779: if (rtime == -1)
780: code = ocode;
1.1 lukem 781: return (rtime);
1.81 lukem 782: }
783:
784: /*
1.143 lukem 785: * Format tm in an RFC2822 compatible manner, with a trailing \n.
786: * Returns a pointer to a static string containing the result.
787: */
788: const char *
789: rfc2822time(const struct tm *tm)
790: {
791: static char result[50];
792:
793: if (strftime(result, sizeof(result),
794: "%a, %d %b %Y %H:%M:%S %z\n", tm) == 0)
795: errx(1, "Can't convert RFC2822 time: buffer too small");
796: return result;
797: }
798:
799: /*
1.117 lukem 800: * Update global `localcwd', which contains the state of the local cwd
1.81 lukem 801: */
802: void
1.117 lukem 803: updatelocalcwd(void)
804: {
805:
806: if (getcwd(localcwd, sizeof(localcwd)) == NULL)
807: localcwd[0] = '\0';
1.144 lukem 808: DPRINTF("updatelocalcwd: got `%s'\n", localcwd);
1.117 lukem 809: }
810:
811: /*
812: * Update global `remotecwd', which contains the state of the remote cwd
813: */
814: void
815: updateremotecwd(void)
1.81 lukem 816: {
817: int overbose, ocode, i;
818: char *cp;
819:
820: overbose = verbose;
821: ocode = code;
1.133 christos 822: if (ftp_debug == 0)
1.81 lukem 823: verbose = -1;
824: if (command("PWD") != COMPLETE)
1.117 lukem 825: goto badremotecwd;
1.81 lukem 826: cp = strchr(reply_string, ' ');
827: if (cp == NULL || cp[0] == '\0' || cp[1] != '"')
1.117 lukem 828: goto badremotecwd;
1.81 lukem 829: cp += 2;
1.117 lukem 830: for (i = 0; *cp && i < sizeof(remotecwd) - 1; i++, cp++) {
1.81 lukem 831: if (cp[0] == '"') {
832: if (cp[1] == '"')
833: cp++;
834: else
835: break;
836: }
1.117 lukem 837: remotecwd[i] = *cp;
1.81 lukem 838: }
1.117 lukem 839: remotecwd[i] = '\0';
1.144 lukem 840: DPRINTF("updateremotecwd: got `%s'\n", remotecwd);
1.117 lukem 841: goto cleanupremotecwd;
842: badremotecwd:
843: remotecwd[0]='\0';
844: cleanupremotecwd:
1.81 lukem 845: verbose = overbose;
846: code = ocode;
1.1 lukem 847: }
848:
1.117 lukem 849: /*
850: * Ensure file is in or under dir.
851: * Returns 1 if so, 0 if not (or an error occurred).
852: */
853: int
854: fileindir(const char *file, const char *dir)
855: {
1.127 lukem 856: char parentdirbuf[PATH_MAX+1], *parentdir;
857: char realdir[PATH_MAX+1];
1.117 lukem 858: size_t dirlen;
859:
1.137 lukem 860: /* determine parent directory of file */
1.127 lukem 861: (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf));
862: parentdir = dirname(parentdirbuf);
863: if (strcmp(parentdir, ".") == 0)
864: return 1; /* current directory is ok */
865:
866: /* find the directory */
867: if (realpath(parentdir, realdir) == NULL) {
868: warn("Unable to determine real path of `%s'", parentdir);
1.117 lukem 869: return 0;
870: }
1.127 lukem 871: if (realdir[0] != '/') /* relative result is ok */
1.117 lukem 872: return 1;
873: dirlen = strlen(dir);
1.127 lukem 874: if (strncmp(realdir, dir, dirlen) == 0 &&
875: (realdir[dirlen] == '/' || realdir[dirlen] == '\0'))
1.117 lukem 876: return 1;
877: return 0;
878: }
1.73 lukem 879:
880: /*
1.1 lukem 881: * List words in stringlist, vertically arranged
882: */
883: void
1.95 lukem 884: list_vertical(StringList *sl)
1.1 lukem 885: {
1.125 lukem 886: int i, j;
887: int columns, lines;
1.1 lukem 888: char *p;
1.125 lukem 889: size_t w, width;
1.1 lukem 890:
1.101 lukem 891: width = 0;
1.1 lukem 892:
893: for (i = 0 ; i < sl->sl_cur ; i++) {
894: w = strlen(sl->sl_str[i]);
895: if (w > width)
896: width = w;
897: }
898: width = (width + 8) &~ 7;
899:
900: columns = ttywidth / width;
901: if (columns == 0)
902: columns = 1;
903: lines = (sl->sl_cur + columns - 1) / columns;
904: for (i = 0; i < lines; i++) {
905: for (j = 0; j < columns; j++) {
906: p = sl->sl_str[j * lines + i];
907: if (p)
1.24 lukem 908: fputs(p, ttyout);
1.1 lukem 909: if (j * lines + i + lines >= sl->sl_cur) {
1.24 lukem 910: putc('\n', ttyout);
1.1 lukem 911: break;
912: }
1.134 christos 913: if (p) {
914: w = strlen(p);
915: while (w < width) {
916: w = (w + 8) &~ 7;
917: (void)putc('\t', ttyout);
918: }
1.1 lukem 919: }
920: }
921: }
1.3 lukem 922: }
923:
924: /*
925: * Update the global ttywidth value, using TIOCGWINSZ.
926: */
927: void
1.95 lukem 928: setttywidth(int a)
1.3 lukem 929: {
930: struct winsize winsize;
1.75 lukem 931: int oerrno = errno;
1.3 lukem 932:
1.34 lukem 933: if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1 &&
934: winsize.ws_col != 0)
1.3 lukem 935: ttywidth = winsize.ws_col;
936: else
937: ttywidth = 80;
1.53 lukem 938: errno = oerrno;
1.3 lukem 939: }
940:
1.73 lukem 941: /*
942: * Change the rate limit up (SIGUSR1) or down (SIGUSR2)
943: */
1.53 lukem 944: void
1.95 lukem 945: crankrate(int sig)
1.53 lukem 946: {
947:
948: switch (sig) {
949: case SIGUSR1:
950: if (rate_get)
951: rate_get += rate_get_incr;
952: if (rate_put)
953: rate_put += rate_put_incr;
954: break;
955: case SIGUSR2:
956: if (rate_get && rate_get > rate_get_incr)
957: rate_get -= rate_get_incr;
958: if (rate_put && rate_put > rate_put_incr)
959: rate_put -= rate_put_incr;
960: break;
961: default:
962: err(1, "crankrate invoked with unknown signal: %d", sig);
963: }
964: }
965:
966:
1.3 lukem 967: /*
1.6 lukem 968: * Setup or cleanup EditLine structures
969: */
1.50 cgd 970: #ifndef NO_EDITCOMPLETE
1.6 lukem 971: void
1.95 lukem 972: controlediting(void)
1.6 lukem 973: {
974: if (editing && el == NULL && hist == NULL) {
1.16 christos 975: HistEvent ev;
1.30 lukem 976: int editmode;
1.16 christos 977:
1.105 cgd 978: el = el_init(getprogname(), stdin, ttyout, stderr);
1.23 christos 979: /* init editline */
1.6 lukem 980: hist = history_init(); /* init the builtin history */
1.23 christos 981: history(hist, &ev, H_SETSIZE, 100);/* remember 100 events */
1.6 lukem 982: el_set(el, EL_HIST, history, hist); /* use history */
983:
984: el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */
1.82 lukem 985: el_set(el, EL_PROMPT, prompt); /* set the prompt functions */
986: el_set(el, EL_RPROMPT, rprompt);
1.6 lukem 987:
988: /* add local file completion, bind to TAB */
989: el_set(el, EL_ADDFN, "ftp-complete",
990: "Context sensitive argument completion",
991: complete);
992: el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
993: el_source(el, NULL); /* read ~/.editrc */
1.30 lukem 994: if ((el_get(el, EL_EDITMODE, &editmode) != -1) && editmode == 0)
995: editing = 0; /* the user doesn't want editing,
996: * so disable, and let statement
997: * below cleanup */
998: else
999: el_set(el, EL_SIGNAL, 1);
1000: }
1001: if (!editing) {
1.6 lukem 1002: if (hist) {
1003: history_end(hist);
1004: hist = NULL;
1005: }
1006: if (el) {
1007: el_end(el);
1008: el = NULL;
1009: }
1010: }
1011: }
1.50 cgd 1012: #endif /* !NO_EDITCOMPLETE */
1.27 thorpej 1013:
1014: /*
1.53 lukem 1015: * Convert the string `arg' to an int, which may have an optional SI suffix
1016: * (`b', `k', `m', `g'). Returns the number for success, -1 otherwise.
1.27 thorpej 1017: */
1018: int
1.95 lukem 1019: strsuftoi(const char *arg)
1.27 thorpej 1020: {
1021: char *cp;
1.53 lukem 1022: long val;
1.27 thorpej 1023:
1.35 christos 1024: if (!isdigit((unsigned char)arg[0]))
1.27 thorpej 1025: return (-1);
1026:
1027: val = strtol(arg, &cp, 10);
1028: if (cp != NULL) {
1.53 lukem 1029: if (cp[0] != '\0' && cp[1] != '\0')
1.27 thorpej 1030: return (-1);
1.53 lukem 1031: switch (tolower((unsigned char)cp[0])) {
1032: case '\0':
1033: case 'b':
1034: break;
1035: case 'k':
1036: val <<= 10;
1037: break;
1038: case 'm':
1039: val <<= 20;
1040: break;
1041: case 'g':
1042: val <<= 30;
1043: break;
1044: default:
1045: return (-1);
1046: }
1.27 thorpej 1047: }
1.53 lukem 1048: if (val < 0 || val > INT_MAX)
1.27 thorpej 1049: return (-1);
1050:
1051: return (val);
1052: }
1053:
1054: /*
1055: * Set up socket buffer sizes before a connection is made.
1056: */
1057: void
1.95 lukem 1058: setupsockbufsize(int sock)
1.27 thorpej 1059: {
1060:
1.121 lukem 1061: if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
1.122 lukem 1062: (void *)&sndbuf_size, sizeof(sndbuf_size)) == -1)
1.138 lukem 1063: warn("Unable to set sndbuf size %d", sndbuf_size);
1.58 lukem 1064:
1.121 lukem 1065: if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
1066: (void *)&rcvbuf_size, sizeof(rcvbuf_size)) == -1)
1.138 lukem 1067: warn("Unable to set rcvbuf size %d", rcvbuf_size);
1.43 lukem 1068: }
1069:
1.78 lukem 1070: /*
1071: * Copy characters from src into dst, \ quoting characters that require it
1072: */
1.43 lukem 1073: void
1.95 lukem 1074: ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen)
1.43 lukem 1075: {
1076: int di, si;
1077:
1078: for (di = si = 0;
1079: src[si] != '\0' && di < dstlen && si < srclen;
1080: di++, si++) {
1081: switch (src[si]) {
1082: case '\\':
1083: case ' ':
1084: case '\t':
1085: case '\r':
1.44 lukem 1086: case '\n':
1.43 lukem 1087: case '"':
1088: dst[di++] = '\\';
1089: if (di >= dstlen)
1090: break;
1091: /* FALLTHROUGH */
1092: default:
1093: dst[di] = src[si];
1094: }
1095: }
1096: dst[di] = '\0';
1.82 lukem 1097: }
1098:
1099: /*
1100: * Copy src into buf (which is len bytes long), expanding % sequences.
1101: */
1102: void
1.95 lukem 1103: formatbuf(char *buf, size_t len, const char *src)
1.82 lukem 1104: {
1.129 lukem 1105: const char *p, *p2, *q;
1.82 lukem 1106: int i, op, updirs, pdirs;
1107:
1108: #define ADDBUF(x) do { \
1109: if (i >= len - 1) \
1110: goto endbuf; \
1111: buf[i++] = (x); \
1112: } while (0)
1113:
1114: p = src;
1115: for (i = 0; *p; p++) {
1116: if (*p != '%') {
1117: ADDBUF(*p);
1118: continue;
1119: }
1120: p++;
1121:
1122: switch (op = *p) {
1123:
1124: case '/':
1125: case '.':
1126: case 'c':
1.117 lukem 1127: p2 = connected ? remotecwd : "";
1.82 lukem 1128: updirs = pdirs = 0;
1129:
1130: /* option to determine fixed # of dirs from path */
1131: if (op == '.' || op == 'c') {
1132: int skip;
1133:
1134: q = p2;
1135: while (*p2) /* calc # of /'s */
1136: if (*p2++ == '/')
1137: updirs++;
1138: if (p[1] == '0') { /* print <x> or ... */
1139: pdirs = 1;
1140: p++;
1141: }
1142: if (p[1] >= '1' && p[1] <= '9') {
1143: /* calc # to skip */
1144: skip = p[1] - '0';
1145: p++;
1146: } else
1147: skip = 1;
1148:
1149: updirs -= skip;
1150: while (skip-- > 0) {
1151: while ((p2 > q) && (*p2 != '/'))
1152: p2--; /* back up */
1153: if (skip && p2 > q)
1154: p2--;
1155: }
1156: if (*p2 == '/' && p2 != q)
1157: p2++;
1158: }
1159:
1160: if (updirs > 0 && pdirs) {
1161: if (i >= len - 5)
1162: break;
1163: if (op == '.') {
1164: ADDBUF('.');
1165: ADDBUF('.');
1166: ADDBUF('.');
1167: } else {
1168: ADDBUF('/');
1169: ADDBUF('<');
1170: if (updirs > 9) {
1171: ADDBUF('9');
1172: ADDBUF('+');
1173: } else
1174: ADDBUF('0' + updirs);
1175: ADDBUF('>');
1176: }
1177: }
1178: for (; *p2; p2++)
1179: ADDBUF(*p2);
1180: break;
1181:
1182: case 'M':
1183: case 'm':
1.128 lukem 1184: for (p2 = connected && hostname ? hostname : "-";
1.112 lukem 1185: *p2 ; p2++) {
1.82 lukem 1186: if (op == 'm' && *p2 == '.')
1187: break;
1188: ADDBUF(*p2);
1189: }
1190: break;
1191:
1192: case 'n':
1193: for (p2 = connected ? username : "-"; *p2 ; p2++)
1194: ADDBUF(*p2);
1195: break;
1196:
1197: case '%':
1198: ADDBUF('%');
1199: break;
1200:
1201: default: /* display unknown codes literally */
1202: ADDBUF('%');
1203: ADDBUF(op);
1204: break;
1205:
1206: }
1207: }
1208: endbuf:
1209: buf[i] = '\0';
1.100 lukem 1210: }
1211:
1212: /*
1.55 lukem 1213: * Determine if given string is an IPv6 address or not.
1214: * Return 1 for yes, 0 for no
1215: */
1216: int
1.95 lukem 1217: isipv6addr(const char *addr)
1.55 lukem 1218: {
1219: int rv = 0;
1220: #ifdef INET6
1.93 itojun 1221: struct addrinfo hints, *res;
1.55 lukem 1222:
1.93 itojun 1223: memset(&hints, 0, sizeof(hints));
1224: hints.ai_family = PF_INET6;
1225: hints.ai_socktype = SOCK_DGRAM; /*dummy*/
1226: hints.ai_flags = AI_NUMERICHOST;
1227: if (getaddrinfo(addr, "0", &hints, &res) != 0)
1228: rv = 0;
1229: else {
1230: rv = 1;
1231: freeaddrinfo(res);
1232: }
1.130 christos 1233: DPRINTF("isipv6addr: got %d for %s\n", rv, addr);
1.55 lukem 1234: #endif
1.86 itojun 1235: return (rv == 1) ? 1 : 0;
1.55 lukem 1236: }
1237:
1.129 lukem 1238: /*
1239: * Read a line from the FILE stream into buf/buflen using fgets(), so up
1240: * to buflen-1 chars will be read and the result will be NUL terminated.
1241: * If the line has a trailing newline it will be removed.
1242: * If the line is too long, excess characters will be read until
1243: * newline/EOF/error.
1244: * If EOF/error occurs or a too-long line is encountered and errormsg
1245: * isn't NULL, it will be changed to a description of the problem.
1246: * (The EOF message has a leading \n for cosmetic purposes).
1247: * Returns:
1248: * >=0 length of line (excluding trailing newline) if all ok
1249: * -1 error occurred
1250: * -2 EOF encountered
1251: * -3 line was too long
1252: */
1253: int
1254: getline(FILE *stream, char *buf, size_t buflen, const char **errormsg)
1255: {
1256: int rv, ch;
1257: size_t len;
1258:
1259: if (fgets(buf, buflen, stream) == NULL) {
1260: if (feof(stream)) { /* EOF */
1261: rv = -2;
1262: if (errormsg)
1263: *errormsg = "\nEOF received";
1264: } else { /* error */
1265: rv = -1;
1266: if (errormsg)
1267: *errormsg = "Error encountered";
1268: }
1269: clearerr(stream);
1270: return rv;
1271: }
1272: len = strlen(buf);
1273: if (buf[len-1] == '\n') { /* clear any trailing newline */
1274: buf[--len] = '\0';
1275: } else if (len == buflen-1) { /* line too long */
1276: while ((ch = getchar()) != '\n' && ch != EOF)
1277: continue;
1278: if (errormsg)
1279: *errormsg = "Input line is too long";
1280: clearerr(stream);
1281: return -3;
1282: }
1283: if (errormsg)
1284: *errormsg = NULL;
1285: return len;
1286: }
1287:
1.27 thorpej 1288: /*
1.138 lukem 1289: * Internal version of connect(2); sets socket buffer sizes,
1290: * binds to a specific local address (if set), and
1.118 lukem 1291: * supports a connection timeout using a non-blocking connect(2) with
1292: * a poll(2).
1293: * Socket fcntl flags are temporarily updated to include O_NONBLOCK;
1294: * these will not be reverted on connection failure.
1.138 lukem 1295: * Returns 0 on success, or -1 upon failure (with an appropriate
1296: * error message displayed.)
1.27 thorpej 1297: */
1298: int
1.132 christos 1299: ftp_connect(int sock, const struct sockaddr *name, socklen_t namelen)
1.27 thorpej 1300: {
1.118 lukem 1301: int flags, rv, timeout, error;
1.121 lukem 1302: socklen_t slen;
1.118 lukem 1303: struct timeval endtime, now, td;
1304: struct pollfd pfd[1];
1.138 lukem 1305: char hname[NI_MAXHOST];
1.145 ! lukem 1306: char sname[NI_MAXSERV];
1.27 thorpej 1307:
1308: setupsockbufsize(sock);
1.138 lukem 1309: if (getnameinfo(name, namelen,
1.145 ! lukem 1310: hname, sizeof(hname), sname, sizeof(sname),
! 1311: NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
1.138 lukem 1312: strlcpy(hname, "?", sizeof(hname));
1.145 ! lukem 1313: strlcpy(sname, "?", sizeof(sname));
! 1314: }
1.115 lukem 1315:
1.138 lukem 1316: if (bindai != NULL) { /* bind to specific addr */
1317: struct addrinfo *ai;
1318:
1319: for (ai = bindai; ai != NULL; ai = ai->ai_next) {
1320: if (ai->ai_family == name->sa_family)
1321: break;
1322: }
1323: if (ai == NULL)
1324: ai = bindai;
1325: if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
1326: char bname[NI_MAXHOST];
1327: int saveerr;
1328:
1329: saveerr = errno;
1330: if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
1331: bname, sizeof(bname), NULL, 0, NI_NUMERICHOST) != 0)
1332: strlcpy(bname, "?", sizeof(bname));
1333: errno = saveerr;
1334: warn("Can't bind to `%s'", bname);
1335: return -1;
1336: }
1337: }
1338:
1339: /* save current socket flags */
1340: if ((flags = fcntl(sock, F_GETFL, 0)) == -1) {
1.145 ! lukem 1341: warn("Can't %s socket flags for connect to `%s:%s'",
! 1342: "save", hname, sname);
1.138 lukem 1343: return -1;
1344: }
1345: /* set non-blocking connect */
1346: if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
1.145 ! lukem 1347: warn("Can't set socket non-blocking for connect to `%s:%s'",
! 1348: hname, sname);
1.138 lukem 1349: return -1;
1350: }
1.118 lukem 1351:
1352: /* NOTE: we now must restore socket flags on successful exit */
1353:
1354: pfd[0].fd = sock;
1355: pfd[0].events = POLLIN|POLLOUT;
1356:
1357: if (quit_time > 0) { /* want a non default timeout */
1358: (void)gettimeofday(&endtime, NULL);
1359: endtime.tv_sec += quit_time; /* determine end time */
1360: }
1361:
1362: rv = connect(sock, name, namelen); /* inititate the connection */
1363: if (rv == -1) { /* connection error */
1.138 lukem 1364: if (errno != EINPROGRESS) { /* error isn't "please wait" */
1365: connecterror:
1.145 ! lukem 1366: warn("Can't connect to `%s:%s'", hname, sname);
1.118 lukem 1367: return -1;
1.138 lukem 1368: }
1.118 lukem 1369:
1370: /* connect EINPROGRESS; wait */
1.115 lukem 1371: do {
1.118 lukem 1372: if (quit_time > 0) { /* determine timeout */
1373: (void)gettimeofday(&now, NULL);
1374: timersub(&endtime, &now, &td);
1375: timeout = td.tv_sec * 1000 + td.tv_usec/1000;
1376: if (timeout < 0)
1377: timeout = 0;
1378: } else {
1379: timeout = INFTIM;
1380: }
1381: pfd[0].revents = 0;
1.132 christos 1382: rv = ftp_poll(pfd, 1, timeout);
1.118 lukem 1383: /* loop until poll ! EINTR */
1.115 lukem 1384: } while (rv == -1 && errno == EINTR);
1.118 lukem 1385:
1386: if (rv == 0) { /* poll (connect) timed out */
1387: errno = ETIMEDOUT;
1.138 lukem 1388: goto connecterror;
1.118 lukem 1389: }
1390:
1391: if (rv == -1) { /* poll error */
1.138 lukem 1392: goto connecterror;
1.118 lukem 1393: } else if (pfd[0].revents & (POLLIN|POLLOUT)) {
1.122 lukem 1394: slen = sizeof(error); /* OK, or pending error */
1.118 lukem 1395: if (getsockopt(sock, SOL_SOCKET, SO_ERROR,
1.138 lukem 1396: &error, &slen) == -1) {
1397: /* Solaris pending error */
1398: goto connecterror;
1399: } else if (error != 0) {
1.118 lukem 1400: errno = error; /* BSD pending error */
1.138 lukem 1401: goto connecterror;
1.118 lukem 1402: }
1403: } else {
1404: errno = EBADF; /* this shouldn't happen ... */
1.138 lukem 1405: goto connecterror;
1.118 lukem 1406: }
1.115 lukem 1407: }
1.118 lukem 1408:
1.138 lukem 1409: if (fcntl(sock, F_SETFL, flags) == -1) {
1410: /* restore socket flags */
1.145 ! lukem 1411: warn("Can't %s socket flags for connect to `%s:%s'",
! 1412: "restore", hname, sname);
1.118 lukem 1413: return -1;
1.138 lukem 1414: }
1.118 lukem 1415: return 0;
1.27 thorpej 1416: }
1417:
1418: /*
1419: * Internal version of listen(2); sets socket buffer sizes first.
1420: */
1421: int
1.132 christos 1422: ftp_listen(int sock, int backlog)
1.27 thorpej 1423: {
1424:
1425: setupsockbufsize(sock);
1426: return (listen(sock, backlog));
1.31 lukem 1427: }
1428:
1.78 lukem 1429: /*
1.118 lukem 1430: * Internal version of poll(2), to allow reimplementation by select(2)
1.120 lukem 1431: * on platforms without the former.
1.118 lukem 1432: */
1433: int
1.132 christos 1434: ftp_poll(struct pollfd *fds, int nfds, int timeout)
1.118 lukem 1435: {
1436: return poll(fds, nfds, timeout);
1437: }
1438:
1439: /*
1.78 lukem 1440: * malloc() with inbuilt error checking
1441: */
1.31 lukem 1442: void *
1.132 christos 1443: ftp_malloc(size_t size)
1.31 lukem 1444: {
1445: void *p;
1446:
1447: p = malloc(size);
1448: if (p == NULL)
1449: err(1, "Unable to allocate %ld bytes of memory", (long)size);
1450: return (p);
1.85 lukem 1451: }
1452:
1453: /*
1454: * sl_init() with inbuilt error checking
1455: */
1456: StringList *
1.132 christos 1457: ftp_sl_init(void)
1.85 lukem 1458: {
1459: StringList *p;
1460:
1461: p = sl_init();
1462: if (p == NULL)
1463: err(1, "Unable to allocate memory for stringlist");
1464: return (p);
1465: }
1466:
1467: /*
1468: * sl_add() with inbuilt error checking
1469: */
1470: void
1.132 christos 1471: ftp_sl_add(StringList *sl, char *i)
1.85 lukem 1472: {
1473:
1474: if (sl_add(sl, i) == -1)
1475: err(1, "Unable to add `%s' to stringlist", i);
1.31 lukem 1476: }
1477:
1.78 lukem 1478: /*
1479: * strdup() with inbuilt error checking
1480: */
1.31 lukem 1481: char *
1.132 christos 1482: ftp_strdup(const char *str)
1.31 lukem 1483: {
1484: char *s;
1485:
1486: if (str == NULL)
1.138 lukem 1487: errx(1, "ftp_strdup: called with NULL argument");
1.31 lukem 1488: s = strdup(str);
1489: if (s == NULL)
1490: err(1, "Unable to allocate memory for string copy");
1491: return (s);
1.27 thorpej 1492: }
CVSweb <webmaster@jp.NetBSD.org>