1*cc361f65SGavin Atkinson /* $NetBSD: util.c,v 1.21 2009/11/15 10:12:37 lukem Exp $ */ 2*cc361f65SGavin Atkinson /* from NetBSD: util.c,v 1.152 2009/07/13 19:05:41 roy Exp */ 3f982db4aSGavin Atkinson 4f982db4aSGavin Atkinson /*- 5*cc361f65SGavin 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 66*cc361f65SGavin Atkinson #include "tnftp.h" 67*cc361f65SGavin Atkinson 68*cc361f65SGavin Atkinson #if 0 /* tnftp */ 69*cc361f65SGavin Atkinson 70f982db4aSGavin Atkinson #include <sys/cdefs.h> 71f982db4aSGavin Atkinson #ifndef lint 72*cc361f65SGavin 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> 91*cc361f65SGavin 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> 99*cc361f65SGavin Atkinson #include <tzfile.h> 100f982db4aSGavin Atkinson #include <unistd.h> 101f982db4aSGavin Atkinson 102*cc361f65SGavin Atkinson #endif /* tnftp */ 103f982db4aSGavin Atkinson 104*cc361f65SGavin 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 110f982db4aSGavin Atkinson setpeer(int argc, char *argv[]) 111f982db4aSGavin Atkinson { 112f982db4aSGavin Atkinson char *host; 113*cc361f65SGavin 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: 127*cc361f65SGavin 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') 140*cc361f65SGavin 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 173*cc361f65SGavin 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 */ 180*cc361f65SGavin Atkinson while (*fline && isspace((int)*fline)) 181*cc361f65SGavin Atkinson fline++; 182f982db4aSGavin Atkinson 183*cc361f65SGavin Atkinson if (strcasecmp(fline, "MDTM") == 0) 184f982db4aSGavin Atkinson features[FEAT_MDTM] = 1; 185*cc361f65SGavin Atkinson else if (strncasecmp(fline, "MLST", sizeof("MLST") - 1) == 0) { 186f982db4aSGavin Atkinson features[FEAT_MLST] = 1; 187*cc361f65SGavin Atkinson } else if (strcasecmp(fline, "REST STREAM") == 0) 188f982db4aSGavin Atkinson features[FEAT_REST_STREAM] = 1; 189*cc361f65SGavin Atkinson else if (strcasecmp(fline, "SIZE") == 0) 190f982db4aSGavin Atkinson features[FEAT_SIZE] = 1; 191*cc361f65SGavin 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 200f982db4aSGavin Atkinson getremoteinfo(void) 201f982db4aSGavin Atkinson { 202f982db4aSGavin Atkinson int overbose, i; 203f982db4aSGavin Atkinson 204f982db4aSGavin Atkinson overbose = verbose; 205*cc361f65SGavin 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; 271*cc361f65SGavin Atkinson #ifndef NO_DEBUG 272*cc361f65SGavin 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 } 282*cc361f65SGavin 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 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; 313*cc361f65SGavin 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 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 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 379*cc361f65SGavin Atkinson ftp_login(const char *host, const char *luser, const char *lpass) 380f982db4aSGavin Atkinson { 381f982db4aSGavin Atkinson char tmp[80]; 382*cc361f65SGavin Atkinson char *fuser, *pass, *facct, *p; 383*cc361f65SGavin Atkinson char emptypass[] = ""; 384*cc361f65SGavin Atkinson const char *errormsg; 385*cc361f65SGavin Atkinson int n, aflag, rval, nlen; 386f982db4aSGavin Atkinson 387*cc361f65SGavin Atkinson aflag = rval = 0; 388*cc361f65SGavin Atkinson fuser = pass = facct = NULL; 389*cc361f65SGavin Atkinson if (luser) 390*cc361f65SGavin Atkinson fuser = ftp_strdup(luser); 391*cc361f65SGavin Atkinson if (lpass) 392*cc361f65SGavin Atkinson pass = ftp_strdup(lpass); 393f982db4aSGavin Atkinson 394*cc361f65SGavin Atkinson DPRINTF("ftp_login: user `%s' pass `%s' host `%s'\n", 395*cc361f65SGavin 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) { 401*cc361f65SGavin Atkinson FREEPTR(fuser); 402*cc361f65SGavin Atkinson fuser = ftp_strdup("anonymous"); /* as per RFC1635 */ 403*cc361f65SGavin Atkinson FREEPTR(pass); 404*cc361f65SGavin Atkinson pass = ftp_strdup(getoptionvalue("anonpass")); 405f982db4aSGavin Atkinson } 406f982db4aSGavin Atkinson 407*cc361f65SGavin Atkinson if (ruserpass(host, &fuser, &pass, &facct) < 0) { 408f982db4aSGavin Atkinson code = -1; 409f982db4aSGavin Atkinson goto cleanup_ftp_login; 410f982db4aSGavin Atkinson } 411f982db4aSGavin Atkinson 412*cc361f65SGavin 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); 417*cc361f65SGavin Atkinson errormsg = NULL; 418*cc361f65SGavin Atkinson nlen = get_line(stdin, tmp, sizeof(tmp), &errormsg); 419*cc361f65SGavin Atkinson if (nlen < 0) { 420*cc361f65SGavin Atkinson fprintf(ttyout, "%s; %s aborted.\n", errormsg, "login"); 421f982db4aSGavin Atkinson code = -1; 422f982db4aSGavin Atkinson goto cleanup_ftp_login; 423*cc361f65SGavin Atkinson } else if (nlen == 0) { 424*cc361f65SGavin Atkinson fuser = ftp_strdup(localname); 425*cc361f65SGavin Atkinson } else { 426*cc361f65SGavin Atkinson fuser = ftp_strdup(tmp); 427f982db4aSGavin Atkinson } 428f982db4aSGavin Atkinson } 429f982db4aSGavin Atkinson 430f982db4aSGavin Atkinson if (gatemode) { 431f982db4aSGavin Atkinson char *nuser; 432*cc361f65SGavin Atkinson size_t len; 433f982db4aSGavin Atkinson 434*cc361f65SGavin Atkinson len = strlen(fuser) + 1 + strlen(host) + 1; 435*cc361f65SGavin Atkinson nuser = ftp_malloc(len); 436*cc361f65SGavin Atkinson (void)strlcpy(nuser, fuser, len); 437f982db4aSGavin Atkinson (void)strlcat(nuser, "@", len); 438f982db4aSGavin Atkinson (void)strlcat(nuser, host, len); 439*cc361f65SGavin Atkinson FREEPTR(fuser); 440*cc361f65SGavin Atkinson fuser = nuser; 441f982db4aSGavin Atkinson } 442f982db4aSGavin Atkinson 443*cc361f65SGavin Atkinson n = command("USER %s", fuser); 444f982db4aSGavin Atkinson if (n == CONTINUE) { 445f982db4aSGavin Atkinson if (pass == NULL) { 446*cc361f65SGavin Atkinson p = getpass("Password: "); 447*cc361f65SGavin Atkinson if (p == NULL) 448*cc361f65SGavin Atkinson p = emptypass; 449*cc361f65SGavin Atkinson pass = ftp_strdup(p); 450*cc361f65SGavin Atkinson memset(p, 0, strlen(p)); 451f982db4aSGavin Atkinson } 452f982db4aSGavin Atkinson n = command("PASS %s", pass); 453*cc361f65SGavin Atkinson memset(pass, 0, strlen(pass)); 454f982db4aSGavin Atkinson } 455f982db4aSGavin Atkinson if (n == CONTINUE) { 456f982db4aSGavin Atkinson aflag++; 457*cc361f65SGavin Atkinson if (facct == NULL) { 458*cc361f65SGavin Atkinson p = getpass("Account: "); 459*cc361f65SGavin Atkinson if (p == NULL) 460*cc361f65SGavin Atkinson p = emptypass; 461*cc361f65SGavin Atkinson facct = ftp_strdup(p); 462*cc361f65SGavin Atkinson memset(p, 0, strlen(p)); 463f982db4aSGavin Atkinson } 464*cc361f65SGavin Atkinson if (facct[0] == '\0') { 465*cc361f65SGavin Atkinson warnx("Login failed"); 466f982db4aSGavin Atkinson goto cleanup_ftp_login; 467f982db4aSGavin Atkinson } 468*cc361f65SGavin Atkinson n = command("ACCT %s", facct); 469*cc361f65SGavin Atkinson memset(facct, 0, strlen(facct)); 470f982db4aSGavin Atkinson } 471f982db4aSGavin Atkinson if ((n != COMPLETE) || 472*cc361f65SGavin Atkinson (!aflag && facct != NULL && command("ACCT %s", facct) != COMPLETE)) { 473*cc361f65SGavin Atkinson warnx("Login failed"); 474f982db4aSGavin Atkinson goto cleanup_ftp_login; 475f982db4aSGavin Atkinson } 476f982db4aSGavin Atkinson rval = 1; 477*cc361f65SGavin 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: 495*cc361f65SGavin Atkinson FREEPTR(fuser); 496*cc361f65SGavin Atkinson if (pass != NULL) 497*cc361f65SGavin Atkinson memset(pass, 0, strlen(pass)); 498*cc361f65SGavin Atkinson FREEPTR(pass); 499*cc361f65SGavin Atkinson if (facct != NULL) 500*cc361f65SGavin Atkinson memset(facct, 0, strlen(facct)); 501*cc361f65SGavin 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 512*cc361f65SGavin Atkinson another(int *pargc, char ***pargv, const char *aprompt) 513f982db4aSGavin Atkinson { 514*cc361f65SGavin Atkinson const char *errormsg; 515*cc361f65SGavin Atkinson int ret, nlen; 516*cc361f65SGavin Atkinson size_t len; 517f982db4aSGavin Atkinson 518*cc361f65SGavin Atkinson len = strlen(line); 519f982db4aSGavin Atkinson if (len >= sizeof(line) - 3) { 520*cc361f65SGavin Atkinson fputs("Sorry, arguments too long.\n", ttyout); 521f982db4aSGavin Atkinson intr(0); 522f982db4aSGavin Atkinson } 523*cc361f65SGavin Atkinson fprintf(ttyout, "(%s) ", aprompt); 524f982db4aSGavin Atkinson line[len++] = ' '; 525*cc361f65SGavin Atkinson errormsg = NULL; 526*cc361f65SGavin Atkinson nlen = get_line(stdin, line + len, sizeof(line)-len, &errormsg); 527*cc361f65SGavin Atkinson if (nlen < 0) { 528*cc361f65SGavin Atkinson fprintf(ttyout, "%s; %s aborted.\n", errormsg, "operation"); 529f982db4aSGavin Atkinson intr(0); 530f982db4aSGavin Atkinson } 531*cc361f65SGavin 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 * 545*cc361f65SGavin 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; 550*cc361f65SGavin Atkinson char temp[MAXPATHLEN]; 551*cc361f65SGavin Atkinson int oldverbose, oldhash, oldprogress, fd; 552*cc361f65SGavin Atkinson char *cp; 553*cc361f65SGavin Atkinson const char *rmode; 554*cc361f65SGavin 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) { 580*cc361f65SGavin 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); 592*cc361f65SGavin Atkinson for (rmode = "w"; *++argv != NULL; rmode = "a") 593*cc361f65SGavin 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) 607*cc361f65SGavin Atkinson warnx("Can't find list of remote files"); 608f982db4aSGavin Atkinson else 609f982db4aSGavin Atkinson *errbuf = 610*cc361f65SGavin 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 * 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) 639*cc361f65SGavin 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) { 644*cc361f65SGavin Atkinson warnx("Glob pattern `%s' not found", pattern); 645f982db4aSGavin Atkinson globfree(&gl); 646f982db4aSGavin Atkinson return (NULL); 647f982db4aSGavin Atkinson } 648*cc361f65SGavin 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 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; 664*cc361f65SGavin 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; 686*cc361f65SGavin 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 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; 708*cc361f65SGavin 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 } 751*cc361f65SGavin Atkinson memset(&timebuf, 0, sizeof(timebuf)); 752f982db4aSGavin Atkinson if (strlen(timestr) != 14 || 753*cc361f65SGavin 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) { 761*cc361f65SGavin Atkinson if (noisy || ftp_debug != 0) 762f982db4aSGavin Atkinson goto bad_parse_time; 763f982db4aSGavin Atkinson else 764f982db4aSGavin Atkinson goto cleanup_parse_time; 765*cc361f65SGavin Atkinson } else { 766*cc361f65SGavin Atkinson DPRINTF("remotemodtime: parsed date `%s' as " LLF 767*cc361f65SGavin Atkinson ", %s", 768*cc361f65SGavin Atkinson timestr, (LLT)rtime, 769*cc361f65SGavin Atkinson rfc2822time(localtime(&rtime))); 770*cc361f65SGavin Atkinson } 771f982db4aSGavin Atkinson } else { 772f982db4aSGavin Atkinson if (r == ERROR && code == 500 && features[FEAT_MDTM] == -1) 773f982db4aSGavin Atkinson features[FEAT_MDTM] = 0; 774*cc361f65SGavin 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 /* 787*cc361f65SGavin Atkinson * Format tm in an RFC2822 compatible manner, with a trailing \n. 788*cc361f65SGavin Atkinson * Returns a pointer to a static string containing the result. 789*cc361f65SGavin Atkinson */ 790*cc361f65SGavin Atkinson const char * 791*cc361f65SGavin Atkinson rfc2822time(const struct tm *tm) 792*cc361f65SGavin Atkinson { 793*cc361f65SGavin Atkinson static char result[50]; 794*cc361f65SGavin Atkinson 795*cc361f65SGavin Atkinson if (strftime(result, sizeof(result), 796*cc361f65SGavin Atkinson "%a, %d %b %Y %H:%M:%S %z\n", tm) == 0) 797*cc361f65SGavin Atkinson errx(1, "Can't convert RFC2822 time: buffer too small"); 798*cc361f65SGavin Atkinson return result; 799*cc361f65SGavin Atkinson } 800*cc361f65SGavin Atkinson 801*cc361f65SGavin Atkinson /* 802f982db4aSGavin Atkinson * Update global `localcwd', which contains the state of the local cwd 803f982db4aSGavin Atkinson */ 804f982db4aSGavin Atkinson void 805f982db4aSGavin Atkinson updatelocalcwd(void) 806f982db4aSGavin Atkinson { 807f982db4aSGavin Atkinson 808f982db4aSGavin Atkinson if (getcwd(localcwd, sizeof(localcwd)) == NULL) 809f982db4aSGavin Atkinson localcwd[0] = '\0'; 810*cc361f65SGavin 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 817f982db4aSGavin Atkinson updateremotecwd(void) 818f982db4aSGavin Atkinson { 819*cc361f65SGavin Atkinson int overbose, ocode; 820*cc361f65SGavin Atkinson size_t i; 821f982db4aSGavin Atkinson char *cp; 822f982db4aSGavin Atkinson 823f982db4aSGavin Atkinson overbose = verbose; 824f982db4aSGavin Atkinson ocode = code; 825*cc361f65SGavin 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'; 843*cc361f65SGavin 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 857f982db4aSGavin Atkinson fileindir(const char *file, const char *dir) 858f982db4aSGavin Atkinson { 859*cc361f65SGavin Atkinson char parentdirbuf[PATH_MAX+1], *parentdir; 860*cc361f65SGavin Atkinson char realdir[PATH_MAX+1]; 861f982db4aSGavin Atkinson size_t dirlen; 862f982db4aSGavin Atkinson 863*cc361f65SGavin Atkinson /* determine parent directory of file */ 864*cc361f65SGavin Atkinson (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf)); 865*cc361f65SGavin Atkinson parentdir = dirname(parentdirbuf); 866*cc361f65SGavin Atkinson if (strcmp(parentdir, ".") == 0) 867*cc361f65SGavin Atkinson return 1; /* current directory is ok */ 868*cc361f65SGavin Atkinson 869*cc361f65SGavin Atkinson /* find the directory */ 870*cc361f65SGavin Atkinson if (realpath(parentdir, realdir) == NULL) { 871*cc361f65SGavin Atkinson warn("Unable to determine real path of `%s'", parentdir); 872f982db4aSGavin Atkinson return 0; 873f982db4aSGavin Atkinson } 874*cc361f65SGavin Atkinson if (realdir[0] != '/') /* relative result is ok */ 875f982db4aSGavin Atkinson return 1; 876f982db4aSGavin Atkinson dirlen = strlen(dir); 877*cc361f65SGavin Atkinson if (strncmp(realdir, dir, dirlen) == 0 && 878*cc361f65SGavin 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 887f982db4aSGavin Atkinson list_vertical(StringList *sl) 888f982db4aSGavin Atkinson { 889*cc361f65SGavin Atkinson size_t i, j; 890*cc361f65SGavin Atkinson size_t columns, lines; 891f982db4aSGavin Atkinson char *p; 892*cc361f65SGavin 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 } 916*cc361f65SGavin 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 } 925*cc361f65SGavin Atkinson } 926f982db4aSGavin Atkinson 927f982db4aSGavin Atkinson /* 928f982db4aSGavin Atkinson * Update the global ttywidth value, using TIOCGWINSZ. 929f982db4aSGavin Atkinson */ 930f982db4aSGavin Atkinson void 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 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 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 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 1061f982db4aSGavin Atkinson setupsockbufsize(int sock) 1062f982db4aSGavin Atkinson { 1063f982db4aSGavin Atkinson 1064f982db4aSGavin Atkinson if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, 1065f982db4aSGavin Atkinson (void *)&sndbuf_size, sizeof(sndbuf_size)) == -1) 1066*cc361f65SGavin Atkinson warn("Unable to set sndbuf size %d", sndbuf_size); 1067f982db4aSGavin Atkinson 1068f982db4aSGavin Atkinson if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, 1069f982db4aSGavin Atkinson (void *)&rcvbuf_size, sizeof(rcvbuf_size)) == -1) 1070*cc361f65SGavin Atkinson warn("Unable to set rcvbuf size %d", rcvbuf_size); 1071f982db4aSGavin Atkinson } 1072f982db4aSGavin Atkinson 1073f982db4aSGavin Atkinson /* 1074f982db4aSGavin Atkinson * Copy characters from src into dst, \ quoting characters that require it 1075f982db4aSGavin Atkinson */ 1076f982db4aSGavin Atkinson void 1077f982db4aSGavin Atkinson ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen) 1078f982db4aSGavin Atkinson { 1079*cc361f65SGavin Atkinson size_t di, si; 1080f982db4aSGavin Atkinson 1081f982db4aSGavin Atkinson for (di = si = 0; 1082f982db4aSGavin Atkinson src[si] != '\0' && di < dstlen && si < srclen; 1083f982db4aSGavin Atkinson di++, si++) { 1084f982db4aSGavin Atkinson switch (src[si]) { 1085f982db4aSGavin Atkinson case '\\': 1086f982db4aSGavin Atkinson case ' ': 1087f982db4aSGavin Atkinson case '\t': 1088f982db4aSGavin Atkinson case '\r': 1089f982db4aSGavin Atkinson case '\n': 1090f982db4aSGavin Atkinson case '"': 1091f982db4aSGavin Atkinson dst[di++] = '\\'; 1092f982db4aSGavin Atkinson if (di >= dstlen) 1093f982db4aSGavin Atkinson break; 1094f982db4aSGavin Atkinson /* FALLTHROUGH */ 1095f982db4aSGavin Atkinson default: 1096f982db4aSGavin Atkinson dst[di] = src[si]; 1097f982db4aSGavin Atkinson } 1098f982db4aSGavin Atkinson } 1099f982db4aSGavin Atkinson dst[di] = '\0'; 1100f982db4aSGavin Atkinson } 1101f982db4aSGavin Atkinson 1102f982db4aSGavin Atkinson /* 1103f982db4aSGavin Atkinson * Copy src into buf (which is len bytes long), expanding % sequences. 1104f982db4aSGavin Atkinson */ 1105f982db4aSGavin Atkinson void 1106f982db4aSGavin Atkinson formatbuf(char *buf, size_t len, const char *src) 1107f982db4aSGavin Atkinson { 1108*cc361f65SGavin Atkinson const char *p, *p2, *q; 1109*cc361f65SGavin Atkinson size_t i; 1110*cc361f65SGavin Atkinson int op, updirs, pdirs; 1111f982db4aSGavin Atkinson 1112f982db4aSGavin Atkinson #define ADDBUF(x) do { \ 1113f982db4aSGavin Atkinson if (i >= len - 1) \ 1114f982db4aSGavin Atkinson goto endbuf; \ 1115f982db4aSGavin Atkinson buf[i++] = (x); \ 1116f982db4aSGavin Atkinson } while (0) 1117f982db4aSGavin Atkinson 1118f982db4aSGavin Atkinson p = src; 1119f982db4aSGavin Atkinson for (i = 0; *p; p++) { 1120f982db4aSGavin Atkinson if (*p != '%') { 1121f982db4aSGavin Atkinson ADDBUF(*p); 1122f982db4aSGavin Atkinson continue; 1123f982db4aSGavin Atkinson } 1124f982db4aSGavin Atkinson p++; 1125f982db4aSGavin Atkinson 1126f982db4aSGavin Atkinson switch (op = *p) { 1127f982db4aSGavin Atkinson 1128f982db4aSGavin Atkinson case '/': 1129f982db4aSGavin Atkinson case '.': 1130f982db4aSGavin Atkinson case 'c': 1131f982db4aSGavin Atkinson p2 = connected ? remotecwd : ""; 1132f982db4aSGavin Atkinson updirs = pdirs = 0; 1133f982db4aSGavin Atkinson 1134f982db4aSGavin Atkinson /* option to determine fixed # of dirs from path */ 1135f982db4aSGavin Atkinson if (op == '.' || op == 'c') { 1136f982db4aSGavin Atkinson int skip; 1137f982db4aSGavin Atkinson 1138f982db4aSGavin Atkinson q = p2; 1139f982db4aSGavin Atkinson while (*p2) /* calc # of /'s */ 1140f982db4aSGavin Atkinson if (*p2++ == '/') 1141f982db4aSGavin Atkinson updirs++; 1142f982db4aSGavin Atkinson if (p[1] == '0') { /* print <x> or ... */ 1143f982db4aSGavin Atkinson pdirs = 1; 1144f982db4aSGavin Atkinson p++; 1145f982db4aSGavin Atkinson } 1146f982db4aSGavin Atkinson if (p[1] >= '1' && p[1] <= '9') { 1147f982db4aSGavin Atkinson /* calc # to skip */ 1148f982db4aSGavin Atkinson skip = p[1] - '0'; 1149f982db4aSGavin Atkinson p++; 1150f982db4aSGavin Atkinson } else 1151f982db4aSGavin Atkinson skip = 1; 1152f982db4aSGavin Atkinson 1153f982db4aSGavin Atkinson updirs -= skip; 1154f982db4aSGavin Atkinson while (skip-- > 0) { 1155f982db4aSGavin Atkinson while ((p2 > q) && (*p2 != '/')) 1156f982db4aSGavin Atkinson p2--; /* back up */ 1157f982db4aSGavin Atkinson if (skip && p2 > q) 1158f982db4aSGavin Atkinson p2--; 1159f982db4aSGavin Atkinson } 1160f982db4aSGavin Atkinson if (*p2 == '/' && p2 != q) 1161f982db4aSGavin Atkinson p2++; 1162f982db4aSGavin Atkinson } 1163f982db4aSGavin Atkinson 1164f982db4aSGavin Atkinson if (updirs > 0 && pdirs) { 1165f982db4aSGavin Atkinson if (i >= len - 5) 1166f982db4aSGavin Atkinson break; 1167f982db4aSGavin Atkinson if (op == '.') { 1168f982db4aSGavin Atkinson ADDBUF('.'); 1169f982db4aSGavin Atkinson ADDBUF('.'); 1170f982db4aSGavin Atkinson ADDBUF('.'); 1171f982db4aSGavin Atkinson } else { 1172f982db4aSGavin Atkinson ADDBUF('/'); 1173f982db4aSGavin Atkinson ADDBUF('<'); 1174f982db4aSGavin Atkinson if (updirs > 9) { 1175f982db4aSGavin Atkinson ADDBUF('9'); 1176f982db4aSGavin Atkinson ADDBUF('+'); 1177f982db4aSGavin Atkinson } else 1178f982db4aSGavin Atkinson ADDBUF('0' + updirs); 1179f982db4aSGavin Atkinson ADDBUF('>'); 1180f982db4aSGavin Atkinson } 1181f982db4aSGavin Atkinson } 1182f982db4aSGavin Atkinson for (; *p2; p2++) 1183f982db4aSGavin Atkinson ADDBUF(*p2); 1184f982db4aSGavin Atkinson break; 1185f982db4aSGavin Atkinson 1186f982db4aSGavin Atkinson case 'M': 1187f982db4aSGavin Atkinson case 'm': 1188*cc361f65SGavin Atkinson for (p2 = connected && hostname ? hostname : "-"; 1189f982db4aSGavin Atkinson *p2 ; p2++) { 1190f982db4aSGavin Atkinson if (op == 'm' && *p2 == '.') 1191f982db4aSGavin Atkinson break; 1192f982db4aSGavin Atkinson ADDBUF(*p2); 1193f982db4aSGavin Atkinson } 1194f982db4aSGavin Atkinson break; 1195f982db4aSGavin Atkinson 1196f982db4aSGavin Atkinson case 'n': 1197f982db4aSGavin Atkinson for (p2 = connected ? username : "-"; *p2 ; p2++) 1198f982db4aSGavin Atkinson ADDBUF(*p2); 1199f982db4aSGavin Atkinson break; 1200f982db4aSGavin Atkinson 1201f982db4aSGavin Atkinson case '%': 1202f982db4aSGavin Atkinson ADDBUF('%'); 1203f982db4aSGavin Atkinson break; 1204f982db4aSGavin Atkinson 1205f982db4aSGavin Atkinson default: /* display unknown codes literally */ 1206f982db4aSGavin Atkinson ADDBUF('%'); 1207f982db4aSGavin Atkinson ADDBUF(op); 1208f982db4aSGavin Atkinson break; 1209f982db4aSGavin Atkinson 1210f982db4aSGavin Atkinson } 1211f982db4aSGavin Atkinson } 1212f982db4aSGavin Atkinson endbuf: 1213f982db4aSGavin Atkinson buf[i] = '\0'; 1214f982db4aSGavin Atkinson } 1215f982db4aSGavin Atkinson 1216f982db4aSGavin Atkinson /* 1217f982db4aSGavin Atkinson * Determine if given string is an IPv6 address or not. 1218f982db4aSGavin Atkinson * Return 1 for yes, 0 for no 1219f982db4aSGavin Atkinson */ 1220f982db4aSGavin Atkinson int 1221f982db4aSGavin Atkinson isipv6addr(const char *addr) 1222f982db4aSGavin Atkinson { 1223f982db4aSGavin Atkinson int rv = 0; 1224f982db4aSGavin Atkinson #ifdef INET6 1225f982db4aSGavin Atkinson struct addrinfo hints, *res; 1226f982db4aSGavin Atkinson 1227f982db4aSGavin Atkinson memset(&hints, 0, sizeof(hints)); 1228*cc361f65SGavin Atkinson hints.ai_family = AF_INET6; 1229f982db4aSGavin Atkinson hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 1230f982db4aSGavin Atkinson hints.ai_flags = AI_NUMERICHOST; 1231f982db4aSGavin Atkinson if (getaddrinfo(addr, "0", &hints, &res) != 0) 1232f982db4aSGavin Atkinson rv = 0; 1233f982db4aSGavin Atkinson else { 1234f982db4aSGavin Atkinson rv = 1; 1235f982db4aSGavin Atkinson freeaddrinfo(res); 1236f982db4aSGavin Atkinson } 1237*cc361f65SGavin Atkinson DPRINTF("isipv6addr: got %d for %s\n", rv, addr); 1238f982db4aSGavin Atkinson #endif 1239f982db4aSGavin Atkinson return (rv == 1) ? 1 : 0; 1240f982db4aSGavin Atkinson } 1241f982db4aSGavin Atkinson 1242*cc361f65SGavin Atkinson /* 1243*cc361f65SGavin Atkinson * Read a line from the FILE stream into buf/buflen using fgets(), so up 1244*cc361f65SGavin Atkinson * to buflen-1 chars will be read and the result will be NUL terminated. 1245*cc361f65SGavin Atkinson * If the line has a trailing newline it will be removed. 1246*cc361f65SGavin Atkinson * If the line is too long, excess characters will be read until 1247*cc361f65SGavin Atkinson * newline/EOF/error. 1248*cc361f65SGavin Atkinson * If EOF/error occurs or a too-long line is encountered and errormsg 1249*cc361f65SGavin Atkinson * isn't NULL, it will be changed to a description of the problem. 1250*cc361f65SGavin Atkinson * (The EOF message has a leading \n for cosmetic purposes). 1251*cc361f65SGavin Atkinson * Returns: 1252*cc361f65SGavin Atkinson * >=0 length of line (excluding trailing newline) if all ok 1253*cc361f65SGavin Atkinson * -1 error occurred 1254*cc361f65SGavin Atkinson * -2 EOF encountered 1255*cc361f65SGavin Atkinson * -3 line was too long 1256*cc361f65SGavin Atkinson */ 1257*cc361f65SGavin Atkinson int 1258*cc361f65SGavin Atkinson get_line(FILE *stream, char *buf, size_t buflen, const char **errormsg) 1259*cc361f65SGavin Atkinson { 1260*cc361f65SGavin Atkinson int rv, ch; 1261*cc361f65SGavin Atkinson size_t len; 1262*cc361f65SGavin Atkinson 1263*cc361f65SGavin Atkinson if (fgets(buf, buflen, stream) == NULL) { 1264*cc361f65SGavin Atkinson if (feof(stream)) { /* EOF */ 1265*cc361f65SGavin Atkinson rv = -2; 1266*cc361f65SGavin Atkinson if (errormsg) 1267*cc361f65SGavin Atkinson *errormsg = "\nEOF received"; 1268*cc361f65SGavin Atkinson } else { /* error */ 1269*cc361f65SGavin Atkinson rv = -1; 1270*cc361f65SGavin Atkinson if (errormsg) 1271*cc361f65SGavin Atkinson *errormsg = "Error encountered"; 1272*cc361f65SGavin Atkinson } 1273*cc361f65SGavin Atkinson clearerr(stream); 1274*cc361f65SGavin Atkinson return rv; 1275*cc361f65SGavin Atkinson } 1276*cc361f65SGavin Atkinson len = strlen(buf); 1277*cc361f65SGavin Atkinson if (buf[len-1] == '\n') { /* clear any trailing newline */ 1278*cc361f65SGavin Atkinson buf[--len] = '\0'; 1279*cc361f65SGavin Atkinson } else if (len == buflen-1) { /* line too long */ 1280*cc361f65SGavin Atkinson while ((ch = getchar()) != '\n' && ch != EOF) 1281*cc361f65SGavin Atkinson continue; 1282*cc361f65SGavin Atkinson if (errormsg) 1283*cc361f65SGavin Atkinson *errormsg = "Input line is too long"; 1284*cc361f65SGavin Atkinson clearerr(stream); 1285*cc361f65SGavin Atkinson return -3; 1286*cc361f65SGavin Atkinson } 1287*cc361f65SGavin Atkinson if (errormsg) 1288*cc361f65SGavin Atkinson *errormsg = NULL; 1289*cc361f65SGavin Atkinson return len; 1290*cc361f65SGavin Atkinson } 1291f982db4aSGavin Atkinson 1292f982db4aSGavin Atkinson /* 1293*cc361f65SGavin Atkinson * Internal version of connect(2); sets socket buffer sizes, 1294*cc361f65SGavin Atkinson * binds to a specific local address (if set), and 1295f982db4aSGavin Atkinson * supports a connection timeout using a non-blocking connect(2) with 1296f982db4aSGavin Atkinson * a poll(2). 1297f982db4aSGavin Atkinson * Socket fcntl flags are temporarily updated to include O_NONBLOCK; 1298f982db4aSGavin Atkinson * these will not be reverted on connection failure. 1299*cc361f65SGavin Atkinson * Returns 0 on success, or -1 upon failure (with an appropriate 1300*cc361f65SGavin Atkinson * error message displayed.) 1301f982db4aSGavin Atkinson */ 1302f982db4aSGavin Atkinson int 1303*cc361f65SGavin Atkinson ftp_connect(int sock, const struct sockaddr *name, socklen_t namelen) 1304f982db4aSGavin Atkinson { 1305f982db4aSGavin Atkinson int flags, rv, timeout, error; 1306f982db4aSGavin Atkinson socklen_t slen; 1307f982db4aSGavin Atkinson struct timeval endtime, now, td; 1308f982db4aSGavin Atkinson struct pollfd pfd[1]; 1309*cc361f65SGavin Atkinson char hname[NI_MAXHOST]; 1310*cc361f65SGavin Atkinson char sname[NI_MAXSERV]; 1311f982db4aSGavin Atkinson 1312f982db4aSGavin Atkinson setupsockbufsize(sock); 1313*cc361f65SGavin Atkinson if (getnameinfo(name, namelen, 1314*cc361f65SGavin Atkinson hname, sizeof(hname), sname, sizeof(sname), 1315*cc361f65SGavin Atkinson NI_NUMERICHOST | NI_NUMERICSERV) != 0) { 1316*cc361f65SGavin Atkinson strlcpy(hname, "?", sizeof(hname)); 1317*cc361f65SGavin Atkinson strlcpy(sname, "?", sizeof(sname)); 1318*cc361f65SGavin Atkinson } 1319f982db4aSGavin Atkinson 1320*cc361f65SGavin Atkinson if (bindai != NULL) { /* bind to specific addr */ 1321*cc361f65SGavin Atkinson struct addrinfo *ai; 1322*cc361f65SGavin Atkinson 1323*cc361f65SGavin Atkinson for (ai = bindai; ai != NULL; ai = ai->ai_next) { 1324*cc361f65SGavin Atkinson if (ai->ai_family == name->sa_family) 1325*cc361f65SGavin Atkinson break; 1326*cc361f65SGavin Atkinson } 1327*cc361f65SGavin Atkinson if (ai == NULL) 1328*cc361f65SGavin Atkinson ai = bindai; 1329*cc361f65SGavin Atkinson if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { 1330*cc361f65SGavin Atkinson char bname[NI_MAXHOST]; 1331*cc361f65SGavin Atkinson int saveerr; 1332*cc361f65SGavin Atkinson 1333*cc361f65SGavin Atkinson saveerr = errno; 1334*cc361f65SGavin Atkinson if (getnameinfo(ai->ai_addr, ai->ai_addrlen, 1335*cc361f65SGavin Atkinson bname, sizeof(bname), NULL, 0, NI_NUMERICHOST) != 0) 1336*cc361f65SGavin Atkinson strlcpy(bname, "?", sizeof(bname)); 1337*cc361f65SGavin Atkinson errno = saveerr; 1338*cc361f65SGavin Atkinson warn("Can't bind to `%s'", bname); 1339*cc361f65SGavin Atkinson return -1; 1340*cc361f65SGavin Atkinson } 1341*cc361f65SGavin Atkinson } 1342*cc361f65SGavin Atkinson 1343*cc361f65SGavin Atkinson /* save current socket flags */ 1344*cc361f65SGavin Atkinson if ((flags = fcntl(sock, F_GETFL, 0)) == -1) { 1345*cc361f65SGavin Atkinson warn("Can't %s socket flags for connect to `%s:%s'", 1346*cc361f65SGavin Atkinson "save", hname, sname); 1347*cc361f65SGavin Atkinson return -1; 1348*cc361f65SGavin Atkinson } 1349*cc361f65SGavin Atkinson /* set non-blocking connect */ 1350*cc361f65SGavin Atkinson if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { 1351*cc361f65SGavin Atkinson warn("Can't set socket non-blocking for connect to `%s:%s'", 1352*cc361f65SGavin Atkinson hname, sname); 1353*cc361f65SGavin Atkinson return -1; 1354*cc361f65SGavin Atkinson } 1355f982db4aSGavin Atkinson 1356f982db4aSGavin Atkinson /* NOTE: we now must restore socket flags on successful exit */ 1357f982db4aSGavin Atkinson 1358f982db4aSGavin Atkinson pfd[0].fd = sock; 1359f982db4aSGavin Atkinson pfd[0].events = POLLIN|POLLOUT; 1360f982db4aSGavin Atkinson 1361f982db4aSGavin Atkinson if (quit_time > 0) { /* want a non default timeout */ 1362f982db4aSGavin Atkinson (void)gettimeofday(&endtime, NULL); 1363f982db4aSGavin Atkinson endtime.tv_sec += quit_time; /* determine end time */ 1364f982db4aSGavin Atkinson } 1365f982db4aSGavin Atkinson 1366f982db4aSGavin Atkinson rv = connect(sock, name, namelen); /* inititate the connection */ 1367f982db4aSGavin Atkinson if (rv == -1) { /* connection error */ 1368*cc361f65SGavin Atkinson if (errno != EINPROGRESS) { /* error isn't "please wait" */ 1369*cc361f65SGavin Atkinson connecterror: 1370*cc361f65SGavin Atkinson warn("Can't connect to `%s:%s'", hname, sname); 1371f982db4aSGavin Atkinson return -1; 1372*cc361f65SGavin Atkinson } 1373f982db4aSGavin Atkinson 1374f982db4aSGavin Atkinson /* connect EINPROGRESS; wait */ 1375f982db4aSGavin Atkinson do { 1376f982db4aSGavin Atkinson if (quit_time > 0) { /* determine timeout */ 1377f982db4aSGavin Atkinson (void)gettimeofday(&now, NULL); 1378f982db4aSGavin Atkinson timersub(&endtime, &now, &td); 1379f982db4aSGavin Atkinson timeout = td.tv_sec * 1000 + td.tv_usec/1000; 1380f982db4aSGavin Atkinson if (timeout < 0) 1381f982db4aSGavin Atkinson timeout = 0; 1382f982db4aSGavin Atkinson } else { 1383f982db4aSGavin Atkinson timeout = INFTIM; 1384f982db4aSGavin Atkinson } 1385f982db4aSGavin Atkinson pfd[0].revents = 0; 1386*cc361f65SGavin Atkinson rv = ftp_poll(pfd, 1, timeout); 1387f982db4aSGavin Atkinson /* loop until poll ! EINTR */ 1388f982db4aSGavin Atkinson } while (rv == -1 && errno == EINTR); 1389f982db4aSGavin Atkinson 1390f982db4aSGavin Atkinson if (rv == 0) { /* poll (connect) timed out */ 1391f982db4aSGavin Atkinson errno = ETIMEDOUT; 1392*cc361f65SGavin Atkinson goto connecterror; 1393f982db4aSGavin Atkinson } 1394f982db4aSGavin Atkinson 1395f982db4aSGavin Atkinson if (rv == -1) { /* poll error */ 1396*cc361f65SGavin Atkinson goto connecterror; 1397f982db4aSGavin Atkinson } else if (pfd[0].revents & (POLLIN|POLLOUT)) { 1398f982db4aSGavin Atkinson slen = sizeof(error); /* OK, or pending error */ 1399f982db4aSGavin Atkinson if (getsockopt(sock, SOL_SOCKET, SO_ERROR, 1400*cc361f65SGavin Atkinson &error, &slen) == -1) { 1401*cc361f65SGavin Atkinson /* Solaris pending error */ 1402*cc361f65SGavin Atkinson goto connecterror; 1403*cc361f65SGavin Atkinson } else if (error != 0) { 1404f982db4aSGavin Atkinson errno = error; /* BSD pending error */ 1405*cc361f65SGavin Atkinson goto connecterror; 1406f982db4aSGavin Atkinson } 1407f982db4aSGavin Atkinson } else { 1408f982db4aSGavin Atkinson errno = EBADF; /* this shouldn't happen ... */ 1409*cc361f65SGavin Atkinson goto connecterror; 1410f982db4aSGavin Atkinson } 1411f982db4aSGavin Atkinson } 1412f982db4aSGavin Atkinson 1413*cc361f65SGavin Atkinson if (fcntl(sock, F_SETFL, flags) == -1) { 1414*cc361f65SGavin Atkinson /* restore socket flags */ 1415*cc361f65SGavin Atkinson warn("Can't %s socket flags for connect to `%s:%s'", 1416*cc361f65SGavin Atkinson "restore", hname, sname); 1417f982db4aSGavin Atkinson return -1; 1418*cc361f65SGavin Atkinson } 1419f982db4aSGavin Atkinson return 0; 1420f982db4aSGavin Atkinson } 1421f982db4aSGavin Atkinson 1422f982db4aSGavin Atkinson /* 1423f982db4aSGavin Atkinson * Internal version of listen(2); sets socket buffer sizes first. 1424f982db4aSGavin Atkinson */ 1425f982db4aSGavin Atkinson int 1426*cc361f65SGavin Atkinson ftp_listen(int sock, int backlog) 1427f982db4aSGavin Atkinson { 1428f982db4aSGavin Atkinson 1429f982db4aSGavin Atkinson setupsockbufsize(sock); 1430f982db4aSGavin Atkinson return (listen(sock, backlog)); 1431f982db4aSGavin Atkinson } 1432f982db4aSGavin Atkinson 1433f982db4aSGavin Atkinson /* 1434f982db4aSGavin Atkinson * Internal version of poll(2), to allow reimplementation by select(2) 1435f982db4aSGavin Atkinson * on platforms without the former. 1436f982db4aSGavin Atkinson */ 1437f982db4aSGavin Atkinson int 1438*cc361f65SGavin Atkinson ftp_poll(struct pollfd *fds, int nfds, int timeout) 1439f982db4aSGavin Atkinson { 1440*cc361f65SGavin Atkinson #if defined(HAVE_POLL) 1441f982db4aSGavin Atkinson return poll(fds, nfds, timeout); 1442*cc361f65SGavin Atkinson 1443*cc361f65SGavin Atkinson #elif defined(HAVE_SELECT) 1444*cc361f65SGavin Atkinson /* implement poll(2) using select(2) */ 1445*cc361f65SGavin Atkinson fd_set rset, wset, xset; 1446*cc361f65SGavin Atkinson const int rsetflags = POLLIN | POLLRDNORM; 1447*cc361f65SGavin Atkinson const int wsetflags = POLLOUT | POLLWRNORM; 1448*cc361f65SGavin Atkinson const int xsetflags = POLLRDBAND; 1449*cc361f65SGavin Atkinson struct timeval tv, *ptv; 1450*cc361f65SGavin Atkinson int i, max, rv; 1451*cc361f65SGavin Atkinson 1452*cc361f65SGavin Atkinson FD_ZERO(&rset); /* build list of read & write events */ 1453*cc361f65SGavin Atkinson FD_ZERO(&wset); 1454*cc361f65SGavin Atkinson FD_ZERO(&xset); 1455*cc361f65SGavin Atkinson max = 0; 1456*cc361f65SGavin Atkinson for (i = 0; i < nfds; i++) { 1457*cc361f65SGavin Atkinson if (fds[i].fd > FD_SETSIZE) { 1458*cc361f65SGavin Atkinson warnx("can't select fd %d", fds[i].fd); 1459*cc361f65SGavin Atkinson errno = EINVAL; 1460*cc361f65SGavin Atkinson return -1; 1461*cc361f65SGavin Atkinson } else if (fds[i].fd > max) 1462*cc361f65SGavin Atkinson max = fds[i].fd; 1463*cc361f65SGavin Atkinson if (fds[i].events & rsetflags) 1464*cc361f65SGavin Atkinson FD_SET(fds[i].fd, &rset); 1465*cc361f65SGavin Atkinson if (fds[i].events & wsetflags) 1466*cc361f65SGavin Atkinson FD_SET(fds[i].fd, &wset); 1467*cc361f65SGavin Atkinson if (fds[i].events & xsetflags) 1468*cc361f65SGavin Atkinson FD_SET(fds[i].fd, &xset); 1469*cc361f65SGavin Atkinson } 1470*cc361f65SGavin Atkinson 1471*cc361f65SGavin Atkinson ptv = &tv; /* determine timeout */ 1472*cc361f65SGavin Atkinson if (timeout == -1) { /* wait forever */ 1473*cc361f65SGavin Atkinson ptv = NULL; 1474*cc361f65SGavin Atkinson } else if (timeout == 0) { /* poll once */ 1475*cc361f65SGavin Atkinson ptv->tv_sec = 0; 1476*cc361f65SGavin Atkinson ptv->tv_usec = 0; 1477*cc361f65SGavin Atkinson } 1478*cc361f65SGavin Atkinson else if (timeout != 0) { /* wait timeout milliseconds */ 1479*cc361f65SGavin Atkinson ptv->tv_sec = timeout / 1000; 1480*cc361f65SGavin Atkinson ptv->tv_usec = (timeout % 1000) * 1000; 1481*cc361f65SGavin Atkinson } 1482*cc361f65SGavin Atkinson rv = select(max + 1, &rset, &wset, &xset, ptv); 1483*cc361f65SGavin Atkinson if (rv <= 0) /* -1 == error, 0 == timeout */ 1484*cc361f65SGavin Atkinson return rv; 1485*cc361f65SGavin Atkinson 1486*cc361f65SGavin Atkinson for (i = 0; i < nfds; i++) { /* determine results */ 1487*cc361f65SGavin Atkinson if (FD_ISSET(fds[i].fd, &rset)) 1488*cc361f65SGavin Atkinson fds[i].revents |= (fds[i].events & rsetflags); 1489*cc361f65SGavin Atkinson if (FD_ISSET(fds[i].fd, &wset)) 1490*cc361f65SGavin Atkinson fds[i].revents |= (fds[i].events & wsetflags); 1491*cc361f65SGavin Atkinson if (FD_ISSET(fds[i].fd, &xset)) 1492*cc361f65SGavin Atkinson fds[i].revents |= (fds[i].events & xsetflags); 1493*cc361f65SGavin Atkinson } 1494*cc361f65SGavin Atkinson return rv; 1495*cc361f65SGavin Atkinson 1496*cc361f65SGavin Atkinson #else 1497*cc361f65SGavin Atkinson # error no way to implement xpoll 1498*cc361f65SGavin Atkinson #endif 1499f982db4aSGavin Atkinson } 1500f982db4aSGavin Atkinson 1501f982db4aSGavin Atkinson /* 1502f982db4aSGavin Atkinson * malloc() with inbuilt error checking 1503f982db4aSGavin Atkinson */ 1504f982db4aSGavin Atkinson void * 1505*cc361f65SGavin Atkinson ftp_malloc(size_t size) 1506f982db4aSGavin Atkinson { 1507f982db4aSGavin Atkinson void *p; 1508f982db4aSGavin Atkinson 1509f982db4aSGavin Atkinson p = malloc(size); 1510f982db4aSGavin Atkinson if (p == NULL) 1511f982db4aSGavin Atkinson err(1, "Unable to allocate %ld bytes of memory", (long)size); 1512f982db4aSGavin Atkinson return (p); 1513f982db4aSGavin Atkinson } 1514f982db4aSGavin Atkinson 1515f982db4aSGavin Atkinson /* 1516f982db4aSGavin Atkinson * sl_init() with inbuilt error checking 1517f982db4aSGavin Atkinson */ 1518f982db4aSGavin Atkinson StringList * 1519*cc361f65SGavin Atkinson ftp_sl_init(void) 1520f982db4aSGavin Atkinson { 1521f982db4aSGavin Atkinson StringList *p; 1522f982db4aSGavin Atkinson 1523f982db4aSGavin Atkinson p = sl_init(); 1524f982db4aSGavin Atkinson if (p == NULL) 1525f982db4aSGavin Atkinson err(1, "Unable to allocate memory for stringlist"); 1526f982db4aSGavin Atkinson return (p); 1527f982db4aSGavin Atkinson } 1528f982db4aSGavin Atkinson 1529f982db4aSGavin Atkinson /* 1530f982db4aSGavin Atkinson * sl_add() with inbuilt error checking 1531f982db4aSGavin Atkinson */ 1532f982db4aSGavin Atkinson void 1533*cc361f65SGavin Atkinson ftp_sl_add(StringList *sl, char *i) 1534f982db4aSGavin Atkinson { 1535f982db4aSGavin Atkinson 1536f982db4aSGavin Atkinson if (sl_add(sl, i) == -1) 1537f982db4aSGavin Atkinson err(1, "Unable to add `%s' to stringlist", i); 1538f982db4aSGavin Atkinson } 1539f982db4aSGavin Atkinson 1540f982db4aSGavin Atkinson /* 1541f982db4aSGavin Atkinson * strdup() with inbuilt error checking 1542f982db4aSGavin Atkinson */ 1543f982db4aSGavin Atkinson char * 1544*cc361f65SGavin Atkinson ftp_strdup(const char *str) 1545f982db4aSGavin Atkinson { 1546f982db4aSGavin Atkinson char *s; 1547f982db4aSGavin Atkinson 1548f982db4aSGavin Atkinson if (str == NULL) 1549*cc361f65SGavin Atkinson errx(1, "ftp_strdup: called with NULL argument"); 1550f982db4aSGavin Atkinson s = strdup(str); 1551f982db4aSGavin Atkinson if (s == NULL) 1552f982db4aSGavin Atkinson err(1, "Unable to allocate memory for string copy"); 1553f982db4aSGavin Atkinson return (s); 1554f982db4aSGavin Atkinson } 1555