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