From 04203a83a6848544e5157be39991291ba0c82525 Mon Sep 17 00:00:00 2001 From: Thomas Cort Date: Fri, 5 Apr 2013 19:02:06 +0000 Subject: [PATCH] Importing usr.bin/ftp --- commands/Makefile | 2 +- commands/ftp101/Makefile | 11 - commands/ftp101/README | 39 - commands/ftp101/crc.c | 132 -- commands/ftp101/file.c | 935 ------------- commands/ftp101/file.h | 33 - commands/ftp101/ftp.1 | 148 -- commands/ftp101/ftp.c | 407 ------ commands/ftp101/ftp.h | 39 - commands/ftp101/local.c | 134 -- commands/ftp101/local.h | 15 - commands/ftp101/net.c | 569 -------- commands/ftp101/net.h | 15 - commands/ftp101/other.c | 173 --- commands/ftp101/other.h | 17 - commands/ftp101/xfer.c | 305 ---- commands/ftp101/xfer.h | 8 - releasetools/nbsd_ports | 1 + usr.bin/Makefile | 2 +- usr.bin/ftp/Makefile | 40 + usr.bin/ftp/cmds.c | 2795 +++++++++++++++++++++++++++++++++++++ usr.bin/ftp/cmdtab.c | 307 ++++ usr.bin/ftp/complete.c | 432 ++++++ usr.bin/ftp/domacro.c | 145 ++ usr.bin/ftp/extern.h | 248 ++++ usr.bin/ftp/fetch.c | 1979 ++++++++++++++++++++++++++ usr.bin/ftp/ftp.1 | 2421 ++++++++++++++++++++++++++++++++ usr.bin/ftp/ftp.c | 2178 +++++++++++++++++++++++++++++ usr.bin/ftp/ftp_var.h | 354 +++++ usr.bin/ftp/main.c | 1057 ++++++++++++++ usr.bin/ftp/progressbar.c | 472 +++++++ usr.bin/ftp/progressbar.h | 92 ++ usr.bin/ftp/ruserpass.c | 318 +++++ usr.bin/ftp/ssl.c | 608 ++++++++ usr.bin/ftp/ssl.h | 62 + usr.bin/ftp/util.c | 1543 ++++++++++++++++++++ usr.bin/ftp/version.h | 38 + 37 files changed, 15092 insertions(+), 2982 deletions(-) delete mode 100644 commands/ftp101/Makefile delete mode 100644 commands/ftp101/README delete mode 100644 commands/ftp101/crc.c delete mode 100644 commands/ftp101/file.c delete mode 100644 commands/ftp101/file.h delete mode 100644 commands/ftp101/ftp.1 delete mode 100644 commands/ftp101/ftp.c delete mode 100644 commands/ftp101/ftp.h delete mode 100644 commands/ftp101/local.c delete mode 100644 commands/ftp101/local.h delete mode 100644 commands/ftp101/net.c delete mode 100644 commands/ftp101/net.h delete mode 100644 commands/ftp101/other.c delete mode 100644 commands/ftp101/other.h delete mode 100644 commands/ftp101/xfer.c delete mode 100644 commands/ftp101/xfer.h create mode 100644 usr.bin/ftp/Makefile create mode 100644 usr.bin/ftp/cmds.c create mode 100644 usr.bin/ftp/cmdtab.c create mode 100644 usr.bin/ftp/complete.c create mode 100644 usr.bin/ftp/domacro.c create mode 100644 usr.bin/ftp/extern.h create mode 100644 usr.bin/ftp/fetch.c create mode 100644 usr.bin/ftp/ftp.1 create mode 100644 usr.bin/ftp/ftp.c create mode 100644 usr.bin/ftp/ftp_var.h create mode 100644 usr.bin/ftp/main.c create mode 100644 usr.bin/ftp/progressbar.c create mode 100644 usr.bin/ftp/progressbar.h create mode 100644 usr.bin/ftp/ruserpass.c create mode 100644 usr.bin/ftp/ssl.c create mode 100644 usr.bin/ftp/ssl.h create mode 100644 usr.bin/ftp/util.c create mode 100644 usr.bin/ftp/version.h diff --git a/commands/Makefile b/commands/Makefile index 7a9d76003..3cd141501 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -10,7 +10,7 @@ SUBDIR= add_route arp ash at backup basename btrace \ dhrystone diff dirname diskctl dumpcore \ eject env expand factor fbdctl \ find finger fingerd fix fold format fortune fsck.mfs \ - ftp101 gcore gcov-pull getty grep hexdump host \ + gcore gcov-pull getty grep hexdump host \ hostaddr id ifconfig ifdef \ intr ipcrm ipcs irdpd isoread last \ less loadkeys loadramdisk logger look lp \ diff --git a/commands/ftp101/Makefile b/commands/ftp101/Makefile deleted file mode 100644 index 553f02a2c..000000000 --- a/commands/ftp101/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# Makefile for ftp -# -# 01/25/96 Initial Release Michael Temari -# 03/08/00 Michael Temari, -# 02/07/05 v. 1.01 Michael Temari, - -PROG= ftp -SRCS= ftp.c local.c file.c xfer.c other.c net.c crc.c -CPPFLAGS+= -DCRC_ONLY - -.include diff --git a/commands/ftp101/README b/commands/ftp101/README deleted file mode 100644 index a60017728..000000000 --- a/commands/ftp101/README +++ /dev/null @@ -1,39 +0,0 @@ -ftp101 --- An FTP client program for Minix 2.0 -written by Michael Temari release 1.01a 2006-06-07 - -Full download: ftp101.tar.Z - -FTP is the File Transfer Protocol client that allows you to connect to -a remote FTP server. - -This version should work equally well with Minix 2 and Minix 3. - -Release 1.01a 2006-06-07: minor documentation edits -Release 1.01 2005-02-07: minor bug fix -Release 1.00 2003-12-14: added "ver" command to display current version - and an ftp(1) man page. - -Installation: unpack the tarball, it will create an ftp101 directory. Although -this is a replacement for the ftp client provided in the Minix 2.0.0 and later -distributions, it is suggested you unpack and compile in the /usr/local/src -directory. - -zcat < ftp101.tar.Z | tar xvfp - - -Invoking make -n will show you what the Makefile will do. - -make (or make ftp) compiles a new ftp binary, leaving it in the source -directory. - -make install compiles the binary and installs it in /usr/bin. - -make installman installs the man page in /usr/local/man/man1. The -directory must exist. - -Note: there is a bug in the version of the ftp client distributed with -Minix 2.0.2 and 2.0.3 that causes a \r (0xd) character to be appended -to file names in the destination directory when files are downloaded in -binary mode using the mget command. The bug was corrected in a release -prior to 1.00. - -Notes by ASW revised 2006-06-07 diff --git a/commands/ftp101/crc.c b/commands/ftp101/crc.c deleted file mode 100644 index b6974cc22..000000000 --- a/commands/ftp101/crc.c +++ /dev/null @@ -1,132 +0,0 @@ -/* Compute checksum Author: Johan W. Stevenson */ - -/* Copyright 1988 by Johan W. Stevenson */ - -#include -#include -#include - -#if !CRC_ONLY - -int errs; - -#if __STDC__ -int main(int argc, char **argv); -void crc(char *fname); -#else -void crc(); -#endif - -int main(argc, argv) -int argc; -char **argv; -{ - char line[256]; - - if (argc <= 1) - crc((char *) 0); - else if (argc == 2 && strcmp(argv[1], "-") == 0) - while (fgets(line, sizeof line, stdin) != NULL) { - if (line[strlen(line) - 1] == '\n') - line[strlen(line) - 1] = '\0'; - crc(line); - } - else - do { - crc(argv[1]); - argv++; - argc--; - } while (argc > 1); - return(errs != 0); -} - -#endif -/* Crctab calculated by Mark G. Mendel, Network Systems Corporation */ -static unsigned short crctab[256] = { - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 -}; - -/* Updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. - * NOTE: First argument must be in range 0 to 255. - * Second argument is referenced twice. - * - * Programmers may incorporate any or all code into their programs, - * giving proper credit within the source. Publication of the - * source routines is permitted so long as proper credit is given - * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, - * Omen Technology. - */ - -#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp) - -#if CRC_ONLY -unsigned short crc(char *fname) -#else -void crc(fname) -char *fname; -#endif -{ - register int c; - register long len = 0; - register unsigned short crc = 0; - register FILE *fp; - -#if CRC_ONLY - if((fp = fopen(fname, "r")) == NULL) - return 0; -#else - if (fname == NULL) - fp = stdin; - else if ((fp = fopen(fname, "r")) == NULL) { - fprintf(stderr, "crc: cannot open %s\n", fname); - errs++; - return; - } -#endif - while ((c = getc(fp)) != EOF) { - len++; - crc = updcrc(c, crc); - } -#if CRC_ONLY - fclose(fp); - return crc; -#else - printf("%05u %6ld", crc, len); - if (fname) { - printf(" %s", fname); - fclose(fp); - } - printf("\n"); -#endif -} diff --git a/commands/ftp101/file.c b/commands/ftp101/file.c deleted file mode 100644 index 3c53e9331..000000000 --- a/commands/ftp101/file.c +++ /dev/null @@ -1,935 +0,0 @@ -/* file.c Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * This file is part of ftp. - * - * - * 01/25/96 Initial Release Michael Temari, - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ftp.h" -#include "file.h" -#include "net.h" - -static char *dir(char *path, int full); -static int asciisize(int fd, off_t *filesize); -static off_t asciisetsize(int fd, off_t filesize); -static int cloneit(char *file, int mode); - -#if (__WORD_SIZE == 4) -static char buffer[8192]; -#else -static char buffer[2048]; -#endif -static char line2[512]; - -static char *dir(path, full) -char *path; -int full; -{ -char cmd[128]; -static char name[32]; - - tmpnam(name); - - if(full) - sprintf(cmd, "ls -l %s > %s", path, name); - else - sprintf(cmd, "ls -dA %s > %s", path, name); - - system(cmd); - - return(name); -} - -int DOascii() -{ -int s; - - if(DOcmdcheck()) - return(0); - - s = DOcommand("TYPE", "A"); - - type = TYPE_A; - - return(s); -} - -int DObinary() -{ -int s; - - if(DOcmdcheck()) - return(0); - - s = DOcommand("TYPE", "I"); - - type = TYPE_I; - - return(s); -} - -int DOblock() -{ -int s; - - if(DOcmdcheck()) - return(0); - - s = DOcommand("MODE", "B"); - - mode = MODE_B; - - return(s); -} - -int DOstream() -{ -int s; - - if(DOcmdcheck()) - return(0); - - s = DOcommand("MODE", "S"); - - mode = MODE_S; - - return(s); -} - -int DOpwd() -{ -int s; - - if(DOcmdcheck()) - return(0); - - s = DOcommand("PWD", ""); - - if(s == 500 || s == 502) - s = DOcommand("XPWD", ""); - - return(s); -} - -int DOcd() -{ -char *path; -int s; - - if(DOcmdcheck()) - return(0); - - path = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Path: ", line2, sizeof(line2)) < 0) - return(-1); - path = line2; - } - - if(!strcmp(path, "..")) - s = DOcommand("CDUP", ""); - else - s = DOcommand("CWD", path); - - if(s == 500 || s == 502) { - if(!strcmp(path, "..")) - s = DOcommand("XCUP", ""); - else - s = DOcommand("XCWD", path); - } - - return(s); -} - -int DOmkdir() -{ -char *path; -int s; - - if(DOcmdcheck()) - return(0); - - path = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Directory: ", line2, sizeof(line2)) < 0) - return(-1); - path = line2; - } - - s = DOcommand("MKD", path); - - if(s == 500 || s == 502) - s = DOcommand("XMKD", path); - - return(s); -} - -int DOrmdir() -{ -char *path; -int s; - - if(DOcmdcheck()) - return(0); - - path = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Directory: ", line2, sizeof(line2)) < 0) - return(-1); - path = line2; - } - - s = DOcommand("RMD", path); - - if(s == 500 || s == 502) - s = DOcommand("XRMD", path); - - return(s); -} - -int DOdelete() -{ -char *file; - - if(DOcmdcheck()) - return(0); - - file = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("File: ", line2, sizeof(line2)) < 0) - return(-1); - file = line2; - } - - return(DOcommand("DELE", file)); -} - -int DOmdtm() -{ -char *file; - - if(DOcmdcheck()) - return(0); - - file = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("File: ", line2, sizeof(line2)) < 0) - return(-1); - file = line2; - } - - return(DOcommand("MDTM", file)); -} - -int DOsize() -{ -char *file; - - if(DOcmdcheck()) - return(0); - - file = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("File: ", line2, sizeof(line2)) < 0) - return(-1); - file = line2; - } - - return(DOcommand("SIZE", file)); -} - -int DOstat() -{ -char *file; - - if(cmdargc < 2) { - if(!linkopen) { - printf("You must \"OPEN\" a connection first.\n"); - return(0); - } else { - return(DOcommand("STAT", "")); - } - } - if(DOcmdcheck()) - return(0); - - file = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("File: ", line2, sizeof(line2)) < 0) - return(-1); - file = line2; - } - - return(DOcommand("STAT", file)); -} - -int DOlist() -{ -char *path; -char *local; -int fd; -int s; - - if(DOcmdcheck()) - return(0); - - path = cmdargv[1]; - - if(cmdargc < 2) - path = ""; - - if(cmdargc < 3) - local = ""; - else - local = cmdargv[2]; - - if(*local == '\0') - fd = 1; - else - fd = open(local, O_WRONLY | O_CREAT | O_TRUNC, 0666); - - if(fd < 0) { - printf("Could not open local file %s. Error %s\n", local, strerror(errno)); - return(0); - } - - s = DOdata("LIST", path, RETR, fd); - - if(fd > 2) - close(fd); - - return(s); -} - -int DOnlst() -{ -char *path; -char *local; -int fd; -int s; - - if(DOcmdcheck()) - return(0); - - path = cmdargv[1]; - - if(cmdargc < 2) - path = ""; - - if(cmdargc < 3) - local = ""; - else - local = cmdargv[2]; - - if(*local == '\0') - fd = 1; - else - fd = open(local, O_WRONLY | O_CREAT | O_TRUNC, 0666); - - if(fd < 0) { - printf("Could not open local file %s. Error %s\n", local, strerror(errno)); - return(0); - } - - s = DOdata("NLST", path, RETR, fd); - - if(fd > 2) - close(fd); - - return(s); -} - -int DOretr() -{ -char *file, *localfile; -int fd; -int s; - - if(DOcmdcheck()) - return(0); - - file = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Remote File: ", line2, sizeof(line2)) < 0) - return(-1); - file = line2; - } - - if(cmdargc < 3) - localfile = file; - else - localfile = cmdargv[2]; - - fd = open(localfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); - - if(fd < 0) { - printf("Could not open local file %s. Error %s\n", localfile, strerror(errno)); - return(0); - } - - s = DOdata("RETR", file, RETR, fd); - - close(fd); - - return(s); -} - -int DOrretr() -{ -char *file, *localfile; -int fd; -int s; -off_t filesize; -char restart[16]; - - if(DOcmdcheck()) - return(0); - - file = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Remote File: ", line2, sizeof(line2)) < 0) - return(-1); - file = line2; - } - - if(cmdargc < 3) - localfile = file; - else - localfile = cmdargv[2]; - - fd = open(localfile, O_RDWR); - - if(fd < 0) { - printf("Could not open local file %s. Error %s\n", localfile, strerror(errno)); - return(0); - } - - if(type == TYPE_A) { - if(asciisize(fd, &filesize)) { - printf("Could not determine ascii file size of %s\n", localfile); - close(fd); - return(0); - } - } else - filesize = lseek(fd, 0, SEEK_END); - - sprintf(restart, "%u", filesize); - - s = DOcommand("REST", restart); - - if(s != 350) { - close(fd); - return(s); - } - - s = DOdata("RETR", file, RETR, fd); - - close(fd); - - return(s); -} - -char *ttime(time_t t) -{ -struct tm *tm; -static char tbuf[16]; - - tm = localtime(&t); - - sprintf(tbuf, "%04d%02d%02d%02d%02d.%02d", - tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); - - return(tbuf); -} - -static int cloneit(file, mode) -char *file; -int mode; -{ -int opr; -int s; -int ss; -struct stat st; -static unsigned short lcrc; -static unsigned short ccrc; -static unsigned long csize; -static char ft; -static unsigned long maj; -static unsigned long min; -static unsigned long uid; -static unsigned long gid; -static unsigned long fmode; -static unsigned long size; -static unsigned long mtime; -struct utimbuf ut; -unsigned short crc(char *fname); - - if(mode == 1) { - /* see if file exists locally */ - ss = stat(file, &st); - - opr = printreply; - printreply = 0; - s = DOcommand("SITE FDET", file); - printreply = opr; - - if((s / 100) != 2) - return(-1); - - sscanf(reply, "%*d %c%lu%lu%lu%lu%lu%lu%lu", - &ft, &maj, &min, &uid, &gid, &fmode, &size, &mtime); - - if(ft == 'f') { - opr = printreply; - printreply = 0; - s = DOcommand("SITE CCRC", file); - printreply = opr; - if((s / 100) != 2) - return(-1); - - sscanf(reply, "%*hu %*s%u%lu", &ccrc, &csize); - if(ss < 0) return(-1); - lcrc = crc(file); - if(size != csize || size != st.st_size || ccrc != lcrc) - return(-1); - } else - if(ss < 0 && ft == 'd') { - s = mkdir(file, fmode); - printf("mkdir %s\n", file); - } else - if((ss < 0) && (ft == 'b' || ft == 'c' || ft == 'p')) { - s = mknod(file, fmode, maj << 8 | min); - printf("mknod %s %lu %lu\n", file, maj, min); - } else - return(0); - } - ss = stat(file, &st); - if(ss < 0) - return(-1); - if(st.st_uid != uid || st.st_gid != gid) { - s = chown(file, uid, gid); - printf("chown %lu:%lu %s\n", uid, gid, file); - } - if(st.st_mode != fmode) { - s = chmod(file, fmode); - printf("chmod %04lo %s\n", fmode, file); - } - if(st.st_mtime != mtime) { - ut.actime = mtime; - ut.modtime = mtime; - s = utime(file, &ut); - printf("touch -m -t %s %s\n", ttime(mtime), file); - } - - return(0); -} - -int DOMretr() -{ -char *files; -int fd, s; -char *p; -FILE *fp; -char name[32]; - - if(DOcmdcheck()) - return(0); - - files = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Files: ", line2, sizeof(line2)) < 0) - return(-1); - files = line2; - } - - tmpnam(name); - - fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666); - - if(fd < 0) { - printf("Could not open local file %s. Error %s\n", name, strerror(errno)); - return(0); - } - - s = DOdata("NLST", files, RETR, fd); - - close(fd); - - if(s == 226 || s == 250) { - fp = fopen(name, "r"); - unlink(name); - if(fp == (FILE *)NULL) { - printf("Unable to open file listing.\n"); - return(0); - } - while(fgets(line2, sizeof(line2), fp) != (char *)NULL) { - p = line2 + strlen(line2) - 1; - if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; - if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; - printf("Retrieving file: %s\n", line2); fflush(stdout); - fd = open(line2, O_WRONLY | O_CREAT | O_TRUNC, 0666); - if(fd < 0) - printf("Unable to open local file %s\n", line2); - else { - s = DOdata("RETR", line2, RETR, fd); - close(fd); - if(s < 0) break; - } - } - fclose(fp); - } else - unlink(name); - - return(s); -} - -int DOappe() -{ -char *file, *remotefile; -int fd; -int s; - - if(DOcmdcheck()) - return(0); - - file = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Local File: ", line2, sizeof(line2)) < 0) - return(-1); - file = line2; - } - - if(cmdargc < 3) - remotefile = file; - else - remotefile = cmdargv[2]; - - fd = open(file, O_RDONLY); - - if(fd < 0) { - printf("Could not open local file %s. Error %s\n", file, strerror(errno)); - return(0); - } - - s = DOdata("APPE", remotefile, STOR, fd); - - close(fd); - - return(s); -} - -int DOstor() -{ -char *file, *remotefile; -int fd; -int s; - - if(DOcmdcheck()) - return(0); - - file = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Local File: ", line2, sizeof(line2)) < 0) - return(-1); - file = line2; - } - - if(cmdargc < 3) - remotefile = file; - else - remotefile = cmdargv[2]; - - fd = open(file, O_RDONLY); - - if(fd < 0) { - printf("Could not open local file %s. Error %s\n", file, strerror(errno)); - return(0); - } - - s = DOdata("STOR", remotefile, STOR, fd); - - close(fd); - - return(s); -} - -int DOrstor() -{ -char *file, *remotefile; -int fd; -int s; -off_t filesize, rmtsize; -char restart[16]; - - if(DOcmdcheck()) - return(0); - - file = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Local File: ", line2, sizeof(line2)) < 0) - return(-1); - file = line2; - } - - if(cmdargc < 3) - remotefile = file; - else - remotefile = cmdargv[2]; - - s = DOcommand("SIZE", remotefile); - - if(s != 215) - return(s); - - rmtsize = atol(reply+4); - - fd = open(file, O_RDONLY); - - if(fd < 0) { - printf("Could not open local file %s. Error %s\n", file, strerror(errno)); - return(0); - } - - if(type == TYPE_A) - filesize = asciisetsize(fd, rmtsize); - else - filesize = lseek(fd, rmtsize, SEEK_SET); - - if(filesize != rmtsize) { - printf("Could not set file start of %s\n", file); - close(fd); - return(0); - } - - sprintf(restart, "%u", rmtsize); - - s = DOcommand("REST", restart); - - if(s != 350) { - close(fd); - return(s); - } - - s = DOdata("STOR", remotefile, STOR, fd); - - close(fd); - - return(s); -} - -int DOstou() -{ -char *file, *remotefile; -int fd; -int s; - - if(DOcmdcheck()) - return(0); - - file = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Local File: ", line2, sizeof(line2)) < 0) - return(-1); - file = line2; - } - - if(cmdargc < 3) - remotefile = file; - else - remotefile = cmdargv[2]; - - fd = open(file, O_RDONLY); - - if(fd < 0) { - printf("Could not open local file %s. Error %s\n", file, strerror(errno)); - return(0); - } - - s = DOdata("STOU", remotefile, STOR, fd); - - close(fd); - - return(s); -} - -int DOMstor() -{ -char *files; -char *name; -char *p; -int fd, s; -FILE *fp; - - if(DOcmdcheck()) - return(0); - - files = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Files: ", line2, sizeof(line2)) < 0) - return(-1); - files = line2; - } - - name = dir(files, 0); - - fp = fopen(name, "r"); - - if(fp == (FILE *)NULL) { - printf("Unable to open listing file.\n"); - return(0); - } - - while(fgets(line2, sizeof(line2), fp) != (char *)NULL) { - p = line2 + strlen(line2) - 1; - if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; - if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; - printf("Sending file: %s\n", line2); fflush(stdout); - fd = open(line2, O_RDONLY); - if(fd < 0) - printf("Unable to open local file %s\n", line2); - else { - s = DOdata("STOR", line2, STOR, fd); - close(fd); - if(s < 0) break; - } - } - fclose(fp); - unlink(name); - - return(s); -} - -static int asciisize(fd, filesize) -int fd; -off_t *filesize; -{ -unsigned long count; -char *p, *pp; -int cnt; - - count = 0; - - while((cnt = read(fd, buffer, sizeof(buffer))) > 0) { - p = buffer; pp = buffer + cnt; - count += cnt; - while(p < pp) - if(*p++ == '\n') - count++; - } - - if(cnt == 0) { - *filesize = count; - return(0); - } - - return(-1); -} - -static off_t asciisetsize(fd, filesize) -int fd; -off_t filesize; -{ -off_t sp; -int s; - - sp = 0; - - while(sp < filesize) { - s = read(fd, buffer, 1); - if(s < 0) - return(-1); - if(s == 0) break; - sp++; - if(*buffer == '\n') - sp++; - } - - return(sp); -} - -int DOclone() -{ -char *files; -int fd, s; -char *p; -FILE *fp; -char name[32]; - - if(DOcmdcheck()) - return(0); - - files = cmdargv[1]; - - tmpnam(name); - - fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666); - - if(fd < 0) { - printf("Could not open local file %s. Error %s\n", name, strerror(errno)); - return(0); - } - - s = DOdata("NLST", files, RETR, fd); - - close(fd); - - if(s == 226 || s == 250) { - fp = fopen(name, "r"); - unlink(name); - if(fp == (FILE *)NULL) { - printf("Unable to open file listing.\n"); - return(0); - } - while(fgets(line2, sizeof(line2), fp) != (char *)NULL) { - p = line2 + strlen(line2) - 1; - if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; - if(p >= line2 && (*p == '\r' || *p == '\n')) *p-- = '\0'; - cmdargv[1] = line2; - if(cloneit(line2, 1)) { - printf("Retrieving file: %s\n", line2); fflush(stdout); - fd = open(line2, O_WRONLY | O_CREAT | O_TRUNC, 0666); - if(fd < 0) - printf("Unable to open local file %s\n", line2); - else { - s = DOdata("RETR", line2, RETR, fd); - close(fd); - if(s < 0) break; - } - s = cloneit(line2, 2); - } - } - fclose(fp); - } else - unlink(name); - - return(s); -} diff --git a/commands/ftp101/file.h b/commands/ftp101/file.h deleted file mode 100644 index bc3ed7f01..000000000 --- a/commands/ftp101/file.h +++ /dev/null @@ -1,33 +0,0 @@ -/* file.h Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * This file is part of ftp. - * - * - * 01/25/96 Initial Release Michael Temari, - */ - -int recvfile(int fd, int fdin); -int sendfile(int fd, int fdout); -int DOascii(void); -int DObinary(void); -int DOblock(void); -int DOstream(void); -int DOpwd(void); -int DOcd(void); -int DOmkdir(void); -int DOrmdir(void); -int DOdelete(void); -int DOmdtm(void); -int DOsize(void); -int DOstat(void); -int DOlist(void); -int DOnlst(void); -int DOretr(void); -int DOrretr(void); -int DOMretr(void); -int DOappe(void); -int DOstor(void); -int DOrstor(void); -int DOstou(void); -int DOMstor(void); -int DOclone(void); diff --git a/commands/ftp101/ftp.1 b/commands/ftp101/ftp.1 deleted file mode 100644 index 078b6a813..000000000 --- a/commands/ftp101/ftp.1 +++ /dev/null @@ -1,148 +0,0 @@ -.TH FTP 1 -.SH NAME -ftp \- a File Transfer Protocol client for Minix -.SH SYNOPSIS -.B ftp -.RI [ server_name ] -.SH DESCRIPTION -.B Ftp -is a File Transfer Protocol client for Minix written by Michael Temari. -.P -There are no command line options for -.B ftp -except for the optional server name, which may be either a numeric IP address -or a domain name resolvable by DNS. -.P -If a server name is specified a connection attempt will be made, and you -will be prompted for a user name and password by the remote system. -Following the login (or immediately, if no server name was specified), the -.br -.B ftp> -.br -prompt is displayed. The following commands are accepted at the prompt: -.P -Command: Description -.br -! Escape to a shell -.br -append Append a file to remote host -.br -ascii Set file transfer type to ascii -.br -binary Set file transfer type to binary -.br -block Set file transfer mode to block -.br -bye Close connection and exit -.br -cd Change directory on remote host -.br -close Close connection -.br -clone Clone a file -.br -del Remove file on remote host -.br -dir Display long form remote host directory listing -.br -exit Close connection and exit -.br -get Retrieve a file from remote host -.br -help Display this text -.br -lcd Change directory on local host -.br -ldir Display long form local host directory listing -.br -lls Display local host directory listing -.br -lmkdir Create directory on local host -.br -lpwd Display current directory on local host -.br -lrmdir Remove directory on local host -.br -ls Display remote host directory listing -.br -mget Retrieve multiple files from remote host -.br -mkdir Create directory on remote host -.br -mod Get file modification time -.br -mput Send multiple files to remote host -.br -noop Send the ftp NOOP command -.br -open Open connection to remote host -.br -pass Enter remote user password -.br -passive Toggle passive mode -.br -put Send a file to remote host -.br -putu Send a file to remote host(unique) -.br -pwd Display current directory on remote host -.br -quit Close connection and exit -.br -quote Send raw ftp command to remote host -.br -reget Restart a partial file retrieve from remote host -.br -remotehelp Display ftp commands implemented on remote host -.br -reput Restart a partial file send to remote host -.br -rm Remove file on remote host -.br -rmdir Remove directory on remote host -.br -site Send a site specific command -.br -size Get file size information -.br -status Get connection/file status information -.br -stream Set file transfer mode to stream -.br -system Get remote system type information -.br -user Enter remote user information -.br -ver Display client version information - -.SH "SEE ALSO" -.BR ftpd (8), -.BR ftpget (1). -.SH NOTES -The FTP protocol passes unencrypted usernames and passwords to clients, -so they are potentially exposed to evildoers with network sniffers. So be -wary of using this to exchange files between your own accounts. Obviously -if you have a root account on another system and the remote system will -accept a login as root this is extremely dangerous. (Many ftp servers will -not allow a connection by root). -.P -Text-mode (ASCII) transfers are the default mode, be sure to enter the -"binary" command if you are downloading a program file or a compressed -archive, in fact anything other than a text file from a machine with a -different text-file format than Minix uses. -.P -If you are behind a firewall you probably need to use passive mode to -successfully transfer files. - -.SH BUGS -None are known, but there may be some unknown ones. Version 1.00 corrects -a bug in previous versions that would append a \\r (0xd) character to file -names on the destination when an mget transfer was used in binary mode. - -.SH AUTHOR -The Minix httpd server was created by and is maintained by Michael Temari -. The earliest version was released in 1992, for use -with Michael's TNet networking extensions for Minix 1.5. -.P -Man page compiled by Al Woodhull -.\" updated 2006-06-18 diff --git a/commands/ftp101/ftp.c b/commands/ftp101/ftp.c deleted file mode 100644 index 3e0e80097..000000000 --- a/commands/ftp101/ftp.c +++ /dev/null @@ -1,407 +0,0 @@ -/* ftp.c Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * ftp An ftp client program for use with TNET. - * - * Usage: ftp [host] - * - * Version: 0.10 06/21/92 (pre-release not yet completed) - * 0.20 07/01/92 - * 0.30 01/15/96 (Minix 1.7.1 initial release) - * 0.40 08/27/96 - * 0.50 03/08/00 - * 1.00 12/12/03 (added ver command) - * 1.01 02/07/05 - * - * Author: Michael Temari, - */ - -#include -#include -#include -#include -#include -#include - -#include "ftp.h" -#include "local.h" -#include "file.h" -#include "other.h" -#include "net.h" - -char *FtpVersion = "1.01 02/07/05"; - -int linkopen; -int loggedin; -int type; -int format; -int mode; -int structure; -int passive; -int atty; - -int cmdargc; -char *cmdargv[NUMARGS]; - -int printreply = 1; -char reply[1024]; - -/* Already declared in stdio.h */ -#define getline ftp_getline - -static void makeargs(char *buff); -int DOver(void); -int DOhelp(void); -static int getline(char *line, int len); -int main(int argc, char *argv[]); - - -static void makeargs(buff) -char *buff; -{ -int i; -char *p; - - for(i = 0; i < NUMARGS; i++) - cmdargv[i] = (char *)0; - - p = buff + strlen(buff) - 1; - while(p >= buff) - if(*p == '\r' || *p == '\n' || isspace(*p)) - *p-- = '\0'; - else - break; - - p = buff; - cmdargc = 0; - while(cmdargc < NUMARGS) { - while(*p && isspace(*p)) - p++; - if(*p == '\0') - break; - cmdargv[cmdargc++] = p; - while(*p && !isspace(*p)) { - if(cmdargc == 1) - *p = tolower(*p); - p++; - } - if(*p == '\0') - break; - *p = '\0'; - p++; - } -} - -int readline(prompt, buff, len) -char *prompt; -char *buff; -int len; -{ -char *p; - - printf(prompt); fflush(stdout); - - if(fgets(buff, len, stdin) == (char *)NULL) { - printf("\nEnd of file on input!\n"); - return(-1); - } - - p = buff + strlen(buff) - 1; - while(p >= buff) - if(*p == '\r' || *p == '\n' || isspace(*p)) - *p-- = '\0'; - else - break; - - if(!atty) { - printf("%s\n", buff); - fflush(stdout); - } - - return(0); -} - -static int getline(line, len) -char *line; -int len; -{ -int s; -int gotcr; - - /* leave room for at end for null */ - len--; - - /* got to be able to put in at least 1 character */ - if(len < 1) - return(-1); - - gotcr = 0; - while(len-- > 0) { - s = read(ftpcomm_fd, line, 1); - if(s != 1) - return(-1); - if(*line == '\n') - break; - gotcr = (*line == '\r'); - line++; - } - if(gotcr) - --line; - - *line = '\0'; - - return(0); -} - -int DOgetreply() -{ -int firsttime; -int s; -char code[4]; - - do { - firsttime = 1; - do { - if((s = getline(reply, sizeof(reply))) < 0) - return(s); - if(printreply) { - printf("%s\n", reply); - fflush(stdout); - } - if(firsttime) { - firsttime = 0; - strncpy(code, reply, 3); - code[3] = '\0'; - } - } while(strncmp(reply, code, 3) || reply[3] == '-'); - s = atoi(code); - } while(s < 200 && s != 125 && s != 150); - - return(s); -} - -int DOcmdcheck() -{ - if(!linkopen) { - printf("You must \"OPEN\" a connection first.\n"); - return(1); - } - - if(!loggedin) { - printf("You must login first.\n"); - return(1); - } - - return(0); -} - -int DOcommand(ftpcommand, ftparg) -char *ftpcommand; -char *ftparg; -{ -int s; -#if 1 -static char ss[64]; - if(*ftparg) - sprintf(ss, "%s %s\r\n", ftpcommand, ftparg); - else - sprintf(ss, "%s\r\n", ftpcommand); - - s = write(ftpcomm_fd, ss, strlen(ss)); - if(s != strlen(ss)) - return(-1); - -#else - s = write(ftpcomm_fd, ftpcommand, strlen(ftpcommand)); - if(s != strlen(ftpcommand)) - return(-1); - - if(*ftparg) { - s = write(ftpcomm_fd, " ", 1); - if(s != 1) - return(-1); - - s = write(ftpcomm_fd, ftparg, strlen(ftparg)); - if(s != strlen(ftparg)) - return(-1); - } - - s = write(ftpcomm_fd, "\r\n", 2); - if(s != 2) - return(-1); -#endif - - return(DOgetreply()); -} - -int DOver() -{ - printf("FTP Version %s\n", FtpVersion); - return(0); -} - -int DOhelp() -{ -char junk[10]; - - printf("Command: Description\n"); - printf("! Escape to a shell\n"); - printf("append Append a file to remote host\n"); - printf("ascii Set file transfer type to ascii\n"); - printf("binary Set file transfer type to binary\n"); - printf("block Set file transfer mode to block\n"); - printf("bye Close connection and exit\n"); - printf("cd Change directory on remote host\n"); - printf("close Close connection\n"); - printf("clone Clone a file\n"); - printf("del Remove file on remote host\n"); - printf("dir Display long form remote host directory listing\n"); - printf("exit Close connection and exit\n"); - printf("get Retrieve a file from remote host\n"); - printf("help Display this text\n"); - - if(readline("Press ENTER to continue... ", junk, sizeof(junk))) - return(-1); - - printf("lcd Change directory on local host\n"); - printf("ldir Display long form local host directory listing\n"); - printf("lls Display local host directory listing\n"); - printf("lmkdir Create directory on local host\n"); - printf("lpwd Display current directory on local host\n"); - printf("lrmdir Remove directory on local host\n"); - printf("ls Display remote host directory listing\n"); - printf("mget Retrieve multiple files from remote host\n"); - printf("mkdir Create directory on remote host\n"); - printf("mod Get file modification time\n"); - printf("mput Send multiple files to remote host\n"); - printf("noop Send the ftp NOOP command\n"); - - if(readline("Press ENTER to continue... ", junk, sizeof(junk))) - return(-1); - - printf("open Open connection to remote host\n"); - printf("pass Enter remote user password\n"); - printf("passive Toggle passive mode\n"); - printf("put Send a file to remote host\n"); - printf("putu Send a file to remote host(unique)\n"); - printf("pwd Display current directory on remote host\n"); - printf("quit Close connection and exit\n"); - printf("quote Send raw ftp command to remote host\n"); - printf("reget Restart a partial file retrieve from remote host\n"); - printf("remotehelp Display ftp commands implemented on remote host\n"); - printf("reput Restart a partial file send to remote host\n"); - printf("rm Remove file on remote host\n"); - printf("rmdir Remove directory on remote host\n"); - - if(readline("Press ENTER to continue... ", junk, sizeof(junk))) - return(-1); - - printf("site Send a site specific command\n"); - printf("size Get file size information\n"); - printf("status Get connection/file status information\n"); - printf("stream Set file transfer mode to stream\n"); - printf("system Get remote system type information\n"); - printf("user Enter remote user information\n"); - printf("ver Display client version information\n"); - - return(0); -} - -struct commands { - char *name; - int(*func) (void); -}; - -static struct commands commands[] = { - "!", DOlshell, - "append", DOappe, - "ascii", DOascii, - "binary", DObinary, - "block", DOblock, - "bye", DOquit, - "cd", DOcd, - "close", DOclose, - "clone", DOclone, - "del", DOdelete, - "dir", DOlist, - "exit", DOquit, - "get", DOretr, - "help", DOhelp, - "lcd", DOlcd, - "ldir", DOllist, - "lls", DOlnlst, - "lmkdir", DOlmkdir, - "lpwd", DOlpwd, - "lrmdir", DOlrmdir, - "ls", DOnlst, - "mget", DOMretr, - "mkdir", DOmkdir, - "mod", DOmdtm, - "mput", DOMstor, - "noop", DOnoop, - "open", DOopen, - "pass", DOpass, - "passive", DOpassive, - "put", DOstor, - "putu", DOstou, - "pwd", DOpwd, - "quit", DOquit, - "quote", DOquote, - "reget", DOrretr, - "remotehelp", DOremotehelp, - "reput", DOrstor, - "rm", DOdelete, - "rmdir", DOrmdir, - "site", DOsite, - "size", DOsize, - "status", DOstat, - "stream", DOstream, - "system", DOsyst, - "user", DOuser, - "ver", DOver, - "", (int (*)())0 -}; - -int main(argc, argv) -int argc; -char *argv[]; -{ -int s; -struct commands *cmd; -static char buffer[128]; - - if(NETinit()) - return(-1); - - FTPinit(); - - s = 0; - - if(argc > 1) { - sprintf(buffer, "open %s ", argv[1]); - makeargs(buffer); - s = DOopen(); - if(atty && s > 0) { - sprintf(buffer, "user"); - makeargs(buffer); - s = DOuser(); - } - } - - while(s >= 0) { - s = readline("ftp>", buffer, sizeof(buffer)); - if(s < 0) break; - makeargs(buffer); - if(cmdargc == 0) continue; - for(cmd = commands; *cmd->name != '\0'; cmd++) - if(!strcmp(cmdargv[0], cmd->name)) - break; - if(*cmd->name != '\0') - s = (*cmd->func)(); - else { - s = 0; - printf("Command \"%s\" not recognized.\n", cmdargv[0]); - } - } - - return(s); -} diff --git a/commands/ftp101/ftp.h b/commands/ftp101/ftp.h deleted file mode 100644 index 251b12983..000000000 --- a/commands/ftp101/ftp.h +++ /dev/null @@ -1,39 +0,0 @@ -/* ftp.h Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * This file is part of ftp. - * - * - * 01/25/96 Initial Release Michael Temari, - */ - -extern int linkopen; -extern int loggedin; -extern int type; -extern int format; -extern int mode; -extern int structure; -extern int passive; -extern int atty; - -#define NUMARGS 10 -extern int cmdargc; -extern char *cmdargv[NUMARGS]; - -extern int printreply; -extern char reply[1024]; - -#define RETR 0 -#define STOR 1 - -#define TYPE_A 0 -#define TYPE_I 1 - -#define MODE_S 0 -#define MODE_B 1 - -#define MODE_B_EOF 64 - -int readline(char *prompt, char *buff, int len); -int DOgetreply(void); -int DOcmdcheck(void); -int DOcommand(char *ftpcommand, char *ftparg); diff --git a/commands/ftp101/local.c b/commands/ftp101/local.c deleted file mode 100644 index 3a15fa2b9..000000000 --- a/commands/ftp101/local.c +++ /dev/null @@ -1,134 +0,0 @@ -/* local.c Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * This file is part of ftp. - * - * - * 01/25/96 Initial Release Michael Temari, - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "ftp.h" -#include "local.h" - -static char line2[512]; - -static void dodir(char *path, int full); - -int DOlpwd() -{ - if(getcwd(line2, sizeof(line2)) == (char *)NULL) - printf("Could not determine local directory. %s\n", strerror(errno)); - else - printf("Current local directory: %s\n", line2); - - return(0); -} - -int DOlcd() -{ -char *path; - - path = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Path: ", line2, sizeof(line2)) < 0) - return(-1); - path = line2; - } - - if(chdir(path)) - printf("Could not change local directory. %s\n", strerror(errno)); - else - return(DOlpwd()); - - return(0); -} - -int DOlmkdir() -{ -char *path; - - path = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Path: ", line2, sizeof(line2)) < 0) - return(-1); - path = line2; - } - - if(mkdir(path, 0777)) - printf("Could not make directory %s. %s\n", path, strerror(errno)); - else - printf("Directory created.\n"); - - return(0); -} - -int DOlrmdir() -{ -char *path; - - path = cmdargv[1]; - - if(cmdargc < 2) { - if(readline("Path: ", line2, sizeof(line2)) < 0) - return(-1); - path = line2; - } - - if(rmdir(path)) - printf("Could not remove directory %s. %s\n", path, strerror(errno)); - else - printf("Directory removed.\n"); - - return(0); -} - -int DOllist(void) -{ - dodir(".", 1); - - return(0); -} - -int DOlnlst(void) -{ - dodir(".", 0); - - return(0); -} - -int DOlshell(void) -{ - (void) system("$SHELL"); - - return(0); -} - -static void dodir(path, full) -char *path; -int full; -{ -static char cmd[128]; -static char name[32]; - - (void) tmpnam(name); - - if(full) - sprintf(cmd, "ls -l %s > %s", path, name); - else - sprintf(cmd, "ls %s > %s", path, name); - - (void) system(cmd); - sprintf(cmd, "more %s", name); - (void) system(cmd); - sprintf(cmd, "rm %s", name); - (void) system(cmd); -} diff --git a/commands/ftp101/local.h b/commands/ftp101/local.h deleted file mode 100644 index fb3966332..000000000 --- a/commands/ftp101/local.h +++ /dev/null @@ -1,15 +0,0 @@ -/* local.h Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * This file is part of ftp. - * - * - * 01/25/96 Initial Release Michael Temari, - */ - -int DOlpwd(void); -int DOlcd(void); -int DOlmkdir(void); -int DOlrmdir(void); -int DOllist(void); -int DOlnlst(void); -int DOlshell(void); diff --git a/commands/ftp101/net.c b/commands/ftp101/net.c deleted file mode 100644 index f6a1556da..000000000 --- a/commands/ftp101/net.c +++ /dev/null @@ -1,569 +0,0 @@ -/* net.c Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * This file is part of ftp. - * - * - * 01/25/96 Initial Release Michael Temari, - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ftp.h" -#include "xfer.h" -#include "net.h" - -void donothing(int sig); - -int ftpcomm_fd; -static ipaddr_t myip; -static ipaddr_t hostip; -static char host[256]; -static int lpid; - -int NETinit() -{ -int s; -char *tcp_device; -int tcp_fd; -nwio_tcpconf_t nwio_tcpconf; - - /* All this just to get our ip address */ - - if((tcp_device = getenv("TCP_DEVICE")) == (char *)NULL) - tcp_device = TCP_DEVICE; - - tcp_fd = open(tcp_device, O_RDWR); - if(tcp_fd < 0) { - perror("ftp: Could not open tcp_device"); - return(-1); - } - s = ioctl(tcp_fd, NWIOGTCPCONF, &nwio_tcpconf); - if(s < 0) { - perror("ftp: Could not get tcp configuration"); - return(-1); - } - - myip = nwio_tcpconf.nwtc_locaddr; - - close(tcp_fd); - - return(0); -} - -int DOopen() -{ -nwio_tcpconf_t tcpconf; -nwio_tcpcl_t tcpcopt; -char *tcp_device; -tcpport_t port; -int s; -struct hostent *hp; -struct servent *servent; - - if(linkopen) { - printf("Use \"CLOSE\" to close the connection first.\n"); - return(0); - } - - if(cmdargc < 2) { - if(readline("Host: ", host, sizeof(host)) < 0) - return(-1); - } else - strncpy(host, cmdargv[1], sizeof(host)); - - if((servent = getservbyname("ftp", "tcp")) == (struct servent *)NULL) { - fprintf(stderr, "ftp: Could not find ftp tcp service\n"); - port = htons(21); - } else - port = (tcpport_t)servent->s_port; - - hp = gethostbyname(host); - if(hp == (struct hostent *)NULL) { - hostip = (ipaddr_t)0; - printf("Unresolved host %s\n", host); - return(0); - } else - memcpy((char *) &hostip, (char *) hp->h_addr, hp->h_length); - - /* This HACK allows the server to establish data connections correctly */ - /* when using the loopback device to talk to ourselves */ - if((hostip & ntohl(0xFF000000)) == inet_addr("127.0.0.0")) - hostip = myip; - - if((tcp_device = getenv("TCP_DEVICE")) == NULL) - tcp_device = "/dev/tcp"; - - if((ftpcomm_fd = open(tcp_device, O_RDWR)) < 0) { - perror("ftp: open error on tcp device"); - return(-1); - } - - tcpconf.nwtc_flags = NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; - tcpconf.nwtc_remaddr = hostip; - tcpconf.nwtc_remport = port; - - s = ioctl(ftpcomm_fd, NWIOSTCPCONF, &tcpconf); - if(s < 0) { - perror("ftp: ioctl error on NWIOSTCPCONF"); - close(ftpcomm_fd); - return(-1); - } - - tcpcopt.nwtcl_flags = 0; - - s = ioctl(ftpcomm_fd, NWIOTCPCONN, &tcpcopt); - if(s < 0) { - perror("ftp: ioctl error on NWIOTCPCONN"); - close(ftpcomm_fd); - return(-1); - } - - s = ioctl(ftpcomm_fd, NWIOGTCPCONF, &tcpconf); - if(s < 0) { - perror("ftp: ioctl error on NWIOGTCPCONF"); - close(ftpcomm_fd); - return(-1); - } - - s = DOgetreply(); - - if(s < 0) { - close(ftpcomm_fd); - return(s); - } - - if(s != 220) { - close(ftpcomm_fd); - return(0); - } - - linkopen = 1; - - return(s); -} - -int DOclose() -{ - if(!linkopen) { - printf("You can't close a connection that isn't open.\n"); - return(0); - } - - close(ftpcomm_fd); - - linkopen = 0; - loggedin = 0; - - return(0); -} - -int DOquit() -{ -int s; - - if(linkopen) { - s = DOcommand("QUIT", ""); - s = DOclose(); - } - - printf("FTP done.\n"); - - exit(0); -} - -void donothing(sig) -int sig; -{ -} - -int DOdata(datacom, file, direction, fd) -char *datacom; -char *file; -int direction; /* RETR or STOR */ -int fd; -{ -nwio_tcpconf_t tcpconf; -nwio_tcpcl_t tcplopt, tcpcopt; -char *tcp_device; -static int ftpdata_fd = -1; -char *buff; -ipaddr_t ripaddr; -tcpport_t rport; -static tcpport_t lport; -int s; -int i; -int wpid; -int cs; -int pfd[2]; -char dummy; -char port[32]; -int wasopen; - - lport = htons(0xF000); - -#ifdef DEBUG - printf("DOdata %s %s %d %d\n", datacom, file, direction, fd); -#endif - - ripaddr = hostip; - rport = htons(2); - - /* here we set up a connection to listen on if not passive mode */ - /* otherwise we use this to connect for passive mode */ - - if((tcp_device = getenv("TCP_DEVICE")) == NULL) - tcp_device = "/dev/tcp"; - - if(ftpdata_fd >= 0 && mode != MODE_B) { - close(ftpdata_fd); - ftpdata_fd = -1; - } - - wasopen = (ftpdata_fd >= 0); - -#ifdef DEBUG - printf("wasopen = %d\n", wasopen); -#endif - - if(wasopen) - goto WASOPEN; - -#ifdef DEBUG - printf("b4 open = %d\n", ftpdata_fd); -#endif - - if(ftpdata_fd == -1) - if((ftpdata_fd = open(tcp_device, O_RDWR)) < 0) { - perror("ftp: open error on tcp device"); - return(0); - } - -#ifdef DEBUG - printf("at open = %d\n", ftpdata_fd); -#endif - - if(passive) { -#ifdef DEBUG - printf("b4 PASV command\n"); -#endif - s = DOcommand("PASV", ""); -#ifdef DEBUG - printf("PASV command returned %d\n", s); -#endif - if(s != 227) { - close(ftpdata_fd); - ftpdata_fd = -1; - return(s); - } - /* decode host and port */ - buff = reply; - while(*buff && (*buff != '(')) buff++; - buff++; - ripaddr = (ipaddr_t)0; - for(i = 0; i < 4; i++) { - ripaddr = (ripaddr << 8) + (ipaddr_t)atoi(buff); - if((buff = strchr(buff, ',')) == (char *)0) { - printf("Could not parse PASV reply\n"); - return(0); - } - buff++; - } - rport = (tcpport_t)atoi(buff); - if((buff = strchr(buff, ',')) == (char *)0) { - printf("Could not parse PASV reply\n"); - return(0); - } - buff++; - rport = (rport << 8) + (tcpport_t)atoi(buff); - ripaddr = ntohl(ripaddr); - rport = ntohs(rport); -#ifdef DEBUG - printf("PASV %08x %04x\n", ripaddr, rport); -#endif - } - - while(1) { - tcpconf.nwtc_flags = NWTC_SET_RA | NWTC_SET_RP; - if(passive || ntohs(lport) >= 0xF000) { - tcpconf.nwtc_flags |= NWTC_LP_SEL; - } else { - /* For no good reason Sun hosts don't like it if they have to - * connect to the same port twice in a short time... - */ - lport = htons(ntohs(lport) + 1); - tcpconf.nwtc_flags |= NWTC_LP_SET; - tcpconf.nwtc_locport = lport; - } - - tcpconf.nwtc_remaddr = ripaddr; - tcpconf.nwtc_remport = rport; - -#ifdef DEBUG - printf("b4 STCPCONF locport = %d\n", lport); -#endif - - s = ioctl(ftpdata_fd, NWIOSTCPCONF, &tcpconf); -#ifdef DEBUG - printf("at STCPCONF %d %d\n", s, errno); -#endif - if(s < 0) { - if(errno == EADDRINUSE) continue; - perror("ftp: ioctl error on NWIOSTCPCONF"); - close(ftpdata_fd); - ftpdata_fd = -1; - return(0); - } - break; - } - -#ifdef DEBUG - printf("b4 GTCPCONF\n"); -#endif - s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); -#ifdef DEBUG - printf("at GTCPCONF %d %d\n", s, errno); -#endif - if(s < 0) { - perror("ftp: ioctl error on NWIOGTCPCONF"); - close(ftpdata_fd); - ftpdata_fd = -1; - return(0); - } - lport = tcpconf.nwtc_locport; - -#ifdef DEBUG - printf("lport = %04x\n", lport); -#endif - - if(passive) { - /* passive mode we connect to them */ - tcpcopt.nwtcl_flags = 0; -#ifdef DEBUG - printf("Doing TCPCONN\n"); -#endif - s = ioctl(ftpdata_fd, NWIOTCPCONN, &tcpcopt); -#ifdef DEBUG - printf("TCPCONN %d %d\n", s, errno); -#endif - if(s < 0) { - perror("ftp: error on ioctl NWIOTCPCONN"); - close(ftpdata_fd); - ftpdata_fd = -1; - return(0); - } -#ifdef DEBUG - printf("GTCPCONF\n"); -#endif - s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); -#ifdef DEBUG - printf("GTCPCONF %d %d\n", s, errno); -#endif - if(s < 0) { - perror("ftp: error on ioctl NWIOGTCPCONF"); - close(ftpdata_fd); - ftpdata_fd = -1; - return(0); - } - } else { - /* we listen for them */ - tcplopt.nwtcl_flags = 0; -#ifdef DEBUG - printf("Listen\n"); -#endif - - if (pipe(pfd) < 0) { - perror("ftp: could not create a pipe"); - close(ftpdata_fd); - ftpdata_fd = -1; - return(0); - } - lpid = fork(); - if(lpid < 0) { - perror("ftp: could not fork listener"); - close(ftpdata_fd); - ftpdata_fd = -1; - close(pfd[0]); - close(pfd[1]); - return(0); - } else if(lpid == 0) { -#ifdef DEBUG - printf("Child here\n"); -#endif - close(pfd[0]); - signal(SIGALRM, donothing); - alarm(15); - close(pfd[1]); -#ifdef DEBUG - printf("child TCPLISTEN\n"); -#endif - s = ioctl(ftpdata_fd, NWIOTCPLISTEN, &tcplopt); - alarm(0); -#ifdef DEBUG - printf("listen %d %d\n", s, errno); -#endif - if(s < 0) - exit(errno); /* error */ - else - exit(0); /* connection made */ - } -#ifdef DEBUG - printf("Fork = %d\n", lpid); -#endif - /* Wait for the pipe to close, then the listener is ready (almost). */ - close(pfd[1]); - (void) read(pfd[0], &dummy, 1); - close(pfd[0]); - while(1) { - wpid = waitpid(lpid, &cs, WNOHANG); -#ifdef DEBUG - printf("waitpid %d %d\n", wpid, cs); - printf("GTCPCONF loop\n"); -#endif - if(wpid != 0) break; - signal(SIGALRM, donothing); - alarm(1); - s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); - alarm(0); -#ifdef DEBUG - printf("GTCPCONF loop %d %d\n", s, errno); -#endif - if(s == -1) break; - sleep(1); - } -#ifdef DEBUG - printf("GTCPCONF = %d\n", s); -#endif - } - -#define hiword(x) ((u16_t)((x) >> 16)) -#define loword(x) ((u16_t)(x & 0xffff)) -#define hibyte(x) (((x) >> 8) & 0xff) -#define lobyte(x) ((x) & 0xff) - - if(!passive) { - if(wpid != 0) { - close(ftpdata_fd); - ftpdata_fd = -1; - cs = (cs >> 8) & 0x00ff; - printf("Child listener error %s\n", strerror(cs)); - return(0); - } - sprintf(port, "%u,%u,%u,%u,%u,%u", - hibyte(hiword(ntohl(myip))), lobyte(hiword(ntohl(myip))), - hibyte(loword(ntohl(myip))), lobyte(loword(ntohl(myip))), - hibyte(ntohs(lport)), lobyte(ntohs(lport))); -#ifdef DEBUG - printf("sending port command %s\n", port); -#endif - s = DOcommand("PORT", port); -#ifdef DEBUG - printf("port command = %d\n", s); -#endif - if(s != 200) { - close(ftpdata_fd); - ftpdata_fd = -1; - kill(lpid, SIGKILL); - (void) wait(&cs); - return(s); - } - } - -WASOPEN: - -#ifdef DEBUG - printf("doing data command %s %s\n", datacom, file); -#endif - s = DOcommand(datacom, file); -#ifdef DEBUG - printf("do command reply %d\n", s); -#endif - if(s == 125 || s == 150) { - if(!passive && !wasopen) { - while(1) { -#ifdef DEBUG - printf("Waiting for child %d\n", lpid); -#endif - s = wait(&cs); -#ifdef DEBUG - printf("Wait returned %d cs=%d errno=%d\n", s, cs, errno); -#endif - if(s < 0 || s == lpid) - break; - } - if(s < 0) { - perror("wait error:"); - close(ftpdata_fd); - ftpdata_fd = -1; - kill(lpid, SIGKILL); - (void) wait(&cs); - return(s); - } - if((cs & 0x00ff)) { - printf("Child listener failed %04x\n", cs); - close(ftpdata_fd); - ftpdata_fd = -1; - return(-1); - } - cs = (cs >> 8) & 0x00ff; - if(cs) { - printf("Child listener error %s\n", strerror(cs)); - close(ftpdata_fd); - ftpdata_fd = -1; - return(DOgetreply()); - } - } -#ifdef DEBUG - printf("Before recvfile/sendfile call\n"); -#endif - switch(direction) { - case RETR: - s = recvfile(fd, ftpdata_fd); - break; - case STOR: - s = sendfile(fd, ftpdata_fd); - break; - } -#ifdef DEBUG - printf("send/recieve %d\n", s); -#endif - if(mode != MODE_B) { - close(ftpdata_fd); - ftpdata_fd = -1; - } - - s = DOgetreply(); -#ifdef DEBUG - printf("send/recieve reply %d\n", s); -#endif - if(mode == MODE_B && s == 226) { - close(ftpdata_fd); - ftpdata_fd = -1; - } - } else { - if(!passive) { - kill(lpid, SIGKILL); - (void) wait(&cs); - } - close(ftpdata_fd); - ftpdata_fd = -1; - } - - return(s); -} diff --git a/commands/ftp101/net.h b/commands/ftp101/net.h deleted file mode 100644 index f4c681f69..000000000 --- a/commands/ftp101/net.h +++ /dev/null @@ -1,15 +0,0 @@ -/* net.h Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * This file is part of ftp. - * - * - * 01/25/96 Initial Release Michael Temari, - */ - -int NETinit(void); -int DOopen(void); -int DOclose(void); -int DOquit(void); -int DOdata(char *datacom, char *file, int direction, int fd); - -extern int ftpcomm_fd; diff --git a/commands/ftp101/other.c b/commands/ftp101/other.c deleted file mode 100644 index 41781e9f8..000000000 --- a/commands/ftp101/other.c +++ /dev/null @@ -1,173 +0,0 @@ -/* other.c Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * ftp An ftp client program for use with TNET. - * - * Author: Michael Temari, - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "ftp.h" -#include "other.h" - -static int docmdargs(char *cmd, int fa); - -void FTPinit() -{ - linkopen = 0; - loggedin = 0; - type = TYPE_A; - format = 0; - mode = MODE_S; - structure = 0; - passive = 0; - atty = isatty(0); -} - -int DOpass() -{ -int s; -struct termios oldtty, newtty; -char *pass; -char password[64]; - - if(!linkopen) { - printf("You must \"OPEN\" a connection first.\n"); - return(0); - } - - pass = cmdargv[1]; - - s = 0; - - if(cmdargc < 2) { - if(atty) { - tcgetattr(fileno(stdout), &oldtty); - newtty = oldtty; - newtty.c_lflag &= ~ECHO; - tcsetattr(fileno(stdout), TCSANOW, &newtty); - } - s = readline("Password: ", password, sizeof(password)); - if(atty) { - tcsetattr(fileno(stdout), TCSANOW, &oldtty); - printf("\n"); - } - pass = password; - } - - if(s < 0) - return(-1); - - s = DOcommand("PASS", pass); - - if(s == 230) - loggedin = 1; - - return(s); -} - -int DOuser() -{ -char *user; -int s; -char username[32]; - - if(!linkopen) { - printf("You must \"OPEN\" a connection first.\n"); - return(0); - } - - loggedin = 0; - - user = cmdargv[1]; - - s = 0; - - if(cmdargc < 2) { - if(readline("Username: ", username, sizeof(username)) < 0) - return(-1); - user = username; - } - - s = DOcommand("USER", user); - - if(atty && s == 331) { - cmdargv[0] = "password"; - cmdargc = 1; - return(DOpass()); - } - - if(s == 230) - loggedin = 1; - - return(s); -} - -int DOnoop() -{ - if(DOcmdcheck()) - return(0); - - return(DOcommand("NOOP", "")); -} - -int DOpassive() -{ - passive = 1 - passive; - - printf("Passive mode is now %s\n", (passive ? "ON" : "OFF")); - - return(0); -} - -int DOsyst() -{ - if(DOcmdcheck()) - return(0); - - return(DOcommand("SYST", "")); -} - -int DOremotehelp() -{ - if(!linkopen) { - printf("You must \"OPEN\" a connection first.\n"); - return(0); - } - - return(DOcommand("HELP", "")); -} - -static int docmdargs(cmd, fa) -char *cmd; -int fa; -{ -int i; -static char args[512]; - - args[0] = '\0'; - - for(i = fa; i < cmdargc; i++) { - if(i != fa) - strcat(args, " "); - strcat(args, cmdargv[i]); - } - - return(DOcommand(cmd, args)); -} - -int DOquote() -{ - return(docmdargs(cmdargv[1], 2)); -} - -int DOsite() -{ - return(docmdargs("SITE", 1)); -} diff --git a/commands/ftp101/other.h b/commands/ftp101/other.h deleted file mode 100644 index d91bb2e9c..000000000 --- a/commands/ftp101/other.h +++ /dev/null @@ -1,17 +0,0 @@ -/* other.h Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * This file is part of ftp. - * - * - * 01/25/96 Initial Release Michael Temari, - */ - -void FTPinit(void); -int DOpass(void); -int DOuser(void); -int DOnoop(void); -int DOpassive(void); -int DOsyst(void); -int DOremotehelp(void); -int DOquote(void); -int DOsite(void); diff --git a/commands/ftp101/xfer.c b/commands/ftp101/xfer.c deleted file mode 100644 index e7128142d..000000000 --- a/commands/ftp101/xfer.c +++ /dev/null @@ -1,305 +0,0 @@ -/* xfer.c Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * This file is part of ftp. - * - * - * 03/14/00 Initial Release Michael Temari, - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ftp.h" -#include "xfer.h" - -static int asciisend(int fd, int fdout); -static int binarysend(int fd, int fdout); -static int asciirecv(int fd, int fdin); -static int binaryrecv(int fd, int fdin); - -#if (__WORD_SIZE == 4) -static char buffer[8192]; -static char bufout[8192]; -#else -static char buffer[2048]; -static char bufout[2048]; -#endif - -static int asciisend(fd, fdout) -int fd; -int fdout; -{ -int s; -char c; -char *p; -char *op, *ope; -unsigned long total=0L; -char block[3]; - - if(atty) { - printf("Sent "); - fflush(stdout); - } - - op = bufout; - ope = bufout + sizeof(bufout) - 3; - - while((s = read(fd, buffer, sizeof(buffer))) > 0) { - total += (long)s; - p = buffer; - while(s-- > 0) { - c = *p++; - if(c == '\n') { - *op++ = '\r'; - total++; - } - *op++ = c; - if(op >= ope) { - if(mode == MODE_B) { - block[0] = '\0'; - *(u16_t *)&block[1] = htons(op - bufout); - write(fdout, block, sizeof(block)); - } - write(fdout, bufout, op - bufout); - op = bufout; - } - } - if(atty) { - printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); - fflush(stdout); - } - } - if(op > bufout) { - if(mode == MODE_B) { - block[0] = MODE_B_EOF; - *(u16_t *)&block[1] = htons(op - bufout); - write(fdout, block, sizeof(block)); - } - write(fdout, bufout, op - bufout); - } else if(mode == MODE_B) { - block[0] = MODE_B_EOF; - *(u16_t *)&block[1] = htons(0); - write(fdout, block, sizeof(block)); - s = 0; - } - if(atty) { - printf("\n"); - fflush(stdout); - } - - return(s); -} - -static int binarysend(fd, fdout) -int fd; -int fdout; -{ -int s; -unsigned long total=0L; -char block[3]; - - if(atty) { - printf("Sent "); - fflush(stdout); - } - - while((s = read(fd, buffer, sizeof(buffer))) > 0) { - if(mode == MODE_B) { - block[0] = MODE_B_EOF; - *(u16_t *)&block[1] = htons(s); - write(fdout, block, sizeof(block)); - } - write(fdout, buffer, s); - total += (long)s; - if(atty) { - printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); - fflush(stdout); - } - } - if(mode == MODE_B) { - block[0] = MODE_B_EOF; - *(u16_t *)&block[1] = htons(0); - write(fdout, block, sizeof(block)); - s = 0; - } - if(atty) { - printf("\n"); - fflush(stdout); - } - - return(s); -} - -int sendfile(fd, fdout) -int fd; -int fdout; -{ -int s; - - switch(type) { - case TYPE_A: - s = asciisend(fd, fdout); - break; - default: - s = binarysend(fd, fdout); - } - - if(s < 0) - return(-1); - else - return(0); -} - -static int asciirecv(fd, fdin) -int fd; -int fdin; -{ -int s; -int gotcr; -char c; -char *p; -char *op, *ope; -unsigned long total=0L; -char block[3]; -unsigned short cnt; - - if(isatty && fd > 2) { - printf("Received "); - fflush(stdout); - } - gotcr = 0; - op = bufout; ope = bufout + sizeof(bufout) - 3; - cnt = 0; - while(1) { - if(mode != MODE_B) - cnt = sizeof(buffer); - else - if(cnt == 0) { - s = read(fdin, block, sizeof(block)); - cnt = ntohs(*(u16_t *)&block[1]); - s = 0; - if(cnt == 0 && block[0] & MODE_B_EOF) - break; - } - s = read(fdin, buffer, cnt > sizeof(buffer) ? sizeof(buffer) : cnt); - if(s <= 0) break; - cnt -= s; - total += (long)s; - p = buffer; - while(s-- > 0) { - c = *p++; - if(gotcr) { - gotcr = 0; - if(c != '\n') - *op++ = '\r'; - } - if(c == '\r') - gotcr = 1; - else - *op++ = c; - if(op >= ope) { - write(fd, bufout, op - bufout); - op = bufout; - } - } - if(atty && fd > 2) { - printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); - fflush(stdout); - } - if(cnt == 0 && mode == MODE_B && block[0] & MODE_B_EOF) { - s = 0; - break; - } - } - if(gotcr) - *op++ = '\r'; - if(op > bufout) - write(fd, bufout, op - bufout); - if(atty && fd > 2) { - printf("\n"); - fflush(stdout); - } - if((mode == MODE_B && cnt != 0) || s != 0) - return(-1); - else - return(0); -} - -static int binaryrecv(fd, fdin) -int fd; -int fdin; -{ -int s; -unsigned long total=0L; -char block[3]; -unsigned short cnt; - - if(atty && fd > 2) { - printf("Received "); - fflush(stdout); - } - cnt = 0; - while(1) { - if(mode != MODE_B) - cnt = sizeof(buffer); - else - if(cnt == 0) { - s = read(fdin, block, sizeof(block)); - cnt = ntohs(*(u16_t *)&block[1]); - s = 0; - if(cnt == 0 && block[0] & MODE_B_EOF) - break; - } - s = read(fdin, buffer, cnt > sizeof(buffer) ? sizeof(buffer) : cnt); - if(s <= 0) break; - cnt -= s; - total += (long)s; - write(fd, buffer, s); - if(atty && fd > 2) { - printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); - fflush(stdout); - } - if(cnt == 0 && mode == MODE_B && block[0] & MODE_B_EOF) { - s = 0; - break; - } - } - if(atty && fd > 2) { - printf("\n"); - fflush(stdout); - } - if((mode == MODE_B && cnt != 0) || s != 0) - return(-1); - else - return(0); -} - -int recvfile(fd, fdin) -int fd; -int fdin; -{ -int s; - - switch(type) { - case TYPE_A: - s = asciirecv(fd, fdin); - break; - default: - s = binaryrecv(fd, fdin); - } - - if(s < 0) - return(-1); - else - return(0); -} diff --git a/commands/ftp101/xfer.h b/commands/ftp101/xfer.h deleted file mode 100644 index bab549309..000000000 --- a/commands/ftp101/xfer.h +++ /dev/null @@ -1,8 +0,0 @@ -/* xfer.h Copyright 1992-2000 by Michael Temari All Rights Reserved - * - * This file is part of ftp. - * - */ - -int recvfile(int fd, int fdin); -int sendfile(int fd, int fdout); diff --git a/releasetools/nbsd_ports b/releasetools/nbsd_ports index 5410a4210..4afa747e0 100644 --- a/releasetools/nbsd_ports +++ b/releasetools/nbsd_ports @@ -131,6 +131,7 @@ 2012/10/17 12:00:00,usr.bin/ctags 2011/09/01 13:37:33,usr.bin/du 2013/03/22 12:00:00,usr.bin/from +2013/04/05 12:00:00,usr.bin/ftp 2013/03/18 12:00:00,usr.bin/head 2012/10/17 12:00:00,usr.bin/genassym 2013/03/09 12:00:00,usr.bin/getopt diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 665422f1a..b1aadeac4 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -10,7 +10,7 @@ SUBDIR= \ col ctags \ du \ \ - from \ + from ftp \ genassym getopt head \ indent infocmp join \ ldd \ diff --git a/usr.bin/ftp/Makefile b/usr.bin/ftp/Makefile new file mode 100644 index 000000000..e2e819d25 --- /dev/null +++ b/usr.bin/ftp/Makefile @@ -0,0 +1,40 @@ +# $NetBSD: Makefile,v 1.36 2012/12/21 18:07:36 christos Exp $ +# from: @(#)Makefile 8.2 (Berkeley) 4/3/94 + +.include + +USE_FORT?= yes # network client + +PROG= ftp +SRCS= cmds.c cmdtab.c complete.c domacro.c fetch.c ftp.c main.c \ + progressbar.c ruserpass.c util.c + +# Uncomment the following to provide defaults for gate-ftp operation +# +#CPPFLAGS+=-DGATE_SERVER=\"ftp-gw.host\" # -DGATE_PORT=21 + +.if defined(__MINIX) +CPPFLAGS+= -DDIRENT_MISSING_D_NAMLEN +.endif # defined (__MINIX) + +.if defined(SMALLPROG) +CPPFLAGS+=-DNO_EDITCOMPLETE -DNO_ABOUT -DNO_AUTH -DNO_HELP -DNO_STATUS -DNO_DEBUG -DNO_USAGE +.else +LDADD+= -ledit -lterminfo +DPADD+= ${LIBEDIT} ${LIBTERMINFO} +.if (${MKCRYPTO} != "no") +CPPFLAGS+= -DWITH_SSL +SRCS+=ssl.c +LDADD+= -lssl -lcrypto +DPADD+= ${LIBSSL} ${LIBCRYPTO} +.endif +.endif + +.if (!defined(SMALLPROG) || defined(SMALLPROG_INET6)) && (${USE_INET6} != "no") +CPPFLAGS+= -DINET6 +.endif + +cmds.o fetch.o: version.h +main.o: ftp_var.h + +.include diff --git a/usr.bin/ftp/cmds.c b/usr.bin/ftp/cmds.c new file mode 100644 index 000000000..66cab2b55 --- /dev/null +++ b/usr.bin/ftp/cmds.c @@ -0,0 +1,2795 @@ +/* $NetBSD: cmds.c,v 1.135 2012/12/22 16:57:09 christos Exp $ */ + +/*- + * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cmds.c 8.6 (Berkeley) 10/9/94"; +#else +__RCSID("$NetBSD: cmds.c,v 1.135 2012/12/22 16:57:09 christos Exp $"); +#endif +#endif /* not lint */ + +/* + * FTP User Program -- Command Routines. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftp_var.h" +#include "version.h" + +static struct types { + const char *t_name; + const char *t_mode; + int t_type; + const char *t_arg; +} types[] = { + { "ascii", "A", TYPE_A, 0 }, + { "binary", "I", TYPE_I, 0 }, + { "image", "I", TYPE_I, 0 }, + { "ebcdic", "E", TYPE_E, 0 }, + { "tenex", "L", TYPE_L, bytename }, + { NULL, NULL, 0, NULL } +}; + +static sigjmp_buf jabort; + +static int confirm(const char *, const char *); +__dead static void mintr(int); +static void mabort(const char *); +static void set_type(const char *); + +static const char *doprocess(char *, size_t, const char *, int, int, int); +static const char *domap(char *, size_t, const char *); +static const char *docase(char *, size_t, const char *); +static const char *dotrans(char *, size_t, const char *); + +/* + * Confirm if "cmd" is to be performed upon "file". + * If "file" is NULL, generate a "Continue with" prompt instead. + */ +static int +confirm(const char *cmd, const char *file) +{ + const char *errormsg; + char cline[BUFSIZ]; + const char *promptleft, *promptright; + + if (!interactive || confirmrest) + return (1); + if (file == NULL) { + promptleft = "Continue with"; + promptright = cmd; + } else { + promptleft = cmd; + promptright = file; + } + while (1) { + fprintf(ttyout, "%s %s [anpqy?]? ", promptleft, promptright); + (void)fflush(ttyout); + if (get_line(stdin, cline, sizeof(cline), &errormsg) < 0) { + mflag = 0; + fprintf(ttyout, "%s; %s aborted\n", errormsg, cmd); + return (0); + } + switch (tolower((unsigned char)*cline)) { + case 'a': + confirmrest = 1; + fprintf(ttyout, + "Prompting off for duration of %s.\n", cmd); + break; + case 'p': + interactive = 0; + fputs("Interactive mode: off.\n", ttyout); + break; + case 'q': + mflag = 0; + fprintf(ttyout, "%s aborted.\n", cmd); + /* FALLTHROUGH */ + case 'n': + return (0); + case '?': + fprintf(ttyout, + " confirmation options:\n" + "\ta answer `yes' for the duration of %s\n" + "\tn answer `no' for this file\n" + "\tp turn off `prompt' mode\n" + "\tq stop the current %s\n" + "\ty answer `yes' for this file\n" + "\t? this help list\n", + cmd, cmd); + continue; /* back to while(1) */ + } + return (1); + } + /* NOTREACHED */ +} + +/* + * Set transfer type. + */ +void +settype(int argc, char *argv[]) +{ + struct types *p; + + if (argc == 0 || argc > 2) { + const char *sep; + + UPRINTF("usage: %s [", argv[0]); + sep = " "; + for (p = types; p->t_name; p++) { + fprintf(ttyout, "%s%s", sep, p->t_name); + sep = " | "; + } + fputs(" ]\n", ttyout); + code = -1; + return; + } + if (argc < 2) { + fprintf(ttyout, "Using %s mode to transfer files.\n", typename); + code = 0; + return; + } + set_type(argv[1]); +} + +void +set_type(const char *ttype) +{ + struct types *p; + int comret; + + for (p = types; p->t_name; p++) + if (strcmp(ttype, p->t_name) == 0) + break; + if (p->t_name == 0) { + fprintf(ttyout, "%s: unknown mode.\n", ttype); + code = -1; + return; + } + if ((p->t_arg != NULL) && (*(p->t_arg) != '\0')) + comret = command("TYPE %s %s", p->t_mode, p->t_arg); + else + comret = command("TYPE %s", p->t_mode); + if (comret == COMPLETE) { + (void)strlcpy(typename, p->t_name, sizeof(typename)); + curtype = type = p->t_type; + } +} + +/* + * Internal form of settype; changes current type in use with server + * without changing our notion of the type for data transfers. + * Used to change to and from ascii for listings. + */ +void +changetype(int newtype, int show) +{ + struct types *p; + int comret, oldverbose = verbose; + + if (newtype == 0) + newtype = TYPE_I; + if (newtype == curtype) + return; + if (ftp_debug == 0 && show == 0) + verbose = 0; + for (p = types; p->t_name; p++) + if (newtype == p->t_type) + break; + if (p->t_name == 0) { + errx(1, "changetype: unknown type %d", newtype); + } + if (newtype == TYPE_L && bytename[0] != '\0') + comret = command("TYPE %s %s", p->t_mode, bytename); + else + comret = command("TYPE %s", p->t_mode); + if (comret == COMPLETE) + curtype = newtype; + verbose = oldverbose; +} + +/* + * Set binary transfer type. + */ +/*VARARGS*/ +void +setbinary(int argc, char *argv[]) +{ + + if (argc == 0) { + UPRINTF("usage: %s\n", argv[0]); + code = -1; + return; + } + set_type("binary"); +} + +/* + * Set ascii transfer type. + */ +/*VARARGS*/ +void +setascii(int argc, char *argv[]) +{ + + if (argc == 0) { + UPRINTF("usage: %s\n", argv[0]); + code = -1; + return; + } + set_type("ascii"); +} + +/* + * Set tenex transfer type. + */ +/*VARARGS*/ +void +settenex(int argc, char *argv[]) +{ + + if (argc == 0) { + UPRINTF("usage: %s\n", argv[0]); + code = -1; + return; + } + set_type("tenex"); +} + +/* + * Set file transfer mode. + */ +/*ARGSUSED*/ +void +setftmode(int argc, char *argv[]) +{ + + if (argc != 2) { + UPRINTF("usage: %s mode-name\n", argv[0]); + code = -1; + return; + } + fprintf(ttyout, "We only support %s mode, sorry.\n", modename); + code = -1; +} + +/* + * Set file transfer format. + */ +/*ARGSUSED*/ +void +setform(int argc, char *argv[]) +{ + + if (argc != 2) { + UPRINTF("usage: %s format\n", argv[0]); + code = -1; + return; + } + fprintf(ttyout, "We only support %s format, sorry.\n", formname); + code = -1; +} + +/* + * Set file transfer structure. + */ +/*ARGSUSED*/ +void +setstruct(int argc, char *argv[]) +{ + + if (argc != 2) { + UPRINTF("usage: %s struct-mode\n", argv[0]); + code = -1; + return; + } + fprintf(ttyout, "We only support %s structure, sorry.\n", structname); + code = -1; +} + +/* + * Send a single file. + */ +void +put(int argc, char *argv[]) +{ + char buf[MAXPATHLEN]; + const char *cmd; + int loc = 0; + char *locfile; + const char *remfile; + + if (argc == 2) { + argc++; + argv[2] = argv[1]; + loc++; + } + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-file"))) + goto usage; + if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { + usage: + UPRINTF("usage: %s local-file [remote-file]\n", argv[0]); + code = -1; + return; + } + if ((locfile = globulize(argv[1])) == NULL) { + code = -1; + return; + } + remfile = argv[2]; + if (loc) /* If argv[2] is a copy of the old argv[1], update it */ + remfile = locfile; + cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR"); + remfile = doprocess(buf, sizeof(buf), remfile, + 0, loc && ntflag, loc && mapflag); + sendrequest(cmd, locfile, remfile, + locfile != argv[1] || remfile != argv[2]); + free(locfile); +} + +static const char * +doprocess(char *dst, size_t dlen, const char *src, + int casef, int transf, int mapf) +{ + if (casef) + src = docase(dst, dlen, src); + if (transf) + src = dotrans(dst, dlen, src); + if (mapf) + src = domap(dst, dlen, src); + return src; +} + +/* + * Send multiple files. + */ +void +mput(int argc, char *argv[]) +{ + int i; + sigfunc oldintr; + int ointer; + const char *tp; + + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "local-files"))) { + UPRINTF("usage: %s local-files\n", argv[0]); + code = -1; + return; + } + mflag = 1; + oldintr = xsignal(SIGINT, mintr); + if (sigsetjmp(jabort, 1)) + mabort(argv[0]); + if (proxy) { + char *cp; + + while ((cp = remglob(argv, 0, NULL)) != NULL) { + if (*cp == '\0' || !connected) { + mflag = 0; + continue; + } + if (mflag && confirm(argv[0], cp)) { + char buf[MAXPATHLEN]; + tp = doprocess(buf, sizeof(buf), cp, + mcase, ntflag, mapflag); + sendrequest((sunique) ? "STOU" : "STOR", + cp, tp, cp != tp || !interactive); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm(argv[0], NULL)) { + mflag++; + } + interactive = ointer; + } + } + } + goto cleanupmput; + } + for (i = 1; i < argc && connected; i++) { + char **cpp; + glob_t gl; + int flags; + + if (!doglob) { + if (mflag && confirm(argv[0], argv[i])) { + char buf[MAXPATHLEN]; + tp = doprocess(buf, sizeof(buf), argv[i], + 0, ntflag, mapflag); + sendrequest((sunique) ? "STOU" : "STOR", + argv[i], tp, tp != argv[i] || !interactive); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm(argv[0], NULL)) { + mflag++; + } + interactive = ointer; + } + } + continue; + } + + memset(&gl, 0, sizeof(gl)); + flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; + if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) { + warnx("Glob pattern `%s' not found", argv[i]); + globfree(&gl); + continue; + } + for (cpp = gl.gl_pathv; cpp && *cpp != NULL && connected; + cpp++) { + if (mflag && confirm(argv[0], *cpp)) { + char buf[MAXPATHLEN]; + tp = *cpp; + tp = doprocess(buf, sizeof(buf), *cpp, + 0, ntflag, mapflag); + sendrequest((sunique) ? "STOU" : "STOR", + *cpp, tp, *cpp != tp || !interactive); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm(argv[0], NULL)) { + mflag++; + } + interactive = ointer; + } + } + } + globfree(&gl); + } + cleanupmput: + (void)xsignal(SIGINT, oldintr); + mflag = 0; +} + +void +reget(int argc, char *argv[]) +{ + + (void)getit(argc, argv, 1, restart_point ? "r+" : "a"); +} + +void +get(int argc, char *argv[]) +{ + + (void)getit(argc, argv, 0, restart_point ? "r+" : "w"); +} + +/* + * Receive one file. + * If restartit is 1, restart the xfer always. + * If restartit is -1, restart the xfer only if the remote file is newer. + */ +int +getit(int argc, char *argv[], int restartit, const char *gmode) +{ + int loc, rval; + char *remfile, *olocfile; + const char *locfile; + char buf[MAXPATHLEN]; + + loc = rval = 0; + if (argc == 2) { + argc++; + argv[2] = argv[1]; + loc++; + } + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "remote-file"))) + goto usage; + if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) { + usage: + UPRINTF("usage: %s remote-file [local-file]\n", argv[0]); + code = -1; + return (0); + } + remfile = argv[1]; + if ((olocfile = globulize(argv[2])) == NULL) { + code = -1; + return (0); + } + locfile = doprocess(buf, sizeof(buf), olocfile, + loc && mcase, loc && ntflag, loc && mapflag); + if (restartit) { + struct stat stbuf; + int ret; + + if (! features[FEAT_REST_STREAM]) { + fprintf(ttyout, + "Restart is not supported by the remote server.\n"); + return (0); + } + ret = stat(locfile, &stbuf); + if (restartit == 1) { + if (ret < 0) { + if (errno != ENOENT) { + warn("Can't stat `%s'", locfile); + goto freegetit; + } + restart_point = 0; + } + else + restart_point = stbuf.st_size; + } else { + if (ret == 0) { + time_t mtime; + + mtime = remotemodtime(argv[1], 0); + if (mtime == -1) + goto freegetit; + if (stbuf.st_mtime >= mtime) { + rval = 1; + goto freegetit; + } + } + } + } + + recvrequest("RETR", locfile, remfile, gmode, + remfile != argv[1] || locfile != argv[2], loc); + restart_point = 0; + freegetit: + (void)free(olocfile); + return (rval); +} + +/* ARGSUSED */ +static void +mintr(int signo) +{ + + alarmtimer(0); + if (fromatty) + write(fileno(ttyout), "\n", 1); + siglongjmp(jabort, 1); +} + +static void +mabort(const char *cmd) +{ + int ointer, oconf; + + if (mflag && fromatty) { + ointer = interactive; + oconf = confirmrest; + interactive = 1; + confirmrest = 0; + if (confirm(cmd, NULL)) { + interactive = ointer; + confirmrest = oconf; + return; + } + interactive = ointer; + confirmrest = oconf; + } + mflag = 0; +} + +/* + * Get multiple files. + */ +void +mget(int argc, char *argv[]) +{ + sigfunc oldintr; + int ointer; + char *cp; + const char *tp; + int volatile restartit; + + if (argc == 0 || + (argc == 1 && !another(&argc, &argv, "remote-files"))) { + UPRINTF("usage: %s remote-files\n", argv[0]); + code = -1; + return; + } + mflag = 1; + restart_point = 0; + restartit = 0; + if (strcmp(argv[0], "mreget") == 0) { + if (! features[FEAT_REST_STREAM]) { + fprintf(ttyout, + "Restart is not supported by the remote server.\n"); + return; + } + restartit = 1; + } + oldintr = xsignal(SIGINT, mintr); + if (sigsetjmp(jabort, 1)) + mabort(argv[0]); + while ((cp = remglob(argv, proxy, NULL)) != NULL) { + char buf[MAXPATHLEN]; + if (*cp == '\0' || !connected) { + mflag = 0; + continue; + } + if (! mflag) + continue; + if (! fileindir(cp, localcwd)) { + fprintf(ttyout, "Skipping non-relative filename `%s'\n", + cp); + continue; + } + if (!confirm(argv[0], cp)) + continue; + tp = doprocess(buf, sizeof(buf), cp, mcase, ntflag, mapflag); + if (restartit) { + struct stat stbuf; + + if (stat(tp, &stbuf) == 0) + restart_point = stbuf.st_size; + else + warn("Can't stat `%s'", tp); + } + recvrequest("RETR", tp, cp, restart_point ? "r+" : "w", + tp != cp || !interactive, 1); + restart_point = 0; + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm(argv[0], NULL)) + mflag++; + interactive = ointer; + } + } + (void)xsignal(SIGINT, oldintr); + mflag = 0; +} + +/* + * Read list of filenames from a local file and get those + */ +void +fget(int argc, char *argv[]) +{ + const char *gmode; + FILE *fp; + char buf[MAXPATHLEN], cmdbuf[MAX_C_NAME]; + + if (argc != 2) { + UPRINTF("usage: %s localfile\n", argv[0]); + code = -1; + return; + } + + fp = fopen(argv[1], "r"); + if (fp == NULL) { + fprintf(ttyout, "Can't open source file %s\n", argv[1]); + code = -1; + return; + } + + (void)strlcpy(cmdbuf, "get", sizeof(cmdbuf)); + argv[0] = cmdbuf; + gmode = restart_point ? "r+" : "w"; + + while (get_line(fp, buf, sizeof(buf), NULL) >= 0) { + if (buf[0] == '\0') + continue; + argv[1] = buf; + (void)getit(argc, argv, 0, gmode); + } + fclose(fp); +} + +const char * +onoff(int val) +{ + + return (val ? "on" : "off"); +} + +/* + * Show status. + */ +/*ARGSUSED*/ +void +status(int argc, char *argv[]) +{ + + if (argc == 0) { + UPRINTF("usage: %s\n", argv[0]); + code = -1; + return; + } +#ifndef NO_STATUS + if (connected) + fprintf(ttyout, "Connected %sto %s.\n", + connected == -1 ? "and logged in" : "", hostname); + else + fputs("Not connected.\n", ttyout); + if (!proxy) { + pswitch(1); + if (connected) { + fprintf(ttyout, "Connected for proxy commands to %s.\n", + hostname); + } + else { + fputs("No proxy connection.\n", ttyout); + } + pswitch(0); + } + fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", onoff(gatemode), + *gateserver ? gateserver : "(none)", gateport); + fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n", + onoff(passivemode), onoff(activefallback)); + fprintf(ttyout, "Mode: %s; Type: %s; Form: %s; Structure: %s.\n", + modename, typename, formname, structname); + fprintf(ttyout, "Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s.\n", + onoff(verbose), onoff(bell), onoff(interactive), onoff(doglob)); + fprintf(ttyout, "Store unique: %s; Receive unique: %s.\n", + onoff(sunique), onoff(runique)); + fprintf(ttyout, "Preserve modification times: %s.\n", onoff(preserve)); + fprintf(ttyout, "Case: %s; CR stripping: %s.\n", onoff(mcase), + onoff(crflag)); + if (ntflag) { + fprintf(ttyout, "Ntrans: (in) %s (out) %s\n", ntin, ntout); + } + else { + fputs("Ntrans: off.\n", ttyout); + } + if (mapflag) { + fprintf(ttyout, "Nmap: (in) %s (out) %s\n", mapin, mapout); + } + else { + fputs("Nmap: off.\n", ttyout); + } + fprintf(ttyout, + "Hash mark printing: %s; Mark count: %d; Progress bar: %s.\n", + onoff(hash), mark, onoff(progress)); + fprintf(ttyout, + "Get transfer rate throttle: %s; maximum: %d; increment %d.\n", + onoff(rate_get), rate_get, rate_get_incr); + fprintf(ttyout, + "Put transfer rate throttle: %s; maximum: %d; increment %d.\n", + onoff(rate_put), rate_put, rate_put_incr); + fprintf(ttyout, + "Socket buffer sizes: send %d, receive %d.\n", + sndbuf_size, rcvbuf_size); + fprintf(ttyout, "Use of PORT cmds: %s.\n", onoff(sendport)); + fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv4: %s%s.\n", onoff(epsv4), + epsv4bad ? " (disabled for this connection)" : ""); + fprintf(ttyout, "Use of EPSV/EPRT cmds for IPv6: %s%s.\n", onoff(epsv6), + epsv6bad ? " (disabled for this connection)" : ""); + fprintf(ttyout, "Command line editing: %s.\n", +#ifdef NO_EDITCOMPLETE + "support not compiled in" +#else /* !def NO_EDITCOMPLETE */ + onoff(editing) +#endif /* !def NO_EDITCOMPLETE */ + ); + if (macnum > 0) { + int i; + + fputs("Macros:\n", ttyout); + for (i=0; i 3) { + UPRINTF( + "usage: %s [ on | off | gateserver [port] ]\n", argv[0]); + code = -1; + return; + } else if (argc < 2) { + gatemode = !gatemode; + } else { + if (argc == 2 && strcasecmp(argv[1], "on") == 0) + gatemode = 1; + else if (argc == 2 && strcasecmp(argv[1], "off") == 0) + gatemode = 0; + else { + if (argc == 3) + gateport = ftp_strdup(argv[2]); + (void)strlcpy(gsbuf, argv[1], sizeof(gsbuf)); + gateserver = gsbuf; + gatemode = 1; + } + } + if (gatemode && (gateserver == NULL || *gateserver == '\0')) { + fprintf(ttyout, + "Disabling gate-ftp mode - no gate-ftp server defined.\n"); + gatemode = 0; + } else { + fprintf(ttyout, "Gate ftp: %s, server %s, port %s.\n", + onoff(gatemode), *gateserver ? gateserver : "(none)", + gateport); + } + code = gatemode; +} + +/* + * Toggle metacharacter interpretation on local file names. + */ +/*VARARGS*/ +void +setglob(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &doglob, "Globbing"); +} + +/* + * Toggle preserving modification times on retrieved files. + */ +/*VARARGS*/ +void +setpreserve(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &preserve, "Preserve modification times"); +} + +/* + * Set debugging mode on/off and/or set level of debugging. + */ +/*VARARGS*/ +void +setdebug(int argc, char *argv[]) +{ + if (argc == 0 || argc > 2) { + UPRINTF("usage: %s [ on | off | debuglevel ]\n", argv[0]); + code = -1; + return; + } else if (argc == 2) { + if (strcasecmp(argv[1], "on") == 0) + ftp_debug = 1; + else if (strcasecmp(argv[1], "off") == 0) + ftp_debug = 0; + else { + int val; + + val = strsuftoi(argv[1]); + if (val < 0) { + fprintf(ttyout, "%s: bad debugging value.\n", + argv[1]); + code = -1; + return; + } + ftp_debug = val; + } + } else + ftp_debug = !ftp_debug; + if (ftp_debug) + options |= SO_DEBUG; + else + options &= ~SO_DEBUG; + fprintf(ttyout, "Debugging %s (ftp_debug=%d).\n", onoff(ftp_debug), ftp_debug); + code = ftp_debug > 0; +} + +/* + * Set current working directory on remote machine. + */ +void +cd(int argc, char *argv[]) +{ + int r; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "remote-directory"))) { + UPRINTF("usage: %s remote-directory\n", argv[0]); + code = -1; + return; + } + r = command("CWD %s", argv[1]); + if (r == ERROR && code == 500) { + if (verbose) + fputs("CWD command not recognized, trying XCWD.\n", + ttyout); + r = command("XCWD %s", argv[1]); + } + if (r == COMPLETE) { + dirchange = 1; + updateremotecwd(); + } +} + +/* + * Set current working directory on local machine. + */ +void +lcd(int argc, char *argv[]) +{ + char *locdir; + + code = -1; + if (argc == 1) { + argc++; + argv[1] = localhome; + } + if (argc != 2) { + UPRINTF("usage: %s [local-directory]\n", argv[0]); + return; + } + if ((locdir = globulize(argv[1])) == NULL) + return; + if (chdir(locdir) == -1) + warn("Can't chdir `%s'", locdir); + else { + updatelocalcwd(); + if (localcwd[0]) { + fprintf(ttyout, "Local directory now: %s\n", localcwd); + code = 0; + } else { + fprintf(ttyout, "Unable to determine local directory\n"); + } + } + (void)free(locdir); +} + +/* + * Delete a single file. + */ +void +delete(int argc, char *argv[]) +{ + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "remote-file"))) { + UPRINTF("usage: %s remote-file\n", argv[0]); + code = -1; + return; + } + if (command("DELE %s", argv[1]) == COMPLETE) + dirchange = 1; +} + +/* + * Delete multiple files. + */ +void +mdelete(int argc, char *argv[]) +{ + sigfunc oldintr; + int ointer; + char *cp; + + if (argc == 0 || + (argc == 1 && !another(&argc, &argv, "remote-files"))) { + UPRINTF("usage: %s [remote-files]\n", argv[0]); + code = -1; + return; + } + mflag = 1; + oldintr = xsignal(SIGINT, mintr); + if (sigsetjmp(jabort, 1)) + mabort(argv[0]); + while ((cp = remglob(argv, 0, NULL)) != NULL) { + if (*cp == '\0') { + mflag = 0; + continue; + } + if (mflag && confirm(argv[0], cp)) { + if (command("DELE %s", cp) == COMPLETE) + dirchange = 1; + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm(argv[0], NULL)) { + mflag++; + } + interactive = ointer; + } + } + } + (void)xsignal(SIGINT, oldintr); + mflag = 0; +} + +/* + * Rename a remote file. + */ +void +renamefile(int argc, char *argv[]) +{ + + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "from-name"))) + goto usage; + if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) { + usage: + UPRINTF("usage: %s from-name to-name\n", argv[0]); + code = -1; + return; + } + if (command("RNFR %s", argv[1]) == CONTINUE && + command("RNTO %s", argv[2]) == COMPLETE) + dirchange = 1; +} + +/* + * Get a directory listing of remote files. + * Supports being invoked as: + * cmd runs + * --- ---- + * dir, ls LIST + * mlsd MLSD + * nlist NLST + * pdir, pls LIST |$PAGER + * pmlsd MLSD |$PAGER + */ +void +ls(int argc, char *argv[]) +{ + const char *cmd; + char *remdir, *locbuf; + const char *locfile; + int pagecmd, mlsdcmd; + + remdir = NULL; + locbuf = NULL; + locfile = "-"; + pagecmd = mlsdcmd = 0; + /* + * the only commands that start with `p' are + * the `pager' versions. + */ + if (argv[0][0] == 'p') + pagecmd = 1; + if (strcmp(argv[0] + pagecmd , "mlsd") == 0) { + if (! features[FEAT_MLST]) { + fprintf(ttyout, + "MLSD is not supported by the remote server.\n"); + return; + } + mlsdcmd = 1; + } + if (argc == 0) + goto usage; + + if (mlsdcmd) + cmd = "MLSD"; + else if (strcmp(argv[0] + pagecmd, "nlist") == 0) + cmd = "NLST"; + else + cmd = "LIST"; + + if (argc > 1) + remdir = argv[1]; + if (argc > 2) + locfile = argv[2]; + if (argc > 3 || ((pagecmd | mlsdcmd) && argc > 2)) { + usage: + if (pagecmd || mlsdcmd) + UPRINTF("usage: %s [remote-path]\n", argv[0]); + else + UPRINTF("usage: %s [remote-path [local-file]]\n", + argv[0]); + code = -1; + goto freels; + } + + if (pagecmd) { + const char *p; + size_t len; + + p = getoptionvalue("pager"); + if (EMPTYSTRING(p)) + p = DEFAULTPAGER; + len = strlen(p) + 2; + locbuf = ftp_malloc(len); + locbuf[0] = '|'; + (void)strlcpy(locbuf + 1, p, len - 1); + locfile = locbuf; + } else if ((strcmp(locfile, "-") != 0) && *locfile != '|') { + if ((locbuf = globulize(locfile)) == NULL || + !confirm("output to local-file:", locbuf)) { + code = -1; + goto freels; + } + locfile = locbuf; + } + recvrequest(cmd, locfile, remdir, "w", 0, 0); + freels: + if (locbuf) + (void)free(locbuf); +} + +/* + * Get a directory listing of multiple remote files. + */ +void +mls(int argc, char *argv[]) +{ + sigfunc oldintr; + int ointer, i; + int volatile dolist; + char * volatile dest, *odest; + const char *lmode; + + if (argc == 0) + goto usage; + if (argc < 2 && !another(&argc, &argv, "remote-files")) + goto usage; + if (argc < 3 && !another(&argc, &argv, "local-file")) { + usage: + UPRINTF("usage: %s remote-files local-file\n", argv[0]); + code = -1; + return; + } + odest = dest = argv[argc - 1]; + argv[argc - 1] = NULL; + if (strcmp(dest, "-") && *dest != '|') + if (((dest = globulize(dest)) == NULL) || + !confirm("output to local-file:", dest)) { + code = -1; + return; + } + dolist = strcmp(argv[0], "mls"); + mflag = 1; + oldintr = xsignal(SIGINT, mintr); + if (sigsetjmp(jabort, 1)) + mabort(argv[0]); + for (i = 1; mflag && i < argc-1 && connected; i++) { + lmode = (i == 1) ? "w" : "a"; + recvrequest(dolist ? "LIST" : "NLST", dest, argv[i], lmode, + 0, 0); + if (!mflag && fromatty) { + ointer = interactive; + interactive = 1; + if (confirm(argv[0], NULL)) { + mflag++; + } + interactive = ointer; + } + } + (void)xsignal(SIGINT, oldintr); + mflag = 0; + if (dest != odest) /* free up after globulize() */ + free(dest); +} + +/* + * Do a shell escape + */ +/*ARGSUSED*/ +void +shell(int argc, char *argv[]) +{ + pid_t pid; + sigfunc oldintr; + char shellnam[MAXPATHLEN]; + const char *shellp, *namep; + int wait_status; + + if (argc == 0) { + UPRINTF("usage: %s [command [args]]\n", argv[0]); + code = -1; + return; + } + oldintr = xsignal(SIGINT, SIG_IGN); + if ((pid = fork()) == 0) { + for (pid = 3; pid < 20; pid++) + (void)close(pid); + (void)xsignal(SIGINT, SIG_DFL); + shellp = getenv("SHELL"); + if (shellp == NULL) + shellp = _PATH_BSHELL; + namep = strrchr(shellp, '/'); + if (namep == NULL) + namep = shellp; + else + namep++; + (void)strlcpy(shellnam, namep, sizeof(shellnam)); + if (ftp_debug) { + fputs(shellp, ttyout); + putc('\n', ttyout); + } + if (argc > 1) { + execl(shellp, shellnam, "-c", altarg, (char *)0); + } + else { + execl(shellp, shellnam, (char *)0); + } + warn("Can't execute `%s'", shellp); + code = -1; + exit(1); + } + if (pid > 0) + while (wait(&wait_status) != pid) + ; + (void)xsignal(SIGINT, oldintr); + if (pid == -1) { + warn("Can't fork a subshell; try again later"); + code = -1; + } else + code = 0; +} + +/* + * Send new user information (re-login) + */ +void +user(int argc, char *argv[]) +{ + char *password; + char emptypass[] = ""; + int n, aflag = 0; + + if (argc == 0) + goto usage; + if (argc < 2) + (void)another(&argc, &argv, "username"); + if (argc < 2 || argc > 4) { + usage: + UPRINTF("usage: %s username [password [account]]\n", + argv[0]); + code = -1; + return; + } + n = command("USER %s", argv[1]); + if (n == CONTINUE) { + if (argc < 3) { + password = getpass("Password: "); + if (password == NULL) + password = emptypass; + } else { + password = argv[2]; + } + n = command("PASS %s", password); + memset(password, 0, strlen(password)); + } + if (n == CONTINUE) { + aflag++; + if (argc < 4) { + password = getpass("Account: "); + if (password == NULL) + password = emptypass; + } else { + password = argv[3]; + } + n = command("ACCT %s", password); + memset(password, 0, strlen(password)); + } + if (n != COMPLETE) { + fputs("Login failed.\n", ttyout); + return; + } + if (!aflag && argc == 4) { + password = argv[3]; + (void)command("ACCT %s", password); + memset(password, 0, strlen(password)); + } + connected = -1; + getremoteinfo(); +} + +/* + * Print working directory on remote machine. + */ +/*VARARGS*/ +void +pwd(int argc, char *argv[]) +{ + + code = -1; + if (argc != 1) { + UPRINTF("usage: %s\n", argv[0]); + return; + } + if (! remotecwd[0]) + updateremotecwd(); + if (! remotecwd[0]) + fprintf(ttyout, "Unable to determine remote directory\n"); + else { + fprintf(ttyout, "Remote directory: %s\n", remotecwd); + code = 0; + } +} + +/* + * Print working directory on local machine. + */ +void +lpwd(int argc, char *argv[]) +{ + + code = -1; + if (argc != 1) { + UPRINTF("usage: %s\n", argv[0]); + return; + } + if (! localcwd[0]) + updatelocalcwd(); + if (! localcwd[0]) + fprintf(ttyout, "Unable to determine local directory\n"); + else { + fprintf(ttyout, "Local directory: %s\n", localcwd); + code = 0; + } +} + +/* + * Make a directory. + */ +void +makedir(int argc, char *argv[]) +{ + int r; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "directory-name"))) { + UPRINTF("usage: %s directory-name\n", argv[0]); + code = -1; + return; + } + r = command("MKD %s", argv[1]); + if (r == ERROR && code == 500) { + if (verbose) + fputs("MKD command not recognized, trying XMKD.\n", + ttyout); + r = command("XMKD %s", argv[1]); + } + if (r == COMPLETE) + dirchange = 1; +} + +/* + * Remove a directory. + */ +void +removedir(int argc, char *argv[]) +{ + int r; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "directory-name"))) { + UPRINTF("usage: %s directory-name\n", argv[0]); + code = -1; + return; + } + r = command("RMD %s", argv[1]); + if (r == ERROR && code == 500) { + if (verbose) + fputs("RMD command not recognized, trying XRMD.\n", + ttyout); + r = command("XRMD %s", argv[1]); + } + if (r == COMPLETE) + dirchange = 1; +} + +/* + * Send a line, verbatim, to the remote machine. + */ +void +quote(int argc, char *argv[]) +{ + + if (argc == 0 || + (argc == 1 && !another(&argc, &argv, "command line to send"))) { + UPRINTF("usage: %s line-to-send\n", argv[0]); + code = -1; + return; + } + quote1("", argc, argv); +} + +/* + * Send a SITE command to the remote machine. The line + * is sent verbatim to the remote machine, except that the + * word "SITE" is added at the front. + */ +void +site(int argc, char *argv[]) +{ + + if (argc == 0 || + (argc == 1 && !another(&argc, &argv, "arguments to SITE command"))){ + UPRINTF("usage: %s line-to-send\n", argv[0]); + code = -1; + return; + } + quote1("SITE ", argc, argv); +} + +/* + * Turn argv[1..argc) into a space-separated string, then prepend initial text. + * Send the result as a one-line command and get response. + */ +void +quote1(const char *initial, int argc, char *argv[]) +{ + int i; + char buf[BUFSIZ]; /* must be >= sizeof(line) */ + + (void)strlcpy(buf, initial, sizeof(buf)); + for (i = 1; i < argc; i++) { + (void)strlcat(buf, argv[i], sizeof(buf)); + if (i < (argc - 1)) + (void)strlcat(buf, " ", sizeof(buf)); + } + if (command("%s", buf) == PRELIM) { + while (getreply(0) == PRELIM) + continue; + } + dirchange = 1; +} + +void +do_chmod(int argc, char *argv[]) +{ + + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "mode"))) + goto usage; + if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { + usage: + UPRINTF("usage: %s mode remote-file\n", argv[0]); + code = -1; + return; + } + (void)command("SITE CHMOD %s %s", argv[1], argv[2]); +} + +#define COMMAND_1ARG(argc, argv, cmd) \ + if (argc == 1) \ + command(cmd); \ + else \ + command(cmd " %s", argv[1]) + +void +do_umask(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc == 0) { + UPRINTF("usage: %s [umask]\n", argv[0]); + code = -1; + return; + } + verbose = 1; + COMMAND_1ARG(argc, argv, "SITE UMASK"); + verbose = oldverbose; +} + +void +idlecmd(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc < 1 || argc > 2) { + UPRINTF("usage: %s [seconds]\n", argv[0]); + code = -1; + return; + } + verbose = 1; + COMMAND_1ARG(argc, argv, "SITE IDLE"); + verbose = oldverbose; +} + +/* + * Ask the other side for help. + */ +void +rmthelp(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc == 0) { + UPRINTF("usage: %s\n", argv[0]); + code = -1; + return; + } + verbose = 1; + COMMAND_1ARG(argc, argv, "HELP"); + verbose = oldverbose; +} + +/* + * Terminate session and exit. + * May be called with 0, NULL. + */ +/*VARARGS*/ +void +quit(int argc, char *argv[]) +{ + + /* this may be called with argc == 0, argv == NULL */ + if (argc == 0 && argv != NULL) { + UPRINTF("usage: %s\n", argv[0]); + code = -1; + return; + } + if (connected) + disconnect(0, NULL); + pswitch(1); + if (connected) + disconnect(0, NULL); + exit(0); +} + +/* + * Terminate session, but don't exit. + * May be called with 0, NULL. + */ +void +disconnect(int argc, char *argv[]) +{ + + /* this may be called with argc == 0, argv == NULL */ + if (argc == 0 && argv != NULL) { + UPRINTF("usage: %s\n", argv[0]); + code = -1; + return; + } + if (!connected) + return; + (void)command("QUIT"); + cleanuppeer(); +} + +void +account(int argc, char *argv[]) +{ + char *ap; + char emptypass[] = ""; + + if (argc == 0 || argc > 2) { + UPRINTF("usage: %s [password]\n", argv[0]); + code = -1; + return; + } + else if (argc == 2) + ap = argv[1]; + else { + ap = getpass("Account:"); + if (ap == NULL) + ap = emptypass; + } + (void)command("ACCT %s", ap); + memset(ap, 0, strlen(ap)); +} + +sigjmp_buf abortprox; + +void +proxabort(int notused) +{ + + sigint_raised = 1; + alarmtimer(0); + if (!proxy) { + pswitch(1); + } + if (connected) { + proxflag = 1; + } + else { + proxflag = 0; + } + pswitch(0); + siglongjmp(abortprox, 1); +} + +void +doproxy(int argc, char *argv[]) +{ + struct cmd *c; + int cmdpos; + sigfunc oldintr; + char cmdbuf[MAX_C_NAME]; + + if (argc == 0 || (argc == 1 && !another(&argc, &argv, "command"))) { + UPRINTF("usage: %s command\n", argv[0]); + code = -1; + return; + } + c = getcmd(argv[1]); + if (c == (struct cmd *) -1) { + fputs("?Ambiguous command.\n", ttyout); + code = -1; + return; + } + if (c == 0) { + fputs("?Invalid command.\n", ttyout); + code = -1; + return; + } + if (!c->c_proxy) { + fputs("?Invalid proxy command.\n", ttyout); + code = -1; + return; + } + if (sigsetjmp(abortprox, 1)) { + code = -1; + return; + } + oldintr = xsignal(SIGINT, proxabort); + pswitch(1); + if (c->c_conn && !connected) { + fputs("Not connected.\n", ttyout); + pswitch(0); + (void)xsignal(SIGINT, oldintr); + code = -1; + return; + } + cmdpos = strcspn(line, " \t"); + if (cmdpos > 0) /* remove leading "proxy " from input buffer */ + memmove(line, line + cmdpos + 1, strlen(line) - cmdpos + 1); + (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf)); + argv[1] = cmdbuf; + (*c->c_handler)(argc-1, argv+1); + if (connected) { + proxflag = 1; + } + else { + proxflag = 0; + } + pswitch(0); + (void)xsignal(SIGINT, oldintr); +} + +void +setcase(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &mcase, "Case mapping"); +} + +/* + * convert the given name to lower case if it's all upper case, into + * a static buffer which is returned to the caller + */ +static const char * +docase(char *dst, size_t dlen, const char *src) +{ + size_t i; + int dochange = 1; + + for (i = 0; src[i] != '\0' && i < dlen - 1; i++) { + dst[i] = src[i]; + if (islower((unsigned char)dst[i])) + dochange = 0; + } + dst[i] = '\0'; + + if (dochange) { + for (i = 0; dst[i] != '\0'; i++) + if (isupper((unsigned char)dst[i])) + dst[i] = tolower((unsigned char)dst[i]); + } + return dst; +} + +void +setcr(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &crflag, "Carriage Return stripping"); +} + +void +setntrans(int argc, char *argv[]) +{ + + if (argc == 0 || argc > 3) { + UPRINTF("usage: %s [inchars [outchars]]\n", argv[0]); + code = -1; + return; + } + if (argc == 1) { + ntflag = 0; + fputs("Ntrans off.\n", ttyout); + code = ntflag; + return; + } + ntflag++; + code = ntflag; + (void)strlcpy(ntin, argv[1], sizeof(ntin)); + if (argc == 2) { + ntout[0] = '\0'; + return; + } + (void)strlcpy(ntout, argv[2], sizeof(ntout)); +} + +static const char * +dotrans(char *dst, size_t dlen, const char *src) +{ + const char *cp1; + char *cp2 = dst; + size_t i, ostop; + + for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) + continue; + for (cp1 = src; *cp1; cp1++) { + int found = 0; + for (i = 0; *(ntin + i) && i < 16; i++) { + if (*cp1 == *(ntin + i)) { + found++; + if (i < ostop) { + *cp2++ = *(ntout + i); + if (cp2 - dst >= (ptrdiff_t)(dlen - 1)) + goto out; + } + break; + } + } + if (!found) { + *cp2++ = *cp1; + } + } +out: + *cp2 = '\0'; + return dst; +} + +void +setnmap(int argc, char *argv[]) +{ + char *cp; + + if (argc == 1) { + mapflag = 0; + fputs("Nmap off.\n", ttyout); + code = mapflag; + return; + } + if (argc == 0 || + (argc < 3 && !another(&argc, &argv, "mapout")) || argc > 3) { + UPRINTF("usage: %s [mapin mapout]\n", argv[0]); + code = -1; + return; + } + mapflag = 1; + code = 1; + cp = strchr(altarg, ' '); + if (proxy) { + while(*++cp == ' ') + continue; + altarg = cp; + cp = strchr(altarg, ' '); + } + *cp = '\0'; + (void)strlcpy(mapin, altarg, MAXPATHLEN); + while (*++cp == ' ') + continue; + (void)strlcpy(mapout, cp, MAXPATHLEN); +} + +static const char * +domap(char *dst, size_t dlen, const char *src) +{ + const char *cp1 = src; + char *cp2 = mapin; + const char *tp[9], *te[9]; + int i, toks[9], toknum = 0, match = 1; + + for (i=0; i < 9; ++i) { + toks[i] = 0; + } + while (match && *cp1 && *cp2) { + switch (*cp2) { + case '\\': + if (*++cp2 != *cp1) { + match = 0; + } + break; + case '$': + if (*(cp2+1) >= '1' && (*cp2+1) <= '9') { + if (*cp1 != *(++cp2+1)) { + toks[toknum = *cp2 - '1']++; + tp[toknum] = cp1; + while (*++cp1 && *(cp2+1) + != *cp1); + te[toknum] = cp1; + } + cp2++; + break; + } + /* FALLTHROUGH */ + default: + if (*cp2 != *cp1) { + match = 0; + } + break; + } + if (match && *cp1) { + cp1++; + } + if (match && *cp2) { + cp2++; + } + } + if (!match && *cp1) /* last token mismatch */ + { + toks[toknum] = 0; + } + cp2 = dst; + *cp2 = '\0'; + cp1 = mapout; + while (*cp1) { + match = 0; + switch (*cp1) { + case '\\': + if (*(cp1 + 1)) { + *cp2++ = *++cp1; + } + break; + case '[': +LOOP: + if (*++cp1 == '$' && + isdigit((unsigned char)*(cp1+1))) { + if (*++cp1 == '0') { + const char *cp3 = src; + + while (*cp3) { + *cp2++ = *cp3++; + } + match = 1; + } + else if (toks[toknum = *cp1 - '1']) { + const char *cp3 = tp[toknum]; + + while (cp3 != te[toknum]) { + *cp2++ = *cp3++; + } + match = 1; + } + } + else { + while (*cp1 && *cp1 != ',' && + *cp1 != ']') { + if (*cp1 == '\\') { + cp1++; + } + else if (*cp1 == '$' && + isdigit((unsigned char)*(cp1+1))) { + if (*++cp1 == '0') { + const char *cp3 = src; + + while (*cp3) { + *cp2++ = *cp3++; + } + } + else if (toks[toknum = + *cp1 - '1']) { + const char *cp3=tp[toknum]; + + while (cp3 != + te[toknum]) { + *cp2++ = *cp3++; + } + } + } + else if (*cp1) { + *cp2++ = *cp1++; + } + } + if (!*cp1) { + fputs( + "nmap: unbalanced brackets.\n", + ttyout); + return (src); + } + match = 1; + cp1--; + } + if (match) { + while (*++cp1 && *cp1 != ']') { + if (*cp1 == '\\' && *(cp1 + 1)) { + cp1++; + } + } + if (!*cp1) { + fputs( + "nmap: unbalanced brackets.\n", + ttyout); + return (src); + } + break; + } + switch (*++cp1) { + case ',': + goto LOOP; + case ']': + break; + default: + cp1--; + goto LOOP; + } + break; + case '$': + if (isdigit((unsigned char)*(cp1 + 1))) { + if (*++cp1 == '0') { + const char *cp3 = src; + + while (*cp3) { + *cp2++ = *cp3++; + } + } + else if (toks[toknum = *cp1 - '1']) { + const char *cp3 = tp[toknum]; + + while (cp3 != te[toknum]) { + *cp2++ = *cp3++; + } + } + break; + } + /* intentional drop through */ + default: + *cp2++ = *cp1; + break; + } + cp1++; + } + *cp2 = '\0'; + return *dst ? dst : src; +} + +void +setpassive(int argc, char *argv[]) +{ + + if (argc == 1) { + passivemode = !passivemode; + activefallback = passivemode; + } else if (argc != 2) { + passiveusage: + UPRINTF("usage: %s [ on | off | auto ]\n", argv[0]); + code = -1; + return; + } else if (strcasecmp(argv[1], "on") == 0) { + passivemode = 1; + activefallback = 0; + } else if (strcasecmp(argv[1], "off") == 0) { + passivemode = 0; + activefallback = 0; + } else if (strcasecmp(argv[1], "auto") == 0) { + passivemode = 1; + activefallback = 1; + } else + goto passiveusage; + fprintf(ttyout, "Passive mode: %s; fallback to active mode: %s.\n", + onoff(passivemode), onoff(activefallback)); + code = passivemode; +} + + +void +setepsv4(int argc, char *argv[]) +{ + code = togglevar(argc, argv, &epsv4, + verbose ? "EPSV/EPRT on IPv4" : NULL); + epsv4bad = 0; +} + +void +setepsv6(int argc, char *argv[]) +{ + code = togglevar(argc, argv, &epsv6, + verbose ? "EPSV/EPRT on IPv6" : NULL); + epsv6bad = 0; +} + +void +setepsv(int argc, char*argv[]) +{ + setepsv4(argc,argv); + setepsv6(argc,argv); +} + +void +setsunique(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &sunique, "Store unique"); +} + +void +setrunique(int argc, char *argv[]) +{ + + code = togglevar(argc, argv, &runique, "Receive unique"); +} + +int +parserate(int argc, char *argv[], int cmdlineopt) +{ + int dir, max, incr, showonly; + sigfunc oldusr1, oldusr2; + + if (argc > 4 || (argc < (cmdlineopt ? 3 : 2))) { + usage: + if (cmdlineopt) + UPRINTF( + "usage: %s (all|get|put),maximum-bytes[,increment-bytes]]\n", + argv[0]); + else + UPRINTF( + "usage: %s (all|get|put) [maximum-bytes [increment-bytes]]\n", + argv[0]); + return -1; + } + dir = max = incr = showonly = 0; +#define RATE_GET 1 +#define RATE_PUT 2 +#define RATE_ALL (RATE_GET | RATE_PUT) + + if (strcasecmp(argv[1], "all") == 0) + dir = RATE_ALL; + else if (strcasecmp(argv[1], "get") == 0) + dir = RATE_GET; + else if (strcasecmp(argv[1], "put") == 0) + dir = RATE_PUT; + else + goto usage; + + if (argc >= 3) { + if ((max = strsuftoi(argv[2])) < 0) + goto usage; + } else + showonly = 1; + + if (argc == 4) { + if ((incr = strsuftoi(argv[3])) <= 0) + goto usage; + } else + incr = DEFAULTINCR; + + oldusr1 = xsignal(SIGUSR1, SIG_IGN); + oldusr2 = xsignal(SIGUSR2, SIG_IGN); + if (dir & RATE_GET) { + if (!showonly) { + rate_get = max; + rate_get_incr = incr; + } + if (!cmdlineopt || verbose) + fprintf(ttyout, + "Get transfer rate throttle: %s; maximum: %d; increment %d.\n", + onoff(rate_get), rate_get, rate_get_incr); + } + if (dir & RATE_PUT) { + if (!showonly) { + rate_put = max; + rate_put_incr = incr; + } + if (!cmdlineopt || verbose) + fprintf(ttyout, + "Put transfer rate throttle: %s; maximum: %d; increment %d.\n", + onoff(rate_put), rate_put, rate_put_incr); + } + (void)xsignal(SIGUSR1, oldusr1); + (void)xsignal(SIGUSR2, oldusr2); + return 0; +} + +void +setrate(int argc, char *argv[]) +{ + + code = parserate(argc, argv, 0); +} + +/* change directory to parent directory */ +void +cdup(int argc, char *argv[]) +{ + int r; + + if (argc == 0) { + UPRINTF("usage: %s\n", argv[0]); + code = -1; + return; + } + r = command("CDUP"); + if (r == ERROR && code == 500) { + if (verbose) + fputs("CDUP command not recognized, trying XCUP.\n", + ttyout); + r = command("XCUP"); + } + if (r == COMPLETE) { + dirchange = 1; + updateremotecwd(); + } +} + +/* + * Restart transfer at specific point + */ +void +restart(int argc, char *argv[]) +{ + + if (argc == 0 || argc > 2) { + UPRINTF("usage: %s [restart-point]\n", argv[0]); + code = -1; + return; + } + if (! features[FEAT_REST_STREAM]) { + fprintf(ttyout, + "Restart is not supported by the remote server.\n"); + return; + } + if (argc == 2) { + off_t rp; + char *ep; + + rp = STRTOLL(argv[1], &ep, 10); + if (rp < 0 || *ep != '\0') + fprintf(ttyout, "restart: Invalid offset `%s'\n", + argv[1]); + else + restart_point = rp; + } + if (restart_point == 0) + fputs("No restart point defined.\n", ttyout); + else + fprintf(ttyout, + "Restarting at " LLF " for next get, put or append\n", + (LLT)restart_point); +} + +/* + * Show remote system type + */ +void +syst(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc == 0) { + UPRINTF("usage: %s\n", argv[0]); + code = -1; + return; + } + verbose = 1; /* If we aren't verbose, this doesn't do anything! */ + (void)command("SYST"); + verbose = oldverbose; +} + +void +macdef(int argc, char *argv[]) +{ + char *tmp; + int c; + + if (argc == 0) + goto usage; + if (macnum == 16) { + fputs("Limit of 16 macros have already been defined.\n", + ttyout); + code = -1; + return; + } + if ((argc < 2 && !another(&argc, &argv, "macro name")) || argc > 2) { + usage: + UPRINTF("usage: %s macro_name\n", argv[0]); + code = -1; + return; + } + if (interactive) + fputs( + "Enter macro line by line, terminating it with a null line.\n", + ttyout); + (void)strlcpy(macros[macnum].mac_name, argv[1], + sizeof(macros[macnum].mac_name)); + if (macnum == 0) + macros[macnum].mac_start = macbuf; + else + macros[macnum].mac_start = macros[macnum - 1].mac_end + 1; + tmp = macros[macnum].mac_start; + while (tmp != macbuf+4096) { + if ((c = getchar()) == EOF) { + fputs("macdef: end of file encountered.\n", ttyout); + code = -1; + return; + } + if ((*tmp = c) == '\n') { + if (tmp == macros[macnum].mac_start) { + macros[macnum++].mac_end = tmp; + code = 0; + return; + } + if (*(tmp-1) == '\0') { + macros[macnum++].mac_end = tmp - 1; + code = 0; + return; + } + *tmp = '\0'; + } + tmp++; + } + while (1) { + while ((c = getchar()) != '\n' && c != EOF) + /* LOOP */; + if (c == EOF || getchar() == '\n') { + fputs("Macro not defined - 4K buffer exceeded.\n", + ttyout); + code = -1; + return; + } + } +} + +/* + * Get size of file on remote machine + */ +void +sizecmd(int argc, char *argv[]) +{ + off_t size; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "remote-file"))) { + UPRINTF("usage: %s remote-file\n", argv[0]); + code = -1; + return; + } + size = remotesize(argv[1], 1); + if (size != -1) + fprintf(ttyout, + "%s\t" LLF "\n", argv[1], (LLT)size); + code = (size > 0); +} + +/* + * Get last modification time of file on remote machine + */ +void +modtime(int argc, char *argv[]) +{ + time_t mtime; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "remote-file"))) { + UPRINTF("usage: %s remote-file\n", argv[0]); + code = -1; + return; + } + mtime = remotemodtime(argv[1], 1); + if (mtime != -1) + fprintf(ttyout, "%s\t%s", argv[1], + rfc2822time(localtime(&mtime))); + code = (mtime > 0); +} + +/* + * Show status on remote machine + */ +void +rmtstatus(int argc, char *argv[]) +{ + + if (argc == 0) { + UPRINTF("usage: %s [remote-file]\n", argv[0]); + code = -1; + return; + } + COMMAND_1ARG(argc, argv, "STAT"); +} + +/* + * Get file if modtime is more recent than current file + */ +void +newer(int argc, char *argv[]) +{ + + if (getit(argc, argv, -1, "w")) + fprintf(ttyout, + "Local file \"%s\" is newer than remote file \"%s\".\n", + argv[2], argv[1]); +} + +/* + * Display one local file through $PAGER. + */ +void +lpage(int argc, char *argv[]) +{ + size_t len; + const char *p; + char *pager, *locfile; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "local-file"))) { + UPRINTF("usage: %s local-file\n", argv[0]); + code = -1; + return; + } + if ((locfile = globulize(argv[1])) == NULL) { + code = -1; + return; + } + p = getoptionvalue("pager"); + if (EMPTYSTRING(p)) + p = DEFAULTPAGER; + len = strlen(p) + strlen(locfile) + 2; + pager = ftp_malloc(len); + (void)strlcpy(pager, p, len); + (void)strlcat(pager, " ", len); + (void)strlcat(pager, locfile, len); + system(pager); + code = 0; + (void)free(pager); + (void)free(locfile); +} + +/* + * Display one remote file through $PAGER. + */ +void +page(int argc, char *argv[]) +{ + int ohash, orestart_point, overbose; + size_t len; + const char *p; + char *pager; + + if (argc == 0 || argc > 2 || + (argc == 1 && !another(&argc, &argv, "remote-file"))) { + UPRINTF("usage: %s remote-file\n", argv[0]); + code = -1; + return; + } + p = getoptionvalue("pager"); + if (EMPTYSTRING(p)) + p = DEFAULTPAGER; + len = strlen(p) + 2; + pager = ftp_malloc(len); + pager[0] = '|'; + (void)strlcpy(pager + 1, p, len - 1); + + ohash = hash; + orestart_point = restart_point; + overbose = verbose; + hash = restart_point = verbose = 0; + recvrequest("RETR", pager, argv[1], "r+", 1, 0); + hash = ohash; + restart_point = orestart_point; + verbose = overbose; + (void)free(pager); +} + +/* + * Set the socket send or receive buffer size. + */ +void +setxferbuf(int argc, char *argv[]) +{ + int size, dir; + + if (argc != 2) { + usage: + UPRINTF("usage: %s size\n", argv[0]); + code = -1; + return; + } + if (strcasecmp(argv[0], "sndbuf") == 0) + dir = RATE_PUT; + else if (strcasecmp(argv[0], "rcvbuf") == 0) + dir = RATE_GET; + else if (strcasecmp(argv[0], "xferbuf") == 0) + dir = RATE_ALL; + else + goto usage; + + if ((size = strsuftoi(argv[1])) == -1) + goto usage; + + if (size == 0) { + fprintf(ttyout, "%s: size must be positive.\n", argv[0]); + goto usage; + } + + if (dir & RATE_PUT) + sndbuf_size = size; + if (dir & RATE_GET) + rcvbuf_size = size; + fprintf(ttyout, "Socket buffer sizes: send %d, receive %d.\n", + sndbuf_size, rcvbuf_size); + code = 0; +} + +/* + * Set or display options (defaults are provided by various env vars) + */ +void +setoption(int argc, char *argv[]) +{ + struct option *o; + + code = -1; + if (argc == 0 || (argc != 1 && argc != 3)) { + UPRINTF("usage: %s [option value]\n", argv[0]); + return; + } + +#define OPTIONINDENT ((int) sizeof("https_proxy")) + if (argc == 1) { + for (o = optiontab; o->name != NULL; o++) { + fprintf(ttyout, "%-*s\t%s\n", OPTIONINDENT, + o->name, o->value ? o->value : ""); + } + } else { + set_option(argv[1], argv[2], 1); + } + code = 0; +} + +void +set_option(const char * option, const char * value, int doverbose) +{ + struct option *o; + + o = getoption(option); + if (o == NULL) { + fprintf(ttyout, "No such option `%s'.\n", option); + return; + } + FREEPTR(o->value); + o->value = ftp_strdup(value); + if (verbose && doverbose) + fprintf(ttyout, "Setting `%s' to `%s'.\n", + o->name, o->value); +} + +/* + * Unset an option + */ +void +unsetoption(int argc, char *argv[]) +{ + struct option *o; + + code = -1; + if (argc == 0 || argc != 2) { + UPRINTF("usage: %s option\n", argv[0]); + return; + } + + o = getoption(argv[1]); + if (o == NULL) { + fprintf(ttyout, "No such option `%s'.\n", argv[1]); + return; + } + FREEPTR(o->value); + fprintf(ttyout, "Unsetting `%s'.\n", o->name); + code = 0; +} + +/* + * Display features supported by the remote host. + */ +void +feat(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc == 0) { + UPRINTF("usage: %s\n", argv[0]); + code = -1; + return; + } + if (! features[FEAT_FEAT]) { + fprintf(ttyout, + "FEAT is not supported by the remote server.\n"); + return; + } + verbose = 1; /* If we aren't verbose, this doesn't do anything! */ + (void)command("FEAT"); + verbose = oldverbose; +} + +void +mlst(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc < 1 || argc > 2) { + UPRINTF("usage: %s [remote-path]\n", argv[0]); + code = -1; + return; + } + if (! features[FEAT_MLST]) { + fprintf(ttyout, + "MLST is not supported by the remote server.\n"); + return; + } + verbose = 1; /* If we aren't verbose, this doesn't do anything! */ + COMMAND_1ARG(argc, argv, "MLST"); + verbose = oldverbose; +} + +void +opts(int argc, char *argv[]) +{ + int oldverbose = verbose; + + if (argc < 2 || argc > 3) { + UPRINTF("usage: %s command [options]\n", argv[0]); + code = -1; + return; + } + if (! features[FEAT_FEAT]) { + fprintf(ttyout, + "OPTS is not supported by the remote server.\n"); + return; + } + verbose = 1; /* If we aren't verbose, this doesn't do anything! */ + if (argc == 2) + command("OPTS %s", argv[1]); + else + command("OPTS %s %s", argv[1], argv[2]); + verbose = oldverbose; +} diff --git a/usr.bin/ftp/cmdtab.c b/usr.bin/ftp/cmdtab.c new file mode 100644 index 000000000..13d3f4b82 --- /dev/null +++ b/usr.bin/ftp/cmdtab.c @@ -0,0 +1,307 @@ +/* $NetBSD: cmdtab.c,v 1.52 2012/12/22 16:57:09 christos Exp $ */ + +/*- + * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cmdtab.c 8.4 (Berkeley) 10/9/94"; +#else +__RCSID("$NetBSD: cmdtab.c,v 1.52 2012/12/22 16:57:09 christos Exp $"); +#endif +#endif /* not lint */ + +#include +#include "ftp_var.h" + +/* + * User FTP -- Command Tables. + */ + +#define HSTR static const char + +#ifndef NO_HELP +HSTR accounthelp[] = "send account command to remote server"; +HSTR appendhelp[] = "append to a file"; +HSTR asciihelp[] = "set ascii transfer type"; +HSTR beephelp[] = "beep when command completed"; +HSTR binaryhelp[] = "set binary transfer type"; +HSTR casehelp[] = "toggle mget upper/lower case id mapping"; +HSTR cdhelp[] = "change remote working directory"; +HSTR cduphelp[] = "change remote working directory to parent directory"; +HSTR chmodhelp[] = "change file permissions of remote file"; +HSTR connecthelp[] = "connect to remote ftp server"; +HSTR crhelp[] = "toggle carriage return stripping on ascii gets"; +HSTR debughelp[] = "toggle/set debugging mode"; +HSTR deletehelp[] = "delete remote file"; +HSTR disconhelp[] = "terminate ftp session"; +HSTR domachelp[] = "execute macro"; +HSTR edithelp[] = "toggle command line editing"; +HSTR epsvhelp[] = "toggle use of EPSV/EPRT on both IPv4 and IPV6 ftp"; +HSTR epsv4help[] = "toggle use of EPSV/EPRT on IPv4 ftp"; +HSTR epsv6help[] = "toggle use of EPSV/EPRT on IPv6 ftp"; +HSTR feathelp[] = "show FEATures supported by remote system"; +HSTR formhelp[] = "set file transfer format"; +HSTR gatehelp[] = "toggle gate-ftp; specify host[:port] to change proxy"; +HSTR globhelp[] = "toggle metacharacter expansion of local file names"; +HSTR hashhelp[] = "toggle printing `#' marks; specify number to set size"; +HSTR helphelp[] = "print local help information"; +HSTR idlehelp[] = "get (set) idle timer on remote side"; +HSTR lcdhelp[] = "change local working directory"; +HSTR lpagehelp[] = "view a local file through your pager"; +HSTR lpwdhelp[] = "print local working directory"; +HSTR lshelp[] = "list contents of remote path"; +HSTR macdefhelp[] = "define a macro"; +HSTR mdeletehelp[] = "delete multiple files"; +HSTR mgethelp[] = "get multiple files"; +HSTR mregethelp[] = "get multiple files restarting at end of local file"; +HSTR fgethelp[] = "get files using a localfile as a source of names"; +HSTR mkdirhelp[] = "make directory on the remote machine"; +HSTR mlshelp[] = "list contents of multiple remote directories"; +HSTR mlsdhelp[] = "list contents of remote directory in a machine " + "parsable form"; +HSTR mlsthelp[] = "list remote path in a machine parsable form"; +HSTR modehelp[] = "set file transfer mode"; +HSTR modtimehelp[] = "show last modification time of remote file"; +HSTR mputhelp[] = "send multiple files"; +HSTR newerhelp[] = "get file if remote file is newer than local file "; +HSTR nmaphelp[] = "set templates for default file name mapping"; +HSTR ntranshelp[] = "set translation table for default file name mapping"; +HSTR optshelp[] = "show or set options for remote commands"; +HSTR pagehelp[] = "view a remote file through your pager"; +HSTR passivehelp[] = "toggle use of passive transfer mode"; +HSTR plshelp[] = "list contents of remote path through your pager"; +HSTR pmlsdhelp[] = "list contents of remote directory in a machine " + "parsable form through your pager"; +HSTR porthelp[] = "toggle use of PORT/LPRT cmd for each data connection"; +HSTR preservehelp[] ="toggle preservation of modification time of " + "retrieved files"; +HSTR progresshelp[] ="toggle transfer progress meter"; +HSTR prompthelp[] = "force interactive prompting on multiple commands"; +HSTR proxyhelp[] = "issue command on alternate connection"; +HSTR pwdhelp[] = "print working directory on remote machine"; +HSTR quithelp[] = "terminate ftp session and exit"; +HSTR quotehelp[] = "send arbitrary ftp command"; +HSTR ratehelp[] = "set transfer rate limit (in bytes/second)"; +HSTR receivehelp[] = "receive file"; +HSTR regethelp[] = "get file restarting at end of local file"; +HSTR remotehelp[] = "get help from remote server"; +HSTR renamehelp[] = "rename file"; +HSTR resethelp[] = "clear queued command replies"; +HSTR restarthelp[]= "restart file transfer at bytecount"; +HSTR rmdirhelp[] = "remove directory on the remote machine"; +HSTR rmtstatushelp[]="show status of remote machine"; +HSTR runiquehelp[] = "toggle store unique for local files"; +HSTR sendhelp[] = "send one file"; +HSTR sethelp[] = "set or display options"; +HSTR shellhelp[] = "escape to the shell"; +HSTR sitehelp[] = "send site specific command to remote server\n" + "\t\tTry \"rhelp site\" or \"site help\" " + "for more information"; +HSTR sizecmdhelp[] = "show size of remote file"; +HSTR statushelp[] = "show current status"; +HSTR structhelp[] = "set file transfer structure"; +HSTR suniquehelp[] = "toggle store unique on remote machine"; +HSTR systemhelp[] = "show remote system type"; +HSTR tenexhelp[] = "set tenex file transfer type"; +HSTR tracehelp[] = "toggle packet tracing"; +HSTR typehelp[] = "set file transfer type"; +HSTR umaskhelp[] = "get (set) umask on remote side"; +HSTR unsethelp[] = "unset an option"; +HSTR usagehelp[] = "show command usage"; +HSTR userhelp[] = "send new user information"; +HSTR verbosehelp[] = "toggle verbose mode"; +HSTR xferbufhelp[] = "set socket send/receive buffer size"; +#endif + +HSTR empty[] = ""; + +#ifdef NO_HELP +#define H(x) empty +#else +#define H(x) x +#endif + +#ifdef NO_EDITCOMPLETE +#define CMPL(x) +#define CMPL0 +#else /* !NO_EDITCOMPLETE */ +#define CMPL(x) #x, +#define CMPL0 empty, +#endif /* !NO_EDITCOMPLETE */ + +struct cmd cmdtab[] = { + { "!", H(shellhelp), 0, 0, 0, CMPL0 shell }, + { "$", H(domachelp), 1, 0, 0, CMPL0 domacro }, + { "account", H(accounthelp), 0, 1, 1, CMPL0 account}, + { "append", H(appendhelp), 1, 1, 1, CMPL(lr) put }, + { "ascii", H(asciihelp), 0, 1, 1, CMPL0 setascii }, + { "bell", H(beephelp), 0, 0, 0, CMPL0 setbell }, + { "binary", H(binaryhelp), 0, 1, 1, CMPL0 setbinary }, + { "bye", H(quithelp), 0, 0, 0, CMPL0 quit }, + { "case", H(casehelp), 0, 0, 1, CMPL0 setcase }, + { "cd", H(cdhelp), 0, 1, 1, CMPL(r) cd }, + { "cdup", H(cduphelp), 0, 1, 1, CMPL0 cdup }, + { "chmod", H(chmodhelp), 0, 1, 1, CMPL(nr) do_chmod }, + { "close", H(disconhelp), 0, 1, 1, CMPL0 disconnect }, + { "cr", H(crhelp), 0, 0, 0, CMPL0 setcr }, + { "debug", H(debughelp), 0, 0, 0, CMPL0 setdebug }, + { "delete", H(deletehelp), 0, 1, 1, CMPL(r) delete }, + { "dir", H(lshelp), 1, 1, 1, CMPL(rl) ls }, + { "disconnect", H(disconhelp), 0, 1, 1, CMPL0 disconnect }, + { "edit", H(edithelp), 0, 0, 0, CMPL0 setedit }, + { "epsv", H(epsvhelp), 0, 0, 0, CMPL0 setepsv }, + { "epsv4", H(epsv4help), 0, 0, 0, CMPL0 setepsv4 }, + { "epsv6", H(epsv6help), 0, 0, 0, CMPL0 setepsv6 }, + { "exit", H(quithelp), 0, 0, 0, CMPL0 quit }, + { "features", H(feathelp), 0, 1, 1, CMPL0 feat }, + { "fget", H(fgethelp), 1, 1, 1, CMPL(l) fget }, + { "form", H(formhelp), 0, 1, 1, CMPL0 setform }, + { "ftp", H(connecthelp), 0, 0, 1, CMPL0 setpeer }, + { "gate", H(gatehelp), 0, 0, 0, CMPL0 setgate }, + { "get", H(receivehelp), 1, 1, 1, CMPL(rl) get }, + { "glob", H(globhelp), 0, 0, 0, CMPL0 setglob }, + { "hash", H(hashhelp), 0, 0, 0, CMPL0 sethash }, + { "help", H(helphelp), 0, 0, 1, CMPL(C) help }, + { "idle", H(idlehelp), 0, 1, 1, CMPL0 idlecmd }, + { "image", H(binaryhelp), 0, 1, 1, CMPL0 setbinary }, + { "lcd", H(lcdhelp), 0, 0, 0, CMPL(l) lcd }, + { "less", H(pagehelp), 1, 1, 1, CMPL(r) page }, + { "lpage", H(lpagehelp), 0, 0, 0, CMPL(l) lpage }, + { "lpwd", H(lpwdhelp), 0, 0, 0, CMPL0 lpwd }, + { "ls", H(lshelp), 1, 1, 1, CMPL(rl) ls }, + { "macdef", H(macdefhelp), 0, 0, 0, CMPL0 macdef }, + { "mdelete", H(mdeletehelp), 1, 1, 1, CMPL(R) mdelete }, + { "mdir", H(mlshelp), 1, 1, 1, CMPL(R) mls }, + { "mget", H(mgethelp), 1, 1, 1, CMPL(R) mget }, + { "mkdir", H(mkdirhelp), 0, 1, 1, CMPL(r) makedir }, + { "mls", H(mlshelp), 1, 1, 1, CMPL(R) mls }, + { "mlsd", H(mlsdhelp), 1, 1, 1, CMPL(r) ls }, + { "mlst", H(mlsthelp), 1, 1, 1, CMPL(r) mlst }, + { "mode", H(modehelp), 0, 1, 1, CMPL0 setftmode }, + { "modtime", H(modtimehelp), 0, 1, 1, CMPL(r) modtime }, + { "more", H(pagehelp), 1, 1, 1, CMPL(r) page }, + { "mput", H(mputhelp), 1, 1, 1, CMPL(L) mput }, + { "mreget", H(mregethelp), 1, 1, 1, CMPL(R) mget }, + { "msend", H(mputhelp), 1, 1, 1, CMPL(L) mput }, + { "newer", H(newerhelp), 1, 1, 1, CMPL(r) newer }, + { "nlist", H(lshelp), 1, 1, 1, CMPL(rl) ls }, + { "nmap", H(nmaphelp), 0, 0, 1, CMPL0 setnmap }, + { "ntrans", H(ntranshelp), 0, 0, 1, CMPL0 setntrans }, + { "open", H(connecthelp), 0, 0, 1, CMPL0 setpeer }, + { "page", H(pagehelp), 1, 1, 1, CMPL(r) page }, + { "passive", H(passivehelp), 0, 0, 0, CMPL0 setpassive }, + { "pdir", H(plshelp), 1, 1, 1, CMPL(r) ls }, + { "pls", H(plshelp), 1, 1, 1, CMPL(r) ls }, + { "pmlsd", H(pmlsdhelp), 1, 1, 1, CMPL(r) ls }, + { "preserve", H(preservehelp),0, 0, 0, CMPL0 setpreserve }, + { "progress", H(progresshelp),0, 0, 0, CMPL0 setprogress }, + { "prompt", H(prompthelp), 0, 0, 0, CMPL0 setprompt }, + { "proxy", H(proxyhelp), 0, 0, 1, CMPL(c) doproxy }, + { "put", H(sendhelp), 1, 1, 1, CMPL(lr) put }, + { "pwd", H(pwdhelp), 0, 1, 1, CMPL0 pwd }, + { "quit", H(quithelp), 0, 0, 0, CMPL0 quit }, + { "quote", H(quotehelp), 1, 1, 1, CMPL0 quote }, + { "rate", H(ratehelp), 0, 0, 0, CMPL0 setrate }, + { "rcvbuf", H(xferbufhelp), 0, 0, 0, CMPL0 setxferbuf }, + { "recv", H(receivehelp), 1, 1, 1, CMPL(rl) get }, + { "reget", H(regethelp), 1, 1, 1, CMPL(rl) reget }, + { "remopts", H(optshelp), 0, 1, 1, CMPL0 opts }, + { "rename", H(renamehelp), 0, 1, 1, CMPL(rr) renamefile }, + { "reset", H(resethelp), 0, 1, 1, CMPL0 reset }, + { "restart", H(restarthelp), 1, 1, 1, CMPL0 restart }, + { "rhelp", H(remotehelp), 0, 1, 1, CMPL0 rmthelp }, + { "rmdir", H(rmdirhelp), 0, 1, 1, CMPL(r) removedir }, + { "rstatus", H(rmtstatushelp),0, 1, 1, CMPL(r) rmtstatus }, + { "runique", H(runiquehelp), 0, 0, 1, CMPL0 setrunique }, + { "send", H(sendhelp), 1, 1, 1, CMPL(lr) put }, + { "sendport", H(porthelp), 0, 0, 0, CMPL0 setport }, + { "set", H(sethelp), 0, 0, 0, CMPL(o) setoption }, + { "site", H(sitehelp), 0, 1, 1, CMPL0 site }, + { "size", H(sizecmdhelp), 1, 1, 1, CMPL(r) sizecmd }, + { "sndbuf", H(xferbufhelp), 0, 0, 0, CMPL0 setxferbuf }, + { "status", H(statushelp), 0, 0, 1, CMPL0 status }, + { "struct", H(structhelp), 0, 1, 1, CMPL0 setstruct }, + { "sunique", H(suniquehelp), 0, 0, 1, CMPL0 setsunique }, + { "system", H(systemhelp), 0, 1, 1, CMPL0 syst }, + { "tenex", H(tenexhelp), 0, 1, 1, CMPL0 settenex }, + { "throttle", H(ratehelp), 0, 0, 0, CMPL0 setrate }, + { "trace", H(tracehelp), 0, 0, 0, CMPL0 settrace }, + { "type", H(typehelp), 0, 1, 1, CMPL0 settype }, + { "umask", H(umaskhelp), 0, 1, 1, CMPL0 do_umask }, + { "unset", H(unsethelp), 0, 0, 0, CMPL(o) unsetoption }, + { "usage", H(usagehelp), 0, 0, 1, CMPL(C) help }, + { "user", H(userhelp), 0, 1, 1, CMPL0 user }, + { "verbose", H(verbosehelp), 0, 0, 0, CMPL0 setverbose }, + { "xferbuf", H(xferbufhelp), 0, 0, 0, CMPL0 setxferbuf }, + { "?", H(helphelp), 0, 0, 1, CMPL(C) help }, + { NULL, NULL, 0, 0, 0, CMPL0 NULL }, +}; + +struct option optiontab[] = { + { "anonpass", NULL }, + { "ftp_proxy", NULL }, + { "http_proxy", NULL }, + { "https_proxy",NULL }, + { "no_proxy", NULL }, + { "pager", NULL }, + { "prompt", NULL }, + { "rprompt", NULL }, + { NULL, NULL }, +}; diff --git a/usr.bin/ftp/complete.c b/usr.bin/ftp/complete.c new file mode 100644 index 000000000..617af07ca --- /dev/null +++ b/usr.bin/ftp/complete.c @@ -0,0 +1,432 @@ +/* $NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp $ */ + +/*- + * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: complete.c,v 1.46 2009/04/12 10:18:52 lukem Exp $"); +#endif /* not lint */ + +/* + * FTP user program - command and file completion routines + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "ftp_var.h" + +#ifndef NO_EDITCOMPLETE + +static int comparstr (const void *, const void *); +static unsigned char complete_ambiguous (char *, int, StringList *); +static unsigned char complete_command (char *, int); +static unsigned char complete_local (char *, int); +static unsigned char complete_option (char *, int); +static unsigned char complete_remote (char *, int); + +static int +comparstr(const void *a, const void *b) +{ + return (strcmp(*(const char * const *)a, *(const char * const *)b)); +} + +/* + * Determine if complete is ambiguous. If unique, insert. + * If no choices, error. If unambiguous prefix, insert that. + * Otherwise, list choices. words is assumed to be filtered + * to only contain possible choices. + * Args: + * word word which started the match + * list list by default + * words stringlist containing possible matches + * Returns a result as per el_set(EL_ADDFN, ...) + */ +static unsigned char +complete_ambiguous(char *word, int list, StringList *words) +{ + char insertstr[MAXPATHLEN]; + char *lastmatch, *p; + size_t i, j; + size_t matchlen, wordlen; + + wordlen = strlen(word); + if (words->sl_cur == 0) + return (CC_ERROR); /* no choices available */ + + if (words->sl_cur == 1) { /* only once choice available */ + p = words->sl_str[0] + wordlen; + if (*p == '\0') /* at end of word? */ + return (CC_REFRESH); + ftpvis(insertstr, sizeof(insertstr), p, strlen(p)); + if (el_insertstr(el, insertstr) == -1) + return (CC_ERROR); + else + return (CC_REFRESH); + } + + if (!list) { + matchlen = 0; + lastmatch = words->sl_str[0]; + matchlen = strlen(lastmatch); + for (i = 1 ; i < words->sl_cur ; i++) { + for (j = wordlen ; j < strlen(words->sl_str[i]); j++) + if (lastmatch[j] != words->sl_str[i][j]) + break; + if (j < matchlen) + matchlen = j; + } + if (matchlen > wordlen) { + ftpvis(insertstr, sizeof(insertstr), + lastmatch + wordlen, matchlen - wordlen); + if (el_insertstr(el, insertstr) == -1) + return (CC_ERROR); + else + return (CC_REFRESH_BEEP); + } + } + + putc('\n', ttyout); + qsort(words->sl_str, words->sl_cur, sizeof(char *), comparstr); + list_vertical(words); + return (CC_REDISPLAY); +} + +/* + * Complete a command + */ +static unsigned char +complete_command(char *word, int list) +{ + struct cmd *c; + StringList *words; + size_t wordlen; + unsigned char rv; + + words = ftp_sl_init(); + wordlen = strlen(word); + + for (c = cmdtab; c->c_name != NULL; c++) { + if (wordlen > strlen(c->c_name)) + continue; + if (strncmp(word, c->c_name, wordlen) == 0) + ftp_sl_add(words, ftp_strdup(c->c_name)); + } + + rv = complete_ambiguous(word, list, words); + if (rv == CC_REFRESH) { + if (el_insertstr(el, " ") == -1) + rv = CC_ERROR; + } + sl_free(words, 1); + return (rv); +} + +/* + * Complete a local file + */ +static unsigned char +complete_local(char *word, int list) +{ + StringList *words; + char dir[MAXPATHLEN]; + char *file; + DIR *dd; + struct dirent *dp; + unsigned char rv; + size_t len; + + if ((file = strrchr(word, '/')) == NULL) { + dir[0] = '.'; + dir[1] = '\0'; + file = word; + } else { + if (file == word) { + dir[0] = '/'; + dir[1] = '\0'; + } else + (void)strlcpy(dir, word, file - word + 1); + file++; + } + if (dir[0] == '~') { + char *p; + + if ((p = globulize(dir)) == NULL) + return (CC_ERROR); + (void)strlcpy(dir, p, sizeof(dir)); + free(p); + } + + if ((dd = opendir(dir)) == NULL) + return (CC_ERROR); + + words = ftp_sl_init(); + len = strlen(file); + + for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + +#if defined(DIRENT_MISSING_D_NAMLEN) + if (len > strlen(dp->d_name)) + continue; +#else + if (len > dp->d_namlen) + continue; +#endif + if (strncmp(file, dp->d_name, len) == 0) { + char *tcp; + + tcp = ftp_strdup(dp->d_name); + ftp_sl_add(words, tcp); + } + } + closedir(dd); + + rv = complete_ambiguous(file, list, words); + if (rv == CC_REFRESH) { + struct stat sb; + char path[MAXPATHLEN]; + + (void)strlcpy(path, dir, sizeof(path)); + (void)strlcat(path, "/", sizeof(path)); + (void)strlcat(path, words->sl_str[0], sizeof(path)); + + if (stat(path, &sb) >= 0) { + char suffix[2] = " "; + + if (S_ISDIR(sb.st_mode)) + suffix[0] = '/'; + if (el_insertstr(el, suffix) == -1) + rv = CC_ERROR; + } + } + sl_free(words, 1); + return (rv); +} +/* + * Complete an option + */ +static unsigned char +complete_option(char *word, int list) +{ + struct option *o; + StringList *words; + size_t wordlen; + unsigned char rv; + + words = ftp_sl_init(); + wordlen = strlen(word); + + for (o = optiontab; o->name != NULL; o++) { + if (wordlen > strlen(o->name)) + continue; + if (strncmp(word, o->name, wordlen) == 0) + ftp_sl_add(words, ftp_strdup(o->name)); + } + + rv = complete_ambiguous(word, list, words); + if (rv == CC_REFRESH) { + if (el_insertstr(el, " ") == -1) + rv = CC_ERROR; + } + sl_free(words, 1); + return (rv); +} + +/* + * Complete a remote file + */ +static unsigned char +complete_remote(char *word, int list) +{ + static StringList *dirlist; + static char lastdir[MAXPATHLEN]; + StringList *words; + char dir[MAXPATHLEN]; + char *file, *cp; + size_t i; + unsigned char rv; + char cmdbuf[MAX_C_NAME]; + char *dummyargv[3] = { NULL, NULL, NULL }; + + (void)strlcpy(cmdbuf, "complete", sizeof(cmdbuf)); + dummyargv[0] = cmdbuf; + dummyargv[1] = dir; + + if ((file = strrchr(word, '/')) == NULL) { + dir[0] = '\0'; + file = word; + } else { + cp = file; + while (*cp == '/' && cp > word) + cp--; + (void)strlcpy(dir, word, cp - word + 2); + file++; + } + + if (dirchange || dirlist == NULL || + strcmp(dir, lastdir) != 0) { /* dir not cached */ + const char *emesg; + + if (dirlist != NULL) + sl_free(dirlist, 1); + dirlist = ftp_sl_init(); + + mflag = 1; + emesg = NULL; + while ((cp = remglob(dummyargv, 0, &emesg)) != NULL) { + char *tcp; + + if (!mflag) + continue; + if (*cp == '\0') { + mflag = 0; + continue; + } + tcp = strrchr(cp, '/'); + if (tcp) + tcp++; + else + tcp = cp; + tcp = ftp_strdup(tcp); + ftp_sl_add(dirlist, tcp); + } + if (emesg != NULL) { + fprintf(ttyout, "\n%s\n", emesg); + return (CC_REDISPLAY); + } + (void)strlcpy(lastdir, dir, sizeof(lastdir)); + dirchange = 0; + } + + words = ftp_sl_init(); + for (i = 0; i < dirlist->sl_cur; i++) { + cp = dirlist->sl_str[i]; + if (strlen(file) > strlen(cp)) + continue; + if (strncmp(file, cp, strlen(file)) == 0) + ftp_sl_add(words, cp); + } + rv = complete_ambiguous(file, list, words); + sl_free(words, 0); + return (rv); +} + +/* + * Generic complete routine + */ +unsigned char +complete(EditLine *cel, int ch) +{ + static char word[FTPBUFLEN]; + static size_t lastc_argc, lastc_argo; + + struct cmd *c; + const LineInfo *lf; + int dolist, cmpltype; + size_t celems, len; + + lf = el_line(cel); + len = lf->lastchar - lf->buffer; + if (len >= sizeof(line)) + return (CC_ERROR); + (void)strlcpy(line, lf->buffer, len + 1); + cursor_pos = line + (lf->cursor - lf->buffer); + lastc_argc = cursor_argc; /* remember last cursor pos */ + lastc_argo = cursor_argo; + makeargv(); /* build argc/argv of current line */ + + if (cursor_argo >= sizeof(word)) + return (CC_ERROR); + + dolist = 0; + /* if cursor and word is same, list alternatives */ + if (lastc_argc == cursor_argc && lastc_argo == cursor_argo + && strncmp(word, margv[cursor_argc] ? margv[cursor_argc] : "", + cursor_argo) == 0) + dolist = 1; + else if (cursor_argc < (size_t)margc) + (void)strlcpy(word, margv[cursor_argc], cursor_argo + 1); + word[cursor_argo] = '\0'; + + if (cursor_argc == 0) + return (complete_command(word, dolist)); + + c = getcmd(margv[0]); + if (c == (struct cmd *)-1 || c == 0) + return (CC_ERROR); + celems = strlen(c->c_complete); + + /* check for 'continuation' completes (which are uppercase) */ + if ((cursor_argc > celems) && (celems > 0) + && isupper((unsigned char) c->c_complete[celems-1])) + cursor_argc = celems; + + if (cursor_argc > celems) + return (CC_ERROR); + + cmpltype = c->c_complete[cursor_argc - 1]; + switch (cmpltype) { + case 'c': /* command complete */ + case 'C': + return (complete_command(word, dolist)); + case 'l': /* local complete */ + case 'L': + return (complete_local(word, dolist)); + case 'n': /* no complete */ + case 'N': /* no complete */ + return (CC_ERROR); + case 'o': /* local complete */ + case 'O': + return (complete_option(word, dolist)); + case 'r': /* remote complete */ + case 'R': + if (connected != -1) { + fputs("\nMust be logged in to complete.\n", + ttyout); + return (CC_REDISPLAY); + } + return (complete_remote(word, dolist)); + default: + errx(1, "complete: unknown complete type `%c'", + cmpltype); + return (CC_ERROR); + } + /* NOTREACHED */ +} + +#endif /* !NO_EDITCOMPLETE */ diff --git a/usr.bin/ftp/domacro.c b/usr.bin/ftp/domacro.c new file mode 100644 index 000000000..04799bc7b --- /dev/null +++ b/usr.bin/ftp/domacro.c @@ -0,0 +1,145 @@ +/* $NetBSD: domacro.c,v 1.22 2009/04/12 10:18:52 lukem Exp $ */ + +/* + * Copyright (c) 1985, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)domacro.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: domacro.c,v 1.22 2009/04/12 10:18:52 lukem Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include + +#include "ftp_var.h" + +void +domacro(int argc, char *argv[]) +{ + int i, j, count = 2, loopflg = 0; + char *cp1, *cp2, line2[FTPBUFLEN]; + struct cmd *c; + char cmdbuf[MAX_C_NAME]; + + if ((argc == 0 && argv != NULL) || + (argc < 2 && !another(&argc, &argv, "macro name"))) { + UPRINTF("usage: %s macro_name [args]\n", argv[0]); + code = -1; + return; + } + for (i = 0; i < macnum; ++i) { + if (!strncmp(argv[1], macros[i].mac_name, 9)) + break; + } + if (i == macnum) { + fprintf(ttyout, "'%s' macro not found.\n", argv[1]); + code = -1; + return; + } + (void)strlcpy(line2, line, sizeof(line2)); + TOP: + cp1 = macros[i].mac_start; + while (cp1 != macros[i].mac_end) { + while (isspace((unsigned char)*cp1)) + cp1++; + cp2 = line; + while (*cp1 != '\0') { + switch(*cp1) { + case '\\': + *cp2++ = *++cp1; + break; + case '$': + if (isdigit((unsigned char)*(cp1+1))) { + j = 0; + while (isdigit((unsigned char)*++cp1)) + j = 10*j + *cp1 - '0'; + cp1--; + if (argc - 2 >= j) { + (void)strlcpy(cp2, argv[j+1], + sizeof(line) - (cp2 - line)); + cp2 += strlen(argv[j+1]); + } + break; + } + if (*(cp1+1) == 'i') { + loopflg = 1; + cp1++; + if (count < argc) { + (void)strlcpy(cp2, argv[count], + sizeof(line) - (cp2 - line)); + cp2 += strlen(argv[count]); + } + break; + } + /* intentional drop through */ + default: + *cp2++ = *cp1; + break; + } + if (*cp1 != '\0') + cp1++; + } + *cp2 = '\0'; + makeargv(); + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + fputs("?Ambiguous command.\n", ttyout); + code = -1; + } else if (c == 0) { + fputs("?Invalid command.\n", ttyout); + code = -1; + } else if (c->c_conn && !connected) { + fputs("Not connected.\n", ttyout); + code = -1; + } else { + if (verbose) { + fputs(line, ttyout); + putc('\n', ttyout); + } + (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf)); + margv[0] = cmdbuf; + (*c->c_handler)(margc, margv); + if (bell && c->c_bell) + (void)putc('\007', ttyout); + (void)strlcpy(line, line2, sizeof(line)); + makeargv(); + argc = margc; + argv = margv; + } + if (cp1 != macros[i].mac_end) + cp1++; + } + if (loopflg && ++count < argc) + goto TOP; +} diff --git a/usr.bin/ftp/extern.h b/usr.bin/ftp/extern.h new file mode 100644 index 000000000..e856ef453 --- /dev/null +++ b/usr.bin/ftp/extern.h @@ -0,0 +1,248 @@ +/* $NetBSD: extern.h,v 1.80 2012/07/04 06:09:37 is Exp $ */ + +/*- + * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1994 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.3 (Berkeley) 10/9/94 + */ + +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +struct sockaddr; +struct tm; +struct addrinfo; + +void abort_remote(FILE *); +void account(int, char **); +void ai_unmapped(struct addrinfo *); +int another(int *, char ***, const char *); +int auto_fetch(int, char **); +int auto_put(int, char **, const char *); +void blkfree(char **); +void cd(int, char **); +void cdup(int, char **); +void changetype(int, int); +void cleanuppeer(void); +void cmdabort(int); +void cmdtimeout(int); +void cmdscanner(void); +int command(const char *, ...) + __attribute__((__format__(__printf__, 1, 2))); +#ifndef NO_EDITCOMPLETE +unsigned char complete(EditLine *, int); +void controlediting(void); +#endif /* !NO_EDITCOMPLETE */ +void crankrate(int); +FILE *dataconn(const char *); +void delete(int, char **); +void disconnect(int, char **); +void do_chmod(int, char **); +void do_umask(int, char **); +void domacro(int, char **); +void doproxy(int, char **); +void feat(int, char **); +void fget(int, char **); +int fileindir(const char *, const char *); +int foregroundproc(void); +void formatbuf(char *, size_t, const char *); +void ftpvis(char *, size_t, const char *, size_t); +int ftp_login(const char *, const char *, const char *); +void get(int, char **); +struct cmd *getcmd(const char *); +int getit(int, char **, int, const char *); +int get_line(FILE *, char *, size_t, const char **); +struct option *getoption(const char *); +char *getoptionvalue(const char *); +void getremoteinfo(void); +int getreply(int); +char *globulize(const char *); +char *gunique(const char *); +void help(int, char **); +char *hookup(const char *, const char *); +void idlecmd(int, char **); +int initconn(void); +__dead void intr(int); +int isipv6addr(const char *); +void list_vertical(StringList *); +void lcd(int, char **); +void lostpeer(int); +void lpage(int, char **); +void lpwd(int, char **); +void ls(int, char **); +void macdef(int, char **); +void makeargv(void); +void makedir(int, char **); +void mdelete(int, char **); +void mget(int, char **); +void mls(int, char **); +void mlst(int, char **); +void modtime(int, char **); +void mput(int, char **); +const char *onoff(int); +void opts(int, char **); +void newer(int, char **); +void page(int, char **); +const char *parse_rfc2616time(struct tm *, const char *); +int parserate(int, char **, int); +char *prompt(void); +__dead void proxabort(int); +void proxtrans(const char *, const char *, const char *); +void psabort(int); +void pswitch(int); +void put(int, char **); +void pwd(int, char **); +void quit(int, char **); +void quote(int, char **); +void quote1(const char *, int, char **); +void recvrequest(const char *, const char *, const char *, + const char *, int, int); +void reget(int, char **); +char *remglob(char **, int, const char **); +time_t remotemodtime(const char *, int); +off_t remotesize(const char *, int); +void removedir(int, char **); +void renamefile(int, char **); +void reset(int, char **); +void restart(int, char **); +const char *rfc2822time(const struct tm *); +void rmthelp(int, char **); +void rmtstatus(int, char **); +char *rprompt(void); +int ruserpass(const char *, char **, char **, char **); +void sendrequest(const char *, const char *, const char *, int); +void setascii(int, char **); +void setbell(int, char **); +void setbinary(int, char **); +void setcase(int, char **); +void setcr(int, char **); +void setdebug(int, char **); +void setedit(int, char **); +void setepsv4(int, char **); +void setepsv6(int, char **); +void setepsv(int, char **); +void setform(int, char **); +void setftmode(int, char **); +void setgate(int, char **); +void setglob(int, char **); +void sethash(int, char **); +void setnmap(int, char **); +void setntrans(int, char **); +void setoption(int, char **); +void setpassive(int, char **); +void setpeer(int, char **); +void setport(int, char **); +void setpreserve(int, char **); +void setprogress(int, char **); +void setprompt(int, char **); +void setrate(int, char **); +void setrunique(int, char **); +void setstruct(int, char **); +void setsunique(int, char **); +void settenex(int, char **); +void settrace(int, char **); +void setttywidth(int); +void settype(int, char **); +void setupsockbufsize(int); +void setverbose(int, char **); +void setxferbuf(int, char **); +void set_option(const char *, const char *, int); +void shell(int, char **); +void site(int, char **); +void sizecmd(int, char **); +char *slurpstring(void); +void status(int, char **); +int strsuftoi(const char *); +void syst(int, char **); +int togglevar(int, char **, int *, const char *); +void unsetoption(int, char **); +void updatelocalcwd(void); +void updateremotecwd(void); +void user(int, char **); +int ftp_connect(int, const struct sockaddr *, socklen_t, int); +int ftp_listen(int, int); +int ftp_poll(struct pollfd *, int, int); +void *ftp_malloc(size_t); +StringList *ftp_sl_init(void); +void ftp_sl_add(StringList *, char *); +char *ftp_strdup(const char *); diff --git a/usr.bin/ftp/fetch.c b/usr.bin/ftp/fetch.c new file mode 100644 index 000000000..418476542 --- /dev/null +++ b/usr.bin/ftp/fetch.c @@ -0,0 +1,1979 @@ +/* $NetBSD: fetch.c,v 1.202 2013/02/23 13:47:36 christos Exp $ */ + +/*- + * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Scott Aaron Bamford. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: fetch.c,v 1.202 2013/02/23 13:47:36 christos Exp $"); +#endif /* not lint */ + +/* + * FTP User Program -- Command line file retrieval + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __minix +#include +#endif + +#include "ssl.h" +#include "ftp_var.h" +#include "version.h" + +typedef enum { + UNKNOWN_URL_T=-1, + HTTP_URL_T, +#ifdef WITH_SSL + HTTPS_URL_T, +#endif + FTP_URL_T, + FILE_URL_T, + CLASSIC_URL_T +} url_t; + +__dead static void aborthttp(int); +#ifndef NO_AUTH +static int auth_url(const char *, char **, const char *, const char *); +static void base64_encode(const unsigned char *, size_t, unsigned char *); +#endif +static int go_fetch(const char *); +static int fetch_ftp(const char *); +static int fetch_url(const char *, const char *, char *, char *); +static const char *match_token(const char **, const char *); +static int parse_url(const char *, const char *, url_t *, char **, + char **, char **, char **, in_port_t *, char **); +static void url_decode(char *); + +static int redirect_loop; + + +#define STRNEQUAL(a,b) (strncasecmp((a), (b), sizeof((b))-1) == 0) +#define ISLWS(x) ((x)=='\r' || (x)=='\n' || (x)==' ' || (x)=='\t') +#define SKIPLWS(x) do { while (ISLWS((*x))) x++; } while (0) + + +#define ABOUT_URL "about:" /* propaganda */ +#define FILE_URL "file://" /* file URL prefix */ +#define FTP_URL "ftp://" /* ftp URL prefix */ +#define HTTP_URL "http://" /* http URL prefix */ +#ifdef WITH_SSL +#define HTTPS_URL "https://" /* https URL prefix */ + +#define IS_HTTP_TYPE(urltype) \ + (((urltype) == HTTP_URL_T) || ((urltype) == HTTPS_URL_T)) +#else +#define IS_HTTP_TYPE(urltype) \ + ((urltype) == HTTP_URL_T) +#endif + +/* + * Determine if token is the next word in buf (case insensitive). + * If so, advance buf past the token and any trailing LWS, and + * return a pointer to the token (in buf). Otherwise, return NULL. + * token may be preceded by LWS. + * token must be followed by LWS or NUL. (I.e, don't partial match). + */ +static const char * +match_token(const char **buf, const char *token) +{ + const char *p, *orig; + size_t tlen; + + tlen = strlen(token); + p = *buf; + SKIPLWS(p); + orig = p; + if (strncasecmp(p, token, tlen) != 0) + return NULL; + p += tlen; + if (*p != '\0' && !ISLWS(*p)) + return NULL; + SKIPLWS(p); + orig = *buf; + *buf = p; + return orig; +} + +#ifndef NO_AUTH +/* + * Generate authorization response based on given authentication challenge. + * Returns -1 if an error occurred, otherwise 0. + * Sets response to a malloc(3)ed string; caller should free. + */ +static int +auth_url(const char *challenge, char **response, const char *guser, + const char *gpass) +{ + const char *cp, *scheme, *errormsg; + char *ep, *clear, *realm; + char uuser[BUFSIZ], *gotpass; + const char *upass; + int rval; + size_t len, clen, rlen; + + *response = NULL; + clear = realm = NULL; + rval = -1; + cp = challenge; + scheme = "Basic"; /* only support Basic authentication */ + gotpass = NULL; + + DPRINTF("auth_url: challenge `%s'\n", challenge); + + if (! match_token(&cp, scheme)) { + warnx("Unsupported authentication challenge `%s'", + challenge); + goto cleanup_auth_url; + } + +#define REALM "realm=\"" + if (STRNEQUAL(cp, REALM)) + cp += sizeof(REALM) - 1; + else { + warnx("Unsupported authentication challenge `%s'", + challenge); + goto cleanup_auth_url; + } +/* XXX: need to improve quoted-string parsing to support \ quoting, etc. */ + if ((ep = strchr(cp, '\"')) != NULL) { + len = ep - cp; + realm = (char *)ftp_malloc(len + 1); + (void)strlcpy(realm, cp, len + 1); + } else { + warnx("Unsupported authentication challenge `%s'", + challenge); + goto cleanup_auth_url; + } + + fprintf(ttyout, "Username for `%s': ", realm); + if (guser != NULL) { + (void)strlcpy(uuser, guser, sizeof(uuser)); + fprintf(ttyout, "%s\n", uuser); + } else { + (void)fflush(ttyout); + if (get_line(stdin, uuser, sizeof(uuser), &errormsg) < 0) { + warnx("%s; can't authenticate", errormsg); + goto cleanup_auth_url; + } + } + if (gpass != NULL) + upass = gpass; + else { + gotpass = getpass("Password: "); + if (gotpass == NULL) { + warnx("Can't read password"); + goto cleanup_auth_url; + } + upass = gotpass; + } + + clen = strlen(uuser) + strlen(upass) + 2; /* user + ":" + pass + "\0" */ + clear = (char *)ftp_malloc(clen); + (void)strlcpy(clear, uuser, clen); + (void)strlcat(clear, ":", clen); + (void)strlcat(clear, upass, clen); + if (gotpass) + memset(gotpass, 0, strlen(gotpass)); + + /* scheme + " " + enc + "\0" */ + rlen = strlen(scheme) + 1 + (clen + 2) * 4 / 3 + 1; + *response = (char *)ftp_malloc(rlen); + (void)strlcpy(*response, scheme, rlen); + len = strlcat(*response, " ", rlen); + /* use `clen - 1' to not encode the trailing NUL */ + base64_encode((unsigned char *)clear, clen - 1, + (unsigned char *)*response + len); + memset(clear, 0, clen); + rval = 0; + + cleanup_auth_url: + FREEPTR(clear); + FREEPTR(realm); + return (rval); +} + +/* + * Encode len bytes starting at clear using base64 encoding into encoded, + * which should be at least ((len + 2) * 4 / 3 + 1) in size. + */ +static void +base64_encode(const unsigned char *clear, size_t len, unsigned char *encoded) +{ + static const unsigned char enc[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + unsigned char *cp; + size_t i; + + cp = encoded; + for (i = 0; i < len; i += 3) { + *(cp++) = enc[((clear[i + 0] >> 2))]; + *(cp++) = enc[((clear[i + 0] << 4) & 0x30) + | ((clear[i + 1] >> 4) & 0x0f)]; + *(cp++) = enc[((clear[i + 1] << 2) & 0x3c) + | ((clear[i + 2] >> 6) & 0x03)]; + *(cp++) = enc[((clear[i + 2] ) & 0x3f)]; + } + *cp = '\0'; + while (i-- > len) + *(--cp) = '='; +} +#endif + +/* + * Decode %xx escapes in given string, `in-place'. + */ +static void +url_decode(char *url) +{ + unsigned char *p, *q; + + if (EMPTYSTRING(url)) + return; + p = q = (unsigned char *)url; + +#define HEXTOINT(x) (x - (isdigit(x) ? '0' : (islower(x) ? 'a' : 'A') - 10)) + while (*p) { + if (p[0] == '%' + && p[1] && isxdigit((unsigned char)p[1]) + && p[2] && isxdigit((unsigned char)p[2])) { + *q++ = HEXTOINT(p[1]) * 16 + HEXTOINT(p[2]); + p+=3; + } else + *q++ = *p++; + } + *q = '\0'; +} + + +/* + * Parse URL of form (per RFC 3986): + * ://[[:]@][:][/] + * Returns -1 if a parse error occurred, otherwise 0. + * It's the caller's responsibility to url_decode() the returned + * user, pass and path. + * + * Sets type to url_t, each of the given char ** pointers to a + * malloc(3)ed strings of the relevant section, and port to + * the number given, or ftpport if ftp://, or httpport if http://. + * + * XXX: this is not totally RFC 3986 compliant; will have the + * leading `/' unless it's an ftp:// URL, as this makes things easier + * for file:// and http:// URLs. ftp:// URLs have the `/' between the + * host and the URL-path removed, but any additional leading slashes + * in the URL-path are retained (because they imply that we should + * later do "CWD" with a null argument). + * + * Examples: + * input URL output path + * --------- ----------- + * "http://host" "/" + * "http://host/" "/" + * "http://host/path" "/path" + * "file://host/dir/file" "dir/file" + * "ftp://host" "" + * "ftp://host/" "" + * "ftp://host//" "/" + * "ftp://host/dir/file" "dir/file" + * "ftp://host//dir/file" "/dir/file" + */ +static int +parse_url(const char *url, const char *desc, url_t *utype, + char **uuser, char **pass, char **host, char **port, + in_port_t *portnum, char **path) +{ + const char *origurl, *tport; + char *cp, *ep, *thost; + size_t len; + + if (url == NULL || desc == NULL || utype == NULL || uuser == NULL + || pass == NULL || host == NULL || port == NULL || portnum == NULL + || path == NULL) + errx(1, "parse_url: invoked with NULL argument!"); + DPRINTF("parse_url: %s `%s'\n", desc, url); + + origurl = url; + *utype = UNKNOWN_URL_T; + *uuser = *pass = *host = *port = *path = NULL; + *portnum = 0; + tport = NULL; + + if (STRNEQUAL(url, HTTP_URL)) { + url += sizeof(HTTP_URL) - 1; + *utype = HTTP_URL_T; + *portnum = HTTP_PORT; + tport = httpport; + } else if (STRNEQUAL(url, FTP_URL)) { + url += sizeof(FTP_URL) - 1; + *utype = FTP_URL_T; + *portnum = FTP_PORT; + tport = ftpport; + } else if (STRNEQUAL(url, FILE_URL)) { + url += sizeof(FILE_URL) - 1; + *utype = FILE_URL_T; +#ifdef WITH_SSL + } else if (STRNEQUAL(url, HTTPS_URL)) { + url += sizeof(HTTPS_URL) - 1; + *utype = HTTPS_URL_T; + *portnum = HTTPS_PORT; + tport = httpsport; +#endif + } else { + warnx("Invalid %s `%s'", desc, url); + cleanup_parse_url: + FREEPTR(*uuser); + if (*pass != NULL) + memset(*pass, 0, strlen(*pass)); + FREEPTR(*pass); + FREEPTR(*host); + FREEPTR(*port); + FREEPTR(*path); + return (-1); + } + + if (*url == '\0') + return (0); + + /* find [user[:pass]@]host[:port] */ + ep = strchr(url, '/'); + if (ep == NULL) + thost = ftp_strdup(url); + else { + len = ep - url; + thost = (char *)ftp_malloc(len + 1); + (void)strlcpy(thost, url, len + 1); + if (*utype == FTP_URL_T) /* skip first / for ftp URLs */ + ep++; + *path = ftp_strdup(ep); + } + + cp = strchr(thost, '@'); /* look for user[:pass]@ in URLs */ + if (cp != NULL) { + if (*utype == FTP_URL_T) + anonftp = 0; /* disable anonftp */ + *uuser = thost; + *cp = '\0'; + thost = ftp_strdup(cp + 1); + cp = strchr(*uuser, ':'); + if (cp != NULL) { + *cp = '\0'; + *pass = ftp_strdup(cp + 1); + } + url_decode(*uuser); + if (*pass) + url_decode(*pass); + } + +#ifdef INET6 + /* + * Check if thost is an encoded IPv6 address, as per + * RFC 3986: + * `[' ipv6-address ']' + */ + if (*thost == '[') { + cp = thost + 1; + if ((ep = strchr(cp, ']')) == NULL || + (ep[1] != '\0' && ep[1] != ':')) { + warnx("Invalid address `%s' in %s `%s'", + thost, desc, origurl); + goto cleanup_parse_url; + } + len = ep - cp; /* change `[xyz]' -> `xyz' */ + memmove(thost, thost + 1, len); + thost[len] = '\0'; + if (! isipv6addr(thost)) { + warnx("Invalid IPv6 address `%s' in %s `%s'", + thost, desc, origurl); + goto cleanup_parse_url; + } + cp = ep + 1; + if (*cp == ':') + cp++; + else + cp = NULL; + } else +#endif /* INET6 */ + if ((cp = strchr(thost, ':')) != NULL) + *cp++ = '\0'; + *host = thost; + + /* look for [:port] */ + if (cp != NULL) { + unsigned long nport; + + nport = strtoul(cp, &ep, 10); + if (*cp == '\0' || *ep != '\0' || + nport < 1 || nport > MAX_IN_PORT_T) { + warnx("Unknown port `%s' in %s `%s'", + cp, desc, origurl); + goto cleanup_parse_url; + } + *portnum = nport; + tport = cp; + } + + if (tport != NULL) + *port = ftp_strdup(tport); + if (*path == NULL) { + const char *emptypath = "/"; + if (*utype == FTP_URL_T) /* skip first / for ftp URLs */ + emptypath++; + *path = ftp_strdup(emptypath); + } + + DPRINTF("parse_url: user `%s' pass `%s' host %s port %s(%d) " + "path `%s'\n", + STRorNULL(*uuser), STRorNULL(*pass), + STRorNULL(*host), STRorNULL(*port), + *portnum ? *portnum : -1, STRorNULL(*path)); + + return (0); +} + +sigjmp_buf httpabort; + +/* + * Retrieve URL, via a proxy if necessary, using HTTP. + * If proxyenv is set, use that for the proxy, otherwise try ftp_proxy or + * http_proxy/https_proxy as appropriate. + * Supports HTTP redirects. + * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection + * is still open (e.g, ftp xfer with trailing /) + */ +static int +fetch_url(const char *url, const char *proxyenv, char *proxyauth, char *wwwauth) +{ + struct addrinfo hints, *res, *res0 = NULL; + int error; + sigfunc volatile oldintr; + sigfunc volatile oldintp; + int volatile s; + struct stat sb; + int volatile ischunked; + int volatile isproxy; + int volatile rval; + int volatile hcode; + int len; + size_t flen; + static size_t bufsize; + static char *xferbuf; + const char *cp, *token; + char *ep; + char buf[FTPBUFLEN]; + const char *errormsg; + char *volatile savefile; + char *volatile auth; + char *volatile location; + char *volatile message; + char *uuser, *pass, *host, *port, *path; + char *volatile decodedpath; + char *puser, *ppass, *useragent; + off_t hashbytes, rangestart, rangeend, entitylen; + int (*volatile closefunc)(FILE *); + FETCH *volatile fin; + FILE *volatile fout; + time_t mtime; + url_t urltype; + in_port_t portnum; +#ifdef WITH_SSL + void *ssl; +#endif + + DPRINTF("fetch_url: `%s' proxyenv `%s'\n", url, STRorNULL(proxyenv)); + + oldintr = oldintp = NULL; + closefunc = NULL; + fin = NULL; + fout = NULL; + s = -1; + savefile = NULL; + auth = location = message = NULL; + ischunked = isproxy = hcode = 0; + rval = 1; + uuser = pass = host = path = decodedpath = puser = ppass = NULL; + + if (parse_url(url, "URL", &urltype, &uuser, &pass, &host, &port, + &portnum, &path) == -1) + goto cleanup_fetch_url; + + if (urltype == FILE_URL_T && ! EMPTYSTRING(host) + && strcasecmp(host, "localhost") != 0) { + warnx("No support for non local file URL `%s'", url); + goto cleanup_fetch_url; + } + + if (EMPTYSTRING(path)) { + if (urltype == FTP_URL_T) { + rval = fetch_ftp(url); + goto cleanup_fetch_url; + } + if (!IS_HTTP_TYPE(urltype) || outfile == NULL) { + warnx("Invalid URL (no file after host) `%s'", url); + goto cleanup_fetch_url; + } + } + + decodedpath = ftp_strdup(path); + url_decode(decodedpath); + + if (outfile) + savefile = ftp_strdup(outfile); + else { + cp = strrchr(decodedpath, '/'); /* find savefile */ + if (cp != NULL) + savefile = ftp_strdup(cp + 1); + else + savefile = ftp_strdup(decodedpath); + } + DPRINTF("fetch_url: savefile `%s'\n", savefile); + if (EMPTYSTRING(savefile)) { + if (urltype == FTP_URL_T) { + rval = fetch_ftp(url); + goto cleanup_fetch_url; + } + warnx("No file after directory (you must specify an " + "output file) `%s'", url); + goto cleanup_fetch_url; + } + + restart_point = 0; + filesize = -1; + rangestart = rangeend = entitylen = -1; + mtime = -1; + if (restartautofetch) { + if (strcmp(savefile, "-") != 0 && *savefile != '|' && + stat(savefile, &sb) == 0) + restart_point = sb.st_size; + } + if (urltype == FILE_URL_T) { /* file:// URLs */ + direction = "copied"; + fin = fetch_open(decodedpath, "r"); + if (fin == NULL) { + warn("Can't open `%s'", decodedpath); + goto cleanup_fetch_url; + } + if (fstat(fetch_fileno(fin), &sb) == 0) { + mtime = sb.st_mtime; + filesize = sb.st_size; + } + if (restart_point) { + if (lseek(fetch_fileno(fin), restart_point, SEEK_SET) < 0) { + warn("Can't seek to restart `%s'", + decodedpath); + goto cleanup_fetch_url; + } + } + if (verbose) { + fprintf(ttyout, "Copying %s", decodedpath); + if (restart_point) + fprintf(ttyout, " (restarting at " LLF ")", + (LLT)restart_point); + fputs("\n", ttyout); + } + if (0 == rcvbuf_size) { + rcvbuf_size = 8 * 1024; /* XXX */ + } + } else { /* ftp:// or http:// URLs */ + const char *leading; + int hasleading; + + if (proxyenv == NULL) { +#ifdef WITH_SSL + if (urltype == HTTPS_URL_T) + proxyenv = getoptionvalue("https_proxy"); +#endif + if (proxyenv == NULL && IS_HTTP_TYPE(urltype)) + proxyenv = getoptionvalue("http_proxy"); + else if (urltype == FTP_URL_T) + proxyenv = getoptionvalue("ftp_proxy"); + } + direction = "retrieved"; + if (! EMPTYSTRING(proxyenv)) { /* use proxy */ + url_t purltype; + char *phost, *ppath; + char *pport, *no_proxy; + in_port_t pportnum; + + isproxy = 1; + + /* check URL against list of no_proxied sites */ + no_proxy = getoptionvalue("no_proxy"); + if (! EMPTYSTRING(no_proxy)) { + char *np, *np_copy, *np_iter; + unsigned long np_port; + size_t hlen, plen; + + np_iter = np_copy = ftp_strdup(no_proxy); + hlen = strlen(host); + while ((cp = strsep(&np_iter, " ,")) != NULL) { + if (*cp == '\0') + continue; + if ((np = strrchr(cp, ':')) != NULL) { + *np++ = '\0'; + np_port = strtoul(np, &ep, 10); + if (*np == '\0' || *ep != '\0') + continue; + if (np_port != portnum) + continue; + } + plen = strlen(cp); + if (hlen < plen) + continue; + if (strncasecmp(host + hlen - plen, + cp, plen) == 0) { + isproxy = 0; + break; + } + } + FREEPTR(np_copy); + if (isproxy == 0 && urltype == FTP_URL_T) { + rval = fetch_ftp(url); + goto cleanup_fetch_url; + } + } + + if (isproxy) { + if (restart_point) { + warnx("Can't restart via proxy URL `%s'", + proxyenv); + goto cleanup_fetch_url; + } + if (parse_url(proxyenv, "proxy URL", &purltype, + &puser, &ppass, &phost, &pport, &pportnum, + &ppath) == -1) + goto cleanup_fetch_url; + + if ((!IS_HTTP_TYPE(purltype) + && purltype != FTP_URL_T) || + EMPTYSTRING(phost) || + (! EMPTYSTRING(ppath) + && strcmp(ppath, "/") != 0)) { + warnx("Malformed proxy URL `%s'", + proxyenv); + FREEPTR(phost); + FREEPTR(pport); + FREEPTR(ppath); + goto cleanup_fetch_url; + } + if (isipv6addr(host) && + strchr(host, '%') != NULL) { + warnx( +"Scoped address notation `%s' disallowed via web proxy", + host); + FREEPTR(phost); + FREEPTR(pport); + FREEPTR(ppath); + goto cleanup_fetch_url; + } + + FREEPTR(host); + host = phost; + FREEPTR(port); + port = pport; + FREEPTR(path); + path = ftp_strdup(url); + FREEPTR(ppath); + urltype = purltype; + } + } /* ! EMPTYSTRING(proxyenv) */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = 0; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(host, port, &hints, &res0); + if (error) { + warnx("Can't LOOKUP `%s:%s': %s", host, port, + (error == EAI_SYSTEM) ? strerror(errno) + : gai_strerror(error)); + goto cleanup_fetch_url; + } + if (res0->ai_canonname) + host = res0->ai_canonname; + + s = -1; +#ifdef WITH_SSL + ssl = NULL; +#endif + for (res = res0; res; res = res->ai_next) { + char hname[NI_MAXHOST], sname[NI_MAXSERV]; + + ai_unmapped(res); + if (getnameinfo(res->ai_addr, res->ai_addrlen, + hname, sizeof(hname), sname, sizeof(sname), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + strlcpy(hname, "?", sizeof(hname)); + strlcpy(sname, "?", sizeof(sname)); + } + + if (verbose && res0->ai_next) { + fprintf(ttyout, "Trying %s:%s ...\n", + hname, sname); + } + + s = socket(res->ai_family, SOCK_STREAM, + res->ai_protocol); + if (s < 0) { + warn( + "Can't create socket for connection to " + "`%s:%s'", hname, sname); + continue; + } + + if (ftp_connect(s, res->ai_addr, res->ai_addrlen, + verbose || !res->ai_next) < 0) { + close(s); + s = -1; + continue; + } + +#ifdef WITH_SSL + if (urltype == HTTPS_URL_T) { + if ((ssl = fetch_start_ssl(s)) == NULL) { + close(s); + s = -1; + continue; + } + } +#endif + + /* success */ + break; + } + + if (s < 0) { + warnx("Can't connect to `%s:%s'", host, port); + goto cleanup_fetch_url; + } + + fin = fetch_fdopen(s, "r+"); + fetch_set_ssl(fin, ssl); + + /* + * Construct and send the request. + */ + if (verbose) + fprintf(ttyout, "Requesting %s\n", url); + leading = " ("; + hasleading = 0; + if (isproxy) { + if (verbose) { + fprintf(ttyout, "%svia %s:%s", leading, + host, port); + leading = ", "; + hasleading++; + } + fetch_printf(fin, "GET %s HTTP/1.0\r\n", path); + if (flushcache) + fetch_printf(fin, "Pragma: no-cache\r\n"); + } else { + fetch_printf(fin, "GET %s HTTP/1.1\r\n", path); + if (strchr(host, ':')) { + char *h, *p; + + /* + * strip off IPv6 scope identifier, since it is + * local to the node + */ + h = ftp_strdup(host); + if (isipv6addr(h) && + (p = strchr(h, '%')) != NULL) { + *p = '\0'; + } + fetch_printf(fin, "Host: [%s]", h); + free(h); + } else + fetch_printf(fin, "Host: %s", host); +#ifdef WITH_SSL + if ((urltype == HTTP_URL_T && portnum != HTTP_PORT) || + (urltype == HTTPS_URL_T && portnum != HTTPS_PORT)) +#else + if (portnum != HTTP_PORT) +#endif + fetch_printf(fin, ":%u", portnum); + fetch_printf(fin, "\r\n"); + fetch_printf(fin, "Accept: */*\r\n"); + fetch_printf(fin, "Connection: close\r\n"); + if (restart_point) { + fputs(leading, ttyout); + fetch_printf(fin, "Range: bytes=" LLF "-\r\n", + (LLT)restart_point); + fprintf(ttyout, "restarting at " LLF, + (LLT)restart_point); + leading = ", "; + hasleading++; + } + if (flushcache) + fetch_printf(fin, "Cache-Control: no-cache\r\n"); + } + if ((useragent=getenv("FTPUSERAGENT")) != NULL) { + fetch_printf(fin, "User-Agent: %s\r\n", useragent); + } else { + fetch_printf(fin, "User-Agent: %s/%s\r\n", + FTP_PRODUCT, FTP_VERSION); + } + if (wwwauth) { + if (verbose) { + fprintf(ttyout, "%swith authorization", + leading); + leading = ", "; + hasleading++; + } + fetch_printf(fin, "Authorization: %s\r\n", wwwauth); + } + if (proxyauth) { + if (verbose) { + fprintf(ttyout, + "%swith proxy authorization", leading); + leading = ", "; + hasleading++; + } + fetch_printf(fin, "Proxy-Authorization: %s\r\n", proxyauth); + } + if (verbose && hasleading) + fputs(")\n", ttyout); + fetch_printf(fin, "\r\n"); + if (fetch_flush(fin) == EOF) { + warn("Writing HTTP request"); + goto cleanup_fetch_url; + } + + /* Read the response */ + len = fetch_getline(fin, buf, sizeof(buf), &errormsg); + if (len < 0) { + if (*errormsg == '\n') + errormsg++; + warnx("Receiving HTTP reply: %s", errormsg); + goto cleanup_fetch_url; + } + while (len > 0 && (ISLWS(buf[len-1]))) + buf[--len] = '\0'; + DPRINTF("fetch_url: received `%s'\n", buf); + + /* Determine HTTP response code */ + cp = strchr(buf, ' '); + if (cp == NULL) + goto improper; + else + cp++; + hcode = strtol(cp, &ep, 10); + if (*ep != '\0' && !isspace((unsigned char)*ep)) + goto improper; + message = ftp_strdup(cp); + + /* Read the rest of the header. */ + while (1) { + len = fetch_getline(fin, buf, sizeof(buf), &errormsg); + if (len < 0) { + if (*errormsg == '\n') + errormsg++; + warnx("Receiving HTTP reply: %s", errormsg); + goto cleanup_fetch_url; + } + while (len > 0 && (ISLWS(buf[len-1]))) + buf[--len] = '\0'; + if (len == 0) + break; + DPRINTF("fetch_url: received `%s'\n", buf); + + /* + * Look for some headers + */ + + cp = buf; + + if (match_token(&cp, "Content-Length:")) { + filesize = STRTOLL(cp, &ep, 10); + if (filesize < 0 || *ep != '\0') + goto improper; + DPRINTF("fetch_url: parsed len as: " LLF "\n", + (LLT)filesize); + + } else if (match_token(&cp, "Content-Range:")) { + if (! match_token(&cp, "bytes")) + goto improper; + + if (*cp == '*') + cp++; + else { + rangestart = STRTOLL(cp, &ep, 10); + if (rangestart < 0 || *ep != '-') + goto improper; + cp = ep + 1; + rangeend = STRTOLL(cp, &ep, 10); + if (rangeend < 0 || rangeend < rangestart) + goto improper; + cp = ep; + } + if (*cp != '/') + goto improper; + cp++; + if (*cp == '*') + cp++; + else { + entitylen = STRTOLL(cp, &ep, 10); + if (entitylen < 0) + goto improper; + cp = ep; + } + if (*cp != '\0') + goto improper; + +#ifndef NO_DEBUG + if (ftp_debug) { + fprintf(ttyout, "parsed range as: "); + if (rangestart == -1) + fprintf(ttyout, "*"); + else + fprintf(ttyout, LLF "-" LLF, + (LLT)rangestart, + (LLT)rangeend); + fprintf(ttyout, "/" LLF "\n", (LLT)entitylen); + } +#endif + if (! restart_point) { + warnx( + "Received unexpected Content-Range header"); + goto cleanup_fetch_url; + } + + } else if (match_token(&cp, "Last-Modified:")) { + struct tm parsed; + const char *t; + + memset(&parsed, 0, sizeof(parsed)); + t = parse_rfc2616time(&parsed, cp); + if (t != NULL) { + parsed.tm_isdst = -1; + if (*t == '\0') + mtime = timegm(&parsed); +#ifndef NO_DEBUG + if (ftp_debug && mtime != -1) { + fprintf(ttyout, + "parsed time as: %s", + rfc2822time(localtime(&mtime))); + } +#endif + } + + } else if (match_token(&cp, "Location:")) { + location = ftp_strdup(cp); + DPRINTF("fetch_url: parsed location as `%s'\n", + cp); + + } else if (match_token(&cp, "Transfer-Encoding:")) { + if (match_token(&cp, "binary")) { + warnx( + "Bogus transfer encoding `binary' (fetching anyway)"); + continue; + } + if (! (token = match_token(&cp, "chunked"))) { + warnx( + "Unsupported transfer encoding `%s'", + token); + goto cleanup_fetch_url; + } + ischunked++; + DPRINTF("fetch_url: using chunked encoding\n"); + + } else if (match_token(&cp, "Proxy-Authenticate:") + || match_token(&cp, "WWW-Authenticate:")) { + if (! (token = match_token(&cp, "Basic"))) { + DPRINTF( + "fetch_url: skipping unknown auth scheme `%s'\n", + token); + continue; + } + FREEPTR(auth); + auth = ftp_strdup(token); + DPRINTF("fetch_url: parsed auth as `%s'\n", cp); + } + + } + /* finished parsing header */ + + switch (hcode) { + case 200: + break; + case 206: + if (! restart_point) { + warnx("Not expecting partial content header"); + goto cleanup_fetch_url; + } + break; + case 300: + case 301: + case 302: + case 303: + case 305: + case 307: + if (EMPTYSTRING(location)) { + warnx( + "No redirection Location provided by server"); + goto cleanup_fetch_url; + } + if (redirect_loop++ > 5) { + warnx("Too many redirections requested"); + goto cleanup_fetch_url; + } + if (hcode == 305) { + if (verbose) + fprintf(ttyout, "Redirected via %s\n", + location); + rval = fetch_url(url, location, + proxyauth, wwwauth); + } else { + if (verbose) + fprintf(ttyout, "Redirected to %s\n", + location); + rval = go_fetch(location); + } + goto cleanup_fetch_url; +#ifndef NO_AUTH + case 401: + case 407: + { + char **authp; + char *auser, *apass; + + if (hcode == 401) { + authp = &wwwauth; + auser = uuser; + apass = pass; + } else { + authp = &proxyauth; + auser = puser; + apass = ppass; + } + if (verbose || *authp == NULL || + auser == NULL || apass == NULL) + fprintf(ttyout, "%s\n", message); + if (EMPTYSTRING(auth)) { + warnx( + "No authentication challenge provided by server"); + goto cleanup_fetch_url; + } + if (*authp != NULL) { + char reply[10]; + + fprintf(ttyout, + "Authorization failed. Retry (y/n)? "); + if (get_line(stdin, reply, sizeof(reply), NULL) + < 0) { + goto cleanup_fetch_url; + } + if (tolower((unsigned char)reply[0]) != 'y') + goto cleanup_fetch_url; + auser = NULL; + apass = NULL; + } + if (auth_url(auth, authp, auser, apass) == 0) { + rval = fetch_url(url, proxyenv, + proxyauth, wwwauth); + memset(*authp, 0, strlen(*authp)); + FREEPTR(*authp); + } + goto cleanup_fetch_url; + } +#endif + default: + if (message) + warnx("Error retrieving file `%s'", message); + else + warnx("Unknown error retrieving file"); + goto cleanup_fetch_url; + } + } /* end of ftp:// or http:// specific setup */ + + /* Open the output file. */ + if (strcmp(savefile, "-") == 0) { + fout = stdout; + } else if (*savefile == '|') { + oldintp = xsignal(SIGPIPE, SIG_IGN); + fout = popen(savefile + 1, "w"); + if (fout == NULL) { + warn("Can't execute `%s'", savefile + 1); + goto cleanup_fetch_url; + } + closefunc = pclose; + } else { + if ((rangeend != -1 && rangeend <= restart_point) || + (rangestart == -1 && filesize != -1 && filesize <= restart_point)) { + /* already done */ + if (verbose) + fprintf(ttyout, "already done\n"); + rval = 0; + goto cleanup_fetch_url; + } + if (restart_point && rangestart != -1) { + if (entitylen != -1) + filesize = entitylen; + if (rangestart != restart_point) { + warnx( + "Size of `%s' differs from save file `%s'", + url, savefile); + goto cleanup_fetch_url; + } + fout = fopen(savefile, "a"); + } else + fout = fopen(savefile, "w"); + if (fout == NULL) { + warn("Can't open `%s'", savefile); + goto cleanup_fetch_url; + } + closefunc = fclose; + } + + /* Trap signals */ + if (sigsetjmp(httpabort, 1)) + goto cleanup_fetch_url; + (void)xsignal(SIGQUIT, psummary); + oldintr = xsignal(SIGINT, aborthttp); + + assert(rcvbuf_size > 0); + if ((size_t)rcvbuf_size > bufsize) { + if (xferbuf) + (void)free(xferbuf); + bufsize = rcvbuf_size; + xferbuf = ftp_malloc(bufsize); + } + + bytes = 0; + hashbytes = mark; + progressmeter(-1); + + /* Finally, suck down the file. */ + do { + long chunksize; + short lastchunk; + + chunksize = 0; + lastchunk = 0; + /* read chunk-size */ + if (ischunked) { + if (fetch_getln(xferbuf, bufsize, fin) == NULL) { + warnx("Unexpected EOF reading chunk-size"); + goto cleanup_fetch_url; + } + errno = 0; + chunksize = strtol(xferbuf, &ep, 16); + if (ep == xferbuf) { + warnx("Invalid chunk-size"); + goto cleanup_fetch_url; + } + if (errno == ERANGE || chunksize < 0) { + errno = ERANGE; + warn("Chunk-size `%.*s'", + (int)(ep-xferbuf), xferbuf); + goto cleanup_fetch_url; + } + + /* + * XXX: Work around bug in Apache 1.3.9 and + * 1.3.11, which incorrectly put trailing + * space after the chunk-size. + */ + while (*ep == ' ') + ep++; + + /* skip [ chunk-ext ] */ + if (*ep == ';') { + while (*ep && *ep != '\r') + ep++; + } + + if (strcmp(ep, "\r\n") != 0) { + warnx("Unexpected data following chunk-size"); + goto cleanup_fetch_url; + } + DPRINTF("fetch_url: got chunk-size of " LLF "\n", + (LLT)chunksize); + if (chunksize == 0) { + lastchunk = 1; + goto chunkdone; + } + } + /* transfer file or chunk */ + while (1) { + struct timeval then, now, td; + off_t bufrem; + + if (rate_get) + (void)gettimeofday(&then, NULL); + bufrem = rate_get ? rate_get : (off_t)bufsize; + if (ischunked) + bufrem = MIN(chunksize, bufrem); + while (bufrem > 0) { + flen = fetch_read(xferbuf, sizeof(char), + MIN((off_t)bufsize, bufrem), fin); + if (flen <= 0) + goto chunkdone; + bytes += flen; + bufrem -= flen; + if (fwrite(xferbuf, sizeof(char), flen, fout) + != flen) { + warn("Writing `%s'", savefile); + goto cleanup_fetch_url; + } + if (hash && !progress) { + while (bytes >= hashbytes) { + (void)putc('#', ttyout); + hashbytes += mark; + } + (void)fflush(ttyout); + } + if (ischunked) { + chunksize -= flen; + if (chunksize <= 0) + break; + } + } + if (rate_get) { + while (1) { + (void)gettimeofday(&now, NULL); + timersub(&now, &then, &td); + if (td.tv_sec > 0) + break; + usleep(1000000 - td.tv_usec); + } + } + if (ischunked && chunksize <= 0) + break; + } + /* read CRLF after chunk*/ + chunkdone: + if (ischunked) { + if (fetch_getln(xferbuf, bufsize, fin) == NULL) { + warnx("Unexpected EOF reading chunk CRLF"); + goto cleanup_fetch_url; + } + if (strcmp(xferbuf, "\r\n") != 0) { + warnx("Unexpected data following chunk"); + goto cleanup_fetch_url; + } + if (lastchunk) + break; + } + } while (ischunked); + +/* XXX: deal with optional trailer & CRLF here? */ + + if (hash && !progress && bytes > 0) { + if (bytes < mark) + (void)putc('#', ttyout); + (void)putc('\n', ttyout); + } + if (fetch_error(fin)) { + warn("Reading file"); + goto cleanup_fetch_url; + } + progressmeter(1); + (void)fflush(fout); + if (closefunc == fclose && mtime != -1) { +#ifdef __minix + struct utimbuf utb; +#endif /* __minix */ + + struct timeval tval[2]; + + (void)gettimeofday(&tval[0], NULL); + +#ifdef __minix + utb.actime = tval[0].tv_sec; + utb.modtime = mtime; +#else /* !__minix */ + tval[1].tv_sec = mtime; + tval[1].tv_usec = 0; +#endif /* !__minix */ + + (*closefunc)(fout); + fout = NULL; + +#ifdef __minix + if (utime(savefile, &utb) == -1) { + fprintf(ttyout, + "Can't change modification time to %s", + rfc2822time(localtime(&mtime))); + } +#else /* !__minix */ + if (utimes(savefile, tval) == -1) { + fprintf(ttyout, + "Can't change modification time to %s", + rfc2822time(localtime(&mtime))); + } +#endif /* !__minix */ + + } + if (bytes > 0) + ptransfer(0); + bytes = 0; + + rval = 0; + goto cleanup_fetch_url; + + improper: + warnx("Improper response from `%s:%s'", host, port); + + cleanup_fetch_url: + if (oldintr) + (void)xsignal(SIGINT, oldintr); + if (oldintp) + (void)xsignal(SIGPIPE, oldintp); + if (fin != NULL) + fetch_close(fin); + else if (s != -1) + close(s); + if (closefunc != NULL && fout != NULL) + (*closefunc)(fout); + if (res0) + freeaddrinfo(res0); + FREEPTR(savefile); + FREEPTR(uuser); + if (pass != NULL) + memset(pass, 0, strlen(pass)); + FREEPTR(pass); + FREEPTR(host); + FREEPTR(port); + FREEPTR(path); + FREEPTR(decodedpath); + FREEPTR(puser); + if (ppass != NULL) + memset(ppass, 0, strlen(ppass)); + FREEPTR(ppass); + FREEPTR(auth); + FREEPTR(location); + FREEPTR(message); + return (rval); +} + +/* + * Abort a HTTP retrieval + */ +static void +aborthttp(int notused) +{ + char msgbuf[100]; + size_t len; + + sigint_raised = 1; + alarmtimer(0); + len = strlcpy(msgbuf, "\nHTTP fetch aborted.\n", sizeof(msgbuf)); + write(fileno(ttyout), msgbuf, len); + siglongjmp(httpabort, 1); +} + +/* + * Retrieve ftp URL or classic ftp argument using FTP. + * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection + * is still open (e.g, ftp xfer with trailing /) + */ +static int +fetch_ftp(const char *url) +{ + char *cp, *xargv[5], rempath[MAXPATHLEN]; + char *host, *path, *dir, *file, *uuser, *pass; + char *port; + char cmdbuf[MAXPATHLEN]; + char dirbuf[4]; + int dirhasglob, filehasglob, rval, transtype, xargc; + int oanonftp, oautologin; + in_port_t portnum; + url_t urltype; + + DPRINTF("fetch_ftp: `%s'\n", url); + host = path = dir = file = uuser = pass = NULL; + port = NULL; + rval = 1; + transtype = TYPE_I; + + if (STRNEQUAL(url, FTP_URL)) { + if ((parse_url(url, "URL", &urltype, &uuser, &pass, + &host, &port, &portnum, &path) == -1) || + (uuser != NULL && *uuser == '\0') || + EMPTYSTRING(host)) { + warnx("Invalid URL `%s'", url); + goto cleanup_fetch_ftp; + } + /* + * Note: Don't url_decode(path) here. We need to keep the + * distinction between "/" and "%2F" until later. + */ + + /* check for trailing ';type=[aid]' */ + if (! EMPTYSTRING(path) && (cp = strrchr(path, ';')) != NULL) { + if (strcasecmp(cp, ";type=a") == 0) + transtype = TYPE_A; + else if (strcasecmp(cp, ";type=i") == 0) + transtype = TYPE_I; + else if (strcasecmp(cp, ";type=d") == 0) { + warnx( + "Directory listing via a URL is not supported"); + goto cleanup_fetch_ftp; + } else { + warnx("Invalid suffix `%s' in URL `%s'", cp, + url); + goto cleanup_fetch_ftp; + } + *cp = 0; + } + } else { /* classic style `[user@]host:[file]' */ + urltype = CLASSIC_URL_T; + host = ftp_strdup(url); + cp = strchr(host, '@'); + if (cp != NULL) { + *cp = '\0'; + uuser = host; + anonftp = 0; /* disable anonftp */ + host = ftp_strdup(cp + 1); + } + cp = strchr(host, ':'); + if (cp != NULL) { + *cp = '\0'; + path = ftp_strdup(cp + 1); + } + } + if (EMPTYSTRING(host)) + goto cleanup_fetch_ftp; + + /* Extract the file and (if present) directory name. */ + dir = path; + if (! EMPTYSTRING(dir)) { + /* + * If we are dealing with classic `[user@]host:[path]' syntax, + * then a path of the form `/file' (resulting from input of the + * form `host:/file') means that we should do "CWD /" before + * retrieving the file. So we set dir="/" and file="file". + * + * But if we are dealing with URLs like `ftp://host/path' then + * a path of the form `/file' (resulting from a URL of the form + * `ftp://host//file') means that we should do `CWD ' (with an + * empty argument) before retrieving the file. So we set + * dir="" and file="file". + * + * If the path does not contain / at all, we set dir=NULL. + * (We get a path without any slashes if we are dealing with + * classic `[user@]host:[file]' or URL `ftp://host/file'.) + * + * In all other cases, we set dir to a string that does not + * include the final '/' that separates the dir part from the + * file part of the path. (This will be the empty string if + * and only if we are dealing with a path of the form `/file' + * resulting from an URL of the form `ftp://host//file'.) + */ + cp = strrchr(dir, '/'); + if (cp == dir && urltype == CLASSIC_URL_T) { + file = cp + 1; + (void)strlcpy(dirbuf, "/", sizeof(dirbuf)); + dir = dirbuf; + } else if (cp != NULL) { + *cp++ = '\0'; + file = cp; + } else { + file = dir; + dir = NULL; + } + } else + dir = NULL; + if (urltype == FTP_URL_T && file != NULL) { + url_decode(file); + /* but still don't url_decode(dir) */ + } + DPRINTF("fetch_ftp: user `%s' pass `%s' host %s port %s " + "path `%s' dir `%s' file `%s'\n", + STRorNULL(uuser), STRorNULL(pass), + STRorNULL(host), STRorNULL(port), + STRorNULL(path), STRorNULL(dir), STRorNULL(file)); + + dirhasglob = filehasglob = 0; + if (doglob && urltype == CLASSIC_URL_T) { + if (! EMPTYSTRING(dir) && strpbrk(dir, "*?[]{}") != NULL) + dirhasglob = 1; + if (! EMPTYSTRING(file) && strpbrk(file, "*?[]{}") != NULL) + filehasglob = 1; + } + + /* Set up the connection */ + oanonftp = anonftp; + if (connected) + disconnect(0, NULL); + anonftp = oanonftp; + (void)strlcpy(cmdbuf, getprogname(), sizeof(cmdbuf)); + xargv[0] = cmdbuf; + xargv[1] = host; + xargv[2] = NULL; + xargc = 2; + if (port) { + xargv[2] = port; + xargv[3] = NULL; + xargc = 3; + } + oautologin = autologin; + /* don't autologin in setpeer(), use ftp_login() below */ + autologin = 0; + setpeer(xargc, xargv); + autologin = oautologin; + if ((connected == 0) || + (connected == 1 && !ftp_login(host, uuser, pass))) { + warnx("Can't connect or login to host `%s:%s'", + host, port ? port : "?"); + goto cleanup_fetch_ftp; + } + + switch (transtype) { + case TYPE_A: + setascii(1, xargv); + break; + case TYPE_I: + setbinary(1, xargv); + break; + default: + errx(1, "fetch_ftp: unknown transfer type %d", transtype); + } + + /* + * Change directories, if necessary. + * + * Note: don't use EMPTYSTRING(dir) below, because + * dir=="" means something different from dir==NULL. + */ + if (dir != NULL && !dirhasglob) { + char *nextpart; + + /* + * If we are dealing with a classic `[user@]host:[path]' + * (urltype is CLASSIC_URL_T) then we have a raw directory + * name (not encoded in any way) and we can change + * directories in one step. + * + * If we are dealing with an `ftp://host/path' URL + * (urltype is FTP_URL_T), then RFC 3986 says we need to + * send a separate CWD command for each unescaped "/" + * in the path, and we have to interpret %hex escaping + * *after* we find the slashes. It's possible to get + * empty components here, (from multiple adjacent + * slashes in the path) and RFC 3986 says that we should + * still do `CWD ' (with a null argument) in such cases. + * + * Many ftp servers don't support `CWD ', so if there's an + * error performing that command, bail out with a descriptive + * message. + * + * Examples: + * + * host: dir="", urltype=CLASSIC_URL_T + * logged in (to default directory) + * host:file dir=NULL, urltype=CLASSIC_URL_T + * "RETR file" + * host:dir/ dir="dir", urltype=CLASSIC_URL_T + * "CWD dir", logged in + * ftp://host/ dir="", urltype=FTP_URL_T + * logged in (to default directory) + * ftp://host/dir/ dir="dir", urltype=FTP_URL_T + * "CWD dir", logged in + * ftp://host/file dir=NULL, urltype=FTP_URL_T + * "RETR file" + * ftp://host//file dir="", urltype=FTP_URL_T + * "CWD ", "RETR file" + * host:/file dir="/", urltype=CLASSIC_URL_T + * "CWD /", "RETR file" + * ftp://host///file dir="/", urltype=FTP_URL_T + * "CWD ", "CWD ", "RETR file" + * ftp://host/%2F/file dir="%2F", urltype=FTP_URL_T + * "CWD /", "RETR file" + * ftp://host/foo/file dir="foo", urltype=FTP_URL_T + * "CWD foo", "RETR file" + * ftp://host/foo/bar/file dir="foo/bar" + * "CWD foo", "CWD bar", "RETR file" + * ftp://host//foo/bar/file dir="/foo/bar" + * "CWD ", "CWD foo", "CWD bar", "RETR file" + * ftp://host/foo//bar/file dir="foo//bar" + * "CWD foo", "CWD ", "CWD bar", "RETR file" + * ftp://host/%2F/foo/bar/file dir="%2F/foo/bar" + * "CWD /", "CWD foo", "CWD bar", "RETR file" + * ftp://host/%2Ffoo/bar/file dir="%2Ffoo/bar" + * "CWD /foo", "CWD bar", "RETR file" + * ftp://host/%2Ffoo%2Fbar/file dir="%2Ffoo%2Fbar" + * "CWD /foo/bar", "RETR file" + * ftp://host/%2Ffoo%2Fbar%2Ffile dir=NULL + * "RETR /foo/bar/file" + * + * Note that we don't need `dir' after this point. + */ + do { + if (urltype == FTP_URL_T) { + nextpart = strchr(dir, '/'); + if (nextpart) { + *nextpart = '\0'; + nextpart++; + } + url_decode(dir); + } else + nextpart = NULL; + DPRINTF("fetch_ftp: dir `%s', nextpart `%s'\n", + STRorNULL(dir), STRorNULL(nextpart)); + if (urltype == FTP_URL_T || *dir != '\0') { + (void)strlcpy(cmdbuf, "cd", sizeof(cmdbuf)); + xargv[0] = cmdbuf; + xargv[1] = dir; + xargv[2] = NULL; + dirchange = 0; + cd(2, xargv); + if (! dirchange) { + if (*dir == '\0' && code == 500) + fprintf(stderr, +"\n" +"ftp: The `CWD ' command (without a directory), which is required by\n" +" RFC 3986 to support the empty directory in the URL pathname (`//'),\n" +" conflicts with the server's conformance to RFC 959.\n" +" Try the same URL without the `//' in the URL pathname.\n" +"\n"); + goto cleanup_fetch_ftp; + } + } + dir = nextpart; + } while (dir != NULL); + } + + if (EMPTYSTRING(file)) { + rval = -1; + goto cleanup_fetch_ftp; + } + + if (dirhasglob) { + (void)strlcpy(rempath, dir, sizeof(rempath)); + (void)strlcat(rempath, "/", sizeof(rempath)); + (void)strlcat(rempath, file, sizeof(rempath)); + file = rempath; + } + + /* Fetch the file(s). */ + xargc = 2; + (void)strlcpy(cmdbuf, "get", sizeof(cmdbuf)); + xargv[0] = cmdbuf; + xargv[1] = file; + xargv[2] = NULL; + if (dirhasglob || filehasglob) { + int ointeractive; + + ointeractive = interactive; + interactive = 0; + if (restartautofetch) + (void)strlcpy(cmdbuf, "mreget", sizeof(cmdbuf)); + else + (void)strlcpy(cmdbuf, "mget", sizeof(cmdbuf)); + xargv[0] = cmdbuf; + mget(xargc, xargv); + interactive = ointeractive; + } else { + if (outfile == NULL) { + cp = strrchr(file, '/'); /* find savefile */ + if (cp != NULL) + outfile = cp + 1; + else + outfile = file; + } + xargv[2] = (char *)outfile; + xargv[3] = NULL; + xargc++; + if (restartautofetch) + reget(xargc, xargv); + else + get(xargc, xargv); + } + + if ((code / 100) == COMPLETE) + rval = 0; + + cleanup_fetch_ftp: + FREEPTR(port); + FREEPTR(host); + FREEPTR(path); + FREEPTR(uuser); + if (pass) + memset(pass, 0, strlen(pass)); + FREEPTR(pass); + return (rval); +} + +/* + * Retrieve the given file to outfile. + * Supports arguments of the form: + * "host:path", "ftp://host/path" if $ftpproxy, call fetch_url() else + * call fetch_ftp() + * "http://host/path" call fetch_url() to use HTTP + * "file:///path" call fetch_url() to copy + * "about:..." print a message + * + * Returns 1 on failure, 0 on completed xfer, -1 if ftp connection + * is still open (e.g, ftp xfer with trailing /) + */ +static int +go_fetch(const char *url) +{ + char *proxyenv; + char *p; + +#ifndef NO_ABOUT + /* + * Check for about:* + */ + if (STRNEQUAL(url, ABOUT_URL)) { + url += sizeof(ABOUT_URL) -1; + if (strcasecmp(url, "ftp") == 0 || + strcasecmp(url, "tnftp") == 0) { + fputs( +"This version of ftp has been enhanced by Luke Mewburn \n" +"for the NetBSD project. Execute `man ftp' for more details.\n", ttyout); + } else if (strcasecmp(url, "lukem") == 0) { + fputs( +"Luke Mewburn is the author of most of the enhancements in this ftp client.\n" +"Please email feedback to .\n", ttyout); + } else if (strcasecmp(url, "netbsd") == 0) { + fputs( +"NetBSD is a freely available and redistributable UNIX-like operating system.\n" +"For more information, see http://www.NetBSD.org/\n", ttyout); + } else if (strcasecmp(url, "version") == 0) { + fprintf(ttyout, "Version: %s %s%s\n", + FTP_PRODUCT, FTP_VERSION, +#ifdef INET6 + "" +#else + " (-IPv6)" +#endif + ); + } else { + fprintf(ttyout, "`%s' is an interesting topic.\n", url); + } + fputs("\n", ttyout); + return (0); + } +#endif + + /* + * Check for file:// and http:// URLs. + */ + if (STRNEQUAL(url, HTTP_URL) +#ifdef WITH_SSL + || STRNEQUAL(url, HTTPS_URL) +#endif + || STRNEQUAL(url, FILE_URL)) + return (fetch_url(url, NULL, NULL, NULL)); + + /* + * If it contains "://" but does not begin with ftp:// + * or something that was already handled, then it's + * unsupported. + * + * If it contains ":" but not "://" then we assume the + * part before the colon is a host name, not an URL scheme, + * so we don't try to match that here. + */ + if ((p = strstr(url, "://")) != NULL && ! STRNEQUAL(url, FTP_URL)) + errx(1, "Unsupported URL scheme `%.*s'", (int)(p - url), url); + + /* + * Try FTP URL-style and host:file arguments next. + * If ftpproxy is set with an FTP URL, use fetch_url() + * Othewise, use fetch_ftp(). + */ + proxyenv = getoptionvalue("ftp_proxy"); + if (!EMPTYSTRING(proxyenv) && STRNEQUAL(url, FTP_URL)) + return (fetch_url(url, NULL, NULL, NULL)); + + return (fetch_ftp(url)); +} + +/* + * Retrieve multiple files from the command line, + * calling go_fetch() for each file. + * + * If an ftp path has a trailing "/", the path will be cd-ed into and + * the connection remains open, and the function will return -1 + * (to indicate the connection is alive). + * If an error occurs the return value will be the offset+1 in + * argv[] of the file that caused a problem (i.e, argv[x] + * returns x+1) + * Otherwise, 0 is returned if all files retrieved successfully. + */ +int +auto_fetch(int argc, char *argv[]) +{ + volatile int argpos, rval; + + argpos = rval = 0; + + if (sigsetjmp(toplevel, 1)) { + if (connected) + disconnect(0, NULL); + if (rval > 0) + rval = argpos + 1; + return (rval); + } + (void)xsignal(SIGINT, intr); + (void)xsignal(SIGPIPE, lostpeer); + + /* + * Loop through as long as there's files to fetch. + */ + for (; (rval == 0) && (argpos < argc); argpos++) { + if (strchr(argv[argpos], ':') == NULL) + break; + redirect_loop = 0; + if (!anonftp) + anonftp = 2; /* Handle "automatic" transfers. */ + rval = go_fetch(argv[argpos]); + if (outfile != NULL && strcmp(outfile, "-") != 0 + && outfile[0] != '|') + outfile = NULL; + if (rval > 0) + rval = argpos + 1; + } + + if (connected && rval != -1) + disconnect(0, NULL); + return (rval); +} + + +/* + * Upload multiple files from the command line. + * + * If an error occurs the return value will be the offset+1 in + * argv[] of the file that caused a problem (i.e, argv[x] + * returns x+1) + * Otherwise, 0 is returned if all files uploaded successfully. + */ +int +auto_put(int argc, char **argv, const char *uploadserver) +{ + char *uargv[4], *path, *pathsep; + int uargc, rval, argpos; + size_t len; + char cmdbuf[MAX_C_NAME]; + + (void)strlcpy(cmdbuf, "mput", sizeof(cmdbuf)); + uargv[0] = cmdbuf; + uargv[1] = argv[0]; + uargc = 2; + uargv[2] = uargv[3] = NULL; + pathsep = NULL; + rval = 1; + + DPRINTF("auto_put: target `%s'\n", uploadserver); + + path = ftp_strdup(uploadserver); + len = strlen(path); + if (path[len - 1] != '/' && path[len - 1] != ':') { + /* + * make sure we always pass a directory to auto_fetch + */ + if (argc > 1) { /* more than one file to upload */ + len = strlen(uploadserver) + 2; /* path + "/" + "\0" */ + free(path); + path = (char *)ftp_malloc(len); + (void)strlcpy(path, uploadserver, len); + (void)strlcat(path, "/", len); + } else { /* single file to upload */ + (void)strlcpy(cmdbuf, "put", sizeof(cmdbuf)); + uargv[0] = cmdbuf; + pathsep = strrchr(path, '/'); + if (pathsep == NULL) { + pathsep = strrchr(path, ':'); + if (pathsep == NULL) { + warnx("Invalid URL `%s'", path); + goto cleanup_auto_put; + } + pathsep++; + uargv[2] = ftp_strdup(pathsep); + pathsep[0] = '/'; + } else + uargv[2] = ftp_strdup(pathsep + 1); + pathsep[1] = '\0'; + uargc++; + } + } + DPRINTF("auto_put: URL `%s' argv[2] `%s'\n", + path, STRorNULL(uargv[2])); + + /* connect and cwd */ + rval = auto_fetch(1, &path); + if(rval >= 0) + goto cleanup_auto_put; + + rval = 0; + + /* target filename provided; upload 1 file */ + /* XXX : is this the best way? */ + if (uargc == 3) { + uargv[1] = argv[0]; + put(uargc, uargv); + if ((code / 100) != COMPLETE) + rval = 1; + } else { /* otherwise a target dir: upload all files to it */ + for(argpos = 0; argv[argpos] != NULL; argpos++) { + uargv[1] = argv[argpos]; + mput(uargc, uargv); + if ((code / 100) != COMPLETE) { + rval = argpos + 1; + break; + } + } + } + + cleanup_auto_put: + free(path); + FREEPTR(uargv[2]); + return (rval); +} diff --git a/usr.bin/ftp/ftp.1 b/usr.bin/ftp/ftp.1 new file mode 100644 index 000000000..931f3b8ee --- /dev/null +++ b/usr.bin/ftp/ftp.1 @@ -0,0 +1,2421 @@ +.\" $NetBSD: ftp.1,v 1.134 2012/12/22 16:57:10 christos Exp $ +.\" +.\" Copyright (c) 1996-2010 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Luke Mewburn. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" +.\" Copyright (c) 1985, 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ftp.1 8.3 (Berkeley) 10/9/94 +.\" +.Dd December 22, 2012 +.Dt FTP 1 +.Os +.Sh NAME +.Nm ftp +.Nd Internet file transfer program +.Sh SYNOPSIS +.Nm +.Op Fl 46AadefginpRtVv +.Op Fl N Ar netrc +.Op Fl o Ar output +.Op Fl P Ar port +.Op Fl q Ar quittime +.Op Fl r Ar retry +.Op Fl s Ar srcaddr +.Bk -words +.\" [-T dir,max[,inc]] +.Oo +.Fl T Xo +.Sm off +.Ar dir , +.Ar max +.Op , Ar inc +.Sm on +.Xc +.Oc +.Ek +.Bk -words +.\" [[user@]host [port]] +.Oo +.Oo Ar user Ns Li \&@ Oc Ns Ar host +.Op Ar port +.Oc +.Ek +.Bk -words +.\" [[user@]host:[path][/]] +.Sm off +.Oo +.Op Ar user Li \&@ +.Ar host Li \&: +.Op Ar path +.Op Li / +.Oc +.Sm on +.Ek +.Bk -words +.\" [file:///path] +.Sm off +.Oo +.Li file:/// Ar path +.Oc +.Sm on +.Ek +.Bk -words +.\" [ftp://[user[:password]@]host[:port]/path[/]] +.Sm off +.Oo +.Li ftp:// +.Oo Ar user +.Op Li \&: Ar password +.Li \&@ Oc +.Ar host Oo Li \&: Ar port Oc +.Li / Ar path +.Op Li / +.Op Li ;type= Ar X +.Oc +.Sm on +.Ek +.Bk -words +.\" [http://[user[:password]@]host[:port]/path] +.Sm off +.Oo +.Li http:// +.Oo Ar user +.Op Li \&: Ar password +.Li \&@ Oc +.Ar host Oo Li \&: Ar port Oc +.Li / Ar path +.Oc +.Sm on +.Ek +.Op Ar \&.\&.\&. +.Nm +.Bk -words +.Fl u Ar URL Ar file +.Ek +.Op Ar \&.\&.\&. +.Sh DESCRIPTION +.Nm +is the user interface to the Internet standard File Transfer Protocol. +The program allows a user to transfer files to and from a +remote network site. +.Pp +The last five arguments will fetch a file using the +.Tn FTP +or +.Tn HTTP +protocols, or by direct copying, into the current directory. +This is ideal for scripts. +Refer to +.Sx AUTO-FETCHING FILES +below for more information. +.Pp +Options may be specified at the command line, or to the +command interpreter. +.Bl -tag -width Fl +.It Fl 4 +Forces +.Nm +to only use IPv4 addresses. +.It Fl 6 +Forces +.Nm +to only use IPv6 addresses. +.It Fl A +Force active mode ftp. +By default, +.Nm +will try to use passive mode ftp and fall back to active mode +if passive is not supported by the server. +This option causes +.Nm +to always use an active connection. +It is only useful for connecting to very old servers that do not +implement passive mode properly. +.It Fl a +Causes +.Nm +to bypass normal login procedure, and use an anonymous login instead. +.It Fl d +Enables debugging. +.It Fl e +Disables command line editing. +This is useful for Emacs ange-ftp mode. +.It Fl f +Forces a cache reload for transfers that go through the +.Tn FTP +or +.Tn HTTP +proxies. +.It Fl g +Disables file name globbing. +.It Fl i +Turns off interactive prompting during +multiple file transfers. +.It Fl N Ar netrc +Use +.Ar netrc +instead of +.Pa ~/.netrc . +Refer to +.Sx THE .netrc FILE +for more information. +.It Fl n +Restrains +.Nm +from attempting +.Dq auto-login +upon initial connection for non auto-fetch transfers. +If auto-login is enabled, +.Nm +will check the +.Pa .netrc +(see below) file in the user's home directory for an entry describing +an account on the remote machine. +If no entry exists, +.Nm +will prompt for the remote machine login name (default is the user +identity on the local machine), and, if necessary, prompt for a password +and an account with which to login. +To override the auto-login for auto-fetch transfers, specify the +username (and optionally, password) as appropriate. +.It Fl o Ar output +When auto-fetching files, save the contents in +.Ar output . +.Ar output +is parsed according to the +.Sx FILE NAMING CONVENTIONS +below. +If +.Ar output +is not +.Sq - +or doesn't start with +.Sq \&| , +then only the first file specified will be retrieved into +.Ar output ; +all other files will be retrieved into the basename of their +remote name. +.It Fl P Ar port +Sets the port number to +.Ar port . +.It Fl p +Enable passive mode operation for use behind connection filtering firewalls. +This option has been deprecated as +.Nm +now tries to use passive mode by default, falling back to active mode +if the server does not support passive connections. +.It Fl q Ar quittime +Quit if the connection has stalled for +.Ar quittime +seconds. +.It Fl R +Restart all non-proxied auto-fetches. +.It Fl r Ar wait +Retry the connection attempt if it failed, pausing for +.Ar wait +seconds. +.It Fl s Ar srcaddr +Uses +.Ar srcaddr +as the local IP address for all connections. +.It Fl t +Enables packet tracing. +.It Fl T Ar direction Ns , Ns Ar maximum Ns Oo , Ns Ar increment Oc +Set the maximum transfer rate for +.Ar direction +to +.Ar maximum +bytes/second, +and if specified, the increment to +.Ar increment +bytes/second. +Refer to +.Ic rate +for more information. +.It Fl u Ar URL file Op \&.\&.\&. +Upload files on the command line to +.Ar URL +where +.Ar URL +is one of the ftp URL types as supported by auto-fetch +(with an optional target filename for single file uploads), and +.Ar file +is one or more local files to be uploaded. +.It Fl V +Disable +.Ic verbose +and +.Ic progress , +overriding the default of enabled when output is to a terminal. +.It Fl v +Enable +.Ic verbose +and +.Ic progress . +This is the default if output is to a terminal (and in the case of +.Ic progress , +.Nm +is the foreground process). +Forces +.Nm +to show all responses from the remote server, as well +as report on data transfer statistics. +.El +.Pp +The client host with which +.Nm +is to communicate may be specified on the command line. +If this is done, +.Nm +will immediately attempt to establish a connection to an +.Tn FTP +server on that host; otherwise, +.Nm +will enter its command interpreter and await instructions +from the user. +When +.Nm +is awaiting commands from the user the prompt +.Ql ftp\*[Gt] +is provided to the user. +The following commands are recognized +by +.Nm ftp : +.Bl -tag -width Ic +.It Ic \&! Op Ar command Op Ar args +Invoke an interactive shell on the local machine. +If there are arguments, the first is taken to be a command to execute +directly, with the rest of the arguments as its arguments. +.It Ic \&$ Ar macro-name Op Ar args +Execute the macro +.Ar macro-name +that was defined with the +.Ic macdef +command. +Arguments are passed to the macro unglobbed. +.It Ic account Op Ar passwd +Supply a supplemental password required by a remote system for access +to resources once a login has been successfully completed. +If no argument is included, the user will be prompted for an account +password in a non-echoing input mode. +.It Ic append Ar local-file Op Ar remote-file +Append a local file to a file on the remote machine. +If +.Ar remote-file +is left unspecified, the local file name is used in naming the +remote file after being altered by any +.Ic ntrans +or +.Ic nmap +setting. +File transfer uses the current settings for +.Ic type , +.Ic format , +.Ic mode , +and +.Ic structure . +.It Ic ascii +Set the file transfer +.Ic type +to network +.Tn ASCII . +This is the default type. +.It Ic bell +Arrange that a bell be sounded after each file transfer +command is completed. +.It Ic binary +Set the file transfer +.Ic type +to support binary image transfer. +.It Ic bye +Terminate the +.Tn FTP +session with the remote server +and exit +.Nm ftp . +An end of file will also terminate the session and exit. +.It Ic case +Toggle remote computer file name case mapping during +.Ic get , +.Ic mget +and +.Ic mput +commands. +When +.Ic case +is on (default is off), remote computer file names with all letters in +upper case are written in the local directory with the letters mapped +to lower case. +.It Ic \&cd Ar remote-directory +Change the working directory on the remote machine +to +.Ar remote-directory . +.It Ic cdup +Change the remote machine working directory to the parent of the +current remote machine working directory. +.It Ic chmod Ar mode remote-file +Change the permission modes of the file +.Ar remote-file +on the remote +system to +.Ar mode . +.It Ic close +Terminate the +.Tn FTP +session with the remote server, and +return to the command interpreter. +Any defined macros are erased. +.It Ic \&cr +Toggle carriage return stripping during +ascii type file retrieval. +Records are denoted by a carriage return/linefeed sequence +during ascii type file transfer. +When +.Ic \&cr +is on (the default), carriage returns are stripped from this +sequence to conform with the +.Ux +single linefeed record +delimiter. +Records on +.Pf non\- Ns Ux +remote systems may contain single linefeeds; +when an ascii type transfer is made, these linefeeds may be +distinguished from a record delimiter only when +.Ic \&cr +is off. +.It Ic delete Ar remote-file +Delete the file +.Ar remote-file +on the remote machine. +.It Ic dir Op Ar remote-path Op Ar local-file +Print a listing of the contents of a +directory on the remote machine. +The listing includes any system-dependent information that the server +chooses to include; for example, most +.Ux +systems will produce +output from the command +.Ql ls \-l . +If +.Ar remote-path +is left unspecified, the current working directory is used. +If interactive prompting is on, +.Nm +will prompt the user to verify that the last argument is indeed the +target local file for receiving +.Ic dir +output. +If no local file is specified, or if +.Ar local-file +is +.Sq Fl , +the output is sent to the terminal. +.It Ic disconnect +A synonym for +.Ic close . +.It Ic edit +Toggle command line editing, and context sensitive command and file +completion. +This is automatically enabled if input is from a terminal, and +disabled otherwise. +.It Ic epsv epsv4 epsv6 +Toggle the use of the extended +.Dv EPSV +and +.Dv EPRT +commands on all IP, IPv4, and IPv6 connections respectively. +First try +.Dv EPSV / +.Dv EPRT , +and then +.Dv PASV / +.Dv PORT . +This is enabled by default. +If an extended command fails then this option will be temporarily +disabled for the duration of the current connection, or until +.Ic epsv , +.Ic epsv4 , +or +.Ic epsv6 +is executed again. +.It Ic exit +A synonym for +.Ic bye . +.It Ic features +Display what features the remote server supports (using the +.Dv FEAT +command). +.It Ic fget Ar localfile +Retrieve the files listed in +.Ar localfile , +which has one line per filename. +.It Ic form Ar format +Set the file transfer +.Ic form +to +.Ar format . +The default (and only supported) +format is +.Dq non-print . +.It Ic ftp Ar host Op Ar port +A synonym for +.Ic open . +.It Ic ftp_debug Op Ar ftp_debug-value +Toggle debugging mode. +If an optional +.Ar ftp_debug-value +is specified it is used to set the debugging level. +When debugging is on, +.Nm +prints each command sent to the remote machine, preceded +by the string +.Ql \-\-\*[Gt] . +.It Ic gate Op Ar host Op Ar port +Toggle gate-ftp mode, which used to connect through the +TIS FWTK and Gauntlet ftp proxies. +This will not be permitted if the gate-ftp server hasn't been set +(either explicitly by the user, or from the +.Ev FTPSERVER +environment variable). +If +.Ar host +is given, +then gate-ftp mode will be enabled, and the gate-ftp server will be set to +.Ar host . +If +.Ar port +is also given, that will be used as the port to connect to on the +gate-ftp server. +.It Ic get Ar remote-file Op Ar local-file +Retrieve the +.Ar remote-file +and store it on the local machine. +If the local +file name is not specified, it is given the same +name it has on the remote machine, subject to +alteration by the current +.Ic case , +.Ic ntrans , +and +.Ic nmap +settings. +The current settings for +.Ic type , +.Ic form , +.Ic mode , +and +.Ic structure +are used while transferring the file. +.It Ic glob +Toggle filename expansion for +.Ic mdelete , +.Ic mget , +.Ic mput , +and +.Ic mreget . +If globbing is turned off with +.Ic glob , +the file name arguments +are taken literally and not expanded. +Globbing for +.Ic mput +is done as in +.Xr csh 1 . +For +.Ic mdelete , +.Ic mget , +and +.Ic mreget , +each remote file name is expanded +separately on the remote machine and the lists are not merged. +Expansion of a directory name is likely to be +different from expansion of the name of an ordinary file: +the exact result depends on the foreign operating system and ftp server, +and can be previewed by doing +.Ql mls remote-files \- +Note: +.Ic mget , +.Ic mput +and +.Ic mreget +are not meant to transfer +entire directory subtrees of files. +That can be done by +transferring a +.Xr tar 1 +archive of the subtree (in binary mode). +.It Ic hash Op Ar size +Toggle hash-sign +.Pq Sq # +printing for each data block transferred. +The size of a data block defaults to 1024 bytes. +This can be changed by specifying +.Ar size +in bytes. +Enabling +.Ic hash +disables +.Ic progress . +.It Ic help Op Ar command +Print an informative message about the meaning of +.Ar command . +If no argument is given, +.Nm +prints a list of the known commands. +.It Ic idle Op Ar seconds +Set the inactivity timer on the remote server to +.Ar seconds +seconds. +If +.Ar seconds +is omitted, the current inactivity timer is printed. +.It Ic image +A synonym for +.Ic binary . +.It Ic lcd Op Ar directory +Change the working directory on the local machine. +If +no +.Ar directory +is specified, the user's home directory is used. +.It Ic less Ar file +A synonym for +.Ic page . +.It Ic lpage Ar local-file +Display +.Ar local-file +with the program specified by the +.Ic "set pager" +option. +.It Ic lpwd +Print the working directory on the local machine. +.It Ic \&ls Op Ar remote-path Op Ar local-file +A synonym for +.Ic dir . +.It Ic macdef Ar macro-name +Define a macro. +Subsequent lines are stored as the macro +.Ar macro-name ; +a null line (consecutive newline characters in a file or carriage +returns from the terminal) terminates macro input mode. +There is a limit of 16 macros and 4096 total characters in all +defined macros. +Macro names can be a maximum of 8 characters. +Macros are only applicable to the current session they are +defined within (or if defined outside a session, to the session +invoked with the next +.Ic open +command), and remain defined until a +.Ic close +command is executed. +To invoke a macro, use the +.Ic $ +command (see above). +.Pp +The macro processor interprets +.Sq $ +and +.Sq \e +as special characters. +A +.Sq $ +followed by a number (or numbers) is replaced by the +corresponding argument on the macro invocation command line. +A +.Sq $ +followed by an +.Sq i +signals the macro processor that the executing macro is to be +looped. +On the first pass +.Dq $i +is replaced by the first argument on the macro invocation command +line, on the second pass it is replaced by the second argument, +and so on. +A +.Sq \e +followed by any character is replaced by that character. +Use the +.Sq \e +to prevent special treatment of the +.Sq $ . +.It Ic mdelete Op Ar remote-files +Delete the +.Ar remote-files +on the remote machine. +.It Ic mdir Ar remote-files local-file +Like +.Ic dir , +except multiple remote files may be specified. +If interactive prompting is on, +.Nm +will prompt the user to verify that the last argument is indeed the +target local file for receiving +.Ic mdir +output. +.It Ic mget Ar remote-files +Expand the +.Ar remote-files +on the remote machine +and do a +.Ic get +for each file name thus produced. +See +.Ic glob +for details on the filename expansion. +Resulting file names will then be processed according to +.Ic case , +.Ic ntrans , +and +.Ic nmap +settings. +Files are transferred into the local working directory, +which can be changed with +.Ql lcd directory ; +new local directories can be created with +.Ql "\&! mkdir directory" . +.It Ic mkdir Ar directory-name +Make a directory on the remote machine. +.It Ic mls Ar remote-files local-file +Like +.Ic ls , +except multiple remote files may be specified, +and the +.Ar local-file +must be specified. +If interactive prompting is on, +.Nm +will prompt the user to verify that the last argument is indeed the +target local file for receiving +.Ic mls +output. +.It Ic mlsd Op Ar remote-path +Display the contents of +.Ar remote-path +(which should default to the current directory if not given) +in a machine-parsable form, using +.Dv MLSD . +The format of display can be changed with +.Sq "remopts mlst ..." . +.It Ic mlst Op Ar remote-path +Display the details about +.Ar remote-path +(which should default to the current directory if not given) +in a machine-parsable form, using +.Dv MLST . +The format of display can be changed with +.Sq "remopts mlst ..." . +.It Ic mode Ar mode-name +Set the file transfer +.Ic mode +to +.Ar mode-name . +The default (and only supported) +mode is +.Dq stream . +.It Ic modtime Ar remote-file +Show the last modification time of the file on the remote machine, in +.Li RFC 2822 +format. +.It Ic more Ar file +A synonym for +.Ic page . +.It Ic mput Ar local-files +Expand wild cards in the list of local files given as arguments +and do a +.Ic put +for each file in the resulting list. +See +.Ic glob +for details of filename expansion. +Resulting file names will then be processed according to +.Ic ntrans +and +.Ic nmap +settings. +.It Ic mreget Ar remote-files +As per +.Ic mget , +but performs a +.Ic reget +instead of +.Ic get . +.It Ic msend Ar local-files +A synonym for +.Ic mput . +.It Ic newer Ar remote-file Op Ar local-file +Get the file only if the modification time of the remote file is more +recent that the file on the current system. +If the file does not +exist on the current system, the remote file is considered +.Ic newer . +Otherwise, this command is identical to +.Ar get . +.It Ic nlist Op Ar remote-path Op Ar local-file +A synonym for +.Ic ls . +.It Ic nmap Op Ar inpattern outpattern +Set or unset the filename mapping mechanism. +If no arguments are specified, the filename mapping mechanism is unset. +If arguments are specified, remote filenames are mapped during +.Ic mput +commands and +.Ic put +commands issued without a specified remote target filename. +If arguments are specified, local filenames are mapped during +.Ic mget +commands and +.Ic get +commands issued without a specified local target filename. +This command is useful when connecting to a +.No non\- Ns Ux +remote computer +with different file naming conventions or practices. +The mapping follows the pattern set by +.Ar inpattern +and +.Ar outpattern . +.Op Ar Inpattern +is a template for incoming filenames (which may have already been +processed according to the +.Ic ntrans +and +.Ic case +settings). +Variable templating is accomplished by including the +sequences +.Dq $1 , +.Dq $2 , +\&... +.Dq $9 +in +.Ar inpattern . +Use +.Sq \e +to prevent this special treatment of the +.Sq $ +character. +All other characters are treated literally, and are used to determine the +.Ic nmap +.Op Ar inpattern +variable values. +For example, given +.Ar inpattern +$1.$2 and the remote file name "mydata.data", $1 would have the value +"mydata", and $2 would have the value "data". +The +.Ar outpattern +determines the resulting mapped filename. +The sequences +.Dq $1 , +.Dq $2 , +\&... +.Dq $9 +are replaced by any value resulting from the +.Ar inpattern +template. +The sequence +.Dq $0 +is replaced by the original filename. +Additionally, the sequence +.Dq Op Ar seq1 , Ar seq2 +is replaced by +.Op Ar seq1 +if +.Ar seq1 +is not a null string; otherwise it is replaced by +.Ar seq2 . +For example, the command +.Pp +.Bd -literal -offset indent -compact +nmap $1.$2.$3 [$1,$2].[$2,file] +.Ed +.Pp +would yield +the output filename "myfile.data" for input filenames "myfile.data" and +"myfile.data.old", "myfile.file" for the input filename "myfile", and +"myfile.myfile" for the input filename ".myfile". +Spaces may be included in +.Ar outpattern , +as in the example: +.Dl nmap $1 sed "s/ *$//" \*[Gt] $1 +Use the +.Sq \e +character to prevent special treatment +of the +.Sq $ , +.Sq \&[ , +.Sq \&] , +and +.Sq \&, +characters. +.It Ic ntrans Op Ar inchars Op Ar outchars +Set or unset the filename character translation mechanism. +If no arguments are specified, the filename character +translation mechanism is unset. +If arguments are specified, characters in +remote filenames are translated during +.Ic mput +commands and +.Ic put +commands issued without a specified remote target filename. +If arguments are specified, characters in +local filenames are translated during +.Ic mget +commands and +.Ic get +commands issued without a specified local target filename. +This command is useful when connecting to a +.No non\- Ns Ux +remote computer +with different file naming conventions or practices. +Characters in a filename matching a character in +.Ar inchars +are replaced with the corresponding character in +.Ar outchars . +If the character's position in +.Ar inchars +is longer than the length of +.Ar outchars , +the character is deleted from the file name. +.It Ic open Ar host Op Ar port +Establish a connection to the specified +.Ar host +.Tn FTP +server. +An optional port number may be supplied, +in which case, +.Nm +will attempt to contact an +.Tn FTP +server at that port. +If the +.Ic "set auto-login" +option is on (default), +.Nm +will also attempt to automatically log the user in to +the +.Tn FTP +server (see below). +.It Ic page Ar file +Retrieve +.Ic file +and display with the program specified by the +.Ic "set pager" +option. +.It Ic passive Op Cm auto +Toggle passive mode (if no arguments are given). +If +.Cm auto +is given, act as if +.Ev FTPMODE +is set to +.Sq auto . +If passive mode is turned on (default), +.Nm +will send a +.Dv PASV +command for all data connections instead of a +.Dv PORT +command. +The +.Dv PASV +command requests that the remote server open a port for the data connection +and return the address of that port. +The remote server listens on that port and the client connects to it. +When using the more traditional +.Dv PORT +command, the client listens on a port and sends that address to the remote +server, who connects back to it. +Passive mode is useful when using +.Nm +through a gateway router or host that controls the directionality of +traffic. +(Note that though +.Tn FTP +servers are required to support the +.Dv PASV +command by +.Li RFC 1123 , +some do not.) +.It Ic pdir Op Ar remote-path +Perform +.Ic dir +.Op Ar remote-path , +and display the result with the program specified by the +.Ic "set pager" +option. +.It Ic pls Op Ar remote-path +Perform +.Ic ls +.Op Ar remote-path , +and display the result with the program specified by the +.Ic "set pager" +option. +.It Ic pmlsd Op Ar remote-path +Perform +.Ic mlsd +.Op Ar remote-path , +and display the result with the program specified by the +.Ic "set pager" +option. +.It Ic preserve +Toggle preservation of modification times on retrieved files. +.It Ic progress +Toggle display of transfer progress bar. +The progress bar will be disabled for a transfer that has +.Ar local-file +as +.Sq Fl +or a command that starts with +.Sq \&| . +Refer to +.Sx FILE NAMING CONVENTIONS +for more information. +Enabling +.Ic progress +disables +.Ic hash . +.It Ic prompt +Toggle interactive prompting. +Interactive prompting +occurs during multiple file transfers to allow the +user to selectively retrieve or store files. +If prompting is turned off (default is on), any +.Ic mget +or +.Ic mput +will transfer all files, and any +.Ic mdelete +will delete all files. +.Pp +When prompting is on, the following commands are available at a prompt: +.Bl -tag -width 2n -offset indent +.It Cm a +Answer +.Sq yes +to the current file, and automatically answer +.Sq yes +to any remaining files for the current command. +.It Cm n +Answer +.Sq no , +and do not transfer the file. +.It Cm p +Answer +.Sq yes +to the current file, and turn off prompt mode +(as is +.Dq prompt off +had been given). +.It Cm q +Terminate the current operation. +.It Cm y +Answer +.Sq yes , +and transfer the file. +.It Cm \&? +Display a help message. +.El +.Pp +Any other response will answer +.Sq yes +to the current file. +.It Ic proxy Ar ftp-command +Execute an ftp command on a secondary control connection. +This command allows simultaneous connection to two remote +.Tn FTP +servers for transferring files between the two servers. +The first +.Ic proxy +command should be an +.Ic open , +to establish the secondary control connection. +Enter the command "proxy ?" to see other +.Tn FTP +commands executable on the secondary connection. +The following commands behave differently when prefaced by +.Ic proxy : +.Ic open +will not define new macros during the auto-login process, +.Ic close +will not erase existing macro definitions, +.Ic get +and +.Ic mget +transfer files from the host on the primary control connection +to the host on the secondary control connection, and +.Ic put , +.Ic mput , +and +.Ic append +transfer files from the host on the secondary control connection +to the host on the primary control connection. +Third party file transfers depend upon support of the +.Tn FTP +protocol +.Dv PASV +command by the server on the secondary control connection. +.It Ic put Ar local-file Op Ar remote-file +Store a local file on the remote machine. +If +.Ar remote-file +is left unspecified, the local file name is used +after processing according to any +.Ic ntrans +or +.Ic nmap +settings +in naming the remote file. +File transfer uses the +current settings for +.Ic type , +.Ic format , +.Ic mode , +and +.Ic structure . +.It Ic pwd +Print the name of the current working directory on the remote +machine. +.It Ic quit +A synonym for +.Ic bye . +.It Ic quote Ar arg1 arg2 ... +The arguments specified are sent, verbatim, to the remote +.Tn FTP +server. +.It Ic rate Ar direction Oo Ar maximum Oo Ar increment Oc Oc +Throttle the maximum transfer rate to +.Ar maximum +bytes/second. +If +.Ar maximum +is 0, disable the throttle. +.Pp +.Ar direction +may be one of: +.Bl -tag -width "all" -offset indent -compact +.It Cm all +Both directions. +.It Cm get +Incoming transfers. +.It Cm put +Outgoing transfers. +.El +.Pp +.Ar maximum +can be modified on the fly by +.Ar increment +bytes (default: 1024) each time a given signal is received: +.Bl -tag -width "SIGUSR1" -offset indent +.It Dv SIGUSR1 +Increment +.Ar maximum +by +.Ar increment +bytes. +.It Dv SIGUSR2 +Decrement +.Ar maximum +by +.Ar increment +bytes. +The result must be a positive number. +.El +.Pp +If +.Ar maximum +is not supplied, the current throttle rates are displayed. +.Pp +Note: +.Ic rate +is not yet implemented for ascii mode transfers. +.It Ic rcvbuf Ar size +Set the size of the socket receive buffer to +.Ar size . +.It Ic recv Ar remote-file Op Ar local-file +A synonym for +.Ic get . +.It Ic reget Ar remote-file Op Ar local-file +.Ic reget +acts like +.Ic get , +except that if +.Ar local-file +exists and is +smaller than +.Ar remote-file , +.Ar local-file +is presumed to be +a partially transferred copy of +.Ar remote-file +and the transfer +is continued from the apparent point of failure. +This command +is useful when transferring very large files over networks that +are prone to dropping connections. +.It Ic remopts Ar command Op Ar command-options +Set options on the remote +.Tn FTP +server for +.Ar command +to +.Ar command-options +(whose absence is handled on a command-specific basis). +Remote +.Tn FTP +commands known to support options include: +.Sq MLST +(used for +.Dv MLSD +and +.Dv MLST ) . +.It Ic rename Op Ar from Op Ar to +Rename the file +.Ar from +on the remote machine, to the file +.Ar to . +.It Ic reset +Clear reply queue. +This command re-synchronizes command/reply sequencing with the remote +.Tn FTP +server. +Resynchronization may be necessary following a violation of the +.Tn FTP +protocol by the remote server. +.It Ic restart Ar marker +Restart the immediately following +.Ic get +or +.Ic put +at the +indicated +.Ar marker . +On +.Ux +systems, marker is usually a byte +offset into the file. +.It Ic rhelp Op Ar command-name +Request help from the remote +.Tn FTP +server. +If a +.Ar command-name +is specified it is supplied to the server as well. +.It Ic rmdir Ar directory-name +Delete a directory on the remote machine. +.It Ic rstatus Op Ar remote-file +With no arguments, show status of remote machine. +If +.Ar remote-file +is specified, show status of +.Ar remote-file +on remote machine. +.It Ic runique +Toggle storing of files on the local system with unique filenames. +If a file already exists with a name equal to the target +local filename for a +.Ic get +or +.Ic mget +command, a ".1" is appended to the name. +If the resulting name matches another existing file, +a ".2" is appended to the original name. +If this process continues up to ".99", an error +message is printed, and the transfer does not take place. +The generated unique filename will be reported. +Note that +.Ic runique +will not affect local files generated from a shell command +(see below). +The default value is off. +.It Ic send Ar local-file Op Ar remote-file +A synonym for +.Ic put . +.It Ic sendport +Toggle the use of +.Dv PORT +commands. +By default, +.Nm +will attempt to use a +.Dv PORT +command when establishing +a connection for each data transfer. +The use of +.Dv PORT +commands can prevent delays +when performing multiple file transfers. +If the +.Dv PORT +command fails, +.Nm +will use the default data port. +When the use of +.Dv PORT +commands is disabled, no attempt will be made to use +.Dv PORT +commands for each data transfer. +This is useful +for certain +.Tn FTP +implementations which do ignore +.Dv PORT +commands but, incorrectly, indicate they've been accepted. +.It Ic set Op Ar option Ar value +Set +.Ar option +to +.Ar value . +If +.Ar option +and +.Ar value +are not given, display all of the options and their values. +The currently supported options are: +.Bl -tag -width "https_proxy" -offset indent +.It Cm anonpass +Defaults to +.Ev $FTPANONPASS +.It Cm ftp_proxy +Defaults to +.Ev $ftp_proxy . +.It Cm http_proxy +Defaults to +.Ev $http_proxy . +.It Cm https_proxy +Defaults to +.Ev $https_proxy . +.It Cm no_proxy +Defaults to +.Ev $no_proxy . +.It Cm pager +Defaults to +.Ev $PAGER . +.It Cm prompt +Defaults to +.Ev $FTPPROMPT . +.It Cm rprompt +Defaults to +.Ev $FTPRPROMPT . +.El +.It Ic site Ar arg1 arg2 ... +The arguments specified are sent, verbatim, to the remote +.Tn FTP +server as a +.Dv SITE +command. +.It Ic size Ar remote-file +Return size of +.Ar remote-file +on remote machine. +.It Ic sndbuf Ar size +Set the size of the socket send buffer to +.Ar size . +.It Ic status +Show the current status of +.Nm ftp . +.It Ic struct Ar struct-name +Set the file transfer +.Ar structure +to +.Ar struct-name . +The default (and only supported) +structure is +.Dq file . +.It Ic sunique +Toggle storing of files on remote machine under unique file names. +The remote +.Tn FTP +server must support +.Tn FTP +protocol +.Dv STOU +command for +successful completion. +The remote server will report unique name. +Default value is off. +.It Ic system +Show the type of operating system running on the remote machine. +.It Ic tenex +Set the file transfer type to that needed to +talk to +.Tn TENEX +machines. +.It Ic throttle +A synonym for +.Ic rate . +.It Ic trace +Toggle packet tracing. +.It Ic type Op Ar type-name +Set the file transfer +.Ic type +to +.Ar type-name . +If no type is specified, the current type +is printed. +The default type is network +.Tn ASCII . +.It Ic umask Op Ar newmask +Set the default umask on the remote server to +.Ar newmask . +If +.Ar newmask +is omitted, the current umask is printed. +.It Ic unset Ar option +Unset +.Ar option . +Refer to +.Ic set +for more information. +.It Ic usage Ar command +Print the usage message for +.Ar command . +.It Ic user Ar user-name Oo Ar password Oo Ar account Oc Oc +Identify yourself to the remote +.Tn FTP +server. +If the +.Ar password +is not specified and the server requires it, +.Nm +will prompt the user for it (after disabling local echo). +If an +.Ar account +field is not specified, and the +.Tn FTP +server +requires it, the user will be prompted for it. +If an +.Ar account +field is specified, an account command will +be relayed to the remote server after the login sequence +is completed if the remote server did not require it +for logging in. +Unless +.Nm +is invoked with +.Dq auto-login +disabled, this process is done automatically on initial connection to the +.Tn FTP +server. +.It Ic verbose +Toggle verbose mode. +In verbose mode, all responses from +the +.Tn FTP +server are displayed to the user. +In addition, +if verbose is on, when a file transfer completes, statistics +regarding the efficiency of the transfer are reported. +By default, +verbose is on. +.It Ic xferbuf Ar size +Set the size of the socket send and receive buffers to +.Ar size . +.It Ic \&? Op Ar command +A synonym for +.Ic help . +.El +.Pp +Command arguments which have embedded spaces may be quoted with +quote +.Sq \&" +marks. +.Pp +Commands which toggle settings can take an explicit +.Ic on +or +.Ic off +argument to force the setting appropriately. +.Pp +Commands which take a byte count as an argument +(e.g., +.Ic hash , +.Ic rate , +and +.Ic xferbuf ) +support an optional suffix on the argument which changes the +interpretation of the argument. +Supported suffixes are: +.Bl -tag -width 3n -offset indent -compact +.It Li b +Causes no modification. +(Optional) +.It Li k +Kilo; multiply the argument by 1024 +.It Li m +Mega; multiply the argument by 1048576 +.It Li g +Giga; multiply the argument by 1073741824 +.El +.Pp +If +.Nm +receives a +.Dv SIGINFO +(see the +.Dq status +argument of +.Xr stty 1 ) +or +.Dv SIGQUIT +signal whilst a transfer is in progress, the current transfer rate +statistics will be written to the standard error output, in the +same format as the standard completion message. +.Sh AUTO-FETCHING FILES +In addition to standard commands, this version of +.Nm +supports an auto-fetch feature. +To enable auto-fetch, simply pass the list of hostnames/files +on the command line. +.Pp +The following formats are valid syntax for an auto-fetch element: +.Bl -tag -width "FOO " +.\" [user@]host:[path][/] +.It Oo Ar user Ns Li \&@ Oc Ns Ar host Ns Li \&: Ns Oo Ar path Oc \ +Ns Oo Li / Oc +.Dq Classic +.Tn FTP +format. +.Pp +If +.Ar path +contains a glob character and globbing is enabled, +(see +.Ic glob ) , +then the equivalent of +.Ql mget path +is performed. +.Pp +If the directory component of +.Ar path +contains no globbing characters, +it is stored locally with the name basename (see +.Xr basename 1 ) +of +.Ic path , +in the current directory. +Otherwise, the full remote name is used as the local name, +relative to the local root directory. +.\" ftp://[user[:password]@]host[:port]/path[/][;type=X] +.It Li ftp:// Ns Oo Ar user Ns Oo Ns Li \&: Ns Ar password Oc Ns Li \&@ Oc \ +Ns Ar host Ns Oo Li \&: Ns Ar port Oc Ns Li / Ns Ar path Ns Oo Li / Oc \ +Ns Oo Li ;type= Ns Ar X Oc +An +.Tn FTP +URL, retrieved using the +.Tn FTP +protocol if +.Ic "set ftp_proxy" +isn't defined. +Otherwise, transfer the URL using +.Tn HTTP +via the proxy defined in +.Ic "set ftp_proxy" . +If +.Ic "set ftp_proxy" +isn't defined and +.Ar user +is given, login as +.Ar user . +In this case, use +.Ar password +if supplied, otherwise prompt the user for one. +.Pp +If a suffix of +.Sq ;type=A +or +.Sq ;type=I +is supplied, then the transfer type will take place as +ascii or binary (respectively). +The default transfer type is binary. +.Pp +In order to be compliant with +.Li RFC 3986 , +.Nm +interprets the +.Ar path +part of an +.Dq ftp:// +auto-fetch URL as follows: +.Bl -bullet +.It +The +.Sq Li / +immediately after the +.Ar host Ns Oo Li \&: Ns Ar port Oc +is interpreted as a separator before the +.Ar path , +and not as part of the +.Ar path +itself. +.It +The +.Ar path +is interpreted as a +.So Li / Sc Ns -separated +list of name components. +For all but the last such component, +.Nm +performs the equivalent of a +.Ic cd +command. +For the last path component, +.Nm +performs the equivalent of a +.Ic get +command. +.It +Empty name components, +which result from +.Sq Li // +within the +.Ar path , +or from an extra +.Sq Li / +at the beginning of the +.Ar path , +will cause the equivalent of a +.Ic cd +command without a directory name. +This is unlikely to be useful. +.It +Any +.Sq Li \&% Ns Ar XX +codes +(per +.Li RFC 3986 ) +within the path components are decoded, with +.Ar XX +representing a character code in hexadecimal. +This decoding takes place after the +.Ar path +has been split into components, +but before each component is used in the equivalent of a +.Ic cd +or +.Ic get +command. +Some often-used codes are +.Sq Li \&%2F +(which represents +.Sq Li / ) +and +.Sq Li \&%7E +(which represents +.Sq Li ~ ) . +.El +.Pp +The above interpretation has the following consequences: +.Bl -bullet +.It +The path is interpreted relative to the +default login directory of the specified user or of the +.Sq anonymous +user. +If the +.Pa / +directory is required, use a leading path of +.Dq %2F . +If a user's home directory is required (and the remote server supports +the syntax), use a leading path of +.Dq %7Euser/ . +For example, to retrieve +.Pa /etc/motd +from +.Sq localhost +as the user +.Sq myname +with the password +.Sq mypass , +use +.Dq ftp://myname:mypass@localhost/%2fetc/motd +.It +The exact +.Ic cd +and +.Ic get +commands can be controlled by careful choice of +where to use +.Sq / +and where to use +.Sq %2F +(or +.Sq %2f ) . +For example, the following URLs correspond to the +equivalents of the indicated commands: +.Bl -tag -width "ftp://host/%2Fdir1%2Fdir2%2Ffile" +.It ftp://host/dir1/dir2/file +.Dq "cd dir1" , +.Dq "cd dir2" , +.Dq "get file" . +.It ftp://host/%2Fdir1/dir2/file +.Dq "cd /dir1" , +.Dq "cd dir2" , +.Dq "get file" . +.It ftp://host/dir1%2Fdir2/file +.Dq "cd dir1/dir2" , +.Dq "get file" . +.It ftp://host/%2Fdir1%2Fdir2/file +.Dq "cd /dir1/dir2" , +.Dq "get file" . +.It ftp://host/dir1%2Fdir2%2Ffile +.Dq "get dir1/dir2/file" . +.It ftp://host/%2Fdir1%2Fdir2%2Ffile +.Dq "get /dir1/dir2/file" . +.El +.It +You must have appropriate access permission for each of the +intermediate directories that is used in the equivalent of a +.Ic cd +command. +.El +.\" http://[user[:password]@]host[:port]/path +.It Li http:// Ns Oo Ar user Ns Oo Li \&: Ns Ar password Oc Ns Li \&@ Oc \ +Ns Ar host Ns Oo Li \&: Ns Ar port Oc Ns Li / Ns Ar path +An +.Tn HTTP +URL, retrieved using the +.Tn HTTP +protocol. +If +.Ic "set http_proxy" +is defined, it is used as a URL to an +.Tn HTTP +proxy server. +If +.Tn HTTP +authorization is required to retrieve +.Ar path , +and +.Sq user +(and optionally +.Sq password ) +is in the URL, use them for the first attempt to authenticate. +.\" https://[user[:password]@]host[:port]/path +.It Li https:// Ns Oo Ar user Ns Oo Li \&: Ns Ar password Oc Ns Li \&@ Oc \ +Ns Ar host Ns Oo Li \&: Ns Ar port Oc Ns Li / Ns Ar path +An +.Tn HTTPS +URL, retrieved using the +.Tn HTTPS +protocol. +If +.Ic "set https_proxy" +is defined, it is used as a URL to an +.Tn HTTPS +proxy server. +If +.Tn HTTPS +authorization is required to retrieve +.Ar path , +and +.Sq user +(and optionally +.Sq password ) +is in the URL, use them for the first attempt to authenticate. +There is currently no certificate validation and verification. +.\" file:///path +.It Li file:/// Ns Ar path +A local URL, copied from +.Pa / Ns Ar path +on the local host. +.\" about: +.It Li about: Ns Ar topic +Display information regarding +.Ar topic ; +no file is retrieved for this auto-fetched element. +Supported values include: +.Bl -tag -width "about:version" +.It Li about:ftp +Information about +.Nm ftp . +.It Li about:version +The version of +.Nm ftp . +Useful to provide when reporting problems. +.El +.El +.Pp +Unless noted otherwise above, and +.Fl o Ar output +is not given, the file is stored in the current directory as the +.Xr basename 1 +of +.Ar path . +Note that if a +.Tn HTTP +redirect is received, the fetch is retried using the new target URL +supplied by the server, with a corresponding new +.Ar path . +Using an explicit +.Fl o Ar output +is recommended, to avoid writing to unexpected file names. +.Pp +If a classic format or an +.Tn FTP +URL format has a trailing +.Sq / +or an empty +.Ar path +component, then +.Nm +will connect to the site and +.Ic cd +to the directory given as the path, and leave the user in interactive +mode ready for further input. +This will not work if +.Ic "set ftp_proxy" +is being used. +.Pp +Direct +.Tn HTTP +transfers use HTTP 1.1. +Proxied +.Tn FTP +and +.Tn HTTP +transfers use HTTP 1.0. +.Pp +If +.Fl R +is given, all auto-fetches that don't go via the +.Tn FTP +or +.Tn HTTP +proxies will be restarted. +For +.Tn FTP , +this is implemented by using +.Nm reget +instead of +.Nm get . +For +.Tn HTTP , +this is implemented by using the +.Sq "Range: bytes=" +.Tn "HTTP/1.1" +directive. +.Pp +If WWW or proxy WWW authentication is required, you will be prompted +to enter a username and password to authenticate with. +.Pp +When specifying IPv6 numeric addresses in a URL, you need to +surround the address in square brackets. +E.g.: +.Dq ftp://[::1]:21/ . +This is because colons are used in IPv6 numeric address as well as +being the separator for the port number. +.Sh ABORTING A FILE TRANSFER +To abort a file transfer, use the terminal interrupt key +(usually Ctrl-C). +Sending transfers will be immediately halted. +Receiving transfers will be halted by sending an +.Tn FTP +protocol +.Dv ABOR +command to the remote server, and discarding any further data received. +The speed at which this is accomplished depends upon the remote +server's support for +.Dv ABOR +processing. +If the remote server does not support the +.Dv ABOR +command, the prompt will not appear until the remote server has completed +sending the requested file. +.Pp +If the terminal interrupt key sequence is used whilst +.Nm +is awaiting a reply from the remote server for the ABOR processing, +then the connection will be closed. +This is different from the traditional behaviour (which ignores the +terminal interrupt during this phase), but is considered more useful. +.Sh FILE NAMING CONVENTIONS +Files specified as arguments to +.Nm +commands are processed according to the following rules. +.Bl -enum +.It +If the file name +.Sq Fl +is specified, the +.Ar stdin +(for reading) or +.Ar stdout +(for writing) is used. +.It +If the first character of the file name is +.Sq \&| , +the +remainder of the argument is interpreted as a shell command. +.Nm +then forks a shell, using +.Xr popen 3 +with the argument supplied, and reads (writes) from the stdout +(stdin). +If the shell command includes spaces, the argument +must be quoted; e.g. +.Dq Qq Li \&| ls\ \-lt . +A particularly +useful example of this mechanism is: +.Dq Li dir \&"\&" \&|more . +.It +Failing the above checks, if +.Dq globbing +is enabled, local file names are expanded according to the rules +used in the +.Xr csh 1 ; +see the +.Ic glob +command. +If the +.Nm +command expects a single local file (e.g. +.Ic put ) , +only the first filename generated by the "globbing" operation is used. +.It +For +.Ic mget +commands and +.Ic get +commands with unspecified local file names, the local filename is +the remote filename, which may be altered by a +.Ic case , +.Ic ntrans , +or +.Ic nmap +setting. +The resulting filename may then be altered if +.Ic runique +is on. +.It +For +.Ic mput +commands and +.Ic put +commands with unspecified remote file names, the remote filename is +the local filename, which may be altered by a +.Ic ntrans +or +.Ic nmap +setting. +The resulting filename may then be altered by the remote server if +.Ic sunique +is on. +.El +.Sh FILE TRANSFER PARAMETERS +The +.Tn FTP +specification specifies many parameters which may affect a file transfer. +The +.Ic type +may be one of +.Dq ascii , +.Dq image +(binary), +.Dq ebcdic , +and +.Dq local byte size +(for +.Tn PDP Ns -10's +and +.Tn PDP Ns -20's +mostly). +.Nm +supports the ascii and image types of file transfer, +plus local byte size 8 for +.Ic tenex +mode transfers. +.Pp +.Nm +supports only the default values for the remaining +file transfer parameters: +.Ic mode , +.Ic form , +and +.Ic struct . +.Sh THE .netrc FILE +The +.Pa .netrc +file contains login and initialization information +used by the auto-login process. +It resides in the user's home directory, +unless overridden with the +.Fl N Ar netrc +option, or specified in the +.Ev NETRC +environment variable. +The following tokens are recognized; they may be separated by spaces, +tabs, or new-lines: +.Bl -tag -width password +.It Ic machine Ar name +Identify a remote machine +.Ar name . +The auto-login process searches the +.Pa .netrc +file for a +.Ic machine +token that matches the remote machine specified on the +.Nm +command line or as an +.Ic open +command argument. +Once a match is made, the subsequent +.Pa .netrc +tokens are processed, +stopping when the end of file is reached or another +.Ic machine +or a +.Ic default +token is encountered. +.It Ic default +This is the same as +.Ic machine +.Ar name +except that +.Ic default +matches any name. +There can be only one +.Ic default +token, and it must be after all +.Ic machine +tokens. +This is normally used as: +.Pp +.Dl default login anonymous password user@site +.Pp +thereby giving the user an automatic anonymous +.Tn FTP +login to +machines not specified in +.Pa .netrc . +This can be overridden +by using the +.Fl n +flag to disable auto-login. +.It Ic login Ar name +Identify a user on the remote machine. +If this token is present, the auto-login process will initiate +a login using the specified +.Ar name . +.It Ic password Ar string +Supply a password. +If this token is present, the auto-login process will supply the +specified string if the remote server requires a password as part +of the login process. +Note that if this token is present in the +.Pa .netrc +file for any user other +than +.Ar anonymous , +.Nm +will abort the auto-login process if the +.Pa .netrc +is readable by +anyone besides the user. +.It Ic account Ar string +Supply an additional account password. +If this token is present, the auto-login process will supply the +specified string if the remote server requires an additional +account password, or the auto-login process will initiate an +.Dv ACCT +command if it does not. +.It Ic macdef Ar name +Define a macro. +This token functions like the +.Nm +.Ic macdef +command functions. +A macro is defined with the specified name; its contents begin with the +next +.Pa .netrc +line and continue until a blank line (consecutive new-line +characters) is encountered. +Like the other tokens in the +.Pa .netrc +file, a +.Ic macdef +is applicable only to the +.Ic machine +definition preceding it. +A +.Ic macdef +entry cannot be used by multiple +.Ic machine +definitions; rather, it must be defined following each +.Ic machine +it is intended to be used with. +If a macro named +.Ic init +is defined, it is automatically executed as the last step in the +auto-login process. +For example, +.Bd -literal -offset indent +default +macdef init +epsv4 off +.Ed +.Pp +followed by a blank line. +.El +.Sh COMMAND LINE EDITING +.Nm +supports interactive command line editing, via the +.Xr editline 3 +library. +It is enabled with the +.Ic edit +command, and is enabled by default if input is from a tty. +Previous lines can be recalled and edited with the arrow keys, +and other GNU Emacs-style editing keys may be used as well. +.Pp +The +.Xr editline 3 +library is configured with a +.Pa .editrc +file - refer to +.Xr editrc 5 +for more information. +.Pp +An extra key binding is available to +.Nm +to provide context sensitive command and filename completion +(including remote file completion). +To use this, bind a key to the +.Xr editline 3 +command +.Ic ftp-complete . +By default, this is bound to the TAB key. +.Sh COMMAND LINE PROMPT +By default, +.Nm +displays a command line prompt of +.Dq "ftp\*[Gt] " +to the user. +This can be changed with the +.Ic "set prompt" +command. +.Pp +A prompt can be displayed on the right side of the screen (after the +command input) with the +.Ic "set rprompt" +command. +.Pp +The following formatting sequences are replaced by the given +information: +.Bl -tag -width "%% " -offset indent +.It Li \&%/ +The current remote working directory. +.\" %c[[0]n], %.[[0]n] +.It \&%c Ns Oo Oo Li 0 Oc Ns Ar n Oc , Ns Li \&%. Ns Oo Oo Li 0 Oc Ns Ar n Oc +The trailing component of the current remote working directory, or +.Em n +trailing components if a digit +.Em n +is given. +If +.Em n +begins with +.Sq 0 , +the number of skipped components precede the trailing component(s) in +the format +.\" ``/trailing'' +.Do +.Sm off +.Li / Li \*[Lt] Va number Li \*[Gt] +.Va trailing +.Sm on +.Dc +(for +.Sq \&%c ) +or +.\" ``...trailing'' +.Dq Li \&... Ns Va trailing +(for +.Sq \&%. ) . +.It Li \&%M +The remote host name. +.It Li \&%m +The remote host name, up to the first +.Sq \&. . +.It Li \&%n +The remote user name. +.It Li \&%% +A single +.Sq % . +.El +.Sh ENVIRONMENT +.Nm +uses the following environment variables. +.Bl -tag -width "FTPSERVERPORT" +.It Ev FTPANONPASS +Password to send in an anonymous +.Tn FTP +transfer. +Defaults to +.Dq Li `whoami`@ . +.It Ev FTPMODE +Overrides the default operation mode. +Support values are: +.Bl -tag -width "passive" +.It Cm active +active mode +.Tn FTP +only +.It Cm auto +automatic determination of passive or active (this is the default) +.It Cm gate +gate-ftp mode +.It Cm passive +passive mode +.Tn FTP +only +.El +.It Ev FTPPROMPT +Command-line prompt to use. +Defaults to +.Dq "ftp\*[Gt] " . +Refer to +.Sx COMMAND LINE PROMPT +for more information. +.It Ev FTPRPROMPT +Command-line right side prompt to use. +Defaults to +.Dq "" . +Refer to +.Sx COMMAND LINE PROMPT +for more information. +.It Ev FTPSERVER +Host to use as gate-ftp server when +.Ic gate +is enabled. +.It Ev FTPSERVERPORT +Port to use when connecting to gate-ftp server when +.Ic gate +is enabled. +Default is port returned by a +.Fn getservbyname +lookup of +.Dq ftpgate/tcp . +.It Ev FTPUSERAGENT +The value to send for the +.Tn HTTP +User-Agent +header. +.It Ev HOME +For default location of a +.Pa .netrc +file, if one exists. +.It Ev NETRC +An alternate location of the +.Pa .netrc +file. +.It Ev PAGER +Used by various commands to display files. +Defaults to +.Xr more 1 +if empty or not set. +.It Ev SHELL +For default shell. +.It Ev ftp_proxy +URL of +.Tn FTP +proxy to use when making +.Tn FTP +URL requests +(if not defined, use the standard +.Tn FTP +protocol). +.Pp +See +.Ev http_proxy +for further notes about proxy use. +.It Ev http_proxy +URL of +.Tn HTTP +proxy to use when making +.Tn HTTP +URL requests. +If proxy authentication is required and there is a username and +password in this URL, they will automatically be used in the first +attempt to authenticate to the proxy. +.Pp +If +.Dq unsafe +URL characters are required in the username or password +(for example +.Sq @ +or +.Sq / ) , +encode them with +.Li RFC 3986 +.Sq Li \&% Ns Ar XX +encoding. +.Pp +Note that the use of a username and password in +.Ev ftp_proxy +and +.Ev http_proxy +may be incompatible with other programs that use it +(such as +.Xr lynx 1 ) . +.Pp +.Em NOTE : +this is not used for interactive sessions, only for command-line +fetches. +.It Ev no_proxy +A space or comma separated list of hosts (or domains) for which +proxying is not to be used. +Each entry may have an optional trailing ":port", which restricts +the matching to connections to that port. +.El +.Sh EXTENDED PASSIVE MODE AND FIREWALLS +Some firewall configurations do not allow +.Nm +to use extended passive mode. +If you find that even a simple +.Ic ls +appears to hang after printing a message such as this: +.Pp +.Dl 229 Entering Extended Passive Mode (|||58551|) +.Pp +then you will need to disable extended passive mode with +.Ic epsv4 off . +See the above section +.Sx The .netrc File +for an example of how to make this automatic. +.Sh SEE ALSO +.Xr getservbyname 3 , +.Xr editrc 5 , +.Xr services 5 , +.Xr ftpd 8 +.Sh STANDARDS +.Nm +attempts to be compliant with: +.Bl -tag -offset indent -width 8n +.It Li RFC 959 +.Em File Transfer Protocol +.It Li RFC 1123 +.Em Requirements for Internet Hosts - Application and Support +.It Li RFC 1635 +.Em How to Use Anonymous FTP +.It Li RFC 2389 +.Em Feature negotiation mechanism for the File Transfer Protocol +.It Li RFC 2428 +.Em FTP Extensions for IPv6 and NATs +.It Li RFC 2616 +.Em Hypertext Transfer Protocol -- HTTP/1.1 +.It Li RFC 2822 +.Em Internet Message Format +.It Li RFC 3659 +.Em Extensions to FTP +.It Li RFC 3986 +.Em Uniform Resource Identifier (URI) +.El +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . +.Pp +Various features such as command line editing, context sensitive +command and file completion, dynamic progress bar, automatic +fetching of files and URLs, modification time preservation, +transfer rate throttling, configurable command line prompt, +and other enhancements over the standard +.Bx +.Nm +were implemented in +.Nx 1.3 +and later releases +by +.An Luke Mewburn +.Aq lukem@NetBSD.org . +.Pp +IPv6 support was added by the WIDE/KAME project +(but may not be present in all non-NetBSD versions of this program, depending +if the operating system supports IPv6 in a similar manner to KAME). +.Sh BUGS +Correct execution of many commands depends upon proper behavior +by the remote server. +.Pp +An error in the treatment of carriage returns +in the +.Bx 4.2 +ascii-mode transfer code +has been corrected. +This correction may result in incorrect transfers of binary files +to and from +.Bx 4.2 +servers using the ascii type. +Avoid this problem by using the binary image type. +.Pp +.Nm +assumes that all IPv4 mapped addresses +.Po +IPv6 addresses with a form like +.Li ::ffff:10.1.1.1 +.Pc +indicate IPv4 destinations which can be handled by +.Dv AF_INET +sockets. +However, in certain IPv6 network configurations, this assumption is not true. +In such an environment, IPv4 mapped addresses must be passed to +.Dv AF_INET6 +sockets directly. +For example, if your site uses a SIIT translator for IPv6-to-IPv4 translation, +.Nm +is unable to support your configuration. diff --git a/usr.bin/ftp/ftp.c b/usr.bin/ftp/ftp.c new file mode 100644 index 000000000..88bc905b2 --- /dev/null +++ b/usr.bin/ftp/ftp.c @@ -0,0 +1,2178 @@ +/* $NetBSD: ftp.c,v 1.164 2012/07/04 06:09:37 is Exp $ */ + +/*- + * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ftp.c 8.6 (Berkeley) 10/27/94"; +#else +__RCSID("$NetBSD: ftp.c,v 1.164 2012/07/04 06:09:37 is Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#ifndef __minix +#include +#include +#endif +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __minix +#include +#endif + +#include "ftp_var.h" + +volatile sig_atomic_t abrtflag; +volatile sig_atomic_t timeoutflag; + +sigjmp_buf ptabort; +int ptabflg; +int ptflag = 0; +char pasv[BUFSIZ]; /* passive port for proxy data connection */ + +static int empty(FILE *, FILE *, int); +__dead static void abort_squared(int); + +struct sockinet { + union sockunion { + struct sockaddr_in su_sin; +#ifdef INET6 + struct sockaddr_in6 su_sin6; +#endif + } si_su; +#if !defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) + int si_len; +#endif +}; + +#if !defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) +# define su_len si_len +#else +# define su_len si_su.su_sin.sin_len +#endif +#define su_family si_su.su_sin.sin_family +#define su_port si_su.su_sin.sin_port + +struct sockinet myctladdr, hisctladdr, data_addr; + +char * +hookup(const char *host, const char *port) +{ + int s = -1, error; + struct addrinfo hints, *res, *res0; + static char hostnamebuf[MAXHOSTNAMELEN]; + socklen_t len; +#ifndef __minix + int on = 1; +#endif + + memset((char *)&hisctladdr, 0, sizeof (hisctladdr)); + memset((char *)&myctladdr, 0, sizeof (myctladdr)); + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = 0; + error = getaddrinfo(host, port, &hints, &res0); + if (error) { + warnx("Can't lookup `%s:%s': %s", host, port, + (error == EAI_SYSTEM) ? strerror(errno) + : gai_strerror(error)); + code = -1; + return (0); + } + + if (res0->ai_canonname) + (void)strlcpy(hostnamebuf, res0->ai_canonname, + sizeof(hostnamebuf)); + else + (void)strlcpy(hostnamebuf, host, sizeof(hostnamebuf)); + hostname = hostnamebuf; + + for (res = res0; res; res = res->ai_next) { + char hname[NI_MAXHOST], sname[NI_MAXSERV]; + + ai_unmapped(res); + if (getnameinfo(res->ai_addr, res->ai_addrlen, + hname, sizeof(hname), sname, sizeof(sname), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + strlcpy(hname, "?", sizeof(hname)); + strlcpy(sname, "?", sizeof(sname)); + } + if (verbose && res0->ai_next) { + /* if we have multiple possibilities */ + fprintf(ttyout, "Trying %s:%s ...\n", hname, sname); + } + s = socket(res->ai_family, SOCK_STREAM, res->ai_protocol); + if (s < 0) { + warn("Can't create socket for connection to `%s:%s'", + hname, sname); + continue; + } + if (ftp_connect(s, res->ai_addr, res->ai_addrlen, + verbose || !res->ai_next) < 0) { + close(s); + s = -1; + continue; + } + + /* finally we got one */ + break; + } + if (s < 0) { + warnx("Can't connect to `%s:%s'", host, port); + code = -1; + freeaddrinfo(res0); + return 0; + } + memcpy(&hisctladdr.si_su, res->ai_addr, res->ai_addrlen); + hisctladdr.su_len = res->ai_addrlen; + freeaddrinfo(res0); + res0 = res = NULL; + + len = hisctladdr.su_len; + if (getsockname(s, (struct sockaddr *)&myctladdr.si_su, &len) == -1) { + warn("Can't determine my address of connection to `%s:%s'", + host, port); + code = -1; + goto bad; + } + myctladdr.su_len = len; + +#ifdef IPTOS_LOWDELAY + if (hisctladdr.su_family == AF_INET) { + int tos = IPTOS_LOWDELAY; + if (setsockopt(s, IPPROTO_IP, IP_TOS, + (void *)&tos, sizeof(tos)) == -1) { + DWARN("setsockopt %s (ignored)", + "IPTOS_LOWDELAY"); + } + } +#endif + cin = fdopen(s, "r"); + cout = fdopen(s, "w"); + if (cin == NULL || cout == NULL) { + warnx("Can't fdopen socket"); + if (cin) + (void)fclose(cin); + if (cout) + (void)fclose(cout); + code = -1; + goto bad; + } + if (verbose) + fprintf(ttyout, "Connected to %s.\n", hostname); + if (getreply(0) > 2) { /* read startup message from server */ + if (cin) + (void)fclose(cin); + if (cout) + (void)fclose(cout); + code = -1; + goto bad; + } + +#ifndef __minix + if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, + (void *)&on, sizeof(on)) == -1) { + DWARN("setsockopt %s (ignored)", "SO_OOBINLINE"); + } +#endif + + return (hostname); + bad: + (void)close(s); + return (NULL); +} + +void +cmdabort(int notused) +{ + int oerrno = errno; + + sigint_raised = 1; + alarmtimer(0); + if (fromatty) + write(fileno(ttyout), "\n", 1); + abrtflag++; + if (ptflag) + siglongjmp(ptabort, 1); + errno = oerrno; +} + +void +cmdtimeout(int notused) +{ + int oerrno = errno; + + alarmtimer(0); + if (fromatty) + write(fileno(ttyout), "\n", 1); + timeoutflag++; + if (ptflag) + siglongjmp(ptabort, 1); + errno = oerrno; +} + +/*VARARGS*/ +int +command(const char *fmt, ...) +{ + va_list ap; + int r; + sigfunc oldsigint; + +#ifndef NO_DEBUG + if (ftp_debug) { + fputs("---> ", ttyout); + va_start(ap, fmt); + if (strncmp("PASS ", fmt, 5) == 0) + fputs("PASS XXXX", ttyout); + else if (strncmp("ACCT ", fmt, 5) == 0) + fputs("ACCT XXXX", ttyout); + else + vfprintf(ttyout, fmt, ap); + va_end(ap); + putc('\n', ttyout); + } +#endif + if (cout == NULL) { + warnx("No control connection for command"); + code = -1; + return (0); + } + + abrtflag = 0; + + oldsigint = xsignal(SIGINT, cmdabort); + + va_start(ap, fmt); + vfprintf(cout, fmt, ap); + va_end(ap); + fputs("\r\n", cout); + (void)fflush(cout); + cpend = 1; + r = getreply(!strcmp(fmt, "QUIT")); + if (abrtflag && oldsigint != SIG_IGN) + (*oldsigint)(SIGINT); + (void)xsignal(SIGINT, oldsigint); + return (r); +} + +static const char *m421[] = { + "remote server timed out. Connection closed", + "user interrupt. Connection closed", + "remote server has closed connection", +}; + +int +getreply(int expecteof) +{ + char current_line[BUFSIZ]; /* last line of previous reply */ + int c, n, lineno; + int dig; + int originalcode = 0, continuation = 0; + sigfunc oldsigint, oldsigalrm; + int pflag = 0; + char *cp, *pt = pasv; + + abrtflag = 0; + timeoutflag = 0; + + oldsigint = xsignal(SIGINT, cmdabort); + oldsigalrm = xsignal(SIGALRM, cmdtimeout); + + for (lineno = 0 ;; lineno++) { + dig = n = code = 0; + cp = current_line; + while (alarmtimer(quit_time ? quit_time : 60), + ((c = getc(cin)) != '\n')) { + if (c == IAC) { /* handle telnet commands */ + switch (c = getc(cin)) { + case WILL: + case WONT: + c = getc(cin); + fprintf(cout, "%c%c%c", IAC, DONT, c); + (void)fflush(cout); + break; + case DO: + case DONT: + c = getc(cin); + fprintf(cout, "%c%c%c", IAC, WONT, c); + (void)fflush(cout); + break; + default: + break; + } + continue; + } + dig++; + if (c == EOF) { + /* + * these will get trashed by pswitch() + * in lostpeer() + */ + int reply_timeoutflag = timeoutflag; + int reply_abrtflag = abrtflag; + + alarmtimer(0); + if (expecteof && feof(cin)) { + (void)xsignal(SIGINT, oldsigint); + (void)xsignal(SIGALRM, oldsigalrm); + code = 221; + return (0); + } + cpend = 0; + lostpeer(0); + if (verbose) { + size_t midx; + if (reply_timeoutflag) + midx = 0; + else if (reply_abrtflag) + midx = 1; + else + midx = 2; + (void)fprintf(ttyout, + "421 Service not available, %s.\n", m421[midx]); + (void)fflush(ttyout); + } + code = 421; + (void)xsignal(SIGINT, oldsigint); + (void)xsignal(SIGALRM, oldsigalrm); + return (4); + } + if (c != '\r' && (verbose > 0 || + ((verbose > -1 && n == '5' && dig > 4) && + (((!n && c < '5') || (n && n < '5')) + || !retry_connect)))) { + if (proxflag && + (dig == 1 || (dig == 5 && verbose == 0))) + fprintf(ttyout, "%s:", hostname); + (void)putc(c, ttyout); + } + if (dig < 4 && isdigit(c)) + code = code * 10 + (c - '0'); + if (!pflag && (code == 227 || code == 228)) + pflag = 1; + else if (!pflag && code == 229) + pflag = 100; + if (dig > 4 && pflag == 1 && isdigit(c)) + pflag = 2; + if (pflag == 2) { + if (c != '\r' && c != ')') { + if (pt < &pasv[sizeof(pasv) - 1]) + *pt++ = c; + } else { + *pt = '\0'; + pflag = 3; + } + } + if (pflag == 100 && c == '(') + pflag = 2; + if (dig == 4 && c == '-') { + if (continuation) + code = 0; + continuation++; + } + if (n == 0) + n = c; + if (cp < ¤t_line[sizeof(current_line) - 1]) + *cp++ = c; + } + if (verbose > 0 || ((verbose > -1 && n == '5') && + (n < '5' || !retry_connect))) { + (void)putc(c, ttyout); + (void)fflush(ttyout); + } + if (cp[-1] == '\r') + cp[-1] = '\0'; + *cp = '\0'; + if (lineno == 0) + (void)strlcpy(reply_string, current_line, + sizeof(reply_string)); + if (lineno > 0 && code == 0 && reply_callback != NULL) + (*reply_callback)(current_line); + if (continuation && code != originalcode) { + if (originalcode == 0) + originalcode = code; + continue; + } + if (n != '1') + cpend = 0; + alarmtimer(0); + (void)xsignal(SIGINT, oldsigint); + (void)xsignal(SIGALRM, oldsigalrm); + if (code == 421 || originalcode == 421) + lostpeer(0); + if (abrtflag && oldsigint != cmdabort && oldsigint != SIG_IGN) + (*oldsigint)(SIGINT); + if (timeoutflag && oldsigalrm != cmdtimeout && + oldsigalrm != SIG_IGN) + (*oldsigalrm)(SIGINT); + return (n - '0'); + } +} + +static int +empty(FILE *ecin, FILE *din, int sec) +{ + int nr, nfd; + struct pollfd pfd[2]; + + nfd = 0; + if (ecin) { + pfd[nfd].fd = fileno(ecin); + pfd[nfd++].events = POLLIN; + } + + if (din) { + pfd[nfd].fd = fileno(din); + pfd[nfd++].events = POLLIN; + } + + if ((nr = ftp_poll(pfd, nfd, sec * 1000)) <= 0) + return nr; + + nr = 0; + nfd = 0; + if (ecin) + nr |= (pfd[nfd++].revents & POLLIN) ? 1 : 0; + if (din) + nr |= (pfd[nfd++].revents & POLLIN) ? 2 : 0; + return nr; +} + +sigjmp_buf xferabort; + +__dead static void +abortxfer(int notused) +{ + char msgbuf[100]; + size_t len; + + sigint_raised = 1; + alarmtimer(0); + mflag = 0; + abrtflag = 0; + switch (direction[0]) { + case 'r': + strlcpy(msgbuf, "\nreceive", sizeof(msgbuf)); + break; + case 's': + strlcpy(msgbuf, "\nsend", sizeof(msgbuf)); + break; + default: + errx(1, "abortxfer: unknown direction `%s'", direction); + } + len = strlcat(msgbuf, " aborted. Waiting for remote to finish abort.\n", + sizeof(msgbuf)); + write(fileno(ttyout), msgbuf, len); + siglongjmp(xferabort, 1); +} + +/* + * Read data from infd & write to outfd, using buf/bufsize as the temporary + * buffer, dealing with short writes. + * If rate_limit != 0, rate-limit the transfer. + * If hash_interval != 0, fputc('c', ttyout) every hash_interval bytes. + * Updates global variables: bytes. + * Returns 0 if ok, 1 if there was a read error, 2 if there was a write error. + * In the case of error, errno contains the appropriate error code. + */ +static int +copy_bytes(int infd, int outfd, char *buf, size_t bufsize, + int rate_limit, int hash_interval) +{ + volatile off_t hashc; + ssize_t inc, outc; + char *bufp; + struct timeval tvthen, tvnow, tvdiff; + off_t bufrem, bufchunk; + int serr; + + hashc = hash_interval; + if (rate_limit) + bufchunk = rate_limit; + else + bufchunk = bufsize; + + while (1) { + if (rate_limit) { + (void)gettimeofday(&tvthen, NULL); + } + errno = 0; + inc = outc = 0; + /* copy bufchunk at a time */ + bufrem = bufchunk; + while (bufrem > 0) { + inc = read(infd, buf, MIN((off_t)bufsize, bufrem)); + if (inc <= 0) + goto copy_done; + bytes += inc; + bufrem -= inc; + bufp = buf; + while (inc > 0) { + outc = write(outfd, bufp, inc); + if (outc < 0) + goto copy_done; + inc -= outc; + bufp += outc; + } + if (hash_interval) { + while (bytes >= hashc) { + (void)putc('#', ttyout); + hashc += hash_interval; + } + (void)fflush(ttyout); + } + } + if (rate_limit) { /* rate limited; wait if necessary */ + while (1) { + (void)gettimeofday(&tvnow, NULL); + timersub(&tvnow, &tvthen, &tvdiff); + if (tvdiff.tv_sec > 0) + break; + usleep(1000000 - tvdiff.tv_usec); + } + } + } + + copy_done: + serr = errno; + if (hash_interval && bytes > 0) { + if (bytes < hash_interval) + (void)putc('#', ttyout); + (void)putc('\n', ttyout); + (void)fflush(ttyout); + } + errno = serr; + if (inc == -1) + return 1; + if (outc == -1) + return 2; + + return 0; +} + +void +sendrequest(const char *cmd, const char *local, const char *remote, + int printnames) +{ + struct stat st; + int c; + FILE *volatile fin; + FILE *volatile dout; + int (*volatile closefunc)(FILE *); + sigfunc volatile oldintr; + sigfunc volatile oldintp; + off_t volatile hashbytes; + int hash_interval; + const char *lmode; + static size_t bufsize; + static char *buf; + int oprogress; + + hashbytes = mark; + direction = "sent"; + dout = NULL; + bytes = 0; + filesize = -1; + oprogress = progress; + if (verbose && printnames) { + if (*local != '-') + fprintf(ttyout, "local: %s ", local); + if (remote) + fprintf(ttyout, "remote: %s\n", remote); + } + if (proxy) { + proxtrans(cmd, local, remote); + return; + } + if (curtype != type) + changetype(type, 0); + closefunc = NULL; + oldintr = NULL; + oldintp = NULL; + lmode = "w"; + if (sigsetjmp(xferabort, 1)) { + while (cpend) + (void)getreply(0); + code = -1; + goto cleanupsend; + } + (void)xsignal(SIGQUIT, psummary); + oldintr = xsignal(SIGINT, abortxfer); + if (strcmp(local, "-") == 0) { + fin = stdin; + progress = 0; + } else if (*local == '|') { + oldintp = xsignal(SIGPIPE, SIG_IGN); + fin = popen(local + 1, "r"); + if (fin == NULL) { + warn("Can't execute `%s'", local + 1); + code = -1; + goto cleanupsend; + } + progress = 0; + closefunc = pclose; + } else { + fin = fopen(local, "r"); + if (fin == NULL) { + warn("Can't open `%s'", local); + code = -1; + goto cleanupsend; + } + closefunc = fclose; + if (fstat(fileno(fin), &st) < 0 || !S_ISREG(st.st_mode)) { + fprintf(ttyout, "%s: not a plain file.\n", local); + code = -1; + goto cleanupsend; + } + filesize = st.st_size; + } + if (initconn()) { + code = -1; + goto cleanupsend; + } + if (sigsetjmp(xferabort, 1)) + goto abort; + + if (restart_point && + (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) { + int rc; + + rc = -1; + switch (curtype) { + case TYPE_A: + rc = fseeko(fin, restart_point, SEEK_SET); + break; + case TYPE_I: + case TYPE_L: + rc = lseek(fileno(fin), restart_point, SEEK_SET); + break; + } + if (rc < 0) { + warn("Can't seek to restart `%s'", local); + goto cleanupsend; + } + if (command("REST " LLF, (LLT)restart_point) != CONTINUE) + goto cleanupsend; + lmode = "r+"; + } + if (remote) { + if (command("%s %s", cmd, remote) != PRELIM) + goto cleanupsend; + } else { + if (command("%s", cmd) != PRELIM) + goto cleanupsend; + } + dirchange = 1; + dout = dataconn(lmode); + if (dout == NULL) + goto abort; + + assert(sndbuf_size > 0); + if ((size_t)sndbuf_size > bufsize) { + if (buf) + (void)free(buf); + bufsize = sndbuf_size; + buf = ftp_malloc(bufsize); + } + + progressmeter(-1); + oldintp = xsignal(SIGPIPE, SIG_IGN); + hash_interval = (hash && (!progress || filesize < 0)) ? mark : 0; + + switch (curtype) { + + case TYPE_I: + case TYPE_L: + c = copy_bytes(fileno(fin), fileno(dout), buf, bufsize, + rate_put, hash_interval); + if (c == 1) { + warn("Reading `%s'", local); + } else if (c == 2) { + if (errno != EPIPE) + warn("Writing to network"); + bytes = -1; + } + break; + + case TYPE_A: + while ((c = getc(fin)) != EOF) { + if (c == '\n') { + while (hash_interval && bytes >= hashbytes) { + (void)putc('#', ttyout); + (void)fflush(ttyout); + hashbytes += mark; + } + if (ferror(dout)) + break; + (void)putc('\r', dout); + bytes++; + } + (void)putc(c, dout); + bytes++; +#if 0 /* this violates RFC 959 */ + if (c == '\r') { + (void)putc('\0', dout); + bytes++; + } +#endif + } + if (hash_interval) { + if (bytes < hashbytes) + (void)putc('#', ttyout); + (void)putc('\n', ttyout); + } + if (ferror(fin)) + warn("Reading `%s'", local); + if (ferror(dout)) { + if (errno != EPIPE) + warn("Writing to network"); + bytes = -1; + } + break; + } + + progressmeter(1); + if (closefunc != NULL) { + (*closefunc)(fin); + fin = NULL; + } + (void)fclose(dout); + dout = NULL; + (void)getreply(0); + if (bytes > 0) + ptransfer(0); + goto cleanupsend; + + abort: + (void)xsignal(SIGINT, oldintr); + oldintr = NULL; + if (!cpend) { + code = -1; + goto cleanupsend; + } + if (data >= 0) { + (void)close(data); + data = -1; + } + if (dout) { + (void)fclose(dout); + dout = NULL; + } + (void)getreply(0); + code = -1; + if (bytes > 0) + ptransfer(0); + + cleanupsend: + if (oldintr) + (void)xsignal(SIGINT, oldintr); + if (oldintp) + (void)xsignal(SIGPIPE, oldintp); + if (data >= 0) { + (void)close(data); + data = -1; + } + if (closefunc != NULL && fin != NULL) + (*closefunc)(fin); + if (dout) + (void)fclose(dout); + progress = oprogress; + restart_point = 0; + bytes = 0; +} + +void +recvrequest(const char *cmd, const char *volatile local, const char *remote, + const char *lmode, int printnames, int ignorespecial) +{ + FILE *volatile fout; + FILE *volatile din; + int (*volatile closefunc)(FILE *); + sigfunc volatile oldintr; + sigfunc volatile oldintp; + int c, d; + int volatile is_retr; + int volatile tcrflag; + int volatile bare_lfs; + static size_t bufsize; + static char *buf; + off_t volatile hashbytes; + int hash_interval; + struct stat st; + time_t mtime; + struct timeval tval[2]; + int oprogress; + int opreserve; + +#ifdef __minix + struct utimbuf utb; +#endif + + fout = NULL; + din = NULL; + hashbytes = mark; + direction = "received"; + bytes = 0; + bare_lfs = 0; + filesize = -1; + oprogress = progress; + opreserve = preserve; + is_retr = (strcmp(cmd, "RETR") == 0); + if (is_retr && verbose && printnames) { + if (ignorespecial || *local != '-') + fprintf(ttyout, "local: %s ", local); + if (remote) + fprintf(ttyout, "remote: %s\n", remote); + } + if (proxy && is_retr) { + proxtrans(cmd, local, remote); + return; + } + closefunc = NULL; + oldintr = NULL; + oldintp = NULL; + tcrflag = !crflag && is_retr; + if (sigsetjmp(xferabort, 1)) { + while (cpend) + (void)getreply(0); + code = -1; + goto cleanuprecv; + } + (void)xsignal(SIGQUIT, psummary); + oldintr = xsignal(SIGINT, abortxfer); + if (ignorespecial || (strcmp(local, "-") && *local != '|')) { + if (access(local, W_OK) < 0) { + char *dir = strrchr(local, '/'); + + if (errno != ENOENT && errno != EACCES) { + warn("Can't access `%s'", local); + code = -1; + goto cleanuprecv; + } + if (dir != NULL) + *dir = 0; + d = access(dir == local ? "/" : + dir ? local : ".", W_OK); + if (dir != NULL) + *dir = '/'; + if (d < 0) { + warn("Can't access `%s'", local); + code = -1; + goto cleanuprecv; + } + if (!runique && errno == EACCES && + chmod(local, (S_IRUSR|S_IWUSR)) < 0) { + warn("Can't chmod `%s'", local); + code = -1; + goto cleanuprecv; + } + if (runique && errno == EACCES && + (local = gunique(local)) == NULL) { + code = -1; + goto cleanuprecv; + } + } + else if (runique && (local = gunique(local)) == NULL) { + code = -1; + goto cleanuprecv; + } + } + if (!is_retr) { + if (curtype != TYPE_A) + changetype(TYPE_A, 0); + } else { + if (curtype != type) + changetype(type, 0); + filesize = remotesize(remote, 0); + if (code == 421 || code == -1) + goto cleanuprecv; + } + if (initconn()) { + code = -1; + goto cleanuprecv; + } + if (sigsetjmp(xferabort, 1)) + goto abort; + if (is_retr && restart_point && + command("REST " LLF, (LLT) restart_point) != CONTINUE) + goto cleanuprecv; + if (! EMPTYSTRING(remote)) { + if (command("%s %s", cmd, remote) != PRELIM) + goto cleanuprecv; + } else { + if (command("%s", cmd) != PRELIM) + goto cleanuprecv; + } + din = dataconn("r"); + if (din == NULL) + goto abort; + if (!ignorespecial && strcmp(local, "-") == 0) { + fout = stdout; + progress = 0; + preserve = 0; + } else if (!ignorespecial && *local == '|') { + oldintp = xsignal(SIGPIPE, SIG_IGN); + fout = popen(local + 1, "w"); + if (fout == NULL) { + warn("Can't execute `%s'", local+1); + goto abort; + } + progress = 0; + preserve = 0; + closefunc = pclose; + } else { + fout = fopen(local, lmode); + if (fout == NULL) { + warn("Can't open `%s'", local); + goto abort; + } + closefunc = fclose; + } + + if (fstat(fileno(fout), &st) != -1 && !S_ISREG(st.st_mode)) { + progress = 0; + preserve = 0; + } + assert(rcvbuf_size > 0); + if ((size_t)rcvbuf_size > bufsize) { + if (buf) + (void)free(buf); + bufsize = rcvbuf_size; + buf = ftp_malloc(bufsize); + } + + progressmeter(-1); + hash_interval = (hash && (!progress || filesize < 0)) ? mark : 0; + + switch (curtype) { + + case TYPE_I: + case TYPE_L: + if (is_retr && restart_point && + lseek(fileno(fout), restart_point, SEEK_SET) < 0) { + warn("Can't seek to restart `%s'", local); + goto cleanuprecv; + } + c = copy_bytes(fileno(din), fileno(fout), buf, bufsize, + rate_get, hash_interval); + if (c == 1) { + if (errno != EPIPE) + warn("Reading from network"); + bytes = -1; + } else if (c == 2) { + warn("Writing `%s'", local); + } + break; + + case TYPE_A: + if (is_retr && restart_point) { + int ch; + off_t i; + + if (fseeko(fout, (off_t)0, SEEK_SET) < 0) + goto done; + for (i = 0; i++ < restart_point;) { + if ((ch = getc(fout)) == EOF) + goto done; + if (ch == '\n') + i++; + } + if (fseeko(fout, (off_t)0, SEEK_CUR) < 0) { + done: + warn("Can't seek to restart `%s'", local); + goto cleanuprecv; + } + } + while ((c = getc(din)) != EOF) { + if (c == '\n') + bare_lfs++; + while (c == '\r') { + while (hash_interval && bytes >= hashbytes) { + (void)putc('#', ttyout); + (void)fflush(ttyout); + hashbytes += mark; + } + bytes++; + if ((c = getc(din)) != '\n' || tcrflag) { + if (ferror(fout)) + goto break2; + (void)putc('\r', fout); + if (c == '\0') { + bytes++; + goto contin2; + } + if (c == EOF) + goto contin2; + } + } + (void)putc(c, fout); + bytes++; + contin2: ; + } + break2: + if (hash_interval) { + if (bytes < hashbytes) + (void)putc('#', ttyout); + (void)putc('\n', ttyout); + } + if (ferror(din)) { + if (errno != EPIPE) + warn("Reading from network"); + bytes = -1; + } + if (ferror(fout)) + warn("Writing `%s'", local); + break; + } + + progressmeter(1); + if (closefunc != NULL) { + (*closefunc)(fout); + fout = NULL; + } + (void)fclose(din); + din = NULL; + (void)getreply(0); + if (bare_lfs) { + fprintf(ttyout, + "WARNING! %d bare linefeeds received in ASCII mode.\n", + bare_lfs); + fputs("File may not have transferred correctly.\n", ttyout); + } + if (bytes >= 0 && is_retr) { + if (bytes > 0) + ptransfer(0); + if (preserve && (closefunc == fclose)) { + mtime = remotemodtime(remote, 0); + if (mtime != -1) { + (void)gettimeofday(&tval[0], NULL); +#ifdef __minix + utb.actime = tval[0].tv_sec; + utb.modtime = mtime; + if (utime(local, &utb) == -1) { + fprintf(ttyout, + "Can't change modification time on %s to %s", + local, + rfc2822time(localtime(&mtime))); + } +#else + tval[1].tv_sec = mtime; + tval[1].tv_usec = 0; + if (utimes(local, tval) == -1) { + fprintf(ttyout, + "Can't change modification time on %s to %s", + local, + rfc2822time(localtime(&mtime))); + } +#endif + } + } + } + goto cleanuprecv; + + abort: + /* + * abort using RFC 959 recommended IP,SYNC sequence + */ + if (! sigsetjmp(xferabort, 1)) { + /* this is the first call */ + (void)xsignal(SIGINT, abort_squared); + if (!cpend) { + code = -1; + goto cleanuprecv; + } + abort_remote(din); + } + code = -1; + if (bytes > 0) + ptransfer(0); + + cleanuprecv: + if (oldintr) + (void)xsignal(SIGINT, oldintr); + if (oldintp) + (void)xsignal(SIGPIPE, oldintp); + if (data >= 0) { + (void)close(data); + data = -1; + } + if (closefunc != NULL && fout != NULL) + (*closefunc)(fout); + if (din) + (void)fclose(din); + progress = oprogress; + preserve = opreserve; + bytes = 0; +} + +/* + * Need to start a listen on the data channel before we send the command, + * otherwise the server's connect may fail. + */ +int +initconn(void) +{ + char *p, *a; + int result, tmpno = 0; + int on = 1; + int error; + unsigned int addr[16], port[2]; + unsigned int af, hal, pal; + socklen_t len; + const char *pasvcmd = NULL; + int overbose; + +#ifdef INET6 +#ifndef NO_DEBUG + if (myctladdr.su_family == AF_INET6 && ftp_debug && + (IN6_IS_ADDR_LINKLOCAL(&myctladdr.si_su.su_sin6.sin6_addr) || + IN6_IS_ADDR_SITELOCAL(&myctladdr.si_su.su_sin6.sin6_addr))) { + warnx("Use of scoped addresses can be troublesome"); + } +#endif +#endif + + reinit: + if (passivemode) { + data_addr = myctladdr; + data = socket(data_addr.su_family, SOCK_STREAM, 0); + if (data < 0) { + warn("Can't create socket for data connection"); + return (1); + } + if ((options & SO_DEBUG) && + setsockopt(data, SOL_SOCKET, SO_DEBUG, + (void *)&on, sizeof(on)) == -1) { + DWARN("setsockopt %s (ignored)", "SO_DEBUG"); + } + result = COMPLETE + 1; + switch (data_addr.su_family) { + case AF_INET: + if (epsv4 && !epsv4bad) { + pasvcmd = "EPSV"; + overbose = verbose; + if (ftp_debug == 0) + verbose = -1; + result = command("EPSV"); + verbose = overbose; + if (verbose > 0 && + (result == COMPLETE || !connected)) + fprintf(ttyout, "%s\n", reply_string); + if (!connected) + return (1); + /* + * this code is to be friendly with broken + * BSDI ftpd + */ + if (code / 10 == 22 && code != 229) { + fputs( +"wrong server: return code must be 229\n", + ttyout); + result = COMPLETE + 1; + } + if (result != COMPLETE) { + epsv4bad = 1; + DPRINTF("disabling epsv4 for this " + "connection\n"); + } + } + if (result != COMPLETE) { + pasvcmd = "PASV"; + result = command("PASV"); + if (!connected) + return (1); + } + break; +#ifdef INET6 + case AF_INET6: + if (epsv6 && !epsv6bad) { + pasvcmd = "EPSV"; + overbose = verbose; + if (ftp_debug == 0) + verbose = -1; + result = command("EPSV"); + verbose = overbose; + if (verbose > 0 && + (result == COMPLETE || !connected)) + fprintf(ttyout, "%s\n", reply_string); + if (!connected) + return (1); + /* + * this code is to be friendly with + * broken BSDI ftpd + */ + if (code / 10 == 22 && code != 229) { + fputs( + "wrong server: return code must be 229\n", + ttyout); + result = COMPLETE + 1; + } + if (result != COMPLETE) { + epsv6bad = 1; + DPRINTF("disabling epsv6 for this " + "connection\n"); + } + } + if (result != COMPLETE) { + pasvcmd = "LPSV"; + result = command("LPSV"); + } + if (!connected) + return (1); + break; +#endif + default: + result = COMPLETE + 1; + break; + } + if (result != COMPLETE) { + if (activefallback) { + (void)close(data); + data = -1; + passivemode = 0; +#if 0 + activefallback = 0; +#endif + goto reinit; + } + fputs("Passive mode refused.\n", ttyout); + goto bad; + } + +#define pack2(var, off) \ + (((var[(off) + 0] & 0xff) << 8) | ((var[(off) + 1] & 0xff) << 0)) +#define pack4(var, off) \ + (((var[(off) + 0] & 0xff) << 24) | ((var[(off) + 1] & 0xff) << 16) | \ + ((var[(off) + 2] & 0xff) << 8) | ((var[(off) + 3] & 0xff) << 0)) +#define UC(b) (((int)b)&0xff) + + /* + * What we've got at this point is a string of comma separated + * one-byte unsigned integer values, separated by commas. + */ + if (strcmp(pasvcmd, "PASV") == 0) { + if (data_addr.su_family != AF_INET) { + fputs( + "Passive mode AF mismatch. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + if (code / 10 == 22 && code != 227) { + fputs("wrong server: return code must be 227\n", + ttyout); + error = 1; + goto bad; + } + error = sscanf(pasv, "%u,%u,%u,%u,%u,%u", + &addr[0], &addr[1], &addr[2], &addr[3], + &port[0], &port[1]); + if (error != 6) { + fputs( +"Passive mode address scan failure. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + error = 0; + memset(&data_addr, 0, sizeof(data_addr)); + data_addr.su_family = AF_INET; + data_addr.su_len = sizeof(struct sockaddr_in); + data_addr.si_su.su_sin.sin_addr.s_addr = + htonl(pack4(addr, 0)); + data_addr.su_port = htons(pack2(port, 0)); + } else if (strcmp(pasvcmd, "LPSV") == 0) { + if (code / 10 == 22 && code != 228) { + fputs("wrong server: return code must be 228\n", + ttyout); + error = 1; + goto bad; + } + switch (data_addr.su_family) { + case AF_INET: + error = sscanf(pasv, +"%u,%u,%u,%u,%u,%u,%u,%u,%u", + &af, &hal, + &addr[0], &addr[1], &addr[2], &addr[3], + &pal, &port[0], &port[1]); + if (error != 9) { + fputs( +"Passive mode address scan failure. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + if (af != 4 || hal != 4 || pal != 2) { + fputs( +"Passive mode AF mismatch. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + + error = 0; + memset(&data_addr, 0, sizeof(data_addr)); + data_addr.su_family = AF_INET; + data_addr.su_len = sizeof(struct sockaddr_in); + data_addr.si_su.su_sin.sin_addr.s_addr = + htonl(pack4(addr, 0)); + data_addr.su_port = htons(pack2(port, 0)); + break; +#ifdef INET6 + case AF_INET6: + error = sscanf(pasv, +"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u", + &af, &hal, + &addr[0], &addr[1], &addr[2], &addr[3], + &addr[4], &addr[5], &addr[6], &addr[7], + &addr[8], &addr[9], &addr[10], + &addr[11], &addr[12], &addr[13], + &addr[14], &addr[15], + &pal, &port[0], &port[1]); + if (error != 21) { + fputs( +"Passive mode address scan failure. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + if (af != 6 || hal != 16 || pal != 2) { + fputs( +"Passive mode AF mismatch. Shouldn't happen!\n", ttyout); + error = 1; + goto bad; + } + + error = 0; + memset(&data_addr, 0, sizeof(data_addr)); + data_addr.su_family = AF_INET6; + data_addr.su_len = sizeof(struct sockaddr_in6); + { + size_t i; + for (i = 0; i < sizeof(struct in6_addr); i++) { + data_addr.si_su.su_sin6.sin6_addr.s6_addr[i] = + UC(addr[i]); + } + } + data_addr.su_port = htons(pack2(port, 0)); + break; +#endif + default: + error = 1; + } + } else if (strcmp(pasvcmd, "EPSV") == 0) { + char delim[4]; + + port[0] = 0; + if (code / 10 == 22 && code != 229) { + fputs("wrong server: return code must be 229\n", + ttyout); + error = 1; + goto bad; + } + if (sscanf(pasv, "%c%c%c%d%c", &delim[0], + &delim[1], &delim[2], &port[1], + &delim[3]) != 5) { + fputs("parse error!\n", ttyout); + error = 1; + goto bad; + } + if (delim[0] != delim[1] || delim[0] != delim[2] + || delim[0] != delim[3]) { + fputs("parse error!\n", ttyout); + error = 1; + goto bad; + } + data_addr = hisctladdr; + data_addr.su_port = htons(port[1]); + } else + goto bad; + + if (ftp_connect(data, (struct sockaddr *)&data_addr.si_su, + data_addr.su_len, 1) < 0) { + if (activefallback) { + (void)close(data); + data = -1; + passivemode = 0; +#if 0 + activefallback = 0; +#endif + goto reinit; + } + goto bad; + } +#ifdef IPTOS_THROUGHPUT + if (data_addr.su_family == AF_INET) { + on = IPTOS_THROUGHPUT; + if (setsockopt(data, IPPROTO_IP, IP_TOS, + (void *)&on, sizeof(on)) == -1) { + DWARN("setsockopt %s (ignored)", + "IPTOS_THROUGHPUT"); + } + } +#endif + return (0); + } + + noport: + data_addr = myctladdr; + if (sendport) + data_addr.su_port = 0; /* let system pick one */ + if (data != -1) + (void)close(data); + data = socket(data_addr.su_family, SOCK_STREAM, 0); + if (data < 0) { + warn("Can't create socket for data connection"); + if (tmpno) + sendport = 1; + return (1); + } + if (!sendport) + if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, + (void *)&on, sizeof(on)) == -1) { + warn("Can't set SO_REUSEADDR on data connection"); + goto bad; + } + if (bind(data, (struct sockaddr *)&data_addr.si_su, + data_addr.su_len) < 0) { + warn("Can't bind for data connection"); + goto bad; + } + if ((options & SO_DEBUG) && + setsockopt(data, SOL_SOCKET, SO_DEBUG, + (void *)&on, sizeof(on)) == -1) { + DWARN("setsockopt %s (ignored)", "SO_DEBUG"); + } + len = sizeof(data_addr.si_su); + memset((char *)&data_addr, 0, sizeof (data_addr)); + if (getsockname(data, (struct sockaddr *)&data_addr.si_su, &len) == -1) { + warn("Can't determine my address of data connection"); + goto bad; + } + data_addr.su_len = len; + if (ftp_listen(data, 1) < 0) + warn("Can't listen to data connection"); + + if (sendport) { + char hname[NI_MAXHOST], sname[NI_MAXSERV]; + struct sockinet tmp; + + switch (data_addr.su_family) { + case AF_INET: + if (!epsv4 || epsv4bad) { + result = COMPLETE + 1; + break; + } + /* FALLTHROUGH */ +#ifdef INET6 + case AF_INET6: + if (!epsv6 || epsv6bad) { + result = COMPLETE + 1; + break; + } +#endif + af = (data_addr.su_family == AF_INET) ? 1 : 2; + tmp = data_addr; +#ifdef INET6 + if (tmp.su_family == AF_INET6) + tmp.si_su.su_sin6.sin6_scope_id = 0; +#endif + if (getnameinfo((struct sockaddr *)&tmp.si_su, + tmp.su_len, hname, sizeof(hname), sname, + sizeof(sname), NI_NUMERICHOST | NI_NUMERICSERV)) { + result = ERROR; + } else { + overbose = verbose; + if (ftp_debug == 0) + verbose = -1; + result = command("EPRT |%u|%s|%s|", af, hname, + sname); + verbose = overbose; + if (verbose > 0 && + (result == COMPLETE || !connected)) + fprintf(ttyout, "%s\n", reply_string); + if (!connected) + return (1); + if (result != COMPLETE) { + epsv4bad = 1; + DPRINTF("disabling epsv4 for this " + "connection\n"); + } + } + break; + default: + result = COMPLETE + 1; + break; + } + if (result == COMPLETE) + goto skip_port; + + switch (data_addr.su_family) { + case AF_INET: + a = (char *)&data_addr.si_su.su_sin.sin_addr; + p = (char *)&data_addr.su_port; + result = command("PORT %d,%d,%d,%d,%d,%d", + UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), + UC(p[0]), UC(p[1])); + break; +#ifdef INET6 + case AF_INET6: { + uint8_t ua[sizeof(data_addr.si_su.su_sin6.sin6_addr)]; + uint8_t up[sizeof(data_addr.su_port)]; + + memcpy(ua, &data_addr.si_su.su_sin6.sin6_addr, + sizeof(ua)); + memcpy(up, &data_addr.su_port, sizeof(up)); + + result = command( + "LPRT %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + 6, 16, + ua[0], ua[1], ua[2], ua[3], + ua[4], ua[5], ua[6], ua[7], + ua[8], ua[9], ua[10], ua[11], + ua[12], ua[13], ua[14], ua[15], + 2, + up[0], up[1]); + break; + } +#endif + default: + result = COMPLETE + 1; /* xxx */ + } + if (!connected) + return (1); + skip_port: + + if (result == ERROR && sendport == -1) { + sendport = 0; + tmpno = 1; + goto noport; + } + return (result != COMPLETE); + } + if (tmpno) + sendport = 1; +#ifdef IPTOS_THROUGHPUT + if (data_addr.su_family == AF_INET) { + on = IPTOS_THROUGHPUT; + if (setsockopt(data, IPPROTO_IP, IP_TOS, + (void *)&on, sizeof(on)) == -1) { + DWARN("setsockopt %s (ignored)", "IPTOS_THROUGHPUT"); + } + } +#endif + return (0); + bad: + (void)close(data); + data = -1; + if (tmpno) + sendport = 1; + return (1); +} + +FILE * +dataconn(const char *lmode) +{ + struct sockinet from; + int s, flags, rv, timeout; + struct timeval endtime, now, td; + struct pollfd pfd[1]; + socklen_t fromlen; + + if (passivemode) /* passive data connection */ + return (fdopen(data, lmode)); + + /* active mode data connection */ + + if ((flags = fcntl(data, F_GETFL, 0)) == -1) + goto dataconn_failed; /* get current socket flags */ + if (fcntl(data, F_SETFL, flags | O_NONBLOCK) == -1) + goto dataconn_failed; /* set non-blocking connect */ + + /* NOTE: we now must restore socket flags on successful exit */ + + /* limit time waiting on listening socket */ + pfd[0].fd = data; + pfd[0].events = POLLIN; + (void)gettimeofday(&endtime, NULL); /* determine end time */ + endtime.tv_sec += (quit_time > 0) ? quit_time: 60; + /* without -q, default to 60s */ + do { + (void)gettimeofday(&now, NULL); + timersub(&endtime, &now, &td); + timeout = td.tv_sec * 1000 + td.tv_usec/1000; + if (timeout < 0) + timeout = 0; + rv = ftp_poll(pfd, 1, timeout); + } while (rv == -1 && errno == EINTR); /* loop until poll ! EINTR */ + if (rv == -1) { + warn("Can't poll waiting before accept"); + goto dataconn_failed; + } + if (rv == 0) { + warnx("Poll timeout waiting before accept"); + goto dataconn_failed; + } + + /* (non-blocking) accept the connection */ + fromlen = myctladdr.su_len; + do { + s = accept(data, (struct sockaddr *) &from.si_su, &fromlen); + } while (s == -1 && errno == EINTR); /* loop until accept ! EINTR */ + if (s == -1) { + warn("Can't accept data connection"); + goto dataconn_failed; + } + + (void)close(data); + data = s; + if (fcntl(data, F_SETFL, flags) == -1) /* restore socket flags */ + goto dataconn_failed; + +#ifdef IPTOS_THROUGHPUT + if (from.su_family == AF_INET) { + int tos = IPTOS_THROUGHPUT; + if (setsockopt(s, IPPROTO_IP, IP_TOS, + (void *)&tos, sizeof(tos)) == -1) { + DWARN("setsockopt %s (ignored)", "IPTOS_THROUGHPUT"); + } + } +#endif + return (fdopen(data, lmode)); + + dataconn_failed: + (void)close(data); + data = -1; + return (NULL); +} + +void +psabort(int notused) +{ + int oerrno = errno; + + sigint_raised = 1; + alarmtimer(0); + abrtflag++; + errno = oerrno; +} + +void +pswitch(int flag) +{ + sigfunc oldintr; + static struct comvars { + int connect; + char name[MAXHOSTNAMELEN]; + struct sockinet mctl; + struct sockinet hctl; + FILE *in; + FILE *out; + int tpe; + int curtpe; + int cpnd; + int sunqe; + int runqe; + int mcse; + int ntflg; + char nti[17]; + char nto[17]; + int mapflg; + char mi[MAXPATHLEN]; + char mo[MAXPATHLEN]; + } proxstruct, tmpstruct; + struct comvars *ip, *op; + + abrtflag = 0; + oldintr = xsignal(SIGINT, psabort); + if (flag) { + if (proxy) + return; + ip = &tmpstruct; + op = &proxstruct; + proxy++; + } else { + if (!proxy) + return; + ip = &proxstruct; + op = &tmpstruct; + proxy = 0; + } + ip->connect = connected; + connected = op->connect; + if (hostname) + (void)strlcpy(ip->name, hostname, sizeof(ip->name)); + else + ip->name[0] = '\0'; + hostname = op->name; + ip->hctl = hisctladdr; + hisctladdr = op->hctl; + ip->mctl = myctladdr; + myctladdr = op->mctl; + ip->in = cin; + cin = op->in; + ip->out = cout; + cout = op->out; + ip->tpe = type; + type = op->tpe; + ip->curtpe = curtype; + curtype = op->curtpe; + ip->cpnd = cpend; + cpend = op->cpnd; + ip->sunqe = sunique; + sunique = op->sunqe; + ip->runqe = runique; + runique = op->runqe; + ip->mcse = mcase; + mcase = op->mcse; + ip->ntflg = ntflag; + ntflag = op->ntflg; + (void)strlcpy(ip->nti, ntin, sizeof(ip->nti)); + (void)strlcpy(ntin, op->nti, sizeof(ntin)); + (void)strlcpy(ip->nto, ntout, sizeof(ip->nto)); + (void)strlcpy(ntout, op->nto, sizeof(ntout)); + ip->mapflg = mapflag; + mapflag = op->mapflg; + (void)strlcpy(ip->mi, mapin, sizeof(ip->mi)); + (void)strlcpy(mapin, op->mi, sizeof(mapin)); + (void)strlcpy(ip->mo, mapout, sizeof(ip->mo)); + (void)strlcpy(mapout, op->mo, sizeof(mapout)); + (void)xsignal(SIGINT, oldintr); + if (abrtflag) { + abrtflag = 0; + (*oldintr)(SIGINT); + } +} + +__dead static void +abortpt(int notused) +{ + + sigint_raised = 1; + alarmtimer(0); + if (fromatty) + write(fileno(ttyout), "\n", 1); + ptabflg++; + mflag = 0; + abrtflag = 0; + siglongjmp(ptabort, 1); +} + +void +proxtrans(const char *cmd, const char *local, const char *remote) +{ + sigfunc volatile oldintr; + int prox_type, nfnd; + int volatile secndflag; + const char *volatile cmd2; + + oldintr = NULL; + secndflag = 0; + if (strcmp(cmd, "RETR")) + cmd2 = "RETR"; + else + cmd2 = runique ? "STOU" : "STOR"; + if ((prox_type = type) == 0) { + if (unix_server && unix_proxy) + prox_type = TYPE_I; + else + prox_type = TYPE_A; + } + if (curtype != prox_type) + changetype(prox_type, 1); + if (command("PASV") != COMPLETE) { + fputs("proxy server does not support third party transfers.\n", + ttyout); + return; + } + pswitch(0); + if (!connected) { + fputs("No primary connection.\n", ttyout); + pswitch(1); + code = -1; + return; + } + if (curtype != prox_type) + changetype(prox_type, 1); + if (command("PORT %s", pasv) != COMPLETE) { + pswitch(1); + return; + } + if (sigsetjmp(ptabort, 1)) + goto abort; + oldintr = xsignal(SIGINT, abortpt); + if ((restart_point && + (command("REST " LLF, (LLT) restart_point) != CONTINUE)) + || (command("%s %s", cmd, remote) != PRELIM)) { + (void)xsignal(SIGINT, oldintr); + pswitch(1); + return; + } + sleep(2); + pswitch(1); + secndflag++; + if ((restart_point && + (command("REST " LLF, (LLT) restart_point) != CONTINUE)) + || (command("%s %s", cmd2, local) != PRELIM)) + goto abort; + ptflag++; + (void)getreply(0); + pswitch(0); + (void)getreply(0); + (void)xsignal(SIGINT, oldintr); + pswitch(1); + ptflag = 0; + fprintf(ttyout, "local: %s remote: %s\n", local, remote); + return; + abort: + if (sigsetjmp(xferabort, 1)) { + (void)xsignal(SIGINT, oldintr); + return; + } + (void)xsignal(SIGINT, abort_squared); + ptflag = 0; + if (strcmp(cmd, "RETR") && !proxy) + pswitch(1); + else if (!strcmp(cmd, "RETR") && proxy) + pswitch(0); + if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */ + if (command("%s %s", cmd2, local) != PRELIM) { + pswitch(0); + if (cpend) + abort_remote(NULL); + } + pswitch(1); + if (ptabflg) + code = -1; + (void)xsignal(SIGINT, oldintr); + return; + } + if (cpend) + abort_remote(NULL); + pswitch(!proxy); + if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */ + if (command("%s %s", cmd2, local) != PRELIM) { + pswitch(0); + if (cpend) + abort_remote(NULL); + pswitch(1); + if (ptabflg) + code = -1; + (void)xsignal(SIGINT, oldintr); + return; + } + } + if (cpend) + abort_remote(NULL); + pswitch(!proxy); + if (cpend) { + if ((nfnd = empty(cin, NULL, 10)) <= 0) { + if (nfnd < 0) + warn("Error aborting proxy command"); + if (ptabflg) + code = -1; + lostpeer(0); + } + (void)getreply(0); + (void)getreply(0); + } + if (proxy) + pswitch(0); + pswitch(1); + if (ptabflg) + code = -1; + (void)xsignal(SIGINT, oldintr); +} + +void +reset(int argc, char *argv[]) +{ + int nfnd = 1; + + if (argc == 0 && argv != NULL) { + UPRINTF("usage: %s\n", argv[0]); + code = -1; + return; + } + while (nfnd > 0) { + if ((nfnd = empty(cin, NULL, 0)) < 0) { + warn("Error resetting connection"); + code = -1; + lostpeer(0); + } else if (nfnd) + (void)getreply(0); + } +} + +char * +gunique(const char *local) +{ + static char new[MAXPATHLEN]; + char *cp = strrchr(local, '/'); + int d, count=0, len; + char ext = '1'; + + if (cp) + *cp = '\0'; + d = access(cp == local ? "/" : cp ? local : ".", W_OK); + if (cp) + *cp = '/'; + if (d < 0) { + warn("Can't access `%s'", local); + return (NULL); + } + len = strlcpy(new, local, sizeof(new)); + cp = &new[len]; + *cp++ = '.'; + while (!d) { + if (++count == 100) { + fputs("runique: can't find unique file name.\n", + ttyout); + return (NULL); + } + *cp++ = ext; + *cp = '\0'; + if (ext == '9') + ext = '0'; + else + ext++; + if ((d = access(new, F_OK)) < 0) + break; + if (ext != '0') + cp--; + else if (*(cp - 2) == '.') + *(cp - 1) = '1'; + else { + *(cp - 2) = *(cp - 2) + 1; + cp--; + } + } + return (new); +} + +/* + * abort_squared -- + * aborts abort_remote(). lostpeer() is called because if the user is + * too impatient to wait or there's another problem then ftp really + * needs to get back to a known state. + */ +static void +abort_squared(int dummy) +{ + char msgbuf[100]; + size_t len; + + sigint_raised = 1; + alarmtimer(0); + len = strlcpy(msgbuf, "\nremote abort aborted; closing connection.\n", + sizeof(msgbuf)); + write(fileno(ttyout), msgbuf, len); + lostpeer(0); + siglongjmp(xferabort, 1); +} + +void +abort_remote(FILE *din) +{ + char buf[BUFSIZ]; + int nfnd; + + if (cout == NULL) { + warnx("Lost control connection for abort"); + if (ptabflg) + code = -1; + lostpeer(0); + return; + } + /* + * send IAC in urgent mode instead of DM because 4.3BSD places oob mark + * after urgent byte rather than before as is protocol now + */ + buf[0] = IAC; + buf[1] = IP; + buf[2] = IAC; + if (send(fileno(cout), buf, 3, MSG_OOB) != 3) + warn("Can't send abort message"); + fprintf(cout, "%cABOR\r\n", DM); + (void)fflush(cout); + if ((nfnd = empty(cin, din, 10)) <= 0) { + if (nfnd < 0) + warn("Can't send abort message"); + if (ptabflg) + code = -1; + lostpeer(0); + } + if (din && (nfnd & 2)) { + while (read(fileno(din), buf, BUFSIZ) > 0) + continue; + } + if (getreply(0) == ERROR && code == 552) { + /* 552 needed for nic style abort */ + (void)getreply(0); + } + (void)getreply(0); +} + +/* + * Ensure that ai->ai_addr is NOT an IPv4 mapped address. + * IPv4 mapped address complicates too many things in FTP + * protocol handling, as FTP protocol is defined differently + * between IPv4 and IPv6. + * + * This may not be the best way to handle this situation, + * since the semantics of IPv4 mapped address is defined in + * the kernel. There are configurations where we should use + * IPv4 mapped address as native IPv6 address, not as + * "an IPv6 address that embeds IPv4 address" (namely, SIIT). + * + * More complete solution would be to have an additional + * getsockopt to grab "real" peername/sockname. "real" + * peername/sockname will be AF_INET if IPv4 mapped address + * is used to embed IPv4 address, and will be AF_INET6 if + * we use it as native. What a mess! + */ +void +ai_unmapped(struct addrinfo *ai) +{ +#ifdef INET6 + struct sockaddr_in6 *sin6; + struct sockaddr_in sin; + socklen_t len; + + if (ai->ai_family != AF_INET6) + return; + if (ai->ai_addrlen != sizeof(struct sockaddr_in6) || + sizeof(sin) > ai->ai_addrlen) + return; + sin6 = (struct sockaddr_in6 *)ai->ai_addr; + if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + return; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + len = sizeof(struct sockaddr_in); + memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], + sizeof(sin.sin_addr)); + sin.sin_port = sin6->sin6_port; + + ai->ai_family = AF_INET; +#if defined(HAVE_STRUCT_SOCKADDR_IN_SIN_LEN) + sin.sin_len = len; +#endif + memcpy(ai->ai_addr, &sin, len); + ai->ai_addrlen = len; +#endif +} + +#ifdef NO_USAGE +void +xusage(void) +{ + fputs("Usage error\n", ttyout); +} +#endif diff --git a/usr.bin/ftp/ftp_var.h b/usr.bin/ftp/ftp_var.h new file mode 100644 index 000000000..7e5040a7b --- /dev/null +++ b/usr.bin/ftp/ftp_var.h @@ -0,0 +1,354 @@ +/* $NetBSD: ftp_var.h,v 1.82 2012/12/21 18:07:36 christos Exp $ */ + +/*- + * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ftp_var.h 8.4 (Berkeley) 10/9/94 + */ + +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * FTP global variables. + */ + +#ifdef SMALL +#undef NO_EDITCOMPLETE +#define NO_EDITCOMPLETE +#undef NO_PROGRESS +#define NO_PROGRESS +#endif + +#include + +#include +#include + +#include + +#include +#include + +#ifndef NO_EDITCOMPLETE +#include +#endif /* !NO_EDITCOMPLETE */ + +#include "extern.h" +#include "progressbar.h" + +/* + * Format of command table. + */ +struct cmd { + const char *c_name; /* name of command */ + const char *c_help; /* help string */ + char c_bell; /* give bell when command completes */ + char c_conn; /* must be connected to use command */ + char c_proxy; /* proxy server may execute */ +#ifndef NO_EDITCOMPLETE + const char *c_complete; /* context sensitive completion list */ +#endif /* !NO_EDITCOMPLETE */ + void (*c_handler)(int, char **); /* function to call */ +}; + +#define MAX_C_NAME 12 /* maximum length of cmd.c_name */ + +/* + * Format of macro table + */ +struct macel { + char mac_name[9]; /* macro name */ + char *mac_start; /* start of macro in macbuf */ + char *mac_end; /* end of macro in macbuf */ +}; + +/* + * Format of option table + */ +struct option { + const char *name; + char *value; +}; + +/* + * Indices to features[]; an array containing status of remote server + * features; -1 not known (FEAT failed), 0 absent, 1 present. + */ +enum { + FEAT_FEAT = 0, /* FEAT, OPTS */ + FEAT_MDTM, /* MDTM */ + FEAT_MLST, /* MLSD, MLST */ + FEAT_REST_STREAM, /* RESTart STREAM */ + FEAT_SIZE, /* SIZE */ + FEAT_TVFS, /* TVFS (not used) */ + FEAT_max +}; + + +/* + * Global defines + */ +#define FTPBUFLEN MAXPATHLEN + 200 +#define MAX_IN_PORT_T 0xffffU + +#define HASHBYTES 1024 /* default mark for `hash' command */ +#define DEFAULTINCR 1024 /* default increment for `rate' command */ + +#define FTP_PORT 21 /* default if ! getservbyname("ftp/tcp") */ +#define HTTP_PORT 80 /* default if ! getservbyname("http/tcp") */ +#define HTTPS_PORT 443 /* default if ! getservbyname("https/tcp") */ +#ifndef GATE_PORT +#define GATE_PORT 21 /* default if ! getservbyname("ftpgate/tcp") */ +#endif +#ifndef GATE_SERVER +#define GATE_SERVER "" /* default server */ +#endif + +#define DEFAULTPAGER "more" /* default pager if $PAGER isn't set */ +#define DEFAULTPROMPT "ftp> " /* default prompt if `set prompt' is empty */ +#define DEFAULTRPROMPT "" /* default rprompt if `set rprompt' is empty */ + +#define TMPFILE "ftpXXXXXXXXXX" + + +#ifndef GLOBAL +#define GLOBAL extern +#endif + +/* + * Options and other state info. + */ +GLOBAL int trace; /* trace packets exchanged */ +GLOBAL int hash; /* print # for each buffer transferred */ +GLOBAL int mark; /* number of bytes between hashes */ +GLOBAL int sendport; /* use PORT/LPRT cmd for each data connection */ +GLOBAL int connected; /* 1 = connected to server, -1 = logged in */ +GLOBAL int interactive; /* interactively prompt on m* cmds */ +GLOBAL int confirmrest; /* confirm rest of current m* cmd */ +GLOBAL int ftp_debug; /* debugging level */ +GLOBAL int bell; /* ring bell on cmd completion */ +GLOBAL int doglob; /* glob local file names */ +GLOBAL int autologin; /* establish user account on connection */ +GLOBAL int proxy; /* proxy server connection active */ +GLOBAL int proxflag; /* proxy connection exists */ +GLOBAL int gatemode; /* use gate-ftp */ +GLOBAL const char *gateserver; /* server to use for gate-ftp */ +GLOBAL int sunique; /* store files on server with unique name */ +GLOBAL int runique; /* store local files with unique name */ +GLOBAL int mcase; /* map upper to lower case for mget names */ +GLOBAL int ntflag; /* use ntin ntout tables for name translation */ +GLOBAL int mapflag; /* use mapin mapout templates on file names */ +GLOBAL int preserve; /* preserve modification time on files */ +GLOBAL int code; /* return/reply code for ftp command */ +GLOBAL int crflag; /* if 1, strip car. rets. on ascii gets */ +GLOBAL int passivemode; /* passive mode enabled */ +GLOBAL int activefallback; /* fall back to active mode if passive fails */ +GLOBAL char *altarg; /* argv[1] with no shell-like preprocessing */ +GLOBAL char ntin[17]; /* input translation table */ +GLOBAL char ntout[17]; /* output translation table */ +GLOBAL char mapin[MAXPATHLEN]; /* input map template */ +GLOBAL char mapout[MAXPATHLEN]; /* output map template */ +GLOBAL char typename[32]; /* name of file transfer type */ +GLOBAL int type; /* requested file transfer type */ +GLOBAL int curtype; /* current file transfer type */ +GLOBAL char structname[32]; /* name of file transfer structure */ +GLOBAL int stru; /* file transfer structure */ +GLOBAL char formname[32]; /* name of file transfer format */ +GLOBAL int form; /* file transfer format */ +GLOBAL char modename[32]; /* name of file transfer mode */ +GLOBAL int mode; /* file transfer mode */ +GLOBAL char bytename[32]; /* local byte size in ascii */ +GLOBAL int bytesize; /* local byte size in binary */ +GLOBAL int anonftp; /* automatic anonymous login */ +GLOBAL int dirchange; /* remote directory changed by cd command */ +GLOBAL int flushcache; /* set HTTP cache flush headers with request */ +GLOBAL int rate_get; /* maximum get xfer rate */ +GLOBAL int rate_get_incr; /* increment for get xfer rate */ +GLOBAL int rate_put; /* maximum put xfer rate */ +GLOBAL int rate_put_incr; /* increment for put xfer rate */ +GLOBAL int retry_connect; /* seconds between retrying connection */ +GLOBAL const char *tmpdir; /* temporary directory */ +GLOBAL int epsv4; /* use EPSV/EPRT on IPv4 connections */ +GLOBAL int epsv4bad; /* EPSV doesn't work on the current server */ +GLOBAL int epsv6; /* use EPSV/EPRT on IPv6 connections */ +GLOBAL int epsv6bad; /* EPSV doesn't work on the current server */ +GLOBAL int editing; /* command line editing enabled */ +GLOBAL int features[FEAT_max]; /* remote FEATures supported */ + +#ifndef NO_EDITCOMPLETE +GLOBAL EditLine *el; /* editline(3) status structure */ +GLOBAL History *hist; /* editline(3) history structure */ +GLOBAL char *cursor_pos; /* cursor position we're looking for */ +GLOBAL size_t cursor_argc; /* location of cursor in margv */ +GLOBAL size_t cursor_argo; /* offset of cursor in margv[cursor_argc] */ +#endif /* !NO_EDITCOMPLETE */ + +GLOBAL char *hostname; /* name of host connected to */ +GLOBAL int unix_server; /* server is unix, can use binary for ascii */ +GLOBAL int unix_proxy; /* proxy is unix, can use binary for ascii */ +GLOBAL char localcwd[MAXPATHLEN]; /* local dir */ +GLOBAL char remotecwd[MAXPATHLEN]; /* remote dir */ +GLOBAL char *username; /* name of user logged in as. (dynamic) */ + +GLOBAL sa_family_t family; /* address family to use for connections */ +GLOBAL const char *ftpport; /* port number to use for FTP connections */ +GLOBAL const char *httpport; /* port number to use for HTTP connections */ +#ifdef WITH_SSL +GLOBAL const char *httpsport; /* port number to use for HTTPS connections */ +#endif +GLOBAL const char *gateport; /* port number to use for gateftp connections */ +GLOBAL struct addrinfo *bindai; /* local address to bind as */ + +GLOBAL char *outfile; /* filename to output URLs to */ +GLOBAL int restartautofetch; /* restart auto-fetch */ + +GLOBAL char line[FTPBUFLEN]; /* input line buffer */ +GLOBAL char *stringbase; /* current scan point in line buffer */ +GLOBAL char argbuf[FTPBUFLEN]; /* argument storage buffer */ +GLOBAL char *argbase; /* current storage point in arg buffer */ +GLOBAL StringList *marg_sl; /* stringlist containing margv */ +GLOBAL int margc; /* count of arguments on input line */ +#define margv (marg_sl->sl_str) /* args parsed from input line */ +GLOBAL int cpend; /* flag: if != 0, then pending server reply */ +GLOBAL int mflag; /* flag: if != 0, then active multi command */ + +GLOBAL int options; /* used during socket creation */ + +GLOBAL int sndbuf_size; /* socket send buffer size */ +GLOBAL int rcvbuf_size; /* socket receive buffer size */ + +GLOBAL int macnum; /* number of defined macros */ +GLOBAL struct macel macros[16]; +GLOBAL char macbuf[4096]; + +GLOBAL char *localhome; /* local home directory */ +GLOBAL char *localname; /* local user name */ +GLOBAL char netrc[MAXPATHLEN]; /* path to .netrc file */ +GLOBAL char reply_string[BUFSIZ]; /* first line of previous reply */ +GLOBAL void (*reply_callback)(const char *); + /* + * function to call for each line in + * the server's reply except for the + * first (`xxx-') and last (`xxx ') + */ + +GLOBAL volatile sig_atomic_t sigint_raised; + +GLOBAL FILE *cin; +GLOBAL FILE *cout; +GLOBAL int data; + +extern struct cmd cmdtab[]; +extern struct option optiontab[]; + + +#define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0')) +#define FREEPTR(x) if ((x) != NULL) { free(x); (x) = NULL; } + +#ifdef BSD4_4 +# define HAVE_STRUCT_SOCKADDR_IN_SIN_LEN 1 +#endif + +#ifdef NO_LONG_LONG +# define STRTOLL(x,y,z) strtol(x,y,z) +#else +# define STRTOLL(x,y,z) strtoll(x,y,z) +#endif + +#ifdef NO_DEBUG +#define DPRINTF(...) +#define DWARN(...) +#else +#define DPRINTF(...) if (ftp_debug) (void)fprintf(ttyout, __VA_ARGS__) +#define DWARN(...) if (ftp_debug) warn(__VA_ARGS__) +#endif + +#define STRorNULL(s) ((s) ? (s) : "") + +#ifdef NO_USAGE +void xusage(void); +#define UPRINTF(...) xusage() +#else +#define UPRINTF(...) (void)fprintf(ttyout, __VA_ARGS__) +#endif diff --git a/usr.bin/ftp/main.c b/usr.bin/ftp/main.c new file mode 100644 index 000000000..c84364d58 --- /dev/null +++ b/usr.bin/ftp/main.c @@ -0,0 +1,1057 @@ +/* $NetBSD: main.c,v 1.122 2012/12/22 16:57:10 christos Exp $ */ + +/*- + * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (C) 1997 and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1985, 1989, 1993, 1994\ + The Regents of the University of California. All rights reserved.\ + Copyright 1996-2008 The NetBSD Foundation, Inc. All rights reserved"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 10/9/94"; +#else +__RCSID("$NetBSD: main.c,v 1.122 2012/12/22 16:57:10 christos Exp $"); +#endif +#endif /* not lint */ + +/* + * FTP User Program -- Command Interface. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GLOBAL /* force GLOBAL decls in ftp_var.h to be declared */ +#include "ftp_var.h" + +#define FTP_PROXY "ftp_proxy" /* env var with FTP proxy location */ +#define HTTP_PROXY "http_proxy" /* env var with HTTP proxy location */ +#define HTTPS_PROXY "https_proxy" /* env var with HTTPS proxy location */ +#define NO_PROXY "no_proxy" /* env var with list of non-proxied + * hosts, comma or space separated */ + +__dead static void usage(void); +static void setupoption(const char *, const char *, const char *); + +int +main(int volatile argc, char **volatile argv) +{ + int ch, rval; + struct passwd *pw; + char *cp, *ep, *anonpass, *upload_path, *src_addr; + const char *anonuser; + int dumbterm, isupload; + size_t len; + + tzset(); + setlocale(LC_ALL, ""); + setprogname(argv[0]); + + sigint_raised = 0; + + ftpport = "ftp"; + httpport = "http"; +#ifdef WITH_SSL + httpsport = "https"; +#endif + gateport = NULL; + cp = getenv("FTPSERVERPORT"); + if (cp != NULL) + gateport = cp; + else + gateport = "ftpgate"; + doglob = 1; + interactive = 1; + autologin = 1; + passivemode = 1; + activefallback = 1; + preserve = 1; + verbose = 0; + progress = 0; + gatemode = 0; + data = -1; + outfile = NULL; + restartautofetch = 0; +#ifndef NO_EDITCOMPLETE + editing = 0; + el = NULL; + hist = NULL; +#endif + bytes = 0; + mark = HASHBYTES; + rate_get = 0; + rate_get_incr = DEFAULTINCR; + rate_put = 0; + rate_put_incr = DEFAULTINCR; +#ifdef INET6 + epsv4 = 1; + epsv6 = 1; +#else + epsv4 = 0; + epsv6 = 0; +#endif + epsv4bad = 0; + epsv6bad = 0; + src_addr = NULL; + upload_path = NULL; + isupload = 0; + reply_callback = NULL; +#ifdef INET6 + family = AF_UNSPEC; +#else + family = AF_INET; /* force AF_INET if no INET6 support */ +#endif + + netrc[0] = '\0'; + cp = getenv("NETRC"); + if (cp != NULL && strlcpy(netrc, cp, sizeof(netrc)) >= sizeof(netrc)) + errx(1, "$NETRC `%s': %s", cp, strerror(ENAMETOOLONG)); + + marg_sl = ftp_sl_init(); + if ((tmpdir = getenv("TMPDIR")) == NULL) + tmpdir = _PATH_TMP; + + /* Set default operation mode based on FTPMODE environment variable */ + if ((cp = getenv("FTPMODE")) != NULL) { + if (strcasecmp(cp, "passive") == 0) { + passivemode = 1; + activefallback = 0; + } else if (strcasecmp(cp, "active") == 0) { + passivemode = 0; + activefallback = 0; + } else if (strcasecmp(cp, "gate") == 0) { + gatemode = 1; + } else if (strcasecmp(cp, "auto") == 0) { + passivemode = 1; + activefallback = 1; + } else + warnx("Unknown $FTPMODE `%s'; using defaults", cp); + } + + if (strcmp(getprogname(), "pftp") == 0) { + passivemode = 1; + activefallback = 0; + } else if (strcmp(getprogname(), "gate-ftp") == 0) + gatemode = 1; + + gateserver = getenv("FTPSERVER"); + if (gateserver == NULL || *gateserver == '\0') + gateserver = GATE_SERVER; + if (gatemode) { + if (*gateserver == '\0') { + warnx( +"Neither $FTPSERVER nor GATE_SERVER is defined; disabling gate-ftp"); + gatemode = 0; + } + } + + cp = getenv("TERM"); + if (cp == NULL || strcmp(cp, "dumb") == 0) + dumbterm = 1; + else + dumbterm = 0; + fromatty = isatty(fileno(stdin)); + ttyout = stdout; + if (isatty(fileno(ttyout))) { + verbose = 1; /* verbose if to a tty */ + if (! dumbterm) { +#ifndef NO_EDITCOMPLETE + if (fromatty) /* editing mode on if tty is usable */ + editing = 1; +#endif +#ifndef NO_PROGRESS + if (foregroundproc()) + progress = 1; /* progress bar on if fg */ +#endif + } + } + + while ((ch = getopt(argc, argv, "46AadefginN:o:pP:q:r:Rs:tT:u:vV")) != -1) { + switch (ch) { + case '4': + family = AF_INET; + break; + + case '6': +#ifdef INET6 + family = AF_INET6; +#else + warnx("INET6 support is not available; ignoring -6"); +#endif + break; + + case 'A': + activefallback = 0; + passivemode = 0; + break; + + case 'a': + anonftp = 1; + break; + + case 'd': + options |= SO_DEBUG; + ftp_debug++; + break; + + case 'e': +#ifndef NO_EDITCOMPLETE + editing = 0; +#endif + break; + + case 'f': + flushcache = 1; + break; + + case 'g': + doglob = 0; + break; + + case 'i': + interactive = 0; + break; + + case 'n': + autologin = 0; + break; + + case 'N': + if (strlcpy(netrc, optarg, sizeof(netrc)) + >= sizeof(netrc)) + errx(1, "%s: %s", optarg, + strerror(ENAMETOOLONG)); + break; + + case 'o': + outfile = optarg; + if (strcmp(outfile, "-") == 0) + ttyout = stderr; + break; + + case 'p': + passivemode = 1; + activefallback = 0; + break; + + case 'P': + ftpport = optarg; + break; + + case 'q': + quit_time = strtol(optarg, &ep, 10); + if (quit_time < 1 || *ep != '\0') + errx(1, "Bad quit value: %s", optarg); + break; + + case 'r': + retry_connect = strtol(optarg, &ep, 10); + if (retry_connect < 1 || *ep != '\0') + errx(1, "Bad retry value: %s", optarg); + break; + + case 'R': + restartautofetch = 1; + break; + + case 's': + src_addr = optarg; + break; + + case 't': + trace = 1; + break; + + case 'T': + { + int targc; + char *targv[6], *oac; + char cmdbuf[MAX_C_NAME]; + + /* look for `dir,max[,incr]' */ + targc = 0; + (void)strlcpy(cmdbuf, "-T", sizeof(cmdbuf)); + targv[targc++] = cmdbuf; + oac = ftp_strdup(optarg); + + while ((cp = strsep(&oac, ",")) != NULL) { + if (*cp == '\0') { + warnx("Bad throttle value `%s'", + optarg); + usage(); + /* NOTREACHED */ + } + targv[targc++] = cp; + if (targc >= 5) + break; + } + if (parserate(targc, targv, 1) == -1) + usage(); + free(oac); + break; + } + + case 'u': + { + isupload = 1; + interactive = 0; + upload_path = ftp_strdup(optarg); + + break; + } + + case 'v': + progress = verbose = 1; + break; + + case 'V': + progress = verbose = 0; + break; + + default: + usage(); + } + } + /* set line buffering on ttyout */ + setvbuf(ttyout, NULL, _IOLBF, 0); + argc -= optind; + argv += optind; + + cpend = 0; /* no pending replies */ + proxy = 0; /* proxy not active */ + crflag = 1; /* strip c.r. on ascii gets */ + sendport = -1; /* not using ports */ + + if (src_addr != NULL) { + struct addrinfo hints; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(src_addr, NULL, &hints, &bindai); + if (error) { + errx(1, "Can't lookup `%s': %s", src_addr, + (error == EAI_SYSTEM) ? strerror(errno) + : gai_strerror(error)); + } + } + + /* + * Cache the user name and home directory. + */ + localhome = NULL; + localname = NULL; + anonuser = "anonymous"; + cp = getenv("HOME"); + if (! EMPTYSTRING(cp)) + localhome = ftp_strdup(cp); + pw = NULL; + cp = getlogin(); + if (cp != NULL) + pw = getpwnam(cp); + if (pw == NULL) + pw = getpwuid(getuid()); + if (pw != NULL) { + if (localhome == NULL && !EMPTYSTRING(pw->pw_dir)) + localhome = ftp_strdup(pw->pw_dir); + localname = ftp_strdup(pw->pw_name); + anonuser = localname; + } + if (netrc[0] == '\0' && localhome != NULL) { + if (strlcpy(netrc, localhome, sizeof(netrc)) >= sizeof(netrc) || + strlcat(netrc, "/.netrc", sizeof(netrc)) >= sizeof(netrc)) { + warnx("%s/.netrc: %s", localhome, + strerror(ENAMETOOLONG)); + netrc[0] = '\0'; + } + } + if (localhome == NULL) + localhome = ftp_strdup("/"); + + /* + * Every anonymous FTP server I've encountered will accept the + * string "username@", and will append the hostname itself. We + * do this by default since many servers are picky about not + * having a FQDN in the anonymous password. + * - thorpej@NetBSD.org + */ + len = strlen(anonuser) + 2; + anonpass = ftp_malloc(len); + (void)strlcpy(anonpass, anonuser, len); + (void)strlcat(anonpass, "@", len); + + /* + * set all the defaults for options defined in + * struct option optiontab[] declared in cmdtab.c + */ + setupoption("anonpass", getenv("FTPANONPASS"), anonpass); + setupoption("ftp_proxy", getenv(FTP_PROXY), ""); + setupoption("http_proxy", getenv(HTTP_PROXY), ""); + setupoption("https_proxy", getenv(HTTPS_PROXY), ""); + setupoption("no_proxy", getenv(NO_PROXY), ""); + setupoption("pager", getenv("PAGER"), DEFAULTPAGER); + setupoption("prompt", getenv("FTPPROMPT"), DEFAULTPROMPT); + setupoption("rprompt", getenv("FTPRPROMPT"), DEFAULTRPROMPT); + + free(anonpass); + + setttywidth(0); +#ifdef SIGINFO + (void)xsignal(SIGINFO, psummary); +#endif + (void)xsignal(SIGQUIT, psummary); + (void)xsignal(SIGUSR1, crankrate); + (void)xsignal(SIGUSR2, crankrate); + (void)xsignal(SIGWINCH, setttywidth); + + if (argc > 0) { + if (isupload) { + rval = auto_put(argc, argv, upload_path); + sigint_or_rval_exit: + if (sigint_raised) { + (void)xsignal(SIGINT, SIG_DFL); + raise(SIGINT); + } + exit(rval); + } else if (strchr(argv[0], ':') != NULL + && ! isipv6addr(argv[0])) { + rval = auto_fetch(argc, argv); + if (rval >= 0) /* -1 == connected and cd-ed */ + goto sigint_or_rval_exit; + } else { + char *xargv[4], *uuser, *host; + char cmdbuf[MAXPATHLEN]; + + if ((rval = sigsetjmp(toplevel, 1))) + goto sigint_or_rval_exit; + (void)xsignal(SIGINT, intr); + (void)xsignal(SIGPIPE, lostpeer); + uuser = NULL; + host = argv[0]; + cp = strchr(host, '@'); + if (cp) { + *cp = '\0'; + uuser = host; + host = cp + 1; + } + (void)strlcpy(cmdbuf, getprogname(), sizeof(cmdbuf)); + xargv[0] = cmdbuf; + xargv[1] = host; + xargv[2] = argv[1]; + xargv[3] = NULL; + do { + int oautologin; + + oautologin = autologin; + if (uuser != NULL) { + anonftp = 0; + autologin = 0; + } + setpeer(argc+1, xargv); + autologin = oautologin; + if (connected == 1 && uuser != NULL) + (void)ftp_login(host, uuser, NULL); + if (!retry_connect) + break; + if (!connected) { + macnum = 0; + fprintf(ttyout, + "Retrying in %d seconds...\n", + retry_connect); + sleep(retry_connect); + } + } while (!connected); + retry_connect = 0; /* connected, stop hiding msgs */ + } + } + if (isupload) + usage(); + +#ifndef NO_EDITCOMPLETE + controlediting(); +#endif /* !NO_EDITCOMPLETE */ + + (void)sigsetjmp(toplevel, 1); + (void)xsignal(SIGINT, intr); + (void)xsignal(SIGPIPE, lostpeer); + for (;;) + cmdscanner(); +} + +/* + * Generate a prompt + */ +char * +prompt(void) +{ + static char **promptopt; + static char buf[MAXPATHLEN]; + + if (promptopt == NULL) { + struct option *o; + + o = getoption("prompt"); + if (o == NULL) + errx(1, "prompt: no such option `prompt'"); + promptopt = &(o->value); + } + formatbuf(buf, sizeof(buf), *promptopt ? *promptopt : DEFAULTPROMPT); + return (buf); +} + +/* + * Generate an rprompt + */ +char * +rprompt(void) +{ + static char **rpromptopt; + static char buf[MAXPATHLEN]; + + if (rpromptopt == NULL) { + struct option *o; + + o = getoption("rprompt"); + if (o == NULL) + errx(1, "rprompt: no such option `rprompt'"); + rpromptopt = &(o->value); + } + formatbuf(buf, sizeof(buf), *rpromptopt ? *rpromptopt : DEFAULTRPROMPT); + return (buf); +} + +/* + * Command parser. + */ +void +cmdscanner(void) +{ + struct cmd *c; + char *p; +#ifndef NO_EDITCOMPLETE + int ch; + size_t num; +#endif + int len; + char cmdbuf[MAX_C_NAME]; + + for (;;) { +#ifndef NO_EDITCOMPLETE + if (!editing) { +#endif /* !NO_EDITCOMPLETE */ + if (fromatty) { + fputs(prompt(), ttyout); + p = rprompt(); + if (*p) + fprintf(ttyout, "%s ", p); + } + (void)fflush(ttyout); + len = get_line(stdin, line, sizeof(line), NULL); + switch (len) { + case -1: /* EOF */ + case -2: /* error */ + if (fromatty) + putc('\n', ttyout); + quit(0, NULL); + /* NOTREACHED */ + case -3: /* too long; try again */ + fputs("Sorry, input line is too long.\n", + ttyout); + continue; + case 0: /* empty; try again */ + continue; + default: /* all ok */ + break; + } +#ifndef NO_EDITCOMPLETE + } else { + const char *buf; + HistEvent ev; + cursor_pos = NULL; + + buf = el_gets(el, &ch); + num = ch; + if (buf == NULL || num == 0) { + if (fromatty) + putc('\n', ttyout); + quit(0, NULL); + } + if (num >= sizeof(line)) { + fputs("Sorry, input line is too long.\n", + ttyout); + break; + } + memcpy(line, buf, num); + if (line[--num] == '\n') { + line[num] = '\0'; + if (num == 0) + break; + } + history(hist, &ev, H_ENTER, buf); + } +#endif /* !NO_EDITCOMPLETE */ + + makeargv(); + if (margc == 0) + continue; + c = getcmd(margv[0]); + if (c == (struct cmd *)-1) { + fputs("?Ambiguous command.\n", ttyout); + continue; + } + if (c == NULL) { +#if !defined(NO_EDITCOMPLETE) + /* + * attempt to el_parse() unknown commands. + * any command containing a ':' would be parsed + * as "[prog:]cmd ...", and will result in a + * false positive if prog != "ftp", so treat + * such commands as invalid. + */ + if (strchr(margv[0], ':') != NULL || + !editing || + el_parse(el, margc, (void *)margv) != 0) +#endif /* !NO_EDITCOMPLETE */ + fputs("?Invalid command.\n", ttyout); + continue; + } + if (c->c_conn && !connected) { + fputs("Not connected.\n", ttyout); + continue; + } + confirmrest = 0; + (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf)); + margv[0] = cmdbuf; + (*c->c_handler)(margc, margv); + if (bell && c->c_bell) + (void)putc('\007', ttyout); + if (c->c_handler != help) + break; + } + (void)xsignal(SIGINT, intr); + (void)xsignal(SIGPIPE, lostpeer); +} + +struct cmd * +getcmd(const char *name) +{ + const char *p, *q; + struct cmd *c, *found; + int nmatches, longest; + + if (name == NULL) + return (0); + + longest = 0; + nmatches = 0; + found = 0; + for (c = cmdtab; (p = c->c_name) != NULL; c++) { + for (q = name; *q == *p++; q++) + if (*q == 0) /* exact match? */ + return (c); + if (!*q) { /* the name was a prefix */ + if (q - name > longest) { + longest = q - name; + nmatches = 1; + found = c; + } else if (q - name == longest) + nmatches++; + } + } + if (nmatches > 1) + return ((struct cmd *)-1); + return (found); +} + +/* + * Slice a string up into argc/argv. + */ + +int slrflag; + +void +makeargv(void) +{ + char *argp; + + stringbase = line; /* scan from first of buffer */ + argbase = argbuf; /* store from first of buffer */ + slrflag = 0; + marg_sl->sl_cur = 0; /* reset to start of marg_sl */ + for (margc = 0; ; margc++) { + argp = slurpstring(); + ftp_sl_add(marg_sl, argp); + if (argp == NULL) + break; + } +#ifndef NO_EDITCOMPLETE + if (cursor_pos == line) { + cursor_argc = 0; + cursor_argo = 0; + } else if (cursor_pos != NULL) { + cursor_argc = margc; + cursor_argo = strlen(margv[margc-1]); + } +#endif /* !NO_EDITCOMPLETE */ +} + +#ifdef NO_EDITCOMPLETE +#define INC_CHKCURSOR(x) (x)++ +#else /* !NO_EDITCOMPLETE */ +#define INC_CHKCURSOR(x) { (x)++ ; \ + if (x == cursor_pos) { \ + cursor_argc = margc; \ + cursor_argo = ap-argbase; \ + cursor_pos = NULL; \ + } } + +#endif /* !NO_EDITCOMPLETE */ + +/* + * Parse string into argbuf; + * implemented with FSM to + * handle quoting and strings + */ +char * +slurpstring(void) +{ + static char bangstr[2] = { '!', '\0' }; + static char dollarstr[2] = { '$', '\0' }; + int got_one = 0; + char *sb = stringbase; + char *ap = argbase; + char *tmp = argbase; /* will return this if token found */ + + if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */ + switch (slrflag) { /* and $ as token for macro invoke */ + case 0: + slrflag++; + INC_CHKCURSOR(stringbase); + return ((*sb == '!') ? bangstr : dollarstr); + /* NOTREACHED */ + case 1: + slrflag++; + altarg = stringbase; + break; + default: + break; + } + } + +S0: + switch (*sb) { + + case '\0': + goto OUT; + + case ' ': + case '\t': + INC_CHKCURSOR(sb); + goto S0; + + default: + switch (slrflag) { + case 0: + slrflag++; + break; + case 1: + slrflag++; + altarg = sb; + break; + default: + break; + } + goto S1; + } + +S1: + switch (*sb) { + + case ' ': + case '\t': + case '\0': + goto OUT; /* end of token */ + + case '\\': + INC_CHKCURSOR(sb); + goto S2; /* slurp next character */ + + case '"': + INC_CHKCURSOR(sb); + goto S3; /* slurp quoted string */ + + default: + *ap = *sb; /* add character to token */ + ap++; + INC_CHKCURSOR(sb); + got_one = 1; + goto S1; + } + +S2: + switch (*sb) { + + case '\0': + goto OUT; + + default: + *ap = *sb; + ap++; + INC_CHKCURSOR(sb); + got_one = 1; + goto S1; + } + +S3: + switch (*sb) { + + case '\0': + goto OUT; + + case '"': + INC_CHKCURSOR(sb); + goto S1; + + default: + *ap = *sb; + ap++; + INC_CHKCURSOR(sb); + got_one = 1; + goto S3; + } + +OUT: + if (got_one) + *ap++ = '\0'; + argbase = ap; /* update storage pointer */ + stringbase = sb; /* update scan pointer */ + if (got_one) { + return (tmp); + } + switch (slrflag) { + case 0: + slrflag++; + break; + case 1: + slrflag++; + altarg = NULL; + break; + default: + break; + } + return (NULL); +} + +/* + * Help/usage command. + * Call each command handler with argc == 0 and argv[0] == name. + */ +void +help(int argc, char *argv[]) +{ + struct cmd *c; + char *nargv[1], *cmd; + const char *p; + int isusage; + + cmd = argv[0]; + isusage = (strcmp(cmd, "usage") == 0); + if (argc == 0 || (isusage && argc == 1)) { + UPRINTF("usage: %s [command [...]]\n", cmd); + return; + } + if (argc == 1) { + StringList *buf; + + buf = ftp_sl_init(); + fprintf(ttyout, + "%sommands may be abbreviated. Commands are:\n\n", + proxy ? "Proxy c" : "C"); + for (c = cmdtab; (p = c->c_name) != NULL; c++) + if (!proxy || c->c_proxy) + ftp_sl_add(buf, ftp_strdup(p)); + list_vertical(buf); + sl_free(buf, 1); + return; + } + +#define HELPINDENT ((int) sizeof("disconnect")) + + while (--argc > 0) { + char *arg; + char cmdbuf[MAX_C_NAME]; + + arg = *++argv; + c = getcmd(arg); + if (c == (struct cmd *)-1) + fprintf(ttyout, "?Ambiguous %s command `%s'\n", + cmd, arg); + else if (c == NULL) + fprintf(ttyout, "?Invalid %s command `%s'\n", + cmd, arg); + else { + if (isusage) { + (void)strlcpy(cmdbuf, c->c_name, sizeof(cmdbuf)); + nargv[0] = cmdbuf; + (*c->c_handler)(0, nargv); + } else + fprintf(ttyout, "%-*s\t%s\n", HELPINDENT, + c->c_name, c->c_help); + } + } +} + +struct option * +getoption(const char *name) +{ + const char *p; + struct option *c; + + if (name == NULL) + return (NULL); + for (c = optiontab; (p = c->name) != NULL; c++) { + if (strcasecmp(p, name) == 0) + return (c); + } + return (NULL); +} + +char * +getoptionvalue(const char *name) +{ + struct option *c; + + if (name == NULL) + errx(1, "getoptionvalue: invoked with NULL name"); + c = getoption(name); + if (c != NULL) + return (c->value); + errx(1, "getoptionvalue: invoked with unknown option `%s'", name); + /* NOTREACHED */ +} + +static void +setupoption(const char *name, const char *value, const char *defaultvalue) +{ + set_option(name, value ? value : defaultvalue, 0); +} + +void +usage(void) +{ + const char *progname = getprogname(); + + (void)fprintf(stderr, +"usage: %s [-46AadefginpRtVv] [-N netrc] [-o outfile] [-P port] [-q quittime]\n" +" [-r retry] [-s srcaddr] [-T dir,max[,inc]]\n" +" [[user@]host [port]] [host:path[/]] [file:///file]\n" +" [ftp://[user[:pass]@]host[:port]/path[/]]\n" +" [http://[user[:pass]@]host[:port]/path] [...]\n" +#ifdef WITH_SSL +" [https://[user[:pass]@]host[:port]/path] [...]\n" +#endif +" %s -u URL file [...]\n", progname, progname); + exit(1); +} diff --git a/usr.bin/ftp/progressbar.c b/usr.bin/ftp/progressbar.c new file mode 100644 index 000000000..2350776d6 --- /dev/null +++ b/usr.bin/ftp/progressbar.c @@ -0,0 +1,472 @@ +/* $NetBSD: progressbar.c,v 1.22 2012/06/27 22:07:36 riastradh Exp $ */ + +/*- + * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: progressbar.c,v 1.22 2012/06/27 22:07:36 riastradh Exp $"); +#endif /* not lint */ + +/* + * FTP User Program -- Misc support routines + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "progressbar.h" + +#if !defined(NO_PROGRESS) +/* + * return non-zero if we're the current foreground process + */ +int +foregroundproc(void) +{ + static pid_t pgrp = -1; + + if (pgrp == -1) + pgrp = getpgrp(); + + return (tcgetpgrp(fileno(ttyout)) == pgrp); +} +#endif /* !defined(NO_PROGRESS) */ + + +static void updateprogressmeter(int); + +/* + * SIGALRM handler to update the progress meter + */ +static void +updateprogressmeter(int dummy) +{ + int oerrno = errno; + + progressmeter(0); + errno = oerrno; +} + +/* + * List of order of magnitude suffixes, per IEC 60027-2. + */ +static const char * const suffixes[] = { + "", /* 2^0 (byte) */ + "KiB", /* 2^10 Kibibyte */ + "MiB", /* 2^20 Mebibyte */ + "GiB", /* 2^30 Gibibyte */ + "TiB", /* 2^40 Tebibyte */ + "PiB", /* 2^50 Pebibyte */ + "EiB", /* 2^60 Exbibyte */ +#if 0 + /* The following are not necessary for signed 64-bit off_t */ + "ZiB", /* 2^70 Zebibyte */ + "YiB", /* 2^80 Yobibyte */ +#endif +}; +#define NSUFFIXES (int)(sizeof(suffixes) / sizeof(suffixes[0])) + +/* + * Display a transfer progress bar if progress is non-zero. + * SIGALRM is hijacked for use by this function. + * - Before the transfer, set filesize to size of file (or -1 if unknown), + * and call with flag = -1. This starts the once per second timer, + * and a call to updateprogressmeter() upon SIGALRM. + * - During the transfer, updateprogressmeter will call progressmeter + * with flag = 0 + * - After the transfer, call with flag = 1 + */ +static struct timeval start; +static struct timeval lastupdate; + +#define BUFLEFT (sizeof(buf) - len) + +void +progressmeter(int flag) +{ + static off_t lastsize; + off_t cursize; + struct timeval now, wait; +#ifndef NO_PROGRESS + struct timeval td; + off_t abbrevsize, bytespersec; + double elapsed; + int ratio, i, remaining, barlength; + + /* + * Work variables for progress bar. + * + * XXX: if the format of the progress bar changes + * (especially the number of characters in the + * `static' portion of it), be sure to update + * these appropriately. + */ +#endif + size_t len; + char buf[256]; /* workspace for progress bar */ +#ifndef NO_PROGRESS +#define BAROVERHEAD 45 /* non `*' portion of progress bar */ + /* + * stars should contain at least + * sizeof(buf) - BAROVERHEAD entries + */ + static const char stars[] = +"*****************************************************************************" +"*****************************************************************************" +"*****************************************************************************"; + +#endif + + if (flag == -1) { + (void)gettimeofday(&start, NULL); + lastupdate = start; + lastsize = restart_point; + } + + (void)gettimeofday(&now, NULL); + cursize = bytes + restart_point; + timersub(&now, &lastupdate, &wait); + if (cursize > lastsize) { + lastupdate = now; + lastsize = cursize; + wait.tv_sec = 0; + } else { +#ifndef STANDALONE_PROGRESS + if (quit_time > 0 && wait.tv_sec > quit_time) { + len = snprintf(buf, sizeof(buf), "\r\n%s: " + "transfer aborted because stalled for %lu sec.\r\n", + getprogname(), (unsigned long)wait.tv_sec); + (void)write(fileno(ttyout), buf, len); + alarmtimer(0); + (void)xsignal(SIGALRM, SIG_DFL); + siglongjmp(toplevel, 1); + } +#endif /* !STANDALONE_PROGRESS */ + } + /* + * Always set the handler even if we are not the foreground process. + */ +#ifdef STANDALONE_PROGRESS + if (progress) { +#else + if (quit_time > 0 || progress) { +#endif /* !STANDALONE_PROGRESS */ + if (flag == -1) { + (void)xsignal_restart(SIGALRM, updateprogressmeter, 1); + alarmtimer(1); /* set alarm timer for 1 Hz */ + } else if (flag == 1) { + alarmtimer(0); + (void)xsignal(SIGALRM, SIG_DFL); + } + } +#ifndef NO_PROGRESS + if (!progress) + return; + len = 0; + + /* + * print progress bar only if we are foreground process. + */ + if (! foregroundproc()) + return; + + len += snprintf(buf + len, BUFLEFT, "\r"); + if (prefix) + len += snprintf(buf + len, BUFLEFT, "%s", prefix); + if (filesize > 0) { + ratio = (int)((double)cursize * 100.0 / (double)filesize); + ratio = MAX(ratio, 0); + ratio = MIN(ratio, 100); + len += snprintf(buf + len, BUFLEFT, "%3d%% ", ratio); + + /* + * calculate the length of the `*' bar, ensuring that + * the number of stars won't exceed the buffer size + */ + barlength = MIN((int)(sizeof(buf) - 1), ttywidth) - BAROVERHEAD; + if (prefix) + barlength -= (int)strlen(prefix); + if (barlength > 0) { + i = barlength * ratio / 100; + len += snprintf(buf + len, BUFLEFT, + "|%.*s%*s|", i, stars, (int)(barlength - i), ""); + } + } + + abbrevsize = cursize; + for (i = 0; abbrevsize >= 100000 && i < NSUFFIXES; i++) + abbrevsize >>= 10; + if (i == NSUFFIXES) + i--; + len += snprintf(buf + len, BUFLEFT, " " LLFP("5") " %-3s ", + (LLT)abbrevsize, + suffixes[i]); + + timersub(&now, &start, &td); + elapsed = td.tv_sec + (td.tv_usec / 1000000.0); + + bytespersec = 0; + if (bytes > 0) { + bytespersec = bytes; + if (elapsed > 0.0) + bytespersec /= elapsed; + } + for (i = 1; bytespersec >= 1024000 && i < NSUFFIXES; i++) + bytespersec >>= 10; + len += snprintf(buf + len, BUFLEFT, + " " LLFP("3") ".%02d %.2sB/s ", + (LLT)(bytespersec / 1024), + (int)((bytespersec % 1024) * 100 / 1024), + suffixes[i]); + + if (filesize > 0) { + if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) { + len += snprintf(buf + len, BUFLEFT, " --:-- ETA"); + } else if (wait.tv_sec >= STALLTIME) { + len += snprintf(buf + len, BUFLEFT, " - stalled -"); + } else { + remaining = (int) + ((filesize - restart_point) / (bytes / elapsed) - + elapsed); + if (remaining >= 100 * SECSPERHOUR) + len += snprintf(buf + len, BUFLEFT, + " --:-- ETA"); + else { + i = remaining / SECSPERHOUR; + if (i) + len += snprintf(buf + len, BUFLEFT, + "%2d:", i); + else + len += snprintf(buf + len, BUFLEFT, + " "); + i = remaining % SECSPERHOUR; + len += snprintf(buf + len, BUFLEFT, + "%02d:%02d ETA", i / 60, i % 60); + } + } + } + if (flag == 1) + len += snprintf(buf + len, BUFLEFT, "\n"); + (void)write(fileno(ttyout), buf, len); + +#endif /* !NO_PROGRESS */ +} + +#ifndef STANDALONE_PROGRESS +/* + * Display transfer statistics. + * Requires start to be initialised by progressmeter(-1), + * direction to be defined by xfer routines, and filesize and bytes + * to be updated by xfer routines + * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr + * instead of ttyout. + */ +void +ptransfer(int siginfo) +{ + struct timeval now, td, wait; + double elapsed; + off_t bytespersec; + int remaining, hh, i; + size_t len; + + char buf[256]; /* Work variable for transfer status. */ + + if (!verbose && !progress && !siginfo) + return; + + (void)gettimeofday(&now, NULL); + timersub(&now, &start, &td); + elapsed = td.tv_sec + (td.tv_usec / 1000000.0); + bytespersec = 0; + if (bytes > 0) { + bytespersec = bytes; + if (elapsed > 0.0) + bytespersec /= elapsed; + } + len = 0; + len += snprintf(buf + len, BUFLEFT, LLF " byte%s %s in ", + (LLT)bytes, bytes == 1 ? "" : "s", direction); + remaining = (int)elapsed; + if (remaining > SECSPERDAY) { + int days; + + days = remaining / SECSPERDAY; + remaining %= SECSPERDAY; + len += snprintf(buf + len, BUFLEFT, + "%d day%s ", days, days == 1 ? "" : "s"); + } + hh = remaining / SECSPERHOUR; + remaining %= SECSPERHOUR; + if (hh) + len += snprintf(buf + len, BUFLEFT, "%2d:", hh); + len += snprintf(buf + len, BUFLEFT, + "%02d:%02d ", remaining / 60, remaining % 60); + + for (i = 1; bytespersec >= 1024000 && i < NSUFFIXES; i++) + bytespersec >>= 10; + if (i == NSUFFIXES) + i--; + len += snprintf(buf + len, BUFLEFT, "(" LLF ".%02d %.2sB/s)", + (LLT)(bytespersec / 1024), + (int)((bytespersec % 1024) * 100 / 1024), + suffixes[i]); + + if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 + && bytes + restart_point <= filesize) { + remaining = (int)((filesize - restart_point) / + (bytes / elapsed) - elapsed); + hh = remaining / SECSPERHOUR; + remaining %= SECSPERHOUR; + len += snprintf(buf + len, BUFLEFT, " ETA: "); + if (hh) + len += snprintf(buf + len, BUFLEFT, "%2d:", hh); + len += snprintf(buf + len, BUFLEFT, "%02d:%02d", + remaining / 60, remaining % 60); + timersub(&now, &lastupdate, &wait); + if (wait.tv_sec >= STALLTIME) + len += snprintf(buf + len, BUFLEFT, " (stalled)"); + } + len += snprintf(buf + len, BUFLEFT, "\n"); + (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, len); +} + +/* + * SIG{INFO,QUIT} handler to print transfer stats if a transfer is in progress + */ +void +psummary(int notused) +{ + int oerrno = errno; + + if (bytes > 0) { + if (fromatty) + write(fileno(ttyout), "\n", 1); + ptransfer(1); + } + errno = oerrno; +} +#endif /* !STANDALONE_PROGRESS */ + + +/* + * Set the SIGALRM interval timer for wait seconds, 0 to disable. + */ +void +alarmtimer(int wait) +{ + struct itimerval itv; + + itv.it_value.tv_sec = wait; + itv.it_value.tv_usec = 0; + itv.it_interval = itv.it_value; + setitimer(ITIMER_REAL, &itv, NULL); +} + + +/* + * Install a POSIX signal handler, allowing the invoker to set whether + * the signal should be restartable or not + */ +sigfunc +xsignal_restart(int sig, sigfunc func, int restartable) +{ + struct sigaction act, oact; + act.sa_handler = func; + + sigemptyset(&act.sa_mask); +#if defined(SA_RESTART) /* 4.4BSD, Posix(?), SVR4 */ + act.sa_flags = restartable ? SA_RESTART : 0; +#elif defined(SA_INTERRUPT) /* SunOS 4.x */ + act.sa_flags = restartable ? 0 : SA_INTERRUPT; +#else +#error "system must have SA_RESTART or SA_INTERRUPT" +#endif + if (sigaction(sig, &act, &oact) < 0) + return (SIG_ERR); + return (oact.sa_handler); +} + +/* + * Install a signal handler with the `restartable' flag set dependent upon + * which signal is being set. (This is a wrapper to xsignal_restart()) + */ +sigfunc +xsignal(int sig, sigfunc func) +{ + int restartable; + + /* + * Some signals print output or change the state of the process. + * There should be restartable, so that reads and writes are + * not affected. Some signals should cause program flow to change; + * these signals should not be restartable, so that the system call + * will return with EINTR, and the program will go do something + * different. If the signal handler calls longjmp() or siglongjmp(), + * it doesn't matter if it's restartable. + */ + + switch(sig) { +#ifdef SIGINFO + case SIGINFO: +#endif + case SIGQUIT: + case SIGUSR1: + case SIGUSR2: + case SIGWINCH: + restartable = 1; + break; + + case SIGALRM: + case SIGINT: + case SIGPIPE: + restartable = 0; + break; + + default: + /* + * This is unpleasant, but I don't know what would be better. + * Right now, this "can't happen" + */ + errx(1, "xsignal_restart: called with signal %d", sig); + } + + return(xsignal_restart(sig, func, restartable)); +} diff --git a/usr.bin/ftp/progressbar.h b/usr.bin/ftp/progressbar.h new file mode 100644 index 000000000..f627e88d5 --- /dev/null +++ b/usr.bin/ftp/progressbar.h @@ -0,0 +1,92 @@ +/* $NetBSD: progressbar.h,v 1.8 2009/04/12 10:18:52 lukem Exp $ */ + +/*- + * Copyright (c) 1996-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef STANDALONE_PROGRESS +#include +#endif /* !STANDALONE_PROGRESS */ + +#ifndef GLOBAL +#define GLOBAL extern +#endif + + +#define STALLTIME 5 /* # of seconds of no xfer before "stalling" */ + +typedef void (*sigfunc)(int); + + +GLOBAL FILE *ttyout; /* stdout, or stderr if retrieving to stdout */ + +GLOBAL int progress; /* display transfer progress bar */ +GLOBAL int ttywidth; /* width of tty */ + +GLOBAL off_t bytes; /* current # of bytes read */ +GLOBAL off_t filesize; /* size of file being transferred */ +GLOBAL off_t restart_point; /* offset to restart transfer */ +GLOBAL char *prefix; /* Text written left of progress bar */ + + +#ifndef STANDALONE_PROGRESS +GLOBAL int fromatty; /* input is from a terminal */ +GLOBAL int verbose; /* print messages coming back from server */ +GLOBAL int quit_time; /* maximum time to wait if stalled */ + +GLOBAL const char *direction; /* direction transfer is occurring */ + +GLOBAL sigjmp_buf toplevel; /* non-local goto stuff for cmd scanner */ +#endif /* !STANDALONE_PROGRESS */ + +int foregroundproc(void); +void alarmtimer(int); +void progressmeter(int); +sigfunc xsignal(int, sigfunc); +sigfunc xsignal_restart(int, sigfunc, int); + +#ifndef STANDALONE_PROGRESS +void psummary(int); +void ptransfer(int); +#endif /* !STANDALONE_PROGRESS */ + +#ifdef NO_LONG_LONG +# define LLF "%ld" +# define LLFP(x) "%" x "ld" +# define LLT long +# define ULLF "%lu" +# define ULLFP(x) "%" x "lu" +# define ULLT unsigned long +#else +# define LLF "%lld" +# define LLFP(x) "%" x "lld" +# define LLT long long +# define ULLF "%llu" +# define ULLFP(x) "%" x "llu" +# define ULLT unsigned long long +#endif diff --git a/usr.bin/ftp/ruserpass.c b/usr.bin/ftp/ruserpass.c new file mode 100644 index 000000000..e43405278 --- /dev/null +++ b/usr.bin/ftp/ruserpass.c @@ -0,0 +1,318 @@ +/* $NetBSD: ruserpass.c,v 1.33 2007/04/17 05:52:04 lukem Exp $ */ + +/* + * Copyright (c) 1985, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ruserpass.c 8.4 (Berkeley) 4/27/95"; +#else +__RCSID("$NetBSD: ruserpass.c,v 1.33 2007/04/17 05:52:04 lukem Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftp_var.h" + +static int token(void); +static FILE *cfile; + +#define DEFAULT 1 +#define LOGIN 2 +#define PASSWD 3 +#define ACCOUNT 4 +#define MACDEF 5 +#define ID 10 +#define MACH 11 + +static char tokval[100]; + +static struct toktab { + const char *tokstr; + int tval; +} toktab[] = { + { "default", DEFAULT }, + { "login", LOGIN }, + { "password", PASSWD }, + { "passwd", PASSWD }, + { "account", ACCOUNT }, + { "machine", MACH }, + { "macdef", MACDEF }, + { NULL, 0 } +}; + +int +ruserpass(const char *host, char **aname, char **apass, char **aacct) +{ + char *tmp; + const char *mydomain; + char myname[MAXHOSTNAMELEN + 1]; + int t, i, c, usedefault = 0; + struct stat stb; + + if (netrc[0] == '\0') + return (0); + cfile = fopen(netrc, "r"); + if (cfile == NULL) { + if (errno != ENOENT) + warn("Can't read `%s'", netrc); + return (0); + } + if (gethostname(myname, sizeof(myname)) < 0) + myname[0] = '\0'; + myname[sizeof(myname) - 1] = '\0'; + if ((mydomain = strchr(myname, '.')) == NULL) + mydomain = ""; + next: + while ((t = token()) > 0) switch(t) { + + case DEFAULT: + usedefault = 1; + /* FALL THROUGH */ + + case MACH: + if (!usedefault) { + if ((t = token()) == -1) + goto bad; + if (t != ID) + continue; + /* + * Allow match either for user's input host name + * or official hostname. Also allow match of + * incompletely-specified host in local domain. + */ + if (strcasecmp(host, tokval) == 0) + goto match; + if (strcasecmp(hostname, tokval) == 0) + goto match; + if ((tmp = strchr(hostname, '.')) != NULL && + strcasecmp(tmp, mydomain) == 0 && + strncasecmp(hostname, tokval, tmp-hostname) == 0 && + tokval[tmp - hostname] == '\0') + goto match; + if ((tmp = strchr(host, '.')) != NULL && + strcasecmp(tmp, mydomain) == 0 && + strncasecmp(host, tokval, tmp - host) == 0 && + tokval[tmp - host] == '\0') + goto match; + continue; + } + match: + while ((t = token()) > 0 && + t != MACH && t != DEFAULT) switch(t) { + + case LOGIN: + if ((t = token()) == -1) + goto bad; + if (t) { + if (*aname == NULL) + *aname = ftp_strdup(tokval); + else { + if (strcmp(*aname, tokval)) + goto next; + } + } + break; + case PASSWD: + if ((*aname == NULL || strcmp(*aname, "anonymous")) && + fstat(fileno(cfile), &stb) >= 0 && + (stb.st_mode & 077) != 0) { + warnx("Error: .netrc file is readable by others"); + warnx("Remove password or make file unreadable by others"); + goto bad; + } + if ((t = token()) == -1) + goto bad; + if (t && *apass == NULL) + *apass = ftp_strdup(tokval); + break; + case ACCOUNT: + if (fstat(fileno(cfile), &stb) >= 0 + && (stb.st_mode & 077) != 0) { + warnx("Error: .netrc file is readable by others"); + warnx("Remove account or make file unreadable by others"); + goto bad; + } + if ((t = token()) == -1) + goto bad; + if (t && *aacct == NULL) + *aacct = ftp_strdup(tokval); + break; + case MACDEF: + if (proxy) { + (void)fclose(cfile); + return (0); + } + while ((c = getc(cfile)) != EOF) + if (c != ' ' && c != '\t') + break; + if (c == EOF || c == '\n') { + fputs("Missing macdef name argument.\n", + ttyout); + goto bad; + } + if (macnum == 16) { + fputs( + "Limit of 16 macros have already been defined.\n", + ttyout); + goto bad; + } + tmp = macros[macnum].mac_name; + *tmp++ = c; + for (i = 0; i < 8 && (c = getc(cfile)) != EOF && + !isspace(c); ++i) { + *tmp++ = c; + } + if (c == EOF) { + fputs( + "Macro definition missing null line terminator.\n", + ttyout); + goto bad; + } + *tmp = '\0'; + if (c != '\n') { + while ((c = getc(cfile)) != EOF && c != '\n'); + } + if (c == EOF) { + fputs( + "Macro definition missing null line terminator.\n", + ttyout); + goto bad; + } + if (macnum == 0) { + macros[macnum].mac_start = macbuf; + } + else { + macros[macnum].mac_start = + macros[macnum-1].mac_end + 1; + } + tmp = macros[macnum].mac_start; + while (tmp != macbuf + 4096) { + if ((c = getc(cfile)) == EOF) { + fputs( + "Macro definition missing null line terminator.\n", + ttyout); + goto bad; + } + *tmp = c; + if (*tmp == '\n') { + if (tmp == macros[macnum].mac_start) { + macros[macnum++].mac_end = tmp; + break; + } else if (*(tmp - 1) == '\0') { + macros[macnum++].mac_end = + tmp - 1; + break; + } + *tmp = '\0'; + } + tmp++; + } + if (tmp == macbuf + 4096) { + fputs("4K macro buffer exceeded.\n", + ttyout); + goto bad; + } + break; + default: + warnx("Unknown .netrc keyword `%s'", tokval); + break; + } + goto done; + } + done: + if (t == -1) + goto bad; + (void)fclose(cfile); + return (0); + bad: + (void)fclose(cfile); + return (-1); +} + +static int +token(void) +{ + char *cp; + int c; + struct toktab *t; + + if (feof(cfile) || ferror(cfile)) + return (0); + while ((c = getc(cfile)) != EOF && + (c == '\n' || c == '\t' || c == ' ' || c == ',')) + continue; + if (c == EOF) + return (0); + cp = tokval; + if (c == '"') { + while ((c = getc(cfile)) != EOF && c != '"') { + if (c == '\\') + if ((c = getc(cfile)) == EOF) + break; + *cp++ = c; + if (cp == tokval + sizeof(tokval)) { + warnx("Token in .netrc too long"); + return (-1); + } + } + } else { + *cp++ = c; + while ((c = getc(cfile)) != EOF + && c != '\n' && c != '\t' && c != ' ' && c != ',') { + if (c == '\\') + if ((c = getc(cfile)) == EOF) + break; + *cp++ = c; + if (cp == tokval + sizeof(tokval)) { + warnx("Token in .netrc too long"); + return (-1); + } + } + } + *cp = 0; + if (tokval[0] == 0) + return (0); + for (t = toktab; t->tokstr; t++) + if (!strcmp(t->tokstr, tokval)) + return (t->tval); + return (ID); +} diff --git a/usr.bin/ftp/ssl.c b/usr.bin/ftp/ssl.c new file mode 100644 index 000000000..b34c86469 --- /dev/null +++ b/usr.bin/ftp/ssl.c @@ -0,0 +1,608 @@ +/* $NetBSD: ssl.c,v 1.2 2012/12/24 22:12:28 christos Exp $ */ + +/*- + * Copyright (c) 1998-2004 Dag-Erling Coïdan Smørgrav + * Copyright (c) 2008, 2010 Joerg Sonnenberger + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: common.c,v 1.53 2007/12/19 00:26:36 des Exp $ + */ + +#include +#ifndef lint +__RCSID("$NetBSD: ssl.c,v 1.2 2012/12/24 22:12:28 christos Exp $"); +#endif + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ssl.h" + +extern int quit_time, verbose, ftp_debug; +extern FILE *ttyout; + +struct fetch_connect { + int sd; /* file/socket descriptor */ + char *buf; /* buffer */ + size_t bufsize; /* buffer size */ + size_t bufpos; /* position of buffer */ + size_t buflen; /* length of buffer contents */ + struct { /* data cached after an + interrupted read */ + char *buf; + size_t size; + size_t pos; + size_t len; + } cache; + int issock; + int iserr; + int iseof; + SSL *ssl; /* SSL handle */ +}; + +/* + * Write a vector to a connection w/ timeout + * Note: can modify the iovec. + */ +static ssize_t +fetch_writev(struct fetch_connect *conn, struct iovec *iov, int iovcnt) +{ + struct timeval now, timeout, delta; + fd_set writefds; + ssize_t len, total; + int r; + + if (quit_time > 0) { + FD_ZERO(&writefds); + gettimeofday(&timeout, NULL); + timeout.tv_sec += quit_time; + } + + total = 0; + while (iovcnt > 0) { + while (quit_time > 0 && !FD_ISSET(conn->sd, &writefds)) { + FD_SET(conn->sd, &writefds); + gettimeofday(&now, NULL); + delta.tv_sec = timeout.tv_sec - now.tv_sec; + delta.tv_usec = timeout.tv_usec - now.tv_usec; + if (delta.tv_usec < 0) { + delta.tv_usec += 1000000; + delta.tv_sec--; + } + if (delta.tv_sec < 0) { + errno = ETIMEDOUT; + return -1; + } + errno = 0; + r = select(conn->sd + 1, NULL, &writefds, NULL, &delta); + if (r == -1) { + if (errno == EINTR) + continue; + return -1; + } + } + errno = 0; + if (conn->ssl != NULL) + len = SSL_write(conn->ssl, iov->iov_base, iov->iov_len); + else + len = writev(conn->sd, iov, iovcnt); + if (len == 0) { + /* we consider a short write a failure */ + /* XXX perhaps we shouldn't in the SSL case */ + errno = EPIPE; + return -1; + } + if (len < 0) { + if (errno == EINTR) + continue; + return -1; + } + total += len; + while (iovcnt > 0 && len >= (ssize_t)iov->iov_len) { + len -= iov->iov_len; + iov++; + iovcnt--; + } + if (iovcnt > 0) { + iov->iov_len -= len; + iov->iov_base = (char *)iov->iov_base + len; + } + } + return total; +} + +/* + * Write to a connection w/ timeout + */ +static int +fetch_write(struct fetch_connect *conn, const char *str, size_t len) +{ + struct iovec iov[1]; + + iov[0].iov_base = (char *)__UNCONST(str); + iov[0].iov_len = len; + return fetch_writev(conn, iov, 1); +} + +/* + * Send a formatted line; optionally echo to terminal + */ +int +fetch_printf(struct fetch_connect *conn, const char *fmt, ...) +{ + va_list ap; + size_t len; + char *msg; + int r; + + va_start(ap, fmt); + len = vasprintf(&msg, fmt, ap); + va_end(ap); + + if (msg == NULL) { + errno = ENOMEM; + return -1; + } + + r = fetch_write(conn, msg, len); + free(msg); + return r; +} + +int +fetch_fileno(struct fetch_connect *conn) +{ + + return conn->sd; +} + +int +fetch_error(struct fetch_connect *conn) +{ + + return conn->iserr; +} + +static void +fetch_clearerr(struct fetch_connect *conn) +{ + + conn->iserr = 0; +} + +int +fetch_flush(struct fetch_connect *conn) +{ + int v; + + if (conn->issock) { +#ifdef TCP_NOPUSH + v = 0; + setsockopt(conn->sd, IPPROTO_TCP, TCP_NOPUSH, &v, sizeof(v)); +#endif + v = 1; + setsockopt(conn->sd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); + } + return 0; +} + +/*ARGSUSED*/ +struct fetch_connect * +fetch_open(const char *fname, const char *fmode) +{ + struct fetch_connect *conn; + int fd; + + fd = open(fname, O_RDONLY); /* XXX: fmode */ + if (fd < 0) + return NULL; + + if ((conn = calloc(1, sizeof(*conn))) == NULL) { + close(fd); + return NULL; + } + + conn->sd = fd; + conn->issock = 0; + return conn; +} + +/*ARGSUSED*/ +struct fetch_connect * +fetch_fdopen(int sd, const char *fmode) +{ + struct fetch_connect *conn; +#if defined(SO_NOSIGPIPE) || defined(TCP_NOPUSH) + int opt = 1; +#endif + + if ((conn = calloc(1, sizeof(*conn))) == NULL) + return NULL; + + conn->sd = sd; + conn->issock = 1; + fcntl(sd, F_SETFD, FD_CLOEXEC); +#ifdef SO_NOSIGPIPE + setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof(opt)); +#endif +#ifdef TCP_NOPUSH + setsockopt(sd, IPPROTO_TCP, TCP_NOPUSH, &opt, sizeof(opt)); +#endif + return conn; +} + +int +fetch_close(struct fetch_connect *conn) +{ + int rv = 0; + + if (conn != NULL) { + fetch_flush(conn); + SSL_free(conn->ssl); + rv = close(conn->sd); + if (rv < 0) { + errno = rv; + rv = EOF; + } + free(conn->cache.buf); + free(conn->buf); + free(conn); + } + return rv; +} + +#define FETCH_READ_WAIT -2 +#define FETCH_READ_ERROR -1 + +static ssize_t +fetch_ssl_read(SSL *ssl, void *buf, size_t len) +{ + ssize_t rlen; + int ssl_err; + + rlen = SSL_read(ssl, buf, len); + if (rlen < 0) { + ssl_err = SSL_get_error(ssl, rlen); + if (ssl_err == SSL_ERROR_WANT_READ || + ssl_err == SSL_ERROR_WANT_WRITE) { + return FETCH_READ_WAIT; + } + ERR_print_errors_fp(ttyout); + return FETCH_READ_ERROR; + } + return rlen; +} + +static ssize_t +fetch_nonssl_read(int sd, void *buf, size_t len) +{ + ssize_t rlen; + + rlen = read(sd, buf, len); + if (rlen < 0) { + if (errno == EAGAIN || errno == EINTR) + return FETCH_READ_WAIT; + return FETCH_READ_ERROR; + } + return rlen; +} + +/* + * Cache some data that was read from a socket but cannot be immediately + * returned because of an interrupted system call. + */ +static int +fetch_cache_data(struct fetch_connect *conn, char *src, size_t nbytes) +{ + + if (conn->cache.size < nbytes) { + char *tmp = realloc(conn->cache.buf, nbytes); + if (tmp == NULL) + return -1; + + conn->cache.buf = tmp; + conn->cache.size = nbytes; + } + + memcpy(conn->cache.buf, src, nbytes); + conn->cache.len = nbytes; + conn->cache.pos = 0; + return 0; +} + +ssize_t +fetch_read(void *ptr, size_t size, size_t nmemb, struct fetch_connect *conn) +{ + struct timeval now, timeout, delta; + fd_set readfds; + ssize_t rlen, total; + size_t len; + char *start, *buf; + + if (quit_time > 0) { + gettimeofday(&timeout, NULL); + timeout.tv_sec += quit_time; + } + + total = 0; + start = buf = ptr; + len = size * nmemb; + + if (conn->cache.len > 0) { + /* + * The last invocation of fetch_read was interrupted by a + * signal after some data had been read from the socket. Copy + * the cached data into the supplied buffer before trying to + * read from the socket again. + */ + total = (conn->cache.len < len) ? conn->cache.len : len; + memcpy(buf, conn->cache.buf, total); + + conn->cache.len -= total; + conn->cache.pos += total; + len -= total; + buf += total; + } + + while (len > 0) { + /* + * The socket is non-blocking. Instead of the canonical + * select() -> read(), we do the following: + * + * 1) call read() or SSL_read(). + * 2) if an error occurred, return -1. + * 3) if we received data but we still expect more, + * update our counters and loop. + * 4) if read() or SSL_read() signaled EOF, return. + * 5) if we did not receive any data but we're not at EOF, + * call select(). + * + * In the SSL case, this is necessary because if we + * receive a close notification, we have to call + * SSL_read() one additional time after we've read + * everything we received. + * + * In the non-SSL case, it may improve performance (very + * slightly) when reading small amounts of data. + */ + if (conn->ssl != NULL) + rlen = fetch_ssl_read(conn->ssl, buf, len); + else + rlen = fetch_nonssl_read(conn->sd, buf, len); + if (rlen == 0) { + break; + } else if (rlen > 0) { + len -= rlen; + buf += rlen; + total += rlen; + continue; + } else if (rlen == FETCH_READ_ERROR) { + if (errno == EINTR) + fetch_cache_data(conn, start, total); + return -1; + } + FD_ZERO(&readfds); + while (!FD_ISSET(conn->sd, &readfds)) { + FD_SET(conn->sd, &readfds); + if (quit_time > 0) { + gettimeofday(&now, NULL); + if (!timercmp(&timeout, &now, >)) { + errno = ETIMEDOUT; + return -1; + } + timersub(&timeout, &now, &delta); + } + errno = 0; + if (select(conn->sd + 1, &readfds, NULL, NULL, + quit_time > 0 ? &delta : NULL) < 0) { + if (errno == EINTR) + continue; + return -1; + } + } + } + return total; +} + +#define MIN_BUF_SIZE 1024 + +/* + * Read a line of text from a connection w/ timeout + */ +char * +fetch_getln(char *str, int size, struct fetch_connect *conn) +{ + size_t tmpsize; + ssize_t len; + char c; + + if (conn->buf == NULL) { + if ((conn->buf = malloc(MIN_BUF_SIZE)) == NULL) { + errno = ENOMEM; + conn->iserr = 1; + return NULL; + } + conn->bufsize = MIN_BUF_SIZE; + } + + if (conn->iserr || conn->iseof) + return NULL; + + if (conn->buflen - conn->bufpos > 0) + goto done; + + conn->buf[0] = '\0'; + conn->bufpos = 0; + conn->buflen = 0; + do { + len = fetch_read(&c, sizeof(c), 1, conn); + if (len == -1) { + conn->iserr = 1; + return NULL; + } + if (len == 0) { + conn->iseof = 1; + break; + } + conn->buf[conn->buflen++] = c; + if (conn->buflen == conn->bufsize) { + char *tmp = conn->buf; + tmpsize = conn->bufsize * 2 + 1; + if ((tmp = realloc(tmp, tmpsize)) == NULL) { + errno = ENOMEM; + conn->iserr = 1; + return NULL; + } + conn->buf = tmp; + conn->bufsize = tmpsize; + } + } while (c != '\n'); + + if (conn->buflen == 0) + return NULL; + done: + tmpsize = MIN(size - 1, (int)(conn->buflen - conn->bufpos)); + memcpy(str, conn->buf + conn->bufpos, tmpsize); + str[tmpsize] = '\0'; + conn->bufpos += tmpsize; + return str; +} + +int +fetch_getline(struct fetch_connect *conn, char *buf, size_t buflen, + const char **errormsg) +{ + size_t len; + int rv; + + if (fetch_getln(buf, buflen, conn) == NULL) { + if (conn->iseof) { /* EOF */ + rv = -2; + if (errormsg) + *errormsg = "\nEOF received"; + } else { /* error */ + rv = -1; + if (errormsg) + *errormsg = "Error encountered"; + } + fetch_clearerr(conn); + return rv; + } + len = strlen(buf); + if (buf[len - 1] == '\n') { /* clear any trailing newline */ + buf[--len] = '\0'; + } else if (len == buflen - 1) { /* line too long */ + while (1) { + char c; + ssize_t rlen = fetch_read(&c, sizeof(c), 1, conn); + if (rlen <= 0 || c == '\n') + break; + } + if (errormsg) + *errormsg = "Input line is too long"; + fetch_clearerr(conn); + return -3; + } + if (errormsg) + *errormsg = NULL; + return len; +} + +void * +fetch_start_ssl(int sock) +{ + SSL *ssl; + SSL_CTX *ctx; + int ret, ssl_err; + + /* Init the SSL library and context */ + if (!SSL_library_init()){ + fprintf(ttyout, "SSL library init failed\n"); + return NULL; + } + + SSL_load_error_strings(); + + ctx = SSL_CTX_new(SSLv23_client_method()); + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + + ssl = SSL_new(ctx); + if (ssl == NULL){ + fprintf(ttyout, "SSL context creation failed\n"); + SSL_CTX_free(ctx); + return NULL; + } + SSL_set_fd(ssl, sock); + while ((ret = SSL_connect(ssl)) == -1) { + ssl_err = SSL_get_error(ssl, ret); + if (ssl_err != SSL_ERROR_WANT_READ && + ssl_err != SSL_ERROR_WANT_WRITE) { + ERR_print_errors_fp(ttyout); + SSL_free(ssl); + return NULL; + } + } + + if (ftp_debug && verbose) { + X509 *cert; + X509_NAME *name; + char *str; + + fprintf(ttyout, "SSL connection established using %s\n", + SSL_get_cipher(ssl)); + cert = SSL_get_peer_certificate(ssl); + name = X509_get_subject_name(cert); + str = X509_NAME_oneline(name, 0, 0); + fprintf(ttyout, "Certificate subject: %s\n", str); + free(str); + name = X509_get_issuer_name(cert); + str = X509_NAME_oneline(name, 0, 0); + fprintf(ttyout, "Certificate issuer: %s\n", str); + free(str); + } + + return ssl; +} + + +void +fetch_set_ssl(struct fetch_connect *conn, void *ssl) +{ + conn->ssl = ssl; +} diff --git a/usr.bin/ftp/ssl.h b/usr.bin/ftp/ssl.h new file mode 100644 index 000000000..9ecc75975 --- /dev/null +++ b/usr.bin/ftp/ssl.h @@ -0,0 +1,62 @@ +/* $NetBSD: ssl.h,v 1.1 2012/12/21 18:07:36 christos Exp $ */ + +/*- + * Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifdef WITH_SSL + +#define FETCH struct fetch_connect +struct fetch_connect; + +int fetch_printf(struct fetch_connect *, const char *fmt, ...); +int fetch_fileno(struct fetch_connect *); +int fetch_error(struct fetch_connect *); +int fetch_flush(struct fetch_connect *); +struct fetch_connect *fetch_open(const char *, const char *); +struct fetch_connect *fetch_fdopen(int, const char *); +int fetch_close(struct fetch_connect *); +ssize_t fetch_read(void *, size_t, size_t, struct fetch_connect *); +char *fetch_getln(char *, int, struct fetch_connect *); +int fetch_getline(struct fetch_connect *, char *, size_t, const char **); +void fetch_set_ssl(struct fetch_connect *, void *); +void *fetch_start_ssl(int); + +#else /* !WITH_SSL */ + +#define FETCH FILE + +#define fetch_printf fprintf +#define fetch_fileno fileno +#define fetch_error ferror +#define fetch_flush fflush +#define fetch_open fopen +#define fetch_fdopen fdopen +#define fetch_close fclose +#define fetch_read fread +#define fetch_getln fgets +#define fetch_getline get_line +#define fetch_set_ssl(a, b) + +#endif /* !WITH_SSL */ diff --git a/usr.bin/ftp/util.c b/usr.bin/ftp/util.c new file mode 100644 index 000000000..ee610c627 --- /dev/null +++ b/usr.bin/ftp/util.c @@ -0,0 +1,1543 @@ +/* $NetBSD: util.c,v 1.158 2013/02/19 23:29:15 dsl Exp $ */ + +/*- + * Copyright (c) 1997-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Copyright (c) 1985, 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__RCSID("$NetBSD: util.c,v 1.158 2013/02/19 23:29:15 dsl Exp $"); +#endif /* not lint */ + +/* + * FTP User Program -- Misc support routines + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ftp_var.h" + +/* + * Connect to peer server and auto-login, if possible. + */ +void +setpeer(int argc, char *argv[]) +{ + char *host; + const char *port; + + if (argc == 0) + goto usage; + if (connected) { + fprintf(ttyout, "Already connected to %s, use close first.\n", + hostname); + code = -1; + return; + } + if (argc < 2) + (void)another(&argc, &argv, "to"); + if (argc < 2 || argc > 3) { + usage: + UPRINTF("usage: %s host-name [port]\n", argv[0]); + code = -1; + return; + } + if (gatemode) + port = gateport; + else + port = ftpport; + if (argc > 2) + port = argv[2]; + + if (gatemode) { + if (gateserver == NULL || *gateserver == '\0') + errx(1, "main: gateserver not defined"); + host = hookup(gateserver, port); + } else + host = hookup(argv[1], port); + + if (host) { + if (gatemode && verbose) { + fprintf(ttyout, + "Connecting via pass-through server %s\n", + gateserver); + } + + connected = 1; + /* + * Set up defaults for FTP. + */ + (void)strlcpy(typename, "ascii", sizeof(typename)); + type = TYPE_A; + curtype = TYPE_A; + (void)strlcpy(formname, "non-print", sizeof(formname)); + form = FORM_N; + (void)strlcpy(modename, "stream", sizeof(modename)); + mode = MODE_S; + (void)strlcpy(structname, "file", sizeof(structname)); + stru = STRU_F; + (void)strlcpy(bytename, "8", sizeof(bytename)); + bytesize = 8; + if (autologin) + (void)ftp_login(argv[1], NULL, NULL); + } +} + +static void +parse_feat(const char *fline) +{ + + /* + * work-around broken ProFTPd servers that can't + * even obey RFC 2389. + */ + while (*fline && isspace((int)*fline)) + fline++; + + if (strcasecmp(fline, "MDTM") == 0) + features[FEAT_MDTM] = 1; + else if (strncasecmp(fline, "MLST", sizeof("MLST") - 1) == 0) { + features[FEAT_MLST] = 1; + } else if (strcasecmp(fline, "REST STREAM") == 0) + features[FEAT_REST_STREAM] = 1; + else if (strcasecmp(fline, "SIZE") == 0) + features[FEAT_SIZE] = 1; + else if (strcasecmp(fline, "TVFS") == 0) + features[FEAT_TVFS] = 1; +} + +/* + * Determine the remote system type (SYST) and features (FEAT). + * Call after a successful login (i.e, connected = -1) + */ +void +getremoteinfo(void) +{ + int overbose, i; + + overbose = verbose; + if (ftp_debug == 0) + verbose = -1; + + /* determine remote system type */ + if (command("SYST") == COMPLETE) { + if (overbose) { + int os_len = strcspn(reply_string + 4, " \r\n\t"); + if (os_len > 1 && reply_string[4 + os_len - 1] == '.') + os_len--; + fprintf(ttyout, "Remote system type is %.*s.\n", + os_len, reply_string + 4); + } + /* + * Decide whether we should default to bninary. + * Traditionally checked for "215 UNIX Type: L8", but + * some printers report "Linux" ! so be more forgiving. + * In reality we probably almost never want text any more. + */ + if (!strncasecmp(reply_string + 4, "unix", 4) || + !strncasecmp(reply_string + 4, "linux", 5)) { + if (proxy) + unix_proxy = 1; + else + unix_server = 1; + /* + * Set type to 0 (not specified by user), + * meaning binary by default, but don't bother + * telling server. We can use binary + * for text files unless changed by the user. + */ + type = 0; + (void)strlcpy(typename, "binary", sizeof(typename)); + if (overbose) + fprintf(ttyout, + "Using %s mode to transfer files.\n", + typename); + } else { + if (proxy) + unix_proxy = 0; + else + unix_server = 0; + if (overbose && + !strncmp(reply_string, "215 TOPS20", 10)) + fputs( +"Remember to set tenex mode when transferring binary files from this machine.\n", + ttyout); + } + } + + /* determine features (if any) */ + for (i = 0; i < FEAT_max; i++) + features[i] = -1; + reply_callback = parse_feat; + if (command("FEAT") == COMPLETE) { + for (i = 0; i < FEAT_max; i++) { + if (features[i] == -1) + features[i] = 0; + } + features[FEAT_FEAT] = 1; + } else + features[FEAT_FEAT] = 0; +#ifndef NO_DEBUG + if (ftp_debug) { +#define DEBUG_FEAT(x) fprintf(ttyout, "features[" #x "] = %d\n", features[(x)]) + DEBUG_FEAT(FEAT_FEAT); + DEBUG_FEAT(FEAT_MDTM); + DEBUG_FEAT(FEAT_MLST); + DEBUG_FEAT(FEAT_REST_STREAM); + DEBUG_FEAT(FEAT_SIZE); + DEBUG_FEAT(FEAT_TVFS); +#undef DEBUG_FEAT + } +#endif + reply_callback = NULL; + + verbose = overbose; +} + +/* + * Reset the various variables that indicate connection state back to + * disconnected settings. + * The caller is responsible for issuing any commands to the remote server + * to perform a clean shutdown before this is invoked. + */ +void +cleanuppeer(void) +{ + + if (cout) + (void)fclose(cout); + cout = NULL; + connected = 0; + unix_server = 0; + unix_proxy = 0; + /* + * determine if anonftp was specifically set with -a + * (1), or implicitly set by auto_fetch() (2). in the + * latter case, disable after the current xfer + */ + if (anonftp == 2) + anonftp = 0; + data = -1; + epsv4bad = 0; + epsv6bad = 0; + if (username) + free(username); + username = NULL; + if (!proxy) + macnum = 0; +} + +/* + * Top-level signal handler for interrupted commands. + */ +void +intr(int signo) +{ + + sigint_raised = 1; + alarmtimer(0); + if (fromatty) + write(fileno(ttyout), "\n", 1); + siglongjmp(toplevel, 1); +} + +/* + * Signal handler for lost connections; cleanup various elements of + * the connection state, and call cleanuppeer() to finish it off. + */ +void +lostpeer(int dummy) +{ + int oerrno = errno; + + alarmtimer(0); + if (connected) { + if (cout != NULL) { + (void)shutdown(fileno(cout), 1+1); + (void)fclose(cout); + cout = NULL; + } + if (data >= 0) { + (void)shutdown(data, 1+1); + (void)close(data); + data = -1; + } + connected = 0; + } + pswitch(1); + if (connected) { + if (cout != NULL) { + (void)shutdown(fileno(cout), 1+1); + (void)fclose(cout); + cout = NULL; + } + connected = 0; + } + proxflag = 0; + pswitch(0); + cleanuppeer(); + errno = oerrno; +} + + +/* + * Login to remote host, using given username & password if supplied. + * Return non-zero if successful. + */ +int +ftp_login(const char *host, const char *luser, const char *lpass) +{ + char tmp[80]; + char *fuser, *pass, *facct, *p; + char emptypass[] = ""; + const char *errormsg; + int n, aflag, rval, nlen; + + aflag = rval = 0; + fuser = pass = facct = NULL; + if (luser) + fuser = ftp_strdup(luser); + if (lpass) + pass = ftp_strdup(lpass); + + DPRINTF("ftp_login: user `%s' pass `%s' host `%s'\n", + STRorNULL(fuser), STRorNULL(pass), STRorNULL(host)); + + /* + * Set up arguments for an anonymous FTP session, if necessary. + */ + if (anonftp) { + FREEPTR(fuser); + fuser = ftp_strdup("anonymous"); /* as per RFC 1635 */ + FREEPTR(pass); + pass = ftp_strdup(getoptionvalue("anonpass")); + } + + if (ruserpass(host, &fuser, &pass, &facct) < 0) { + code = -1; + goto cleanup_ftp_login; + } + + while (fuser == NULL) { + if (localname) + fprintf(ttyout, "Name (%s:%s): ", host, localname); + else + fprintf(ttyout, "Name (%s): ", host); + errormsg = NULL; + nlen = get_line(stdin, tmp, sizeof(tmp), &errormsg); + if (nlen < 0) { + fprintf(ttyout, "%s; %s aborted.\n", errormsg, "login"); + code = -1; + goto cleanup_ftp_login; + } else if (nlen == 0) { + fuser = ftp_strdup(localname); + } else { + fuser = ftp_strdup(tmp); + } + } + + if (gatemode) { + char *nuser; + size_t len; + + len = strlen(fuser) + 1 + strlen(host) + 1; + nuser = ftp_malloc(len); + (void)strlcpy(nuser, fuser, len); + (void)strlcat(nuser, "@", len); + (void)strlcat(nuser, host, len); + FREEPTR(fuser); + fuser = nuser; + } + + n = command("USER %s", fuser); + if (n == CONTINUE) { + if (pass == NULL) { + p = getpass("Password: "); + if (p == NULL) + p = emptypass; + pass = ftp_strdup(p); + memset(p, 0, strlen(p)); + } + n = command("PASS %s", pass); + memset(pass, 0, strlen(pass)); + } + if (n == CONTINUE) { + aflag++; + if (facct == NULL) { + p = getpass("Account: "); + if (p == NULL) + p = emptypass; + facct = ftp_strdup(p); + memset(p, 0, strlen(p)); + } + if (facct[0] == '\0') { + warnx("Login failed"); + goto cleanup_ftp_login; + } + n = command("ACCT %s", facct); + memset(facct, 0, strlen(facct)); + } + if ((n != COMPLETE) || + (!aflag && facct != NULL && command("ACCT %s", facct) != COMPLETE)) { + warnx("Login failed"); + goto cleanup_ftp_login; + } + rval = 1; + username = ftp_strdup(fuser); + if (proxy) + goto cleanup_ftp_login; + + connected = -1; + getremoteinfo(); + for (n = 0; n < macnum; ++n) { + if (!strcmp("init", macros[n].mac_name)) { + (void)strlcpy(line, "$init", sizeof(line)); + makeargv(); + domacro(margc, margv); + break; + } + } + updatelocalcwd(); + updateremotecwd(); + + cleanup_ftp_login: + FREEPTR(fuser); + if (pass != NULL) + memset(pass, 0, strlen(pass)); + FREEPTR(pass); + if (facct != NULL) + memset(facct, 0, strlen(facct)); + FREEPTR(facct); + return (rval); +} + +/* + * `another' gets another argument, and stores the new argc and argv. + * It reverts to the top level (via intr()) on EOF/error. + * + * Returns false if no new arguments have been added. + */ +int +another(int *pargc, char ***pargv, const char *aprompt) +{ + const char *errormsg; + int ret, nlen; + size_t len; + + len = strlen(line); + if (len >= sizeof(line) - 3) { + fputs("Sorry, arguments too long.\n", ttyout); + intr(0); + } + fprintf(ttyout, "(%s) ", aprompt); + line[len++] = ' '; + errormsg = NULL; + nlen = get_line(stdin, line + len, sizeof(line)-len, &errormsg); + if (nlen < 0) { + fprintf(ttyout, "%s; %s aborted.\n", errormsg, "operation"); + intr(0); + } + len += nlen; + makeargv(); + ret = margc > *pargc; + *pargc = margc; + *pargv = margv; + return (ret); +} + +/* + * glob files given in argv[] from the remote server. + * if errbuf isn't NULL, store error messages there instead + * of writing to the screen. + */ +char * +remglob(char *argv[], int doswitch, const char **errbuf) +{ + static char buf[MAXPATHLEN]; + static FILE *ftemp = NULL; + static char **args; + char temp[MAXPATHLEN]; + int oldverbose, oldhash, oldprogress, fd; + char *cp; + const char *rmode; + size_t len; + + if (!mflag || !connected) { + if (!doglob) + args = NULL; + else { + if (ftemp) { + (void)fclose(ftemp); + ftemp = NULL; + } + } + return (NULL); + } + if (!doglob) { + if (args == NULL) + args = argv; + if ((cp = *++args) == NULL) + args = NULL; + return (cp); + } + if (ftemp == NULL) { + len = strlcpy(temp, tmpdir, sizeof(temp)); + if (temp[len - 1] != '/') + (void)strlcat(temp, "/", sizeof(temp)); + (void)strlcat(temp, TMPFILE, sizeof(temp)); + if ((fd = mkstemp(temp)) < 0) { + warn("Unable to create temporary file `%s'", temp); + return (NULL); + } + close(fd); + oldverbose = verbose; + verbose = (errbuf != NULL) ? -1 : 0; + oldhash = hash; + oldprogress = progress; + hash = 0; + progress = 0; + if (doswitch) + pswitch(!proxy); + for (rmode = "w"; *++argv != NULL; rmode = "a") + recvrequest("NLST", temp, *argv, rmode, 0, 0); + if ((code / 100) != COMPLETE) { + if (errbuf != NULL) + *errbuf = reply_string; + } + if (doswitch) + pswitch(!proxy); + verbose = oldverbose; + hash = oldhash; + progress = oldprogress; + ftemp = fopen(temp, "r"); + (void)unlink(temp); + if (ftemp == NULL) { + if (errbuf == NULL) + warnx("Can't find list of remote files"); + else + *errbuf = + "Can't find list of remote files"; + return (NULL); + } + } + if (fgets(buf, sizeof(buf), ftemp) == NULL) { + (void)fclose(ftemp); + ftemp = NULL; + return (NULL); + } + if ((cp = strchr(buf, '\n')) != NULL) + *cp = '\0'; + return (buf); +} + +/* + * Glob a local file name specification with the expectation of a single + * return value. Can't control multiple values being expanded from the + * expression, we return only the first. + * Returns NULL on error, or a pointer to a buffer containing the filename + * that's the caller's responsiblity to free(3) when finished with. + */ +char * +globulize(const char *pattern) +{ + glob_t gl; + int flags; + char *p; + + if (!doglob) + return (ftp_strdup(pattern)); + + flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; + memset(&gl, 0, sizeof(gl)); + if (glob(pattern, flags, NULL, &gl) || gl.gl_pathc == 0) { + warnx("Glob pattern `%s' not found", pattern); + globfree(&gl); + return (NULL); + } + p = ftp_strdup(gl.gl_pathv[0]); + globfree(&gl); + return (p); +} + +/* + * determine size of remote file + */ +off_t +remotesize(const char *file, int noisy) +{ + int overbose, r; + off_t size; + + overbose = verbose; + size = -1; + if (ftp_debug == 0) + verbose = -1; + if (! features[FEAT_SIZE]) { + if (noisy) + fprintf(ttyout, + "SIZE is not supported by remote server.\n"); + goto cleanup_remotesize; + } + r = command("SIZE %s", file); + if (r == COMPLETE) { + char *cp, *ep; + + cp = strchr(reply_string, ' '); + if (cp != NULL) { + cp++; + size = STRTOLL(cp, &ep, 10); + if (*ep != '\0' && !isspace((unsigned char)*ep)) + size = -1; + } + } else { + if (r == ERROR && code == 500 && features[FEAT_SIZE] == -1) + features[FEAT_SIZE] = 0; + if (noisy && ftp_debug == 0) { + fputs(reply_string, ttyout); + putc('\n', ttyout); + } + } + cleanup_remotesize: + verbose = overbose; + return (size); +} + +/* + * determine last modification time (in GMT) of remote file + */ +time_t +remotemodtime(const char *file, int noisy) +{ + int overbose, ocode, r; + time_t rtime; + + overbose = verbose; + ocode = code; + rtime = -1; + if (ftp_debug == 0) + verbose = -1; + if (! features[FEAT_MDTM]) { + if (noisy) + fprintf(ttyout, + "MDTM is not supported by remote server.\n"); + goto cleanup_parse_time; + } + r = command("MDTM %s", file); + if (r == COMPLETE) { + struct tm timebuf; + char *timestr, *frac; + + /* + * time-val = 14DIGIT [ "." 1*DIGIT ] + * YYYYMMDDHHMMSS[.sss] + * mdtm-response = "213" SP time-val CRLF / error-response + */ + timestr = reply_string + 4; + + /* + * parse fraction. + * XXX: ignored for now + */ + frac = strchr(timestr, '\r'); + if (frac != NULL) + *frac = '\0'; + frac = strchr(timestr, '.'); + if (frac != NULL) + *frac++ = '\0'; + if (strlen(timestr) == 15 && strncmp(timestr, "191", 3) == 0) { + /* + * XXX: Workaround for lame ftpd's that return + * `19100' instead of `2000' + */ + fprintf(ttyout, + "Y2K warning! Incorrect time-val `%s' received from server.\n", + timestr); + timestr++; + timestr[0] = '2'; + timestr[1] = '0'; + fprintf(ttyout, "Converted to `%s'\n", timestr); + } + memset(&timebuf, 0, sizeof(timebuf)); + if (strlen(timestr) != 14 || + (strptime(timestr, "%Y%m%d%H%M%S", &timebuf) == NULL)) { + bad_parse_time: + fprintf(ttyout, "Can't parse time `%s'.\n", timestr); + goto cleanup_parse_time; + } + timebuf.tm_isdst = -1; + rtime = timegm(&timebuf); + if (rtime == -1) { + if (noisy || ftp_debug != 0) + goto bad_parse_time; + else + goto cleanup_parse_time; + } else { + DPRINTF("remotemodtime: parsed time `%s' as " LLF + ", %s", + timestr, (LLT)rtime, + rfc2822time(localtime(&rtime))); + } + } else { + if (r == ERROR && code == 500 && features[FEAT_MDTM] == -1) + features[FEAT_MDTM] = 0; + if (noisy && ftp_debug == 0) { + fputs(reply_string, ttyout); + putc('\n', ttyout); + } + } + cleanup_parse_time: + verbose = overbose; + if (rtime == -1) + code = ocode; + return (rtime); +} + +/* + * Format tm in an RFC 2822 compatible manner, with a trailing \n. + * Returns a pointer to a static string containing the result. + */ +const char * +rfc2822time(const struct tm *tm) +{ + static char result[50]; + + if (strftime(result, sizeof(result), + "%a, %d %b %Y %H:%M:%S %z\n", tm) == 0) + errx(1, "Can't convert RFC 2822 time: buffer too small"); + return result; +} + +/* + * Parse HTTP-date as per RFC 2616. + * Return a pointer to the next character of the consumed date string, + * or NULL if failed. + */ +const char * +parse_rfc2616time(struct tm *parsed, const char *httpdate) +{ + const char *t; + const char *curlocale; + + /* The representation of %a depends on the current locale. */ + curlocale = setlocale(LC_TIME, NULL); + (void)setlocale(LC_TIME, "C"); + /* RFC 1123 */ + if ((t = strptime(httpdate, "%a, %d %b %Y %H:%M:%S GMT", parsed)) || + /* RFC 850 */ + (t = strptime(httpdate, "%a, %d-%b-%y %H:%M:%S GMT", parsed)) || + /* asctime */ + (t = strptime(httpdate, "%a, %b %d %H:%M:%S %Y", parsed))) { + ; /* do nothing */ + } + (void)setlocale(LC_TIME, curlocale); + return t; +} + +/* + * Update global `localcwd', which contains the state of the local cwd + */ +void +updatelocalcwd(void) +{ + + if (getcwd(localcwd, sizeof(localcwd)) == NULL) + localcwd[0] = '\0'; + DPRINTF("updatelocalcwd: got `%s'\n", localcwd); +} + +/* + * Update global `remotecwd', which contains the state of the remote cwd + */ +void +updateremotecwd(void) +{ + int overbose, ocode; + size_t i; + char *cp; + + overbose = verbose; + ocode = code; + if (ftp_debug == 0) + verbose = -1; + if (command("PWD") != COMPLETE) + goto badremotecwd; + cp = strchr(reply_string, ' '); + if (cp == NULL || cp[0] == '\0' || cp[1] != '"') + goto badremotecwd; + cp += 2; + for (i = 0; *cp && i < sizeof(remotecwd) - 1; i++, cp++) { + if (cp[0] == '"') { + if (cp[1] == '"') + cp++; + else + break; + } + remotecwd[i] = *cp; + } + remotecwd[i] = '\0'; + DPRINTF("updateremotecwd: got `%s'\n", remotecwd); + goto cleanupremotecwd; + badremotecwd: + remotecwd[0]='\0'; + cleanupremotecwd: + verbose = overbose; + code = ocode; +} + +/* + * Ensure file is in or under dir. + * Returns 1 if so, 0 if not (or an error occurred). + */ +int +fileindir(const char *file, const char *dir) +{ + char parentdirbuf[PATH_MAX+1], *parentdir; + char realdir[PATH_MAX+1]; + size_t dirlen; + + /* determine parent directory of file */ + (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf)); + parentdir = dirname(parentdirbuf); + if (strcmp(parentdir, ".") == 0) + return 1; /* current directory is ok */ + + /* find the directory */ + if (realpath(parentdir, realdir) == NULL) { + warn("Unable to determine real path of `%s'", parentdir); + return 0; + } + if (realdir[0] != '/') /* relative result is ok */ + return 1; + dirlen = strlen(dir); + if (strncmp(realdir, dir, dirlen) == 0 && + (realdir[dirlen] == '/' || realdir[dirlen] == '\0')) + return 1; + return 0; +} + +/* + * List words in stringlist, vertically arranged + */ +void +list_vertical(StringList *sl) +{ + size_t i, j; + size_t columns, lines; + char *p; + size_t w, width; + + width = 0; + + for (i = 0 ; i < sl->sl_cur ; i++) { + w = strlen(sl->sl_str[i]); + if (w > width) + width = w; + } + width = (width + 8) &~ 7; + + columns = ttywidth / width; + if (columns == 0) + columns = 1; + lines = (sl->sl_cur + columns - 1) / columns; + for (i = 0; i < lines; i++) { + for (j = 0; j < columns; j++) { + p = sl->sl_str[j * lines + i]; + if (p) + fputs(p, ttyout); + if (j * lines + i + lines >= sl->sl_cur) { + putc('\n', ttyout); + break; + } + if (p) { + w = strlen(p); + while (w < width) { + w = (w + 8) &~ 7; + (void)putc('\t', ttyout); + } + } + } + } +} + +/* + * Update the global ttywidth value, using TIOCGWINSZ. + */ +void +setttywidth(int a) +{ + struct winsize winsize; + int oerrno = errno; + + if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1 && + winsize.ws_col != 0) + ttywidth = winsize.ws_col; + else + ttywidth = 80; + errno = oerrno; +} + +/* + * Change the rate limit up (SIGUSR1) or down (SIGUSR2) + */ +void +crankrate(int sig) +{ + + switch (sig) { + case SIGUSR1: + if (rate_get) + rate_get += rate_get_incr; + if (rate_put) + rate_put += rate_put_incr; + break; + case SIGUSR2: + if (rate_get && rate_get > rate_get_incr) + rate_get -= rate_get_incr; + if (rate_put && rate_put > rate_put_incr) + rate_put -= rate_put_incr; + break; + default: + err(1, "crankrate invoked with unknown signal: %d", sig); + } +} + + +/* + * Setup or cleanup EditLine structures + */ +#ifndef NO_EDITCOMPLETE +void +controlediting(void) +{ + if (editing && el == NULL && hist == NULL) { + HistEvent ev; + int editmode; + + el = el_init(getprogname(), stdin, ttyout, stderr); + /* init editline */ + hist = history_init(); /* init the builtin history */ + history(hist, &ev, H_SETSIZE, 100);/* remember 100 events */ + el_set(el, EL_HIST, history, hist); /* use history */ + + el_set(el, EL_EDITOR, "emacs"); /* default editor is emacs */ + el_set(el, EL_PROMPT, prompt); /* set the prompt functions */ + el_set(el, EL_RPROMPT, rprompt); + + /* add local file completion, bind to TAB */ + el_set(el, EL_ADDFN, "ftp-complete", + "Context sensitive argument completion", + complete); + el_set(el, EL_BIND, "^I", "ftp-complete", NULL); + el_source(el, NULL); /* read ~/.editrc */ + if ((el_get(el, EL_EDITMODE, &editmode) != -1) && editmode == 0) + editing = 0; /* the user doesn't want editing, + * so disable, and let statement + * below cleanup */ + else + el_set(el, EL_SIGNAL, 1); + } + if (!editing) { + if (hist) { + history_end(hist); + hist = NULL; + } + if (el) { + el_end(el); + el = NULL; + } + } +} +#endif /* !NO_EDITCOMPLETE */ + +/* + * Convert the string `arg' to an int, which may have an optional SI suffix + * (`b', `k', `m', `g'). Returns the number for success, -1 otherwise. + */ +int +strsuftoi(const char *arg) +{ + char *cp; + long val; + + if (!isdigit((unsigned char)arg[0])) + return (-1); + + val = strtol(arg, &cp, 10); + if (cp != NULL) { + if (cp[0] != '\0' && cp[1] != '\0') + return (-1); + switch (tolower((unsigned char)cp[0])) { + case '\0': + case 'b': + break; + case 'k': + val <<= 10; + break; + case 'm': + val <<= 20; + break; + case 'g': + val <<= 30; + break; + default: + return (-1); + } + } + if (val < 0 || val > INT_MAX) + return (-1); + + return (val); +} + +/* + * Set up socket buffer sizes before a connection is made. + */ +void +setupsockbufsize(int sock) +{ + socklen_t slen; + + if (0 == rcvbuf_size) { + slen = sizeof(rcvbuf_size); + if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF, + (void *)&rcvbuf_size, &slen) == -1) + err(1, "Unable to determine rcvbuf size"); + if (rcvbuf_size <= 0) + rcvbuf_size = 8 * 1024; + if (rcvbuf_size > 8 * 1024 * 1024) + rcvbuf_size = 8 * 1024 * 1024; + DPRINTF("setupsockbufsize: rcvbuf_size determined as %d\n", + rcvbuf_size); + } + if (0 == sndbuf_size) { + slen = sizeof(sndbuf_size); + if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, + (void *)&sndbuf_size, &slen) == -1) + err(1, "Unable to determine sndbuf size"); + if (sndbuf_size <= 0) + sndbuf_size = 8 * 1024; + if (sndbuf_size > 8 * 1024 * 1024) + sndbuf_size = 8 * 1024 * 1024; + DPRINTF("setupsockbufsize: sndbuf_size determined as %d\n", + sndbuf_size); + } + + if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, + (void *)&sndbuf_size, sizeof(sndbuf_size)) == -1) + warn("Unable to set sndbuf size %d", sndbuf_size); + + if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + (void *)&rcvbuf_size, sizeof(rcvbuf_size)) == -1) + warn("Unable to set rcvbuf size %d", rcvbuf_size); +} + +/* + * Copy characters from src into dst, \ quoting characters that require it + */ +void +ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen) +{ + size_t di, si; + + di = si = 0; + while (src[si] != '\0' && di < dstlen && si < srclen) { + switch (src[si]) { + case '\\': + case ' ': + case '\t': + case '\r': + case '\n': + case '"': + /* + * Need room for two characters and NUL, avoiding + * incomplete escape sequences at end of dst. + */ + if (di >= dstlen - 3) + break; + dst[di++] = '\\'; + /* FALLTHROUGH */ + default: + dst[di] = src[si++]; + if (di < dstlen) + di++; + } + } + dst[di] = '\0'; +} + +/* + * Copy src into buf (which is len bytes long), expanding % sequences. + */ +void +formatbuf(char *buf, size_t len, const char *src) +{ + const char *p, *p2, *q; + size_t i; + int op, updirs, pdirs; + +#define ADDBUF(x) do { \ + if (i >= len - 1) \ + goto endbuf; \ + buf[i++] = (x); \ + } while (0) + + p = src; + for (i = 0; *p; p++) { + if (*p != '%') { + ADDBUF(*p); + continue; + } + p++; + + switch (op = *p) { + + case '/': + case '.': + case 'c': + p2 = connected ? remotecwd : ""; + updirs = pdirs = 0; + + /* option to determine fixed # of dirs from path */ + if (op == '.' || op == 'c') { + int skip; + + q = p2; + while (*p2) /* calc # of /'s */ + if (*p2++ == '/') + updirs++; + if (p[1] == '0') { /* print or ... */ + pdirs = 1; + p++; + } + if (p[1] >= '1' && p[1] <= '9') { + /* calc # to skip */ + skip = p[1] - '0'; + p++; + } else + skip = 1; + + updirs -= skip; + while (skip-- > 0) { + while ((p2 > q) && (*p2 != '/')) + p2--; /* back up */ + if (skip && p2 > q) + p2--; + } + if (*p2 == '/' && p2 != q) + p2++; + } + + if (updirs > 0 && pdirs) { + if (i >= len - 5) + break; + if (op == '.') { + ADDBUF('.'); + ADDBUF('.'); + ADDBUF('.'); + } else { + ADDBUF('/'); + ADDBUF('<'); + if (updirs > 9) { + ADDBUF('9'); + ADDBUF('+'); + } else + ADDBUF('0' + updirs); + ADDBUF('>'); + } + } + for (; *p2; p2++) + ADDBUF(*p2); + break; + + case 'M': + case 'm': + for (p2 = connected && hostname ? hostname : "-"; + *p2 ; p2++) { + if (op == 'm' && *p2 == '.') + break; + ADDBUF(*p2); + } + break; + + case 'n': + for (p2 = connected ? username : "-"; *p2 ; p2++) + ADDBUF(*p2); + break; + + case '%': + ADDBUF('%'); + break; + + default: /* display unknown codes literally */ + ADDBUF('%'); + ADDBUF(op); + break; + + } + } + endbuf: + buf[i] = '\0'; +} + +/* + * Determine if given string is an IPv6 address or not. + * Return 1 for yes, 0 for no + */ +int +isipv6addr(const char *addr) +{ + int rv = 0; +#ifdef INET6 + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(addr, "0", &hints, &res) != 0) + rv = 0; + else { + rv = 1; + freeaddrinfo(res); + } + DPRINTF("isipv6addr: got %d for %s\n", rv, addr); +#endif + return (rv == 1) ? 1 : 0; +} + +/* + * Read a line from the FILE stream into buf/buflen using fgets(), so up + * to buflen-1 chars will be read and the result will be NUL terminated. + * If the line has a trailing newline it will be removed. + * If the line is too long, excess characters will be read until + * newline/EOF/error. + * If EOF/error occurs or a too-long line is encountered and errormsg + * isn't NULL, it will be changed to a description of the problem. + * (The EOF message has a leading \n for cosmetic purposes). + * Returns: + * >=0 length of line (excluding trailing newline) if all ok + * -1 error occurred + * -2 EOF encountered + * -3 line was too long + */ +int +get_line(FILE *stream, char *buf, size_t buflen, const char **errormsg) +{ + int rv, ch; + size_t len; + + if (fgets(buf, buflen, stream) == NULL) { + if (feof(stream)) { /* EOF */ + rv = -2; + if (errormsg) + *errormsg = "\nEOF received"; + } else { /* error */ + rv = -1; + if (errormsg) + *errormsg = "Error encountered"; + } + clearerr(stream); + return rv; + } + len = strlen(buf); + if (buf[len-1] == '\n') { /* clear any trailing newline */ + buf[--len] = '\0'; + } else if (len == buflen-1) { /* line too long */ + while ((ch = getchar()) != '\n' && ch != EOF) + continue; + if (errormsg) + *errormsg = "Input line is too long"; + clearerr(stream); + return -3; + } + if (errormsg) + *errormsg = NULL; + return len; +} + +/* + * Internal version of connect(2); sets socket buffer sizes, + * binds to a specific local address (if set), and + * supports a connection timeout using a non-blocking connect(2) with + * a poll(2). + * Socket fcntl flags are temporarily updated to include O_NONBLOCK; + * these will not be reverted on connection failure. + * Returns 0 on success, or -1 upon failure (with an appropriate + * error message displayed.) + */ +int +ftp_connect(int sock, const struct sockaddr *name, socklen_t namelen, int pe) +{ + int flags, rv, timeout, error; + socklen_t slen; + struct timeval endtime, now, td; + struct pollfd pfd[1]; + char hname[NI_MAXHOST]; + char sname[NI_MAXSERV]; + + setupsockbufsize(sock); + if (getnameinfo(name, namelen, + hname, sizeof(hname), sname, sizeof(sname), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) { + strlcpy(hname, "?", sizeof(hname)); + strlcpy(sname, "?", sizeof(sname)); + } + + if (bindai != NULL) { /* bind to specific addr */ + struct addrinfo *ai; + + for (ai = bindai; ai != NULL; ai = ai->ai_next) { + if (ai->ai_family == name->sa_family) + break; + } + if (ai == NULL) + ai = bindai; + if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) { + char bname[NI_MAXHOST]; + int saveerr; + + saveerr = errno; + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, + bname, sizeof(bname), NULL, 0, NI_NUMERICHOST) != 0) + strlcpy(bname, "?", sizeof(bname)); + errno = saveerr; + warn("Can't bind to `%s'", bname); + return -1; + } + } + + /* save current socket flags */ + if ((flags = fcntl(sock, F_GETFL, 0)) == -1) { + warn("Can't %s socket flags for connect to `%s:%s'", + "save", hname, sname); + return -1; + } + /* set non-blocking connect */ + if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) == -1) { + warn("Can't set socket non-blocking for connect to `%s:%s'", + hname, sname); + return -1; + } + + /* NOTE: we now must restore socket flags on successful exit */ + + pfd[0].fd = sock; + pfd[0].events = POLLIN|POLLOUT; + + if (quit_time > 0) { /* want a non default timeout */ + (void)gettimeofday(&endtime, NULL); + endtime.tv_sec += quit_time; /* determine end time */ + } + + rv = connect(sock, name, namelen); /* inititate the connection */ + if (rv == -1) { /* connection error */ + if (errno != EINPROGRESS) { /* error isn't "please wait" */ + if (pe || (errno != EHOSTUNREACH)) + connecterror: + warn("Can't connect to `%s:%s'", hname, sname); + return -1; + } + + /* connect EINPROGRESS; wait */ + do { + if (quit_time > 0) { /* determine timeout */ + (void)gettimeofday(&now, NULL); + timersub(&endtime, &now, &td); + timeout = td.tv_sec * 1000 + td.tv_usec/1000; + if (timeout < 0) + timeout = 0; + } else { + timeout = INFTIM; + } + pfd[0].revents = 0; + rv = ftp_poll(pfd, 1, timeout); + /* loop until poll ! EINTR */ + } while (rv == -1 && errno == EINTR); + + if (rv == 0) { /* poll (connect) timed out */ + errno = ETIMEDOUT; + goto connecterror; + } + + if (rv == -1) { /* poll error */ + goto connecterror; + } else if (pfd[0].revents & (POLLIN|POLLOUT)) { + slen = sizeof(error); /* OK, or pending error */ + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, + &error, &slen) == -1) { + /* Solaris pending error */ + goto connecterror; + } else if (error != 0) { + errno = error; /* BSD pending error */ + goto connecterror; + } + } else { + errno = EBADF; /* this shouldn't happen ... */ + goto connecterror; + } + } + + if (fcntl(sock, F_SETFL, flags) == -1) { + /* restore socket flags */ + warn("Can't %s socket flags for connect to `%s:%s'", + "restore", hname, sname); + return -1; + } + return 0; +} + +/* + * Internal version of listen(2); sets socket buffer sizes first. + */ +int +ftp_listen(int sock, int backlog) +{ + + setupsockbufsize(sock); + return (listen(sock, backlog)); +} + +/* + * Internal version of poll(2), to allow reimplementation by select(2) + * on platforms without the former. + */ +int +ftp_poll(struct pollfd *fds, int nfds, int timeout) +{ + return poll(fds, nfds, timeout); +} + +/* + * malloc() with inbuilt error checking + */ +void * +ftp_malloc(size_t size) +{ + void *p; + + p = malloc(size); + if (p == NULL) + err(1, "Unable to allocate %ld bytes of memory", (long)size); + return (p); +} + +/* + * sl_init() with inbuilt error checking + */ +StringList * +ftp_sl_init(void) +{ + StringList *p; + + p = sl_init(); + if (p == NULL) + err(1, "Unable to allocate memory for stringlist"); + return (p); +} + +/* + * sl_add() with inbuilt error checking + */ +void +ftp_sl_add(StringList *sl, char *i) +{ + + if (sl_add(sl, i) == -1) + err(1, "Unable to add `%s' to stringlist", i); +} + +/* + * strdup() with inbuilt error checking + */ +char * +ftp_strdup(const char *str) +{ + char *s; + + if (str == NULL) + errx(1, "ftp_strdup: called with NULL argument"); + s = strdup(str); + if (s == NULL) + err(1, "Unable to allocate memory for string copy"); + return (s); +} diff --git a/usr.bin/ftp/version.h b/usr.bin/ftp/version.h new file mode 100644 index 000000000..9d94b4319 --- /dev/null +++ b/usr.bin/ftp/version.h @@ -0,0 +1,38 @@ +/* $NetBSD: version.h,v 1.83 2013/02/06 16:37:20 christos Exp $ */ + +/*- + * Copyright (c) 1999-2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FTP_PRODUCT +#define FTP_PRODUCT "NetBSD-ftp" +#endif + +#ifndef FTP_VERSION +#define FTP_VERSION "20121224" +#endif