1 /* $FreeBSD: src/usr.bin/ftp/util.c,v 1.12.2.4 2002/08/27 09:55:08 yar Exp $	*/
2 /*	$NetBSD: util.c,v 1.16.2.1 1997/11/18 01:02:33 mellon Exp $	*/
3 
4 /*
5  * Copyright (c) 2002, 2003, 2004 Nick Leuta
6  * Copyright (c) 1985, 1989, 1993, 1994
7  *	The Regents of the University of California.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by the University of
20  *	California, Berkeley and its contributors.
21  * 4. Neither the name of the University nor the names of its contributors
22  *    may be used to endorse or promote products derived from this software
23  *    without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 
38 #include <sys/cdefs.h>
39 
40 #ifndef lint
41 static const char __RCSID[] = "$FreeBSD: src/usr.bin/ftp/util.c,v 1.12.2.4 2002/08/27 09:55:08 yar Exp $";
42 static const char __RCSID_SOURCE[] = "$NetBSD: util.c,v 1.16.2.1 1997/11/18 01:02:33 mellon Exp $";
43 #endif /* not lint */
44 
45 /*
46  * FTP User Program -- Misc support routines
47  */
48 #include <sys/ioctl.h>
49 #include <sys/time.h>
50 #include <arpa/ftp.h>
51 
52 #include <ctype.h>
53 #include <err.h>
54 #include <fcntl.h>
55 #include <glob.h>
56 #include <limits.h>
57 #include <pwd.h>
58 #include <stdint.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <time.h>
63 #include <unistd.h>
64 #ifdef INET6
65 #include <netdb.h>
66 #endif
67 
68 #ifdef LINUX
69 #include <signal.h>
70 #endif /* LINUX */
71 
72 #include <port_base.h>
73 
74 #include "ftp_var.h"
75 #include "pathnames.h"
76 
77 #ifndef	SECSPERHOUR
78 #define	SECSPERHOUR	(60*60)
79 #endif
80 
81 /*
82  * Connect to peer server and
83  * auto-login, if possible.
84  */
85 void
setpeer(argc,argv)86 setpeer(argc, argv)
87 	int argc;
88 	char *argv[];
89 {
90 	char *host;
91 	char *port;
92 
93 	if (connected) {
94 		printf("Already connected to %s, use close first.\n",
95 		    hostname);
96 		code = -1;
97 		return;
98 	}
99 #ifdef USE_SSL
100 	ssl_reinit();
101 #endif /*USE_SSL*/
102 	if (argc < 2)
103 		(void)another(&argc, &argv, "to");
104 	if (argc < 2 || argc > 3) {
105 		printf("usage: %s host-name [port]\n", argv[0]);
106 		code = -1;
107 		return;
108 	}
109 	if (gatemode)
110 		port = gateport;
111 	else
112 		port = ftpport;
113 	if (argc > 2)
114 		port = strdup(argv[2]);
115 
116 	if (gatemode) {
117 		if (gateserver == NULL || *gateserver == '\0')
118 			errx(1, "gateserver not defined (shouldn't happen)");
119 		host = hookup(gateserver, port);
120 	} else
121 		host = hookup(argv[1], port);
122 
123 	if (host) {
124 		int overbose;
125 
126 		if (gatemode) {
127 			if (command("PASSERVE %s", argv[1]) != COMPLETE)
128 				return;
129 			if (verbose)
130 				printf("Connected via pass-through server %s\n",
131 				    gateserver);
132 		}
133 
134 		connected = 1;
135 		try_epsv4 = epsv4;
136 		try_epsv6 = epsv6;
137 		/*
138 		 * Set up defaults for FTP.
139 		 */
140 		(void)strcpy(typename, "ascii"), type = TYPE_A;
141 		curtype = TYPE_A;
142 		(void)strcpy(formname, "non-print"), form = FORM_N;
143 		(void)strcpy(modename, "stream"), mode = MODE_S;
144 		(void)strcpy(structname, "file"), stru = STRU_F;
145 		(void)strcpy(bytename, "8"), bytesize = 8;
146 		if (autologin)
147 			(void)login(argv[1], NULL, NULL);
148 
149 		overbose = verbose;
150 		if (debug == 0)
151 			verbose = -1;
152 		if (command("SYST") == COMPLETE && overbose) {
153 			char *cp, c;
154 			c = 0;
155 			cp = strchr(reply_string+4, ' ');
156 			if (cp == NULL)
157 				cp = strchr(reply_string+4, '\r');
158 			if (cp) {
159 				if (cp[-1] == '.')
160 					cp--;
161 				c = *cp;
162 				*cp = '\0';
163 			}
164 
165 			printf("Remote system type is %s.\n", reply_string + 4);
166 			if (cp)
167 				*cp = c;
168 		}
169 		if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
170 			if (proxy)
171 				unix_proxy = 1;
172 			else
173 				unix_server = 1;
174 			/*
175 			 * Set type to 0 (not specified by user),
176 			 * meaning binary by default, but don't bother
177 			 * telling server.  We can use binary
178 			 * for text files unless changed by the user.
179 			 */
180 			type = 0;
181 			(void)strcpy(typename, "binary");
182 			if (overbose)
183 			    printf("Using %s mode to transfer files.\n",
184 				typename);
185 		} else {
186 			if (proxy)
187 				unix_proxy = 0;
188 			else
189 				unix_server = 0;
190 			if (overbose &&
191 			    !strncmp(reply_string, "215 TOPS20", 10))
192 				puts(
193 "Remember to set tenex mode when transferring binary files from this machine.");
194 		}
195 		verbose = overbose;
196 	}
197 }
198 
199 
200 /*
201  * login to remote host, using given username & password if supplied
202  */
203 int
login(host,user,pass)204 login(host, user, pass)
205 	const char *host;
206 	char *user, *pass;
207 {
208 	char tmp[80];
209 	char *acct;
210 	char anonpass[MAXLOGNAME + 1 + MAXHOSTNAMELEN];	/* "user@hostname" */
211 	char hostname[MAXHOSTNAMELEN];
212 	struct passwd *pw;
213 	int n, aflag = 0;
214 
215 	acct = NULL;
216 	if (user == NULL) {
217 		if (ruserpass(host, &user, &pass, &acct) < 0) {
218 			code = -1;
219 			return (0);
220 		}
221 	}
222 
223 	/*
224 	 * Set up arguments for an anonymous FTP session, if necessary.
225 	 */
226 	if ((user == NULL || pass == NULL) && anonftp) {
227 		memset(anonpass, 0, sizeof(anonpass));
228 		memset(hostname, 0, sizeof(hostname));
229 
230 		/*
231 		 * Set up anonymous login password.
232 		 */
233 		if ((user = getlogin()) == NULL) {
234 			if ((pw = getpwuid(getuid())) == NULL)
235 				user = "anonymous";
236 			else
237 				user = pw->pw_name;
238 		}
239 		gethostname(hostname, MAXHOSTNAMELEN);
240 #ifndef DONT_CHEAT_ANONPASS
241 		/*
242 		 * Every anonymous FTP server I've encountered
243 		 * will accept the string "username@", and will
244 		 * append the hostname itself.  We do this by default
245 		 * since many servers are picky about not having
246 		 * a FQDN in the anonymous password. - thorpej@netbsd.org
247 		 */
248 		snprintf(anonpass, sizeof(anonpass) - 1, "%s@",
249 		    user);
250 #else
251 		snprintf(anonpass, sizeof(anonpass) - 1, "%s@%s",
252 		    user, hp->h_name);
253 #endif
254 		pass = anonpass;
255 		user = "anonymous";	/* as per RFC 1635 */
256 	}
257 
258 	while (user == NULL) {
259 		char *myname = getlogin();
260 
261 		if (myname == NULL && (pw = getpwuid(getuid())) != NULL)
262 			myname = pw->pw_name;
263 		if (myname)
264 			printf("Name (%s:%s): ", host, myname);
265 		else
266 			printf("Name (%s): ", host);
267 		if (fgets(tmp, sizeof(tmp) - 1, stdin) == NULL)
268 			return (0);
269 		tmp[strlen(tmp) - 1] = '\0';
270 		if (*tmp == '\0')
271 			user = myname;
272 		else
273 			user = tmp;
274 	}
275 #ifdef USE_SSL
276 	if (ssl_secure_flags & SSL_ENABLED) {
277 		n = auth();
278 		if (/* TLS/SSL handshake failed */
279 		    (n < 0)
280 	            /* TLS/SSL session negotiation failed */
281 		    || (!(ssl_secure_flags & SSL_USE_NONSECURE) && !n))
282 			return 0;
283 	}
284 #endif /* USE_SSL */
285 	n = command("USER %s", user);
286 	if (n == CONTINUE) {
287 		if (pass == NULL)
288 			pass = getpass("Password:");
289 		n = command("PASS %s", pass);
290 	}
291 	if (n == CONTINUE) {
292 		aflag++;
293 		if (acct == NULL)
294 			acct = getpass("Account:");
295 		n = command("ACCT %s", acct);
296 	}
297 	if ((n != COMPLETE) ||
298 	    (!aflag && acct != NULL && command("ACCT %s", acct) != COMPLETE)) {
299 		warnx("Login failed.");
300 		return (0);
301 	}
302 #ifdef USE_SSL
303 	ssl_try_setprot();
304 #endif /* USE_SSL */
305 	if (proxy)
306 		return (1);
307 	connected = -1;
308 	for (n = 0; n < macnum; ++n) {
309 		if (!strcmp("init", macros[n].mac_name)) {
310 			(void)strcpy(line, "$init");
311 			makeargv();
312 			domacro(margc, margv);
313 			break;
314 		}
315 	}
316 	return (1);
317 }
318 
319 /*
320  * `another' gets another argument, and stores the new argc and argv.
321  * It reverts to the top level (via main.c's intr()) on EOF/error.
322  *
323  * Returns false if no new arguments have been added.
324  */
325 int
another(pargc,pargv,prompt)326 another(pargc, pargv, prompt)
327 	int *pargc;
328 	char ***pargv;
329 	const char *prompt;
330 {
331 	int len = strlen(line), ret;
332 
333 	if (len >= sizeof(line) - 3) {
334 		puts("sorry, arguments too long.");
335 		intr();
336 	}
337 	printf("(%s) ", prompt);
338 	line[len++] = ' ';
339 	if (fgets(&line[len], sizeof(line) - len, stdin) == NULL)
340 		intr();
341 	len += strlen(&line[len]);
342 	if (len > 0 && line[len - 1] == '\n')
343 		line[len - 1] = '\0';
344 	makeargv();
345 	ret = margc > *pargc;
346 	*pargc = margc;
347 	*pargv = margv;
348 	return (ret);
349 }
350 
351 /*
352  * glob files given in argv[] from the remote server.
353  * if errbuf isn't NULL, store error messages there instead
354  * of writing to the screen.
355  */
356 char *
remglob(argv,doswitch,errbuf)357 remglob(argv, doswitch, errbuf)
358         char *argv[];
359         int doswitch;
360 	char **errbuf;
361 {
362         char temp[MAXPATHLEN];
363         static char buf[MAXPATHLEN];
364         static FILE *ftemp = NULL;
365         static char **args;
366         int oldverbose, oldhash, fd;
367         char *cp, *mode;
368 
369         if (!mflag) {
370                 if (!doglob)
371                         args = NULL;
372                 else {
373                         if (ftemp) {
374                                 (void)fclose(ftemp);
375                                 ftemp = NULL;
376                         }
377                 }
378                 return (NULL);
379         }
380         if (!doglob) {
381                 if (args == NULL)
382                         args = argv;
383                 if ((cp = *++args) == NULL)
384                         args = NULL;
385                 return (cp);
386         }
387         if (ftemp == NULL) {
388                 (void)snprintf(temp, sizeof(temp), "%s/%s", tmpdir, TMPFILE);
389                 if ((fd = mkstemp(temp)) < 0) {
390                         warn("unable to create temporary file %s", temp);
391                         return (NULL);
392                 }
393                 close(fd);
394                 oldverbose = verbose;
395 		verbose = (errbuf != NULL) ? -1 : 0;
396                 oldhash = hash;
397                 hash = 0;
398                 if (doswitch)
399                         pswitch(!proxy);
400                 for (mode = "w"; *++argv != NULL; mode = "a")
401                         recvrequest("NLST", temp, *argv, mode, 0, 0);
402 		if ((code / 100) != COMPLETE) {
403 			if (errbuf != NULL)
404 				*errbuf = reply_string;
405 		}
406                 if (doswitch)
407                         pswitch(!proxy);
408                 verbose = oldverbose;
409 		hash = oldhash;
410                 ftemp = fopen(temp, "r");
411                 (void)unlink(temp);
412                 if (ftemp == NULL) {
413 			if (errbuf == NULL)
414 				puts("can't find list of remote files, oops.");
415 			else
416 				*errbuf =
417 				    "can't find list of remote files, oops.";
418                         return (NULL);
419                 }
420         }
421         if (fgets(buf, sizeof(buf), ftemp) == NULL) {
422                 (void)fclose(ftemp);
423 		ftemp = NULL;
424                 return (NULL);
425         }
426         if ((cp = strchr(buf, '\n')) != NULL)
427                 *cp = '\0';
428         return (buf);
429 }
430 
431 int
confirm(cmd,file)432 confirm(cmd, file)
433 	const char *cmd, *file;
434 {
435 	char line[BUFSIZ];
436 
437 	if (!interactive || confirmrest)
438 		return (1);
439 	printf("%s %s? ", cmd, file);
440 	(void)fflush(stdout);
441 	if (fgets(line, sizeof(line), stdin) == NULL)
442 		return (0);
443 	switch (tolower((unsigned char)*line)) {
444 		case 'n':
445 			return (0);
446 		case 'p':
447 			interactive = 0;
448 			puts("Interactive mode: off.");
449 			break;
450 		case 'a':
451 			confirmrest = 1;
452 			printf("Prompting off for duration of %s.\n", cmd);
453 			break;
454 	}
455 	return (1);
456 }
457 
458 /*
459  * Glob a local file name specification with
460  * the expectation of a single return value.
461  * Can't control multiple values being expanded
462  * from the expression, we return only the first.
463  */
464 int
globulize(cpp)465 globulize(cpp)
466 	char **cpp;
467 {
468 	glob_t gl;
469 	int flags;
470 
471 	if (!doglob)
472 		return (1);
473 
474 
475 #ifdef LINUX
476 	flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
477 #else /* BSD source */
478 	flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE;
479 #endif /* BSD source */
480 
481 	memset(&gl, 0, sizeof(gl));
482 	if (glob(*cpp, flags, NULL, &gl) ||
483 	    gl.gl_pathc == 0) {
484 		warnx("%s: not found", *cpp);
485 		globfree(&gl);
486 		return (0);
487 	}
488 		/* XXX: caller should check if *cpp changed, and
489 		 *	free(*cpp) if that is the case
490 		 */
491 	*cpp = strdup(gl.gl_pathv[0]);
492 	globfree(&gl);
493 	return (1);
494 }
495 
496 /*
497  * determine size of remote file
498  */
499 off_t
remotesize(file,noisy)500 remotesize(file, noisy)
501 	const char *file;
502 	int noisy;
503 {
504 	int overbose;
505 	off_t size;
506 
507 	overbose = verbose;
508 	size = -1;
509 	if (debug == 0)
510 		verbose = -1;
511 	if (command("SIZE %s", file) == COMPLETE) {
512 		char *cp, *ep;
513 
514 		cp = strchr(reply_string, ' ');
515 		if (cp != NULL) {
516 			cp++;
517 			size = (off_t)strtoq(cp, &ep, 10);
518 			if (*ep != '\0' && !isspace((unsigned char)*ep))
519 				size = -1;
520 		}
521 	} else if (noisy && debug == 0)
522 		puts(reply_string);
523 	verbose = overbose;
524 	return (size);
525 }
526 
527 /*
528  * determine last modification time (in GMT) of remote file
529  */
530 time_t
remotemodtime(file,noisy)531 remotemodtime(file, noisy)
532 	const char *file;
533 	int noisy;
534 {
535 	struct tm timebuf;
536 	time_t rtime;
537 	int len, month, ocode, overbose, y2kbug, year;
538 	char *fmt;
539 	char mtbuf[17];
540 
541 	overbose = verbose;
542 	ocode = code;
543 	rtime = -1;
544 	if (debug == 0)
545 		verbose = -1;
546 	if (command("MDTM %s", file) == COMPLETE) {
547 		memset(&timebuf, 0, sizeof(timebuf));
548 		/*
549 		 * Parse the time string, which is expected to be 14
550 		 * characters long.  Some broken servers send tm_year
551 		 * formatted with "19%02d", which produces an incorrect
552 		 * (but parsable) 15 characters for years >= 2000.
553 		 * Scan for invalid trailing junk by accepting up to 16
554 		 * characters.
555 		 */
556 		if (sscanf(reply_string, "%*s %16s", mtbuf) == 1) {
557 			fmt = NULL;
558 			len = strlen(mtbuf);
559 			y2kbug = 0;
560 			if (len == 15 && strncmp(mtbuf, "19", 2) == 0) {
561 				fmt = "19%03d%02d%02d%02d%02d%02d";
562 				y2kbug = 1;
563 			} else if (len == 14)
564 				fmt = "%04d%02d%02d%02d%02d%02d";
565 			if (fmt != NULL) {
566 				if (sscanf(mtbuf, fmt, &year, &month,
567 				    &timebuf.tm_mday, &timebuf.tm_hour,
568 				    &timebuf.tm_min, &timebuf.tm_sec) == 6) {
569 					timebuf.tm_isdst = -1;
570 					timebuf.tm_mon = month - 1;
571 					if (y2kbug)
572 						timebuf.tm_year = year;
573 					else
574 						timebuf.tm_year = year - 1900;
575 					rtime = mktime(&timebuf);
576 				}
577 			}
578 		}
579 		if (rtime == -1) {
580 			if (noisy || debug != 0)
581 				printf("Can't convert %s to a time.\n", mtbuf);
582 		} else
583 			rtime += timebuf.tm_gmtoff;	/* conv. local -> GMT */
584 	} else if (noisy && debug == 0)
585 		puts(reply_string);
586 	verbose = overbose;
587 	if (rtime == -1)
588 		code = ocode;
589 	return (rtime);
590 }
591 
592 void updateprogressmeter __P((int));
593 
594 void
updateprogressmeter(dummy)595 updateprogressmeter(dummy)
596 	int dummy;
597 {
598 	static pid_t pgrp = -1;
599 	int ctty_pgrp;
600 
601 	if (pgrp == -1)
602 		pgrp = getpgrp();
603 
604 	/*
605 	 * print progress bar only if we are foreground process.
606 	 */
607 	if (ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 &&
608 	    ctty_pgrp == (int)pgrp)
609 		progressmeter(0);
610 }
611 
612 /*
613  * Display a transfer progress bar if progress is non-zero.
614  * SIGALRM is hijacked for use by this function.
615  * - Before the transfer, set filesize to size of file (or -1 if unknown),
616  *   and call with flag = -1. This starts the once per second timer,
617  *   and a call to updateprogressmeter() upon SIGALRM.
618  * - During the transfer, updateprogressmeter will call progressmeter
619  *   with flag = 0
620  * - After the transfer, call with flag = 1
621  */
622 static struct timeval start;
623 
624 void
progressmeter(flag)625 progressmeter(flag)
626 	int flag;
627 {
628 	/*
629 	 * List of order of magnitude prefixes.
630 	 * The last is `P', as 2^64 = 16384 Petabytes
631 	 */
632 	static const char prefixes[] = " KMGTP";
633 
634 	static struct timeval lastupdate;
635 	static off_t lastsize;
636 	struct timeval now, td, wait;
637 	off_t cursize, abbrevsize;
638 	double elapsed;
639 	int ratio, barlength, i, len;
640 	off_t remaining;
641 	char buf[256];
642 
643 	len = 0;
644 
645 	if (flag == -1) {
646 		(void)gettimeofday(&start, NULL);
647 		lastupdate = start;
648 		lastsize = restart_point;
649 	}
650 	(void)gettimeofday(&now, NULL);
651 	if (!progress || filesize <= 0)
652 		return;
653 	cursize = bytes + restart_point;
654 
655 #ifdef LINUX /* Linux port */
656 	ratio = (intmax_t)cursize * 100 / filesize;
657 #else /* BSD source */
658 	ratio = cursize * 100 / filesize;
659 #endif /* BSD source */
660 	ratio = MAX(ratio, 0);
661 	ratio = MIN(ratio, 100);
662 	len += snprintf(buf + len, sizeof(buf) - len, "\r%3d%% ", ratio);
663 
664 	barlength = ttywidth - 30;
665 	if (barlength > 0) {
666 		i = barlength * ratio / 100;
667 		len += snprintf(buf + len, sizeof(buf) - len,
668 		    "|%.*s%*s|", i,
669 "*****************************************************************************"
670 "*****************************************************************************",
671 		    barlength - i, "");
672 	}
673 
674 	i = 0;
675 	abbrevsize = cursize;
676 	while (abbrevsize >= 100000 && i < sizeof(prefixes)) {
677 		i++;
678 		abbrevsize >>= 10;
679 	}
680 	len += snprintf(buf + len, sizeof(buf) - len,
681 	    " %5jd %c%c ", (intmax_t)abbrevsize, prefixes[i],
682 	    prefixes[i] == ' ' ? ' ' : 'B');
683 
684 	timersub(&now, &lastupdate, &wait);
685 	if (cursize > lastsize) {
686 		lastupdate = now;
687 		lastsize = cursize;
688 #if 0
689 /* the usage of total time will be more correct for calculating of average
690  * tranfer speed, especially on slow links with non-zero packet lost rate */
691 		if (wait.tv_sec >= STALLTIME) {	/* fudge out stalled time */
692 			start.tv_sec += wait.tv_sec;
693 			start.tv_usec += wait.tv_usec;
694 		}
695 #endif
696 		wait.tv_sec = 0;
697 	}
698 
699 	timersub(&now, &start, &td);
700 	elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
701 
702 	if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
703 		len += snprintf(buf + len, sizeof(buf) - len,
704 		    "   --:-- ETA");
705 	} else if (wait.tv_sec >= STALLTIME) {
706 		len += snprintf(buf + len, sizeof(buf) - len,
707 		    " - stalled -");
708 	} else {
709 		remaining =
710 		    ((filesize - restart_point) / (bytes / elapsed) - elapsed);
711 		if (remaining >= 100 * SECSPERHOUR)
712 			len += snprintf(buf + len, sizeof(buf) - len,
713 			    "   --:-- ETA");
714 		else {
715 			i = remaining / SECSPERHOUR;
716 			if (i)
717 				len += snprintf(buf + len, sizeof(buf) - len,
718 				    "%2d:", i);
719 			else
720 				len += snprintf(buf + len, sizeof(buf) - len,
721 				    "   ");
722 			i = remaining % SECSPERHOUR;
723 			len += snprintf(buf + len, sizeof(buf) - len,
724 			    "%02d:%02d ETA", i / 60, i % 60);
725 		}
726 	}
727 	(void)write(STDOUT_FILENO, buf, len);
728 
729 	if (flag == -1) {
730 		(void)signal(SIGALRM, updateprogressmeter);
731 		alarmtimer(1);		/* set alarm timer for 1 Hz */
732 	} else if (flag == 1) {
733 		alarmtimer(0);
734 		(void)putchar('\n');
735 	}
736 	fflush(stdout);
737 }
738 
739 /*
740  * Display transfer statistics.
741  * Requires start to be initialised by progressmeter(-1),
742  * direction to be defined by xfer routines, and filesize and bytes
743  * to be updated by xfer routines
744  * If siginfo is nonzero, an ETA is displayed, and the output goes to STDERR
745  * instead of STDOUT.
746  */
747 void
ptransfer(siginfo)748 ptransfer(siginfo)
749 	int siginfo;
750 {
751 	struct timeval now, td;
752 	double elapsed;
753 	off_t bs;
754 	int meg, remaining, hh, len;
755 	char buf[100];
756 
757 	if (!verbose && !siginfo)
758 		return;
759 
760 	(void)gettimeofday(&now, NULL);
761 	timersub(&now, &start, &td);
762 	elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
763 	bs = bytes / (elapsed == 0.0 ? 1 : elapsed);
764 	meg = 0;
765 	if (bs > (1024 * 1024))
766 		meg = 1;
767 	len = 0;
768 	len += snprintf(buf + len, sizeof(buf) - len,
769 	    "%jd byte%s %s in %.2f seconds (%.2f %sB/s)\n",
770 	    (intmax_t)bytes, bytes == 1 ? "" : "s", direction, elapsed,
771 	    bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K");
772 	if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0
773 	    && bytes + restart_point <= filesize) {
774 		remaining = (int)((filesize - restart_point) /
775 				  (bytes / elapsed) - elapsed);
776 		hh = remaining / SECSPERHOUR;
777 		remaining %= SECSPERHOUR;
778 		len--;	 		/* decrement len to overwrite \n */
779 		len += snprintf(buf + len, sizeof(buf) - len,
780 		    "  ETA: %02d:%02d:%02d\n", hh, remaining / 60,
781 		    remaining % 60);
782 	}
783 	(void)write(siginfo ? STDERR_FILENO : STDOUT_FILENO, buf, len);
784 }
785 
786 /*
787  * List words in stringlist, vertically arranged
788  */
789 void
list_vertical(sl)790 list_vertical(sl)
791 	StringList *sl;
792 {
793 	int i, j, w;
794 	int columns, width, lines, items;
795 	char *p;
796 
797 	width = items = 0;
798 
799 	for (i = 0 ; i < sl->sl_cur ; i++) {
800 		w = strlen(sl->sl_str[i]);
801 		if (w > width)
802 			width = w;
803 	}
804 	width = (width + 8) &~ 7;
805 
806 	columns = ttywidth / width;
807 	if (columns == 0)
808 		columns = 1;
809 	lines = (sl->sl_cur + columns - 1) / columns;
810 	for (i = 0; i < lines; i++) {
811 		for (j = 0; j < columns; j++) {
812 			p = sl->sl_str[j * lines + i];
813 			if (p)
814 				fputs(p, stdout);
815 			if (j * lines + i + lines >= sl->sl_cur) {
816 				putchar('\n');
817 				break;
818 			}
819 			w = strlen(p);
820 			while (w < width) {
821 				w = (w + 8) &~ 7;
822 				(void)putchar('\t');
823 			}
824 		}
825 	}
826 }
827 
828 /*
829  * Update the global ttywidth value, using TIOCGWINSZ.
830  */
831 void
setttywidth(a)832 setttywidth(a)
833 	int a;
834 {
835 	struct winsize winsize;
836 
837 	if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1)
838 		ttywidth = winsize.ws_col;
839 	else
840 		ttywidth = 80;
841 }
842 
843 /*
844  * Set the SIGALRM interval timer for wait seconds, 0 to disable.
845  */
846 void
alarmtimer(wait)847 alarmtimer(wait)
848 	int wait;
849 {
850 	struct itimerval itv;
851 
852 	itv.it_value.tv_sec = wait;
853 	itv.it_value.tv_usec = 0;
854 	itv.it_interval = itv.it_value;
855 	setitimer(ITIMER_REAL, &itv, NULL);
856 }
857 
858 /*
859  * Setup or cleanup EditLine structures
860  */
861 #ifndef SMALL
862 void
controlediting()863 controlediting()
864 {
865 	if (editing && el == NULL && hist == NULL) {
866 		el = el_init(__progname, stdin, stdout); /* init editline */
867 		hist = history_init();		/* init the builtin history */
868 		history(hist, H_EVENT, 100);	/* remember 100 events */
869 		el_set(el, EL_HIST, history, hist);	/* use history */
870 
871 		el_set(el, EL_EDITOR, "emacs");	/* default editor is emacs */
872 		el_set(el, EL_PROMPT, prompt);	/* set the prompt function */
873 
874 		/* add local file completion, bind to TAB */
875 		el_set(el, EL_ADDFN, "ftp-complete",
876 		    "Context sensitive argument completion",
877 		    complete);
878 		el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
879 
880 		el_source(el, NULL);	/* read ~/.editrc */
881 		el_set(el, EL_SIGNAL, 1);
882 	} else if (!editing) {
883 		if (hist) {
884 			history_end(hist);
885 			hist = NULL;
886 		}
887 		if (el) {
888 			el_end(el);
889 			el = NULL;
890 		}
891 	}
892 }
893 #endif /* !SMALL */
894 
895 /*
896  * Determine if given string is an IPv6 address or not.
897  * Return 1 for yes, 0 for no
898  */
899 int
isipv6addr(const char * addr)900 isipv6addr(const char *addr)
901 {
902 	int rv = 0;
903 #ifdef INET6
904 	struct addrinfo hints, *res;
905 
906 	memset(&hints, 0, sizeof(hints));
907 	hints.ai_family = PF_INET6;
908 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
909 	hints.ai_flags = AI_NUMERICHOST;
910 	if (getaddrinfo(addr, "0", &hints, &res) != 0)
911 		rv = 0;
912 	else {
913 		rv = 1;
914 		freeaddrinfo(res);
915 	}
916 	if (debug)
917 		printf("isipv6addr: got %d for %s\n", rv, addr);
918 #endif
919 	return (rv == 1) ? 1 : 0;
920 }
921 
922 #ifdef USE_SSL
923 /*
924  * Start TLS/SSL protected session
925  * return codes are:
926  * "1" - TLS/SSL negotiation successfull, "0" - TLS/SSL negotiation failed,
927  * "-1" - errors occured during TLS/SSL handshake
928  */
929 int
auth()930 auth()
931 {
932 	int n = 0, rcode = 0;
933 	char *ssl_version;
934 	int ssl_bits;
935 	SSL_CIPHER *ssl_cipher;
936 
937 	switch (ssl_secure_flags & SSL_ENABLED) {
938 	case SSL_USE_TLS:
939 		n = command("AUTH TLS");
940 		if (n == COMPLETE) {
941 			break;
942 		} else {
943 			printf("TLS not available\n");
944 			break;
945 		}
946 	case SSL_ENABLED:
947 		n = command("AUTH TLS");
948 		if (n == COMPLETE) break;
949 	case SSL_USE_COMPAT:
950 		n = command("AUTH SSL");
951 		if (n == COMPLETE || n == CONTINUE) {
952 			ssl_compat_flag = 1;
953 			ssl_encrypt_data = 1;
954 			/* It's a some sort of a workaround for the problem of
955 			 * the reply code: 234 in FTP-TLS draft v7 and RFC2228
956 			 * vs 334 in some early versions of the draft. */
957 			n = COMPLETE;
958 			break;
959 		}
960 	default:
961 		printf("TLS/SSL not available\n");
962 	}
963 
964 	if (n == COMPLETE) {
965 		rcode = 1;
966 		/* Do SSL */
967 		ssl_con = (SSL *)SSL_new(ssl_ctx);
968 		SSL_set_connect_state(ssl_con);
969 
970 		SSL_set_fd(ssl_con, fileno(cout));
971 		set_ssl_trace(ssl_con);
972 
973 		if ((n = SSL_connect(ssl_con)) <= 0) {
974 			ssl_log_err(bio_err, "SSL_connect error");
975 
976 			switch (SSL_get_verify_result(ssl_con)) {
977 			case X509_V_ERR_CRL_SIGNATURE_FAILURE:
978 				ssl_log_msgn(bio_err, "Reason: Invalid signature on CRL!");
979 				break;
980 			case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
981 				ssl_log_msgn(bio_err, "Reason: Found CRL has invalid nextUpdate field.");
982 				break;
983 			case X509_V_ERR_CRL_HAS_EXPIRED:
984 				ssl_log_msgn(bio_err, "Reason: Found CRL expired - revoking all certificates until you get updated CRL.");
985 				break;
986 			case X509_V_ERR_CERT_REVOKED:
987 				ssl_log_msgn(bio_err, "Reason: Certificate is revoked");
988 				break;
989 			default:
990 				break;
991 			}
992 
993 			warnx("TLS/SSL connection to server failed");
994 			/* abort time methinks ... */
995 			/* exit(1);*/
996 			rcode = -1;
997 		} else {
998 			ssl_version = SSL_get_cipher_version(ssl_con);
999 			ssl_cipher = SSL_get_current_cipher(ssl_con);
1000 			SSL_CIPHER_get_bits(ssl_cipher, &ssl_bits);
1001 			fprintf(stderr, "[%s, cipher %s, %d bits]\n",
1002 			    ssl_version, SSL_CIPHER_get_name(ssl_cipher),
1003 			    ssl_bits);
1004 			fflush(stderr);
1005 			ssl_active_flag = 1;
1006 		}
1007 	}
1008 
1009 	return(rcode);
1010 }
1011 #endif /* USE_SSL */
1012