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