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