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