xref: /dragonfly/contrib/tnftp/src/util.c (revision 3a184c67)
1*3a184c67SAntonio Huete Jimenez /*	$NetBSD: util.c,v 1.25 2021/08/27 01:38:49 lukem Exp $	*/
2*3a184c67SAntonio Huete Jimenez /*	from	NetBSD: util.c,v 1.162 2021/04/25 08:26:35 lukem Exp	*/
36cdfca03SJohn Marino 
46cdfca03SJohn Marino /*-
5*3a184c67SAntonio Huete Jimenez  * Copyright (c) 1997-2020 The NetBSD Foundation, Inc.
66cdfca03SJohn Marino  * All rights reserved.
76cdfca03SJohn Marino  *
86cdfca03SJohn Marino  * This code is derived from software contributed to The NetBSD Foundation
96cdfca03SJohn Marino  * by Luke Mewburn.
106cdfca03SJohn Marino  *
116cdfca03SJohn Marino  * This code is derived from software contributed to The NetBSD Foundation
126cdfca03SJohn Marino  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
136cdfca03SJohn Marino  * NASA Ames Research Center.
146cdfca03SJohn Marino  *
156cdfca03SJohn Marino  * Redistribution and use in source and binary forms, with or without
166cdfca03SJohn Marino  * modification, are permitted provided that the following conditions
176cdfca03SJohn Marino  * are met:
186cdfca03SJohn Marino  * 1. Redistributions of source code must retain the above copyright
196cdfca03SJohn Marino  *    notice, this list of conditions and the following disclaimer.
206cdfca03SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
216cdfca03SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
226cdfca03SJohn Marino  *    documentation and/or other materials provided with the distribution.
236cdfca03SJohn Marino  *
246cdfca03SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
256cdfca03SJohn Marino  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
266cdfca03SJohn Marino  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
276cdfca03SJohn Marino  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
286cdfca03SJohn Marino  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
296cdfca03SJohn Marino  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
306cdfca03SJohn Marino  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
316cdfca03SJohn Marino  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
326cdfca03SJohn Marino  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
336cdfca03SJohn Marino  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
346cdfca03SJohn Marino  * POSSIBILITY OF SUCH DAMAGE.
356cdfca03SJohn Marino  */
366cdfca03SJohn Marino 
376cdfca03SJohn Marino /*
386cdfca03SJohn Marino  * Copyright (c) 1985, 1989, 1993, 1994
396cdfca03SJohn Marino  *	The Regents of the University of California.  All rights reserved.
406cdfca03SJohn Marino  *
416cdfca03SJohn Marino  * Redistribution and use in source and binary forms, with or without
426cdfca03SJohn Marino  * modification, are permitted provided that the following conditions
436cdfca03SJohn Marino  * are met:
446cdfca03SJohn Marino  * 1. Redistributions of source code must retain the above copyright
456cdfca03SJohn Marino  *    notice, this list of conditions and the following disclaimer.
466cdfca03SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
476cdfca03SJohn Marino  *    notice, this list of conditions and the following disclaimer in the
486cdfca03SJohn Marino  *    documentation and/or other materials provided with the distribution.
496cdfca03SJohn Marino  * 3. Neither the name of the University nor the names of its contributors
506cdfca03SJohn Marino  *    may be used to endorse or promote products derived from this software
516cdfca03SJohn Marino  *    without specific prior written permission.
526cdfca03SJohn Marino  *
536cdfca03SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
546cdfca03SJohn Marino  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
556cdfca03SJohn Marino  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
566cdfca03SJohn Marino  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
576cdfca03SJohn Marino  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
586cdfca03SJohn Marino  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
596cdfca03SJohn Marino  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
606cdfca03SJohn Marino  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
616cdfca03SJohn Marino  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
626cdfca03SJohn Marino  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
636cdfca03SJohn Marino  * SUCH DAMAGE.
646cdfca03SJohn Marino  */
656cdfca03SJohn Marino 
666cdfca03SJohn Marino #include "tnftp.h"
676cdfca03SJohn Marino 
686cdfca03SJohn Marino #if 0	/* tnftp */
696cdfca03SJohn Marino 
706cdfca03SJohn Marino #include <sys/cdefs.h>
716cdfca03SJohn Marino #ifndef lint
72*3a184c67SAntonio Huete Jimenez __RCSID(" NetBSD: util.c,v 1.162 2021/04/25 08:26:35 lukem Exp  ");
736cdfca03SJohn Marino #endif /* not lint */
746cdfca03SJohn Marino 
756cdfca03SJohn Marino /*
766cdfca03SJohn Marino  * FTP User Program -- Misc support routines
776cdfca03SJohn Marino  */
786cdfca03SJohn Marino #include <sys/param.h>
796cdfca03SJohn Marino #include <sys/socket.h>
806cdfca03SJohn Marino #include <sys/ioctl.h>
816cdfca03SJohn Marino #include <sys/time.h>
826cdfca03SJohn Marino #include <netinet/in.h>
836cdfca03SJohn Marino #include <arpa/ftp.h>
846cdfca03SJohn Marino 
856cdfca03SJohn Marino #include <ctype.h>
866cdfca03SJohn Marino #include <err.h>
876cdfca03SJohn Marino #include <errno.h>
886cdfca03SJohn Marino #include <fcntl.h>
896cdfca03SJohn Marino #include <glob.h>
906cdfca03SJohn Marino #include <signal.h>
916cdfca03SJohn Marino #include <libgen.h>
926cdfca03SJohn Marino #include <limits.h>
936cdfca03SJohn Marino #include <locale.h>
946cdfca03SJohn Marino #include <netdb.h>
956cdfca03SJohn Marino #include <stdio.h>
966cdfca03SJohn Marino #include <stdlib.h>
976cdfca03SJohn Marino #include <string.h>
986cdfca03SJohn Marino #include <termios.h>
996cdfca03SJohn Marino #include <time.h>
1006cdfca03SJohn Marino #include <tzfile.h>
1016cdfca03SJohn Marino #include <unistd.h>
1026cdfca03SJohn Marino 
1036cdfca03SJohn Marino #endif	/* tnftp */
1046cdfca03SJohn Marino 
1056cdfca03SJohn Marino #include "ftp_var.h"
1066cdfca03SJohn Marino 
1076cdfca03SJohn Marino /*
1086cdfca03SJohn Marino  * Connect to peer server and auto-login, if possible.
1096cdfca03SJohn Marino  */
1106cdfca03SJohn Marino void
setpeer(int argc,char * argv[])1116cdfca03SJohn Marino setpeer(int argc, char *argv[])
1126cdfca03SJohn Marino {
1136cdfca03SJohn Marino 	char *host;
1146cdfca03SJohn Marino 	const char *port;
1156cdfca03SJohn Marino 
1166cdfca03SJohn Marino 	if (argc == 0)
1176cdfca03SJohn Marino 		goto usage;
1186cdfca03SJohn Marino 	if (connected) {
1196cdfca03SJohn Marino 		fprintf(ttyout, "Already connected to %s, use close first.\n",
1206cdfca03SJohn Marino 		    hostname);
1216cdfca03SJohn Marino 		code = -1;
1226cdfca03SJohn Marino 		return;
1236cdfca03SJohn Marino 	}
1246cdfca03SJohn Marino 	if (argc < 2)
1256cdfca03SJohn Marino 		(void)another(&argc, &argv, "to");
1266cdfca03SJohn Marino 	if (argc < 2 || argc > 3) {
1276cdfca03SJohn Marino  usage:
1286cdfca03SJohn Marino 		UPRINTF("usage: %s host-name [port]\n", argv[0]);
1296cdfca03SJohn Marino 		code = -1;
1306cdfca03SJohn Marino 		return;
1316cdfca03SJohn Marino 	}
1326cdfca03SJohn Marino 	if (gatemode)
1336cdfca03SJohn Marino 		port = gateport;
1346cdfca03SJohn Marino 	else
1356cdfca03SJohn Marino 		port = ftpport;
1366cdfca03SJohn Marino 	if (argc > 2)
1376cdfca03SJohn Marino 		port = argv[2];
1386cdfca03SJohn Marino 
1396cdfca03SJohn Marino 	if (gatemode) {
1406cdfca03SJohn Marino 		if (gateserver == NULL || *gateserver == '\0')
1416cdfca03SJohn Marino 			errx(1, "main: gateserver not defined");
1426cdfca03SJohn Marino 		host = hookup(gateserver, port);
1436cdfca03SJohn Marino 	} else
1446cdfca03SJohn Marino 		host = hookup(argv[1], port);
1456cdfca03SJohn Marino 
1466cdfca03SJohn Marino 	if (host) {
1476cdfca03SJohn Marino 		if (gatemode && verbose) {
1486cdfca03SJohn Marino 			fprintf(ttyout,
1496cdfca03SJohn Marino 			    "Connecting via pass-through server %s\n",
1506cdfca03SJohn Marino 			    gateserver);
1516cdfca03SJohn Marino 		}
1526cdfca03SJohn Marino 
1536cdfca03SJohn Marino 		connected = 1;
1546cdfca03SJohn Marino 		/*
1556cdfca03SJohn Marino 		 * Set up defaults for FTP.
1566cdfca03SJohn Marino 		 */
1576cdfca03SJohn Marino 		(void)strlcpy(typename, "ascii", sizeof(typename));
1586cdfca03SJohn Marino 		type = TYPE_A;
1596cdfca03SJohn Marino 		curtype = TYPE_A;
1606cdfca03SJohn Marino 		(void)strlcpy(formname, "non-print", sizeof(formname));
1616cdfca03SJohn Marino 		form = FORM_N;
1626cdfca03SJohn Marino 		(void)strlcpy(modename, "stream", sizeof(modename));
1636cdfca03SJohn Marino 		mode = MODE_S;
1646cdfca03SJohn Marino 		(void)strlcpy(structname, "file", sizeof(structname));
1656cdfca03SJohn Marino 		stru = STRU_F;
1666cdfca03SJohn Marino 		(void)strlcpy(bytename, "8", sizeof(bytename));
1676cdfca03SJohn Marino 		bytesize = 8;
1686cdfca03SJohn Marino 		if (autologin)
1696cdfca03SJohn Marino 			(void)ftp_login(argv[1], NULL, NULL);
1706cdfca03SJohn Marino 	}
1716cdfca03SJohn Marino }
1726cdfca03SJohn Marino 
1736cdfca03SJohn Marino static void
parse_feat(const char * fline)1746cdfca03SJohn Marino parse_feat(const char *fline)
1756cdfca03SJohn Marino {
1766cdfca03SJohn Marino 
1776cdfca03SJohn Marino 			/*
1786cdfca03SJohn Marino 			 * work-around broken ProFTPd servers that can't
1796cdfca03SJohn Marino 			 * even obey RFC 2389.
1806cdfca03SJohn Marino 			 */
1816cdfca03SJohn Marino 	while (*fline && isspace((int)*fline))
1826cdfca03SJohn Marino 		fline++;
1836cdfca03SJohn Marino 
1846cdfca03SJohn Marino 	if (strcasecmp(fline, "MDTM") == 0)
1856cdfca03SJohn Marino 		features[FEAT_MDTM] = 1;
1866cdfca03SJohn Marino 	else if (strncasecmp(fline, "MLST", sizeof("MLST") - 1) == 0) {
1876cdfca03SJohn Marino 		features[FEAT_MLST] = 1;
1886cdfca03SJohn Marino 	} else if (strcasecmp(fline, "REST STREAM") == 0)
1896cdfca03SJohn Marino 		features[FEAT_REST_STREAM] = 1;
1906cdfca03SJohn Marino 	else if (strcasecmp(fline, "SIZE") == 0)
1916cdfca03SJohn Marino 		features[FEAT_SIZE] = 1;
1926cdfca03SJohn Marino 	else if (strcasecmp(fline, "TVFS") == 0)
1936cdfca03SJohn Marino 		features[FEAT_TVFS] = 1;
1946cdfca03SJohn Marino }
1956cdfca03SJohn Marino 
1966cdfca03SJohn Marino /*
1976cdfca03SJohn Marino  * Determine the remote system type (SYST) and features (FEAT).
1986cdfca03SJohn Marino  * Call after a successful login (i.e, connected = -1)
1996cdfca03SJohn Marino  */
2006cdfca03SJohn Marino void
getremoteinfo(void)2016cdfca03SJohn Marino getremoteinfo(void)
2026cdfca03SJohn Marino {
2036cdfca03SJohn Marino 	int overbose, i;
2046cdfca03SJohn Marino 
2056cdfca03SJohn Marino 	overbose = verbose;
2066cdfca03SJohn Marino 	if (ftp_debug == 0)
2076cdfca03SJohn Marino 		verbose = -1;
2086cdfca03SJohn Marino 
2096cdfca03SJohn Marino 			/* determine remote system type */
2106cdfca03SJohn Marino 	if (command("SYST") == COMPLETE) {
2116cdfca03SJohn Marino 		if (overbose) {
2126cdfca03SJohn Marino 			int os_len = strcspn(reply_string + 4, " \r\n\t");
2136cdfca03SJohn Marino 			if (os_len > 1 && reply_string[4 + os_len - 1] == '.')
2146cdfca03SJohn Marino 				os_len--;
2156cdfca03SJohn Marino 			fprintf(ttyout, "Remote system type is %.*s.\n",
2166cdfca03SJohn Marino 			    os_len, reply_string + 4);
2176cdfca03SJohn Marino 		}
2186cdfca03SJohn Marino 		/*
2196cdfca03SJohn Marino 		 * Decide whether we should default to bninary.
2206cdfca03SJohn Marino 		 * Traditionally checked for "215 UNIX Type: L8", but
2216cdfca03SJohn Marino 		 * some printers report "Linux" ! so be more forgiving.
2226cdfca03SJohn Marino 		 * In reality we probably almost never want text any more.
2236cdfca03SJohn Marino 		 */
2246cdfca03SJohn Marino 		if (!strncasecmp(reply_string + 4, "unix", 4) ||
2256cdfca03SJohn Marino 		    !strncasecmp(reply_string + 4, "linux", 5)) {
2266cdfca03SJohn Marino 			if (proxy)
2276cdfca03SJohn Marino 				unix_proxy = 1;
2286cdfca03SJohn Marino 			else
2296cdfca03SJohn Marino 				unix_server = 1;
2306cdfca03SJohn Marino 			/*
2316cdfca03SJohn Marino 			 * Set type to 0 (not specified by user),
2326cdfca03SJohn Marino 			 * meaning binary by default, but don't bother
2336cdfca03SJohn Marino 			 * telling server.  We can use binary
2346cdfca03SJohn Marino 			 * for text files unless changed by the user.
2356cdfca03SJohn Marino 			 */
2366cdfca03SJohn Marino 			type = 0;
2376cdfca03SJohn Marino 			(void)strlcpy(typename, "binary", sizeof(typename));
2386cdfca03SJohn Marino 			if (overbose)
2396cdfca03SJohn Marino 			    fprintf(ttyout,
2406cdfca03SJohn Marino 				"Using %s mode to transfer files.\n",
2416cdfca03SJohn Marino 				typename);
2426cdfca03SJohn Marino 		} else {
2436cdfca03SJohn Marino 			if (proxy)
2446cdfca03SJohn Marino 				unix_proxy = 0;
2456cdfca03SJohn Marino 			else
2466cdfca03SJohn Marino 				unix_server = 0;
2476cdfca03SJohn Marino 			if (overbose &&
2486cdfca03SJohn Marino 			    !strncmp(reply_string, "215 TOPS20", 10))
2496cdfca03SJohn Marino 				fputs(
2506cdfca03SJohn Marino "Remember to set tenex mode when transferring binary files from this machine.\n",
2516cdfca03SJohn Marino 				    ttyout);
2526cdfca03SJohn Marino 		}
2536cdfca03SJohn Marino 	}
2546cdfca03SJohn Marino 
2556cdfca03SJohn Marino 			/* determine features (if any) */
2566cdfca03SJohn Marino 	for (i = 0; i < FEAT_max; i++)
2576cdfca03SJohn Marino 		features[i] = -1;
2586cdfca03SJohn Marino 	reply_callback = parse_feat;
2596cdfca03SJohn Marino 	if (command("FEAT") == COMPLETE) {
2606cdfca03SJohn Marino 		for (i = 0; i < FEAT_max; i++) {
2616cdfca03SJohn Marino 			if (features[i] == -1)
2626cdfca03SJohn Marino 				features[i] = 0;
2636cdfca03SJohn Marino 		}
2646cdfca03SJohn Marino 		features[FEAT_FEAT] = 1;
2656cdfca03SJohn Marino 	} else
2666cdfca03SJohn Marino 		features[FEAT_FEAT] = 0;
2676cdfca03SJohn Marino #ifndef NO_DEBUG
2686cdfca03SJohn Marino 	if (ftp_debug) {
2696cdfca03SJohn Marino #define DEBUG_FEAT(x) fprintf(ttyout, "features[" #x "] = %d\n", features[(x)])
2706cdfca03SJohn Marino 		DEBUG_FEAT(FEAT_FEAT);
2716cdfca03SJohn Marino 		DEBUG_FEAT(FEAT_MDTM);
2726cdfca03SJohn Marino 		DEBUG_FEAT(FEAT_MLST);
2736cdfca03SJohn Marino 		DEBUG_FEAT(FEAT_REST_STREAM);
2746cdfca03SJohn Marino 		DEBUG_FEAT(FEAT_SIZE);
2756cdfca03SJohn Marino 		DEBUG_FEAT(FEAT_TVFS);
2766cdfca03SJohn Marino #undef DEBUG_FEAT
2776cdfca03SJohn Marino 	}
2786cdfca03SJohn Marino #endif
2796cdfca03SJohn Marino 	reply_callback = NULL;
2806cdfca03SJohn Marino 
2816cdfca03SJohn Marino 	verbose = overbose;
2826cdfca03SJohn Marino }
2836cdfca03SJohn Marino 
2846cdfca03SJohn Marino /*
2856cdfca03SJohn Marino  * Reset the various variables that indicate connection state back to
2866cdfca03SJohn Marino  * disconnected settings.
2876cdfca03SJohn Marino  * The caller is responsible for issuing any commands to the remote server
2886cdfca03SJohn Marino  * to perform a clean shutdown before this is invoked.
2896cdfca03SJohn Marino  */
2906cdfca03SJohn Marino void
cleanuppeer(void)2916cdfca03SJohn Marino cleanuppeer(void)
2926cdfca03SJohn Marino {
2936cdfca03SJohn Marino 
2946cdfca03SJohn Marino 	if (cout)
2956cdfca03SJohn Marino 		(void)fclose(cout);
2966cdfca03SJohn Marino 	cout = NULL;
2976cdfca03SJohn Marino 	connected = 0;
2986cdfca03SJohn Marino 	unix_server = 0;
2996cdfca03SJohn Marino 	unix_proxy = 0;
3006cdfca03SJohn Marino 			/*
3016cdfca03SJohn Marino 			 * determine if anonftp was specifically set with -a
3026cdfca03SJohn Marino 			 * (1), or implicitly set by auto_fetch() (2). in the
3036cdfca03SJohn Marino 			 * latter case, disable after the current xfer
3046cdfca03SJohn Marino 			 */
3056cdfca03SJohn Marino 	if (anonftp == 2)
3066cdfca03SJohn Marino 		anonftp = 0;
3076cdfca03SJohn Marino 	data = -1;
3086cdfca03SJohn Marino 	epsv4bad = 0;
3096cdfca03SJohn Marino 	epsv6bad = 0;
3106cdfca03SJohn Marino 	if (username)
3116cdfca03SJohn Marino 		free(username);
3126cdfca03SJohn Marino 	username = NULL;
3136cdfca03SJohn Marino 	if (!proxy)
3146cdfca03SJohn Marino 		macnum = 0;
3156cdfca03SJohn Marino }
3166cdfca03SJohn Marino 
3176cdfca03SJohn Marino /*
3186cdfca03SJohn Marino  * Top-level signal handler for interrupted commands.
3196cdfca03SJohn Marino  */
3206cdfca03SJohn Marino void
intr(int signo)3216cdfca03SJohn Marino intr(int signo)
3226cdfca03SJohn Marino {
3236cdfca03SJohn Marino 
3246cdfca03SJohn Marino 	sigint_raised = 1;
3256cdfca03SJohn Marino 	alarmtimer(0);
3266cdfca03SJohn Marino 	if (fromatty)
3276cdfca03SJohn Marino 		write(fileno(ttyout), "\n", 1);
3286cdfca03SJohn Marino 	siglongjmp(toplevel, 1);
3296cdfca03SJohn Marino }
3306cdfca03SJohn Marino 
3316cdfca03SJohn Marino /*
3326cdfca03SJohn Marino  * Signal handler for lost connections; cleanup various elements of
3336cdfca03SJohn Marino  * the connection state, and call cleanuppeer() to finish it off.
334*3a184c67SAntonio Huete Jimenez  * This function is not signal safe, so exit if called by a signal.
3356cdfca03SJohn Marino  */
3366cdfca03SJohn Marino void
lostpeer(int signo)337*3a184c67SAntonio Huete Jimenez lostpeer(int signo)
3386cdfca03SJohn Marino {
3396cdfca03SJohn Marino 	int oerrno = errno;
3406cdfca03SJohn Marino 
3416cdfca03SJohn Marino 	alarmtimer(0);
3426cdfca03SJohn Marino 	if (connected) {
3436cdfca03SJohn Marino 		if (cout != NULL) {
3446cdfca03SJohn Marino 			(void)shutdown(fileno(cout), 1+1);
3456cdfca03SJohn Marino 			(void)fclose(cout);
3466cdfca03SJohn Marino 			cout = NULL;
3476cdfca03SJohn Marino 		}
3486cdfca03SJohn Marino 		if (data >= 0) {
3496cdfca03SJohn Marino 			(void)shutdown(data, 1+1);
3506cdfca03SJohn Marino 			(void)close(data);
3516cdfca03SJohn Marino 			data = -1;
3526cdfca03SJohn Marino 		}
3536cdfca03SJohn Marino 		connected = 0;
3546cdfca03SJohn Marino 	}
3556cdfca03SJohn Marino 	pswitch(1);
3566cdfca03SJohn Marino 	if (connected) {
3576cdfca03SJohn Marino 		if (cout != NULL) {
3586cdfca03SJohn Marino 			(void)shutdown(fileno(cout), 1+1);
3596cdfca03SJohn Marino 			(void)fclose(cout);
3606cdfca03SJohn Marino 			cout = NULL;
3616cdfca03SJohn Marino 		}
3626cdfca03SJohn Marino 		connected = 0;
3636cdfca03SJohn Marino 	}
3646cdfca03SJohn Marino 	proxflag = 0;
3656cdfca03SJohn Marino 	pswitch(0);
3666cdfca03SJohn Marino 	cleanuppeer();
367*3a184c67SAntonio Huete Jimenez 	if (signo) {
368*3a184c67SAntonio Huete Jimenez 		errx(1, "lostpeer due to signal %d", signo);
369*3a184c67SAntonio Huete Jimenez 	}
3706cdfca03SJohn Marino 	errno = oerrno;
3716cdfca03SJohn Marino }
3726cdfca03SJohn Marino 
3736cdfca03SJohn Marino 
3746cdfca03SJohn Marino /*
3756cdfca03SJohn Marino  * Login to remote host, using given username & password if supplied.
3766cdfca03SJohn Marino  * Return non-zero if successful.
3776cdfca03SJohn Marino  */
3786cdfca03SJohn Marino int
ftp_login(const char * host,const char * luser,const char * lpass)3796cdfca03SJohn Marino ftp_login(const char *host, const char *luser, const char *lpass)
3806cdfca03SJohn Marino {
3816cdfca03SJohn Marino 	char tmp[80];
3826cdfca03SJohn Marino 	char *fuser, *pass, *facct, *p;
3836cdfca03SJohn Marino 	char emptypass[] = "";
3846cdfca03SJohn Marino 	const char *errormsg;
3856cdfca03SJohn Marino 	int n, aflag, rval, nlen;
3866cdfca03SJohn Marino 
3876cdfca03SJohn Marino 	aflag = rval = 0;
3886cdfca03SJohn Marino 	fuser = pass = facct = NULL;
3896cdfca03SJohn Marino 	if (luser)
3906cdfca03SJohn Marino 		fuser = ftp_strdup(luser);
3916cdfca03SJohn Marino 	if (lpass)
3926cdfca03SJohn Marino 		pass = ftp_strdup(lpass);
3936cdfca03SJohn Marino 
3946cdfca03SJohn Marino 	DPRINTF("ftp_login: user `%s' pass `%s' host `%s'\n",
3956cdfca03SJohn Marino 	    STRorNULL(fuser), STRorNULL(pass), STRorNULL(host));
3966cdfca03SJohn Marino 
3976cdfca03SJohn Marino 	/*
3986cdfca03SJohn Marino 	 * Set up arguments for an anonymous FTP session, if necessary.
3996cdfca03SJohn Marino 	 */
4006cdfca03SJohn Marino 	if (anonftp) {
4016cdfca03SJohn Marino 		FREEPTR(fuser);
4026cdfca03SJohn Marino 		fuser = ftp_strdup("anonymous");	/* as per RFC 1635 */
4036cdfca03SJohn Marino 		FREEPTR(pass);
4046cdfca03SJohn Marino 		pass = ftp_strdup(getoptionvalue("anonpass"));
4056cdfca03SJohn Marino 	}
4066cdfca03SJohn Marino 
4076cdfca03SJohn Marino 	if (ruserpass(host, &fuser, &pass, &facct) < 0) {
4086cdfca03SJohn Marino 		code = -1;
4096cdfca03SJohn Marino 		goto cleanup_ftp_login;
4106cdfca03SJohn Marino 	}
4116cdfca03SJohn Marino 
4126cdfca03SJohn Marino 	while (fuser == NULL) {
4136cdfca03SJohn Marino 		if (localname)
4146cdfca03SJohn Marino 			fprintf(ttyout, "Name (%s:%s): ", host, localname);
4156cdfca03SJohn Marino 		else
4166cdfca03SJohn Marino 			fprintf(ttyout, "Name (%s): ", host);
4176cdfca03SJohn Marino 		errormsg = NULL;
4186cdfca03SJohn Marino 		nlen = get_line(stdin, tmp, sizeof(tmp), &errormsg);
4196cdfca03SJohn Marino 		if (nlen < 0) {
4206cdfca03SJohn Marino 			fprintf(ttyout, "%s; %s aborted.\n", errormsg, "login");
4216cdfca03SJohn Marino 			code = -1;
4226cdfca03SJohn Marino 			goto cleanup_ftp_login;
4236cdfca03SJohn Marino 		} else if (nlen == 0) {
4246cdfca03SJohn Marino 			fuser = ftp_strdup(localname);
4256cdfca03SJohn Marino 		} else {
4266cdfca03SJohn Marino 			fuser = ftp_strdup(tmp);
4276cdfca03SJohn Marino 		}
4286cdfca03SJohn Marino 	}
4296cdfca03SJohn Marino 
4306cdfca03SJohn Marino 	if (gatemode) {
4316cdfca03SJohn Marino 		char *nuser;
4326cdfca03SJohn Marino 		size_t len;
4336cdfca03SJohn Marino 
4346cdfca03SJohn Marino 		len = strlen(fuser) + 1 + strlen(host) + 1;
4356cdfca03SJohn Marino 		nuser = ftp_malloc(len);
4366cdfca03SJohn Marino 		(void)strlcpy(nuser, fuser, len);
4376cdfca03SJohn Marino 		(void)strlcat(nuser, "@",  len);
4386cdfca03SJohn Marino 		(void)strlcat(nuser, host, len);
4396cdfca03SJohn Marino 		FREEPTR(fuser);
4406cdfca03SJohn Marino 		fuser = nuser;
4416cdfca03SJohn Marino 	}
4426cdfca03SJohn Marino 
4436cdfca03SJohn Marino 	n = command("USER %s", fuser);
4446cdfca03SJohn Marino 	if (n == CONTINUE) {
4456cdfca03SJohn Marino 		if (pass == NULL) {
4466cdfca03SJohn Marino 			p = getpass("Password: ");
4476cdfca03SJohn Marino 			if (p == NULL)
4486cdfca03SJohn Marino 				p = emptypass;
4496cdfca03SJohn Marino 			pass = ftp_strdup(p);
4506cdfca03SJohn Marino 			memset(p, 0, strlen(p));
4516cdfca03SJohn Marino 		}
4526cdfca03SJohn Marino 		n = command("PASS %s", pass);
4536cdfca03SJohn Marino 		memset(pass, 0, strlen(pass));
4546cdfca03SJohn Marino 	}
4556cdfca03SJohn Marino 	if (n == CONTINUE) {
4566cdfca03SJohn Marino 		aflag++;
4576cdfca03SJohn Marino 		if (facct == NULL) {
4586cdfca03SJohn Marino 			p = getpass("Account: ");
4596cdfca03SJohn Marino 			if (p == NULL)
4606cdfca03SJohn Marino 				p = emptypass;
4616cdfca03SJohn Marino 			facct = ftp_strdup(p);
4626cdfca03SJohn Marino 			memset(p, 0, strlen(p));
4636cdfca03SJohn Marino 		}
4646cdfca03SJohn Marino 		if (facct[0] == '\0') {
4656cdfca03SJohn Marino 			warnx("Login failed");
4666cdfca03SJohn Marino 			goto cleanup_ftp_login;
4676cdfca03SJohn Marino 		}
4686cdfca03SJohn Marino 		n = command("ACCT %s", facct);
4696cdfca03SJohn Marino 		memset(facct, 0, strlen(facct));
4706cdfca03SJohn Marino 	}
4716cdfca03SJohn Marino 	if ((n != COMPLETE) ||
4726cdfca03SJohn Marino 	    (!aflag && facct != NULL && command("ACCT %s", facct) != COMPLETE)) {
4736cdfca03SJohn Marino 		warnx("Login failed");
4746cdfca03SJohn Marino 		goto cleanup_ftp_login;
4756cdfca03SJohn Marino 	}
4766cdfca03SJohn Marino 	rval = 1;
4776cdfca03SJohn Marino 	username = ftp_strdup(fuser);
4786cdfca03SJohn Marino 	if (proxy)
4796cdfca03SJohn Marino 		goto cleanup_ftp_login;
4806cdfca03SJohn Marino 
4816cdfca03SJohn Marino 	connected = -1;
4826cdfca03SJohn Marino 	getremoteinfo();
4836cdfca03SJohn Marino 	for (n = 0; n < macnum; ++n) {
4846cdfca03SJohn Marino 		if (!strcmp("init", macros[n].mac_name)) {
4856cdfca03SJohn Marino 			(void)strlcpy(line, "$init", sizeof(line));
4866cdfca03SJohn Marino 			makeargv();
4876cdfca03SJohn Marino 			domacro(margc, margv);
4886cdfca03SJohn Marino 			break;
4896cdfca03SJohn Marino 		}
4906cdfca03SJohn Marino 	}
4916cdfca03SJohn Marino 	updatelocalcwd();
492*3a184c67SAntonio Huete Jimenez 	remotecwd[0] = '\0';
493*3a184c67SAntonio Huete Jimenez 	remcwdvalid = 0;
4946cdfca03SJohn Marino 
4956cdfca03SJohn Marino  cleanup_ftp_login:
4966cdfca03SJohn Marino 	FREEPTR(fuser);
4976cdfca03SJohn Marino 	if (pass != NULL)
4986cdfca03SJohn Marino 		memset(pass, 0, strlen(pass));
4996cdfca03SJohn Marino 	FREEPTR(pass);
5006cdfca03SJohn Marino 	if (facct != NULL)
5016cdfca03SJohn Marino 		memset(facct, 0, strlen(facct));
5026cdfca03SJohn Marino 	FREEPTR(facct);
5036cdfca03SJohn Marino 	return (rval);
5046cdfca03SJohn Marino }
5056cdfca03SJohn Marino 
5066cdfca03SJohn Marino /*
5076cdfca03SJohn Marino  * `another' gets another argument, and stores the new argc and argv.
5086cdfca03SJohn Marino  * It reverts to the top level (via intr()) on EOF/error.
5096cdfca03SJohn Marino  *
5106cdfca03SJohn Marino  * Returns false if no new arguments have been added.
5116cdfca03SJohn Marino  */
5126cdfca03SJohn Marino int
another(int * pargc,char *** pargv,const char * aprompt)5136cdfca03SJohn Marino another(int *pargc, char ***pargv, const char *aprompt)
5146cdfca03SJohn Marino {
5156cdfca03SJohn Marino 	const char	*errormsg;
5166cdfca03SJohn Marino 	int		ret, nlen;
5176cdfca03SJohn Marino 	size_t		len;
5186cdfca03SJohn Marino 
5196cdfca03SJohn Marino 	len = strlen(line);
5206cdfca03SJohn Marino 	if (len >= sizeof(line) - 3) {
5216cdfca03SJohn Marino 		fputs("Sorry, arguments too long.\n", ttyout);
5226cdfca03SJohn Marino 		intr(0);
5236cdfca03SJohn Marino 	}
5246cdfca03SJohn Marino 	fprintf(ttyout, "(%s) ", aprompt);
5256cdfca03SJohn Marino 	line[len++] = ' ';
5266cdfca03SJohn Marino 	errormsg = NULL;
5276cdfca03SJohn Marino 	nlen = get_line(stdin, line + len, sizeof(line)-len, &errormsg);
5286cdfca03SJohn Marino 	if (nlen < 0) {
5296cdfca03SJohn Marino 		fprintf(ttyout, "%s; %s aborted.\n", errormsg, "operation");
5306cdfca03SJohn Marino 		intr(0);
5316cdfca03SJohn Marino 	}
5326cdfca03SJohn Marino 	len += nlen;
5336cdfca03SJohn Marino 	makeargv();
5346cdfca03SJohn Marino 	ret = margc > *pargc;
5356cdfca03SJohn Marino 	*pargc = margc;
5366cdfca03SJohn Marino 	*pargv = margv;
5376cdfca03SJohn Marino 	return (ret);
5386cdfca03SJohn Marino }
5396cdfca03SJohn Marino 
5406cdfca03SJohn Marino /*
5416cdfca03SJohn Marino  * glob files given in argv[] from the remote server.
5426cdfca03SJohn Marino  * if errbuf isn't NULL, store error messages there instead
5436cdfca03SJohn Marino  * of writing to the screen.
5446cdfca03SJohn Marino  */
5456cdfca03SJohn Marino char *
remglob(char * argv[],int doswitch,const char ** errbuf)5466cdfca03SJohn Marino remglob(char *argv[], int doswitch, const char **errbuf)
5476cdfca03SJohn Marino {
5486cdfca03SJohn Marino 	static char buf[MAXPATHLEN];
5496cdfca03SJohn Marino 	static FILE *ftemp = NULL;
5506cdfca03SJohn Marino 	static char **args;
5516cdfca03SJohn Marino 	char temp[MAXPATHLEN];
5526cdfca03SJohn Marino 	int oldverbose, oldhash, oldprogress, fd;
5536cdfca03SJohn Marino 	char *cp;
5546cdfca03SJohn Marino 	const char *rmode;
5556cdfca03SJohn Marino 	size_t len;
5566cdfca03SJohn Marino 
5576cdfca03SJohn Marino 	if (!mflag || !connected) {
5586cdfca03SJohn Marino 		if (!doglob)
5596cdfca03SJohn Marino 			args = NULL;
5606cdfca03SJohn Marino 		else {
5616cdfca03SJohn Marino 			if (ftemp) {
5626cdfca03SJohn Marino 				(void)fclose(ftemp);
5636cdfca03SJohn Marino 				ftemp = NULL;
5646cdfca03SJohn Marino 			}
5656cdfca03SJohn Marino 		}
5666cdfca03SJohn Marino 		return (NULL);
5676cdfca03SJohn Marino 	}
5686cdfca03SJohn Marino 	if (!doglob) {
5696cdfca03SJohn Marino 		if (args == NULL)
5706cdfca03SJohn Marino 			args = argv;
5716cdfca03SJohn Marino 		if ((cp = *++args) == NULL)
5726cdfca03SJohn Marino 			args = NULL;
5736cdfca03SJohn Marino 		return (cp);
5746cdfca03SJohn Marino 	}
5756cdfca03SJohn Marino 	if (ftemp == NULL) {
5766cdfca03SJohn Marino 		len = strlcpy(temp, tmpdir, sizeof(temp));
5776cdfca03SJohn Marino 		if (temp[len - 1] != '/')
5786cdfca03SJohn Marino 			(void)strlcat(temp, "/", sizeof(temp));
5796cdfca03SJohn Marino 		(void)strlcat(temp, TMPFILE, sizeof(temp));
5806cdfca03SJohn Marino 		if ((fd = mkstemp(temp)) < 0) {
5816cdfca03SJohn Marino 			warn("Unable to create temporary file `%s'", temp);
5826cdfca03SJohn Marino 			return (NULL);
5836cdfca03SJohn Marino 		}
5846cdfca03SJohn Marino 		close(fd);
5856cdfca03SJohn Marino 		oldverbose = verbose;
5866cdfca03SJohn Marino 		verbose = (errbuf != NULL) ? -1 : 0;
5876cdfca03SJohn Marino 		oldhash = hash;
5886cdfca03SJohn Marino 		oldprogress = progress;
5896cdfca03SJohn Marino 		hash = 0;
5906cdfca03SJohn Marino 		progress = 0;
5916cdfca03SJohn Marino 		if (doswitch)
5926cdfca03SJohn Marino 			pswitch(!proxy);
5936cdfca03SJohn Marino 		for (rmode = "w"; *++argv != NULL; rmode = "a")
5946cdfca03SJohn Marino 			recvrequest("NLST", temp, *argv, rmode, 0, 0);
5956cdfca03SJohn Marino 		if ((code / 100) != COMPLETE) {
5966cdfca03SJohn Marino 			if (errbuf != NULL)
5976cdfca03SJohn Marino 				*errbuf = reply_string;
5986cdfca03SJohn Marino 		}
5996cdfca03SJohn Marino 		if (doswitch)
6006cdfca03SJohn Marino 			pswitch(!proxy);
6016cdfca03SJohn Marino 		verbose = oldverbose;
6026cdfca03SJohn Marino 		hash = oldhash;
6036cdfca03SJohn Marino 		progress = oldprogress;
6046cdfca03SJohn Marino 		ftemp = fopen(temp, "r");
6056cdfca03SJohn Marino 		(void)unlink(temp);
6066cdfca03SJohn Marino 		if (ftemp == NULL) {
6076cdfca03SJohn Marino 			if (errbuf == NULL)
6086cdfca03SJohn Marino 				warnx("Can't find list of remote files");
6096cdfca03SJohn Marino 			else
6106cdfca03SJohn Marino 				*errbuf =
6116cdfca03SJohn Marino 				    "Can't find list of remote files";
6126cdfca03SJohn Marino 			return (NULL);
6136cdfca03SJohn Marino 		}
6146cdfca03SJohn Marino 	}
6156cdfca03SJohn Marino 	if (fgets(buf, sizeof(buf), ftemp) == NULL) {
6166cdfca03SJohn Marino 		(void)fclose(ftemp);
6176cdfca03SJohn Marino 		ftemp = NULL;
6186cdfca03SJohn Marino 		return (NULL);
6196cdfca03SJohn Marino 	}
6206cdfca03SJohn Marino 	if ((cp = strchr(buf, '\n')) != NULL)
6216cdfca03SJohn Marino 		*cp = '\0';
6226cdfca03SJohn Marino 	return (buf);
6236cdfca03SJohn Marino }
6246cdfca03SJohn Marino 
6256cdfca03SJohn Marino /*
6266cdfca03SJohn Marino  * Glob a local file name specification with the expectation of a single
6276cdfca03SJohn Marino  * return value. Can't control multiple values being expanded from the
6286cdfca03SJohn Marino  * expression, we return only the first.
6296cdfca03SJohn Marino  * Returns NULL on error, or a pointer to a buffer containing the filename
6306cdfca03SJohn Marino  * that's the caller's responsiblity to free(3) when finished with.
6316cdfca03SJohn Marino  */
6326cdfca03SJohn Marino char *
globulize(const char * pattern)6336cdfca03SJohn Marino globulize(const char *pattern)
6346cdfca03SJohn Marino {
6356cdfca03SJohn Marino 	glob_t gl;
6366cdfca03SJohn Marino 	int flags;
6376cdfca03SJohn Marino 	char *p;
6386cdfca03SJohn Marino 
6396cdfca03SJohn Marino 	if (!doglob)
6406cdfca03SJohn Marino 		return (ftp_strdup(pattern));
6416cdfca03SJohn Marino 
6426cdfca03SJohn Marino 	flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
6436cdfca03SJohn Marino 	memset(&gl, 0, sizeof(gl));
6446cdfca03SJohn Marino 	if (glob(pattern, flags, NULL, &gl) || gl.gl_pathc == 0) {
6456cdfca03SJohn Marino 		warnx("Glob pattern `%s' not found", pattern);
6466cdfca03SJohn Marino 		globfree(&gl);
6476cdfca03SJohn Marino 		return (NULL);
6486cdfca03SJohn Marino 	}
6496cdfca03SJohn Marino 	p = ftp_strdup(gl.gl_pathv[0]);
6506cdfca03SJohn Marino 	globfree(&gl);
6516cdfca03SJohn Marino 	return (p);
6526cdfca03SJohn Marino }
6536cdfca03SJohn Marino 
6546cdfca03SJohn Marino /*
6556cdfca03SJohn Marino  * determine size of remote file
6566cdfca03SJohn Marino  */
6576cdfca03SJohn Marino off_t
remotesize(const char * file,int noisy)6586cdfca03SJohn Marino remotesize(const char *file, int noisy)
6596cdfca03SJohn Marino {
6606cdfca03SJohn Marino 	int overbose, r;
6616cdfca03SJohn Marino 	off_t size;
6626cdfca03SJohn Marino 
6636cdfca03SJohn Marino 	overbose = verbose;
6646cdfca03SJohn Marino 	size = -1;
6656cdfca03SJohn Marino 	if (ftp_debug == 0)
6666cdfca03SJohn Marino 		verbose = -1;
6676cdfca03SJohn Marino 	if (! features[FEAT_SIZE]) {
6686cdfca03SJohn Marino 		if (noisy)
6696cdfca03SJohn Marino 			fprintf(ttyout,
6706cdfca03SJohn Marino 			    "SIZE is not supported by remote server.\n");
6716cdfca03SJohn Marino 		goto cleanup_remotesize;
6726cdfca03SJohn Marino 	}
6736cdfca03SJohn Marino 	r = command("SIZE %s", file);
6746cdfca03SJohn Marino 	if (r == COMPLETE) {
6756cdfca03SJohn Marino 		char *cp, *ep;
6766cdfca03SJohn Marino 
6776cdfca03SJohn Marino 		cp = strchr(reply_string, ' ');
6786cdfca03SJohn Marino 		if (cp != NULL) {
6796cdfca03SJohn Marino 			cp++;
6806cdfca03SJohn Marino 			size = STRTOLL(cp, &ep, 10);
6816cdfca03SJohn Marino 			if (*ep != '\0' && !isspace((unsigned char)*ep))
6826cdfca03SJohn Marino 				size = -1;
6836cdfca03SJohn Marino 		}
6846cdfca03SJohn Marino 	} else {
6856cdfca03SJohn Marino 		if (r == ERROR && code == 500 && features[FEAT_SIZE] == -1)
6866cdfca03SJohn Marino 			features[FEAT_SIZE] = 0;
6876cdfca03SJohn Marino 		if (noisy && ftp_debug == 0) {
6886cdfca03SJohn Marino 			fputs(reply_string, ttyout);
6896cdfca03SJohn Marino 			putc('\n', ttyout);
6906cdfca03SJohn Marino 		}
6916cdfca03SJohn Marino 	}
6926cdfca03SJohn Marino  cleanup_remotesize:
6936cdfca03SJohn Marino 	verbose = overbose;
6946cdfca03SJohn Marino 	return (size);
6956cdfca03SJohn Marino }
6966cdfca03SJohn Marino 
6976cdfca03SJohn Marino /*
6986cdfca03SJohn Marino  * determine last modification time (in GMT) of remote file
6996cdfca03SJohn Marino  */
7006cdfca03SJohn Marino time_t
remotemodtime(const char * file,int noisy)7016cdfca03SJohn Marino remotemodtime(const char *file, int noisy)
7026cdfca03SJohn Marino {
7036cdfca03SJohn Marino 	int	overbose, ocode, r;
7046cdfca03SJohn Marino 	time_t	rtime;
7056cdfca03SJohn Marino 
7066cdfca03SJohn Marino 	overbose = verbose;
7076cdfca03SJohn Marino 	ocode = code;
7086cdfca03SJohn Marino 	rtime = -1;
7096cdfca03SJohn Marino 	if (ftp_debug == 0)
7106cdfca03SJohn Marino 		verbose = -1;
7116cdfca03SJohn Marino 	if (! features[FEAT_MDTM]) {
7126cdfca03SJohn Marino 		if (noisy)
7136cdfca03SJohn Marino 			fprintf(ttyout,
7146cdfca03SJohn Marino 			    "MDTM is not supported by remote server.\n");
7156cdfca03SJohn Marino 		goto cleanup_parse_time;
7166cdfca03SJohn Marino 	}
7176cdfca03SJohn Marino 	r = command("MDTM %s", file);
7186cdfca03SJohn Marino 	if (r == COMPLETE) {
7196cdfca03SJohn Marino 		struct tm timebuf;
7206cdfca03SJohn Marino 		char *timestr, *frac;
7216cdfca03SJohn Marino 
7226cdfca03SJohn Marino 		/*
7236cdfca03SJohn Marino 		 * time-val = 14DIGIT [ "." 1*DIGIT ]
7246cdfca03SJohn Marino 		 *		YYYYMMDDHHMMSS[.sss]
7256cdfca03SJohn Marino 		 * mdtm-response = "213" SP time-val CRLF / error-response
7266cdfca03SJohn Marino 		 */
7276cdfca03SJohn Marino 		timestr = reply_string + 4;
7286cdfca03SJohn Marino 
7296cdfca03SJohn Marino 					/*
7306cdfca03SJohn Marino 					 * parse fraction.
7316cdfca03SJohn Marino 					 * XXX: ignored for now
7326cdfca03SJohn Marino 					 */
7336cdfca03SJohn Marino 		frac = strchr(timestr, '\r');
7346cdfca03SJohn Marino 		if (frac != NULL)
7356cdfca03SJohn Marino 			*frac = '\0';
7366cdfca03SJohn Marino 		frac = strchr(timestr, '.');
7376cdfca03SJohn Marino 		if (frac != NULL)
7386cdfca03SJohn Marino 			*frac++ = '\0';
7396cdfca03SJohn Marino 		if (strlen(timestr) == 15 && strncmp(timestr, "191", 3) == 0) {
7406cdfca03SJohn Marino 			/*
741*3a184c67SAntonio Huete Jimenez 			 * XXX:	Workaround for buggy ftp servers that return
7426cdfca03SJohn Marino 			 *	`19100' instead of `2000'
7436cdfca03SJohn Marino 			 */
7446cdfca03SJohn Marino 			fprintf(ttyout,
7456cdfca03SJohn Marino 	    "Y2K warning! Incorrect time-val `%s' received from server.\n",
7466cdfca03SJohn Marino 			    timestr);
7476cdfca03SJohn Marino 			timestr++;
7486cdfca03SJohn Marino 			timestr[0] = '2';
7496cdfca03SJohn Marino 			timestr[1] = '0';
7506cdfca03SJohn Marino 			fprintf(ttyout, "Converted to `%s'\n", timestr);
7516cdfca03SJohn Marino 		}
7526cdfca03SJohn Marino 		memset(&timebuf, 0, sizeof(timebuf));
7536cdfca03SJohn Marino 		if (strlen(timestr) != 14 ||
7546cdfca03SJohn Marino 		    (strptime(timestr, "%Y%m%d%H%M%S", &timebuf) == NULL)) {
7556cdfca03SJohn Marino  bad_parse_time:
7566cdfca03SJohn Marino 			fprintf(ttyout, "Can't parse time `%s'.\n", timestr);
7576cdfca03SJohn Marino 			goto cleanup_parse_time;
7586cdfca03SJohn Marino 		}
7596cdfca03SJohn Marino 		timebuf.tm_isdst = -1;
7606cdfca03SJohn Marino 		rtime = timegm(&timebuf);
7616cdfca03SJohn Marino 		if (rtime == -1) {
7626cdfca03SJohn Marino 			if (noisy || ftp_debug != 0)
7636cdfca03SJohn Marino 				goto bad_parse_time;
7646cdfca03SJohn Marino 			else
7656cdfca03SJohn Marino 				goto cleanup_parse_time;
7666cdfca03SJohn Marino 		} else {
7676cdfca03SJohn Marino 			DPRINTF("remotemodtime: parsed time `%s' as " LLF
7686cdfca03SJohn Marino 			    ", %s",
7696cdfca03SJohn Marino 			    timestr, (LLT)rtime,
7706cdfca03SJohn Marino 			    rfc2822time(localtime(&rtime)));
7716cdfca03SJohn Marino 		}
7726cdfca03SJohn Marino 	} else {
7736cdfca03SJohn Marino 		if (r == ERROR && code == 500 && features[FEAT_MDTM] == -1)
7746cdfca03SJohn Marino 			features[FEAT_MDTM] = 0;
7756cdfca03SJohn Marino 		if (noisy && ftp_debug == 0) {
7766cdfca03SJohn Marino 			fputs(reply_string, ttyout);
7776cdfca03SJohn Marino 			putc('\n', ttyout);
7786cdfca03SJohn Marino 		}
7796cdfca03SJohn Marino 	}
7806cdfca03SJohn Marino  cleanup_parse_time:
7816cdfca03SJohn Marino 	verbose = overbose;
7826cdfca03SJohn Marino 	if (rtime == -1)
7836cdfca03SJohn Marino 		code = ocode;
7846cdfca03SJohn Marino 	return (rtime);
7856cdfca03SJohn Marino }
7866cdfca03SJohn Marino 
7876cdfca03SJohn Marino /*
7886cdfca03SJohn Marino  * Format tm in an RFC 2822 compatible manner, with a trailing \n.
7896cdfca03SJohn Marino  * Returns a pointer to a static string containing the result.
7906cdfca03SJohn Marino  */
7916cdfca03SJohn Marino const char *
rfc2822time(const struct tm * tm)7926cdfca03SJohn Marino rfc2822time(const struct tm *tm)
7936cdfca03SJohn Marino {
7946cdfca03SJohn Marino 	static char result[50];
7956cdfca03SJohn Marino 
7966cdfca03SJohn Marino 	if (strftime(result, sizeof(result),
7976cdfca03SJohn Marino 	    "%a, %d %b %Y %H:%M:%S %z\n", tm) == 0)
7986cdfca03SJohn Marino 		errx(1, "Can't convert RFC 2822 time: buffer too small");
7996cdfca03SJohn Marino 	return result;
8006cdfca03SJohn Marino }
8016cdfca03SJohn Marino 
8026cdfca03SJohn Marino /*
8036cdfca03SJohn Marino  * Parse HTTP-date as per RFC 2616.
8046cdfca03SJohn Marino  * Return a pointer to the next character of the consumed date string,
8056cdfca03SJohn Marino  * or NULL if failed.
8066cdfca03SJohn Marino  */
8076cdfca03SJohn Marino const char *
parse_rfc2616time(struct tm * parsed,const char * httpdate)8086cdfca03SJohn Marino parse_rfc2616time(struct tm *parsed, const char *httpdate)
8096cdfca03SJohn Marino {
8106cdfca03SJohn Marino 	const char *t;
8116cdfca03SJohn Marino #if defined(HAVE_SETLOCALE)
8126cdfca03SJohn Marino 	const char *curlocale;
8136cdfca03SJohn Marino 
8146cdfca03SJohn Marino 	/* The representation of %a depends on the current locale. */
8156cdfca03SJohn Marino 	curlocale = setlocale(LC_TIME, NULL);
8166cdfca03SJohn Marino 	(void)setlocale(LC_TIME, "C");
8176cdfca03SJohn Marino #endif
8186cdfca03SJohn Marino 								/* RFC 1123 */
8196cdfca03SJohn Marino 	if ((t = strptime(httpdate, "%a, %d %b %Y %H:%M:%S GMT", parsed)) ||
8206cdfca03SJohn Marino 								/* RFC 850 */
8216cdfca03SJohn Marino 	    (t = strptime(httpdate, "%a, %d-%b-%y %H:%M:%S GMT", parsed)) ||
8226cdfca03SJohn Marino 								/* asctime */
8236cdfca03SJohn Marino 	    (t = strptime(httpdate, "%a, %b %d %H:%M:%S %Y", parsed))) {
8246cdfca03SJohn Marino 		;			/* do nothing */
8256cdfca03SJohn Marino 	}
8266cdfca03SJohn Marino #if defined(HAVE_SETLOCALE)
8276cdfca03SJohn Marino 	(void)setlocale(LC_TIME, curlocale);
8286cdfca03SJohn Marino #endif
8296cdfca03SJohn Marino 	return t;
8306cdfca03SJohn Marino }
8316cdfca03SJohn Marino 
8326cdfca03SJohn Marino /*
8336cdfca03SJohn Marino  * Update global `localcwd', which contains the state of the local cwd
8346cdfca03SJohn Marino  */
8356cdfca03SJohn Marino void
updatelocalcwd(void)8366cdfca03SJohn Marino updatelocalcwd(void)
8376cdfca03SJohn Marino {
8386cdfca03SJohn Marino 
8396cdfca03SJohn Marino 	if (getcwd(localcwd, sizeof(localcwd)) == NULL)
8406cdfca03SJohn Marino 		localcwd[0] = '\0';
8416cdfca03SJohn Marino 	DPRINTF("updatelocalcwd: got `%s'\n", localcwd);
8426cdfca03SJohn Marino }
8436cdfca03SJohn Marino 
8446cdfca03SJohn Marino /*
8456cdfca03SJohn Marino  * Update global `remotecwd', which contains the state of the remote cwd
8466cdfca03SJohn Marino  */
8476cdfca03SJohn Marino void
updateremotecwd(void)8486cdfca03SJohn Marino updateremotecwd(void)
8496cdfca03SJohn Marino {
8506cdfca03SJohn Marino 	int	 overbose, ocode;
8516cdfca03SJohn Marino 	size_t	 i;
8526cdfca03SJohn Marino 	char	*cp;
8536cdfca03SJohn Marino 
854*3a184c67SAntonio Huete Jimenez 	remcwdvalid = 1;	/* whether it works or not, we are done */
8556cdfca03SJohn Marino 	overbose = verbose;
8566cdfca03SJohn Marino 	ocode = code;
8576cdfca03SJohn Marino 	if (ftp_debug == 0)
8586cdfca03SJohn Marino 		verbose = -1;
8596cdfca03SJohn Marino 	if (command("PWD") != COMPLETE)
8606cdfca03SJohn Marino 		goto badremotecwd;
8616cdfca03SJohn Marino 	cp = strchr(reply_string, ' ');
8626cdfca03SJohn Marino 	if (cp == NULL || cp[0] == '\0' || cp[1] != '"')
8636cdfca03SJohn Marino 		goto badremotecwd;
8646cdfca03SJohn Marino 	cp += 2;
8656cdfca03SJohn Marino 	for (i = 0; *cp && i < sizeof(remotecwd) - 1; i++, cp++) {
8666cdfca03SJohn Marino 		if (cp[0] == '"') {
8676cdfca03SJohn Marino 			if (cp[1] == '"')
8686cdfca03SJohn Marino 				cp++;
8696cdfca03SJohn Marino 			else
8706cdfca03SJohn Marino 				break;
8716cdfca03SJohn Marino 		}
8726cdfca03SJohn Marino 		remotecwd[i] = *cp;
8736cdfca03SJohn Marino 	}
8746cdfca03SJohn Marino 	remotecwd[i] = '\0';
8756cdfca03SJohn Marino 	DPRINTF("updateremotecwd: got `%s'\n", remotecwd);
8766cdfca03SJohn Marino 	goto cleanupremotecwd;
8776cdfca03SJohn Marino  badremotecwd:
8786cdfca03SJohn Marino 	remotecwd[0]='\0';
8796cdfca03SJohn Marino  cleanupremotecwd:
8806cdfca03SJohn Marino 	verbose = overbose;
8816cdfca03SJohn Marino 	code = ocode;
8826cdfca03SJohn Marino }
8836cdfca03SJohn Marino 
8846cdfca03SJohn Marino /*
8856cdfca03SJohn Marino  * Ensure file is in or under dir.
8866cdfca03SJohn Marino  * Returns 1 if so, 0 if not (or an error occurred).
8876cdfca03SJohn Marino  */
8886cdfca03SJohn Marino int
fileindir(const char * file,const char * dir)8896cdfca03SJohn Marino fileindir(const char *file, const char *dir)
8906cdfca03SJohn Marino {
8916cdfca03SJohn Marino 	char	parentdirbuf[PATH_MAX+1], *parentdir;
8926cdfca03SJohn Marino 	char	realdir[PATH_MAX+1];
8936cdfca03SJohn Marino 	size_t	dirlen;
8946cdfca03SJohn Marino 
8956cdfca03SJohn Marino 					/* determine parent directory of file */
8966cdfca03SJohn Marino 	(void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf));
8976cdfca03SJohn Marino 	parentdir = dirname(parentdirbuf);
8986cdfca03SJohn Marino 	if (strcmp(parentdir, ".") == 0)
8996cdfca03SJohn Marino 		return 1;		/* current directory is ok */
9006cdfca03SJohn Marino 
9016cdfca03SJohn Marino 					/* find the directory */
9026cdfca03SJohn Marino 	if (realpath(parentdir, realdir) == NULL) {
9036cdfca03SJohn Marino 		warn("Unable to determine real path of `%s'", parentdir);
9046cdfca03SJohn Marino 		return 0;
9056cdfca03SJohn Marino 	}
9066cdfca03SJohn Marino 	if (realdir[0] != '/')		/* relative result is ok */
9076cdfca03SJohn Marino 		return 1;
9086cdfca03SJohn Marino 	dirlen = strlen(dir);
9096cdfca03SJohn Marino 	if (strncmp(realdir, dir, dirlen) == 0 &&
9106cdfca03SJohn Marino 	    (realdir[dirlen] == '/' || realdir[dirlen] == '\0'))
9116cdfca03SJohn Marino 		return 1;
9126cdfca03SJohn Marino 	return 0;
9136cdfca03SJohn Marino }
9146cdfca03SJohn Marino 
9156cdfca03SJohn Marino /*
9166cdfca03SJohn Marino  * List words in stringlist, vertically arranged
9176cdfca03SJohn Marino  */
9186cdfca03SJohn Marino void
list_vertical(StringList * sl)9196cdfca03SJohn Marino list_vertical(StringList *sl)
9206cdfca03SJohn Marino {
9216cdfca03SJohn Marino 	size_t i, j;
9226cdfca03SJohn Marino 	size_t columns, lines;
9236cdfca03SJohn Marino 	char *p;
9246cdfca03SJohn Marino 	size_t w, width;
9256cdfca03SJohn Marino 
9266cdfca03SJohn Marino 	width = 0;
9276cdfca03SJohn Marino 
9286cdfca03SJohn Marino 	for (i = 0 ; i < sl->sl_cur ; i++) {
9296cdfca03SJohn Marino 		w = strlen(sl->sl_str[i]);
9306cdfca03SJohn Marino 		if (w > width)
9316cdfca03SJohn Marino 			width = w;
9326cdfca03SJohn Marino 	}
9336cdfca03SJohn Marino 	width = (width + 8) &~ 7;
9346cdfca03SJohn Marino 
9356cdfca03SJohn Marino 	columns = ttywidth / width;
9366cdfca03SJohn Marino 	if (columns == 0)
9376cdfca03SJohn Marino 		columns = 1;
9386cdfca03SJohn Marino 	lines = (sl->sl_cur + columns - 1) / columns;
9396cdfca03SJohn Marino 	for (i = 0; i < lines; i++) {
9406cdfca03SJohn Marino 		for (j = 0; j < columns; j++) {
9416cdfca03SJohn Marino 			p = sl->sl_str[j * lines + i];
9426cdfca03SJohn Marino 			if (p)
9436cdfca03SJohn Marino 				fputs(p, ttyout);
9446cdfca03SJohn Marino 			if (j * lines + i + lines >= sl->sl_cur) {
9456cdfca03SJohn Marino 				putc('\n', ttyout);
9466cdfca03SJohn Marino 				break;
9476cdfca03SJohn Marino 			}
9486cdfca03SJohn Marino 			if (p) {
9496cdfca03SJohn Marino 				w = strlen(p);
9506cdfca03SJohn Marino 				while (w < width) {
9516cdfca03SJohn Marino 					w = (w + 8) &~ 7;
9526cdfca03SJohn Marino 					(void)putc('\t', ttyout);
9536cdfca03SJohn Marino 				}
9546cdfca03SJohn Marino 			}
9556cdfca03SJohn Marino 		}
9566cdfca03SJohn Marino 	}
9576cdfca03SJohn Marino }
9586cdfca03SJohn Marino 
9596cdfca03SJohn Marino /*
9606cdfca03SJohn Marino  * Update the global ttywidth value, using TIOCGWINSZ.
9616cdfca03SJohn Marino  */
9626cdfca03SJohn Marino void
setttywidth(int a)9636cdfca03SJohn Marino setttywidth(int a)
9646cdfca03SJohn Marino {
9656cdfca03SJohn Marino 	struct winsize winsize;
9666cdfca03SJohn Marino 	int oerrno = errno;
9676cdfca03SJohn Marino 
9686cdfca03SJohn Marino 	if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1 &&
9696cdfca03SJohn Marino 	    winsize.ws_col != 0)
9706cdfca03SJohn Marino 		ttywidth = winsize.ws_col;
9716cdfca03SJohn Marino 	else
9726cdfca03SJohn Marino 		ttywidth = 80;
9736cdfca03SJohn Marino 	errno = oerrno;
9746cdfca03SJohn Marino }
9756cdfca03SJohn Marino 
9766cdfca03SJohn Marino /*
9776cdfca03SJohn Marino  * Change the rate limit up (SIGUSR1) or down (SIGUSR2)
9786cdfca03SJohn Marino  */
9796cdfca03SJohn Marino void
crankrate(int sig)9806cdfca03SJohn Marino crankrate(int sig)
9816cdfca03SJohn Marino {
9826cdfca03SJohn Marino 
9836cdfca03SJohn Marino 	switch (sig) {
9846cdfca03SJohn Marino 	case SIGUSR1:
9856cdfca03SJohn Marino 		if (rate_get)
9866cdfca03SJohn Marino 			rate_get += rate_get_incr;
9876cdfca03SJohn Marino 		if (rate_put)
9886cdfca03SJohn Marino 			rate_put += rate_put_incr;
9896cdfca03SJohn Marino 		break;
9906cdfca03SJohn Marino 	case SIGUSR2:
9916cdfca03SJohn Marino 		if (rate_get && rate_get > rate_get_incr)
9926cdfca03SJohn Marino 			rate_get -= rate_get_incr;
9936cdfca03SJohn Marino 		if (rate_put && rate_put > rate_put_incr)
9946cdfca03SJohn Marino 			rate_put -= rate_put_incr;
9956cdfca03SJohn Marino 		break;
9966cdfca03SJohn Marino 	default:
9976cdfca03SJohn Marino 		err(1, "crankrate invoked with unknown signal: %d", sig);
9986cdfca03SJohn Marino 	}
9996cdfca03SJohn Marino }
10006cdfca03SJohn Marino 
10016cdfca03SJohn Marino 
10026cdfca03SJohn Marino /*
10036cdfca03SJohn Marino  * Setup or cleanup EditLine structures
10046cdfca03SJohn Marino  */
10056cdfca03SJohn Marino #ifndef NO_EDITCOMPLETE
10066cdfca03SJohn Marino void
controlediting(void)10076cdfca03SJohn Marino controlediting(void)
10086cdfca03SJohn Marino {
10096cdfca03SJohn Marino 	if (editing && el == NULL && hist == NULL) {
10106cdfca03SJohn Marino 		HistEvent ev;
10116cdfca03SJohn Marino 		int editmode;
10126cdfca03SJohn Marino 
10136cdfca03SJohn Marino 		el = el_init(getprogname(), stdin, ttyout, stderr);
10146cdfca03SJohn Marino 		/* init editline */
10156cdfca03SJohn Marino 		hist = history_init();		/* init the builtin history */
10166cdfca03SJohn Marino 		history(hist, &ev, H_SETSIZE, 100);/* remember 100 events */
10176cdfca03SJohn Marino 		el_set(el, EL_HIST, history, hist);	/* use history */
10186cdfca03SJohn Marino 
10196cdfca03SJohn Marino 		el_set(el, EL_EDITOR, "emacs");	/* default editor is emacs */
10206cdfca03SJohn Marino 		el_set(el, EL_PROMPT, prompt);	/* set the prompt functions */
10216cdfca03SJohn Marino 		el_set(el, EL_RPROMPT, rprompt);
10226cdfca03SJohn Marino 
10236cdfca03SJohn Marino 		/* add local file completion, bind to TAB */
10246cdfca03SJohn Marino 		el_set(el, EL_ADDFN, "ftp-complete",
10256cdfca03SJohn Marino 		    "Context sensitive argument completion",
10266cdfca03SJohn Marino 		    complete);
10276cdfca03SJohn Marino 		el_set(el, EL_BIND, "^I", "ftp-complete", NULL);
10286cdfca03SJohn Marino 		el_source(el, NULL);	/* read ~/.editrc */
10296cdfca03SJohn Marino 		if ((el_get(el, EL_EDITMODE, &editmode) != -1) && editmode == 0)
10306cdfca03SJohn Marino 			editing = 0;	/* the user doesn't want editing,
10316cdfca03SJohn Marino 					 * so disable, and let statement
10326cdfca03SJohn Marino 					 * below cleanup */
10336cdfca03SJohn Marino 		else
10346cdfca03SJohn Marino 			el_set(el, EL_SIGNAL, 1);
10356cdfca03SJohn Marino 	}
10366cdfca03SJohn Marino 	if (!editing) {
10376cdfca03SJohn Marino 		if (hist) {
10386cdfca03SJohn Marino 			history_end(hist);
10396cdfca03SJohn Marino 			hist = NULL;
10406cdfca03SJohn Marino 		}
10416cdfca03SJohn Marino 		if (el) {
10426cdfca03SJohn Marino 			el_end(el);
10436cdfca03SJohn Marino 			el = NULL;
10446cdfca03SJohn Marino 		}
10456cdfca03SJohn Marino 	}
10466cdfca03SJohn Marino }
10476cdfca03SJohn Marino #endif /* !NO_EDITCOMPLETE */
10486cdfca03SJohn Marino 
10496cdfca03SJohn Marino /*
10506cdfca03SJohn Marino  * Convert the string `arg' to an int, which may have an optional SI suffix
10516cdfca03SJohn Marino  * (`b', `k', `m', `g'). Returns the number for success, -1 otherwise.
10526cdfca03SJohn Marino  */
10536cdfca03SJohn Marino int
strsuftoi(const char * arg)10546cdfca03SJohn Marino strsuftoi(const char *arg)
10556cdfca03SJohn Marino {
10566cdfca03SJohn Marino 	char *cp;
10576cdfca03SJohn Marino 	long val;
10586cdfca03SJohn Marino 
10596cdfca03SJohn Marino 	if (!isdigit((unsigned char)arg[0]))
10606cdfca03SJohn Marino 		return (-1);
10616cdfca03SJohn Marino 
10626cdfca03SJohn Marino 	val = strtol(arg, &cp, 10);
10636cdfca03SJohn Marino 	if (cp != NULL) {
10646cdfca03SJohn Marino 		if (cp[0] != '\0' && cp[1] != '\0')
10656cdfca03SJohn Marino 			 return (-1);
10666cdfca03SJohn Marino 		switch (tolower((unsigned char)cp[0])) {
10676cdfca03SJohn Marino 		case '\0':
10686cdfca03SJohn Marino 		case 'b':
10696cdfca03SJohn Marino 			break;
10706cdfca03SJohn Marino 		case 'k':
10716cdfca03SJohn Marino 			val <<= 10;
10726cdfca03SJohn Marino 			break;
10736cdfca03SJohn Marino 		case 'm':
10746cdfca03SJohn Marino 			val <<= 20;
10756cdfca03SJohn Marino 			break;
10766cdfca03SJohn Marino 		case 'g':
10776cdfca03SJohn Marino 			val <<= 30;
10786cdfca03SJohn Marino 			break;
10796cdfca03SJohn Marino 		default:
10806cdfca03SJohn Marino 			return (-1);
10816cdfca03SJohn Marino 		}
10826cdfca03SJohn Marino 	}
10836cdfca03SJohn Marino 	if (val < 0 || val > INT_MAX)
10846cdfca03SJohn Marino 		return (-1);
10856cdfca03SJohn Marino 
10866cdfca03SJohn Marino 	return (val);
10876cdfca03SJohn Marino }
10886cdfca03SJohn Marino 
10896cdfca03SJohn Marino /*
10906cdfca03SJohn Marino  * Set up socket buffer sizes before a connection is made.
10916cdfca03SJohn Marino  */
10926cdfca03SJohn Marino void
setupsockbufsize(int sock)10936cdfca03SJohn Marino setupsockbufsize(int sock)
10946cdfca03SJohn Marino {
10956cdfca03SJohn Marino 	socklen_t slen;
10966cdfca03SJohn Marino 
10976cdfca03SJohn Marino 	if (0 == rcvbuf_size) {
10986cdfca03SJohn Marino 		slen = sizeof(rcvbuf_size);
10996cdfca03SJohn Marino 		if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF,
11006cdfca03SJohn Marino 		    (void *)&rcvbuf_size, &slen) == -1)
11016cdfca03SJohn Marino 			err(1, "Unable to determine rcvbuf size");
11026cdfca03SJohn Marino 		if (rcvbuf_size <= 0)
11036cdfca03SJohn Marino 			rcvbuf_size = 8 * 1024;
11046cdfca03SJohn Marino 		if (rcvbuf_size > 8 * 1024 * 1024)
11056cdfca03SJohn Marino 			rcvbuf_size = 8 * 1024 * 1024;
11066cdfca03SJohn Marino 		DPRINTF("setupsockbufsize: rcvbuf_size determined as %d\n",
11076cdfca03SJohn Marino 		    rcvbuf_size);
11086cdfca03SJohn Marino 	}
11096cdfca03SJohn Marino 	if (0 == sndbuf_size) {
11106cdfca03SJohn Marino 		slen = sizeof(sndbuf_size);
11116cdfca03SJohn Marino 		if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF,
11126cdfca03SJohn Marino 		    (void *)&sndbuf_size, &slen) == -1)
11136cdfca03SJohn Marino 			err(1, "Unable to determine sndbuf size");
11146cdfca03SJohn Marino 		if (sndbuf_size <= 0)
11156cdfca03SJohn Marino 			sndbuf_size = 8 * 1024;
11166cdfca03SJohn Marino 		if (sndbuf_size > 8 * 1024 * 1024)
11176cdfca03SJohn Marino 			sndbuf_size = 8 * 1024 * 1024;
11186cdfca03SJohn Marino 		DPRINTF("setupsockbufsize: sndbuf_size determined as %d\n",
11196cdfca03SJohn Marino 		    sndbuf_size);
11206cdfca03SJohn Marino 	}
11216cdfca03SJohn Marino 
11226cdfca03SJohn Marino 	if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
11236cdfca03SJohn Marino 	    (void *)&sndbuf_size, sizeof(sndbuf_size)) == -1)
11246cdfca03SJohn Marino 		warn("Unable to set sndbuf size %d", sndbuf_size);
11256cdfca03SJohn Marino 
11266cdfca03SJohn Marino 	if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
11276cdfca03SJohn Marino 	    (void *)&rcvbuf_size, sizeof(rcvbuf_size)) == -1)
11286cdfca03SJohn Marino 		warn("Unable to set rcvbuf size %d", rcvbuf_size);
11296cdfca03SJohn Marino }
11306cdfca03SJohn Marino 
11316cdfca03SJohn Marino /*
11326cdfca03SJohn Marino  * Copy characters from src into dst, \ quoting characters that require it
11336cdfca03SJohn Marino  */
11346cdfca03SJohn Marino void
ftpvis(char * dst,size_t dstlen,const char * src,size_t srclen)11356cdfca03SJohn Marino ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen)
11366cdfca03SJohn Marino {
11376cdfca03SJohn Marino 	size_t	di, si;
11386cdfca03SJohn Marino 
11396cdfca03SJohn Marino 	di = si = 0;
11406cdfca03SJohn Marino 	while (src[si] != '\0' && di < dstlen && si < srclen) {
11416cdfca03SJohn Marino 		switch (src[si]) {
11426cdfca03SJohn Marino 		case '\\':
11436cdfca03SJohn Marino 		case ' ':
11446cdfca03SJohn Marino 		case '\t':
11456cdfca03SJohn Marino 		case '\r':
11466cdfca03SJohn Marino 		case '\n':
11476cdfca03SJohn Marino 		case '"':
11486cdfca03SJohn Marino 			/*
11496cdfca03SJohn Marino 			 * Need room for two characters and NUL, avoiding
11506cdfca03SJohn Marino 			 * incomplete escape sequences at end of dst.
11516cdfca03SJohn Marino 			 */
11526cdfca03SJohn Marino 			if (di >= dstlen - 3)
11536cdfca03SJohn Marino 				break;
11546cdfca03SJohn Marino 			dst[di++] = '\\';
11556cdfca03SJohn Marino 			/* FALLTHROUGH */
11566cdfca03SJohn Marino 		default:
11576cdfca03SJohn Marino 			dst[di] = src[si++];
11586cdfca03SJohn Marino 			if (di < dstlen)
11596cdfca03SJohn Marino 				di++;
11606cdfca03SJohn Marino 		}
11616cdfca03SJohn Marino 	}
11626cdfca03SJohn Marino 	dst[di] = '\0';
11636cdfca03SJohn Marino }
11646cdfca03SJohn Marino 
11656cdfca03SJohn Marino /*
11666cdfca03SJohn Marino  * Copy src into buf (which is len bytes long), expanding % sequences.
11676cdfca03SJohn Marino  */
11686cdfca03SJohn Marino void
formatbuf(char * buf,size_t len,const char * src)11696cdfca03SJohn Marino formatbuf(char *buf, size_t len, const char *src)
11706cdfca03SJohn Marino {
11716cdfca03SJohn Marino 	const char	*p, *p2, *q;
11726cdfca03SJohn Marino 	size_t		 i;
11736cdfca03SJohn Marino 	int		 op, updirs, pdirs;
11746cdfca03SJohn Marino 
11756cdfca03SJohn Marino #define ADDBUF(x) do { \
11766cdfca03SJohn Marino 		if (i >= len - 1) \
11776cdfca03SJohn Marino 			goto endbuf; \
11786cdfca03SJohn Marino 		buf[i++] = (x); \
11796cdfca03SJohn Marino 	} while (0)
11806cdfca03SJohn Marino 
11816cdfca03SJohn Marino 	p = src;
11826cdfca03SJohn Marino 	for (i = 0; *p; p++) {
11836cdfca03SJohn Marino 		if (*p != '%') {
11846cdfca03SJohn Marino 			ADDBUF(*p);
11856cdfca03SJohn Marino 			continue;
11866cdfca03SJohn Marino 		}
11876cdfca03SJohn Marino 		p++;
11886cdfca03SJohn Marino 
11896cdfca03SJohn Marino 		switch (op = *p) {
11906cdfca03SJohn Marino 
11916cdfca03SJohn Marino 		case '/':
11926cdfca03SJohn Marino 		case '.':
11936cdfca03SJohn Marino 		case 'c':
1194*3a184c67SAntonio Huete Jimenez 			if (connected && !remcwdvalid)
1195*3a184c67SAntonio Huete Jimenez 				updateremotecwd();
11966cdfca03SJohn Marino 			p2 = connected ? remotecwd : "";
11976cdfca03SJohn Marino 			updirs = pdirs = 0;
11986cdfca03SJohn Marino 
11996cdfca03SJohn Marino 			/* option to determine fixed # of dirs from path */
12006cdfca03SJohn Marino 			if (op == '.' || op == 'c') {
12016cdfca03SJohn Marino 				int skip;
12026cdfca03SJohn Marino 
12036cdfca03SJohn Marino 				q = p2;
12046cdfca03SJohn Marino 				while (*p2)		/* calc # of /'s */
12056cdfca03SJohn Marino 					if (*p2++ == '/')
12066cdfca03SJohn Marino 						updirs++;
12076cdfca03SJohn Marino 				if (p[1] == '0') {	/* print <x> or ... */
12086cdfca03SJohn Marino 					pdirs = 1;
12096cdfca03SJohn Marino 					p++;
12106cdfca03SJohn Marino 				}
12116cdfca03SJohn Marino 				if (p[1] >= '1' && p[1] <= '9') {
12126cdfca03SJohn Marino 							/* calc # to skip  */
12136cdfca03SJohn Marino 					skip = p[1] - '0';
12146cdfca03SJohn Marino 					p++;
12156cdfca03SJohn Marino 				} else
12166cdfca03SJohn Marino 					skip = 1;
12176cdfca03SJohn Marino 
12186cdfca03SJohn Marino 				updirs -= skip;
12196cdfca03SJohn Marino 				while (skip-- > 0) {
12206cdfca03SJohn Marino 					while ((p2 > q) && (*p2 != '/'))
12216cdfca03SJohn Marino 						p2--;	/* back up */
12226cdfca03SJohn Marino 					if (skip && p2 > q)
12236cdfca03SJohn Marino 						p2--;
12246cdfca03SJohn Marino 				}
12256cdfca03SJohn Marino 				if (*p2 == '/' && p2 != q)
12266cdfca03SJohn Marino 					p2++;
12276cdfca03SJohn Marino 			}
12286cdfca03SJohn Marino 
12296cdfca03SJohn Marino 			if (updirs > 0 && pdirs) {
12306cdfca03SJohn Marino 				if (i >= len - 5)
12316cdfca03SJohn Marino 					break;
12326cdfca03SJohn Marino 				if (op == '.') {
12336cdfca03SJohn Marino 					ADDBUF('.');
12346cdfca03SJohn Marino 					ADDBUF('.');
12356cdfca03SJohn Marino 					ADDBUF('.');
12366cdfca03SJohn Marino 				} else {
12376cdfca03SJohn Marino 					ADDBUF('/');
12386cdfca03SJohn Marino 					ADDBUF('<');
12396cdfca03SJohn Marino 					if (updirs > 9) {
12406cdfca03SJohn Marino 						ADDBUF('9');
12416cdfca03SJohn Marino 						ADDBUF('+');
12426cdfca03SJohn Marino 					} else
12436cdfca03SJohn Marino 						ADDBUF('0' + updirs);
12446cdfca03SJohn Marino 					ADDBUF('>');
12456cdfca03SJohn Marino 				}
12466cdfca03SJohn Marino 			}
12476cdfca03SJohn Marino 			for (; *p2; p2++)
12486cdfca03SJohn Marino 				ADDBUF(*p2);
12496cdfca03SJohn Marino 			break;
12506cdfca03SJohn Marino 
12516cdfca03SJohn Marino 		case 'M':
12526cdfca03SJohn Marino 		case 'm':
12536cdfca03SJohn Marino 			for (p2 = connected && hostname ? hostname : "-";
12546cdfca03SJohn Marino 			    *p2 ; p2++) {
12556cdfca03SJohn Marino 				if (op == 'm' && *p2 == '.')
12566cdfca03SJohn Marino 					break;
12576cdfca03SJohn Marino 				ADDBUF(*p2);
12586cdfca03SJohn Marino 			}
12596cdfca03SJohn Marino 			break;
12606cdfca03SJohn Marino 
12616cdfca03SJohn Marino 		case 'n':
12626cdfca03SJohn Marino 			for (p2 = connected ? username : "-"; *p2 ; p2++)
12636cdfca03SJohn Marino 				ADDBUF(*p2);
12646cdfca03SJohn Marino 			break;
12656cdfca03SJohn Marino 
12666cdfca03SJohn Marino 		case '%':
12676cdfca03SJohn Marino 			ADDBUF('%');
12686cdfca03SJohn Marino 			break;
12696cdfca03SJohn Marino 
12706cdfca03SJohn Marino 		default:		/* display unknown codes literally */
12716cdfca03SJohn Marino 			ADDBUF('%');
12726cdfca03SJohn Marino 			ADDBUF(op);
12736cdfca03SJohn Marino 			break;
12746cdfca03SJohn Marino 
12756cdfca03SJohn Marino 		}
12766cdfca03SJohn Marino 	}
12776cdfca03SJohn Marino  endbuf:
12786cdfca03SJohn Marino 	buf[i] = '\0';
12796cdfca03SJohn Marino }
12806cdfca03SJohn Marino 
12816cdfca03SJohn Marino /*
12826cdfca03SJohn Marino  * Determine if given string is an IPv6 address or not.
12836cdfca03SJohn Marino  * Return 1 for yes, 0 for no
12846cdfca03SJohn Marino  */
12856cdfca03SJohn Marino int
isipv6addr(const char * addr)12866cdfca03SJohn Marino isipv6addr(const char *addr)
12876cdfca03SJohn Marino {
12886cdfca03SJohn Marino 	int rv = 0;
12896cdfca03SJohn Marino #ifdef INET6
12906cdfca03SJohn Marino 	struct addrinfo hints, *res;
12916cdfca03SJohn Marino 
12926cdfca03SJohn Marino 	memset(&hints, 0, sizeof(hints));
12936cdfca03SJohn Marino 	hints.ai_family = AF_INET6;
12946cdfca03SJohn Marino 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
12956cdfca03SJohn Marino 	hints.ai_flags = AI_NUMERICHOST;
12966cdfca03SJohn Marino 	if (getaddrinfo(addr, "0", &hints, &res) != 0)
12976cdfca03SJohn Marino 		rv = 0;
12986cdfca03SJohn Marino 	else {
12996cdfca03SJohn Marino 		rv = 1;
13006cdfca03SJohn Marino 		freeaddrinfo(res);
13016cdfca03SJohn Marino 	}
13026cdfca03SJohn Marino 	DPRINTF("isipv6addr: got %d for %s\n", rv, addr);
13036cdfca03SJohn Marino #endif
13046cdfca03SJohn Marino 	return (rv == 1) ? 1 : 0;
13056cdfca03SJohn Marino }
13066cdfca03SJohn Marino 
13076cdfca03SJohn Marino /*
13086cdfca03SJohn Marino  * Read a line from the FILE stream into buf/buflen using fgets(), so up
13096cdfca03SJohn Marino  * to buflen-1 chars will be read and the result will be NUL terminated.
13106cdfca03SJohn Marino  * If the line has a trailing newline it will be removed.
13116cdfca03SJohn Marino  * If the line is too long, excess characters will be read until
13126cdfca03SJohn Marino  * newline/EOF/error.
13136cdfca03SJohn Marino  * If EOF/error occurs or a too-long line is encountered and errormsg
13146cdfca03SJohn Marino  * isn't NULL, it will be changed to a description of the problem.
13156cdfca03SJohn Marino  * (The EOF message has a leading \n for cosmetic purposes).
13166cdfca03SJohn Marino  * Returns:
13176cdfca03SJohn Marino  *	>=0	length of line (excluding trailing newline) if all ok
13186cdfca03SJohn Marino  *	-1	error occurred
13196cdfca03SJohn Marino  *	-2	EOF encountered
13206cdfca03SJohn Marino  *	-3	line was too long
13216cdfca03SJohn Marino  */
13226cdfca03SJohn Marino int
get_line(FILE * stream,char * buf,size_t buflen,const char ** errormsg)13236cdfca03SJohn Marino get_line(FILE *stream, char *buf, size_t buflen, const char **errormsg)
13246cdfca03SJohn Marino {
13256cdfca03SJohn Marino 	int	rv, ch;
13266cdfca03SJohn Marino 	size_t	len;
13276cdfca03SJohn Marino 
13286cdfca03SJohn Marino 	if (fgets(buf, buflen, stream) == NULL) {
13296cdfca03SJohn Marino 		if (feof(stream)) {	/* EOF */
13306cdfca03SJohn Marino 			rv = -2;
13316cdfca03SJohn Marino 			if (errormsg)
13326cdfca03SJohn Marino 				*errormsg = "\nEOF received";
13336cdfca03SJohn Marino 		} else  {		/* error */
13346cdfca03SJohn Marino 			rv = -1;
13356cdfca03SJohn Marino 			if (errormsg)
13366cdfca03SJohn Marino 				*errormsg = "Error encountered";
13376cdfca03SJohn Marino 		}
13386cdfca03SJohn Marino 		clearerr(stream);
13396cdfca03SJohn Marino 		return rv;
13406cdfca03SJohn Marino 	}
13416cdfca03SJohn Marino 	len = strlen(buf);
13426cdfca03SJohn Marino 	if (buf[len-1] == '\n') {	/* clear any trailing newline */
13436cdfca03SJohn Marino 		buf[--len] = '\0';
13446cdfca03SJohn Marino 	} else if (len == buflen-1) {	/* line too long */
13456cdfca03SJohn Marino 		while ((ch = getchar()) != '\n' && ch != EOF)
13466cdfca03SJohn Marino 			continue;
13476cdfca03SJohn Marino 		if (errormsg)
13486cdfca03SJohn Marino 			*errormsg = "Input line is too long";
13496cdfca03SJohn Marino 		clearerr(stream);
13506cdfca03SJohn Marino 		return -3;
13516cdfca03SJohn Marino 	}
13526cdfca03SJohn Marino 	if (errormsg)
13536cdfca03SJohn Marino 		*errormsg = NULL;
13546cdfca03SJohn Marino 	return len;
13556cdfca03SJohn Marino }
13566cdfca03SJohn Marino 
13576cdfca03SJohn Marino /*
13586cdfca03SJohn Marino  * Internal version of connect(2); sets socket buffer sizes,
13596cdfca03SJohn Marino  * binds to a specific local address (if set), and
13606cdfca03SJohn Marino  * supports a connection timeout using a non-blocking connect(2) with
13616cdfca03SJohn Marino  * a poll(2).
13626cdfca03SJohn Marino  * Socket fcntl flags are temporarily updated to include O_NONBLOCK;
13636cdfca03SJohn Marino  * these will not be reverted on connection failure.
13646cdfca03SJohn Marino  * Returns 0 on success, or -1 upon failure (with an appropriate
13656cdfca03SJohn Marino  * error message displayed.)
13666cdfca03SJohn Marino  */
13676cdfca03SJohn Marino int
ftp_connect(int sock,const struct sockaddr * name,socklen_t namelen,int pe)13686cdfca03SJohn Marino ftp_connect(int sock, const struct sockaddr *name, socklen_t namelen, int pe)
13696cdfca03SJohn Marino {
13706cdfca03SJohn Marino 	int		flags, rv, timeout, error;
13716cdfca03SJohn Marino 	socklen_t	slen;
13726cdfca03SJohn Marino 	struct timeval	endtime, now, td;
13736cdfca03SJohn Marino 	struct pollfd	pfd[1];
13746cdfca03SJohn Marino 	char		hname[NI_MAXHOST];
13756cdfca03SJohn Marino 	char		sname[NI_MAXSERV];
13766cdfca03SJohn Marino 
13776cdfca03SJohn Marino 	setupsockbufsize(sock);
13786cdfca03SJohn Marino 	if (getnameinfo(name, namelen,
13796cdfca03SJohn Marino 	    hname, sizeof(hname), sname, sizeof(sname),
13806cdfca03SJohn Marino 	    NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
13816cdfca03SJohn Marino 		strlcpy(hname, "?", sizeof(hname));
13826cdfca03SJohn Marino 		strlcpy(sname, "?", sizeof(sname));
13836cdfca03SJohn Marino 	}
13846cdfca03SJohn Marino 
13856cdfca03SJohn Marino 	if (bindai != NULL) {			/* bind to specific addr */
13866cdfca03SJohn Marino 		struct addrinfo *ai;
13876cdfca03SJohn Marino 
13886cdfca03SJohn Marino 		for (ai = bindai; ai != NULL; ai = ai->ai_next) {
13896cdfca03SJohn Marino 			if (ai->ai_family == name->sa_family)
13906cdfca03SJohn Marino 				break;
13916cdfca03SJohn Marino 		}
13926cdfca03SJohn Marino 		if (ai == NULL)
13936cdfca03SJohn Marino 			ai = bindai;
13946cdfca03SJohn Marino 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
13956cdfca03SJohn Marino 			char	bname[NI_MAXHOST];
13966cdfca03SJohn Marino 			int	saveerr;
13976cdfca03SJohn Marino 
13986cdfca03SJohn Marino 			saveerr = errno;
13996cdfca03SJohn Marino 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
14006cdfca03SJohn Marino 			    bname, sizeof(bname), NULL, 0, NI_NUMERICHOST) != 0)
14016cdfca03SJohn Marino 				strlcpy(bname, "?", sizeof(bname));
14026cdfca03SJohn Marino 			errno = saveerr;
14036cdfca03SJohn Marino 			warn("Can't bind to `%s'", bname);
14046cdfca03SJohn Marino 			return -1;
14056cdfca03SJohn Marino 		}
14066cdfca03SJohn Marino 	}
14076cdfca03SJohn Marino 
14086cdfca03SJohn Marino 						/* save current socket flags */
14096cdfca03SJohn Marino 	if ((flags = fcntl(sock, F_GETFL, 0)) == -1) {
14106cdfca03SJohn Marino 		warn("Can't %s socket flags for connect to `%s:%s'",
14116cdfca03SJohn Marino 		    "save", hname, sname);
14126cdfca03SJohn Marino 		return -1;
14136cdfca03SJohn Marino 	}
14146cdfca03SJohn Marino 						/* set non-blocking connect */
14156cdfca03SJohn Marino 	if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) {
14166cdfca03SJohn Marino 		warn("Can't set socket non-blocking for connect to `%s:%s'",
14176cdfca03SJohn Marino 		    hname, sname);
14186cdfca03SJohn Marino 		return -1;
14196cdfca03SJohn Marino 	}
14206cdfca03SJohn Marino 
14216cdfca03SJohn Marino 	/* NOTE: we now must restore socket flags on successful exit */
14226cdfca03SJohn Marino 
14236cdfca03SJohn Marino 	pfd[0].fd = sock;
14246cdfca03SJohn Marino 	pfd[0].events = POLLIN|POLLOUT;
14256cdfca03SJohn Marino 
14266cdfca03SJohn Marino 	if (quit_time > 0) {			/* want a non default timeout */
14276cdfca03SJohn Marino 		(void)gettimeofday(&endtime, NULL);
14286cdfca03SJohn Marino 		endtime.tv_sec += quit_time;	/* determine end time */
14296cdfca03SJohn Marino 	}
14306cdfca03SJohn Marino 
14316cdfca03SJohn Marino 	rv = connect(sock, name, namelen);	/* inititate the connection */
14326cdfca03SJohn Marino 	if (rv == -1) {				/* connection error */
14336cdfca03SJohn Marino 		if (errno != EINPROGRESS) {	/* error isn't "please wait" */
14346cdfca03SJohn Marino 			if (pe || (errno != EHOSTUNREACH))
14356cdfca03SJohn Marino  connecterror:
14366cdfca03SJohn Marino 				warn("Can't connect to `%s:%s'", hname, sname);
14376cdfca03SJohn Marino 			return -1;
14386cdfca03SJohn Marino 		}
14396cdfca03SJohn Marino 
14406cdfca03SJohn Marino 						/* connect EINPROGRESS; wait */
14416cdfca03SJohn Marino 		do {
14426cdfca03SJohn Marino 			if (quit_time > 0) {	/* determine timeout */
14436cdfca03SJohn Marino 				(void)gettimeofday(&now, NULL);
14446cdfca03SJohn Marino 				timersub(&endtime, &now, &td);
14456cdfca03SJohn Marino 				timeout = td.tv_sec * 1000 + td.tv_usec/1000;
14466cdfca03SJohn Marino 				if (timeout < 0)
14476cdfca03SJohn Marino 					timeout = 0;
14486cdfca03SJohn Marino 			} else {
14496cdfca03SJohn Marino 				timeout = INFTIM;
14506cdfca03SJohn Marino 			}
14516cdfca03SJohn Marino 			pfd[0].revents = 0;
14526cdfca03SJohn Marino 			rv = ftp_poll(pfd, 1, timeout);
14536cdfca03SJohn Marino 						/* loop until poll ! EINTR */
14546cdfca03SJohn Marino 		} while (rv == -1 && errno == EINTR);
14556cdfca03SJohn Marino 
14566cdfca03SJohn Marino 		if (rv == 0) {			/* poll (connect) timed out */
14576cdfca03SJohn Marino 			errno = ETIMEDOUT;
14586cdfca03SJohn Marino 			goto connecterror;
14596cdfca03SJohn Marino 		}
14606cdfca03SJohn Marino 
14616cdfca03SJohn Marino 		if (rv == -1) {			/* poll error */
14626cdfca03SJohn Marino 			goto connecterror;
14636cdfca03SJohn Marino 		} else if (pfd[0].revents & (POLLIN|POLLOUT)) {
14646cdfca03SJohn Marino 			slen = sizeof(error);	/* OK, or pending error */
14656cdfca03SJohn Marino 			if (getsockopt(sock, SOL_SOCKET, SO_ERROR,
14666cdfca03SJohn Marino 			    &error, &slen) == -1) {
14676cdfca03SJohn Marino 						/* Solaris pending error */
14686cdfca03SJohn Marino 				goto connecterror;
14696cdfca03SJohn Marino 			} else if (error != 0) {
14706cdfca03SJohn Marino 				errno = error;	/* BSD pending error */
14716cdfca03SJohn Marino 				goto connecterror;
14726cdfca03SJohn Marino 			}
14736cdfca03SJohn Marino 		} else {
14746cdfca03SJohn Marino 			errno = EBADF;		/* this shouldn't happen ... */
14756cdfca03SJohn Marino 			goto connecterror;
14766cdfca03SJohn Marino 		}
14776cdfca03SJohn Marino 	}
14786cdfca03SJohn Marino 
14796cdfca03SJohn Marino 	if (fcntl(sock, F_SETFL, flags) == -1) {
14806cdfca03SJohn Marino 						/* restore socket flags */
14816cdfca03SJohn Marino 		warn("Can't %s socket flags for connect to `%s:%s'",
14826cdfca03SJohn Marino 		    "restore", hname, sname);
14836cdfca03SJohn Marino 		return -1;
14846cdfca03SJohn Marino 	}
14856cdfca03SJohn Marino 	return 0;
14866cdfca03SJohn Marino }
14876cdfca03SJohn Marino 
14886cdfca03SJohn Marino /*
14896cdfca03SJohn Marino  * Internal version of listen(2); sets socket buffer sizes first.
14906cdfca03SJohn Marino  */
14916cdfca03SJohn Marino int
ftp_listen(int sock,int backlog)14926cdfca03SJohn Marino ftp_listen(int sock, int backlog)
14936cdfca03SJohn Marino {
14946cdfca03SJohn Marino 
14956cdfca03SJohn Marino 	setupsockbufsize(sock);
14966cdfca03SJohn Marino 	return (listen(sock, backlog));
14976cdfca03SJohn Marino }
14986cdfca03SJohn Marino 
14996cdfca03SJohn Marino /*
15006cdfca03SJohn Marino  * Internal version of poll(2), to allow reimplementation by select(2)
15016cdfca03SJohn Marino  * on platforms without the former.
15026cdfca03SJohn Marino  */
15036cdfca03SJohn Marino int
ftp_poll(struct pollfd * fds,int nfds,int timeout)15046cdfca03SJohn Marino ftp_poll(struct pollfd *fds, int nfds, int timeout)
15056cdfca03SJohn Marino {
15066cdfca03SJohn Marino #if defined(HAVE_POLL)
15076cdfca03SJohn Marino 	return poll(fds, nfds, timeout);
15086cdfca03SJohn Marino 
15096cdfca03SJohn Marino #elif defined(HAVE_SELECT)
15106cdfca03SJohn Marino 		/* implement poll(2) using select(2) */
15116cdfca03SJohn Marino 	fd_set		rset, wset, xset;
15126cdfca03SJohn Marino 	const int	rsetflags = POLLIN | POLLRDNORM;
15136cdfca03SJohn Marino 	const int	wsetflags = POLLOUT | POLLWRNORM;
15146cdfca03SJohn Marino 	const int	xsetflags = POLLRDBAND;
15156cdfca03SJohn Marino 	struct timeval	tv, *ptv;
15166cdfca03SJohn Marino 	int		i, max, rv;
15176cdfca03SJohn Marino 
15186cdfca03SJohn Marino 	FD_ZERO(&rset);			/* build list of read & write events */
15196cdfca03SJohn Marino 	FD_ZERO(&wset);
15206cdfca03SJohn Marino 	FD_ZERO(&xset);
15216cdfca03SJohn Marino 	max = 0;
15226cdfca03SJohn Marino 	for (i = 0; i < nfds; i++) {
15236cdfca03SJohn Marino 		if (fds[i].fd > FD_SETSIZE) {
15246cdfca03SJohn Marino 			warnx("can't select fd %d", fds[i].fd);
15256cdfca03SJohn Marino 			errno = EINVAL;
15266cdfca03SJohn Marino 			return -1;
15276cdfca03SJohn Marino 		} else if (fds[i].fd > max)
15286cdfca03SJohn Marino 			max = fds[i].fd;
15296cdfca03SJohn Marino 		if (fds[i].events & rsetflags)
15306cdfca03SJohn Marino 			FD_SET(fds[i].fd, &rset);
15316cdfca03SJohn Marino 		if (fds[i].events & wsetflags)
15326cdfca03SJohn Marino 			FD_SET(fds[i].fd, &wset);
15336cdfca03SJohn Marino 		if (fds[i].events & xsetflags)
15346cdfca03SJohn Marino 			FD_SET(fds[i].fd, &xset);
15356cdfca03SJohn Marino 	}
15366cdfca03SJohn Marino 
15376cdfca03SJohn Marino 	ptv = &tv;			/* determine timeout */
15386cdfca03SJohn Marino 	if (timeout == -1) {		/* wait forever */
15396cdfca03SJohn Marino 		ptv = NULL;
15406cdfca03SJohn Marino 	} else if (timeout == 0) {	/* poll once */
15416cdfca03SJohn Marino 		ptv->tv_sec = 0;
15426cdfca03SJohn Marino 		ptv->tv_usec = 0;
15436cdfca03SJohn Marino 	}
15446cdfca03SJohn Marino 	else if (timeout != 0) {	/* wait timeout milliseconds */
15456cdfca03SJohn Marino 		ptv->tv_sec = timeout / 1000;
15466cdfca03SJohn Marino 		ptv->tv_usec = (timeout % 1000) * 1000;
15476cdfca03SJohn Marino 	}
15486cdfca03SJohn Marino 	rv = select(max + 1, &rset, &wset, &xset, ptv);
15496cdfca03SJohn Marino 	if (rv <= 0)			/* -1 == error, 0 == timeout */
15506cdfca03SJohn Marino 		return rv;
15516cdfca03SJohn Marino 
15526cdfca03SJohn Marino 	for (i = 0; i < nfds; i++) {	/* determine results */
15536cdfca03SJohn Marino 		if (FD_ISSET(fds[i].fd, &rset))
15546cdfca03SJohn Marino 			fds[i].revents |= (fds[i].events & rsetflags);
15556cdfca03SJohn Marino 		if (FD_ISSET(fds[i].fd, &wset))
15566cdfca03SJohn Marino 			fds[i].revents |= (fds[i].events & wsetflags);
15576cdfca03SJohn Marino 		if (FD_ISSET(fds[i].fd, &xset))
15586cdfca03SJohn Marino 			fds[i].revents |= (fds[i].events & xsetflags);
15596cdfca03SJohn Marino 	}
15606cdfca03SJohn Marino 	return rv;
15616cdfca03SJohn Marino 
15626cdfca03SJohn Marino #else
15636cdfca03SJohn Marino # error no way to implement xpoll
15646cdfca03SJohn Marino #endif
15656cdfca03SJohn Marino }
15666cdfca03SJohn Marino 
1567*3a184c67SAntonio Huete Jimenez #ifndef SMALL
15686cdfca03SJohn Marino /*
15696cdfca03SJohn Marino  * malloc() with inbuilt error checking
15706cdfca03SJohn Marino  */
15716cdfca03SJohn Marino void *
ftp_malloc(size_t size)15726cdfca03SJohn Marino ftp_malloc(size_t size)
15736cdfca03SJohn Marino {
15746cdfca03SJohn Marino 	void *p;
15756cdfca03SJohn Marino 
15766cdfca03SJohn Marino 	p = malloc(size);
15776cdfca03SJohn Marino 	if (p == NULL)
15786cdfca03SJohn Marino 		err(1, "Unable to allocate %ld bytes of memory", (long)size);
15796cdfca03SJohn Marino 	return (p);
15806cdfca03SJohn Marino }
15816cdfca03SJohn Marino 
15826cdfca03SJohn Marino /*
15836cdfca03SJohn Marino  * sl_init() with inbuilt error checking
15846cdfca03SJohn Marino  */
15856cdfca03SJohn Marino StringList *
ftp_sl_init(void)15866cdfca03SJohn Marino ftp_sl_init(void)
15876cdfca03SJohn Marino {
15886cdfca03SJohn Marino 	StringList *p;
15896cdfca03SJohn Marino 
15906cdfca03SJohn Marino 	p = sl_init();
15916cdfca03SJohn Marino 	if (p == NULL)
15926cdfca03SJohn Marino 		err(1, "Unable to allocate memory for stringlist");
15936cdfca03SJohn Marino 	return (p);
15946cdfca03SJohn Marino }
15956cdfca03SJohn Marino 
15966cdfca03SJohn Marino /*
15976cdfca03SJohn Marino  * sl_add() with inbuilt error checking
15986cdfca03SJohn Marino  */
15996cdfca03SJohn Marino void
ftp_sl_add(StringList * sl,char * i)16006cdfca03SJohn Marino ftp_sl_add(StringList *sl, char *i)
16016cdfca03SJohn Marino {
16026cdfca03SJohn Marino 
16036cdfca03SJohn Marino 	sl_add(sl, i);
16046cdfca03SJohn Marino }
16056cdfca03SJohn Marino 
16066cdfca03SJohn Marino /*
16076cdfca03SJohn Marino  * strdup() with inbuilt error checking
16086cdfca03SJohn Marino  */
16096cdfca03SJohn Marino char *
ftp_strdup(const char * str)16106cdfca03SJohn Marino ftp_strdup(const char *str)
16116cdfca03SJohn Marino {
16126cdfca03SJohn Marino 	char *s;
16136cdfca03SJohn Marino 
16146cdfca03SJohn Marino 	if (str == NULL)
16156cdfca03SJohn Marino 		errx(1, "ftp_strdup: called with NULL argument");
16166cdfca03SJohn Marino 	s = strdup(str);
16176cdfca03SJohn Marino 	if (s == NULL)
16186cdfca03SJohn Marino 		err(1, "Unable to allocate memory for string copy");
16196cdfca03SJohn Marino 	return (s);
16206cdfca03SJohn Marino }
16216cdfca03SJohn Marino #endif
1622*3a184c67SAntonio Huete Jimenez