diff options
Diffstat (limited to 'usr.bin/ftp')
-rw-r--r-- | usr.bin/ftp/Makefile | 9 | ||||
-rw-r--r-- | usr.bin/ftp/cmd.c | 637 | ||||
-rw-r--r-- | usr.bin/ftp/cmds.c | 1688 | ||||
-rw-r--r-- | usr.bin/ftp/cmds.h | 83 | ||||
-rw-r--r-- | usr.bin/ftp/cmdtab.c | 215 | ||||
-rw-r--r-- | usr.bin/ftp/complete.c | 381 | ||||
-rw-r--r-- | usr.bin/ftp/cookie.c | 231 | ||||
-rw-r--r-- | usr.bin/ftp/domacro.c | 149 | ||||
-rw-r--r-- | usr.bin/ftp/extern.h | 150 | ||||
-rw-r--r-- | usr.bin/ftp/fetch.c | 1668 | ||||
-rw-r--r-- | usr.bin/ftp/file.c | 55 | ||||
-rw-r--r-- | usr.bin/ftp/ftp.1 | 1634 | ||||
-rw-r--r-- | usr.bin/ftp/ftp.c | 2302 | ||||
-rw-r--r-- | usr.bin/ftp/ftp.h | 116 | ||||
-rw-r--r-- | usr.bin/ftp/ftp_var.h | 231 | ||||
-rw-r--r-- | usr.bin/ftp/http.c | 784 | ||||
-rw-r--r-- | usr.bin/ftp/list.c | 86 | ||||
-rw-r--r-- | usr.bin/ftp/main.c | 1174 | ||||
-rw-r--r-- | usr.bin/ftp/pathnames.h | 37 | ||||
-rw-r--r-- | usr.bin/ftp/progressmeter.c | 353 | ||||
-rw-r--r-- | usr.bin/ftp/ruserpass.c | 317 | ||||
-rw-r--r-- | usr.bin/ftp/small.c | 730 | ||||
-rw-r--r-- | usr.bin/ftp/small.h | 35 | ||||
-rw-r--r-- | usr.bin/ftp/stringlist.c | 97 | ||||
-rw-r--r-- | usr.bin/ftp/stringlist.h | 56 | ||||
-rw-r--r-- | usr.bin/ftp/url.c | 417 | ||||
-rw-r--r-- | usr.bin/ftp/util.c | 1160 | ||||
-rw-r--r-- | usr.bin/ftp/xmalloc.c | 147 | ||||
-rw-r--r-- | usr.bin/ftp/xmalloc.h | 41 |
29 files changed, 3452 insertions, 11531 deletions
diff --git a/usr.bin/ftp/Makefile b/usr.bin/ftp/Makefile index fc039e142c3..2a03d827f6d 100644 --- a/usr.bin/ftp/Makefile +++ b/usr.bin/ftp/Makefile @@ -1,15 +1,10 @@ -# $OpenBSD: Makefile,v 1.30 2016/05/06 22:06:09 jca Exp $ - -# Define SMALL to disable command line editing and https support +# Define SMALL to disable command line editing #CFLAGS+=-DSMALL PROG= ftp -SRCS= cmds.c cmdtab.c complete.c cookie.c domacro.c fetch.c ftp.c \ - list.c main.c ruserpass.c small.c stringlist.c util.c +SRCS= cmd.c file.c ftp.c http.c main.c progressmeter.c url.c util.c xmalloc.c LDADD+= -ledit -lcurses -lutil -ltls -lssl -lcrypto DPADD+= ${LIBEDIT} ${LIBCURSES} ${LIBUTIL} ${LIBTLS} ${LIBSSL} ${LIBCRYPTO} -#COPTS+= -Wall -Wconversion -Wstrict-prototypes -Wmissing-prototypes - .include <bsd.prog.mk> diff --git a/usr.bin/ftp/cmd.c b/usr.bin/ftp/cmd.c new file mode 100644 index 00000000000..faf7949ab3b --- /dev/null +++ b/usr.bin/ftp/cmd.c @@ -0,0 +1,637 @@ +/* + * Copyright (c) 2018 Sunil Nimmagadda <sunil@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/socket.h> +#include <sys/stat.h> + +#include <arpa/telnet.h> + +#include <err.h> +#include <errno.h> +#include <histedit.h> +#include <libgen.h> +#include <limits.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ftp.h" + +#define ARGVMAX 64 + +static void cmd_interrupt(int); +static int cmd_lookup(const char *); +static FILE *data_fopen(const char *); +static void do_open(int, char **); +static void do_help(int, char **); +static void do_quit(int, char **); +static void do_ls(int, char **); +static void do_pwd(int, char **); +static void do_cd(int, char **); +static void do_get(int, char **); +static void do_passive(int, char **); +static void do_lcd(int, char **); +static void do_lpwd(int, char **); +static void do_put(int, char **); +static void do_mget(int, char **); +static void ftp_abort(void); +static char *prompt(void); + +static FILE *ctrl_fp, *data_fp; + +static struct { + const char *name; + const char *info; + void (*cmd)(int, char **); + int conn_required; +} cmd_tbl[] = { + { "open", "connect to remote ftp server", do_open, 0 }, + { "close", "terminate ftp session", do_quit, 1 }, + { "help", "print local help information", do_help, 0 }, + { "?", "print local help information", do_help, 0 }, + { "quit", "terminate ftp session and exit", do_quit, 0 }, + { "exit", "terminate ftp session and exit", do_quit, 0 }, + { "ls", "list contents of remote directory", do_ls, 1 }, + { "pwd", "print working directory on remote machine", do_pwd, 1 }, + { "cd", "change remote working directory", do_cd, 1 }, + { "nlist", "nlist contents of remote directory", do_ls, 1 }, + { "get", "receive file", do_get, 1 }, + { "passive", "toggle passive transfer mode", do_passive, 0 }, + { "lcd", "change local working directory", do_lcd, 0 }, + { "lpwd", "print local working directory", do_lpwd, 0 }, + { "put", "send one file", do_put, 1 }, + { "mget", "get multiple files", do_mget, 1 }, + { "mput", "send multiple files", do_mget, 1 }, +}; + +static void +cmd_interrupt(int signo) +{ + const char msg[] = "\rwaiting for remote to finish abort\n"; + int save_errno = errno; + + if (data_fp != NULL) + (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); + + interrupted = 1; + errno = save_errno; +} + +void +cmd(const char *host, const char *port, const char *path) +{ + HistEvent hev; + EditLine *el; + History *hist; + const char *line; + char **ap, *argv[ARGVMAX], *cp; + int count, i; + + if ((el = el_init(getprogname(), stdin, stdout, stderr)) == NULL) + err(1, "couldn't initialise editline"); + + if ((hist = history_init()) == NULL) + err(1, "couldn't initialise editline history"); + + history(hist, &hev, H_SETSIZE, 100); + el_set(el, EL_HIST, history, hist); + el_set(el, EL_PROMPT, prompt); + el_set(el, EL_EDITOR, "emacs"); + el_set(el, EL_TERMINAL, NULL); + el_set(el, EL_SIGNAL, 1); + el_source(el, NULL); + + if (host != NULL) { + argv[0] = "open"; + argv[1] = (char *)host; + argv[2] = port ? (char *)port : "21"; + do_open(3, argv); + /* If we don't have a connection, exit */ + if (ctrl_fp == NULL) + exit(1); + + if (path != NULL) { + argv[0] = "cd"; + argv[1] = (char *)path; + do_cd(2, argv); + } + } + + for (;;) { + signal(SIGINT, SIG_IGN); + if ((line = el_gets(el, &count)) == NULL || count <= 0) { + if (verbose) + fprintf(stderr, "\n"); + argv[0] = "quit"; + do_quit(1, argv); + break; + } + + if (count <= 1) + continue; + + if ((cp = strrchr(line, '\n')) != NULL) + *cp = '\0'; + + history(hist, &hev, H_ENTER, line); + for (ap = argv; ap < &argv[ARGVMAX - 1] && + (*ap = strsep((char **)&line, " \t")) != NULL;) { + if (**ap != '\0') + ap++; + } + *ap = NULL; + + if (argv[0] == NULL) + continue; + + if ((i = cmd_lookup(argv[0])) == -1) { + fprintf(stderr, "Invalid command.\n"); + continue; + } + + if (cmd_tbl[i].conn_required && ctrl_fp == NULL) { + fprintf(stderr, "Not connected.\n"); + continue; + } + + interrupted = 0; + signal(SIGINT, cmd_interrupt); + cmd_tbl[i].cmd(ap - argv, argv); + + if (strcmp(cmd_tbl[i].name, "quit") == 0 || + strcmp(cmd_tbl[i].name, "exit") == 0) + break; + } + + el_end(el); +} + +static int +cmd_lookup(const char *cmd) +{ + size_t i; + + for (i = 0; i < nitems(cmd_tbl); i++) + if (strcmp(cmd, cmd_tbl[i].name) == 0) + return i; + + return -1; +} + +static char * +prompt(void) +{ + return "ftp> "; +} + +static FILE * +data_fopen(const char *mode) +{ + int fd; + + fd = activemode ? ftp_eprt(ctrl_fp) : ftp_epsv(ctrl_fp); + if (fd == -1) { + if (io_debug) + fprintf(stderr, "Failed to open data connection"); + + return NULL; + } + + return fdopen(fd, mode); +} + +static void +ftp_abort(void) +{ + char buf[BUFSIZ]; + + snprintf(buf, sizeof buf, "%c%c%c", IAC, IP, IAC); + if (send(fileno(ctrl_fp), buf, 3, MSG_OOB) != 3) + warn("abort"); + + ftp_command(ctrl_fp, "%cABOR", DM); +} + +static void +do_open(int argc, char **argv) +{ + const char *host = NULL, *port = "21"; + char *buf = NULL; + size_t n = 0; + int sock; + + if (ctrl_fp != NULL) { + fprintf(stderr, "already connected, use close first.\n"); + return; + } + + switch (argc) { + case 3: + port = argv[2]; + /* FALLTHROUGH */ + case 2: + host = argv[1]; + break; + default: + fprintf(stderr, "usage: open host [port]\n"); + return; + } + + if ((sock = tcp_connect(host, port, 0)) == -1) + return; + + fprintf(stderr, "Connected to %s.\n", host); + if ((ctrl_fp = fdopen(sock, "r+")) == NULL) + err(1, "%s: fdopen", __func__); + + /* greeting */ + ftp_getline(&buf, &n, 0, ctrl_fp); + free(buf); + if (ftp_auth(ctrl_fp, NULL, NULL) != P_OK) { + fclose(ctrl_fp); + ctrl_fp = NULL; + } +} + +static void +do_help(int argc, char **argv) +{ + size_t i; + int j; + + if (argc == 1) { + for (i = 0; i < nitems(cmd_tbl); i++) + fprintf(stderr, "%s\n", cmd_tbl[i].name); + + return; + } + + for (i = 1; i < (size_t)argc; i++) { + if ((j = cmd_lookup(argv[i])) == -1) + fprintf(stderr, "invalid help command %s\n", argv[i]); + else + fprintf(stderr, "%s\t%s\n", argv[i], cmd_tbl[j].info); + } +} + +static void +do_quit(int argc, char **argv) +{ + if (ctrl_fp == NULL) + return; + + ftp_command(ctrl_fp, "QUIT"); + fclose(ctrl_fp); + ctrl_fp = NULL; +} + +static void +do_ls(int argc, char **argv) +{ + FILE *dst_fp = stdout; + const char *cmd, *local_fname = NULL, *remote_dir = NULL; + char *buf = NULL; + size_t n = 0; + ssize_t len; + int r; + + switch (argc) { + case 3: + if (strcmp(argv[2], "-") != 0) + local_fname = argv[2]; + /* FALLTHROUGH */ + case 2: + remote_dir = argv[1]; + /* FALLTHROUGH */ + case 1: + break; + default: + fprintf(stderr, "usage: ls [remote-directory [local-file]]\n"); + return; + } + + if ((data_fp = data_fopen("r")) == NULL) + return; + + if (local_fname && (dst_fp = fopen(local_fname, "w")) == NULL) { + warn("fopen %s", local_fname); + fclose(data_fp); + data_fp = NULL; + return; + } + + cmd = (strcmp(argv[0], "ls") == 0) ? "LIST" : "NLST"; + if (remote_dir != NULL) + r = ftp_command(ctrl_fp, "%s %s", cmd, remote_dir); + else + r = ftp_command(ctrl_fp, "%s", cmd); + + if (r != P_PRE) { + fclose(data_fp); + data_fp = NULL; + if (dst_fp != stdout) + fclose(dst_fp); + + return; + } + + while ((len = getline(&buf, &n, data_fp)) != -1 && !interrupted) { + buf[len - 1] = '\0'; + if (len >= 2 && buf[len - 2] == '\r') + buf[len - 2] = '\0'; + + fprintf(dst_fp, "%s\n", buf); + } + + if (interrupted) + ftp_abort(); + + fclose(data_fp); + data_fp = NULL; + ftp_getline(&buf, &n, 0, ctrl_fp); + free(buf); + if (dst_fp != stdout) + fclose(dst_fp); +} + +static void +do_get(int argc, char **argv) +{ + FILE *dst_fp; + const char *local_fname = NULL, *p, *remote_fname; + char *buf = NULL; + size_t n = 0; + off_t file_sz, offset = 0; + + switch (argc) { + case 3: + local_fname = argv[2]; + /* FALLTHROUGH */ + case 2: + remote_fname = argv[1]; + break; + default: + fprintf(stderr, "usage: get remote-file [local-file]\n"); + return; + } + + if (local_fname == NULL) + local_fname = remote_fname; + + if (ftp_command(ctrl_fp, "TYPE I") != P_OK) + return; + + log_info("local: %s remote: %s\n", local_fname, remote_fname); + if (ftp_size(ctrl_fp, remote_fname, &file_sz, &buf) != P_OK) { + fprintf(stderr, "%s", buf); + return; + } + + if ((data_fp = data_fopen("r")) == NULL) + return; + + if ((dst_fp = fopen(local_fname, "w")) == NULL) { + warn("%s", local_fname); + fclose(data_fp); + data_fp = NULL; + return; + } + + if (ftp_command(ctrl_fp, "RETR %s", remote_fname) != P_PRE) { + fclose(data_fp); + data_fp = NULL; + fclose(dst_fp); + return; + } + + if (progressmeter) { + p = basename(remote_fname); + start_progress_meter(p, NULL, file_sz, &offset); + } + + copy_file(dst_fp, data_fp, &offset); + if (progressmeter) + stop_progress_meter(); + + if (interrupted) + ftp_abort(); + + fclose(data_fp); + data_fp = NULL; + fclose(dst_fp); + ftp_getline(&buf, &n, 0, ctrl_fp); + free(buf); +} + +static void +do_pwd(int argc, char **argv) +{ + ftp_command(ctrl_fp, "PWD"); +} + +static void +do_cd(int argc, char **argv) +{ + if (argc != 2) { + fprintf(stderr, "usage: cd remote-directory\n"); + return; + } + + ftp_command(ctrl_fp, "CWD %s", argv[1]); +} + +static void +do_passive(int argc, char **argv) +{ + switch (argc) { + case 1: + break; + case 2: + if (strcmp(argv[1], "on") == 0 || strcmp(argv[1], "off") == 0) + break; + + /* FALLTHROUGH */ + default: + fprintf(stderr, "usage: passive [on | off]\n"); + return; + } + + if (argv[1] != NULL) { + activemode = (strcmp(argv[1], "off") == 0) ? 1 : 0; + fprintf(stderr, "passive mode is %s\n", argv[1]); + return; + } + + activemode = !activemode; + fprintf(stderr, "passive mode is %s\n", activemode ? "off" : "on"); +} + +static void +do_lcd(int argc, char **argv) +{ + struct passwd *pw = NULL; + const char *dir, *login; + char cwd[PATH_MAX]; + + switch (argc) { + case 1: + case 2: + break; + default: + fprintf(stderr, "usage: lcd [local-directory]\n"); + return; + } + + if ((login = getlogin()) != NULL) + pw = getpwnam(login); + + if (pw == NULL && (pw = getpwuid(getuid())) == NULL) { + fprintf(stderr, "Failed to get home directory\n"); + return; + } + + dir = argv[1] ? argv[1] : pw->pw_dir; + if (chdir(dir) != 0) { + warn("local: %s", dir); + return; + } + + if (getcwd(cwd, sizeof cwd) == NULL) { + warn("getcwd"); + return; + } + + fprintf(stderr, "Local directory now %s\n", cwd); +} + +static void +do_lpwd(int argc, char **argv) +{ + char cwd[PATH_MAX]; + + if (getcwd(cwd, sizeof cwd) == NULL) { + warn("getcwd"); + return; + } + + fprintf(stderr, "Local directory %s\n", cwd); +} + +static void +do_put(int argc, char **argv) +{ + struct stat sb; + FILE *src_fp; + const char *local_fname, *p, *remote_fname = NULL; + char *buf = NULL; + size_t n = 0; + off_t file_sz, offset = 0; + + switch (argc) { + case 3: + remote_fname = argv[2]; + /* FALLTHROUGH */ + case 2: + local_fname = argv[1]; + break; + default: + fprintf(stderr, "usage: put local-file [remote-file]\n"); + return; + } + + if (remote_fname == NULL) + remote_fname = local_fname; + + if (ftp_command(ctrl_fp, "TYPE I") != P_OK) + return; + + log_info("local: %s remote: %s\n", local_fname, remote_fname); + if ((data_fp = data_fopen("w")) == NULL) + return; + + if ((src_fp = fopen(local_fname, "r")) == NULL) { + warn("%s", local_fname); + fclose(data_fp); + data_fp = NULL; + return; + } + + if (fstat(fileno(src_fp), &sb) != 0) { + warn("%s", local_fname); + fclose(data_fp); + data_fp = NULL; + fclose(src_fp); + return; + } + file_sz = sb.st_size; + + if (ftp_command(ctrl_fp, "STOR %s", remote_fname) != P_PRE) { + fclose(data_fp); + data_fp = NULL; + fclose(src_fp); + return; + } + + if (progressmeter) { + p = basename(remote_fname); + start_progress_meter(p, NULL, file_sz, &offset); + } + + copy_file(data_fp, src_fp, &offset); + if (progressmeter) + stop_progress_meter(); + + if (interrupted) + ftp_abort(); + + fclose(data_fp); + data_fp = NULL; + fclose(src_fp); + ftp_getline(&buf, &n, 0, ctrl_fp); + free(buf); +} + +static void +do_mget(int argc, char **argv) +{ + void (*fn)(int, char **); + const char *usage; + char *args[2]; + int i; + + if (strcmp(argv[0], "mget") == 0) { + fn = do_get; + args[0] = "get"; + usage = "mget remote-files"; + } else { + fn = do_put; + args[0] = "put"; + usage = "mput local-files"; + } + + if (argc == 1) { + fprintf(stderr, "usage: %s\n", usage); + return; + } + + for (i = 1; i < argc && !interrupted; i++) { + args[1] = argv[i]; + fn(2, args); + } +} diff --git a/usr.bin/ftp/cmds.c b/usr.bin/ftp/cmds.c deleted file mode 100644 index e47c8bf1da3..00000000000 --- a/usr.bin/ftp/cmds.c +++ /dev/null @@ -1,1688 +0,0 @@ -/* $OpenBSD: cmds.c,v 1.80 2018/01/24 13:25:25 tb Exp $ */ -/* $NetBSD: cmds.c,v 1.27 1997/08/18 10:20:15 lukem Exp $ */ - -/* - * 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. - */ - -/* - * 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. - */ - -#ifndef SMALL - -/* - * FTP User Program -- Command Routines. - */ -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <arpa/ftp.h> - -#include <ctype.h> -#include <err.h> -#include <fnmatch.h> -#include <glob.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> - -#include "ftp_var.h" -#include "pathnames.h" -#include "cmds.h" - -/* - * Set ascii transfer type. - */ -/*ARGSUSED*/ -void -setascii(int argc, char *argv[]) -{ - - stype[1] = "ascii"; - settype(2, stype); -} - -/* - * Set file transfer mode. - */ -/*ARGSUSED*/ -void -setftmode(int argc, char *argv[]) -{ - - fprintf(ttyout, "We only support %s mode, sorry.\n", modename); - code = -1; -} - -/* - * Set file transfer format. - */ -/*ARGSUSED*/ -void -setform(int argc, char *argv[]) -{ - - fprintf(ttyout, "We only support %s format, sorry.\n", formname); - code = -1; -} - -/* - * Set file transfer structure. - */ -/*ARGSUSED*/ -void -setstruct(int argc, char *argv[]) -{ - - fprintf(ttyout, "We only support %s structure, sorry.\n", structname); - code = -1; -} - -void -reput(int argc, char *argv[]) -{ - - (void)putit(argc, argv, 1); -} - -void -put(int argc, char *argv[]) -{ - - (void)putit(argc, argv, 0); -} - -/* - * Send a single file. - */ -void -putit(int argc, char *argv[], int restartit) -{ - char *cmd; - int loc = 0; - char *oldargv1, *oldargv2; - - if (argc == 2) { - argc++; - argv[2] = argv[1]; - loc++; - } - if (argc < 2 && !another(&argc, &argv, "local-file")) - goto usage; - if ((argc < 3 && !another(&argc, &argv, "remote-file")) || argc > 3) { -usage: - fprintf(ttyout, "usage: %s local-file [remote-file]\n", - argv[0]); - code = -1; - return; - } - oldargv1 = argv[1]; - oldargv2 = argv[2]; - if (!globulize(&argv[1])) { - code = -1; - return; - } - /* - * If "globulize" modifies argv[1], and argv[2] is a copy of - * the old argv[1], make it a copy of the new argv[1]. - */ - if (argv[1] != oldargv1 && argv[2] == oldargv1) { - argv[2] = argv[1]; - } - if (restartit == 1) { - if (curtype != type) - changetype(type, 0); - restart_point = remotesize(argv[2], 1); - if (restart_point < 0) { - restart_point = 0; - code = -1; - return; - } - } - if (strcmp(argv[0], "append") == 0) { - restartit = 1; - } - cmd = restartit ? "APPE" : ((sunique) ? "STOU" : "STOR"); - if (loc && ntflag) { - argv[2] = dotrans(argv[2]); - } - if (loc && mapflag) { - argv[2] = domap(argv[2]); - } - sendrequest(cmd, argv[1], argv[2], - argv[1] != oldargv1 || argv[2] != oldargv2); - restart_point = 0; - if (oldargv1 != argv[1]) /* free up after globulize() */ - free(argv[1]); -} - -/* - * Send multiple files. - */ -void -mput(int argc, char *argv[]) -{ - extern int optind, optreset; - int ch, i, restartit = 0; - sig_t oldintr; - char *cmd, *tp, *xargv[] = { argv[0], NULL, NULL }; - const char *errstr; - static int depth = 0, max_depth = 0; - - optind = optreset = 1; - - if (depth) - depth++; - - while ((ch = getopt(argc, argv, "cd:r")) != -1) { - switch(ch) { - case 'c': - restartit = 1; - break; - case 'd': - max_depth = strtonum(optarg, 0, INT_MAX, &errstr); - if (errstr != NULL) { - fprintf(ttyout, "bad depth value, %s: %s\n", - errstr, optarg); - code = -1; - return; - } - break; - case 'r': - depth = 1; - break; - default: - goto usage; - } - } - - if (argc - optind < 1 && !another(&argc, &argv, "local-files")) { -usage: - fprintf(ttyout, "usage: %s [-cr] [-d depth] local-files\n", - argv[0]); - code = -1; - return; - } - - argv[optind - 1] = argv[0]; - argc -= optind - 1; - argv += optind - 1; - - mname = argv[0]; - mflag = 1; - - oldintr = signal(SIGINT, mabort); - (void)setjmp(jabort); - if (proxy) { - char *cp, *tp2, tmpbuf[PATH_MAX]; - - while ((cp = remglob(argv, 0, NULL)) != NULL) { - if (*cp == '\0') { - mflag = 0; - continue; - } - if (mflag && confirm(argv[0], cp)) { - tp = cp; - if (mcase) { - while (*tp && !islower((unsigned char)*tp)) { - tp++; - } - if (!*tp) { - tp = cp; - tp2 = tmpbuf; - while ((*tp2 = *tp) != '\0') { - if (isupper((unsigned char)*tp2)) { - *tp2 = - tolower((unsigned char)*tp2); - } - tp++; - tp2++; - } - } - tp = tmpbuf; - } - if (ntflag) { - tp = dotrans(tp); - } - if (mapflag) { - tp = domap(tp); - } - if (restartit == 1) { - off_t ret; - - if (curtype != type) - changetype(type, 0); - ret = remotesize(tp, 0); - restart_point = (ret < 0) ? 0 : ret; - } - cmd = restartit ? "APPE" : ((sunique) ? - "STOU" : "STOR"); - sendrequest(cmd, cp, tp, - cp != tp || !interactive); - restart_point = 0; - if (!mflag && fromatty) { - if (confirm(argv[0], NULL)) - mflag = 1; - } - } - } - (void)signal(SIGINT, oldintr); - mflag = 0; - return; - } - - for (i = 1; i < argc; i++) { - char **cpp; - glob_t gl; - int flags; - - /* Copy files without word expansion */ - if (!doglob) { - if (mflag && confirm(argv[0], argv[i])) { - tp = (ntflag) ? dotrans(argv[i]) : argv[i]; - tp = (mapflag) ? domap(tp) : tp; - if (restartit == 1) { - off_t ret; - - if (curtype != type) - changetype(type, 0); - ret = remotesize(tp, 0); - restart_point = (ret < 0) ? 0 : ret; - } - cmd = restartit ? "APPE" : ((sunique) ? - "STOU" : "STOR"); - sendrequest(cmd, argv[i], tp, - tp != argv[i] || !interactive); - restart_point = 0; - if (!mflag && fromatty) { - if (confirm(argv[0], NULL)) - mflag = 1; - } - } - continue; - } - - /* expanding file names */ - memset(&gl, 0, sizeof(gl)); - flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; - if (glob(argv[i], flags, NULL, &gl) || gl.gl_pathc == 0) { - warnx("%s: not found", argv[i]); - globfree(&gl); - continue; - } - - /* traverse all expanded file names */ - for (cpp = gl.gl_pathv; cpp && *cpp != NULL; cpp++) { - struct stat filestat; - - if (!mflag) - continue; - if (stat(*cpp, &filestat) != 0) { - warn("local: %s", *cpp); - continue; - } - if (S_ISDIR(filestat.st_mode) && depth == max_depth) - continue; - if (!confirm(argv[0], *cpp)) - continue; - - /* - * If file is a directory then create a new one - * at the remote machine. - */ - if (S_ISDIR(filestat.st_mode)) { - xargv[1] = *cpp; - makedir(2, xargv); - cd(2, xargv); - if (dirchange != 1) { - warnx("remote: %s", *cpp); - continue; - } - - if (chdir(*cpp) != 0) { - warn("local: %s", *cpp); - goto out; - } - - /* Copy the whole directory recursively. */ - xargv[1] = "*"; - mput(2, xargv); - - if (chdir("..") != 0) { - mflag = 0; - warn("local: %s", *cpp); - goto out; - } - - out: - xargv[1] = ".."; - cd(2, xargv); - if (dirchange != 1) { - warnx("remote: %s", *cpp); - mflag = 0; - } - continue; - } - - tp = (ntflag) ? dotrans(*cpp) : *cpp; - tp = (mapflag) ? domap(tp) : tp; - if (restartit == 1) { - off_t ret; - - if (curtype != type) - changetype(type, 0); - ret = remotesize(tp, 0); - restart_point = (ret < 0) ? 0 : ret; - } - cmd = restartit ? "APPE" : ((sunique) ? - "STOU" : "STOR"); - sendrequest(cmd, *cpp, tp, - *cpp != tp || !interactive); - restart_point = 0; - if (!mflag && fromatty) { - if (confirm(argv[0], NULL)) - mflag = 1; - } - } - globfree(&gl); - } - - (void)signal(SIGINT, oldintr); - - if (depth) - depth--; - if (depth == 0 || mflag == 0) - depth = max_depth = mflag = 0; -} - -void -reget(int argc, char *argv[]) -{ - - (void)getit(argc, argv, 1, "a+w"); -} - -char * -onoff(int bool) -{ - - return (bool ? "on" : "off"); -} - -/* - * Show status. - */ -/*ARGSUSED*/ -void -status(int argc, char *argv[]) -{ - int i; - - 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.\n", onoff(passivemode)); - 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, "Use of PORT/LPRT 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, "Command line editing: %s.\n", onoff(editing)); - if (macnum > 0) { - fputs("Macros:\n", ttyout); - for (i=0; i<macnum; i++) { - fprintf(ttyout, "\t%s\n", macros[i].mac_name); - } - } - code = 0; -} - -/* - * Toggle a variable - */ -int -togglevar(int argc, char *argv[], int *var, const char *mesg) -{ - if (argc < 2) { - *var = !*var; - } else if (argc == 2 && strcasecmp(argv[1], "on") == 0) { - *var = 1; - } else if (argc == 2 && strcasecmp(argv[1], "off") == 0) { - *var = 0; - } else { - fprintf(ttyout, "usage: %s [on | off]\n", argv[0]); - return (-1); - } - if (mesg) - fprintf(ttyout, "%s %s.\n", mesg, onoff(*var)); - return (*var); -} - -/* - * Set beep on cmd completed mode. - */ -/*ARGSUSED*/ -void -setbell(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &bell, "Bell mode"); -} - -/* - * Set command line editing - */ -/*ARGSUSED*/ -void -setedit(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &editing, "Editing mode"); - controlediting(); -} - -/* - * Toggle use of IPv4 EPSV/EPRT - */ -/*ARGSUSED*/ -void -setepsv4(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &epsv4, "EPSV/EPRT on IPv4"); - epsv4bad = 0; -} - -/* - * Turn on packet tracing. - */ -/*ARGSUSED*/ -void -settrace(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &trace, "Packet tracing"); -} - -/* - * Toggle hash mark printing during transfers, or set hash mark bytecount. - */ -/*ARGSUSED*/ -void -sethash(int argc, char *argv[]) -{ - if (argc == 1) - hash = !hash; - else if (argc != 2) { - fprintf(ttyout, "usage: %s [on | off | size]\n", argv[0]); - code = -1; - return; - } else if (strcasecmp(argv[1], "on") == 0) - hash = 1; - else if (strcasecmp(argv[1], "off") == 0) - hash = 0; - else { - int nmark; - const char *errstr; - - nmark = strtonum(argv[1], 1, INT_MAX, &errstr); - if (errstr) { - fprintf(ttyout, "bytecount value is %s: %s\n", - errstr, argv[1]); - code = -1; - return; - } - mark = nmark; - hash = 1; - } - fprintf(ttyout, "Hash mark printing %s", onoff(hash)); - if (hash) - fprintf(ttyout, " (%d bytes/hash mark)", mark); - fputs(".\n", ttyout); - code = hash; -} - -/* - * Turn on printing of server echo's. - */ -/*ARGSUSED*/ -void -setverbose(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &verbose, "Verbose mode"); -} - -/* - * Toggle PORT/LPRT cmd use before each data connection. - */ -/*ARGSUSED*/ -void -setport(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &sendport, "Use of PORT/LPRT cmds"); -} - -/* - * Toggle transfer progress bar. - */ -/*ARGSUSED*/ -void -setprogress(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &progress, "Progress bar"); -} - -/* - * Turn on interactive prompting during mget, mput, and mdelete. - */ -/*ARGSUSED*/ -void -setprompt(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &interactive, "Interactive mode"); -} - -/* - * Toggle gate-ftp mode, or set gate-ftp server - */ -/*ARGSUSED*/ -void -setgate(int argc, char *argv[]) -{ - static char gsbuf[HOST_NAME_MAX+1]; - - if (argc > 3) { - fprintf(ttyout, "usage: %s [on | off | host [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 = strdup(argv[2]); - if (gateport == NULL) - err(1, NULL); - } - 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. - */ -/*ARGSUSED*/ -void -setglob(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &doglob, "Globbing"); -} - -/* - * Toggle preserving modification times on retrieved files. - */ -/*ARGSUSED*/ -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. - */ -/*ARGSUSED*/ -void -setdebug(int argc, char *argv[]) -{ - if (argc > 2) { - fprintf(ttyout, "usage: %s [on | off | debuglevel]\n", argv[0]); - code = -1; - return; - } else if (argc == 2) { - if (strcasecmp(argv[1], "on") == 0) - debug = 1; - else if (strcasecmp(argv[1], "off") == 0) - debug = 0; - else { - const char *errstr; - int val; - - val = strtonum(argv[1], 0, INT_MAX, &errstr); - if (errstr) { - fprintf(ttyout, "debugging value is %s: %s\n", - errstr, argv[1]); - code = -1; - return; - } - debug = val; - } - } else - debug = !debug; - if (debug) - options |= SO_DEBUG; - else - options &= ~SO_DEBUG; - fprintf(ttyout, "Debugging %s (debug=%d).\n", onoff(debug), debug); - code = debug > 0; -} - -/* - * Set current working directory on local machine. - */ -void -lcd(int argc, char *argv[]) -{ - char buf[PATH_MAX]; - char *oldargv1; - - if (argc < 2) - argc++, argv[1] = home; - if (argc != 2) { - fprintf(ttyout, "usage: %s [local-directory]\n", argv[0]); - code = -1; - return; - } - oldargv1 = argv[1]; - if (!globulize(&argv[1])) { - code = -1; - return; - } - if (chdir(argv[1]) < 0) { - warn("local: %s", argv[1]); - code = -1; - } else { - if (getcwd(buf, sizeof(buf)) != NULL) - fprintf(ttyout, "Local directory now %s\n", buf); - else - warn("getcwd: %s", argv[1]); - code = 0; - } - if (oldargv1 != argv[1]) /* free up after globulize() */ - free(argv[1]); -} - -/* - * Delete a single file. - */ -void -deletecmd(int argc, char *argv[]) -{ - - if ((argc < 2 && !another(&argc, &argv, "remote-file")) || argc > 2) { - fprintf(ttyout, "usage: %s remote-file\n", argv[0]); - code = -1; - return; - } - (void)command("DELE %s", argv[1]); -} - -/* - * Delete multiple files. - */ -void -mdelete(int argc, char *argv[]) -{ - sig_t oldintr; - char *cp; - - if (argc < 2 && !another(&argc, &argv, "remote-files")) { - fprintf(ttyout, "usage: %s remote-files\n", argv[0]); - code = -1; - return; - } - mname = argv[0]; - mflag = 1; - oldintr = signal(SIGINT, mabort); - (void)setjmp(jabort); - while ((cp = remglob(argv, 0, NULL)) != NULL) { - if (*cp == '\0') { - mflag = 0; - continue; - } - if (mflag && confirm(argv[0], cp)) { - (void)command("DELE %s", cp); - if (!mflag && fromatty) { - if (confirm(argv[0], NULL)) - mflag = 1; - } - } - } - (void)signal(SIGINT, oldintr); - mflag = 0; -} - -/* - * Rename a remote file. - */ -void -renamefile(int argc, char *argv[]) -{ - - if (argc < 2 && !another(&argc, &argv, "from-name")) - goto usage; - if ((argc < 3 && !another(&argc, &argv, "to-name")) || argc > 3) { -usage: - fprintf(ttyout, "usage: %s from-name to-name\n", argv[0]); - code = -1; - return; - } - if (command("RNFR %s", argv[1]) == CONTINUE) - (void)command("RNTO %s", argv[2]); -} - -/* - * Get a directory listing of remote files. - */ -void -ls(int argc, char *argv[]) -{ - const char *cmd; - char *oldargv2, *globargv2; - - if (argc < 2) - argc++, argv[1] = NULL; - if (argc < 3) - argc++, argv[2] = "-"; - if (argc > 3) { - fprintf(ttyout, "usage: %s [remote-directory [local-file]]\n", - argv[0]); - code = -1; - return; - } - cmd = strcmp(argv[0], "nlist") == 0 ? "NLST" : "LIST"; - oldargv2 = argv[2]; - if (strcmp(argv[2], "-") && !globulize(&argv[2])) { - code = -1; - return; - } - globargv2 = argv[2]; - if (strcmp(argv[2], "-") && *argv[2] != '|' && (!globulize(&argv[2]) || - !confirm("output to local-file:", argv[2]))) { - code = -1; - goto freels; - } - recvrequest(cmd, argv[2], argv[1], "w", 0, 0); - - /* flush results in case commands are coming from a pipe */ - fflush(ttyout); -freels: - if (argv[2] != globargv2) /* free up after globulize() */ - free(argv[2]); - if (globargv2 != oldargv2) - free(globargv2); -} - -/* - * Get a directory listing of multiple remote files. - */ -void -mls(int argc, char *argv[]) -{ - sig_t oldintr; - int i; - char lmode[1], *dest, *odest; - - if (argc < 2 && !another(&argc, &argv, "remote-files")) - goto usage; - if (argc < 3 && !another(&argc, &argv, "local-file")) { -usage: - fprintf(ttyout, "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 (!globulize(&dest) || - !confirm("output to local-file:", dest)) { - code = -1; - return; - } - mname = argv[0]; - mflag = 1; - oldintr = signal(SIGINT, mabort); - (void)setjmp(jabort); - for (i = 1; mflag && i < argc-1; ++i) { - *lmode = (i == 1) ? 'w' : 'a'; - recvrequest("LIST", dest, argv[i], lmode, 0, 0); - if (!mflag && fromatty) { - if (confirm(argv[0], NULL)) - mflag ++; - } - } - (void)signal(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; - sig_t old1, old2; - char shellnam[PATH_MAX], *shellp, *namep; - int wait_status; - - old1 = signal (SIGINT, SIG_IGN); - old2 = signal (SIGQUIT, SIG_IGN); - if ((pid = fork()) == 0) { - (void)closefrom(3); - (void)signal(SIGINT, SIG_DFL); - (void)signal(SIGQUIT, SIG_DFL); - shellp = getenv("SHELL"); - if (shellp == NULL || *shellp == '\0') - shellp = _PATH_BSHELL; - namep = strrchr(shellp, '/'); - if (namep == NULL) - namep = shellp; - shellnam[0] = '-'; - (void)strlcpy(shellnam + 1, ++namep, sizeof(shellnam) - 1); - if (strcmp(namep, "sh") != 0) - shellnam[0] = '+'; - if (debug) { - fputs(shellp, ttyout); - fputc('\n', ttyout); - (void)fflush(ttyout); - } - if (argc > 1) { - execl(shellp, shellnam, "-c", altarg, (char *)NULL); - } - else { - execl(shellp, shellnam, (char *)NULL); - } - warn("%s", shellp); - code = -1; - exit(1); - } - if (pid > 0) - while (wait(&wait_status) != pid) - ; - (void)signal(SIGINT, old1); - (void)signal(SIGQUIT, old2); - if (pid == -1) { - warn("Try again later"); - code = -1; - } - else { - code = 0; - } -} - -/* - * Send new user information (re-login) - */ -void -user(int argc, char *argv[]) -{ - char acctname[80]; - int n, aflag = 0; - - if (argc < 2) - (void)another(&argc, &argv, "username"); - if (argc < 2 || argc > 4) { - fprintf(ttyout, "usage: %s username [password [account]]\n", - argv[0]); - code = -1; - return; - } - n = command("USER %s", argv[1]); - if (n == CONTINUE) { - if (argc < 3 ) - argv[2] = getpass("Password:"), argc++; - n = command("PASS %s", argv[2]); - } - if (n == CONTINUE) { - if (argc < 4) { - (void)fputs("Account: ", ttyout); - (void)fflush(ttyout); - if (fgets(acctname, sizeof(acctname), stdin) == NULL) { - clearerr(stdin); - goto fail; - } - - acctname[strcspn(acctname, "\n")] = '\0'; - - argv[3] = acctname; - argc++; - } - n = command("ACCT %s", argv[3]); - aflag++; - } - if (n != COMPLETE) { - fail: - fputs("Login failed.\n", ttyout); - return; - } - if (!aflag && argc == 4) { - (void)command("ACCT %s", argv[3]); - } - connected = -1; -} - -/* - * Print working directory on remote machine. - */ -/*ARGSUSED*/ -void -pwd(int argc, char *argv[]) -{ - int oldverbose = verbose; - - /* - * If we aren't verbose, this doesn't do anything! - */ - verbose = 1; - if (command("PWD") == ERROR && code == 500) { - fputs("PWD command not recognized, trying XPWD.\n", ttyout); - (void)command("XPWD"); - } - verbose = oldverbose; -} - -/* - * Print working directory on local machine. - */ -/* ARGSUSED */ -void -lpwd(int argc, char *argv[]) -{ - char buf[PATH_MAX]; - - if (getcwd(buf, sizeof(buf)) != NULL) - fprintf(ttyout, "Local directory %s\n", buf); - else - warn("getcwd"); - code = 0; -} - -/* - * Make a directory. - */ -void -makedir(int argc, char *argv[]) -{ - - if ((argc < 2 && !another(&argc, &argv, "directory-name")) || - argc > 2) { - fprintf(ttyout, "usage: %s directory-name\n", argv[0]); - code = -1; - return; - } - if (command("MKD %s", argv[1]) == ERROR && code == 500) { - if (verbose) - fputs("MKD command not recognized, trying XMKD.\n", ttyout); - (void)command("XMKD %s", argv[1]); - } -} - -/* - * Remove a directory. - */ -void -removedir(int argc, char *argv[]) -{ - - if ((argc < 2 && !another(&argc, &argv, "directory-name")) || - argc > 2) { - fprintf(ttyout, "usage: %s directory-name\n", argv[0]); - code = -1; - return; - } - if (command("RMD %s", argv[1]) == ERROR && code == 500) { - if (verbose) - fputs("RMD command not recognized, trying XRMD.\n", ttyout); - (void)command("XRMD %s", argv[1]); - } -} - -/* - * Send a line, verbatim, to the remote machine. - */ -void -quote(int argc, char *argv[]) -{ - - if (argc < 2 && !another(&argc, &argv, "command line to send")) { - fprintf(ttyout, "usage: %s arg ...\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 < 2 && !another(&argc, &argv, "arguments to SITE command")) { - fprintf(ttyout, "usage: %s arg ...\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, len; - char buf[BUFSIZ]; /* must be >= sizeof(line) */ - - (void)strlcpy(buf, initial, sizeof(buf)); - if (argc > 1) { - for (i = 1, len = strlen(buf); i < argc && len < sizeof(buf)-1; i++) { - /* Space for next arg */ - if (len > 1) - buf[len++] = ' '; - - /* Sanity check */ - if (len >= sizeof(buf) - 1) - break; - - /* Copy next argument, NUL terminate always */ - strlcpy(&buf[len], argv[i], sizeof(buf) - len); - - /* Update string length */ - len = strlen(buf); - } - } - - /* Make double (triple?) sure the sucker is NUL terminated */ - buf[sizeof(buf) - 1] = '\0'; - - if (command("%s", buf) == PRELIM) { - while (getreply(0) == PRELIM) - continue; - } -} - -void -do_chmod(int argc, char *argv[]) -{ - - if (argc < 2 && !another(&argc, &argv, "mode")) - goto usage; - if ((argc < 3 && !another(&argc, &argv, "file")) || argc > 3) { -usage: - fprintf(ttyout, "usage: %s mode file\n", argv[0]); - code = -1; - return; - } - (void)command("SITE CHMOD %s %s", argv[1], argv[2]); -} - -void -do_umask(int argc, char *argv[]) -{ - int oldverbose = verbose; - - verbose = 1; - (void)command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]); - verbose = oldverbose; -} - -void -idle(int argc, char *argv[]) -{ - int oldverbose = verbose; - - verbose = 1; - (void)command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]); - verbose = oldverbose; -} - -/* - * Ask the other side for help. - */ -void -rmthelp(int argc, char *argv[]) -{ - int oldverbose = verbose; - - verbose = 1; - (void)command(argc == 1 ? "HELP" : "HELP %s", argv[1]); - verbose = oldverbose; -} - -/* - * Terminate session and exit. - */ -/*ARGSUSED*/ -void -quit(int argc, char *argv[]) -{ - - if (connected) - disconnect(0, 0); - pswitch(1); - if (connected) { - disconnect(0, 0); - } - exit(0); -} - -void -account(int argc, char *argv[]) -{ - char *ap; - - if (argc > 2) { - fprintf(ttyout, "usage: %s [password]\n", argv[0]); - code = -1; - return; - } - else if (argc == 2) - ap = argv[1]; - else - ap = getpass("Account:"); - (void)command("ACCT %s", ap); -} - -jmp_buf abortprox; - -/* ARGSUSED */ -void -proxabort(int signo) -{ - int save_errno = errno; - - alarmtimer(0); - if (!proxy) { - pswitch(1); - } - if (connected) { - proxflag = 1; - } - else { - proxflag = 0; - } - pswitch(0); - errno = save_errno; - longjmp(abortprox, 1); -} - -void -doproxy(int argc, char *argv[]) -{ - struct cmd *c; - int cmdpos; - sig_t oldintr; - - if (argc < 2 && !another(&argc, &argv, "command")) { - fprintf(ttyout, "usage: %s command\n", argv[0]); - code = -1; - return; - } - c = getcmd(argv[1]); - if (c == (struct cmd *) -1) { - fputs("?Ambiguous command.\n", ttyout); - (void)fflush(ttyout); - code = -1; - return; - } - if (c == 0) { - fputs("?Invalid command.\n", ttyout); - (void)fflush(ttyout); - code = -1; - return; - } - if (!c->c_proxy) { - fputs("?Invalid proxy command.\n", ttyout); - (void)fflush(ttyout); - code = -1; - return; - } - if (setjmp(abortprox)) { - code = -1; - return; - } - oldintr = signal(SIGINT, proxabort); - pswitch(1); - if (c->c_conn && !connected) { - fputs("Not connected.\n", ttyout); - (void)fflush(ttyout); - pswitch(0); - (void)signal(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); - (*c->c_handler)(argc-1, argv+1); - if (connected) { - proxflag = 1; - } - else { - proxflag = 0; - } - pswitch(0); - (void)signal(SIGINT, oldintr); -} - -void -setcase(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &mcase, "Case mapping"); -} - -void -setcr(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &crflag, "Carriage Return stripping"); -} - -void -setntrans(int argc, char *argv[]) -{ - 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)); -} - -void -setnmap(int argc, char *argv[]) -{ - char *cp; - - if (argc == 1) { - mapflag = 0; - fputs("Nmap off.\n", ttyout); - code = mapflag; - return; - } - if ((argc < 3 && !another(&argc, &argv, "outpattern")) || argc > 3) { - fprintf(ttyout, "usage: %s [inpattern outpattern]\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)strncpy(mapin, altarg, PATH_MAX - 1); - while (*++cp == ' ') - continue; - (void)strncpy(mapout, cp, PATH_MAX - 1); -} - -void -setpassive(int argc, char *argv[]) -{ - - code = togglevar(argc, argv, &passivemode, - verbose ? "Passive mode" : NULL); -} - -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"); -} - -/* change directory to parent directory */ -/* ARGSUSED */ -void -cdup(int argc, char *argv[]) -{ - int r; - - 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; -} - -/* - * Restart transfer at specific point - */ -void -restart(int argc, char *argv[]) -{ - off_t nrestart_point; - char *ep; - - if (argc != 2) - fputs("restart: offset not specified.\n", ttyout); - else { - nrestart_point = strtoll(argv[1], &ep, 10); - if (nrestart_point == LLONG_MAX || *ep != '\0') - fputs("restart: invalid offset.\n", ttyout); - else { - fprintf(ttyout, "Restarting at %lld. Execute get, put " - "or append to initiate transfer\n", - (long long)nrestart_point); - restart_point = nrestart_point; - } - } -} - -/* - * Show remote system type - */ -/* ARGSUSED */ -void -syst(int argc, char *argv[]) -{ - - (void)command("SYST"); -} - -void -macdef(int argc, char *argv[]) -{ - char *tmp; - int c; - - 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) { - fprintf(ttyout, "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 < 2 && !another(&argc, &argv, "file")) || argc > 2) { - fprintf(ttyout, "usage: %s file\n", argv[0]); - code = -1; - return; - } - size = remotesize(argv[1], 1); - if (size != -1) - fprintf(ttyout, "%s\t%lld\n", argv[1], (long long)size); - code = size; -} - -/* - * Get last modification time of file on remote machine - */ -void -modtime(int argc, char *argv[]) -{ - time_t mtime; - - if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) { - fprintf(ttyout, "usage: %s file\n", argv[0]); - code = -1; - return; - } - mtime = remotemodtime(argv[1], 1); - if (mtime != -1) - fprintf(ttyout, "%s\t%s", argv[1], asctime(localtime(&mtime))); - code = mtime; -} - -/* - * Show status on remote machine - */ -void -rmtstatus(int argc, char *argv[]) -{ - - (void)command(argc > 1 ? "STAT %s" : "STAT" , argv[1]); -} - -/* - * Get file if modtime is more recent than current file - */ -void -newer(int argc, char *argv[]) -{ - - (void)getit(argc, argv, -1, "w"); -} - -/* - * Display one file through $PAGER (defaults to "more"). - */ -void -page(int argc, char *argv[]) -{ - off_t orestart_point; - int ohash, overbose; - char *p, *pager, *oldargv1; - - if ((argc < 2 && !another(&argc, &argv, "file")) || argc > 2) { - fprintf(ttyout, "usage: %s file\n", argv[0]); - code = -1; - return; - } - oldargv1 = argv[1]; - if (!globulize(&argv[1])) { - code = -1; - return; - } - p = getenv("PAGER"); - if (p == NULL || (*p == '\0')) - p = PAGER; - if (asprintf(&pager, "|%s", p) == -1) - errx(1, "Can't allocate memory for $PAGER"); - - orestart_point = restart_point; - ohash = hash; - overbose = verbose; - restart_point = hash = verbose = 0; - recvrequest("RETR", pager, argv[1], "r+w", 1, 0); - (void)free(pager); - restart_point = orestart_point; - hash = ohash; - verbose = overbose; - if (oldargv1 != argv[1]) /* free up after globulize() */ - free(argv[1]); -} - -#endif /* !SMALL */ - diff --git a/usr.bin/ftp/cmds.h b/usr.bin/ftp/cmds.h deleted file mode 100644 index 1ddae53a6d3..00000000000 --- a/usr.bin/ftp/cmds.h +++ /dev/null @@ -1,83 +0,0 @@ -/* $OpenBSD: cmds.h,v 1.2 2015/01/30 04:45:45 tedu Exp $ */ - -/* - * Copyright (c) 2009 Martynas Venckus <martynas@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -void setascii(int, char **); -void setftmode(int, char **); -void setform(int, char **); -void setstruct(int, char **); -void reput(int, char **); -void put(int, char **); -void putit(int, char **, int); -void mput(int, char **); -void reget(int, char **); -char *onoff(int); -void status(int, char **); -int togglevar(int, char **, int *, const char *); -void setbell(int, char **); -void setedit(int, char **); -void setepsv4(int, char **); -void settrace(int, char **); -void sethash(int, char **); -void setverbose(int, char **); -void setport(int, char **); -void setprogress(int, char **); -void setprompt(int, char **); -void setgate(int, char **); -void setglob(int, char **); -void setpreserve(int, char **); -void setdebug(int, char **); -void lcd(int, char **); -void deletecmd(int, char **); -void mdelete(int, char **); -void renamefile(int, char **); -void ls(int, char **); -void mls(int, char **); -void shell(int, char **); -void user(int, char **); -void pwd(int, char **); -void lpwd(int, char **); -void makedir(int, char **); -void removedir(int, char **); -void quote(int, char **); -void site(int, char **); -void quote1(const char *, int, char **); -void do_chmod(int, char **); -void do_umask(int, char **); -void idle(int, char **); -void rmthelp(int, char **); -void quit(int, char **); -void account(int, char **); -void proxabort(int); -void doproxy(int, char **); -void setcase(int, char **); -void setcr(int, char **); -void setntrans(int, char **); -void setnmap(int, char **); -void setpassive(int, char **); -void setsunique(int, char **); -void setrunique(int, char **); -void cdup(int, char **); -void restart(int, char **); -void syst(int, char **); -void macdef(int, char **); -void sizecmd(int, char **); -void modtime(int, char **); -void rmtstatus(int, char **); -void newer(int, char **); -void page(int, char **); - diff --git a/usr.bin/ftp/cmdtab.c b/usr.bin/ftp/cmdtab.c deleted file mode 100644 index 20361f6f420..00000000000 --- a/usr.bin/ftp/cmdtab.c +++ /dev/null @@ -1,215 +0,0 @@ -/* $OpenBSD: cmdtab.c,v 1.29 2017/01/21 08:33:07 krw Exp $ */ -/* $NetBSD: cmdtab.c,v 1.17 1997/08/18 10:20:17 lukem Exp $ */ - -/* - * 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. - */ - -#ifndef SMALL - -#include <stdio.h> -#include "ftp_var.h" -#include "cmds.h" - -/* - * User FTP -- Command Tables. - */ - -char accounthelp[] = "send account command to remote server"; -char appendhelp[] = "append to a file"; -char asciihelp[] = "set ascii transfer type"; -char beephelp[] = "beep when command completed"; -char binaryhelp[] = "set binary transfer type"; -char casehelp[] = "toggle mget upper/lower case id mapping"; -char cdhelp[] = "change remote working directory"; -char cduphelp[] = "change remote working directory to parent directory"; -char chmodhelp[] = "change file permissions of remote file"; -char connecthelp[] = "connect to remote ftp server"; -char crhelp[] = "toggle carriage return stripping on ascii gets"; -char debughelp[] = "toggle/set debugging mode"; -char deletehelp[] = "delete remote file"; -char dirhelp[] = "list contents of remote directory"; -char disconhelp[] = "terminate ftp session"; -char domachelp[] = "execute macro"; -char edithelp[] = "toggle command line editing"; -char epsv4help[] = "toggle use of EPSV/EPRT on IPv4 ftp"; -char formhelp[] = "set file transfer format"; -char gatehelp[] = "toggle gate-ftp; specify host[:port] to change proxy"; -char globhelp[] = "toggle metacharacter expansion of local file names"; -char hashhelp[] = "toggle printing `#' marks; specify number to set size"; -char helphelp[] = "print local help information"; -char idlehelp[] = "get (set) idle timer on remote side"; -char lcdhelp[] = "change local working directory"; -char lpwdhelp[] = "print local working directory"; -char lshelp[] = "list contents of remote directory"; -char macdefhelp[] = "define a macro"; -char mdeletehelp[] = "delete multiple files"; -char mdirhelp[] = "list contents of multiple remote directories"; -char mgethelp[] = "get multiple files"; -char mkdirhelp[] = "make directory on the remote machine"; -char mlshelp[] = "list contents of multiple remote directories"; -char modehelp[] = "set file transfer mode"; -char modtimehelp[] = "show last modification time of remote file"; -char mputhelp[] = "send multiple files"; -char newerhelp[] = "get file if remote file is newer than local file "; -char nlisthelp[] = "nlist contents of remote directory"; -char nmaphelp[] = "set templates for default file name mapping"; -char ntranshelp[] = "set translation table for default file name mapping"; -char pagehelp[] = "view a remote file through your pager"; -char passivehelp[] = "toggle passive transfer mode"; -char porthelp[] = "toggle use of PORT/LPRT cmd for each data connection"; -char preservehelp[] ="toggle preservation of modification time of " - "retrieved files"; -char progresshelp[] ="toggle transfer progress meter"; -char prompthelp[] = "toggle interactive prompting on multiple commands"; -char proxyhelp[] = "issue command on alternate connection"; -char pwdhelp[] = "print working directory on remote machine"; -char quithelp[] = "terminate ftp session and exit"; -char quotehelp[] = "send arbitrary ftp command"; -char receivehelp[] = "receive file"; -char regethelp[] = "get file restarting at end of local file"; -char reputhelp[] = "put file restarting at end of remote file"; -char remotehelp[] = "get help from remote server"; -char renamehelp[] = "rename file"; -char resethelp[] = "clear queued command replies"; -char restarthelp[]= "restart file transfer at bytecount"; -char rmdirhelp[] = "remove directory on the remote machine"; -char rmtstatushelp[]="show status of remote machine"; -char runiquehelp[] = "toggle store unique for local files"; -char sendhelp[] = "send one file"; -char shellhelp[] = "escape to the shell"; -char sitehelp[] = "send site specific command to remote server\n" - "\t\tTry \"rhelp site\" or \"site help\" " - "for more information"; -char sizecmdhelp[] = "show size of remote file"; -char statushelp[] = "show current status"; -char structhelp[] = "set file transfer structure"; -char suniquehelp[] = "toggle store unique on remote machine"; -char systemhelp[] = "show remote system type"; -char tracehelp[] = "toggle packet tracing"; -char typehelp[] = "set file transfer type"; -char umaskhelp[] = "get (set) umask on remote side"; -char userhelp[] = "send new user information"; -char verbosehelp[] = "toggle verbose mode"; - -#define CMPL(x) __STRING(x), -#define CMPL0 "", -#define H(x) x - -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) deletecmd }, - { "dir", H(dirhelp), 1, 1, 1, CMPL(rl) ls }, - { "disconnect", H(disconhelp), 0, 1, 1, CMPL0 disconnect }, - { "edit", H(edithelp), 0, 0, 0, CMPL0 setedit }, - { "epsv4", H(epsv4help), 0, 0, 0, CMPL0 setepsv4 }, - { "exit", H(quithelp), 0, 0, 0, CMPL0 quit }, - { "form", H(formhelp), 0, 1, 1, CMPL0 setform }, - { "ftp", H(connecthelp), 0, 0, 1, CMPL0 setpeer }, - { "get", H(receivehelp), 1, 1, 1, CMPL(rl) get }, - { "gate", H(gatehelp), 0, 0, 0, CMPL0 setgate }, - { "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 idle }, - { "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 }, - { "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(mdirhelp), 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 }, - { "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 }, - { "msend", H(mputhelp), 1, 1, 1, CMPL(L) mput }, - { "newer", H(newerhelp), 1, 1, 1, CMPL(r) newer }, - { "nlist", H(nlisthelp), 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 }, - { "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 }, - { "recv", H(receivehelp), 1, 1, 1, CMPL(rl) get }, - { "reget", H(regethelp), 1, 1, 1, CMPL(rl) reget }, - { "rename", H(renamehelp), 0, 1, 1, CMPL(rr) renamefile }, - { "reput", H(reputhelp), 1, 1, 1, CMPL(lr) reput }, - { "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 }, - { "site", H(sitehelp), 0, 1, 1, CMPL0 site }, - { "size", H(sizecmdhelp), 1, 1, 1, CMPL(r) sizecmd }, - { "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 }, - { "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 }, - { "user", H(userhelp), 0, 1, 1, CMPL0 user }, - { "verbose", H(verbosehelp), 0, 0, 0, CMPL0 setverbose }, - { "?", H(helphelp), 0, 0, 1, CMPL(C) help }, - { 0 } -}; - -int NCMDS = (sizeof(cmdtab) / sizeof(cmdtab[0])) - 1; - -#endif /* !SMALL */ - diff --git a/usr.bin/ftp/complete.c b/usr.bin/ftp/complete.c deleted file mode 100644 index 3c137e2620a..00000000000 --- a/usr.bin/ftp/complete.c +++ /dev/null @@ -1,381 +0,0 @@ -/* $OpenBSD: complete.c,v 1.31 2017/08/01 15:04:44 anton Exp $ */ -/* $NetBSD: complete.c,v 1.10 1997/08/18 10:20:18 lukem Exp $ */ - -/*- - * Copyright (c) 1997 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 SMALL - -/* - * FTP user program - command and file completion routines - */ - -#include <ctype.h> -#include <err.h> -#include <dirent.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "ftp_var.h" - -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_remote(char *, int); -static void ftpvis(char *, size_t, const char *, size_t); - -static int -comparstr(const void *a, const void *b) -{ - return (strcmp(*(char **)a, *(char **)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 - */ -static unsigned char -complete_ambiguous(char *word, int list, StringList *words) -{ - char insertstr[PATH_MAX * 2]; - char *lastmatch; - int 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 */ - char *p = words->sl_str[0] + wordlen; - ftpvis(insertstr, sizeof(insertstr), p, strlen(p)); - if (el_insertstr(el, insertstr) == -1) - return (CC_ERROR); - else - return (CC_REFRESH); - } - - if (!list) { - 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 - /* - * XXX: really want CC_REFRESH_BEEP - */ - return (CC_REFRESH); - } - } - - 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 = 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) - sl_add(words, c->c_name); - } - - rv = complete_ambiguous(word, list, words); - sl_free(words, 0); - return (rv); -} - -/* - * Complete a local file - */ -static unsigned char -complete_local(char *word, int list) -{ - StringList *words; - char dir[PATH_MAX]; - char *file; - DIR *dd; - struct dirent *dp; - unsigned char rv; - - 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, (size_t)(file - word) + 1); - } - file++; - } - - if ((dd = opendir(dir)) == NULL) - return (CC_ERROR); - - words = sl_init(); - - for (dp = readdir(dd); dp != NULL; dp = readdir(dd)) { - if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) - continue; - if (strlen(file) > dp->d_namlen) - continue; - if (strncmp(file, dp->d_name, strlen(file)) == 0) { - char *tcp; - - tcp = strdup(dp->d_name); - if (tcp == NULL) - errx(1, "Can't allocate memory for local dir"); - sl_add(words, tcp); - } - } - closedir(dd); - - rv = complete_ambiguous(file, list, words); - 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[PATH_MAX]; - StringList *words; - char dir[PATH_MAX]; - char *file, *cp; - int i; - unsigned char rv; - - char *dummyargv[] = { "complete", dir, NULL }; - - if ((file = strrchr(word, '/')) == NULL) { - dir[0] = '.'; - dir[1] = '\0'; - file = word; - } else { - cp = file; - while (*cp == '/' && cp > word) - cp--; - (void)strlcpy(dir, word, (size_t)(cp - word + 2)); - file++; - } - - if (dirchange || strcmp(dir, lastdir) != 0) { /* dir not cached */ - char *emesg; - - sl_free(dirlist, 1); - dirlist = sl_init(); - - mflag = 1; - emesg = NULL; - if (debug) - (void)putc('\n', ttyout); - 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 = strdup(tcp); - if (tcp == NULL) - errx(1, "Can't allocate memory for remote dir"); - 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 = 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) - sl_add(words, cp); - } - rv = complete_ambiguous(file, list, words); - sl_free(words, 0); - return (rv); -} - -/* - * Generic complete routine - */ -unsigned char -complete(EditLine *el, int ch) -{ - static char word[FTPBUFLEN]; - static int lastc_argc, lastc_argo; - struct cmd *c; - const LineInfo *lf; - int celems, dolist; - size_t len; - - lf = el_line(el); - len = lf->lastchar - lf->buffer; - if (len >= sizeof(line)) - return (CC_ERROR); - (void)memcpy(line, lf->buffer, len); - line[len] = '\0'; - 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], cursor_argo) == 0) - dolist = 1; - else if (cursor_argo) - memcpy(word, margv[cursor_argc], cursor_argo); - 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); - - switch (c->c_complete[cursor_argc - 1]) { - case 'l': /* local complete */ - case 'L': - return (complete_local(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)); - case 'c': /* command complete */ - case 'C': - return (complete_command(word, dolist)); - case 'n': /* no complete */ - return (CC_ERROR); - } - - return (CC_ERROR); -} - -/* - * Copy characters from src into dst, \ quoting characters that require it. - */ -static void -ftpvis(char *dst, size_t dstlen, const char *src, size_t srclen) -{ - size_t di, si; - - di = si = 0; - while (di + 1 < dstlen && si < srclen && src[si] != '\0') { - 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 + 3 >= dstlen) - break; - dst[di++] = '\\'; - /* FALLTHROUGH */ - default: - dst[di++] = src[si++]; - } - } - if (dstlen != 0) - dst[di] = '\0'; -} -#endif /* !SMALL */ diff --git a/usr.bin/ftp/cookie.c b/usr.bin/ftp/cookie.c deleted file mode 100644 index 22ea989ea3c..00000000000 --- a/usr.bin/ftp/cookie.c +++ /dev/null @@ -1,231 +0,0 @@ -/* $OpenBSD: cookie.c,v 1.7 2017/01/21 08:33:07 krw Exp $ */ - -/* - * Copyright (c) 2007 Pierre-Yves Ritschard <pyr@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef NOSSL - -#include <sys/types.h> -#include <sys/queue.h> - -#include <err.h> -#include <errno.h> -#include <fnmatch.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <time.h> - -#include "ftp_var.h" - -struct cookie { - TAILQ_ENTRY(cookie) entry; - TAILQ_ENTRY(cookie) tempentry; - u_int8_t flags; -#define F_SECURE 0x01 -#define F_TAILMATCH 0x02 -#define F_NOEXPIRY 0x04 -#define F_MATCHPATH 0x08 - time_t expires; - char *domain; - char *path; - char *key; - char *val; -}; -TAILQ_HEAD(cookiejar, cookie); - -typedef enum { - DOMAIN = 0, TAILMATCH = 1, PATH = 2, SECURE = 3, - EXPIRES = 4, NAME = 5, VALUE = 6, DONE = 7 -} field_t; - -static struct cookiejar jar; - -void -cookie_load(void) -{ - field_t field; - size_t len; - time_t date; - char *line; - char *lbuf; - char *param; - const char *estr; - FILE *fp; - struct cookie *ck; - - if (cookiefile == NULL) - return; - - TAILQ_INIT(&jar); - fp = fopen(cookiefile, "r"); - if (fp == NULL) - err(1, "cannot open cookie file %s", cookiefile); - date = time(NULL); - lbuf = NULL; - while ((line = fgetln(fp, &len)) != NULL) { - if (line[len - 1] == '\n') { - line[len - 1] = '\0'; - --len; - } else { - if ((lbuf = malloc(len + 1)) == NULL) - err(1, NULL); - memcpy(lbuf, line, len); - lbuf[len] = '\0'; - line = lbuf; - } - line[strcspn(line, "\r")] = '\0'; - - line += strspn(line, " \t"); - if ((*line == '#') || (*line == '\0')) { - continue; - } - field = DOMAIN; - ck = calloc(1, sizeof(*ck)); - if (ck == NULL) - err(1, NULL); - while ((param = strsep(&line, "\t")) != NULL) { - switch (field) { - case DOMAIN: - if (*param == '.') { - if (asprintf(&ck->domain, - "*%s", param) == -1) - err(1, NULL); - } else { - ck->domain = strdup(param); - if (ck->domain == NULL) - err(1, NULL); - } - break; - case TAILMATCH: - if (strcasecmp(param, "TRUE") == 0) { - ck->flags |= F_TAILMATCH; - } else if (strcasecmp(param, "FALSE") != 0) { - errx(1, "invalid cookie file"); - } - break; - case PATH: - if (strcmp(param, "/") != 0) { - ck->flags |= F_MATCHPATH; - if (asprintf(&ck->path, - "%s*", param) == -1) - err(1, NULL); - } - break; - case SECURE: - if (strcasecmp(param, "TRUE") == 0) { - ck->flags |= F_SECURE; - } else if (strcasecmp(param, "FALSE") != 0) { - errx(1, "invalid cookie file"); - } - break; - case EXPIRES: - /* - * rely on sizeof(time_t) being 4 - */ - ck->expires = strtonum(param, 0, - INT_MAX, &estr); - if (estr) { - if (errno == ERANGE) - ck->flags |= F_NOEXPIRY; - else - errx(1, "invalid cookie file"); - } - break; - case NAME: - ck->key = strdup(param); - if (ck->key == NULL) - err(1, NULL); - break; - case VALUE: - ck->val = strdup(param); - if (ck->val == NULL) - err(1, NULL); - break; - case DONE: - errx(1, "invalid cookie file"); - break; - } - field++; - } - if (field != DONE) - errx(1, "invalid cookie file"); - if (ck->expires < date && !(ck->flags & F_NOEXPIRY)) { - free(ck->val); - free(ck->key); - free(ck->path); - free(ck->domain); - free(ck); - } else - TAILQ_INSERT_TAIL(&jar, ck, entry); - } - free(lbuf); - fclose(fp); -} - -void -cookie_get(const char *domain, const char *path, int secure, char **pstr) -{ - size_t len; - size_t headlen; - char *head; - char *str; - struct cookie *ck; - struct cookiejar tempjar; - - *pstr = NULL; - - if (cookiefile == NULL) - return; - - TAILQ_INIT(&tempjar); - len = strlen("Cookie\r\n"); - - TAILQ_FOREACH(ck, &jar, entry) { - if (fnmatch(ck->domain, domain, 0) == 0 && - (secure || !(ck->flags & F_SECURE))) { - - if (ck->flags & F_MATCHPATH && - fnmatch(ck->path, path, 0) != 0) - continue; - - len += strlen(ck->key) + strlen(ck->val) + - strlen("; ="); - TAILQ_INSERT_TAIL(&tempjar, ck, tempentry); - } - } - if (TAILQ_EMPTY(&tempjar)) - return; - len += 1; - str = malloc(len); - if (str == NULL) - err(1, NULL); - - (void)strlcpy(str, "Cookie:", len); - TAILQ_FOREACH(ck, &tempjar, tempentry) { - head = str + strlen(str); - headlen = len - strlen(str); - - snprintf(head, headlen, "%s %s=%s", - (ck == TAILQ_FIRST(&tempjar))? "" : ";", ck->key, ck->val); - } - if (strlcat(str, "\r\n", len) >= len) - errx(1, "cookie header truncated"); - *pstr = str; -} - -#endif /* !SMALL */ - diff --git a/usr.bin/ftp/domacro.c b/usr.bin/ftp/domacro.c deleted file mode 100644 index af554c98267..00000000000 --- a/usr.bin/ftp/domacro.c +++ /dev/null @@ -1,149 +0,0 @@ -/* $OpenBSD: domacro.c,v 1.19 2017/01/21 08:33:07 krw Exp $ */ -/* $NetBSD: domacro.c,v 1.10 1997/07/20 09:45:45 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. - */ - -#ifndef SMALL - -#include <ctype.h> -#include <signal.h> -#include <stdio.h> -#include <string.h> - -#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; - - if (argc < 2 && !another(&argc, &argv, "macro name")) { - fprintf(ttyout, "usage: %s macro-name\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; - } - /* FALLTHROUGH */ - 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); - fputc('\n', ttyout); - } - (*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; - } -} - -#endif /* !SMALL */ - diff --git a/usr.bin/ftp/extern.h b/usr.bin/ftp/extern.h deleted file mode 100644 index 3f573b8760d..00000000000 --- a/usr.bin/ftp/extern.h +++ /dev/null @@ -1,150 +0,0 @@ -/* $OpenBSD: extern.h,v 1.49 2017/01/21 08:33:07 krw Exp $ */ -/* $NetBSD: extern.h,v 1.17 1997/08/18 10:20:19 lukem Exp $ */ - -/* - * 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. - */ - -/*- - * 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 - */ - -#include <sys/types.h> - -void abort_remote(FILE *); -void abortpt(int); -void abortrecv(int); -void alarmtimer(int); -int another(int *, char ***, const char *); -int auto_fetch(int, char **, char *); -void blkfree(char **); -void cdup(int, char **); -void cmdabort(int); -void cmdscanner(int); -int command(const char *, ...); -int confirm(const char *, const char *); -int connect_wait(int); -FILE *dataconn(const char *); -int foregroundproc(void); -int fileindir(const char *, const char *); -struct cmd *getcmd(const char *); -int getreply(int); -int globulize(char **); -char *gunique(const char *); -void help(int, char **); -char *hookup(char *, char *); -int initconn(void); -void intr(void); -int isurl(const char *); -int ftp_login(const char *, char *, char *); -void lostpeer(void); -void makeargv(void); -void progressmeter(int, const char *); -char *prompt(void); -void proxtrans(const char *, const char *, const char *); -void psabort(int); -void psummary(int); -void pswitch(int); -void ptransfer(int); -void recvrequest(const char *, const char *, const char *, - const char *, int, int); -char *remglob(char **, int, char **); -off_t remotesize(const char *, int); -time_t remotemodtime(const char *, int); -void reset(int, char **); -void rmthelp(int, char **); -void sethash(int, char **); -void setpeer(int, char **); -void setttywidth(int); -char *slurpstring(void); - -__dead void usage(void); - -void cookie_get(const char *, const char *, int, char **); -void cookie_load(void); - -#ifndef SMALL -void abortsend(int); -unsigned char complete(EditLine *, int); -void controlediting(void); -void domacro(int, char **); -void list_vertical(StringList *); -void parse_list(char **, char *); -char *remglob2(char **, int, char **, FILE **ftemp, char *type); -int ruserpass(const char *, char **, char **, char **); -void sendrequest(const char *, const char *, const char *, int); -#endif /* !SMALL */ - -extern jmp_buf abortprox; -extern int abrtflag; -extern FILE *cout; -extern int data; -extern char *home; -extern jmp_buf jabort; -extern int family; -extern int proxy; -extern char reply_string[]; -extern off_t restart_point; -extern int keep_alive_timeout; -extern int connect_timeout; -extern int pipeout; -extern char *action; - -#ifndef SMALL -extern int NCMDS; -#endif /* !SMALL */ - -extern char *__progname; /* from crt0.o */ - diff --git a/usr.bin/ftp/fetch.c b/usr.bin/ftp/fetch.c deleted file mode 100644 index 30f73122da5..00000000000 --- a/usr.bin/ftp/fetch.c +++ /dev/null @@ -1,1668 +0,0 @@ -/* $OpenBSD: fetch.c,v 1.167 2018/02/10 06:25:16 jsing Exp $ */ -/* $NetBSD: fetch.c,v 1.14 1997/08/18 10:20:20 lukem Exp $ */ - -/*- - * Copyright (c) 1997 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Jason Thorpe and 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. - */ - -/* - * FTP User Program -- Command line file retrieval - */ - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> - -#include <netinet/in.h> - -#include <arpa/ftp.h> -#include <arpa/inet.h> - -#include <ctype.h> -#include <err.h> -#include <libgen.h> -#include <netdb.h> -#include <fcntl.h> -#include <signal.h> -#include <stdio.h> -#include <stdarg.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <util.h> -#include <resolv.h> - -#ifndef NOSSL -#include <tls.h> -#else /* !NOSSL */ -struct tls; -#endif /* !NOSSL */ - -#include "ftp_var.h" -#include "cmds.h" - -static int url_get(const char *, const char *, const char *, int); -void aborthttp(int); -void abortfile(int); -char hextochar(const char *); -char *urldecode(const char *); -char *recode_credentials(const char *_userinfo); -int ftp_printf(FILE *, struct tls *, const char *, ...) __attribute__((format(printf, 3, 4))); -char *ftp_readline(FILE *, struct tls *, size_t *); -size_t ftp_read(FILE *, struct tls *, char *, size_t); -#ifndef NOSSL -int proxy_connect(int, char *, char *); -int SSL_vprintf(struct tls *, const char *, va_list); -char *SSL_readline(struct tls *, size_t *); -#endif /* !NOSSL */ - -#define FTP_URL "ftp://" /* ftp URL prefix */ -#define HTTP_URL "http://" /* http URL prefix */ -#define HTTPS_URL "https://" /* https URL prefix */ -#define FILE_URL "file:" /* file URL prefix */ -#define FTP_PROXY "ftp_proxy" /* env var with ftp proxy location */ -#define HTTP_PROXY "http_proxy" /* env var with http proxy location */ - -#define EMPTYSTRING(x) ((x) == NULL || (*(x) == '\0')) - -static const char at_encoding_warning[] = - "Extra `@' characters in usernames and passwords should be encoded as %%40"; - -jmp_buf httpabort; - -static int redirect_loop; - -/* - * Determine whether the character needs encoding, per RFC1738: - * - No corresponding graphic US-ASCII. - * - Unsafe characters. - */ -static int -unsafe_char(const char *c0) -{ - const char *unsafe_chars = " <>\"#{}|\\^~[]`"; - const unsigned char *c = (const unsigned char *)c0; - - /* - * No corresponding graphic US-ASCII. - * Control characters and octets not used in US-ASCII. - */ - return (iscntrl(*c) || !isascii(*c) || - - /* - * Unsafe characters. - * '%' is also unsafe, if is not followed by two - * hexadecimal digits. - */ - strchr(unsafe_chars, *c) != NULL || - (*c == '%' && (!isxdigit(*++c) || !isxdigit(*++c)))); -} - -/* - * Encode given URL, per RFC1738. - * Allocate and return string to the caller. - */ -static char * -url_encode(const char *path) -{ - size_t i, length, new_length; - char *epath, *epathp; - - length = new_length = strlen(path); - - /* - * First pass: - * Count unsafe characters, and determine length of the - * final URL. - */ - for (i = 0; i < length; i++) - if (unsafe_char(path + i)) - new_length += 2; - - epath = epathp = malloc(new_length + 1); /* One more for '\0'. */ - if (epath == NULL) - err(1, "Can't allocate memory for URL encoding"); - - /* - * Second pass: - * Encode, and copy final URL. - */ - for (i = 0; i < length; i++) - if (unsafe_char(path + i)) { - snprintf(epathp, 4, "%%" "%02x", - (unsigned char)path[i]); - epathp += 3; - } else - *(epathp++) = path[i]; - - *epathp = '\0'; - return (epath); -} - -/* ARGSUSED */ -static void -tooslow(int signo) -{ - dprintf(STDERR_FILENO, "%s: connect taking too long\n", __progname); - _exit(2); -} - -/* - * Retrieve URL, via the proxy in $proxyvar if necessary. - * Modifies the string argument given. - * Returns -1 on failure, 0 on success - */ -static int -url_get(const char *origline, const char *proxyenv, const char *outfile, int lastfile) -{ - char pbuf[NI_MAXSERV], hbuf[NI_MAXHOST], *cp, *portnum, *path, ststr[4]; - char *hosttail, *cause = "unknown", *newline, *host, *port, *buf = NULL; - char *epath, *redirurl, *loctail, *h, *p; - int error, i, isftpurl = 0, isfileurl = 0, isredirect = 0, rval = -1; - struct addrinfo hints, *res0, *res; - const char * volatile savefile; - char * volatile proxyurl = NULL; - char *credentials = NULL; - volatile int fd = -1, out = -1; - volatile sig_t oldintr, oldinti; - FILE *fin = NULL; - off_t hashbytes; - const char *errstr; - ssize_t len, wlen; - char *proxyhost = NULL; -#ifndef NOSSL - char *sslpath = NULL, *sslhost = NULL; - char *full_host = NULL; - const char *scheme; - int ishttpurl = 0, ishttpsurl = 0; -#endif /* !NOSSL */ -#ifndef SMALL - char *locbase; - struct addrinfo *ares = NULL; -#endif - struct tls *tls = NULL; - int status; - int save_errno; - const size_t buflen = 128 * 1024; - - direction = "received"; - - newline = strdup(origline); - if (newline == NULL) - errx(1, "Can't allocate memory to parse URL"); - if (strncasecmp(newline, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) { - host = newline + sizeof(HTTP_URL) - 1; -#ifndef SMALL - ishttpurl = 1; - scheme = HTTP_URL; -#endif /* !SMALL */ - } else if (strncasecmp(newline, FTP_URL, sizeof(FTP_URL) - 1) == 0) { - host = newline + sizeof(FTP_URL) - 1; - isftpurl = 1; -#ifndef SMALL - scheme = FTP_URL; -#endif /* !SMALL */ - } else if (strncasecmp(newline, FILE_URL, sizeof(FILE_URL) - 1) == 0) { - host = newline + sizeof(FILE_URL) - 1; - isfileurl = 1; -#ifndef NOSSL - scheme = FILE_URL; - } else if (strncasecmp(newline, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0) { - host = newline + sizeof(HTTPS_URL) - 1; - ishttpsurl = 1; - scheme = HTTPS_URL; -#endif /* !NOSSL */ - } else - errx(1, "url_get: Invalid URL '%s'", newline); - - if (isfileurl) { - path = host; - } else { - path = strchr(host, '/'); /* Find path */ - if (EMPTYSTRING(path)) { - if (outfile) { /* No slash, but */ - path=strchr(host,'\0'); /* we have outfile. */ - goto noslash; - } - if (isftpurl) - goto noftpautologin; - warnx("No `/' after host (use -o): %s", origline); - goto cleanup_url_get; - } - *path++ = '\0'; - if (EMPTYSTRING(path) && !outfile) { - if (isftpurl) - goto noftpautologin; - warnx("No filename after host (use -o): %s", origline); - goto cleanup_url_get; - } - } - -noslash: - -#ifndef NOSSL - /* - * Look for auth header in host, since now host does not - * contain the path. Basic auth from RFC 2617, valid - * characters for path are in RFC 3986 section 3.3. - */ - if (proxyenv == NULL && (ishttpurl || ishttpsurl)) { - if ((p = strchr(host, '@')) != NULL) { - *p = '\0'; - credentials = recode_credentials(host); - host = p + 1; - } - } -#endif /* NOSSL */ - - if (outfile) - savefile = outfile; - else { - if (path[strlen(path) - 1] == '/') /* Consider no file */ - savefile = NULL; /* after dir invalid. */ - else - savefile = basename(path); - } - - if (EMPTYSTRING(savefile)) { - if (isftpurl) - goto noftpautologin; - warnx("No filename after directory (use -o): %s", origline); - goto cleanup_url_get; - } - -#ifndef SMALL - if (resume && pipeout) { - warnx("can't append to stdout"); - goto cleanup_url_get; - } -#endif /* !SMALL */ - - if (!isfileurl && proxyenv != NULL) { /* use proxy */ -#ifndef NOSSL - if (ishttpsurl) { - sslpath = strdup(path); - sslhost = strdup(host); - if (! sslpath || ! sslhost) - errx(1, "Can't allocate memory for https path/host."); - } -#endif /* !NOSSL */ - proxyhost = strdup(host); - if (proxyhost == NULL) - errx(1, "Can't allocate memory for proxy host."); - proxyurl = strdup(proxyenv); - if (proxyurl == NULL) - errx(1, "Can't allocate memory for proxy URL."); - if (strncasecmp(proxyurl, HTTP_URL, sizeof(HTTP_URL) - 1) == 0) - host = proxyurl + sizeof(HTTP_URL) - 1; - else if (strncasecmp(proxyurl, FTP_URL, sizeof(FTP_URL) - 1) == 0) - host = proxyurl + sizeof(FTP_URL) - 1; - else { - warnx("Malformed proxy URL: %s", proxyenv); - goto cleanup_url_get; - } - if (EMPTYSTRING(host)) { - warnx("Malformed proxy URL: %s", proxyenv); - goto cleanup_url_get; - } - if (*--path == '\0') - *path = '/'; /* add / back to real path */ - path = strchr(host, '/'); /* remove trailing / on host */ - if (!EMPTYSTRING(path)) - *path++ = '\0'; /* i guess this ++ is useless */ - - path = strchr(host, '@'); /* look for credentials in proxy */ - if (!EMPTYSTRING(path)) { - *path = '\0'; - if (strchr(host, ':') == NULL) { - warnx("Malformed proxy URL: %s", proxyenv); - goto cleanup_url_get; - } - credentials = recode_credentials(host); - *path = '@'; /* restore @ in proxyurl */ - - /* - * This removes the password from proxyurl, - * filling with stars - */ - for (host = 1 + strchr(proxyurl + 5, ':'); *host != '@'; - host++) - *host = '*'; - - host = path + 1; - } - - path = newline; - } - - if (isfileurl) { - struct stat st; - - fd = open(path, O_RDONLY); - if (fd == -1) { - warn("Can't open file %s", path); - goto cleanup_url_get; - } - - if (fstat(fd, &st) == -1) - filesize = -1; - else - filesize = st.st_size; - - /* Open the output file. */ - if (!pipeout) { -#ifndef SMALL - if (resume) - out = open(savefile, O_CREAT | O_WRONLY | - O_APPEND, 0666); - - else -#endif /* !SMALL */ - out = open(savefile, O_CREAT | O_WRONLY | - O_TRUNC, 0666); - if (out < 0) { - warn("Can't open %s", savefile); - goto cleanup_url_get; - } - } else - out = fileno(stdout); - -#ifndef SMALL - if (resume) { - if (fstat(out, &st) == -1) { - warn("Can't fstat %s", savefile); - goto cleanup_url_get; - } - if (lseek(fd, st.st_size, SEEK_SET) == -1) { - warn("Can't lseek %s", path); - goto cleanup_url_get; - } - restart_point = st.st_size; - } -#endif /* !SMALL */ - - /* Trap signals */ - oldintr = NULL; - oldinti = NULL; - if (setjmp(httpabort)) { - if (oldintr) - (void)signal(SIGINT, oldintr); - if (oldinti) - (void)signal(SIGINFO, oldinti); - goto cleanup_url_get; - } - oldintr = signal(SIGINT, abortfile); - - bytes = 0; - hashbytes = mark; - progressmeter(-1, path); - - if ((buf = malloc(buflen)) == NULL) - errx(1, "Can't allocate memory for transfer buffer"); - - /* Finally, suck down the file. */ - i = 0; - oldinti = signal(SIGINFO, psummary); - while ((len = read(fd, buf, buflen)) > 0) { - bytes += len; - for (cp = buf; len > 0; len -= i, cp += i) { - if ((i = write(out, cp, len)) == -1) { - warn("Writing %s", savefile); - signal(SIGINFO, oldinti); - goto cleanup_url_get; - } - else if (i == 0) - break; - } - if (hash && !progress) { - while (bytes >= hashbytes) { - (void)putc('#', ttyout); - hashbytes += mark; - } - (void)fflush(ttyout); - } - } - signal(SIGINFO, oldinti); - if (hash && !progress && bytes > 0) { - if (bytes < mark) - (void)putc('#', ttyout); - (void)putc('\n', ttyout); - (void)fflush(ttyout); - } - if (len != 0) { - warn("Reading from file"); - goto cleanup_url_get; - } - progressmeter(1, NULL); - if (verbose) - ptransfer(0); - (void)signal(SIGINT, oldintr); - - rval = 0; - goto cleanup_url_get; - } - - if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL && - (hosttail[1] == '\0' || hosttail[1] == ':')) { - host++; - *hosttail++ = '\0'; -#ifndef SMALL - if (asprintf(&full_host, "[%s]", host) == -1) - errx(1, "Cannot allocate memory for hostname"); -#endif /* !SMALL */ - } else - hosttail = host; - - portnum = strrchr(hosttail, ':'); /* find portnum */ - if (portnum != NULL) - *portnum++ = '\0'; -#ifndef NOSSL - port = portnum ? portnum : (ishttpsurl ? httpsport : httpport); -#else /* !NOSSL */ - port = portnum ? portnum : httpport; -#endif /* !NOSSL */ - -#ifndef SMALL - if (full_host == NULL) - if ((full_host = strdup(host)) == NULL) - errx(1, "Cannot allocate memory for hostname"); - if (debug) - fprintf(ttyout, "host %s, port %s, path %s, " - "save as %s, auth %s.\n", host, port, path, - savefile, credentials ? credentials : "none"); -#endif /* !SMALL */ - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; - error = getaddrinfo(host, port, &hints, &res0); - /* - * If the services file is corrupt/missing, fall back - * on our hard-coded defines. - */ - if (error == EAI_SERVICE && port == httpport) { - snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT); - error = getaddrinfo(host, pbuf, &hints, &res0); -#ifndef NOSSL - } else if (error == EAI_SERVICE && port == httpsport) { - snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT); - error = getaddrinfo(host, pbuf, &hints, &res0); -#endif /* !NOSSL */ - } - if (error) { - warnx("%s: %s", host, gai_strerror(error)); - goto cleanup_url_get; - } - -#ifndef SMALL - if (srcaddr) { - hints.ai_flags |= AI_NUMERICHOST; - error = getaddrinfo(srcaddr, NULL, &hints, &ares); - if (error) { - warnx("%s: %s", srcaddr, gai_strerror(error)); - goto cleanup_url_get; - } - } -#endif /* !SMALL */ - - /* ensure consistent order of the output */ - if (verbose) - setvbuf(ttyout, NULL, _IOLBF, 0); - - fd = -1; - for (res = res0; res; res = res->ai_next) { - if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, - sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) - strlcpy(hbuf, "(unknown)", sizeof(hbuf)); - if (verbose) - fprintf(ttyout, "Trying %s...\n", hbuf); - - fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (fd == -1) { - cause = "socket"; - continue; - } - -#ifndef SMALL - if (srcaddr) { - if (ares->ai_family != res->ai_family) { - close(fd); - fd = -1; - errno = EINVAL; - cause = "bind"; - continue; - } - if (bind(fd, ares->ai_addr, ares->ai_addrlen) < 0) { - save_errno = errno; - close(fd); - errno = save_errno; - fd = -1; - cause = "bind"; - continue; - } - } -#endif /* !SMALL */ - - if (connect_timeout) { - (void)signal(SIGALRM, tooslow); - alarmtimer(connect_timeout); - } - - for (error = connect(fd, res->ai_addr, res->ai_addrlen); - error != 0 && errno == EINTR; error = connect_wait(fd)) - continue; - if (error != 0) { - save_errno = errno; - close(fd); - errno = save_errno; - fd = -1; - cause = "connect"; - continue; - } - - /* get port in numeric */ - if (getnameinfo(res->ai_addr, res->ai_addrlen, NULL, 0, - pbuf, sizeof(pbuf), NI_NUMERICSERV) == 0) - port = pbuf; - else - port = NULL; - -#ifndef NOSSL - if (proxyenv && sslhost) - proxy_connect(fd, sslhost, credentials); -#endif /* !NOSSL */ - break; - } - freeaddrinfo(res0); -#ifndef SMALL - if (srcaddr) - freeaddrinfo(ares); -#endif /* !SMALL */ - if (fd < 0) { - warn("%s", cause); - goto cleanup_url_get; - } - -#ifndef NOSSL - if (ishttpsurl) { - if (proxyenv && sslpath) { - ishttpsurl = 0; - proxyurl = NULL; - path = sslpath; - } - if (sslhost == NULL) { - sslhost = strdup(host); - if (sslhost == NULL) - errx(1, "Can't allocate memory for https host."); - } - if ((tls = tls_client()) == NULL) { - fprintf(ttyout, "failed to create SSL client\n"); - goto cleanup_url_get; - } - if (tls_configure(tls, tls_config) != 0) { - fprintf(ttyout, "SSL configuration failure: %s\n", - tls_error(tls)); - goto cleanup_url_get; - } - if (tls_connect_socket(tls, fd, sslhost) != 0) { - fprintf(ttyout, "SSL failure: %s\n", tls_error(tls)); - goto cleanup_url_get; - } - } else { - fin = fdopen(fd, "r+"); - fd = -1; - } -#else /* !NOSSL */ - fin = fdopen(fd, "r+"); - fd = -1; -#endif /* !NOSSL */ - -#ifdef SMALL - if (lastfile) { - if (pipeout) { - if (pledge("stdio rpath inet dns tty", NULL) == -1) - err(1, "pledge"); - } else { - if (pledge("stdio rpath wpath cpath inet dns tty", NULL) == -1) - err(1, "pledge"); - } - } -#endif - - if (connect_timeout) { - signal(SIGALRM, SIG_DFL); - alarmtimer(0); - } - - /* - * Construct and send the request. Proxy requests don't want leading /. - */ -#ifndef NOSSL - cookie_get(host, path, ishttpsurl, &buf); -#endif /* !NOSSL */ - - epath = url_encode(path); - if (proxyurl) { - if (verbose) { - fprintf(ttyout, "Requesting %s (via %s)\n", - origline, proxyurl); - } - /* - * Host: directive must use the destination host address for - * the original URI (path). - */ - if (credentials) - ftp_printf(fin, tls, "GET %s HTTP/1.0\r\n" - "Proxy-Authorization: Basic %s\r\n" - "Host: %s\r\n%s%s\r\n\r\n", - epath, credentials, - proxyhost, buf ? buf : "", httpuseragent); - else - ftp_printf(fin, tls, "GET %s HTTP/1.0\r\n" - "Host: %s\r\n%s%s\r\n\r\n", - epath, proxyhost, buf ? buf : "", httpuseragent); - } else { - if (verbose) - fprintf(ttyout, "Requesting %s\n", origline); -#ifndef SMALL - if (resume) { - struct stat stbuf; - - if (stat(savefile, &stbuf) == 0) - restart_point = stbuf.st_size; - else - restart_point = 0; - } -#endif /* SMALL */ -#ifndef NOSSL - if (credentials) { - ftp_printf(fin, tls, - "GET /%s %s\r\nAuthorization: Basic %s\r\nHost: ", - epath, restart_point ? - "HTTP/1.1\r\nConnection: close" : "HTTP/1.0", - credentials); - free(credentials); - credentials = NULL; - } else -#endif /* NOSSL */ - ftp_printf(fin, tls, "GET /%s %s\r\nHost: ", epath, -#ifndef SMALL - restart_point ? "HTTP/1.1\r\nConnection: close" : -#endif /* !SMALL */ - "HTTP/1.0"); - if (proxyhost) { - ftp_printf(fin, tls, "%s", proxyhost); - port = NULL; - } else if (strchr(host, ':')) { - /* - * strip off scoped address portion, since it's - * local to node - */ - h = strdup(host); - if (h == NULL) - errx(1, "Can't allocate memory."); - if ((p = strchr(h, '%')) != NULL) - *p = '\0'; - ftp_printf(fin, tls, "[%s]", h); - free(h); - } else - ftp_printf(fin, tls, "%s", host); - - /* - * Send port number only if it's specified and does not equal - * 80. Some broken HTTP servers get confused if you explicitly - * send them the port number. - */ -#ifndef NOSSL - if (port && strcmp(port, (ishttpsurl ? "443" : "80")) != 0) - ftp_printf(fin, tls, ":%s", port); - if (restart_point) - ftp_printf(fin, tls, "\r\nRange: bytes=%lld-", - (long long)restart_point); -#else /* !NOSSL */ - if (port && strcmp(port, "80") != 0) - ftp_printf(fin, tls, ":%s", port); -#endif /* !NOSSL */ - ftp_printf(fin, tls, "\r\n%s%s\r\n\r\n", - buf ? buf : "", httpuseragent); - } - free(epath); - -#ifndef NOSSL - free(buf); -#endif /* !NOSSL */ - buf = NULL; - - if (fin != NULL && fflush(fin) == EOF) { - warn("Writing HTTP request"); - goto cleanup_url_get; - } - if ((buf = ftp_readline(fin, tls, &len)) == NULL) { - warn("Receiving HTTP reply"); - goto cleanup_url_get; - } - - while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) - buf[--len] = '\0'; -#ifndef SMALL - if (debug) - fprintf(ttyout, "received '%s'\n", buf); -#endif /* !SMALL */ - - cp = strchr(buf, ' '); - if (cp == NULL) - goto improper; - else - cp++; - - strlcpy(ststr, cp, sizeof(ststr)); - status = strtonum(ststr, 200, 416, &errstr); - if (errstr) { - warnx("Error retrieving file: %s", cp); - goto cleanup_url_get; - } - - switch (status) { - case 200: /* OK */ -#ifndef SMALL - /* - * When we request a partial file, and we receive an HTTP 200 - * it is a good indication that the server doesn't support - * range requests, and is about to send us the entire file. - * If the restart_point == 0, then we are not actually - * requesting a partial file, and an HTTP 200 is appropriate. - */ - if (resume && restart_point != 0) { - warnx("Server does not support resume."); - restart_point = resume = 0; - } - /* FALLTHROUGH */ - case 206: /* Partial Content */ -#endif /* !SMALL */ - break; - case 301: /* Moved Permanently */ - case 302: /* Found */ - case 303: /* See Other */ - case 307: /* Temporary Redirect */ - isredirect++; - if (redirect_loop++ > 10) { - warnx("Too many redirections requested"); - goto cleanup_url_get; - } - break; -#ifndef SMALL - case 416: /* Requested Range Not Satisfiable */ - warnx("File is already fully retrieved."); - goto cleanup_url_get; -#endif /* !SMALL */ - default: - warnx("Error retrieving file: %s", cp); - goto cleanup_url_get; - } - - /* - * Read the rest of the header. - */ - free(buf); - filesize = -1; - - for (;;) { - if ((buf = ftp_readline(fin, tls, &len)) == NULL) { - warn("Receiving HTTP reply"); - goto cleanup_url_get; - } - - while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) - buf[--len] = '\0'; - if (len == 0) - break; -#ifndef SMALL - if (debug) - fprintf(ttyout, "received '%s'\n", buf); -#endif /* !SMALL */ - - /* Look for some headers */ - cp = buf; -#define CONTENTLEN "Content-Length: " - if (strncasecmp(cp, CONTENTLEN, sizeof(CONTENTLEN) - 1) == 0) { - size_t s; - cp += sizeof(CONTENTLEN) - 1; - if ((s = strcspn(cp, " \t"))) - *(cp+s) = 0; - filesize = strtonum(cp, 0, LLONG_MAX, &errstr); - if (errstr != NULL) - goto improper; -#ifndef SMALL - if (restart_point) - filesize += restart_point; -#endif /* !SMALL */ -#define LOCATION "Location: " - } else if (isredirect && - strncasecmp(cp, LOCATION, sizeof(LOCATION) - 1) == 0) { - cp += sizeof(LOCATION) - 1; - /* - * If there is a colon before the first slash, this URI - * is not relative. RFC 3986 4.2 - */ - if (cp[strcspn(cp, ":/")] != ':') { -#ifdef SMALL - errx(1, "Relative redirect not supported"); -#else /* SMALL */ - /* XXX doesn't handle protocol-relative URIs */ - if (*cp == '/') { - locbase = NULL; - cp++; - } else { - locbase = strdup(path); - if (locbase == NULL) - errx(1, "Can't allocate memory" - " for location base"); - loctail = strchr(locbase, '#'); - if (loctail != NULL) - *loctail = '\0'; - loctail = strchr(locbase, '?'); - if (loctail != NULL) - *loctail = '\0'; - loctail = strrchr(locbase, '/'); - if (loctail == NULL) { - free(locbase); - locbase = NULL; - } else - loctail[1] = '\0'; - } - /* Contruct URL from relative redirect */ - if (asprintf(&redirurl, "%s%s%s%s/%s%s", - scheme, full_host, - portnum ? ":" : "", - portnum ? portnum : "", - locbase ? locbase : "", - cp) == -1) - errx(1, "Cannot build " - "redirect URL"); - free(locbase); -#endif /* SMALL */ - } else if ((redirurl = strdup(cp)) == NULL) - errx(1, "Cannot allocate memory for URL"); - loctail = strchr(redirurl, '#'); - if (loctail != NULL) - *loctail = '\0'; - if (verbose) - fprintf(ttyout, "Redirected to %s\n", redirurl); - if (fin != NULL) { - fclose(fin); - fin = NULL; - } - if (fd != -1) { - close(fd); - fd = -1; - } - rval = url_get(redirurl, proxyenv, savefile, lastfile); - free(redirurl); - goto cleanup_url_get; - } - free(buf); - } - - /* Open the output file. */ - if (!pipeout) { -#ifndef SMALL - if (resume) - out = open(savefile, O_CREAT | O_WRONLY | O_APPEND, - 0666); - else -#endif /* !SMALL */ - out = open(savefile, O_CREAT | O_WRONLY | O_TRUNC, - 0666); - if (out < 0) { - warn("Can't open %s", savefile); - goto cleanup_url_get; - } - } else { - out = fileno(stdout); -#ifdef SMALL - if (lastfile) { - if (pledge("stdio tty", NULL) == -1) - err(1, "pledge"); - } -#endif - } - - /* Trap signals */ - oldintr = NULL; - oldinti = NULL; - if (setjmp(httpabort)) { - if (oldintr) - (void)signal(SIGINT, oldintr); - if (oldinti) - (void)signal(SIGINFO, oldinti); - goto cleanup_url_get; - } - oldintr = signal(SIGINT, aborthttp); - - bytes = 0; - hashbytes = mark; - progressmeter(-1, path); - - free(buf); - - /* Finally, suck down the file. */ - if ((buf = malloc(buflen)) == NULL) - errx(1, "Can't allocate memory for transfer buffer"); - i = 0; - len = 1; - oldinti = signal(SIGINFO, psummary); - while (len > 0) { - len = ftp_read(fin, tls, buf, buflen); - bytes += len; - for (cp = buf, wlen = len; wlen > 0; wlen -= i, cp += i) { - if ((i = write(out, cp, wlen)) == -1) { - warn("Writing %s", savefile); - signal(SIGINFO, oldinti); - goto cleanup_url_get; - } - else if (i == 0) - break; - } - if (hash && !progress) { - while (bytes >= hashbytes) { - (void)putc('#', ttyout); - hashbytes += mark; - } - (void)fflush(ttyout); - } - } - signal(SIGINFO, oldinti); - if (hash && !progress && bytes > 0) { - if (bytes < mark) - (void)putc('#', ttyout); - (void)putc('\n', ttyout); - (void)fflush(ttyout); - } - if (len != 0) { - warn("Reading from socket"); - goto cleanup_url_get; - } - progressmeter(1, NULL); - if ( -#ifndef SMALL - !resume && -#endif /* !SMALL */ - filesize != -1 && len == 0 && bytes != filesize) { - if (verbose) - fputs("Read short file.\n", ttyout); - goto cleanup_url_get; - } - - if (verbose) - ptransfer(0); - (void)signal(SIGINT, oldintr); - - rval = 0; - goto cleanup_url_get; - -noftpautologin: - warnx( - "Auto-login using ftp URLs isn't supported when using $ftp_proxy"); - goto cleanup_url_get; - -improper: - warnx("Improper response from %s", host); - -cleanup_url_get: -#ifndef NOSSL - if (tls != NULL) { - if (tls_session_fd != -1) - dprintf(STDERR_FILENO, "tls session resumed: %s\n", - tls_conn_session_resumed(tls) ? "yes" : "no"); - do { - i = tls_close(tls); - } while (i == TLS_WANT_POLLIN || i == TLS_WANT_POLLOUT); - tls_free(tls); - } - free(full_host); - free(sslhost); -#endif /* !NOSSL */ - if (fin != NULL) { - fclose(fin); - fin = NULL; - } - if (fd != -1) { - close(fd); - fd = -1; - } - if (out >= 0 && out != fileno(stdout)) - close(out); - free(buf); - free(proxyhost); - free(proxyurl); - free(newline); - free(credentials); - return (rval); -} - -/* - * Abort a http retrieval - */ -/* ARGSUSED */ -void -aborthttp(int signo) -{ - - alarmtimer(0); - fputs("\nhttp fetch aborted.\n", ttyout); - (void)fflush(ttyout); - longjmp(httpabort, 1); -} - -/* - * Abort a http retrieval - */ -/* ARGSUSED */ -void -abortfile(int signo) -{ - - alarmtimer(0); - fputs("\nfile fetch aborted.\n", ttyout); - (void)fflush(ttyout); - longjmp(httpabort, 1); -} - -/* - * Retrieve multiple files from the command line, transferring - * files of the form "host:path", "ftp://host/path" using the - * ftp protocol, and files of the form "http://host/path" using - * the http protocol. - * If path has a trailing "/", then return (-1); - * 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[], char *outfile) -{ - char *xargv[5]; - char *cp, *url, *host, *dir, *file, *portnum; - char *username, *pass, *pathstart; - char *ftpproxy, *httpproxy; - int rval, xargc, lastfile; - volatile int argpos; - int dirhasglob, filehasglob, oautologin; - char rempath[PATH_MAX]; - - argpos = 0; - - if (setjmp(toplevel)) { - if (connected) - disconnect(0, NULL); - return (argpos + 1); - } - (void)signal(SIGINT, (sig_t)intr); - (void)signal(SIGPIPE, (sig_t)lostpeer); - - if ((ftpproxy = getenv(FTP_PROXY)) != NULL && *ftpproxy == '\0') - ftpproxy = NULL; - if ((httpproxy = getenv(HTTP_PROXY)) != NULL && *httpproxy == '\0') - httpproxy = NULL; - - /* - * Loop through as long as there's files to fetch. - */ - username = pass = NULL; - for (rval = 0; (rval == 0) && (argpos < argc); free(url), argpos++) { - if (strchr(argv[argpos], ':') == NULL) - break; - - free(username); - free(pass); - host = dir = file = portnum = username = pass = NULL; - - lastfile = (argv[argpos+1] == NULL); - - /* - * We muck with the string, so we make a copy. - */ - url = strdup(argv[argpos]); - if (url == NULL) - errx(1, "Can't allocate memory for auto-fetch."); - - /* - * Try HTTP URL-style arguments first. - */ - if (strncasecmp(url, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || -#ifndef NOSSL - /* even if we compiled without SSL, url_get will check */ - strncasecmp(url, HTTPS_URL, sizeof(HTTPS_URL) -1) == 0 || -#endif /* !NOSSL */ - strncasecmp(url, FILE_URL, sizeof(FILE_URL) - 1) == 0) { - redirect_loop = 0; - if (url_get(url, httpproxy, outfile, lastfile) == -1) - rval = argpos + 1; - continue; - } - - /* - * Try FTP URL-style arguments next. If ftpproxy is - * set, use url_get() instead of standard ftp. - * Finally, try host:file. - */ - host = url; - if (strncasecmp(url, FTP_URL, sizeof(FTP_URL) - 1) == 0) { - char *passend, *passagain, *userend; - - if (ftpproxy) { - if (url_get(url, ftpproxy, outfile, lastfile) == -1) - rval = argpos + 1; - continue; - } - host += sizeof(FTP_URL) - 1; - dir = strchr(host, '/'); - - /* Look for [user:pass@]host[:port] */ - - /* check if we have "user:pass@" */ - userend = strchr(host, ':'); - passend = strchr(host, '@'); - if (passend && userend && userend < passend && - (!dir || passend < dir)) { - username = host; - pass = userend + 1; - host = passend + 1; - *userend = *passend = '\0'; - passagain = strchr(host, '@'); - if (strchr(pass, '@') != NULL || - (passagain != NULL && passagain < dir)) { - warnx(at_encoding_warning); - username = pass = NULL; - goto bad_ftp_url; - } - - if (EMPTYSTRING(username)) { -bad_ftp_url: - warnx("Invalid URL: %s", argv[argpos]); - rval = argpos + 1; - username = pass = NULL; - continue; - } - username = urldecode(username); - pass = urldecode(pass); - } - - /* check [host]:port, or [host] */ - if (host[0] == '[') { - cp = strchr(host, ']'); - if (cp && (!dir || cp < dir)) { - if (cp + 1 == dir || cp[1] == ':') { - host++; - *cp++ = '\0'; - } else - cp = NULL; - } else - cp = host; - } else - cp = host; - - /* split off host[:port] if there is */ - if (cp) { - portnum = strchr(cp, ':'); - pathstart = strchr(cp, '/'); - /* : in path is not a port # indicator */ - if (portnum && pathstart && - pathstart < portnum) - portnum = NULL; - - if (!portnum) - ; - else { - if (!dir) - ; - else if (portnum + 1 < dir) { - *portnum++ = '\0'; - /* - * XXX should check if portnum - * is decimal number - */ - } else { - /* empty portnum */ - goto bad_ftp_url; - } - } - } else - portnum = NULL; - } else { /* classic style `host:file' */ - dir = strchr(host, ':'); - } - if (EMPTYSTRING(host)) { - rval = argpos + 1; - continue; - } - - /* - * If dir is NULL, the file wasn't specified - * (URL looked something like ftp://host) - */ - if (dir != NULL) - *dir++ = '\0'; - - /* - * Extract the file and (if present) directory name. - */ - if (!EMPTYSTRING(dir)) { - cp = strrchr(dir, '/'); - if (cp != NULL) { - *cp++ = '\0'; - file = cp; - } else { - file = dir; - dir = NULL; - } - } -#ifndef SMALL - if (debug) - fprintf(ttyout, - "user %s:%s host %s port %s dir %s file %s\n", - username, pass ? "XXXX" : NULL, host, portnum, - dir, file); -#endif /* !SMALL */ - - /* - * Set up the connection. - */ - if (connected) - disconnect(0, NULL); - xargv[0] = __progname; - xargv[1] = host; - xargv[2] = NULL; - xargc = 2; - if (!EMPTYSTRING(portnum)) { - xargv[2] = portnum; - xargv[3] = NULL; - xargc = 3; - } - oautologin = autologin; - if (username == NULL) - anonftp = 1; - else { - anonftp = 0; - autologin = 0; - } - setpeer(xargc, xargv); - autologin = oautologin; - if (connected == 0 || - (connected == 1 && autologin && (username == NULL || - !ftp_login(host, username, pass)))) { - warnx("Can't connect or login to host `%s'", host); - rval = argpos + 1; - continue; - } - - /* Always use binary transfers. */ - setbinary(0, NULL); - - dirhasglob = filehasglob = 0; - if (doglob) { - if (!EMPTYSTRING(dir) && - strpbrk(dir, "*?[]{}") != NULL) - dirhasglob = 1; - if (!EMPTYSTRING(file) && - strpbrk(file, "*?[]{}") != NULL) - filehasglob = 1; - } - - /* Change directories, if necessary. */ - if (!EMPTYSTRING(dir) && !dirhasglob) { - xargv[0] = "cd"; - xargv[1] = dir; - xargv[2] = NULL; - cd(2, xargv); - if (!dirchange) { - rval = argpos + 1; - continue; - } - } - - if (EMPTYSTRING(file)) { -#ifndef SMALL - rval = -1; -#else /* !SMALL */ - recvrequest("NLST", "-", NULL, "w", 0, 0); - rval = 0; -#endif /* !SMALL */ - continue; - } - - if (verbose) - fprintf(ttyout, "Retrieving %s/%s\n", dir ? dir : "", file); - - if (dirhasglob) { - snprintf(rempath, sizeof(rempath), "%s/%s", dir, file); - file = rempath; - } - - /* Fetch the file(s). */ - xargc = 2; - xargv[0] = "get"; - xargv[1] = file; - xargv[2] = NULL; - if (dirhasglob || filehasglob) { - int ointeractive; - - ointeractive = interactive; - interactive = 0; - xargv[0] = "mget"; -#ifndef SMALL - if (resume) { - xargc = 3; - xargv[1] = "-c"; - xargv[2] = file; - xargv[3] = NULL; - } -#endif /* !SMALL */ - mget(xargc, xargv); - interactive = ointeractive; - } else { - if (outfile != NULL) { - xargv[2] = outfile; - xargv[3] = NULL; - xargc++; - } -#ifndef SMALL - if (resume) - reget(xargc, xargv); - else -#endif /* !SMALL */ - get(xargc, xargv); - } - - if ((code / 100) != COMPLETE) - rval = argpos + 1; - } - if (connected && rval != -1) - disconnect(0, NULL); - return (rval); -} - -char * -urldecode(const char *str) -{ - char *ret, c; - int i, reallen; - - if (str == NULL) - return NULL; - if ((ret = malloc(strlen(str)+1)) == NULL) - err(1, "Can't allocate memory for URL decoding"); - for (i = 0, reallen = 0; str[i] != '\0'; i++, reallen++, ret++) { - c = str[i]; - if (c == '+') { - *ret = ' '; - continue; - } - - /* Cannot use strtol here because next char - * after %xx may be a digit. - */ - if (c == '%' && isxdigit((unsigned char)str[i+1]) && - isxdigit((unsigned char)str[i+2])) { - *ret = hextochar(&str[i+1]); - i+=2; - continue; - } - *ret = c; - } - *ret = '\0'; - - return ret-reallen; -} - -char * -recode_credentials(const char *userinfo) -{ - char *ui, *creds; - size_t ulen, credsize; - - /* url-decode the user and pass */ - ui = urldecode(userinfo); - - ulen = strlen(ui); - credsize = (ulen + 2) / 3 * 4 + 1; - creds = malloc(credsize); - if (creds == NULL) - errx(1, "out of memory"); - if (b64_ntop(ui, ulen, creds, credsize) == -1) - errx(1, "error in base64 encoding"); - free(ui); - return (creds); -} - -char -hextochar(const char *str) -{ - unsigned char c, ret; - - c = str[0]; - ret = c; - if (isalpha(c)) - ret -= isupper(c) ? 'A' - 10 : 'a' - 10; - else - ret -= '0'; - ret *= 16; - - c = str[1]; - ret += c; - if (isalpha(c)) - ret -= isupper(c) ? 'A' - 10 : 'a' - 10; - else - ret -= '0'; - return ret; -} - -int -isurl(const char *p) -{ - - if (strncasecmp(p, FTP_URL, sizeof(FTP_URL) - 1) == 0 || - strncasecmp(p, HTTP_URL, sizeof(HTTP_URL) - 1) == 0 || -#ifndef NOSSL - strncasecmp(p, HTTPS_URL, sizeof(HTTPS_URL) - 1) == 0 || -#endif /* !NOSSL */ - strncasecmp(p, FILE_URL, sizeof(FILE_URL) - 1) == 0 || - strstr(p, ":/")) - return (1); - return (0); -} - -char * -ftp_readline(FILE *fp, struct tls *tls, size_t *lenp) -{ - if (fp != NULL) - return fparseln(fp, lenp, NULL, "\0\0\0", 0); -#ifndef NOSSL - else if (tls != NULL) - return SSL_readline(tls, lenp); -#endif /* !NOSSL */ - else - return NULL; -} - -size_t -ftp_read(FILE *fp, struct tls *tls, char *buf, size_t len) -{ -#ifndef NOSSL - ssize_t tret; -#endif - size_t ret = 0; - - if (fp != NULL) - ret = fread(buf, sizeof(char), len, fp); -#ifndef NOSSL - else if (tls != NULL) { - do { - tret = tls_read(tls, buf, len); - } while (tret == TLS_WANT_POLLIN || tret == TLS_WANT_POLLOUT); - if (tret < 0) - errx(1, "SSL read error: %s", tls_error(tls)); - ret = (size_t)tret; - } -#endif /* !NOSSL */ - return (ret); -} - -int -ftp_printf(FILE *fp, struct tls *tls, const char *fmt, ...) -{ - int ret; - va_list ap; - - va_start(ap, fmt); - - if (fp != NULL) - ret = vfprintf(fp, fmt, ap); -#ifndef NOSSL - else if (tls != NULL) - ret = SSL_vprintf(tls, fmt, ap); -#endif /* !NOSSL */ - else - ret = 0; - - va_end(ap); -#ifndef SMALL - if (debug) { - va_start(ap, fmt); - ret = vfprintf(ttyout, fmt, ap); - va_end(ap); - } -#endif /* !SMALL */ - return (ret); -} - -#ifndef NOSSL -int -SSL_vprintf(struct tls *tls, const char *fmt, va_list ap) -{ - char *string, *buf; - size_t len; - int ret; - - if ((ret = vasprintf(&string, fmt, ap)) == -1) - return ret; - buf = string; - len = ret; - while (len > 0) { - ret = tls_write(tls, buf, len); - if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) - continue; - if (ret < 0) - errx(1, "SSL write error: %s", tls_error(tls)); - buf += ret; - len -= ret; - } - free(string); - return ret; -} - -char * -SSL_readline(struct tls *tls, size_t *lenp) -{ - size_t i, len; - char *buf, *q, c; - int ret; - - len = 128; - if ((buf = malloc(len)) == NULL) - errx(1, "Can't allocate memory for transfer buffer"); - for (i = 0; ; i++) { - if (i >= len - 1) { - if ((q = reallocarray(buf, len, 2)) == NULL) - errx(1, "Can't expand transfer buffer"); - buf = q; - len *= 2; - } - do { - ret = tls_read(tls, &c, 1); - } while (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT); - if (ret < 0) - errx(1, "SSL read error: %s", tls_error(tls)); - - buf[i] = c; - if (c == '\n') { - buf[i] = '\0'; - break; - } - } - *lenp = i; - return (buf); -} - -int -proxy_connect(int socket, char *host, char *cookie) -{ - int l; - char buf[1024]; - char *connstr, *hosttail, *port; - - if (*host == '[' && (hosttail = strrchr(host, ']')) != NULL && - (hosttail[1] == '\0' || hosttail[1] == ':')) { - host++; - *hosttail++ = '\0'; - } else - hosttail = host; - - port = strrchr(hosttail, ':'); /* find portnum */ - if (port != NULL) - *port++ = '\0'; - if (!port) - port = "443"; - - if (cookie) { - l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n" - "Proxy-Authorization: Basic %s\r\n%s\r\n\r\n", - host, port, cookie, HTTP_USER_AGENT); - } else { - l = asprintf(&connstr, "CONNECT %s:%s HTTP/1.1\r\n%s\r\n\r\n", - host, port, HTTP_USER_AGENT); - } - - if (l == -1) - errx(1, "Could not allocate memory to assemble connect string!"); -#ifndef SMALL - if (debug) - printf("%s", connstr); -#endif /* !SMALL */ - if (write(socket, connstr, l) != l) - err(1, "Could not send connect string"); - read(socket, &buf, sizeof(buf)); /* only proxy header XXX: error handling? */ - free(connstr); - return(200); -} -#endif /* !NOSSL */ diff --git a/usr.bin/ftp/file.c b/usr.bin/ftp/file.c new file mode 100644 index 00000000000..7b641fc8c4c --- /dev/null +++ b/usr.bin/ftp/file.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/stat.h> + +#include <err.h> +#include <fcntl.h> +#include <stdio.h> + +#include "ftp.h" + +struct imsgbuf; + +static FILE *src_fp; + +struct url * +file_request(struct imsgbuf *ibuf, struct url *url, off_t *offset, off_t *sz) +{ + struct stat sb; + int src_fd; + + if ((src_fd = fd_request(url->path, O_RDONLY, NULL)) == -1) + err(1, "Can't open file %s", url->path); + + if (fstat(src_fd, &sb) == 0) + *sz = sb.st_size; + + if ((src_fp = fdopen(src_fd, "r")) == NULL) + err(1, "%s: fdopen", __func__); + + if (*offset && fseeko(src_fp, *offset, SEEK_SET) == -1) + err(1, "%s: fseeko", __func__); + + return url; +} + +void +file_save(struct url *url, FILE *dst_fp, off_t *offset) +{ + copy_file(dst_fp, src_fp, offset); + fclose(src_fp); +} diff --git a/usr.bin/ftp/ftp.1 b/usr.bin/ftp/ftp.1 index ecbddc1fec7..5fa5b019073 100644 --- a/usr.bin/ftp/ftp.1 +++ b/usr.bin/ftp/ftp.1 @@ -1,6 +1,3 @@ -.\" $OpenBSD: ftp.1,v 1.109 2019/05/09 14:51:34 naddy Exp $ -.\" $NetBSD: ftp.1,v 1.22 1997/08/18 10:20:22 lukem Exp $ -.\" .\" Copyright (c) 1985, 1989, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -30,57 +27,39 @@ .\" .\" @(#)ftp.1 8.3 (Berkeley) 10/9/94 .\" -.Dd $Mdocdate: May 9 2019 $ +.\" Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: May 12 2019 $ .Dt FTP 1 .Os .Sh NAME .Nm ftp .Nd Internet file transfer program .Sh SYNOPSIS -.Nm ftp -.Op Fl 46AadEegiMmnptVv +.Nm +.Op Fl 46AVv .Op Fl D Ar title -.Op Fl k Ar seconds -.Op Fl P Ar port -.Op Fl r Ar seconds -.Op Fl s Ar srcaddr .Op Ar host Op Ar port -.Nm ftp -.Op Fl C -.Op Fl o Ar output -.Op Fl s Ar srcaddr -.Sm off -.Pf ftp:// Op Ar user : password No @ -.Ar host Op : Ar port -.No / Ar file Op / -.Sm on -.Ar ... -.Nm ftp -.Op Fl C -.Op Fl c Ar cookie +.Nm +.Op Fl 46ACMV +.Op Fl D Ar title .Op Fl o Ar output -.Op Fl S Ar ssl_options -.Op Fl s Ar srcaddr +.Op Fl S Ar tls_options .Op Fl U Ar useragent .Op Fl w Ar seconds -.Sm off -.Pf http Oo s Oc :// -.Op Ar user : password No @ -.Ar host Op : Ar port -.No / Ar file -.Sm on -.Ar ... -.Nm ftp -.Op Fl C -.Op Fl o Ar output -.Op Fl s Ar srcaddr -.Pf file: Ar -.Nm ftp -.Op Fl C -.Op Fl o Ar output -.Op Fl s Ar srcaddr -.Ar host : Ns / Ns Ar file Ns Op / -.Ar ... +.Ar url ... .Sh DESCRIPTION .Nm is the user interface to the Internet standard File Transfer @@ -88,8 +67,8 @@ Protocol (FTP). The program allows a user to transfer files to and from a remote network site. .Pp -The latter four usage formats will fetch a file using either the -FTP, HTTP, or HTTPS protocols into the current directory. +The latter usage format will fetch a file using either the +FTP, HTTP or HTTPS protocols into the current directory. This is ideal for scripts. Refer to .Sx AUTO-FETCHING FILES @@ -104,7 +83,7 @@ to use IPv4 addresses only. .It Fl 6 Forces .Nm -to use IPv6 addresses only. +to use IPv6 addreses only. .It Fl A Force active mode FTP. By default, @@ -116,125 +95,34 @@ This option causes 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 the normal login procedure and use an anonymous login instead. .It Fl C Continue a previously interrupted file transfer. .Nm -will continue transferring from an offset equal to the length of -.Ar file . +will continue transferring from an offset equal to the length of file. .Pp -Resuming HTTP(S) transfers are only supported -if the remote server supports the +Resuming HTTP(S) transfers are only supported if the remote server supports the .Dq Range header. -.It Fl c Ar cookie -Load a Netscape-like cookiejar file -for HTTP and HTTPS transfers. -With this option relevant cookies from the jar are sent with each HTTP(S) -request. -Setting the -.Ev http_cookies -environment variable has the same effect. -If both the -.Ev http_cookies -environment variable is set and the -.Fl c -argument is given, the latter takes precedence. .It Fl D Ar title -Specify a short -.Ar title -for the start of the progress bar. -.It Fl d -Enables debugging. -.It Fl E -Disables EPSV/EPRT command on IPv4 connections. -.It Fl e -Disables command line editing. -Useful for Emacs ange-ftp. -.It Fl g -Disables file name globbing. -.It Fl i -Turns off interactive prompting during -multiple file transfers. -.It Fl k Ar seconds -When greater than zero, -sends a byte after each -.Ar seconds -period over the control connection during long transfers, -so that incorrectly configured network equipment won't -aggressively drop it. -The FTP protocol supports a -.Dv NOOP -command that can be used for that purpose. -This assumes the FTP server can deal with extra commands coming over -the control connection during a transfer. -Well-behaved servers queue those commands, and process them after the -transfer. -By default, -.Nm -will send a byte every 60 seconds. +Specify a short title for the start of the progress bar. .It Fl M Causes .Nm -to never display the progress meter in cases where it would do -so by default. -.It Fl m -Causes -.Nm -to always display the progress meter in cases where it would not do -so by default. -.It Fl n -Restrains -.Nm -from attempting -.Dq auto-login -upon initial connection. -If auto-login is enabled, -.Nm -will check the -.Pa .netrc -file (see below) 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 log in. +to never display the progress meter in cases where it would do so by default. .It Fl o Ar output -When fetching a single file or URL, save the contents in +When fetching a file or URL, save the contents in .Ar output . -To make the contents go to stdout, -use -.Sq - -for +To make the contents go to stdout, use `-' for .Ar output . -.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 r Ar seconds -Retry to connect if failed, pausing for number of -.Ar seconds . -.It Fl S Ar ssl_options -SSL/TLS options to use with HTTPS transfers. +.It Fl S Ar tls_options +TLS options to use with HTTPS transfers. The following settings are available: .Bl -tag -width Ds .It Cm cafile Ns = Ns Ar /path/to/cert.pem -PEM encoded file containing CA certificates used for certificate -validation. +PEM encoded file containing CA certificates used for certificate validation. .It Cm capath Ns = Ns Ar /path/to/certs/ Directory containing PEM encoded CA certificates used for certificate validation. -Such a directory can be prepared using the c_rehash script distributed with -OpenSSL. .It Cm ciphers Ns = Ns Ar cipher_list Specify the list of ciphers that will be used by .Nm . @@ -243,12 +131,17 @@ See the .Cm ciphers subcommand. .It Cm depth Ns = Ns Ar max_depth -Maximum depth of the certificate chain allowed when performing -validation. -.It Cm do -Perform server certificate validation. +Maximum depth of the certificate chain allowed when performing validation. .It Cm dont Don't perform server certificate validation. +.It Cm protocols Ns = Ns Ar string +Specify the TLS protocols to use. +If not specified the value +.Qq all +is used. +Refer to the +.Xr tls_config_parse_protocols 3 +function for other valid protocol string values. .It Cm muststaple Require the server to present a valid OCSP stapling in the TLS handshake. .It Cm noverifytime @@ -257,8 +150,8 @@ Disable validation of certificate times and OCSP validation. Specify a file to use for TLS session data. If this file has a non-zero length, the session data will be read from this file and the client will attempt to resume the TLS session with the server. -Upon completion of a successful TLS handshake this file will be updated -with new session data, if available. +Upon completion of a successful TLS handshake this file will be updated with +new session data, if available. This file will be created if it does not already exist. .El .Pp @@ -272,14 +165,6 @@ or setting is provided, .Pa /etc/ssl/cert.pem will be used. -.It Fl s Ar srcaddr -Use -.Ar srcaddr -on the local machine as the source address -of the connection. -Only useful on systems with more than one address. -.It Fl t -Enables packet tracing. .It Fl U Ar useragent Set .Ar useragent @@ -287,18 +172,9 @@ as the User-Agent for HTTP(S) URL requests. If not specified, the default User-Agent is .Dq OpenBSD ftp . .It Fl V -Disable verbose mode, overriding the default of enabled when input -is from a terminal. -.It Fl v -Enable verbose mode. -This is the default if input is from a terminal. -Forces -.Nm -to show all responses from the remote server, as well -as report on data transfer statistics. +Disable verbose mode. .It Fl w Ar seconds -For URL format connections to HTTP/HTTPS servers, abort a -slow connection after +Abort a slow connection after .Ar seconds . .El .Pp @@ -321,251 +197,32 @@ The following commands are recognized by .Nm : .Bl -tag -width Fl -.It Ic \&! Oo Ar command -.Op Ar arg ... -.Oc -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 arg ... -Execute the macro -.Ar macro-name -that was defined with the -.Ic macdef -command. -Arguments are passed to the macro unglobbed. -.It Ic \&? Op Ar command -A synonym for -.Ic help . -.It Ic account Op Ar password -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 . -.It Ic bell Op Ic on | off -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. -This is the default type. -.It Ic bye -Terminate the FTP session with the remote server and exit -.Nm . -An end-of-file will also terminate the session and exit. -.It Ic case Op Ic on | off -Toggle remote computer file name case mapping during -.Ic mget -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 file -Change the permission modes of -.Ar file -on the remote -system to -.Ar mode . +.It Ic open Ar host Op Ar port +Establish a connection to the specified +.Ar host +FTP server. +An optional port number may be supplied, +in which case +.Nm +will attempt to contact an FTP server at that port. .It Ic close Terminate the FTP session with the remote server and return to the command interpreter. -Any defined macros are erased. -.It Ic cr Op Ic on | off -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 non-UNIX -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 debug Oo Ic on | off | -.Ar debuglevel -.Oc -Toggle debugging mode. -If an optional -.Ar debuglevel -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 delete Ar remote-file -Delete the file -.Ar remote-file -on the remote machine. -.It Ic dir Op Ar remote-directory Op Ar local-file -A synonym for -.Ic ls . -.It Ic disconnect -A synonym for -.Ic close . -.It Ic edit Op Ic on | off -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 epsv4 Op Ic on | off -Toggle use of EPSV/EPRT command on IPv4 connection. -.It Ic exit -A synonym for -.Ic bye . -.It Ic form Ar format -Set the file transfer -.Ic form -to -.Ar format . -The default format is -.Dq file . -.It Ic ftp Ar host Op Ar port -A synonym for -.Ic open . -.It Ic gate Oo Ic on | off | -.Ar host Op Ar port -.Oc -Toggle gate-ftp mode. -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 Op Ic on | off -Toggle filename expansion for -.Ic mdelete , -.Ic mget -and -.Ic mput . -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 -and -.Ic mget , -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 -.Dq mls remote-files - . -Note: -.Ic mget -and -.Ic mput -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 Oo Ic on | off | -.Ar size -.Oc -Toggle hash mark -.Pq Ql # -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. .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 lcd Op Ar local-directory -Change the working directory on the local machine. -If -no -.Ar local-directory -is specified, the user's home directory is used. -.It Ic less Ar file +.It Ic \&? Op Ar command A synonym for -.Ic page . -.It Ic lpwd -Print the working directory on the local machine. +.Ic help . +.It Ic quit +Terminate the FTP session with the remote server and exit +.Nm . +.It Ic exit +A synonym for +.Ic quit . .It Ic ls Op Ar remote-directory 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 @@ -576,211 +233,17 @@ systems will produce output from the command If .Ar remote-directory 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 ls -output. If no local file is specified, or if .Ar local-file is .Sq - , the output is sent to the terminal. -.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 in (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 -.Ql $ -and -.Ql \e -as special characters. -A -.Ql $ -followed by a number (or numbers) is replaced by the -corresponding argument on the macro invocation command line. -A -.Ql $ -followed by an -.Sq i -tells the macro processor that the -executing macro is to be looped. -On the first pass -.Ql $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 -.Ql \e -followed by any character is replaced by that character. -Use the -.Ql \e -to prevent special treatment of the -.Ql $ . -.It Ic mdelete Op Ar remote-files -Delete the -.Ar remote-files -on the remote machine. -.It Ic mdir Ar remote-files local-file -A synonym for -.Ic mls . -.It Xo Ic mget -.Op Fl cnr -.Op Fl d Ar depth -.Ar remote-files -.Xc -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" . -.Pp -The options are as follows: -.Bl -tag -width Ds -.It Fl c -Use -.Ic reget -instead of -.Ic get . -.It Fl d Ar depth -Specify the maximum recursion level -.Ar depth . -The default is 0, which means unlimited. -.It Fl n -Use -.Ic newer -instead of -.Ic get . -.It Fl r -Recursively descend the directory tree, transferring all files and -directories. -.El -.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 mode Op Ar mode-name -Set the file transfer -.Ic mode -to -.Ar mode-name . -The default mode is -.Dq stream -mode. -.It Ic modtime Ar file -Show the last modification time of -.Ar file -on the remote machine. -.It Ic more Ar file -A synonym for -.Ic page . -.It Xo Ic mput -.Op Fl cr -.Op Fl d Ar depth -.Ar local-files -.Xc -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. -.Pp -If the -.Fl c -flag is specified then -The options are as follows: -.Bl -tag -width Ds -.It Fl c -Use -.Ic reput -instead of -.Ic put . -.It Fl d Ar depth -Specify the maximum recursion level -.Ar depth . -The default is 0, which means unlimited. -.It Fl r -Recursively descend the directory tree, transferring all files and -directories. -.El -.It Xo Ic msend -.Op Fl c -.Ar local-files -.Xc -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 than 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-directory Op Ar local-file Print a list of the files in a directory on the remote machine. If .Ar remote-directory 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 nlist -output. If no local file is specified, or if .Ar local-file is @@ -790,163 +253,20 @@ Note that on some servers, the .Ic nlist command will only return information on normal files (not directories or special files). -.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 non-UNIX remote computer -with different file naming conventions or practices. -.Pp -The mapping follows the pattern set by -.Ar inpattern -and -.Ar outpattern . -.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 -.Ql $1 , -.Ql $2 , -\&..., -.Ql $9 -in -.Ar inpattern . -Use -.Ql \e -to prevent this special treatment of the -.Ql $ -character. -All other characters are treated literally, and are used to determine the -.Ic nmap -.Ar inpattern -variable values. -.Pp -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 -.Ql $1 , -.Ql $2 , -\&..., -.Ql $9 -are replaced by any value resulting from the -.Ar inpattern -template. -The sequence -.Ql $0 -is replaced by the original filename. -Additionally, the sequence -.Sq Op Ar seq1 , Ar seq2 -is replaced by -.Ar seq1 -if -.Ar seq1 -is not a null string; otherwise it is replaced by -.Ar seq2 . -For example: -.Pp -.Dl nmap $1.$2.$3 [$1,$2].[$2,file] -.Pp -This command would yield the output filename -.Pa myfile.data -for input filenames -.Pa myfile.data -and -.Pa myfile.data.old ; -.Pa myfile.file -for the input filename -.Pa myfile ; -and -.Pa myfile.myfile -for the input filename -.Pa .myfile . -Spaces may be included in -.Ar outpattern -by quoting them, -as in the following example: -.Bd -literal -offset indent -nmap $1.$2 "$1 $2" -.Ed -.Pp -Use the -.Ql \e -character to prevent special treatment -of the -.Ql $ , -.Ql \&[ , -.Ql \&] , -and -.Ql \&, -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 non-UNIX 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 -FTP server. -An optional port number may be supplied, -in which case -.Nm -will attempt to contact an FTP server at that port. -If the -.Ic auto-login -option is on (default), -.Nm -will also attempt to automatically log the user in to -the FTP server (see below). -.It Ic page Ar file -Retrieve -.Ic file -and display with the program defined in -.Ev PAGER -(defaulting to -.Xr more 1 -if -.Ev PAGER -is null or not defined). +.It Ic pwd +Print the name of the current working directory on the remote +machine. +.It Ic cd Ar remote-directory +Change the working directory on the remote machine +to +.Ar remote-directory . +.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. .It Ic passive Op Ic on | off Toggle passive mode. If passive mode is turned on (default is on), @@ -954,367 +274,43 @@ If passive mode is turned on (default is on), will send a .Dv EPSV command for all data connections instead of the usual -.Dv PORT +.Dv EPRT command. The -.Dv PASV +.Dv EPSV 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 +.Dv EPRT 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 FTP servers are required to support the -.Dv PASV -command by RFC 1123, some do not.) -.It Ic preserve Op Ic on | off -Toggle preservation of modification times on retrieved files. -.It Ic progress Op Ic on | off -Toggle display of transfer progress bar. -The progress bar will be disabled for a transfer that has -.Ar local-file -as -.Sq - -or a command that starts with -.Sq \&| . -Refer to -.Sx FILE NAMING CONVENTIONS -for more information. -.It Ic prompt Op Ic on | off -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 Ic ?\& -Print help message. -.It Ic a -Answer -.Dq yes -to the current file and automatically answer -.Dq yes -to any remaining files for the current command. -.It Ic n -Do not transfer the file. -.It Ic p -Answer -.Dq yes -to the current file and turn off prompt mode -(as if -.Dq prompt off -had been given). -.It Ic q -Answer -.Dq no -to the current file and automatically answer -.Dq no -to any remaining files for the current command. -.It Ic y -Transfer the file. -.El -.It Ic proxy Ar command -Execute an FTP command on a secondary control connection. -This command allows simultaneous connection to two remote 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 -.Ic proxy ?\& -to see other 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 FTP protocol -.Dv PASV -command by the server on the secondary control connection. +.It Ic lcd Op Ar local-directory +Change the working directory on the local machine. +If +no +.Ar local-directory +is specified, the user's home directory is used. +.It Ic lpwd +Print the working directory on the local machine. .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 arg ... -The arguments specified are sent, verbatim, to the remote FTP server. -.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 -Reget acts like 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 rename Ar from-name to-name -Rename the file -.Ar from-name -on the remote machine to the file -.Ar to-name . -.It Ic reput Ar local-file Op Ar remote-file -Reput acts like put, except that if -.Ar remote-file -exists and is -smaller than -.Ar local-file , -.Ar remote-file -is presumed to be -a partially transferred copy of -.Ar local-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 reset -Clear reply queue. -This command re-synchronizes command/reply sequencing with the remote -FTP server. -Resynchronization may be necessary following a violation of the FTP protocol -by the remote server. -.It Ic restart Ar marker -Restart the immediately following +is left unspecified, the local file name is used. +.It Ic mget Ar remote-files +Do a .Ic get -or +for each file name specified. +.It Ic mput Ar local-files +Do a .Ic put -at the -indicated -.Ar marker . -On -.Ux -systems, -.Ar marker -is usually a byte -offset into the file. -.It Ic rhelp Op Ar command-name -Request help from the remote 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 file -With no arguments, show status of remote machine. -If -.Ar file -is specified, show status of -.Ar file -on remote machine. -.It Ic runique Op Ic on | off -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 -.Dq .1 -is appended to the name. -If the resulting name matches another existing file, -a -.Dq .2 -is appended to the original name. -If this process continues up to -.Dq .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 Op Ic on | off -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 FTP implementations which do ignore -.Dv PORT -commands but, incorrectly, indicate they've been accepted. -.It Ic site Ar arg ... -The arguments specified are sent, verbatim, to the remote FTP server as a -.Dv SITE -command. -.It Ic size Ar file -Return size of -.Ar file -on remote machine. -.It Ic status -Show the current status of -.Nm . -.\" .It Ic struct Op Ar struct-name -.\" Set the file transfer -.\" .Ar structure -.\" to -.\" .Ar struct-name . -.\" By default, -.\" .Dq file -.\" structure is used. -.It Ic sunique Op Ic on | off -Toggle storing of files on remote machine under unique file names. -The remote FTP server must support the FTP protocol -.Dv STOU -command for -successful completion. -The remote server will report the unique name. -Default value is off. -.It Ic system -Show the type of operating system running on the remote machine. -.It Ic trace Op Ic on | off -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 -.Dq binary . -.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 Xo -.Ic user Ar username -.Op Ar password Op Ar account -.Xc -Identify yourself to the remote 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 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 -FTP server. -.It Ic verbose Op Ic on | off -Toggle verbose mode. -In verbose mode, all responses from -the 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. +for each file name specified. .El -.Pp -Command arguments which have embedded spaces may be quoted with -quote -.Pq Ql \&" -marks. -.Pp -Commands which toggle settings can take an explicit -.Ic on -or -.Ic off -argument to force the setting appropriately. -.Pp -If -.Nm -receives a -.Dv SIGINFO -(see the -.Dq status -argument of -.Xr stty 1 ) -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 @@ -1324,15 +320,10 @@ on the command line. .Pp The following formats are valid syntax for an auto-fetch element: .Bl -tag -width Ds -.It Ar host : Ns / Ns Ar file Ns Op / -.Dq Classic -.Nm -format. .Sm off -.It Xo -.Pf ftp:// Op Ar user : password No @ +.It Xo ftp:// .Ar host Op : Ar port -.No / Ar file Op / +.No / Ar file .Xc .Sm on An FTP URL, retrieved using the FTP protocol if @@ -1340,20 +331,8 @@ An FTP URL, retrieved using the FTP protocol if isn't defined. Otherwise, transfer using HTTP via the proxy defined in .Ev ftp_proxy . -If a -.Ar user -and -.Ar password -are given and -.Ev ftp_proxy -isn't defined, -log in as -.Ar user -with a password of -.Ar password . .Sm off -.It Xo -.Pf http:// Op Ar user : password No @ +.It Xo http:// .Ar host Op : Ar port .No / Ar file .Xc @@ -1362,21 +341,8 @@ An HTTP URL, retrieved using the HTTP protocol. If .Ev http_proxy is defined, it is used as a URL to an HTTP proxy server. -If a -.Ar user -and -.Ar password -are given and -.Ev http_proxy -isn't defined, -log in as -.Ar user -with a password of -.Ar password -using Basic authentication. .Sm off -.It Xo -.Pf https:// Op Ar user : password No @ +.It Xo https:// .Ar host Op : Ar port .No / Ar file .Xc @@ -1386,367 +352,19 @@ If .Ev http_proxy is defined, this HTTPS proxy server will be used to fetch the file using the CONNECT method. -If a -.Ar user -and -.Ar password -are given and -.Ev http_proxy -isn't defined, -log in as -.Ar user -with a password of -.Ar password -using Basic authentication. .It Pf file: Ar file .Ar file is retrieved from a mounted file system. .El -.Pp -If a classic format or an FTP URL format has a trailing -.Sq / , -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. -.Pp -If -.Ar file -contains a glob character and globbing is enabled -(see -.Ic glob ) , -then the equivalent of -.Ic mget Ar file -is performed. -.Pp -If no -.Fl o -option is specified, and -the directory component of -.Ar file -contains no globbing characters, -then -it is stored in the current directory as the -.Xr basename 1 -of -.Ar file . -If -.Fl o Ar output -is specified, then -.Ar file -is stored as -.Ar output . -Otherwise, the remote name is used as the local name. -.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 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, an -.Ql ftp\*(Gt -prompt will not appear until the remote server has completed -sending the requested file. -.Pp -The terminal interrupt key sequence will be ignored when -.Nm -has completed any local processing and is awaiting a reply -from the remote server. -A long delay in this mode may result from the ABOR processing described -above, or from unexpected behavior by the remote server, including -violations of the FTP protocol. -If the delay results from unexpected remote server behavior, the local -.Nm -program must be killed by hand. -.Sh FILE NAMING CONVENTIONS -Files specified as arguments to -.Nm -commands are processed according to the following rules. -.Bl -enum -.It -If -.Sq - -is specified as a local file name, the standard input (for reading) -or standard output (for writing) -is used. -.It -If the first character of a local 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 standard output -(standard input). -If the shell command includes spaces, the argument -must be quoted; e.g., -.Qq ls -lt . -A particularly -useful example of this mechanism is: -.Qq ls \&. |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 -.Ic glob -command. -If the -.Nm -command expects a single local file (e.g., -.Ic put ) , -only the first filename generated by the -.Dq 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 FTP specification specifies many parameters which may -affect a file transfer. -The -.Ic type -may be one of -.Dq ascii , -.Dq binary , -or -.Dq image . -.Nm -supports the ASCII and image types of file transfer. -.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. -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 -.Ar automatic -anonymous 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 null 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 utilized 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. -.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 ENVIRONMENT .Nm utilizes the following environment variables: -.Bl -tag -width "FTPSERVERPORT" -.It Ev FTPMODE -Overrides the default operation mode. -Recognized values are: -.Pp -.Bl -tag -width "passive " -offset indent -compact -.It passive -passive mode FTP only -.It active -active mode FTP only -.It auto -automatic determination of passive or active (this is the default) -.It gate -gate-ftp mode -.El -.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 HOME -For default location of a -.Pa .netrc -file, if one exists. -.It Ev PAGER -Used by -.Ic page -to display files. -.It Ev SHELL -For default shell. +.Bl -tag -width Ds .It Ev ftp_proxy URL of FTP proxy to use when making FTP URL requests (if not defined, use the standard FTP protocol). .It Ev http_proxy -URL of HTTP proxy to use when making HTTP or HTTPS URL requests. -.It Ev http_cookies -Path of a Netscape-like cookiejar file to use when making -HTTP or HTTPS URL requests. +URL of HTTP proxy to use when making HTTP(S) URL requests. .El .Sh PORT ALLOCATION For active mode data connections, @@ -1758,45 +376,19 @@ variables .Va net.inet.ip.porthifirst and .Va net.inet.ip.porthilast . -.Sh SEE ALSO -.Xr basename 1 , -.Xr csh 1 , -.Xr more 1 , -.Xr stty 1 , -.Xr tar 1 , -.Xr tftp 1 , -.Xr editline 3 , -.Xr getservbyname 3 , -.Xr popen 3 , -.Xr editrc 5 , -.Xr services 5 , -.Xr ftp-proxy 8 , -.Xr ftpd 8 -.Sh STANDARDS -.Rs -.%A J. Postel -.%A J. Reynolds -.%D October 1985 -.%R RFC 959 -.%T FILE TRANSFER PROTOCOL (FTP) -.Re -.Pp -.Rs -.%A P. Hethmon -.%D March 2007 -.%R RFC 3659 -.%T Extensions to FTP -.Re .Sh HISTORY The .Nm -command appeared in +command first appeard in .Bx 4.2 . -.Sh BUGS -Correct execution of many commands depends upon proper behavior -by the remote server. -.Pp -In the recursive mode of -.Ic mget , -files and directories starting with whitespace are ignored -because the list cannot be parsed any other way. +A complete rewrite of the +.Nm +command first appeared in +.Ox x.x . +.Sh AUTHORS +.An Sunil Nimmagadda Aq Mt sunil@openbsd.org +.Sh CAVEATS +While aborting a data transfer, certain FTP servers violate +the protocol by not responding with a 426 reply first, thereby making +.Nm +wait indefinitely for a correct reply. diff --git a/usr.bin/ftp/ftp.c b/usr.bin/ftp/ftp.c index 8ac659d6121..f3f051d7be8 100644 --- a/usr.bin/ftp/ftp.c +++ b/usr.bin/ftp/ftp.c @@ -1,2088 +1,450 @@ -/* $OpenBSD: ftp.c,v 1.100 2016/08/22 16:27:00 millert Exp $ */ -/* $NetBSD: ftp.c,v 1.27 1997/08/18 10:20:23 lukem Exp $ */ - /* - * Copyright (C) 1997 and 1998 WIDE Project. - * All rights reserved. + * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org> * - * 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. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * 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. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* - * 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 <sys/types.h> -#include <sys/stat.h> #include <sys/socket.h> -#include <netinet/in.h> -#include <netinet/ip.h> #include <arpa/inet.h> -#include <arpa/ftp.h> -#include <arpa/telnet.h> +#include <netinet/in.h> -#include <ctype.h> #include <err.h> #include <errno.h> +#include <libgen.h> +#include <limits.h> #include <netdb.h> -#include <poll.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <utime.h> -#include "ftp_var.h" +#include "ftp.h" +#include "xmalloc.h" -union sockaddr_union { - struct sockaddr sa; - struct sockaddr_in sin; - struct sockaddr_in6 sin6; -}; +static FILE *ctrl_fp; +static int data_fd; -union sockaddr_union myctladdr, hisctladdr, data_addr; +void +ftp_connect(struct url *url, struct url *proxy, int timeout) +{ + char *buf = NULL; + size_t n = 0; + int sock; -int data = -1; -int abrtflag = 0; -jmp_buf ptabort; -int ptabflg; -int ptflag = 0; -off_t restart_point = 0; + if (proxy) { + http_connect(url, proxy, timeout); + return; + } + if ((sock = tcp_connect(url->host, url->port, timeout)) == -1) + exit(1); -FILE *cin, *cout; + if ((ctrl_fp = fdopen(sock, "r+")) == NULL) + err(1, "%s: fdopen", __func__); -char * -hookup(char *host, char *port) -{ - int s, tos, error; - static char hostnamebuf[HOST_NAME_MAX+1]; - struct addrinfo hints, *res, *res0; -#ifndef SMALL - struct addrinfo *ares; -#endif - char hbuf[NI_MAXHOST]; - char *cause = "unknown"; - socklen_t namelen; - - epsv4bad = 0; - - memset((char *)&hisctladdr, 0, sizeof (hisctladdr)); - 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 == EAI_SERVICE) { - /* - * If the services file is corrupt/missing, fall back - * on our hard-coded defines. - */ - char pbuf[NI_MAXSERV]; - - pbuf[0] = '\0'; - if (strcmp(port, "ftp") == 0) - snprintf(pbuf, sizeof(pbuf), "%d", FTP_PORT); - else if (strcmp(port, "ftpgate") == 0) - snprintf(pbuf, sizeof(pbuf), "%d", GATE_PORT); - else if (strcmp(port, "http") == 0) - snprintf(pbuf, sizeof(pbuf), "%d", HTTP_PORT); -#ifndef SMALL - else if (strcmp(port, "https") == 0) - snprintf(pbuf, sizeof(pbuf), "%d", HTTPS_PORT); -#endif /* !SMALL */ - if (pbuf[0]) - error = getaddrinfo(host, pbuf, &hints, &res0); - } - if (error) { - if (error == EAI_SERVICE) - warnx("%s: bad port number `%s'", host, port); - else - warnx("%s: %s", host, gai_strerror(error)); - code = -1; - return (0); + /* greeting */ + if (ftp_getline(&buf, &n, 0, ctrl_fp) != P_OK) { + warnx("Can't connect to host `%s'", url->host); + ftp_command(ctrl_fp, "QUIT"); + exit(1); } - if (res0->ai_canonname) - strlcpy(hostnamebuf, res0->ai_canonname, sizeof(hostnamebuf)); - else - strlcpy(hostnamebuf, host, sizeof(hostnamebuf)); - hostname = hostnamebuf; - -#ifndef SMALL - if (srcaddr) { - struct addrinfo ahints; - - memset(&ahints, 0, sizeof(ahints)); - ahints.ai_family = family; - ahints.ai_socktype = SOCK_STREAM; - ahints.ai_flags |= AI_NUMERICHOST; - ahints.ai_protocol = 0; - - error = getaddrinfo(srcaddr, NULL, &ahints, &ares); - if (error) { - warnx("%s: %s", srcaddr, gai_strerror(error)); - code = -1; - return (0); - } + free(buf); + log_info("Connected to %s\n", url->host); + if (ftp_auth(ctrl_fp, NULL, NULL) != P_OK) { + warnx("Can't login to host `%s'", url->host); + ftp_command(ctrl_fp, "QUIT"); + exit(1); } -#endif /* !SMALL */ - - s = -1; - for (res = res0; res; res = res->ai_next) { - if (res0->ai_next) /* if we have multiple possibilities */ - { - if (getnameinfo(res->ai_addr, res->ai_addrlen, - hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) - strlcpy(hbuf, "unknown", sizeof(hbuf)); - if (verbose) - fprintf(ttyout, "Trying %s...\n", hbuf); - } - s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (s < 0) { - cause = "socket"; - continue; - } -#ifndef SMALL - if (srcaddr) { - if (ares->ai_family != res->ai_family) { - close(s); - s = -1; - errno = EINVAL; - cause = "bind"; - continue; - } - if (bind(s, ares->ai_addr, ares->ai_addrlen) < 0) { - cause = "bind"; - error = errno; - close(s); - errno = error; - s = -1; - continue; - } - } -#endif /* !SMALL */ - for (error = connect(s, res->ai_addr, res->ai_addrlen); - error != 0 && errno == EINTR; error = connect_wait(s)) - continue; - if (error != 0) { - /* this "if" clause is to prevent print warning twice */ - if (verbose && res->ai_next) { - if (getnameinfo(res->ai_addr, res->ai_addrlen, - hbuf, sizeof(hbuf), NULL, 0, - NI_NUMERICHOST) != 0) - strlcpy(hbuf, "(unknown)", - sizeof(hbuf)); - warn("connect to address %s", hbuf); - } - cause = "connect"; - error = errno; - close(s); - errno = error; - s = -1; - continue; - } +} - /* finally we got one */ - break; - } - if (s < 0) { - warn("%s", cause); - code = -1; - freeaddrinfo(res0); - return 0; - } - memcpy(&hisctladdr, res->ai_addr, res->ai_addrlen); - namelen = res->ai_addrlen; - freeaddrinfo(res0); - res0 = res = NULL; -#ifndef SMALL - if (srcaddr) { - freeaddrinfo(ares); - ares = NULL; - } -#endif /* !SMALL */ - if (getsockname(s, &myctladdr.sa, &namelen) < 0) { - warn("getsockname"); - code = -1; - goto bad; - } - if (hisctladdr.sa.sa_family == AF_INET) { - tos = IPTOS_LOWDELAY; - if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0) - warn("setsockopt TOS (ignored)"); +struct url * +ftp_get(struct url *url, struct url *proxy, off_t *offset, off_t *sz) +{ + char *buf = NULL, *dir, *file; + + if (proxy) { + url = http_get(url, proxy, offset, sz); + /* this url should now be treated as HTTP */ + url->scheme = S_HTTP; + return url; } - cin = fdopen(s, "r"); - cout = fdopen(s, "w"); - if (cin == NULL || cout == NULL) { - warnx("fdopen failed."); - if (cin) - (void)fclose(cin); - if (cout) - (void)fclose(cout); - code = -1; - goto bad; + + log_info("Using binary mode to transfer files.\n"); + if (ftp_command(ctrl_fp, "TYPE I") != P_OK) + errx(1, "Failed to set mode to binary"); + + dir = dirname(url->path); + if (ftp_command(ctrl_fp, "CWD %s", dir) != P_OK) + errx(1, "CWD command failed"); + + log_info("Retrieving %s\n", url->path); + file = basename(url->path); + if (strcmp(url->fname, "-")) + log_info("local: %s remote: %s\n", url->fname, file); + else + log_info("remote: %s\n", file); + + if (ftp_size(ctrl_fp, file, sz, &buf) != P_OK) { + fprintf(stderr, "%s", buf); + ftp_command(ctrl_fp, "QUIT"); + exit(1); } - 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; + free(buf); + + if (activemode) + data_fd = ftp_eprt(ctrl_fp); + else if ((data_fd = ftp_epsv(ctrl_fp)) == -1) + data_fd = ftp_eprt(ctrl_fp); + + if (data_fd == -1) + errx(1, "Failed to establish data connection"); + + if (*offset && ftp_command(ctrl_fp, "REST %lld", *offset) != P_INTER) + errx(1, "REST command failed"); + + if (ftp_command(ctrl_fp, "RETR %s", file) != P_PRE) { + ftp_command(ctrl_fp, "QUIT"); + exit(1); } - { - int ret, on = 1; - - ret = setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)); -#ifndef SMALL - if (ret < 0 && debug) - warn("setsockopt"); -#endif /* !SMALL */ + + return url; +} + +void +ftp_save(struct url *url, FILE *dst_fp, off_t *offset) +{ + struct sockaddr_storage ss; + FILE *data_fp; + socklen_t len; + int s; + + if (activemode) { + len = sizeof(ss); + if ((s = accept(data_fd, (struct sockaddr *)&ss, &len)) == -1) + err(1, "%s: accept", __func__); + + close(data_fd); + data_fd = s; } - return (hostname); -bad: - (void)close(s); - return (NULL); + if ((data_fp = fdopen(data_fd, "r")) == NULL) + err(1, "%s: fdopen data_fd", __func__); + + copy_file(dst_fp, data_fp, offset); + fclose(data_fp); } -/* ARGSUSED */ void -cmdabort(int signo) +ftp_quit(struct url *url) { - int save_errno = errno; + char *buf = NULL; + size_t n = 0; - alarmtimer(0); - (void) write(fileno(ttyout), "\n\r", 2); - abrtflag++; + if (ftp_getline(&buf, &n, 0, ctrl_fp) != P_OK) + errx(1, "error retrieving file %s", url->fname); - errno = save_errno; - if (ptflag) - longjmp(ptabort, 1); + free(buf); + ftp_command(ctrl_fp, "QUIT"); + fclose(ctrl_fp); } int -command(const char *fmt, ...) +ftp_getline(char **lineptr, size_t *n, int suppress_output, FILE *fp) { - va_list ap; - int r; - sig_t oldintr; - - abrtflag = 0; -#ifndef SMALL - if (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); - (void)fflush(ttyout); - } -#endif /* !SMALL */ - if (cout == NULL) { - warnx("No control connection for command."); - code = -1; - return (0); - } - oldintr = signal(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 && oldintr != SIG_IGN) - (*oldintr)(SIGINT); - (void)signal(SIGINT, oldintr); - return (r); -} + ssize_t len; + char *bufp, code[4]; + const char *errstr; + int lookup[] = { P_PRE, P_OK, P_INTER, N_TRANS, N_PERM }; -int keep_alive_timeout = 60; /* 0 -> no timeout */ -static int full_noops_sent = 0; -static time_t last_timestamp = 0; /* 0 -> no measurement yet */ -static char noop[] = "NOOP\r\n"; -#define NOOP_LENGTH (sizeof noop - 1) -static int current_nop_pos = 0; /* 0 -> no noop started */ + if ((len = getline(lineptr, n, fp)) == -1) + err(1, "%s: getline", __func__); -/* to achieve keep alive, we send noop one byte at a time */ -static void -send_noop_char(void) -{ -#ifndef SMALL - if (debug) - fprintf(ttyout, "---> %c\n", noop[current_nop_pos]); -#endif /* !SMALL */ - fputc(noop[current_nop_pos++], cout); - (void)fflush(cout); - if (current_nop_pos >= NOOP_LENGTH) { - full_noops_sent++; - current_nop_pos = 0; - } -} + bufp = *lineptr; + if (!suppress_output) + log_info("%s", bufp); -static void -may_reset_noop_timeout(void) -{ - if (keep_alive_timeout != 0) - last_timestamp = time(NULL); -} + if (len < 4) + errx(1, "%s: line too short", __func__); -static void -may_receive_noop_ack(void) -{ - int i; + (void)strlcpy(code, bufp, sizeof code); + if (bufp[3] == ' ') + goto done; - if (cout == NULL) { - /* Lost connection; so just pretend we're fine. */ - current_nop_pos = full_noops_sent = 0; - return; - } + /* multi-line reply */ + while (!(strncmp(code, bufp, 3) == 0 && bufp[3] == ' ')) { + if ((len = getline(lineptr, n, fp)) == -1) + err(1, "%s: getline", __func__); + + bufp = *lineptr; + if (!suppress_output) + log_info("%s", bufp); - /* finish sending last incomplete noop */ - if (current_nop_pos != 0) { - fputs(&(noop[current_nop_pos]), cout); -#ifndef SMALL - if (debug) - fprintf(ttyout, "---> %s\n", &(noop[current_nop_pos])); -#endif /* !SMALL */ - (void)fflush(cout); - current_nop_pos = 0; - full_noops_sent++; + if (len < 4) + continue; } - /* and get the replies */ - for (i = 0; i < full_noops_sent; i++) - (void)getreply(0); - full_noops_sent = 0; -} + done: + (void)strtonum(code, 100, 553, &errstr); + if (errstr) + errx(1, "%s: Response code is %s: %s", __func__, errstr, code); -static void -may_send_noop_char(void) -{ - if (keep_alive_timeout != 0) { - if (last_timestamp != 0) { - time_t t = time(NULL); - - if (t - last_timestamp >= keep_alive_timeout) { - last_timestamp = t; - send_noop_char(); - } - } else { - last_timestamp = time(NULL); - } - } + return lookup[code[0] - '1']; } -char reply_string[BUFSIZ]; /* first line of previous reply */ - int -getreply(int expecteof) +ftp_command(FILE *fp, const char *fmt, ...) { - char current_line[BUFSIZ]; /* last line of previous reply */ - int c, n, lineno; - int dig; - int originalcode = 0, continuation = 0; - sig_t oldintr; - int pflag = 0; - char *cp, *pt = pasv; - - memset(current_line, 0, sizeof(current_line)); - oldintr = signal(SIGINT, cmdabort); - for (lineno = 0 ;; lineno++) { - dig = n = code = 0; - cp = current_line; - while ((c = fgetc(cin)) != '\n') { - if (c == IAC) { /* handle telnet commands */ - switch (c = fgetc(cin)) { - case WILL: - case WONT: - c = fgetc(cin); - fprintf(cout, "%c%c%c", IAC, DONT, c); - (void)fflush(cout); - break; - case DO: - case DONT: - c = fgetc(cin); - fprintf(cout, "%c%c%c", IAC, WONT, c); - (void)fflush(cout); - break; - default: - break; - } - continue; - } - dig++; - if (c == EOF) { - if (expecteof) { - (void)signal(SIGINT, oldintr); - code = 221; - return (0); - } - lostpeer(); - if (verbose) { - fputs( -"421 Service not available, remote server has closed connection.\n", ttyout); - (void)fflush(ttyout); - } - code = 421; - 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 (lineno == 0) { - size_t len = cp - current_line; - - if (len > sizeof(reply_string)) - len = sizeof(reply_string); - - (void)strlcpy(reply_string, current_line, len); - } - if (continuation && code != originalcode) { - if (originalcode == 0) - originalcode = code; - continue; - } - *cp = '\0'; - if (n != '1') - cpend = 0; - (void)signal(SIGINT, oldintr); - if (code == 421 || originalcode == 421) - lostpeer(); - if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN) - (*oldintr)(SIGINT); - return (n - '0'); - } -} + va_list ap; + char *buf = NULL, *cmd; + size_t n = 0; + int r; + + va_start(ap, fmt); + r = vasprintf(&cmd, fmt, ap); + va_end(ap); + if (r < 0) + errx(1, "%s: vasprintf", __func__); -#ifndef SMALL -jmp_buf sendabort; + if (io_debug) + fprintf(stderr, ">>> %s\n", cmd); + + if (fprintf(fp, "%s\r\n", cmd) < 0) + errx(1, "%s: fprintf", __func__); + + (void)fflush(fp); + free(cmd); + r = ftp_getline(&buf, &n, 0, fp); + free(buf); + return r; -/* ARGSUSED */ -void -abortsend(int signo) -{ - int save_errno = errno; - alarmtimer(0); - mflag = 0; - abrtflag = 0; -#define MSG "\nsend aborted\nwaiting for remote to finish abort.\n" - (void) write(fileno(ttyout), MSG, strlen(MSG)); -#undef MSG - - errno = save_errno; - longjmp(sendabort, 1); } -void -sendrequest(const char *cmd, const char *local, const char *remote, - int printnames) +int +ftp_auth(FILE *fp, const char *user, const char *pass) { - struct stat st; - int c, d; - FILE * volatile fin, * volatile dout; - int (* volatile closefunc)(FILE *); - volatile sig_t oldinti, oldintr, oldintp; - volatile off_t hashbytes; - char * volatile lmode; - char buf[BUFSIZ], *bufp; - int oprogress, serrno; - - hashbytes = mark; - direction = "sent"; - dout = NULL; - bytes = 0; - filesize = -1; - oprogress = progress; - if (verbose && printnames) { - if (local && *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; - oldinti = NULL; - lmode = "w"; - if (setjmp(sendabort)) { - while (cpend) { - (void)getreply(0); - } - if (data >= 0) { - (void)close(data); - data = -1; - } - if (oldintr) - (void)signal(SIGINT, oldintr); - if (oldintp) - (void)signal(SIGPIPE, oldintp); - if (oldinti) - (void)signal(SIGINFO, oldinti); - progress = oprogress; - code = -1; - return; - } - oldintr = signal(SIGINT, abortsend); - oldinti = signal(SIGINFO, psummary); - if (strcmp(local, "-") == 0) { - fin = stdin; - if (progress == 1) - progress = 0; - } else if (*local == '|') { - oldintp = signal(SIGPIPE, SIG_IGN); - fin = popen(local + 1, "r"); - if (fin == NULL) { - warn("%s", local + 1); - (void)signal(SIGINT, oldintr); - (void)signal(SIGPIPE, oldintp); - (void)signal(SIGINFO, oldinti); - code = -1; - return; - } - if (progress == 1) - progress = 0; - closefunc = pclose; - } else { - fin = fopen(local, "r"); - if (fin == NULL) { - warn("local: %s", local); - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - code = -1; - return; - } - closefunc = fclose; - if (fstat(fileno(fin), &st) < 0 || - (st.st_mode & S_IFMT) != S_IFREG) { - fprintf(ttyout, "%s: not a plain file.\n", local); - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - fclose(fin); - code = -1; - return; - } - filesize = st.st_size; - } - if (initconn()) { - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - if (oldintp) - (void)signal(SIGPIPE, oldintp); - code = -1; - progress = oprogress; - if (closefunc != NULL) - (*closefunc)(fin); - return; - } - if (setjmp(sendabort)) - goto abort; - - if (restart_point && - (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) { - int rc = -1; - - switch (curtype) { - case TYPE_A: - rc = fseeko(fin, restart_point, SEEK_SET); - break; - case TYPE_I: - if (lseek(fileno(fin), restart_point, SEEK_SET) != -1) - rc = 0; - break; - } - if (rc == -1) { - warn("local: %s", local); - progress = oprogress; - if (closefunc != NULL) - (*closefunc)(fin); - return; - } - if (command("REST %lld", (long long) restart_point) - != CONTINUE) { - progress = oprogress; - if (closefunc != NULL) - (*closefunc)(fin); - return; - } - lmode = "r+w"; - } - if (remote) { - if (command("%s %s", cmd, remote) != PRELIM) { - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - progress = oprogress; - if (oldintp) - (void)signal(SIGPIPE, oldintp); - if (closefunc != NULL) - (*closefunc)(fin); - return; - } - } else - if (command("%s", cmd) != PRELIM) { - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - progress = oprogress; - if (oldintp) - (void)signal(SIGPIPE, oldintp); - if (closefunc != NULL) - (*closefunc)(fin); - return; - } - dout = dataconn(lmode); - if (dout == NULL) - goto abort; - progressmeter(-1, remote); - may_reset_noop_timeout(); - oldintp = signal(SIGPIPE, SIG_IGN); - serrno = 0; - switch (curtype) { - - case TYPE_I: - d = 0; - while ((c = read(fileno(fin), buf, sizeof(buf))) > 0) { - may_send_noop_char(); - bytes += c; - for (bufp = buf; c > 0; c -= d, bufp += d) - if ((d = write(fileno(dout), bufp, (size_t)c)) - <= 0) - break; - if (hash && (!progress || filesize < 0) ) { - while (bytes >= hashbytes) { - (void)putc('#', ttyout); - hashbytes += mark; - } - (void)fflush(ttyout); - } - } - if (c == -1 || d == -1) - serrno = errno; - if (hash && (!progress || filesize < 0) && bytes > 0) { - if (bytes < mark) - (void)putc('#', ttyout); - (void)putc('\n', ttyout); - (void)fflush(ttyout); - } - if (c < 0) - warnc(serrno, "local: %s", local); - if (d < 0) { - if (serrno != EPIPE) - warnc(serrno, "netout"); - bytes = -1; - } - break; + char *addr = NULL, hn[HOST_NAME_MAX+1], *un; + int code; - case TYPE_A: - while ((c = fgetc(fin)) != EOF) { - may_send_noop_char(); - if (c == '\n') { - while (hash && (!progress || filesize < 0) && - (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 (ferror(fin) || ferror(dout)) - serrno = errno; - if (hash && (!progress || filesize < 0)) { - if (bytes < hashbytes) - (void)putc('#', ttyout); - (void)putc('\n', ttyout); - (void)fflush(ttyout); - } - if (ferror(fin)) - warnc(serrno, "local: %s", local); - if (ferror(dout)) { - if (errno != EPIPE) - warnc(serrno, "netout"); - bytes = -1; - } - break; - } - progressmeter(1, NULL); - progress = oprogress; - if (closefunc != NULL) - (*closefunc)(fin); - (void)fclose(dout); - (void)getreply(0); - may_receive_noop_ack(); - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - if (oldintp) - (void)signal(SIGPIPE, oldintp); - if (bytes > 0) - ptransfer(0); - return; -abort: - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - progress = oprogress; - if (oldintp) - (void)signal(SIGPIPE, oldintp); - if (!cpend) { - code = -1; - return; - } - if (data >= 0) { - (void)close(data); - data = -1; - } - if (dout) - (void)fclose(dout); - (void)getreply(0); - code = -1; - if (closefunc != NULL && fin != NULL) - (*closefunc)(fin); - if (bytes > 0) - ptransfer(0); -} -#endif /* !SMALL */ + code = ftp_command(fp, "USER %s", user ? user : "anonymous"); + if (code != P_OK && code != P_INTER) + return code; -jmp_buf recvabort; + if (pass == NULL) { + if (gethostname(hn, sizeof hn) == -1) + err(1, "%s: gethostname", __func__); -/* ARGSUSED */ -void -abortrecv(int signo) -{ + un = getlogin(); + xasprintf(&addr, "%s@%s", un ? un : "anonymous", hn); + } - alarmtimer(0); - mflag = 0; - abrtflag = 0; - fputs("\nreceive aborted\nwaiting for remote to finish abort.\n", ttyout); - (void)fflush(ttyout); - longjmp(recvabort, 1); + code = ftp_command(fp, "PASS %s", pass ? pass : addr); + free(addr); + return code; } -void -recvrequest(const char *cmd, const char * volatile local, const char *remote, - const char *lmode, int printnames, int ignorespecial) +int +ftp_size(FILE *fp, const char *fn, off_t *sizep, char **buf) { - FILE * volatile fout, * volatile din; - int (* volatile closefunc)(FILE *); - volatile sig_t oldinti, oldintr, oldintp; - int c, d, serrno; - volatile int is_retr, tcrflag, bare_lfs; - static size_t bufsize; - static char *buf; - volatile off_t hashbytes; - struct stat st; - time_t mtime; - int oprogress; - int opreserve; - - fout = NULL; - din = NULL; - oldinti = 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 (local && (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 (setjmp(recvabort)) { - while (cpend) { - (void)getreply(0); - } - if (data >= 0) { - (void)close(data); - data = -1; - } - if (oldintr) - (void)signal(SIGINT, oldintr); - if (oldinti) - (void)signal(SIGINFO, oldinti); - progress = oprogress; - preserve = opreserve; - code = -1; - return; - } - oldintr = signal(SIGINT, abortrecv); - oldinti = signal(SIGINFO, psummary); - if (ignorespecial || (strcmp(local, "-") && *local != '|')) { - if (access(local, W_OK) < 0) { - char *dir; - - if (errno != ENOENT && errno != EACCES) { - warn("local: %s", local); - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - code = -1; - return; - } - dir = strrchr(local, '/'); - if (dir != NULL) - *dir = 0; - d = access(dir == local ? "/" : dir ? local : ".", W_OK); - if (dir != NULL) - *dir = '/'; - if (d < 0) { - warn("local: %s", local); - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - code = -1; - return; - } - if (!runique && errno == EACCES && - chmod(local, (S_IRUSR|S_IWUSR)) < 0) { - warn("local: %s", local); - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - code = -1; - return; - } - if (runique && errno == EACCES && - (local = gunique(local)) == NULL) { - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - code = -1; - return; - } - } - else if (runique && (local = gunique(local)) == NULL) { - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - code = -1; - return; - } - } - if (!is_retr) { - if (curtype != TYPE_A) - changetype(TYPE_A, 0); - } else { - if (curtype != type) - changetype(type, 0); - filesize = remotesize(remote, 0); - } - if (initconn()) { - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - code = -1; - return; - } - if (setjmp(recvabort)) - goto abort; - if (is_retr && restart_point && - command("REST %lld", (long long) restart_point) != CONTINUE) - return; - if (remote) { - if (command("%s %s", cmd, remote) != PRELIM) { - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - return; - } - } else { - if (command("%s", cmd) != PRELIM) { - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - return; - } - } - din = dataconn("r"); - if (din == NULL) - goto abort; - if (!ignorespecial && strcmp(local, "-") == 0) { - fout = stdout; - preserve = 0; - } else if (!ignorespecial && *local == '|') { - oldintp = signal(SIGPIPE, SIG_IGN); - fout = popen(local + 1, "w"); - if (fout == NULL) { - warn("%s", local+1); - goto abort; - } - if (progress == 1) - progress = 0; - preserve = 0; - closefunc = pclose; - } else { - fout = fopen(local, lmode); - if (fout == NULL) { - warn("local: %s", local); - goto abort; - } - closefunc = fclose; - } - if (fstat(fileno(fout), &st) < 0 || st.st_blksize == 0) - st.st_blksize = BUFSIZ; - if (st.st_blksize > bufsize) { - (void)free(buf); - buf = malloc((unsigned)st.st_blksize); - if (buf == NULL) { - warn("malloc"); - bufsize = 0; - goto abort; - } - bufsize = st.st_blksize; - } - if ((st.st_mode & S_IFMT) != S_IFREG) { - if (progress == 1) - progress = 0; - preserve = 0; - } - progressmeter(-1, remote); - may_reset_noop_timeout(); - serrno = 0; - switch (curtype) { - - case TYPE_I: - if (restart_point && - lseek(fileno(fout), restart_point, SEEK_SET) < 0) { - warn("local: %s", local); - progress = oprogress; - preserve = opreserve; - if (closefunc != NULL) - (*closefunc)(fout); - return; - } - errno = d = 0; - while ((c = read(fileno(din), buf, bufsize)) > 0) { - ssize_t wr; - size_t rd = c; - - may_send_noop_char(); - d = 0; - do { - wr = write(fileno(fout), buf + d, rd); - if (wr == -1) { - d = -1; - break; - } - d += wr; - rd -= wr; - } while (d < c); - if (rd != 0) - break; - bytes += c; - if (hash && (!progress || filesize < 0)) { - while (bytes >= hashbytes) { - (void)putc('#', ttyout); - hashbytes += mark; - } - (void)fflush(ttyout); - } - } - if (c == -1 || d < c) - serrno = errno; - if (hash && (!progress || filesize < 0) && bytes > 0) { - if (bytes < mark) - (void)putc('#', ttyout); - (void)putc('\n', ttyout); - (void)fflush(ttyout); - } - if (c < 0) { - if (serrno != EPIPE) - warnc(serrno, "netin"); - bytes = -1; - } - if (d < c) { - if (d < 0) - warnc(serrno, "local: %s", local); - else - warnx("%s: short write", local); - } - break; + size_t n = 0; + off_t file_sz; + int code; - case TYPE_A: - if (restart_point) { - int i, n, ch; - - if (fseek(fout, 0L, SEEK_SET) < 0) - goto done; - n = restart_point; - for (i = 0; i++ < n;) { - if ((ch = fgetc(fout)) == EOF) { - if (!ferror(fout)) - errno = 0; - goto done; - } - if (ch == '\n') - i++; - } - if (fseek(fout, 0L, SEEK_CUR) < 0) { -done: - if (errno) - warn("local: %s", local); - else - warnx("local: %s", local); - progress = oprogress; - preserve = opreserve; - if (closefunc != NULL) - (*closefunc)(fout); - return; - } - } - while ((c = fgetc(din)) != EOF) { - may_send_noop_char(); - if (c == '\n') - bare_lfs++; - while (c == '\r') { - while (hash && (!progress || filesize < 0) && - (bytes >= hashbytes)) { - (void)putc('#', ttyout); - (void)fflush(ttyout); - hashbytes += mark; - } - bytes++; - if ((c = fgetc(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 (ferror(din) || ferror(fout)) - serrno = errno; - 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 (hash && (!progress || filesize < 0)) { - if (bytes < hashbytes) - (void)putc('#', ttyout); - (void)putc('\n', ttyout); - (void)fflush(ttyout); - } - if (ferror(din)) { - if (serrno != EPIPE) - warnc(serrno, "netin"); - bytes = -1; - } - if (ferror(fout)) - warnc(serrno, "local: %s", local); - break; - } - progressmeter(1, NULL); - progress = oprogress; - preserve = opreserve; - if (closefunc != NULL) - (*closefunc)(fout); - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - if (oldintp) - (void)signal(SIGPIPE, oldintp); - (void)fclose(din); - (void)getreply(0); - may_receive_noop_ack(); - if (bytes >= 0 && is_retr) { - if (bytes > 0) - ptransfer(0); - if (preserve && (closefunc == fclose)) { - mtime = remotemodtime(remote, 0); - if (mtime != -1) { - struct utimbuf ut; - - ut.actime = time(NULL); - ut.modtime = mtime; - if (utime(local, &ut) == -1) - fprintf(ttyout, - "Can't change modification time on %s to %s", - local, asctime(localtime(&mtime))); - } - } - } - return; - -abort: - /* abort using RFC959 recommended IP,SYNC sequence */ - progress = oprogress; - preserve = opreserve; - if (oldintp) - (void)signal(SIGPIPE, oldintp); - (void)signal(SIGINT, SIG_IGN); - if (!cpend) { - code = -1; - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); - return; - } + if (io_debug) + fprintf(stderr, ">>> SIZE %s\n", fn); - abort_remote(din); - code = -1; - if (data >= 0) { - (void)close(data); - data = -1; - } - if (closefunc != NULL && fout != NULL) - (*closefunc)(fout); - if (din) - (void)fclose(din); - if (bytes > 0) - ptransfer(0); - (void)signal(SIGINT, oldintr); - (void)signal(SIGINFO, oldinti); + if (fprintf(fp, "SIZE %s\r\n", fn) < 0) + errx(1, "%s: fprintf", __func__); + + (void)fflush(fp); + if ((code = ftp_getline(buf, &n, 1, fp)) != P_OK) + return code; + + if (sscanf(*buf, "%*u %lld", &file_sz) != 1) + errx(1, "%s: sscanf size", __func__); + + if (sizep) + *sizep = file_sz; + + return code; } -/* - * Need to start a listen on the data channel before we send the command, - * otherwise the server's connect may fail. - */ int -initconn(void) +ftp_eprt(FILE *fp) { - char *p, *a; - int result = ERROR, tmpno = 0; - int on = 1; - int error; - u_int addr[16], port[2]; - u_int af, hal, pal; - char *pasvcmd = NULL; - socklen_t namelen; -#ifndef SMALL - struct addrinfo *ares; -#endif - - if (myctladdr.sa.sa_family == AF_INET6 - && (IN6_IS_ADDR_LINKLOCAL(&myctladdr.sin6.sin6_addr) - || IN6_IS_ADDR_SITELOCAL(&myctladdr.sin6.sin6_addr))) { - warnx("use of scoped address can be troublesome"); - } -#ifndef SMALL - if (srcaddr) { - struct addrinfo ahints; - - memset(&ahints, 0, sizeof(ahints)); - ahints.ai_family = family; - ahints.ai_socktype = SOCK_STREAM; - ahints.ai_flags |= AI_NUMERICHOST; - ahints.ai_protocol = 0; - - error = getaddrinfo(srcaddr, NULL, &ahints, &ares); - if (error) { - warnx("%s: %s", srcaddr, gai_strerror(error)); - code = -1; - return (0); - } + struct sockaddr_storage ss; + char addr[NI_MAXHOST], port[NI_MAXSERV], *eprt; + socklen_t len; + int e, on, ret, sock; + + len = sizeof(ss); + memset(&ss, 0, len); + if (getsockname(fileno(fp), (struct sockaddr *)&ss, &len) == -1) { + warn("%s: getsockname", __func__); + return -1; } -#endif /* !SMALL */ -reinit: - if (passivemode) { - data_addr = myctladdr; - data = socket(data_addr.sa.sa_family, SOCK_STREAM, 0); - if (data < 0) { - warn("socket"); - return (1); - } -#ifndef SMALL - if (srcaddr) { - if (bind(data, ares->ai_addr, ares->ai_addrlen) < 0) { - warn("bind"); - close(data); - return (1); - } - } - if ((options & SO_DEBUG) && - setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, - sizeof(on)) < 0) - warn("setsockopt (ignored)"); -#endif /* !SMALL */ - switch (data_addr.sa.sa_family) { - case AF_INET: - if (epsv4 && !epsv4bad) { - int ov; - /* shut this command up in case it fails */ - ov = verbose; - verbose = -1; - result = command(pasvcmd = "EPSV"); - /* - * now back to whatever verbosity we had before - * and we can try PASV - */ - verbose = ov; - if (code / 10 == 22 && code != 229) { - fputs( -"wrong server: return code must be 229\n", - ttyout); - result = COMPLETE + 1; - } - if (result != COMPLETE) { - epsv4bad = 1; -#ifndef SMALL - if (debug) { - fputs( -"disabling epsv4 for this connection\n", - ttyout); - } -#endif /* !SMALL */ - } - } - if (result != COMPLETE) - result = command(pasvcmd = "PASV"); - break; - case AF_INET6: - result = command(pasvcmd = "EPSV"); - if (code / 10 == 22 && code != 229) { - fputs( -"wrong server: return code must be 229\n", - ttyout); - result = COMPLETE + 1; - } - if (result != COMPLETE) - result = command(pasvcmd = "LPSV"); - break; - default: - result = COMPLETE + 1; - break; - } - if (result != COMPLETE) { - if (activefallback) { - (void)close(data); - data = -1; - passivemode = 0; - activefallback = 0; - 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)) - - /* - * What we've got at this point is a string of comma separated - * one-byte unsigned integer values, separated by commas. - */ - if (!pasvcmd) - goto bad; - if (strcmp(pasvcmd, "PASV") == 0) { - if (data_addr.sa.sa_family != AF_INET) { - fputs( -"Passive mode AF mismatch. Shouldn't happen!\n", ttyout); - goto bad; - } - if (code / 10 == 22 && code != 227) { - fputs("wrong server: return code must be 227\n", - ttyout); - 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); - goto bad; - } - memset(&data_addr, 0, sizeof(data_addr)); - data_addr.sin.sin_family = AF_INET; - data_addr.sin.sin_len = sizeof(struct sockaddr_in); - data_addr.sin.sin_addr.s_addr = - htonl(pack4(addr, 0)); - data_addr.sin.sin_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); - goto bad; - } - switch (data_addr.sa.sa_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); - goto bad; - } - if (af != 4 || hal != 4 || pal != 2) { - fputs( -"Passive mode AF mismatch. Shouldn't happen!\n", ttyout); - error = 1; - goto bad; - } - - memset(&data_addr, 0, sizeof(data_addr)); - data_addr.sin.sin_family = AF_INET; - data_addr.sin.sin_len = sizeof(struct sockaddr_in); - data_addr.sin.sin_addr.s_addr = - htonl(pack4(addr, 0)); - data_addr.sin.sin_port = htons(pack2(port, 0)); - break; - 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); - goto bad; - } - if (af != 6 || hal != 16 || pal != 2) { - fputs( -"Passive mode AF mismatch. Shouldn't happen!\n", ttyout); - goto bad; - } - - memset(&data_addr, 0, sizeof(data_addr)); - data_addr.sin6.sin6_family = AF_INET6; - data_addr.sin6.sin6_len = sizeof(struct sockaddr_in6); - { - u_int32_t *p32; - p32 = (u_int32_t *)&data_addr.sin6.sin6_addr; - p32[0] = htonl(pack4(addr, 0)); - p32[1] = htonl(pack4(addr, 4)); - p32[2] = htonl(pack4(addr, 8)); - p32[3] = htonl(pack4(addr, 12)); - } - data_addr.sin6.sin6_port = htons(pack2(port, 0)); - break; - default: - fputs("Bad family!\n", ttyout); - goto bad; - } - } 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); - 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); - goto bad; - } - if (delim[0] != delim[1] || delim[0] != delim[2] - || delim[0] != delim[3]) { - fputs("parse error!\n", ttyout); - goto bad; - } - data_addr = hisctladdr; - data_addr.sin.sin_port = htons(port[1]); - } else - goto bad; - - for (error = connect(data, &data_addr.sa, data_addr.sa.sa_len); - error != 0 && errno == EINTR; - error = connect_wait(data)) - continue; - if (error != 0) { - if (activefallback) { - (void)close(data); - data = -1; - passivemode = 0; - activefallback = 0; - goto reinit; - } - warn("connect"); - goto bad; - } - if (data_addr.sa.sa_family == AF_INET) { - on = IPTOS_THROUGHPUT; - if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, - sizeof(int)) < 0) - warn("setsockopt TOS (ignored)"); - } - return (0); + + /* pick a free port */ + switch (ss.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&ss)->sin_port = 0; + break; + case AF_INET6: + ((struct sockaddr_in6 *)&ss)->sin6_port = 0; + break; + default: + errx(1, "%s: Invalid socket family", __func__); } -noport: - data_addr = myctladdr; - if (sendport) - data_addr.sin.sin_port = 0; /* let system pick one */ - if (data != -1) - (void)close(data); - data = socket(data_addr.sa.sa_family, SOCK_STREAM, 0); - if (data < 0) { - warn("socket"); - if (tmpno) - sendport = 1; - return (1); + if ((sock = socket(ss.ss_family, SOCK_STREAM, 0)) == -1) { + warn("%s: socket", __func__); + return -1; } - if (!sendport) - if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, - sizeof(on)) < 0) { - warn("setsockopt (reuse address)"); - goto bad; - } - switch (data_addr.sa.sa_family) { + + switch (ss.ss_family) { case AF_INET: on = IP_PORTRANGE_HIGH; - if (setsockopt(data, IPPROTO_IP, IP_PORTRANGE, + if (setsockopt(sock, IPPROTO_IP, IP_PORTRANGE, (char *)&on, sizeof(on)) < 0) warn("setsockopt IP_PORTRANGE (ignored)"); break; case AF_INET6: on = IPV6_PORTRANGE_HIGH; - if (setsockopt(data, IPPROTO_IPV6, IPV6_PORTRANGE, + if (setsockopt(sock, IPPROTO_IPV6, IPV6_PORTRANGE, (char *)&on, sizeof(on)) < 0) warn("setsockopt IPV6_PORTRANGE (ignored)"); break; } - if (bind(data, &data_addr.sa, data_addr.sa.sa_len) < 0) { - warn("bind"); - goto bad; - } -#ifndef SMALL - if (options & SO_DEBUG && - setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, - sizeof(on)) < 0) - warn("setsockopt (ignored)"); -#endif /* !SMALL */ - namelen = sizeof(data_addr); - if (getsockname(data, &data_addr.sa, &namelen) < 0) { - warn("getsockname"); - goto bad; - } - if (listen(data, 1) < 0) - warn("listen"); - -#define UC(b) (((int)b)&0xff) - - if (sendport) { - char hname[NI_MAXHOST], pbuf[NI_MAXSERV]; - int af_tmp; - union sockaddr_union tmp; - - tmp = data_addr; - switch (tmp.sa.sa_family) { - case AF_INET: - if (!epsv4 || epsv4bad) { - result = COMPLETE +1; - break; - } - /*FALLTHROUGH*/ - case AF_INET6: - if (tmp.sa.sa_family == AF_INET6) - tmp.sin6.sin6_scope_id = 0; - af_tmp = (tmp.sa.sa_family == AF_INET) ? 1 : 2; - if (getnameinfo(&tmp.sa, tmp.sa.sa_len, hname, - sizeof(hname), pbuf, sizeof(pbuf), - NI_NUMERICHOST | NI_NUMERICSERV)) { - result = ERROR; - } else { - result = command("EPRT |%d|%s|%s|", - af_tmp, hname, pbuf); - if (result != COMPLETE) { - epsv4bad = 1; -#ifndef SMALL - if (debug) { - fputs( -"disabling epsv4 for this connection\n", - ttyout); - } -#endif /* !SMALL */ - } - } - break; - default: - result = COMPLETE + 1; - break; - } - if (result == COMPLETE) - goto skip_port; - - switch (data_addr.sa.sa_family) { - case AF_INET: - a = (char *)&data_addr.sin.sin_addr; - p = (char *)&data_addr.sin.sin_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; - case AF_INET6: - a = (char *)&data_addr.sin6.sin6_addr; - p = (char *)&data_addr.sin6.sin6_port; - 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, - UC(a[0]),UC(a[1]),UC(a[2]),UC(a[3]), - UC(a[4]),UC(a[5]),UC(a[6]),UC(a[7]), - UC(a[8]),UC(a[9]),UC(a[10]),UC(a[11]), - UC(a[12]),UC(a[13]),UC(a[14]),UC(a[15]), - 2, UC(p[0]), UC(p[1])); - break; - default: - result = COMPLETE + 1; /* xxx */ - } - skip_port: - - if (result == ERROR && sendport == -1) { - sendport = 0; - tmpno = 1; - goto noport; - } - return (result != COMPLETE); - } - if (tmpno) - sendport = 1; - if (data_addr.sa.sa_family == AF_INET) { - on = IPTOS_THROUGHPUT; - if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, - sizeof(int)) < 0) - warn("setsockopt TOS (ignored)"); + + if (bind(sock, (struct sockaddr *)&ss, len) == -1) { + close(sock); + warn("%s: bind", __func__); + return -1; } - return (0); -bad: - (void)close(data), data = -1; - if (tmpno) - sendport = 1; - return (1); -} -FILE * -dataconn(const char *lmode) -{ - union sockaddr_union from; - socklen_t fromlen = myctladdr.sa.sa_len; - int s; - - if (passivemode) - return (fdopen(data, lmode)); - - s = accept(data, &from.sa, &fromlen); - if (s < 0) { - warn("accept"); - (void)close(data), data = -1; - return (NULL); + if (listen(sock, 1) == -1) { + close(sock); + warn("%s: listen", __func__); + return -1; } - (void)close(data); - data = s; - if (from.sa.sa_family == AF_INET) { - int tos = IPTOS_THROUGHPUT; - if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, - sizeof(int)) < 0) { - warn("setsockopt TOS (ignored)"); - } + + /* Find out the ephermal port chosen */ + len = sizeof(ss); + memset(&ss, 0, len); + if (getsockname(sock, (struct sockaddr *)&ss, &len) == -1) { + close(sock); + warn("%s: getsockname", __func__); + return -1; } - return (fdopen(data, lmode)); -} -/* ARGSUSED */ -void -psummary(int signo) -{ - int save_errno = errno; + if ((e = getnameinfo((struct sockaddr *)&ss, len, + addr, sizeof(addr), port, sizeof(port), + NI_NUMERICHOST | NI_NUMERICSERV)) != 0) { + close(sock); + warn("%s: getnameinfo: %s", __func__, gai_strerror(e)); + return -1; + } - if (bytes > 0) - ptransfer(1); - errno = save_errno; -} + xasprintf(&eprt, "EPRT |%d|%s|%s|", + ss.ss_family == AF_INET ? 1 : 2, addr, port); -/* ARGSUSED */ -void -psabort(int signo) -{ + ret = ftp_command(fp, "%s", eprt); + free(eprt); + if (ret != P_OK) { + close(sock); + return -1; + } - alarmtimer(0); - abrtflag++; + return sock; } -void -pswitch(int flag) +int +ftp_epsv(FILE *fp) { - sig_t oldintr; - static struct comvars { - int connect; - char name[HOST_NAME_MAX+1]; - union sockaddr_union mctl; - union sockaddr_union 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[PATH_MAX]; - char mo[PATH_MAX]; - } proxstruct, tmpstruct; - struct comvars *ip, *op; - - abrtflag = 0; - oldintr = signal(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)signal(SIGINT, oldintr); - if (abrtflag) { - abrtflag = 0; - (*oldintr)(SIGINT); - } -} + struct sockaddr_storage ss; + char *buf = NULL, delim[4], *s, *e; + size_t n = 0; + socklen_t len; + int error, port, sock; -/* ARGSUSED */ -void -abortpt(int signo) -{ + if (io_debug) + fprintf(stderr, ">>> EPSV\n"); - alarmtimer(0); - putc('\n', ttyout); - (void)fflush(ttyout); - ptabflg++; - mflag = 0; - abrtflag = 0; - longjmp(ptabort, 1); -} + if (fprintf(fp, "EPSV\r\n") < 0) + errx(1, "%s: fprintf", __func__); -void -proxtrans(const char *cmd, const char *local, const char *remote) -{ - volatile sig_t oldintr; - int prox_type, nfnd; - volatile int secndflag; - char * volatile cmd2; - struct pollfd pfd[1]; - - 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 (setjmp(ptabort)) - goto abort; - oldintr = signal(SIGINT, abortpt); - if (command("%s %s", cmd, remote) != PRELIM) { - (void)signal(SIGINT, oldintr); - pswitch(1); - return; - } - sleep(2); - pswitch(1); - secndflag++; - if (command("%s %s", cmd2, local) != PRELIM) - goto abort; - ptflag++; - (void)getreply(0); - pswitch(0); - (void)getreply(0); - (void)signal(SIGINT, oldintr); - pswitch(1); - ptflag = 0; - fprintf(ttyout, "local: %s remote: %s\n", local, remote); - return; -abort: - (void)signal(SIGINT, SIG_IGN); - 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)signal(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)signal(SIGINT, oldintr); - return; - } + (void)fflush(fp); + if (ftp_getline(&buf, &n, 1, fp) != P_OK) { + free(buf); + return -1; } - if (cpend) - abort_remote(NULL); - pswitch(!proxy); - if (cpend) { - pfd[0].fd = fileno(cin); - pfd[0].events = POLLIN; - if ((nfnd = poll(pfd, 1, 10 * 1000)) <= 0) { - if (nfnd < 0) - warn("abort"); - if (ptabflg) - code = -1; - lostpeer(); - } - (void)getreply(0); - (void)getreply(0); - } - if (proxy) - pswitch(0); - pswitch(1); - if (ptabflg) - code = -1; - (void)signal(SIGINT, oldintr); -} -#ifndef SMALL -/* ARGSUSED */ -void -reset(int argc, char *argv[]) -{ - struct pollfd pfd[1]; - int nfnd = 1; - - pfd[0].fd = fileno(cin); - pfd[0].events = POLLIN; - while (nfnd > 0) { - if ((nfnd = poll(pfd, 1, 0)) < 0) { - warn("reset"); - code = -1; - lostpeer(); - } else if (nfnd) { - (void)getreply(0); - } + if ((s = strchr(buf, '(')) == NULL || (e = strchr(s, ')')) == NULL) { + warnx("Malformed EPSV reply"); + free(buf); + return -1; } -} -#endif -char * -gunique(const char *local) -{ - static char new[PATH_MAX]; - char *cp = strrchr(local, '/'); - int d, count=0; - char ext = '1'; - - if (cp) - *cp = '\0'; - d = access(cp == local ? "/" : cp ? local : ".", W_OK); - if (cp) - *cp = '/'; - if (d < 0) { - warn("local: %s", local); - return ((char *) 0); + s++; + *e = '\0'; + if (sscanf(s, "%c%c%c%d%c", &delim[0], &delim[1], &delim[2], + &port, &delim[3]) != 5) { + warnx("EPSV parse error"); + free(buf); + return -1; } - (void)strlcpy(new, local, sizeof new); - cp = new + strlen(new); - *cp++ = '.'; - while (!d) { - if (++count == 100) { - fputs("runique: can't find unique file name.\n", ttyout); - return ((char *) 0); - } - *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--; - } + free(buf); + + if (delim[0] != delim[1] || delim[0] != delim[2] + || delim[0] != delim[3]) { + warnx("EPSV parse error"); + return -1; } - return (new); -} -jmp_buf forceabort; + len = sizeof(ss); + memset(&ss, 0, len); + if (getpeername(fileno(fp), (struct sockaddr *)&ss, &len) == -1) { + warn("%s: getpeername", __func__); + return -1; + } -/* ARGSUSED */ -static void -abortforce(int signo) -{ - int save_errno = errno; + switch (ss.ss_family) { + case AF_INET: + ((struct sockaddr_in *)&ss)->sin_port = htons(port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)&ss)->sin6_port = htons(port); + break; + default: + errx(1, "%s: Invalid socket family", __func__); + } -#define MSG "Forced abort. The connection will be closed.\n" - (void) write(fileno(ttyout), MSG, strlen(MSG)); -#undef MSG + if ((sock = socket(ss.ss_family, SOCK_STREAM, 0)) == -1) { + warn("%s: socket", __func__); + return -1; + } - errno = save_errno; - longjmp(forceabort, 1); -} + for (error = connect(sock, (struct sockaddr *)&ss, len); + error != 0 && errno == EINTR; error = connect_wait(sock)) + continue; -void -abort_remote(FILE *din) -{ - char buf[BUFSIZ]; - nfds_t nfds; - int nfnd; - struct pollfd pfd[2]; - sig_t oldintr; - - if (cout == NULL || setjmp(forceabort)) { - if (cout) - fclose(cout); - warnx("Lost control connection for abort."); - if (ptabflg) - code = -1; - lostpeer(); - return; + if (error != 0) { + warn("%s: connect", __func__); + return -1; } - oldintr = signal(SIGINT, abortforce); - - /* - * send IAC in urgent mode instead of DM because 4.3BSD places oob mark - * after urgent byte rather than before as is protocol now - */ - snprintf(buf, sizeof buf, "%c%c%c", IAC, IP, IAC); - if (send(fileno(cout), buf, 3, MSG_OOB) != 3) - warn("abort"); - fprintf(cout, "%cABOR\r\n", DM); - (void)fflush(cout); - pfd[0].fd = fileno(cin); - pfd[0].events = POLLIN; - nfds = 1; - if (din) { - pfd[1].fd = fileno(din); - pfd[1].events = POLLIN; - nfds++; - } - if ((nfnd = poll(pfd, nfds, 10 * 1000)) <= 0) { - if (nfnd < 0) - warn("abort"); - if (ptabflg) - code = -1; - lostpeer(); - } - if (din && (pfd[1].revents & POLLIN)) { - while (read(fileno(din), buf, BUFSIZ) > 0) - /* LOOP */; - } - if (getreply(0) == ERROR && code == 552) { - /* 552 needed for nic style abort */ - (void)getreply(0); - } - (void)getreply(0); - (void)signal(SIGINT, oldintr); + return sock; } diff --git a/usr.bin/ftp/ftp.h b/usr.bin/ftp/ftp.h new file mode 100644 index 00000000000..0d5975ac6be --- /dev/null +++ b/usr.bin/ftp/ftp.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> + +#define S_HTTP 0 +#define S_FTP 1 +#define S_FILE 2 +#define S_HTTPS 3 + +#define TMPBUF_LEN 131072 +#define IMSG_OPEN 1 + +#define P_PRE 100 +#define P_OK 200 +#define P_INTER 300 +#define N_TRANS 400 +#define N_PERM 500 + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +struct imsg; +struct imsgbuf; + +struct url { + int scheme; + int ipliteral; + char *host; + char *port; + char *path; + char *basic_auth; + + char *fname; + int chunked; +}; + +/* cmd.c */ +void cmd(const char *, const char *, const char *); + +/* main.c */ +extern struct imsgbuf child_ibuf; +extern const char *useragent; +extern int activemode, family, io_debug, verbose, progressmeter; +extern volatile sig_atomic_t interrupted; +extern FILE *msgout; + +/* file.c */ +struct url *file_request(struct imsgbuf *, struct url *, off_t *, off_t *); +void file_save(struct url *, FILE *, off_t *); + +/* ftp.c */ +void ftp_connect(struct url *, struct url *, int); +struct url *ftp_get(struct url *, struct url *, off_t *, off_t *); +void ftp_quit(struct url *); +void ftp_save(struct url *, FILE *, off_t *); +int ftp_auth(FILE *, const char *, const char *); +int ftp_command(FILE *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))) + __attribute__((__nonnull__ (2))); +int ftp_eprt(FILE *); +int ftp_epsv(FILE *); +int ftp_getline(char **, size_t *, int, FILE *); +int ftp_size(FILE *, const char *, off_t *, char **); + +/* http.c */ +void http_connect(struct url *, struct url *, int); +struct url *http_get(struct url *, struct url *, off_t *, off_t *); +void http_close(struct url *); +void http_save(struct url *, FILE *, off_t *); +void https_init(char *); + +/* progressmeter.c */ +void start_progress_meter(const char *, const char *, off_t, off_t *); +void stop_progress_meter(void); + +/* url.c */ +int scheme_lookup(const char *); +void url_connect(struct url *, struct url *, int); +char *url_encode(const char *); +void url_free(struct url *); +struct url *url_parse(const char *); +struct url *url_request(struct url *, struct url *, off_t *, off_t *); +void url_save(struct url *, FILE *, off_t *); +void url_close(struct url *); +char *url_str(struct url *); +void log_request(const char *, struct url *, struct url *); + +/* util.c */ +int connect_wait(int); +void copy_file(FILE *, FILE *, off_t *); +int tcp_connect(const char *, const char *, int); +int fd_request(char *, int, off_t *); +int read_message(struct imsgbuf *, struct imsg *); +void send_message(struct imsgbuf *, int, uint32_t, void *, size_t, int); +void log_info(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) + __attribute__((__nonnull__ (1))); diff --git a/usr.bin/ftp/ftp_var.h b/usr.bin/ftp/ftp_var.h deleted file mode 100644 index de6b1b17369..00000000000 --- a/usr.bin/ftp/ftp_var.h +++ /dev/null @@ -1,231 +0,0 @@ -/* $OpenBSD: ftp_var.h,v 1.41 2018/02/10 06:25:16 jsing Exp $ */ -/* $NetBSD: ftp_var.h,v 1.18 1997/08/18 10:20:25 lukem Exp $ */ - -/* - * 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. - */ - -/* - * 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 - */ - -/* - * FTP global variables. - */ - -#include <sys/signal.h> -#include <limits.h> -#include <setjmp.h> - -#ifndef SMALL -#include <histedit.h> -#endif /* !SMALL */ - -#include <tls.h> - -#include "stringlist.h" -#include "extern.h" -#include "small.h" - -#define HASHBYTES 1024 -#define FTPBUFLEN PATH_MAX + 200 - -#define STALLTIME 5 /* # of seconds of no xfer before "stalling" */ - -#define FTP_PORT 21 /* default if ! getservbyname("ftp/tcp") */ -#define GATE_PORT 21 /* default if ! getservbyname("ftpgate/tcp") */ -#define HTTP_PORT 80 /* default if ! getservbyname("http/tcp") */ -#define HTTPS_PORT 443 /* default if ! getservbyname("https/tcp") */ -#define HTTP_USER_AGENT "User-Agent: OpenBSD ftp" /* User-Agent string sent to web server */ - -#define PAGER "more" /* default pager if $PAGER isn't set */ - -/* - * Options and other state info. - */ -int trace; /* trace packets exchanged */ -int hash; /* print # for each buffer transferred */ -int mark; /* number of bytes between hashes */ -int sendport; /* use PORT/LPRT cmd for each data connection */ -int verbose; /* print messages coming back from server */ -int connected; /* 1 = connected to server, -1 = logged in */ -int fromatty; /* input is from a terminal */ -int interactive; /* interactively prompt on m* cmds */ -#ifndef SMALL -int confirmrest; /* confirm rest of current m* cmd */ -int debug; /* debugging level */ -int bell; /* ring bell on cmd completion */ -char *altarg; /* argv[1] with no shell-like preprocessing */ -#endif /* !SMALL */ -int doglob; /* glob local file names */ -int autologin; /* establish user account on connection */ -int proxy; /* proxy server connection active */ -int proxflag; /* proxy connection exists */ -int gatemode; /* use gate-ftp */ -char *gateserver; /* server to use for gate-ftp */ -int sunique; /* store files on server with unique name */ -int runique; /* store local files with unique name */ -int mcase; /* map upper to lower case for mget names */ -int ntflag; /* use ntin ntout tables for name translation */ -int mapflag; /* use mapin mapout templates on file names */ -int preserve; /* preserve modification time on files */ -int progress; /* display transfer progress bar */ -int code; /* return/reply code for ftp command */ -int crflag; /* if 1, strip car. rets. on ascii gets */ -char pasv[BUFSIZ]; /* passive port for proxy data connection */ -int passivemode; /* passive mode enabled */ -int activefallback; /* fall back to active mode if passive fails */ -char ntin[17]; /* input translation table */ -char ntout[17]; /* output translation table */ -char mapin[PATH_MAX]; /* input map template */ -char mapout[PATH_MAX]; /* output map template */ -char typename[32]; /* name of file transfer type */ -int type; /* requested file transfer type */ -int curtype; /* current file transfer type */ -char structname[32]; /* name of file transfer structure */ -int stru; /* file transfer structure */ -char formname[32]; /* name of file transfer format */ -int form; /* file transfer format */ -char modename[32]; /* name of file transfer mode */ -int mode; /* file transfer mode */ -char bytename[32]; /* local byte size in ascii */ -int bytesize; /* local byte size in binary */ -int anonftp; /* automatic anonymous login */ -int dirchange; /* remote directory changed by cd command */ -unsigned int retry_connect; /* retry connect if failed */ -int ttywidth; /* width of tty */ -int epsv4; /* use EPSV/EPRT on IPv4 connections */ -int epsv4bad; /* EPSV doesn't work on the current server */ - -#ifndef SMALL -int editing; /* command line editing enabled */ -EditLine *el; /* editline(3) status structure */ -History *hist; /* editline(3) history structure */ -char *cursor_pos; /* cursor position we're looking for */ -size_t cursor_argc; /* location of cursor in margv */ -size_t cursor_argo; /* offset of cursor in margv[cursor_argc] */ -int resume; /* continue transfer */ -char *srcaddr; /* source address to bind to */ -#endif /* !SMALL */ - -char *cookiefile; /* cookie jar to use */ - -off_t bytes; /* current # of bytes read */ -off_t filesize; /* size of file being transferred */ -char *direction; /* direction transfer is occurring */ - -char *hostname; /* name of host connected to */ -int unix_server; /* server is unix, can use binary for ascii */ -int unix_proxy; /* proxy is unix, can use binary for ascii */ - -char *ftpport; /* port number to use for ftp connections */ -char *httpport; /* port number to use for http connections */ -#ifndef NOSSL -char *httpsport; /* port number to use for https connections */ -#endif /* !SMALL */ -char *httpuseragent; /* user agent for http(s) connections */ -char *gateport; /* port number to use for gateftp connections */ - -jmp_buf toplevel; /* non-local goto stuff for cmd scanner */ - -#ifndef SMALL -char line[FTPBUFLEN]; /* input line buffer */ -char *argbase; /* current storage point in arg buffer */ -char *stringbase; /* current scan point in line buffer */ -char argbuf[FTPBUFLEN]; /* argument storage buffer */ -StringList *marg_sl; /* stringlist containing margv */ -int margc; /* count of arguments on input line */ -int options; /* used during socket creation */ -#endif /* !SMALL */ - -#define margv (marg_sl->sl_str) /* args parsed from input line */ -int cpend; /* flag: if != 0, then pending server reply */ -int mflag; /* flag: if != 0, then active multi command */ - -/* - * Format of command table. - */ -struct cmd { - char *c_name; /* name of command */ - 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 SMALL - char *c_complete; /* context sensitive completion list */ -#endif /* !SMALL */ - void (*c_handler)(int, char **); /* function to call */ -}; - -struct macel { - char mac_name[9]; /* macro name */ - char *mac_start; /* start of macro in macbuf */ - char *mac_end; /* end of macro in macbuf */ -}; - -#ifndef SMALL -int macnum; /* number of defined macros */ -struct macel macros[16]; -char macbuf[4096]; -#endif /* !SMALL */ - -FILE *ttyout; /* stdout or stderr, depending on interactive */ - -extern struct cmd cmdtab[]; - -#ifndef NOSSL -extern struct tls_config *tls_config; -extern int tls_session_fd; -#endif /* !NOSSL */ diff --git a/usr.bin/ftp/http.c b/usr.bin/ftp/http.c new file mode 100644 index 00000000000..e5c7a3c7292 --- /dev/null +++ b/usr.bin/ftp/http.c @@ -0,0 +1,784 @@ +/* + * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org> + * Copyright (c) 2012 - 2015 Reyk Floeter <reyk@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <err.h> +#include <fcntl.h> +#include <libgen.h> +#include <limits.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#ifndef NOSSL +#include <tls.h> +#endif + +#include "ftp.h" +#include "xmalloc.h" + +#define MAX_REDIRECTS 10 + +#ifndef NOSSL +#define MINBUF 128 + +static struct tls_config *tls_config; +static struct tls *ctx; +static int tls_session_fd = -1; +static char * const tls_verify_opts[] = { +#define HTTP_TLS_CAFILE 0 + "cafile", +#define HTTP_TLS_CAPATH 1 + "capath", +#define HTTP_TLS_CIPHERS 2 + "ciphers", +#define HTTP_TLS_DONTVERIFY 3 + "dont", +#define HTTP_TLS_VERIFYDEPTH 4 + "depth", +#define HTTP_TLS_MUSTSTAPLE 5 + "muststaple", +#define HTTP_TLS_NOVERIFYTIME 6 + "noverifytime", +#define HTTP_TLS_SESSION 7 + "session", +#define HTTP_TLS_DOVERIFY 8 + "do", + NULL +}; +#endif /* NOSSL */ + +/* + * HTTP status codes based on IANA assignments (2014-06-11 version): + * https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml + * plus legacy (306) and non-standard (420). + */ +static struct http_status { + int code; + const char *name; +} http_status[] = { + { 100, "Continue" }, + { 101, "Switching Protocols" }, + { 102, "Processing" }, + /* 103-199 unassigned */ + { 200, "OK" }, + { 201, "Created" }, + { 202, "Accepted" }, + { 203, "Non-Authoritative Information" }, + { 204, "No Content" }, + { 205, "Reset Content" }, + { 206, "Partial Content" }, + { 207, "Multi-Status" }, + { 208, "Already Reported" }, + /* 209-225 unassigned */ + { 226, "IM Used" }, + /* 227-299 unassigned */ + { 300, "Multiple Choices" }, + { 301, "Moved Permanently" }, + { 302, "Found" }, + { 303, "See Other" }, + { 304, "Not Modified" }, + { 305, "Use Proxy" }, + { 306, "Switch Proxy" }, + { 307, "Temporary Redirect" }, + { 308, "Permanent Redirect" }, + /* 309-399 unassigned */ + { 400, "Bad Request" }, + { 401, "Unauthorized" }, + { 402, "Payment Required" }, + { 403, "Forbidden" }, + { 404, "Not Found" }, + { 405, "Method Not Allowed" }, + { 406, "Not Acceptable" }, + { 407, "Proxy Authentication Required" }, + { 408, "Request Timeout" }, + { 409, "Conflict" }, + { 410, "Gone" }, + { 411, "Length Required" }, + { 412, "Precondition Failed" }, + { 413, "Payload Too Large" }, + { 414, "URI Too Long" }, + { 415, "Unsupported Media Type" }, + { 416, "Range Not Satisfiable" }, + { 417, "Expectation Failed" }, + { 418, "I'm a teapot" }, + /* 419-421 unassigned */ + { 420, "Enhance Your Calm" }, + { 422, "Unprocessable Entity" }, + { 423, "Locked" }, + { 424, "Failed Dependency" }, + /* 425 unassigned */ + { 426, "Upgrade Required" }, + /* 427 unassigned */ + { 428, "Precondition Required" }, + { 429, "Too Many Requests" }, + /* 430 unassigned */ + { 431, "Request Header Fields Too Large" }, + /* 432-450 unassigned */ + { 451, "Unavailable For Legal Reasons" }, + /* 452-499 unassigned */ + { 500, "Internal Server Error" }, + { 501, "Not Implemented" }, + { 502, "Bad Gateway" }, + { 503, "Service Unavailable" }, + { 504, "Gateway Timeout" }, + { 505, "HTTP Version Not Supported" }, + { 506, "Variant Also Negotiates" }, + { 507, "Insufficient Storage" }, + { 508, "Loop Detected" }, + /* 509 unassigned */ + { 510, "Not Extended" }, + { 511, "Network Authentication Required" }, + /* 512-599 unassigned */ + { 0, NULL }, + }; + +struct http_headers { + char *location; + off_t content_length; + int chunked; +}; + +static void decode_chunk(int, uint, FILE *); +static char *header_lookup(const char *, const char *); +static const char *http_error(int); +static void http_headers_free(struct http_headers *); +static ssize_t http_getline(int, char **, size_t *); +static size_t http_read(int, char *, size_t); +static struct url *http_redirect(struct url *, char *); +static void http_save_chunks(struct url *, FILE *, off_t *); +static int http_status_cmp(const void *, const void *); +static int http_request(int, const char *, + struct http_headers **); +static char *relative_path_resolve(const char *, const char *); + +#ifndef NOSSL +static void tls_copy_file(struct url *, FILE *, off_t *); +static ssize_t tls_getline(char **, size_t *, struct tls *); +#endif + +static FILE *fp; + +void +http_connect(struct url *url, struct url *proxy, int timeout) +{ + const char *host, *port; + int sock; + + host = proxy ? proxy->host : url->host; + port = proxy ? proxy->port : url->port; + if ((sock = tcp_connect(host, port, timeout)) == -1) + exit(1); + + if ((fp = fdopen(sock, "r+")) == NULL) + err(1, "%s: fdopen", __func__); + +#ifndef NOSSL + struct http_headers *headers; + char *auth = NULL, *req; + int authlen = 0, code; + + if (url->scheme != S_HTTPS) + return; + + if (proxy) { + if (url->basic_auth) + authlen = xasprintf(&auth, + "Proxy-Authorization: Basic %s\r\n", + url->basic_auth); + + xasprintf(&req, + "CONNECT %s:%s HTTP/1.0\r\n" + "User-Agent: %s\r\n" + "%s" + "\r\n", + url->host, url->port, + useragent, + url->basic_auth ? auth : ""); + + freezero(auth, authlen); + if ((code = http_request(S_HTTP, req, &headers)) != 200) + errx(1, "%s: failed to CONNECT to %s:%s: %s", + __func__, url->host, url->port, http_error(code)); + + free(req); + http_headers_free(headers); + } + + if ((ctx = tls_client()) == NULL) + errx(1, "failed to create tls client"); + + if (tls_configure(ctx, tls_config) != 0) + errx(1, "%s: %s", __func__, tls_error(ctx)); + + if (tls_connect_socket(ctx, sock, url->host) != 0) + errx(1, "%s: %s", __func__, tls_error(ctx)); +#endif /* NOSSL */ +} + +struct url * +http_get(struct url *url, struct url *proxy, off_t *offset, off_t *sz) +{ + struct http_headers *headers; + char *auth = NULL, *path = NULL, *range = NULL, *req; + int authlen = 0, code, redirects = 0; + + redirected: + log_request("Requesting", url, proxy); + if (*offset) + xasprintf(&range, "Range: bytes=%lld-\r\n", *offset); + + if (url->basic_auth) + authlen = xasprintf(&auth, "Authorization: Basic %s\r\n", + url->basic_auth); + + if (proxy && url->scheme != S_HTTPS) + path = url_str(url); + else if (url->path) + path = url_encode(url->path); + + xasprintf(&req, + "GET %s HTTP/1.1\r\n" + "Host: %s\r\n" + "%s" + "%s" + "Connection: close\r\n" + "User-Agent: %s\r\n" + "\r\n", + path ? path : "/", + url->host, + *offset ? range : "", + url->basic_auth ? auth : "", + useragent); + code = http_request(url->scheme, req, &headers); + freezero(auth, authlen); + free(range); + free(path); + free(req); + switch (code) { + case 200: + if (*offset) { + warnx("Server does not support resume."); + *offset = 0; + } + break; + case 206: + break; + case 301: + case 302: + case 303: + case 307: + http_close(url); + if (++redirects > MAX_REDIRECTS) + errx(1, "Too many redirections requested"); + + if (headers->location == NULL) + errx(1, "%s: Location header missing", __func__); + + url = http_redirect(url, headers->location); + http_headers_free(headers); + log_request("Redirected to", url, proxy); + http_connect(url, proxy, 0); + goto redirected; + case 416: + errx(1, "File is already fully retrieved."); + break; + default: + errx(1, "Error retrieving file: %d %s", code, http_error(code)); + } + + *sz = headers->content_length + *offset; + url->chunked = headers->chunked; + http_headers_free(headers); + return url; +} + +void +http_save(struct url *url, FILE *dst_fp, off_t *offset) +{ + if (url->chunked) + http_save_chunks(url, dst_fp, offset); +#ifndef NOSSL + else if (url->scheme == S_HTTPS) + tls_copy_file(url, dst_fp, offset); +#endif + else + copy_file(dst_fp, fp, offset); +} + +static struct url * +http_redirect(struct url *old_url, char *location) +{ + struct url *new_url; + + /* absolute uri reference */ + if (strncasecmp(location, "http", 4) == 0 || + strncasecmp(location, "https", 5) == 0) { + if ((new_url = url_parse(location)) == NULL) + exit(1); + + goto done; + } + + /* relative uri reference */ + new_url = xcalloc(1, sizeof *new_url); + new_url->scheme = old_url->scheme; + new_url->host = xstrdup(old_url->host); + new_url->port = xstrdup(old_url->port); + + /* absolute-path reference */ + if (location[0] == '/') + new_url->path = xstrdup(location); + else + new_url->path = relative_path_resolve(old_url->path, location); + + done: + new_url->fname = xstrdup(old_url->fname); + url_free(old_url); + return new_url; +} + +static char * +relative_path_resolve(const char *base_path, const char *location) +{ + char *new_path, *p; + + /* trim fragment component from both uri */ + if ((p = strchr(location, '#')) != NULL) + *p = '\0'; + if (base_path && (p = strchr(base_path, '#')) != NULL) + *p = '\0'; + + if (base_path == NULL) + xasprintf(&new_path, "/%s", location); + else if (base_path[strlen(base_path) - 1] == '/') + xasprintf(&new_path, "%s%s", base_path, location); + else { + p = dirname(base_path); + xasprintf(&new_path, "%s/%s", + strcmp(p, ".") == 0 ? "" : p, location); + } + + return new_path; +} + +static void +http_save_chunks(struct url *url, FILE *dst_fp, off_t *offset) +{ + char *buf = NULL; + size_t n = 0; + uint chunk_sz; + + http_getline(url->scheme, &buf, &n); + if (sscanf(buf, "%x", &chunk_sz) != 1) + errx(1, "%s: Failed to get chunk size", __func__); + + while (chunk_sz > 0) { + decode_chunk(url->scheme, chunk_sz, dst_fp); + *offset += chunk_sz; + http_getline(url->scheme, &buf, &n); + if (sscanf(buf, "%x", &chunk_sz) != 1) + errx(1, "%s: Failed to get chunk size", __func__); + } + + free(buf); +} + +static void +decode_chunk(int scheme, uint sz, FILE *dst_fp) +{ + size_t bufsz; + size_t r; + char buf[BUFSIZ], crlf[2]; + + bufsz = sizeof(buf); + while (sz > 0) { + if (sz < bufsz) + bufsz = sz; + + r = http_read(scheme, buf, bufsz); + if (fwrite(buf, 1, r, dst_fp) != r) + errx(1, "%s: fwrite", __func__); + + sz -= r; + } + + /* CRLF terminating the chunk */ + if (http_read(scheme, crlf, sizeof(crlf)) != sizeof(crlf)) + errx(1, "%s: Failed to read terminal crlf", __func__); + + if (crlf[0] != '\r' || crlf[1] != '\n') + errx(1, "%s: Invalid chunked encoding", __func__); +} + +void +http_close(struct url *url) +{ +#ifndef NOSSL + ssize_t r; + + if (url->scheme == S_HTTPS) { + if (tls_session_fd != -1) + dprintf(STDERR_FILENO, "tls session resumed: %s\n", + tls_conn_session_resumed(ctx) ? "yes" : "no"); + + do { + r = tls_close(ctx); + } while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT); + tls_free(ctx); + } + +#endif + fclose(fp); +} + +static int +http_request(int scheme, const char *req, struct http_headers **hdrs) +{ + struct http_headers *headers; + const char *e; + char *buf = NULL, *p; + size_t n = 0; + ssize_t buflen; + uint code; +#ifndef NOSSL + ssize_t nw; +#endif + + if (io_debug) + fprintf(stderr, "<<< %s", req); + + switch (scheme) { +#ifndef NOSSL + case S_HTTPS: + do { + nw = tls_write(ctx, req, strlen(req)); + } while (nw == TLS_WANT_POLLIN || nw == TLS_WANT_POLLOUT); + if (nw == -1) + errx(1, "%s: tls_write: %s", __func__, tls_error(ctx)); + break; +#endif + case S_FTP: + case S_HTTP: + if (fprintf(fp, "%s", req) < 0) + errx(1, "%s: fprintf", __func__); + (void)fflush(fp); + break; + } + + http_getline(scheme, &buf, &n); + if (io_debug) + fprintf(stderr, ">>> %s", buf); + + if (sscanf(buf, "%*s %u %*s", &code) != 1) + errx(1, "%s: failed to extract status code", __func__); + + if (code < 100 || code > 511) + errx(1, "%s: invalid status code %d", __func__, code); + + headers = xcalloc(1, sizeof *headers); + for (;;) { + buflen = http_getline(scheme, &buf, &n); + buflen -= 1; + if (buflen > 0 && buf[buflen - 1] == '\r') + buflen -= 1; + buf[buflen] = '\0'; + + if (io_debug) + fprintf(stderr, ">>> %s\n", buf); + + if (buflen == 0) + break; /* end of headers */ + + if ((p = header_lookup(buf, "Content-Length:")) != NULL) { + headers->content_length = strtonum(p, 0, INT64_MAX, &e); + if (e) + err(1, "%s: Content-Length is %s: %lld", + __func__, e, headers->content_length); + } + + if ((p = header_lookup(buf, "Location:")) != NULL) + headers->location = xstrdup(p); + + if ((p = header_lookup(buf, "Transfer-Encoding:")) != NULL) + if (strcasestr(p, "chunked") != NULL) + headers->chunked = 1; + + } + + *hdrs = headers; + free(buf); + return code; +} + +static void +http_headers_free(struct http_headers *headers) +{ + if (headers == NULL) + return; + + free(headers->location); + free(headers); +} + +static char * +header_lookup(const char *buf, const char *key) +{ + char *p; + + if (strncasecmp(buf, key, strlen(key)) == 0) { + if ((p = strchr(buf, ' ')) == NULL) + errx(1, "Failed to parse %s", key); + return ++p; + } + + return NULL; +} + +static const char * +http_error(int code) +{ + struct http_status error, *res; + + /* Set up key */ + error.code = code; + + if ((res = bsearch(&error, http_status, + sizeof(http_status) / sizeof(http_status[0]) - 1, + sizeof(http_status[0]), http_status_cmp)) != NULL) + return (res->name); + + return (NULL); +} + +static int +http_status_cmp(const void *a, const void *b) +{ + const struct http_status *ea = a; + const struct http_status *eb = b; + + return (ea->code - eb->code); +} + + +static ssize_t +http_getline(int scheme, char **buf, size_t *n) +{ + ssize_t buflen; + + switch (scheme) { +#ifndef NOSSL + case S_HTTPS: + if ((buflen = tls_getline(buf, n, ctx)) == -1) + errx(1, "%s: tls_getline", __func__); + break; +#endif + case S_FTP: + case S_HTTP: + if ((buflen = getline(buf, n, fp)) == -1) + err(1, "%s: getline", __func__); + break; + default: + errx(1, "%s: invalid scheme", __func__); + } + + return buflen; +} + +static size_t +http_read(int scheme, char *buf, size_t size) +{ + size_t r; +#ifndef NOSSL + ssize_t rs; +#endif + + switch (scheme) { +#ifndef NOSSL + case S_HTTPS: + do { + rs = tls_read(ctx, buf, size); + } while (rs == TLS_WANT_POLLIN || rs == TLS_WANT_POLLOUT); + if (rs == -1) + errx(1, "%s: tls_read: %s", __func__, tls_error(ctx)); + r = rs; + break; +#endif + case S_HTTP: + if ((r = fread(buf, 1, size, fp)) < size) + if (!feof(fp)) + errx(1, "%s: fread", __func__); + break; + default: + errx(1, "%s: invalid scheme", __func__); + } + + return r; +} + +#ifndef NOSSL +void +https_init(char *tls_options) +{ + char *str; + int depth; + const char *ca_file, *errstr; + + if (tls_init() != 0) + errx(1, "tls_init failed"); + + if ((tls_config = tls_config_new()) == NULL) + errx(1, "tls_config_new failed"); + + if (tls_config_set_ciphers(tls_config, "legacy") != 0) + errx(1, "tls set ciphers failed: %s", + tls_config_error(tls_config)); + + ca_file = tls_default_ca_cert_file(); + while (tls_options && *tls_options) { + switch (getsubopt(&tls_options, tls_verify_opts, &str)) { + case HTTP_TLS_CAFILE: + if (str == NULL) + errx(1, "missing CA file"); + ca_file = str; + break; + case HTTP_TLS_CAPATH: + if (str == NULL) + errx(1, "missing ca path"); + if (tls_config_set_ca_path(tls_config, str) != 0) + errx(1, "tls ca path failed"); + break; + case HTTP_TLS_CIPHERS: + if (str == NULL) + errx(1, "missing cipher list"); + if (tls_config_set_ciphers(tls_config, str) != 0) + errx(1, "tls set ciphers failed"); + break; + case HTTP_TLS_DONTVERIFY: + tls_config_insecure_noverifycert(tls_config); + tls_config_insecure_noverifyname(tls_config); + break; + case HTTP_TLS_VERIFYDEPTH: + if (str == NULL) + errx(1, "missing depth"); + depth = strtonum(str, 0, INT_MAX, &errstr); + if (errstr) + errx(1, "Cert validation depth is %s", errstr); + tls_config_set_verify_depth(tls_config, depth); + break; + case HTTP_TLS_MUSTSTAPLE: + tls_config_ocsp_require_stapling(tls_config); + break; + case HTTP_TLS_NOVERIFYTIME: + tls_config_insecure_noverifytime(tls_config); + break; + case HTTP_TLS_SESSION: + if (str == NULL) + errx(1, "missing session file"); + tls_session_fd = open(str, O_RDWR|O_CREAT, 0600); + if (tls_session_fd == -1) + err(1, "failed to open or create session file " + "'%s'", str); + if (tls_config_set_session_fd(tls_config, + tls_session_fd) == -1) + errx(1, "failed to set session: %s", + tls_config_error(tls_config)); + break; + case HTTP_TLS_DOVERIFY: + /* For compatibility, we do verify by default */ + break; + default: + errx(1, "Unknown -S suboption `%s'", + suboptarg ? suboptarg : ""); + } + } + + if (tls_config_set_ca_file(tls_config, ca_file) == -1) + errx(1, "tls_config_set_ca_file failed"); +} + +static ssize_t +tls_getline(char **buf, size_t *buflen, struct tls *tls) +{ + char *newb; + size_t newlen, off; + int ret; + unsigned char c; + + if (buf == NULL || buflen == NULL) + return -1; + + /* If buf is NULL, we have to assume a size of zero */ + if (*buf == NULL) + *buflen = 0; + + off = 0; + do { + do { + ret = tls_read(tls, &c, 1); + } while (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT); + if (ret == -1) + return -1; + + /* Ensure we can handle it */ + if (off + 2 > SSIZE_MAX) + return -1; + + newlen = off + 2; /* reserve space for NUL terminator */ + if (newlen > *buflen) { + newlen = newlen < MINBUF ? MINBUF : *buflen * 2; + newb = recallocarray(*buf, *buflen, newlen, 1); + if (newb == NULL) + return -1; + + *buf = newb; + *buflen = newlen; + } + + *(*buf + off) = c; + off += 1; + } while (c != '\n'); + + *(*buf + off) = '\0'; + return off; +} + +static void +tls_copy_file(struct url *url, FILE *dst_fp, off_t *offset) +{ + char *tmp_buf; + ssize_t r; + + tmp_buf = xmalloc(TMPBUF_LEN); + for (;;) { + do { + r = tls_read(ctx, tmp_buf, TMPBUF_LEN); + } while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT); + + if (r == -1) + errx(1, "%s: tls_read: %s", __func__, tls_error(ctx)); + else if (r == 0) + break; + + *offset += r; + if (fwrite(tmp_buf, 1, r, dst_fp) != (size_t)r) + err(1, "%s: fwrite", __func__); + } + free(tmp_buf); +} +#endif /* NOSSL */ diff --git a/usr.bin/ftp/list.c b/usr.bin/ftp/list.c deleted file mode 100644 index f9e4ff518bb..00000000000 --- a/usr.bin/ftp/list.c +++ /dev/null @@ -1,86 +0,0 @@ -/* $OpenBSD: list.c,v 1.7 2013/11/13 20:41:14 deraadt Exp $ */ - -/* - * Copyright (c) 2008 Martynas Venckus <martynas@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef SMALL - -#include <string.h> - -void parse_list(char **, char *); - -static void -parse_unix(char **line, char *type) -{ - char *tok; - int field = 0; - - while ((tok = strsep(line, " \t")) != NULL) { - if (*tok == '\0') - continue; - - if (field == 0) - *type = *tok; - - if (field == 7) { - if (line == NULL || *line == NULL) - break; - while (**line == ' ' || **line == '\t') - (*line)++; - break; - } - - field++; - } -} - -static void -parse_windows(char **line, char *type) -{ - char *tok; - int field = 0; - - *type = '-'; - while ((tok = strsep(line, " \t")) != NULL) { - if (*tok == '\0') - continue; - - if (field == 2 && strcmp(tok, "<DIR>") == 0) - *type = 'd'; - - if (field == 2) { - if (line == NULL || *line == NULL) - break; - while (**line == ' ' || **line == '\t') - (*line)++; - break; - } - - field++; - } -} - -void -parse_list(char **line, char *type) -{ - if (**line >= '0' && **line <= '9') - parse_windows(line, type); - else - parse_unix(line, type); -} - -#endif /* !SMALL */ - diff --git a/usr.bin/ftp/main.c b/usr.bin/ftp/main.c index eae8ec8b3c2..9c432c376ec 100644 --- a/usr.bin/ftp/main.c +++ b/usr.bin/ftp/main.c @@ -1,449 +1,149 @@ -/* $OpenBSD: main.c,v 1.120 2018/02/10 06:25:16 jsing Exp $ */ -/* $NetBSD: main.c,v 1.24 1997/08/18 10:20:26 lukem Exp $ */ - /* - * Copyright (C) 1997 and 1998 WIDE Project. - * All rights reserved. + * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org> * - * 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. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * 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. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* - * 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 User Program -- Command Interface. - */ +#include <sys/cdefs.h> #include <sys/types.h> +#include <sys/queue.h> +#include <sys/stat.h> #include <sys/socket.h> +#include <sys/wait.h> -#include <ctype.h> #include <err.h> +#include <errno.h> #include <fcntl.h> -#include <netdb.h> -#include <pwd.h> +#include <imsg.h> +#include <libgen.h> +#include <signal.h> #include <stdio.h> -#include <errno.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <tls.h> - -#include "cmds.h" -#include "ftp_var.h" - -int connect_timeout; - -#ifndef NOSSL -char * const ssl_verify_opts[] = { -#define SSL_CAFILE 0 - "cafile", -#define SSL_CAPATH 1 - "capath", -#define SSL_CIPHERS 2 - "ciphers", -#define SSL_DONTVERIFY 3 - "dont", -#define SSL_DOVERIFY 4 - "do", -#define SSL_VERIFYDEPTH 5 - "depth", -#define SSL_MUSTSTAPLE 6 - "muststaple", -#define SSL_NOVERIFYTIME 7 - "noverifytime", -#define SSL_SESSION 8 - "session", - NULL -}; - -struct tls_config *tls_config; -int tls_session_fd = -1; - -static void -process_ssl_options(char *cp) -{ - const char *errstr; - long long depth; - char *str; - - while (*cp) { - switch (getsubopt(&cp, ssl_verify_opts, &str)) { - case SSL_CAFILE: - if (str == NULL) - errx(1, "missing CA file"); - if (tls_config_set_ca_file(tls_config, str) != 0) - errx(1, "tls ca file failed: %s", - tls_config_error(tls_config)); - break; - case SSL_CAPATH: - if (str == NULL) - errx(1, "missing CA directory path"); - if (tls_config_set_ca_path(tls_config, str) != 0) - errx(1, "tls ca path failed: %s", - tls_config_error(tls_config)); - break; - case SSL_CIPHERS: - if (str == NULL) - errx(1, "missing cipher list"); - if (tls_config_set_ciphers(tls_config, str) != 0) - errx(1, "tls ciphers failed: %s", - tls_config_error(tls_config)); - break; - case SSL_DONTVERIFY: - tls_config_insecure_noverifycert(tls_config); - tls_config_insecure_noverifyname(tls_config); - break; - case SSL_DOVERIFY: - tls_config_verify(tls_config); - break; - case SSL_VERIFYDEPTH: - if (str == NULL) - errx(1, "missing depth"); - depth = strtonum(str, 0, INT_MAX, &errstr); - if (errstr) - errx(1, "certificate validation depth is %s", - errstr); - tls_config_set_verify_depth(tls_config, (int)depth); - break; - case SSL_MUSTSTAPLE: - tls_config_ocsp_require_stapling(tls_config); - break; - case SSL_NOVERIFYTIME: - tls_config_insecure_noverifytime(tls_config); - break; - case SSL_SESSION: - if (str == NULL) - errx(1, "missing session file"); - if ((tls_session_fd = open(str, O_RDWR|O_CREAT, - 0600)) == -1) - err(1, "failed to open or create session file " - "'%s'", str); - if (tls_config_set_session_fd(tls_config, - tls_session_fd) == -1) - errx(1, "failed to set session: %s", - tls_config_error(tls_config)); - break; - default: - errx(1, "unknown -S suboption `%s'", - suboptarg ? suboptarg : ""); - /* NOTREACHED */ - } - } -} -#endif /* !NOSSL */ - -int family = PF_UNSPEC; -int pipeout; +#include "ftp.h" +#include "xmalloc.h" + +static int auto_fetch(int, char **, int, char **); +static void child(int, int, char **); +static int parent(int, pid_t); +static struct url *proxy_parse(const char *); +static struct url *get_proxy(int); +static void re_exec(int, int, char **); +static void validate_output_fname(struct url *, const char *); +static __dead void usage(void); + +struct imsgbuf child_ibuf; +const char *useragent = "OpenBSD ftp"; +int activemode, family = AF_UNSPEC, io_debug; +int progressmeter, verbose = 1; +volatile sig_atomic_t interrupted = 0; +FILE *msgout = stdout; + +static const char *title; +static char *tls_options, *oarg; +static int connect_timeout, resume; int -main(volatile int argc, char *argv[]) +main(int argc, char **argv) { - int ch, rval; -#ifndef SMALL - int top; -#endif - struct passwd *pw = NULL; - char *cp, homedir[PATH_MAX]; - char *outfile = NULL; - const char *errstr; - int dumb_terminal = 0; - - ftpport = "ftp"; - httpport = "http"; -#ifndef NOSSL - httpsport = "https"; -#endif /* !NOSSL */ - gateport = getenv("FTPSERVERPORT"); - if (gateport == NULL || *gateport == '\0') - gateport = "ftpgate"; - doglob = 1; - interactive = 1; - autologin = 1; - passivemode = 1; - activefallback = 1; - preserve = 1; - verbose = 0; - progress = 0; - gatemode = 0; -#ifndef NOSSL - cookiefile = NULL; -#endif /* NOSSL */ -#ifndef SMALL - editing = 0; - el = NULL; - hist = NULL; - resume = 0; - srcaddr = NULL; - marg_sl = sl_init(); -#endif /* !SMALL */ - mark = HASHBYTES; - epsv4 = 1; - epsv4bad = 0; - - /* Set default operation mode based on FTPMODE environment variable */ - if ((cp = getenv("FTPMODE")) != NULL && *cp != '\0') { - if (strcmp(cp, "passive") == 0) { - passivemode = 1; - activefallback = 0; - } else if (strcmp(cp, "active") == 0) { - passivemode = 0; - activefallback = 0; - } else if (strcmp(cp, "gate") == 0) { - gatemode = 1; - } else if (strcmp(cp, "auto") == 0) { - passivemode = 1; - activefallback = 1; - } else - warnx("unknown FTPMODE: %s. Using defaults", cp); - } - - if (strcmp(__progname, "gate-ftp") == 0) - gatemode = 1; - gateserver = getenv("FTPSERVER"); - if (gateserver == NULL) - gateserver = ""; - if (gatemode) { - if (*gateserver == '\0') { - warnx( -"Neither $FTPSERVER nor $GATE_SERVER is defined; disabling gate-ftp"); - gatemode = 0; - } - } - - cp = getenv("TERM"); - dumb_terminal = (cp == NULL || *cp == '\0' || !strcmp(cp, "dumb") || - !strcmp(cp, "emacs") || !strcmp(cp, "su")); - fromatty = isatty(fileno(stdin)); - if (fromatty) { - verbose = 1; /* verbose if from a tty */ -#ifndef SMALL - if (!dumb_terminal) - editing = 1; /* editing mode on if tty is usable */ -#endif /* !SMALL */ - } - - ttyout = stdout; - if (isatty(fileno(ttyout)) && !dumb_terminal && foregroundproc()) - progress = 1; /* progress bar on if tty is usable */ - -#ifndef NOSSL - cookiefile = getenv("http_cookies"); - if (tls_init() != 0) - errx(1, "tls init failed"); - if (tls_config == NULL) { - tls_config = tls_config_new(); - if (tls_config == NULL) - errx(1, "tls config failed"); - if (tls_config_set_protocols(tls_config, - TLS_PROTOCOLS_ALL) != 0) - errx(1, "tls set protocols failed: %s", - tls_config_error(tls_config)); - if (tls_config_set_ciphers(tls_config, "legacy") != 0) - errx(1, "tls set ciphers failed: %s", - tls_config_error(tls_config)); - } -#endif /* !NOSSL */ - - httpuseragent = NULL; - + const char *e; + char **save_argv, *term; + int ch, csock, dumb_terminal, rexec, save_argc; + + if (isatty(fileno(stdin)) != 1) + verbose = 0; + + io_debug = getenv("IO_DEBUG") != NULL; + term = getenv("TERM"); + dumb_terminal = (term == NULL || *term == '\0' || + !strcmp(term, "dumb") || !strcmp(term, "emacs") || + !strcmp(term, "su")); + if (isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) && !dumb_terminal) + progressmeter = 1; + + csock = rexec = 0; + save_argc = argc; + save_argv = argv; while ((ch = getopt(argc, argv, - "46AaCc:dD:Eegik:Mmno:pP:r:S:s:tU:vVw:")) != -1) { + "46AaCc:dD:Eegik:Mmno:pP:r:S:s:tU:vVw:xz:")) != -1) { switch (ch) { case '4': - family = PF_INET; + family = AF_INET; break; case '6': - family = PF_INET6; + family = AF_INET6; break; case 'A': - activefallback = 0; - passivemode = 0; + activemode = 1; break; - - case 'a': - anonftp = 1; - break; - case 'C': -#ifndef SMALL resume = 1; -#endif /* !SMALL */ break; - - case 'c': -#ifndef SMALL - cookiefile = optarg; -#endif /* !SMALL */ - break; - case 'D': - action = optarg; - break; - case 'd': -#ifndef SMALL - options |= SO_DEBUG; - debug++; -#endif /* !SMALL */ - break; - - case 'E': - epsv4 = 0; - break; - - case 'e': -#ifndef SMALL - editing = 0; -#endif /* !SMALL */ + title = optarg; break; - - case 'g': - doglob = 0; - break; - - case 'i': - interactive = 0; - break; - - case 'k': - keep_alive_timeout = strtonum(optarg, 0, INT_MAX, - &errstr); - if (errstr != NULL) { - warnx("keep alive amount is %s: %s", errstr, - optarg); - usage(); - } + case 'o': + oarg = optarg; + if (!strlen(oarg)) + oarg = NULL; break; case 'M': - progress = 0; + progressmeter = 0; break; case 'm': - progress = -1; + progressmeter = 1; break; - - case 'n': - autologin = 0; + case 'S': + tls_options = optarg; break; - - case 'o': - outfile = optarg; - if (*outfile == '\0') { - pipeout = 0; - outfile = NULL; - ttyout = stdout; - } else { - pipeout = strcmp(outfile, "-") == 0; - ttyout = pipeout ? stderr : stdout; - } + case 'U': + useragent = optarg; break; - - case 'p': - passivemode = 1; - activefallback = 0; + case 'V': + verbose = 0; break; - - case 'P': - ftpport = optarg; + case 'w': + connect_timeout = strtonum(optarg, 0, 200, &e); + if (e) + errx(1, "-w: %s", e); break; - - case 'r': - retry_connect = strtonum(optarg, 0, INT_MAX, &errstr); - if (errstr != NULL) { - warnx("retry amount is %s: %s", errstr, - optarg); - usage(); - } + /* options for internal use only */ + case 'x': + rexec = 1; break; - - case 'S': -#ifndef NOSSL - process_ssl_options(optarg); -#endif /* !NOSSL */ + case 'z': + csock = strtonum(optarg, 3, getdtablesize() - 1, &e); + if (e) + errx(1, "-z: %s", e); break; - + /* Ignoring all remaining options */ + case 'a': + case 'c': + case 'd': + case 'E': + case 'e': + case 'g': + case 'i': + case 'k': + case 'n': + case 'P': + case 'p': + case 'r': case 's': -#ifndef SMALL - srcaddr = optarg; -#endif /* !SMALL */ - break; - case 't': - trace = 1; - break; - -#ifndef SMALL - case 'U': - free (httpuseragent); - if (strcspn(optarg, "\r\n") != strlen(optarg)) - errx(1, "Invalid User-Agent: %s.", optarg); - if (asprintf(&httpuseragent, "User-Agent: %s", - optarg) == -1) - errx(1, "Can't allocate memory for HTTP(S) " - "User-Agent"); - break; -#endif /* !SMALL */ - case 'v': - verbose = 1; - break; - - case 'V': - verbose = 0; - break; - - case 'w': - connect_timeout = strtonum(optarg, 0, 200, &errstr); - if (errstr) - errx(1, "-w: %s", errstr); break; default: usage(); @@ -452,517 +152,265 @@ main(volatile int argc, char *argv[]) argc -= optind; argv += optind; -#ifndef NOSSL - cookie_load(); -#endif /* !NOSSL */ - if (httpuseragent == NULL) - httpuseragent = HTTP_USER_AGENT; - - cpend = 0; /* no pending replies */ - proxy = 0; /* proxy not active */ - crflag = 1; /* strip c.r. on ascii gets */ - sendport = -1; /* not using ports */ - /* - * Set up the home directory in case we're globbing. - */ - cp = getlogin(); - if (cp != NULL) { - pw = getpwnam(cp); - } - if (pw == NULL) - pw = getpwuid(getuid()); - if (pw != NULL) { - (void)strlcpy(homedir, pw->pw_dir, sizeof homedir); - home = homedir; - } - - setttywidth(0); - (void)signal(SIGWINCH, setttywidth); + if (rexec) + child(csock, argc, argv); - if (argc > 0) { - if (isurl(argv[0])) { - if (pipeout) { #ifndef SMALL - if (pledge("stdio rpath dns tty inet proc exec fattr", - NULL) == -1) - err(1, "pledge"); -#else - if (pledge("stdio rpath dns tty inet fattr", - NULL) == -1) - err(1, "pledge"); -#endif - } else { -#ifndef SMALL - if (pledge("stdio rpath wpath cpath dns tty inet proc exec fattr", - NULL) == -1) - err(1, "pledge"); + struct url *url; + + switch (argc) { + case 0: + cmd(NULL, NULL, NULL); + return 0; + case 1: + case 2: + switch (scheme_lookup(argv[0])) { + case -1: + cmd(argv[0], argv[1], NULL); + return 0; + case S_FTP: + if ((url = url_parse(argv[0])) == NULL) + exit(1); + + if (url->path && + url->path[strlen(url->path) - 1] != '/') + break; /* auto fetch */ + + cmd(url->host, url->port, url->path); + return 0; + } + break; + } #else - if (pledge("stdio rpath wpath cpath dns tty inet fattr", - NULL) == -1) - err(1, "pledge"); + if (argc == 0) + usage(); #endif - } - rval = auto_fetch(argc, argv, outfile); - if (rval >= 0) /* -1 == connected and cd-ed */ - exit(rval); - } else { -#ifndef SMALL - char *xargv[5]; - - if (setjmp(toplevel)) - exit(0); - (void)signal(SIGINT, (sig_t)intr); - (void)signal(SIGPIPE, (sig_t)lostpeer); - xargv[0] = __progname; - xargv[1] = argv[0]; - xargv[2] = argv[1]; - xargv[3] = argv[2]; - xargv[4] = NULL; - do { - setpeer(argc+1, xargv); - if (!retry_connect) - break; - if (!connected) { - macnum = 0; - fputs("Retrying...\n", ttyout); - sleep(retry_connect); - } - } while (!connected); - retry_connect = 0; /* connected, stop hiding msgs */ -#endif /* !SMALL */ - } - } -#ifndef SMALL - controlediting(); - top = setjmp(toplevel) == 0; - if (top) { - (void)signal(SIGINT, (sig_t)intr); - (void)signal(SIGPIPE, (sig_t)lostpeer); - } - for (;;) { - cmdscanner(top); - top = 1; - } -#else /* !SMALL */ - usage(); -#endif /* !SMALL */ + return auto_fetch(argc, argv, save_argc, save_argv); } -void -intr(void) +static int +auto_fetch(int argc, char **argv, int sargc, char **sargv) { - int save_errno = errno; - - write(fileno(ttyout), "\n\r", 2); - alarmtimer(0); + pid_t pid; + int sp[2]; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) != 0) + err(1, "socketpair"); + + switch (pid = fork()) { + case -1: + err(1, "fork"); + case 0: + close(sp[0]); + re_exec(sp[1], sargc, sargv); + } - errno = save_errno; - longjmp(toplevel, 1); + close(sp[1]); + return parent(sp[0], pid); } -void -lostpeer(void) +static void +re_exec(int sock, int argc, char **argv) { - int save_errno = errno; - - alarmtimer(0); - if (connected) { - if (cout != NULL) { - (void)shutdown(fileno(cout), SHUT_RDWR); - (void)fclose(cout); - cout = NULL; - } - if (data >= 0) { - (void)shutdown(data, SHUT_RDWR); - (void)close(data); - data = -1; - } - connected = 0; - } - pswitch(1); - if (connected) { - if (cout != NULL) { - (void)shutdown(fileno(cout), SHUT_RDWR); - (void)fclose(cout); - cout = NULL; - } - connected = 0; - } - proxflag = 0; - pswitch(0); - errno = save_errno; + char **nargv, *sock_str; + int i, j, nargc; + + nargc = argc + 4; + nargv = xcalloc(nargc, sizeof(*nargv)); + xasprintf(&sock_str, "%d", sock); + i = 0; + nargv[i++] = argv[0]; + nargv[i++] = "-z"; + nargv[i++] = sock_str; + nargv[i++] = "-x"; + for (j = 1; j < argc; j++) + nargv[i++] = argv[j]; + + execvp(nargv[0], nargv); + err(1, "execvp"); } -#ifndef SMALL -/* - * Generate a prompt - */ -char * -prompt(void) +static int +parent(int sock, pid_t child_pid) { - return ("ftp> "); -} + struct imsgbuf ibuf; + struct imsg imsg; + struct stat sb; + off_t offset; + int fd, save_errno, sig, status; -/* - * Command parser. - */ -void -cmdscanner(int top) -{ - struct cmd *c; - int num; - HistEvent hev; + setproctitle("%s", "parent"); + if (pledge("stdio cpath rpath wpath sendfd", NULL) == -1) + err(1, "pledge"); - if (!top && !editing) - (void)putc('\n', ttyout); + imsg_init(&ibuf, sock); for (;;) { - if (!editing) { - if (fromatty) { - fputs(prompt(), ttyout); - (void)fflush(ttyout); - } - if (fgets(line, sizeof(line), stdin) == NULL) - quit(0, 0); - num = strlen(line); - if (num == 0) - break; - if (line[--num] == '\n') { - if (num == 0) - break; - line[num] = '\0'; - } else if (num == sizeof(line) - 2) { - fputs("sorry, input line too long.\n", ttyout); - while ((num = getchar()) != '\n' && num != EOF) - /* void */; - break; - } /* else it was a line without a newline */ - } else { - const char *buf; - cursor_pos = NULL; - - if ((buf = el_gets(el, &num)) == NULL || num == 0) { - putc('\n', ttyout); - fflush(ttyout); - quit(0, 0); - } - if (buf[--num] == '\n') { - if (num == 0) - break; - } - if (num >= sizeof(line)) { - fputs("sorry, input line too long.\n", ttyout); - break; - } - memcpy(line, buf, (size_t)num); - line[num] = '\0'; - history(hist, &hev, H_ENTER, buf); + if (read_message(&ibuf, &imsg) == 0) + break; + + if (imsg.hdr.type != IMSG_OPEN) + errx(1, "%s: IMSG_OPEN expected", __func__); + + offset = 0; + fd = open(imsg.data, imsg.hdr.peerid, 0666); + save_errno = errno; + if (fd != -1 && fstat(fd, &sb) == 0) { + if (sb.st_mode & S_IFDIR) { + close(fd); + fd = -1; + save_errno = EISDIR; + } else + offset = sb.st_size; } - makeargv(); - if (margc == 0) - continue; - c = getcmd(margv[0]); - if (c == (struct cmd *)-1) { - fputs("?Ambiguous command.\n", ttyout); - continue; - } - if (c == 0) { - /* - * Give editline(3) a shot at unknown commands. - * XXX - bogus commands with a colon in - * them will not elicit an error. - */ - if (editing && - el_parse(el, margc, (const char **)margv) != 0) - fputs("?Invalid command.\n", ttyout); - continue; - } - if (c->c_conn && !connected) { - fputs("Not connected.\n", ttyout); - continue; - } - confirmrest = 0; - (*c->c_handler)(margc, margv); - if (bell && c->c_bell) - (void)putc('\007', ttyout); - if (c->c_handler != help) - break; - } - (void)signal(SIGINT, (sig_t)intr); - (void)signal(SIGPIPE, (sig_t)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++; - } + send_message(&ibuf, IMSG_OPEN, save_errno, + &offset, sizeof offset, fd); + imsg_free(&imsg); } - if (nmatches > 1) - return ((struct cmd *)-1); - return (found); -} -/* - * Slice a string up into argc/argv. - */ + close(sock); + done: + if (waitpid(child_pid, &status, 0) == -1 && errno != ECHILD) + err(1, "wait"); -int slrflag; + sig = WTERMSIG(status); + if (WIFSIGNALED(status) && sig != SIGPIPE) + errx(1, "child terminated: signal %d", sig); -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(); - sl_add(marg_sl, argp); - if (argp == NULL) - break; - } - if (cursor_pos == line) { - cursor_argc = 0; - cursor_argo = 0; - } else if (cursor_pos != NULL) { - cursor_argc = margc; - cursor_argo = strlen(margv[margc-1]); - } + return WEXITSTATUS(status); } -#define INC_CHKCURSOR(x) { (x)++ ; \ - if (x == cursor_pos) { \ - cursor_argc = margc; \ - cursor_argo = ap-argbase; \ - cursor_pos = NULL; \ - } } - -/* - * Parse string into argbuf; - * implemented with FSM to - * handle quoting and strings - */ -char * -slurpstring(void) +static void +child(int sock, int argc, char **argv) { - 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 == '!') ? "!" : "$"); - /* NOTREACHED */ - case 1: - slrflag++; - altarg = stringbase; - break; - default: - break; - } - } - -S0: - switch (*sb) { - - case '\0': - goto OUT; - - case ' ': - case '\t': - INC_CHKCURSOR(sb); - goto S0; + struct url *url; + FILE *dst_fp; + char *p; + off_t offset, sz; + int fd, i, tostdout; - default: - switch (slrflag) { - case 0: - slrflag++; - break; - case 1: - slrflag++; - altarg = sb; - break; - default: - break; + setproctitle("%s", "child"); +#ifndef NOSSL + https_init(tls_options); +#endif + if (pledge("stdio inet dns recvfd tty", NULL) == -1) + err(1, "pledge"); + if (!progressmeter && pledge("stdio inet dns recvfd", NULL) == -1) + err(1, "pledge"); + + imsg_init(&child_ibuf, sock); + tostdout = oarg && (strcmp(oarg, "-") == 0); + if (resume && tostdout) + errx(1, "can't append to stdout"); + + for (i = 0; i < argc; i++) { + fd = -1; + offset = sz = 0; + + if ((url = url_parse(argv[i])) == NULL) + exit(1); + + validate_output_fname(url, argv[i]); + url_connect(url, get_proxy(url->scheme), connect_timeout); + if (resume) + fd = fd_request(url->fname, O_WRONLY|O_APPEND, &offset); + + url = url_request(url, get_proxy(url->scheme), &offset, &sz); + if (resume && offset == 0 && fd != -1) + if (ftruncate(fd, 0) != 0) + err(1, "ftruncate"); + + if (fd == -1 && !tostdout && + (fd = fd_request(url->fname, + O_CREAT|O_TRUNC|O_WRONLY, NULL)) == -1) + err(1, "Can't open file %s", url->fname); + + if (tostdout) { + dst_fp = stdout; + msgout = stderr; + } else if ((dst_fp = fdopen(fd, "w")) == NULL) + err(1, "%s: fdopen", __func__); + + if (progressmeter) { + p = basename(url->path); + start_progress_meter(p, title, sz, &offset); } - goto S1; - } - -S1: - switch (*sb) { - - case ' ': - case '\t': - case '\0': - goto OUT; /* end of token */ - case '\\': - INC_CHKCURSOR(sb); - goto S2; /* slurp next character */ + url_save(url, dst_fp, &offset); + if (progressmeter) + stop_progress_meter(); - case '"': - INC_CHKCURSOR(sb); - goto S3; /* slurp quoted string */ + if (!tostdout) + fclose(dst_fp); - default: - *ap = *sb; /* add character to token */ - ap++; - INC_CHKCURSOR(sb); - got_one = 1; - goto S1; + url_close(url); + url_free(url); } -S2: - switch (*sb) { + exit(0); +} - case '\0': - goto OUT; +static struct url * +get_proxy(int scheme) +{ + static struct url *ftp_proxy, *http_proxy; + switch (scheme) { + case S_HTTP: + case S_HTTPS: + if (http_proxy) + return http_proxy; + else + return (http_proxy = proxy_parse("http_proxy")); + case S_FTP: + if (ftp_proxy) + return ftp_proxy; + else + return (ftp_proxy = proxy_parse("ftp_proxy")); default: - *ap = *sb; - ap++; - INC_CHKCURSOR(sb); - got_one = 1; - goto S1; + return NULL; } +} -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; - } +static void +validate_output_fname(struct url *url, const char *name) +{ + url->fname = xstrdup(oarg ? oarg : basename(url->path)); + if (strcmp(url->fname, "/") == 0) + errx(1, "No filename after host (use -o): %s", name); -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 = (char *) 0; - break; - default: - break; - } - return (NULL); + if (strcmp(url->fname, ".") == 0) + errx(1, "No '/' after host (use -o): %s", name); } -/* - * Help command. - * Call each command handler with argc == 0 and argv[0] == name. - */ -void -help(int argc, char *argv[]) +static struct url * +proxy_parse(const char *name) { - struct cmd *c; - - if (argc == 1) { - StringList *buf; - - buf = sl_init(); - fprintf(ttyout, "%sommands may be abbreviated. Commands are:\n\n", - proxy ? "Proxy c" : "C"); - for (c = cmdtab; c < &cmdtab[NCMDS]; c++) - if (c->c_name && (!proxy || c->c_proxy)) - sl_add(buf, c->c_name); - list_vertical(buf); - sl_free(buf, 0); - return; - } + struct url *proxy; + char *str; -#define HELPINDENT ((int) sizeof("disconnect")) + if ((str = getenv(name)) == NULL) + return NULL; - while (--argc > 0) { - char *arg; + if (strlen(str) == 0) + return NULL; - arg = *++argv; - c = getcmd(arg); - if (c == (struct cmd *)-1) - fprintf(ttyout, "?Ambiguous help command %s\n", arg); - else if (c == NULL) - fprintf(ttyout, "?Invalid help command %s\n", arg); - else - fprintf(ttyout, "%-*s\t%s\n", HELPINDENT, - c->c_name, c->c_help); - } + if ((proxy = url_parse(str)) == NULL) + exit(1); + + if (proxy->scheme != S_HTTP) + errx(1, "Malformed proxy URL: %s", str); + + return proxy; } -#endif /* !SMALL */ -__dead void +static __dead void usage(void) { - fprintf(stderr, "usage: " -#ifndef SMALL - "ftp [-46AadEegiMmnptVv] [-D title] [-k seconds] [-P port] " - "[-r seconds]\n" - " [-s srcaddr] [host [port]]\n" - " ftp [-C] [-o output] [-s srcaddr]\n" - " ftp://[user:password@]host[:port]/file[/] ...\n" - " ftp [-C] [-c cookie] [-o output] [-S ssl_options] " - "[-s srcaddr]\n" - " [-U useragent] [-w seconds] " - "http[s]://[user:password@]host[:port]/file ...\n" - " ftp [-C] [-o output] [-s srcaddr] file:file ...\n" - " ftp [-C] [-o output] [-s srcaddr] host:/file[/] ...\n" -#else /* !SMALL */ - "ftp [-o output] " - "ftp://[user:password@]host[:port]/file[/] ...\n" -#ifndef NOSSL - " ftp [-o output] [-S ssl_options] [-w seconds] " - "http[s]://[user:password@]host[:port]/file ...\n" -#else - " ftp [-o output] [-w seconds] http://host[:port]/file ...\n" -#endif /* NOSSL */ - " ftp [-o output] file:file ...\n" - " ftp [-o output] host:/file[/] ...\n" -#endif /* !SMALL */ - ); + fprintf(stderr, "usage: %s [-46ACVM] [-D title] [-o output] " + "[-S tls_options] [-U useragent] " + "[-w seconds] url ...\n", getprogname()); + exit(1); } diff --git a/usr.bin/ftp/pathnames.h b/usr.bin/ftp/pathnames.h deleted file mode 100644 index 3ac4dacdf17..00000000000 --- a/usr.bin/ftp/pathnames.h +++ /dev/null @@ -1,37 +0,0 @@ -/* $OpenBSD: pathnames.h,v 1.7 2003/06/03 02:56:08 millert Exp $ */ -/* $NetBSD: pathnames.h,v 1.7 1997/01/09 20:19:40 tls Exp $ */ - -/* - * Copyright (c) 1989, 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. - * - * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 - */ - -#include <paths.h> - -#define TMPFILE "ftpXXXXXXXXXX" diff --git a/usr.bin/ftp/progressmeter.c b/usr.bin/ftp/progressmeter.c new file mode 100644 index 00000000000..e4322e9df7a --- /dev/null +++ b/usr.bin/ftp/progressmeter.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org> + * Copyright (c) 2003 Nils Nordman. 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 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. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> + +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "ftp.h" + +#define DEFAULT_WINSIZE 80 +#define MAX_WINSIZE 512 +#define UPDATE_INTERVAL 1 /* update the progress meter every second */ +#define STALL_TIME 5 /* we're stalled after this many seconds */ + +time_t monotime(void); + +/* formats and inserts the specified size into the given buffer */ +static void format_size(char *, int, off_t); +static void format_rate(char *, int, off_t); + +/* window resizing */ +static void sig_winch(int); +static void setscreensize(void); + +/* updates the progressmeter to reflect the current state of the transfer */ +void refresh_progress_meter(void); + +/* signal handler for updating the progress meter */ +static void update_progress_meter(int); + +static const char *title; /* short title for the start of progress bar */ +static time_t start; /* start progress */ +static time_t last_update; /* last progress update */ +static off_t start_pos; /* initial position of transfer */ +static off_t end_pos; /* ending position of transfer */ +static off_t cur_pos; /* transfer position as of last refresh */ +static off_t offset; /* initial offset from start_pos */ +static volatile off_t *counter; /* progress counter */ +static long stalled; /* how long we have been stalled */ +static int bytes_per_second; /* current speed in bytes per second */ +static int win_size; /* terminal window size */ +static volatile sig_atomic_t win_resized; /* for window resizing */ +static const char *filename; /* To be displayed in non-verbose mode */ +/* units for format_size */ +static const char unit[] = " KMGT"; + +time_t +monotime(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + err(1, "monotime"); + + return ts.tv_sec; +} + +static void +format_rate(char *buf, int size, off_t bytes) +{ + int i; + + bytes *= 100; + for (i = 0; bytes >= 100*1000 && unit[i] != 'T'; i++) + bytes = (bytes + 512) / 1024; + if (i == 0) { + i++; + bytes = (bytes + 512) / 1024; + } + snprintf(buf, size, "%3lld.%1lld%c%s", + (long long) (bytes + 5) / 100, + (long long) (bytes + 5) / 10 % 10, + unit[i], + "B"); +} + +static void +format_size(char *buf, int size, off_t bytes) +{ + int i; + + for (i = 0; bytes >= 10000 && unit[i] != 'T'; i++) + bytes = (bytes + 512) / 1024; + snprintf(buf, size, "%4lld%c%s", + (long long) bytes, + unit[i], + i ? "B" : " "); +} + +void +refresh_progress_meter(void) +{ + char buf[MAX_WINSIZE + 1]; + const char *dot = ""; + time_t now; + off_t transferred, bytes_left; + double elapsed; + int len, cur_speed, hours, minutes, seconds, barlength, i; + int percent, overhead = 30; + + transferred = *counter - (cur_pos ? cur_pos : start_pos); + cur_pos = *counter; + now = monotime(); + bytes_left = end_pos - cur_pos; + + if (bytes_left > 0) + elapsed = now - last_update; + else { + elapsed = now - start; + /* Calculate true total speed when done */ + transferred = end_pos - start_pos; + bytes_per_second = 0; + } + + /* calculate speed */ + if (elapsed != 0) + cur_speed = (transferred / elapsed); + else + cur_speed = transferred; + +#define AGE_FACTOR 0.9 + if (bytes_per_second != 0) { + bytes_per_second = (bytes_per_second * AGE_FACTOR) + + (cur_speed * (1.0 - AGE_FACTOR)); + } else + bytes_per_second = cur_speed; + + buf[0] = '\0'; + /* title */ + if (!verbose && title != NULL) { + len = strlen(title); + if (len < 7) + len = 7; + else if (len > 12) { + len = 12; + dot = "..."; + overhead += 3; + } + snprintf(buf, sizeof buf, "\r%-*.*s%s ", len, len, title, dot); + overhead += len + 1; + } else + snprintf(buf, sizeof buf, "\r"); + + if (end_pos == 0 || cur_pos == end_pos) + percent = 100; + else + percent = ((float)cur_pos / end_pos) * 100; + + /* filename and percent */ + if (!verbose && filename != NULL) { + len = strlen(filename); + if (len < 12) + len = 12; + else if (len > 25) { + len = 22; + dot = "..."; + overhead += 3; + } + snprintf(buf + strlen(buf), sizeof buf - strlen(buf), + "%-*.*s%s %3d%% ", len, len, filename, dot, percent); + overhead += len + 1; + } else + snprintf(buf, sizeof buf, "\r%3d%% ", percent); + + /* bar */ + barlength = win_size - overhead; + if (barlength > 0) { + i = barlength * percent / 100; + snprintf(buf + strlen(buf), sizeof buf - strlen(buf), + "|%.*s%*s| ", i, + "*******************************************************" + "*******************************************************" + "*******************************************************" + "*******************************************************" + "*******************************************************" + "*******************************************************" + "*******************************************************", + barlength - i, ""); + + } + + /* amount transferred */ + format_size(buf + strlen(buf), win_size - strlen(buf), cur_pos); + strlcat(buf, " ", win_size); + + /* ETA */ + if (!transferred) + stalled += elapsed; + else + stalled = 0; + + if (stalled >= STALL_TIME) + strlcat(buf, "- stalled -", win_size); + else if (bytes_per_second == 0 && bytes_left) + strlcat(buf, " --:-- ETA", win_size); + else { + if (bytes_left > 0) + seconds = bytes_left / bytes_per_second; + else + seconds = elapsed; + + hours = seconds / 3600; + seconds -= hours * 3600; + minutes = seconds / 60; + seconds -= minutes * 60; + + if (hours != 0) + snprintf(buf + strlen(buf), win_size - strlen(buf), + "%d:%02d:%02d", hours, minutes, seconds); + else + snprintf(buf + strlen(buf), win_size - strlen(buf), + " %02d:%02d", minutes, seconds); + + if (bytes_left > 0) + strlcat(buf, " ETA", win_size); + else + strlcat(buf, " ", win_size); + } + + write(STDERR_FILENO, buf, strlen(buf)); + last_update = now; +} + +static void +update_progress_meter(int ignore) +{ + int save_errno; + + save_errno = errno; + + if (win_resized) { + setscreensize(); + win_resized = 0; + } + + refresh_progress_meter(); + + signal(SIGALRM, update_progress_meter); + alarm(UPDATE_INTERVAL); + errno = save_errno; +} + +void +start_progress_meter(const char *fn, const char *t, off_t filesize, off_t *ctr) +{ + start = last_update = monotime(); + start_pos = *ctr; + offset = *ctr; + cur_pos = 0; + end_pos = 0; + counter = ctr; + stalled = 0; + bytes_per_second = 0; + filename = fn; + title = t; + + /* + * Suppress progressmeter if filesize isn't known when + * Content-Length header has bogus values. + */ + if (filesize <= 0) + return; + + end_pos = filesize; + setscreensize(); + refresh_progress_meter(); + + signal(SIGALRM, update_progress_meter); + signal(SIGWINCH, sig_winch); + alarm(UPDATE_INTERVAL); +} + +void +stop_progress_meter(void) +{ + char rate_str[32]; + double elapsed; + + alarm(0); + + /* Ensure we complete the progress */ + if (end_pos && cur_pos != end_pos) + refresh_progress_meter(); + + if (end_pos) + write(STDERR_FILENO, "\n", 1); + + if (!verbose) + return; + + elapsed = monotime() - start; + if (end_pos == 0) { + if (elapsed != 0) + bytes_per_second = *counter / elapsed; + else + bytes_per_second = *counter; + } + + format_rate(rate_str, sizeof rate_str, bytes_per_second); + fprintf(stderr, "%lld bytes received in %.2f seconds (%s/s)\n", + (end_pos) ? cur_pos - offset : *counter, elapsed, rate_str); +} + +static void +sig_winch(int sig) +{ + win_resized = 1; +} + +static void +setscreensize(void) +{ + struct winsize winsize; + + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &winsize) != -1 && + winsize.ws_col != 0) { + if (winsize.ws_col > MAX_WINSIZE) + win_size = MAX_WINSIZE; + else + win_size = winsize.ws_col; + } else + win_size = DEFAULT_WINSIZE; + win_size += 1; /* trailing \0 */ +} diff --git a/usr.bin/ftp/ruserpass.c b/usr.bin/ftp/ruserpass.c deleted file mode 100644 index 795b3012491..00000000000 --- a/usr.bin/ftp/ruserpass.c +++ /dev/null @@ -1,317 +0,0 @@ -/* $OpenBSD: ruserpass.c,v 1.30 2015/01/16 06:40:08 deraadt Exp $ */ -/* $NetBSD: ruserpass.c,v 1.14 1997/07/20 09:46:01 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. - */ - -#ifndef SMALL - -#include <sys/types.h> -#include <sys/stat.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#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 { - 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 *hdir, buf[PATH_MAX], *tmp; - char myname[HOST_NAME_MAX+1], *mydomain; - int t, i, c, usedefault = 0; - struct stat stb; - - hdir = getenv("HOME"); - if (hdir == NULL || *hdir == '\0') - return (0); - i = snprintf(buf, sizeof(buf), "%s/.netrc", hdir); - if (i < 0 || i >= sizeof(buf)) { - warnc(ENAMETOOLONG, "%s/.netrc", hdir); - return (0); - } - cfile = fopen(buf, "r"); - if (cfile == NULL) { - if (errno != ENOENT) - warn("%s", buf); - return (0); - } - if (gethostname(myname, sizeof(myname)) < 0) - myname[0] = '\0'; - if ((mydomain = strchr(myname, '.')) == NULL) - mydomain = ""; -next: - while ((t = token()) > 0) switch(t) { - - case DEFAULT: - usedefault = 1; - /* FALLTHROUGH */ - - 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, - (size_t)(tmp - hostname)) == 0 && - tokval[tmp - hostname] == '\0') - goto match; - if ((tmp = strchr(host, '.')) != NULL && - strcasecmp(tmp, mydomain) == 0 && - strncasecmp(host, tokval, - (size_t)(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 == 0) { - if ((*aname = strdup(tokval)) == NULL) - err(1, "strdup"); - } 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 == 0) { - if ((*apass = strdup(tokval)) == NULL) - err(1, "strdup"); - } - 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 == 0) { - if ((*aacct = strdup(tokval)) == NULL) - err(1, "strdup"); - } - break; - case MACDEF: - if (proxy) { - (void)fclose(cfile); - return (0); - } - while ((c = fgetc(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 = fgetc(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 = fgetc(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 = fgetc(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 = fgetc(cfile)) != EOF && - (c == '\n' || c == '\t' || c == ' ' || c == ',')) - continue; - if (c == EOF) - return (0); - cp = tokval; - if (c == '"') { - while ((c = fgetc(cfile)) != EOF && c != '"') { - if (c == '\\' && (c = fgetc(cfile)) == EOF) - break; - *cp++ = c; - if (cp == tokval + sizeof(tokval)) { - warnx("Token in .netrc too long"); - return (-1); - } - } - } else { - *cp++ = c; - while ((c = fgetc(cfile)) != EOF - && c != '\n' && c != '\t' && c != ' ' && c != ',') { - if (c == '\\' && (c = fgetc(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); -} - -#endif /* !SMALL */ - diff --git a/usr.bin/ftp/small.c b/usr.bin/ftp/small.c deleted file mode 100644 index 07f5ddd214b..00000000000 --- a/usr.bin/ftp/small.c +++ /dev/null @@ -1,730 +0,0 @@ -/* $OpenBSD: small.c,v 1.9 2017/01/21 08:33:07 krw Exp $ */ -/* $NetBSD: cmds.c,v 1.27 1997/08/18 10:20:15 lukem Exp $ */ - -/* - * 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. - */ - -/* - * 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 User Program -- Command Routines. - */ -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <arpa/ftp.h> - -#include <ctype.h> -#include <err.h> -#include <fnmatch.h> -#include <glob.h> -#include <netdb.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> - -#include "ftp_var.h" -#include "pathnames.h" -#include "small.h" - -jmp_buf jabort; -char *mname; -char *home = "/"; - -struct types { - char *t_name; - char *t_mode; - int t_type; -} types[] = { - { "ascii", "A", TYPE_A }, - { "binary", "I", TYPE_I }, - { "image", "I", TYPE_I }, - { NULL } -}; - -/* - * Set transfer type. - */ -void -settype(int argc, char *argv[]) -{ - struct types *p; - int comret; - - if (argc > 2) { - char *sep; - - fprintf(ttyout, "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; - } - for (p = types; p->t_name; p++) - if (strcmp(argv[1], p->t_name) == 0) - break; - if (p->t_name == 0) { - fprintf(ttyout, "%s: unknown mode.\n", argv[1]); - code = -1; - return; - } - 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 ( -#ifndef SMALL - !debug && -#endif /* !SMALL */ - show == 0) - verbose = 0; - for (p = types; p->t_name; p++) - if (newtype == p->t_type) - break; - if (p->t_name == 0) { - warnx("internal error: unknown type %d.", newtype); - return; - } - 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; -} - -char *stype[] = { - "type", - "", - 0 -}; - -/* - * Set binary transfer type. - */ -/*ARGSUSED*/ -void -setbinary(int argc, char *argv[]) -{ - - stype[1] = "binary"; - settype(2, stype); -} - -void -get(int argc, char *argv[]) -{ - - (void)getit(argc, argv, 0, restart_point ? "a+w" : "w" ); -} - -/* - * Receive one file. - */ -int -getit(int argc, char *argv[], int restartit, const char *mode) -{ - int loc = 0; - int rval = 0; - char *oldargv1, *oldargv2, *globargv2; - - if (argc == 2) { - argc++; - argv[2] = argv[1]; - loc++; - } -#ifndef SMALL - if (argc < 2 && !another(&argc, &argv, "remote-file")) - goto usage; - if ((argc < 3 && !another(&argc, &argv, "local-file")) || argc > 3) { -usage: - fprintf(ttyout, "usage: %s remote-file [local-file]\n", - argv[0]); - code = -1; - return (0); - } -#endif /* !SMALL */ - oldargv1 = argv[1]; - oldargv2 = argv[2]; - if (!globulize(&argv[2])) { - code = -1; - return (0); - } - globargv2 = argv[2]; - if (loc && mcase) { - char *tp = argv[1], *tp2, tmpbuf[PATH_MAX]; - - while (*tp && !islower((unsigned char)*tp)) { - tp++; - } - if (!*tp) { - tp = argv[2]; - tp2 = tmpbuf; - while ((*tp2 = *tp) != '\0') { - if (isupper((unsigned char)*tp2)) { - *tp2 = tolower((unsigned char)*tp2); - } - tp++; - tp2++; - } - argv[2] = tmpbuf; - } - } - if (loc && ntflag) - argv[2] = dotrans(argv[2]); - if (loc && mapflag) - argv[2] = domap(argv[2]); -#ifndef SMALL - if (restartit) { - struct stat stbuf; - int ret; - - ret = stat(argv[2], &stbuf); - if (restartit == 1) { - restart_point = (ret < 0) ? 0 : 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; - fprintf(ttyout, - "Local file \"%s\" is newer "\ - "than remote file \"%s\".\n", - argv[2], argv[1]); - goto freegetit; - } - } - } - } -#endif /* !SMALL */ - - recvrequest("RETR", argv[2], argv[1], mode, - argv[1] != oldargv1 || argv[2] != oldargv2 || !interactive, loc); - restart_point = 0; -#ifndef SMALL -freegetit: -#endif - if (oldargv2 != globargv2) /* free up after globulize() */ - free(globargv2); - return (rval); -} - -/* XXX - Signal race. */ -/* ARGSUSED */ -void -mabort(int signo) -{ - int save_errno = errno; - - alarmtimer(0); - (void) write(fileno(ttyout), "\n\r", 2); -#ifndef SMALL - if (mflag && fromatty) { - /* XXX signal race, crazy unbelievable stdio misuse */ - if (confirm(mname, NULL)) { - errno = save_errno; - longjmp(jabort, 1); - } - } -#endif /* !SMALL */ - mflag = 0; - errno = save_errno; - longjmp(jabort, 1); -} - -/* - * Get multiple files. - */ -void -mget(int argc, char *argv[]) -{ - extern int optind, optreset; - sig_t oldintr; - int xargc = 2; - char *cp, localcwd[PATH_MAX], *xargv[] = { argv[0], NULL, NULL }; - static int restartit = 0; -#ifndef SMALL - extern char *optarg; - const char *errstr; - int ch, i = 1; - char type = 0, *dummyargv[] = { argv[0], ".", NULL }; - FILE *ftemp = NULL; - static int depth = 0, max_depth = 0; - - optind = optreset = 1; - - if (depth) - depth++; - - while ((ch = getopt(argc, argv, "cd:nr")) != -1) { - switch(ch) { - case 'c': - restartit = 1; - break; - case 'd': - max_depth = strtonum(optarg, 0, INT_MAX, &errstr); - if (errstr != NULL) { - fprintf(ttyout, "bad depth value, %s: %s\n", - errstr, optarg); - code = -1; - return; - } - break; - case 'n': - restartit = -1; - break; - case 'r': - depth = 1; - break; - default: - goto usage; - } - } - - if (argc - optind < 1 && !another(&argc, &argv, "remote-files")) { -usage: - fprintf(ttyout, "usage: %s [-cnr] [-d depth] remote-files\n", - argv[0]); - code = -1; - return; - } - - argv[optind - 1] = argv[0]; - argc -= optind - 1; - argv += optind - 1; -#endif /* !SMALL */ - - mname = argv[0]; - mflag = 1; - if (getcwd(localcwd, sizeof(localcwd)) == NULL) - err(1, "can't get cwd"); - - oldintr = signal(SIGINT, mabort); - (void)setjmp(jabort); - while ((cp = -#ifdef SMALL - remglob(argv, proxy, NULL)) != NULL - ) { -#else /* SMALL */ - depth ? remglob2(dummyargv, proxy, NULL, &ftemp, &type) : - remglob(argv, proxy, NULL)) != NULL - || (mflag && depth && ++i < argc) - ) { - if (cp == NULL) - continue; -#endif /* SMALL */ - if (*cp == '\0') { - mflag = 0; - continue; - } - if (!mflag) - continue; -#ifndef SMALL - if (depth && fnmatch(argv[i], cp, FNM_PATHNAME) != 0) - continue; -#endif /* !SMALL */ - if (!fileindir(cp, localcwd)) { - fprintf(ttyout, "Skipping non-relative filename `%s'\n", - cp); - continue; - } -#ifndef SMALL - if (type == 'd' && depth == max_depth) - continue; - if (!confirm(argv[0], cp)) - continue; - if (type == 'd') { - mkdir(cp, 0755); - if (chdir(cp) != 0) { - warn("local: %s", cp); - continue; - } - - xargv[1] = cp; - cd(xargc, xargv); - if (dirchange != 1) - goto out; - - xargv[1] = "*"; - mget(xargc, xargv); - - xargv[1] = ".."; - cd(xargc, xargv); - if (dirchange != 1) { - mflag = 0; - goto out; - } - -out: - if (chdir("..") != 0) { - warn("local: %s", cp); - mflag = 0; - } - continue; - } - if (type == 's') - /* Currently ignored. */ - continue; -#endif /* !SMALL */ - xargv[1] = cp; - (void)getit(xargc, xargv, restartit, - (restartit == 1 || restart_point) ? "a+w" : "w"); -#ifndef SMALL - if (!mflag && fromatty) { - if (confirm(argv[0], NULL)) - mflag = 1; - } -#endif /* !SMALL */ - } - (void)signal(SIGINT, oldintr); -#ifndef SMALL - if (depth) - depth--; - if (depth == 0 || mflag == 0) - depth = max_depth = mflag = restartit = 0; -#else /* !SMALL */ - mflag = 0; -#endif /* !SMALL */ -} - -/* - * Set current working directory on remote machine. - */ -void -cd(int argc, char *argv[]) -{ - int r; - -#ifndef SMALL - if ((argc < 2 && !another(&argc, &argv, "remote-directory")) || - argc > 2) { - fprintf(ttyout, "usage: %s remote-directory\n", argv[0]); - code = -1; - return; - } -#endif /* !SMALL */ - 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 == ERROR && code == 550) { - dirchange = 0; - return; - } - if (r == COMPLETE) - dirchange = 1; -} - -/* - * Terminate session, but don't exit. - */ -/* ARGSUSED */ -void -disconnect(int argc, char *argv[]) -{ - - if (!connected) - return; - (void)command("QUIT"); - if (cout) { - (void)fclose(cout); - } - cout = NULL; - connected = 0; - data = -1; -#ifndef SMALL - if (!proxy) { - macnum = 0; - } -#endif /* !SMALL */ -} - -char * -dotrans(char *name) -{ - static char new[PATH_MAX]; - char *cp1, *cp2 = new; - int i, ostop, found; - - for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++) - continue; - for (cp1 = name; *cp1; cp1++) { - found = 0; - for (i = 0; *(ntin + i) && i < 16; i++) { - if (*cp1 == *(ntin + i)) { - found++; - if (i < ostop) { - *cp2++ = *(ntout + i); - } - break; - } - } - if (!found) { - *cp2++ = *cp1; - } - } - *cp2 = '\0'; - return (new); -} - -char * -domap(char *name) -{ - static char new[PATH_MAX]; - char *cp1 = name, *cp2 = mapin; - 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; - } - cp1 = new; - *cp1 = '\0'; - cp2 = mapout; - while (*cp2) { - match = 0; - switch (*cp2) { - case '\\': - if (*(cp2 + 1)) { - *cp1++ = *++cp2; - } - break; - case '[': -LOOP: - if (*++cp2 == '$' && isdigit((unsigned char)*(cp2 + 1))) { - if (*++cp2 == '0') { - char *cp3 = name; - - while (*cp3) { - *cp1++ = *cp3++; - } - match = 1; - } - else if (toks[toknum = *cp2 - '1']) { - char *cp3 = tp[toknum]; - - while (cp3 != te[toknum]) { - *cp1++ = *cp3++; - } - match = 1; - } - } - else { - while (*cp2 && *cp2 != ',' && - *cp2 != ']') { - if (*cp2 == '\\') { - cp2++; - } - else if (*cp2 == '$' && - isdigit((unsigned char)*(cp2 + 1))) { - if (*++cp2 == '0') { - char *cp3 = name; - - while (*cp3) { - *cp1++ = *cp3++; - } - } - else if (toks[toknum = - *cp2 - '1']) { - char *cp3=tp[toknum]; - - while (cp3 != - te[toknum]) { - *cp1++ = *cp3++; - } - } - } - else if (*cp2) { - *cp1++ = *cp2++; - } - } - if (!*cp2) { - fputs( -"nmap: unbalanced brackets.\n", ttyout); - return (name); - } - match = 1; - cp2--; - } - if (match) { - while (*++cp2 && *cp2 != ']') { - if (*cp2 == '\\' && *(cp2 + 1)) { - cp2++; - } - } - if (!*cp2) { - fputs( -"nmap: unbalanced brackets.\n", ttyout); - return (name); - } - break; - } - switch (*++cp2) { - case ',': - goto LOOP; - case ']': - break; - default: - cp2--; - goto LOOP; - } - break; - case '$': - if (isdigit((unsigned char)*(cp2 + 1))) { - if (*++cp2 == '0') { - char *cp3 = name; - - while (*cp3) { - *cp1++ = *cp3++; - } - } - else if (toks[toknum = *cp2 - '1']) { - char *cp3 = tp[toknum]; - - while (cp3 != te[toknum]) { - *cp1++ = *cp3++; - } - } - break; - } - /* FALLTHROUGH */ - default: - *cp1++ = *cp2; - break; - } - cp2++; - } - *cp1 = '\0'; - if (!*new) { - return (name); - } - return (new); -} - diff --git a/usr.bin/ftp/small.h b/usr.bin/ftp/small.h deleted file mode 100644 index a051f2dc621..00000000000 --- a/usr.bin/ftp/small.h +++ /dev/null @@ -1,35 +0,0 @@ -/* $OpenBSD: small.h,v 1.1 2009/05/05 19:35:30 martynas Exp $ */ - -/* - * Copyright (c) 2009 Martynas Venckus <martynas@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -extern jmp_buf jabort; -extern char *mname; -extern char *home; -extern char *stype[]; - -void settype(int, char **); -void changetype(int, int); -void setbinary(int, char **); -void get(int, char **); -int getit(int, char **, int, const char *); -void mabort(int); -void mget(int, char **); -void cd(int, char **); -void disconnect(int, char **); -char *dotrans(char *); -char *domap(char *); - diff --git a/usr.bin/ftp/stringlist.c b/usr.bin/ftp/stringlist.c deleted file mode 100644 index 0e7628721d2..00000000000 --- a/usr.bin/ftp/stringlist.c +++ /dev/null @@ -1,97 +0,0 @@ -/* $OpenBSD: stringlist.c,v 1.12 2015/05/20 23:39:55 schwarze Exp $ */ -/* $NetBSD: stringlist.c,v 1.2 1997/01/17 07:26:20 lukem Exp $ */ - -/* - * Copyright (c) 1994 Christos Zoulas - * 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 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. - */ - -#ifndef SMALL - -#include <stdio.h> -#include <string.h> -#include <err.h> -#include <stdlib.h> - -#include "stringlist.h" - -#define _SL_CHUNKSIZE 20 - -/* - * sl_init(): Initialize a string list - */ -StringList * -sl_init(void) -{ - StringList *sl = malloc(sizeof(StringList)); - if (sl == NULL) - err(1, "stringlist"); - - sl->sl_cur = 0; - sl->sl_max = _SL_CHUNKSIZE; - sl->sl_str = calloc(sl->sl_max, sizeof(char *)); - if (sl->sl_str == NULL) - err(1, "stringlist"); - return sl; -} - - -/* - * sl_add(): Add an item to the string list - */ -void -sl_add(StringList *sl, char *name) -{ - if (sl->sl_cur == sl->sl_max - 1) { - sl->sl_max += _SL_CHUNKSIZE; - sl->sl_str = reallocarray(sl->sl_str, sl->sl_max, - sizeof(char *)); - if (sl->sl_str == NULL) - err(1, "stringlist"); - } - sl->sl_str[sl->sl_cur++] = name; -} - - -/* - * sl_free(): Free a stringlist - */ -void -sl_free(StringList *sl, int all) -{ - size_t i; - - if (sl == NULL) - return; - if (sl->sl_str) { - if (all) - for (i = 0; i < sl->sl_cur; i++) - free(sl->sl_str[i]); - free(sl->sl_str); - } - free(sl); -} - -#endif /* !SMALL */ - diff --git a/usr.bin/ftp/stringlist.h b/usr.bin/ftp/stringlist.h deleted file mode 100644 index 7c5b6ae12ae..00000000000 --- a/usr.bin/ftp/stringlist.h +++ /dev/null @@ -1,56 +0,0 @@ -/* $OpenBSD: stringlist.h,v 1.6 2017/01/21 08:33:07 krw Exp $ */ -/* $NetBSD: stringlist.h,v 1.2 1997/01/17 06:11:36 lukem Exp $ */ - -/* - * Copyright (c) 1994 Christos Zoulas - * 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 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. - */ - -#ifndef SMALL - -#ifndef _STRINGLIST_H -#define _STRINGLIST_H - -#include <sys/types.h> - -/* - * Simple string list - */ -typedef struct _stringlist { - char **sl_str; - size_t sl_max; - size_t sl_cur; -} StringList; - -__BEGIN_DECLS -StringList *sl_init(void); -void sl_add(StringList *, char *); -void sl_free(StringList *, int); -char *sl_find(StringList *, char *); -__END_DECLS - -#endif /* _STRINGLIST_H */ - -#endif /* !SMALL */ - diff --git a/usr.bin/ftp/url.c b/usr.bin/ftp/url.c new file mode 100644 index 00000000000..3087aa1d834 --- /dev/null +++ b/usr.bin/ftp/url.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2017 Sunil Nimmagadda <sunil@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason Thorpe and 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 <sys/types.h> + +#include <netinet/in.h> +#include <resolv.h> + +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> + +#include "ftp.h" +#include "xmalloc.h" + +#define BASICAUTH_LEN 1024 + +static void authority_parse(const char *, char **, char **, char **); +static int ipv6_parse(const char *, char **, char **); +static int unsafe_char(const char *); + +#ifndef NOSSL +const char *scheme_str[] = { "http:", "ftp:", "file:", "https:" }; +const char *port_str[] = { "80", "21", NULL, "443" }; +#else +const char *scheme_str[] = { "http:", "ftp:", "file:" }; +const char *port_str[] = { "80", "21", NULL }; +#endif + +int +scheme_lookup(const char *str) +{ + size_t i; + + for (i = 0; i < nitems(scheme_str); i++) + if (strncasecmp(str, scheme_str[i], strlen(scheme_str[i])) == 0) + return i; + + return -1; +} + +static int +ipv6_parse(const char *str, char **host, char **port) +{ + char *p; + + if ((p = strchr(str, ']')) == NULL) { + warnx("%s: invalid IPv6 address: %s", __func__, str); + return 1; + } + + *p++ = '\0'; + if (strlen(str + 1) > 0) + *host = xstrdup(str + 1); + + if (*p == '\0') + return 0; + + if (*p++ != ':') { + warnx("%s: invalid port: %s", __func__, p); + free(*host); + return 1; + } + + if (strlen(p) > 0) + *port = xstrdup(p); + + return 0; +} + +static void +authority_parse(const char *str, char **host, char **port, char **basic_auth) +{ + char *p; + + if ((p = strchr(str, '@')) != NULL) { + *basic_auth = xcalloc(1, BASICAUTH_LEN); + if (b64_ntop((unsigned char *)str, p - str, + *basic_auth, BASICAUTH_LEN) == -1) + errx(1, "base64 encode failed"); + + str = ++p; + } + + if ((p = strchr(str, ':')) != NULL) { + *p++ = '\0'; + if (strlen(p) > 0) + *port = xstrdup(p); + } + + if (strlen(str) > 0) + *host = xstrdup(str); +} + +struct url * +url_parse(const char *str) +{ + struct url *url; + const char *p, *q; + char *basic_auth, *host, *port, *path, *s; + size_t len; + int ipliteral, scheme; + + p = str; + ipliteral = 0; + host = port = path = basic_auth = NULL; + while (isblank((unsigned char)*p)) + p++; + + if ((q = strchr(p, ':')) == NULL) { + warnx("%s: scheme missing: %s", __func__, str); + return NULL; + } + + if ((scheme = scheme_lookup(p)) == -1) { + warnx("%s: invalid scheme: %s", __func__, p); + return NULL; + } + + p = ++q; + if (strncmp(p, "//", 2) != 0) { + if (scheme == S_FILE) + goto done; + else { + warnx("%s: invalid url: %s", __func__, str); + return NULL; + } + } + + p += 2; + len = strlen(p); + /* Authority terminated by a '/' if present */ + if ((q = strchr(p, '/')) != NULL) + len = q - p; + + s = xstrndup(p, len); + if (*p == '[') { + if (ipv6_parse(s, &host, &port) != 0) { + free(s); + return NULL; + } + ipliteral = 1; + } else + authority_parse(s, &host, &port, &basic_auth); + + free(s); + if (port == NULL && scheme != S_FILE) + port = xstrdup(port_str[scheme]); + + done: + if (q != NULL) + path = xstrdup(q); + + if (io_debug) { + fprintf(stderr, + "scheme: %s\nhost: %s\nport: %s\npath: %s\n", + scheme_str[scheme], host, port, path); + } + + url = xcalloc(1, sizeof *url); + url->scheme = scheme; + url->host = host; + url->port = port; + url->path = path; + url->basic_auth = basic_auth; + url->ipliteral = ipliteral; + return url; +} + +void +url_free(struct url *url) +{ + if (url == NULL) + return; + + free(url->host); + free(url->port); + free(url->path); + freezero(url->basic_auth, BASICAUTH_LEN); + free(url->fname); + free(url); +} + +void +url_connect(struct url *url, struct url *proxy, int timeout) +{ + switch (url->scheme) { + case S_HTTP: + case S_HTTPS: + http_connect(url, proxy, timeout); + break; + case S_FTP: + ftp_connect(url, proxy, timeout); + break; + } +} + +struct url * +url_request(struct url *url, struct url *proxy, off_t *offset, off_t *sz) +{ + switch (url->scheme) { + case S_HTTP: + case S_HTTPS: + return http_get(url, proxy, offset, sz); + case S_FTP: + return ftp_get(url, proxy, offset, sz); + case S_FILE: + return file_request(&child_ibuf, url, offset, sz); + } + + return NULL; +} + +void +url_save(struct url *url, FILE *dst_fp, off_t *offset) +{ + switch (url->scheme) { + case S_HTTP: + case S_HTTPS: + http_save(url, dst_fp, offset); + break; + case S_FTP: + ftp_save(url, dst_fp, offset); + break; + case S_FILE: + file_save(url, dst_fp, offset); + break; + } +} + +void +url_close(struct url *url) +{ + switch (url->scheme) { + case S_HTTP: + case S_HTTPS: + http_close(url); + break; + case S_FTP: + ftp_quit(url); + break; + } +} + +char * +url_str(struct url *url) +{ + char *host, *str; + int custom_port; + + custom_port = strcmp(url->port, port_str[url->scheme]) ? 1 : 0; + if (url->ipliteral) + xasprintf(&host, "[%s]", url->host); + else + host = xstrdup(url->host); + + xasprintf(&str, "%s//%s%s%s%s", + scheme_str[url->scheme], + host, + custom_port ? ":" : "", + custom_port ? url->port : "", + url->path ? url->path : "/"); + + free(host); + return str; +} + +/* + * Encode given URL, per RFC1738. + * Allocate and return string to the caller. + */ +char * +url_encode(const char *path) +{ + size_t i, length, new_length; + char *epath, *epathp; + + length = new_length = strlen(path); + + /* + * First pass: + * Count unsafe characters, and determine length of the + * final URL. + */ + for (i = 0; i < length; i++) + if (unsafe_char(path + i)) + new_length += 2; + + epath = epathp = xmalloc(new_length + 1); /* One more for '\0'. */ + + /* + * Second pass: + * Encode, and copy final URL. + */ + for (i = 0; i < length; i++) + if (unsafe_char(path + i)) { + snprintf(epathp, 4, "%%" "%02x", + (unsigned char)path[i]); + epathp += 3; + } else + *(epathp++) = path[i]; + + *epathp = '\0'; + return epath; +} + +/* + * Determine whether the character needs encoding, per RFC1738: + * - No corresponding graphic US-ASCII. + * - Unsafe characters. + */ +static int +unsafe_char(const char *c0) +{ + const char *unsafe_chars = " <>\"#{}|\\^~[]`"; + const unsigned char *c = (const unsigned char *)c0; + + /* + * No corresponding graphic US-ASCII. + * Control characters and octets not used in US-ASCII. + */ + return (iscntrl(*c) || !isascii(*c) || + + /* + * Unsafe characters. + * '%' is also unsafe, if is not followed by two + * hexadecimal digits. + */ + strchr(unsafe_chars, *c) != NULL || + (*c == '%' && (!isxdigit(*++c) || !isxdigit(*++c)))); +} + +void +log_request(const char *prefix, struct url *url, struct url *proxy) +{ + char *host; + int custom_port; + + if (url->scheme == S_FILE) + return; + + custom_port = strcmp(url->port, port_str[url->scheme]) ? 1 : 0; + if (url->ipliteral) + xasprintf(&host, "[%s]", url->host); + else + host = xstrdup(url->host); + + if (proxy) + log_info("%s %s//%s%s%s%s" + " (via %s//%s%s%s)\n", + prefix, + scheme_str[url->scheme], + host, + custom_port ? ":" : "", + custom_port ? url->port : "", + url->path ? url->path : "", + + /* via proxy part */ + (proxy->scheme == S_HTTP) ? "http" : "https", + proxy->host, + proxy->port ? ":" : "", + proxy->port ? proxy->port : ""); + else + log_info("%s %s//%s%s%s%s\n", + prefix, + scheme_str[url->scheme], + host, + custom_port ? ":" : "", + custom_port ? url->port : "", + url->path ? url->path : ""); + + free(host); +} diff --git a/usr.bin/ftp/util.c b/usr.bin/ftp/util.c index 1c82f3fb478..7ea0ad433e0 100644 --- a/usr.bin/ftp/util.c +++ b/usr.bin/ftp/util.c @@ -1,1099 +1,227 @@ -/* $OpenBSD: util.c,v 1.86 2017/12/23 20:04:23 cheloha Exp $ */ -/* $NetBSD: util.c,v 1.12 1997/08/18 10:20:27 lukem Exp $ */ - -/*- - * Copyright (c) 1997-1999 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. + * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org> * - * 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. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * 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. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* - * FTP User Program -- Misc support routines - */ -#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/queue.h> #include <sys/socket.h> -#include <sys/time.h> -#include <arpa/ftp.h> -#include <ctype.h> #include <err.h> #include <errno.h> -#include <fcntl.h> -#include <libgen.h> -#include <glob.h> +#include <imsg.h> +#include <netdb.h> #include <poll.h> -#include <pwd.h> #include <signal.h> +#include <stdarg.h> #include <stdio.h> -#include <stdlib.h> #include <string.h> -#include <time.h> +#include <stdlib.h> #include <unistd.h> -#include "ftp_var.h" -#include "pathnames.h" - -#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) -#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) - -static void updateprogressmeter(int); - -/* - * Connect to peer server and - * auto-login, if possible. - */ -void -setpeer(int argc, char *argv[]) -{ - char *host, *port; - - if (connected) { - fprintf(ttyout, "Already connected to %s, use close first.\n", - hostname); - code = -1; - return; - } -#ifndef SMALL - if (argc < 2) - (void)another(&argc, &argv, "to"); - if (argc < 2 || argc > 3) { - fprintf(ttyout, "usage: %s host [port]\n", argv[0]); - code = -1; - return; - } -#endif /* !SMALL */ - if (gatemode) - port = gateport; - else - port = ftpport; - if (argc > 2) - port = argv[2]; - - if (gatemode) { - if (gateserver == NULL || *gateserver == '\0') - errx(1, "gateserver not defined (shouldn't happen)"); - host = hookup(gateserver, port); - } else - host = hookup(argv[1], port); - - if (host) { - int overbose; - - if (gatemode) { - if (command("PASSERVE %s", argv[1]) != COMPLETE) - return; - if (verbose) - fprintf(ttyout, - "Connected via pass-through server %s\n", - gateserver); - } - - connected = 1; - /* - * Set up defaults for FTP. - */ - (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; - - /* - * 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. - */ - (void)strlcpy(typename, "binary", sizeof typename); - curtype = TYPE_A; - type = 0; - if (autologin) - (void)ftp_login(argv[1], NULL, NULL); +#include "ftp.h" +#include "xmalloc.h" - overbose = verbose; -#ifndef SMALL - if (!debug) -#endif /* !SMALL */ - verbose = -1; - if (command("SYST") == COMPLETE && overbose) { - char *cp, c; - c = 0; - cp = strchr(reply_string + 4, ' '); - if (cp == NULL) - cp = strchr(reply_string + 4, '\r'); - if (cp) { - if (cp[-1] == '.') - cp--; - c = *cp; - *cp = '\0'; - } - - fprintf(ttyout, "Remote system type is %s.\n", reply_string + 4); - if (cp) - *cp = c; - } - if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) { - if (proxy) - unix_proxy = 1; - else - unix_server = 1; - if (overbose) - fprintf(ttyout, "Using %s mode to transfer files.\n", - typename); - } else { - if (proxy) - unix_proxy = 0; - else - unix_server = 0; - } - verbose = overbose; - } -} - -/* - * login to remote host, using given username & password if supplied - */ -int -ftp_login(const char *host, char *user, char *pass) -{ - char tmp[80], *acctname = NULL, host_name[HOST_NAME_MAX+1]; - char anonpass[LOGIN_NAME_MAX + 1 + HOST_NAME_MAX+1]; /* "user@hostname" */ - int n, aflag = 0, retry = 0; - struct passwd *pw; - -#ifndef SMALL - if (user == NULL && !anonftp) { - if (ruserpass(host, &user, &pass, &acctname) < 0) { - code = -1; - return (0); - } - } -#endif /* !SMALL */ - - /* - * Set up arguments for an anonymous FTP session, if necessary. - */ - if ((user == NULL || pass == NULL) && anonftp) { - memset(anonpass, 0, sizeof(anonpass)); - memset(host_name, 0, sizeof(host_name)); - - /* - * Set up anonymous login password. - */ - if ((user = getlogin()) == NULL) { - if ((pw = getpwuid(getuid())) == NULL) - user = "anonymous"; - else - user = pw->pw_name; - } - gethostname(host_name, sizeof(host_name)); -#ifndef DONT_CHEAT_ANONPASS - /* - * 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 - */ - snprintf(anonpass, sizeof(anonpass) - 1, "%s@", - user); -#else - snprintf(anonpass, sizeof(anonpass) - 1, "%s@%s", - user, hp->h_name); -#endif - pass = anonpass; - user = "anonymous"; /* as per RFC 1635 */ - } - -tryagain: - if (retry) - user = "ftp"; /* some servers only allow "ftp" */ - - while (user == NULL) { - char *myname = getlogin(); - - if (myname == NULL && (pw = getpwuid(getuid())) != NULL) - myname = pw->pw_name; - if (myname) - fprintf(ttyout, "Name (%s:%s): ", host, myname); - else - fprintf(ttyout, "Name (%s): ", host); - user = myname; - if (fgets(tmp, sizeof(tmp), stdin) != NULL) { - tmp[strcspn(tmp, "\n")] = '\0'; - if (tmp[0] != '\0') - user = tmp; - } - else - exit(0); - } - n = command("USER %s", user); - if (n == CONTINUE) { - if (pass == NULL) - pass = getpass("Password:"); - n = command("PASS %s", pass); - } - if (n == CONTINUE) { - aflag++; - if (acctname == NULL) - acctname = getpass("Account:"); - n = command("ACCT %s", acctname); - } - if ((n != COMPLETE) || - (!aflag && acctname != NULL && command("ACCT %s", acctname) != COMPLETE)) { - warnx("Login %s failed.", user); - if (retry || !anonftp) - return (0); - else - retry = 1; - goto tryagain; - } - if (proxy) - return (1); - connected = -1; -#ifndef SMALL - for (n = 0; n < macnum; ++n) { - if (!strcmp("init", macros[n].mac_name)) { - (void)strlcpy(line, "$init", sizeof line); - makeargv(); - domacro(margc, margv); - break; - } - } -#endif /* SMALL */ - return (1); -} +static void tooslow(int); /* - * `another' gets another argument, and stores the new argc and argv. - * It reverts to the top level (via main.c's intr()) on EOF/error. - * - * Returns false if no new arguments have been added. + * Wait for an asynchronous connect(2) attempt to finish. */ -#ifndef SMALL int -another(int *pargc, char ***pargv, const char *prompt) -{ - int len = strlen(line), ret; - - if (len >= sizeof(line) - 3) { - fputs("sorry, arguments too long.\n", ttyout); - intr(); - } - fprintf(ttyout, "(%s) ", prompt); - line[len++] = ' '; - if (fgets(&line[len], (int)(sizeof(line) - len), stdin) == NULL) { - clearerr(stdin); - intr(); - } - len += strlen(&line[len]); - if (len > 0 && line[len - 1] == '\n') - line[len - 1] = '\0'; - makeargv(); - ret = margc > *pargc; - *pargc = margc; - *pargv = margv; - return (ret); -} -#endif /* !SMALL */ - -/* - * glob files given in argv[] from the remote server. - * if errbuf isn't NULL, store error messages there instead - * of writing to the screen. - * if type isn't NULL, use LIST instead of NLST, and store filetype. - * 'd' means directory, 's' means symbolic link, '-' means plain - * file. - */ -char * -remglob2(char *argv[], int doswitch, char **errbuf, FILE **ftemp, char *type) +connect_wait(int s) { - char temp[PATH_MAX], *bufp, *cp, *lmode; - static char buf[PATH_MAX], **args; - int oldverbose, oldhash, fd; - - if (!mflag) { - 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) { - int len; - - cp = _PATH_TMP; - len = strlen(cp); - if (len + sizeof(TMPFILE) + (cp[len-1] != '/') > sizeof(temp)) { - warnx("unable to create temporary file: %s", - strerror(ENAMETOOLONG)); - return (NULL); - } - - (void)strlcpy(temp, cp, sizeof temp); - if (temp[len-1] != '/') - temp[len++] = '/'; - (void)strlcpy(&temp[len], TMPFILE, sizeof temp - len); - 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; - hash = 0; - if (doswitch) - pswitch(!proxy); - for (lmode = "w"; *++argv != NULL; lmode = "a") - recvrequest(type ? "LIST" : "NLST", temp, *argv, lmode, - 0, 0); - if ((code / 100) != COMPLETE) { - if (errbuf != NULL) - *errbuf = reply_string; - } - if (doswitch) - pswitch(!proxy); - verbose = oldverbose; - hash = oldhash; - *ftemp = fopen(temp, "r"); - (void)unlink(temp); - if (*ftemp == NULL) { - if (errbuf == NULL) - fputs("can't find list of remote files, oops.\n", - ttyout); - else - *errbuf = - "can't find list of remote files, oops."; - return (NULL); - } - } -#ifndef SMALL -again: -#endif - if (fgets(buf, sizeof(buf), *ftemp) == NULL) { - (void)fclose(*ftemp); - *ftemp = NULL; - return (NULL); - } + struct pollfd pfd[1]; + int error = 0; + socklen_t len = sizeof(error); - buf[strcspn(buf, "\n")] = '\0'; - bufp = buf; + pfd[0].fd = s; + pfd[0].events = POLLOUT; -#ifndef SMALL - if (type) { - parse_list(&bufp, type); - if (!bufp || - (bufp[0] == '.' && /* LIST defaults to -a on some ftp */ - (bufp[1] == '\0' || /* servers. Ignore '.' and '..'. */ - (bufp[1] == '.' && bufp[2] == '\0')))) - goto again; + if (poll(pfd, 1, -1) == -1) + return -1; + if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) + return -1; + if (error != 0) { + errno = error; + return -1; } -#endif /* !SMALL */ - - return (bufp); + return 0; } -/* - * wrapper for remglob2 - */ -char * -remglob(char *argv[], int doswitch, char **errbuf) +static void +tooslow(int signo) { - static FILE *ftemp = NULL; - - return remglob2(argv, doswitch, errbuf, &ftemp, NULL); + dprintf(STDERR_FILENO, "%s: connect taking too long\n", getprogname()); + _exit(2); } -#ifndef SMALL int -confirm(const char *cmd, const char *file) +tcp_connect(const char *host, const char *port, int timeout) { - char str[BUFSIZ]; + struct addrinfo hints, *res, *res0; + char hbuf[NI_MAXHOST]; + const char *cause = NULL; + int error, s = -1, save_errno; - if (file && (confirmrest || !interactive)) - return (1); -top: - if (file) - fprintf(ttyout, "%s %s? ", cmd, file); - else - fprintf(ttyout, "Continue with %s? ", cmd); - (void)fflush(ttyout); - if (fgets(str, sizeof(str), stdin) == NULL) - goto quit; - switch (tolower((unsigned char)*str)) { - case '?': - fprintf(ttyout, - "? help\n" - "a answer yes to all\n" - "n answer no\n" - "p turn off prompt mode\n" - "q answer no to all\n" - "y answer yes\n"); - goto top; - case 'a': - confirmrest = 1; - fprintf(ttyout, "Prompting off for duration of %s.\n", - cmd); - break; - case 'n': - return (0); - case 'p': - interactive = 0; - fputs("Interactive mode: off.\n", ttyout); - break; - case 'q': -quit: - mflag = 0; - clearerr(stdin); - return (0); - case 'y': - return(1); - break; - default: - fprintf(ttyout, "?, a, n, p, q, y " - "are the only acceptable commands!\n"); - goto top; - break; + if (host == NULL) { + warnx("hostname missing"); + return -1; } - return (1); -} -#endif /* !SMALL */ -/* - * 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. - */ -int -globulize(char **cpp) -{ - glob_t gl; - int flags; - - if (!doglob) - return (1); - - flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; - memset(&gl, 0, sizeof(gl)); - if (glob(*cpp, flags, NULL, &gl) || - gl.gl_pathc == 0) { - warnx("%s: not found", *cpp); - globfree(&gl); - return (0); + memset(&hints, 0, sizeof hints); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + if ((error = getaddrinfo(host, port, &hints, &res0))) { + warnx("%s: %s", host, gai_strerror(error)); + return -1; } - /* XXX: caller should check if *cpp changed, and - * free(*cpp) if that is the case - */ - *cpp = strdup(gl.gl_pathv[0]); - if (*cpp == NULL) - err(1, NULL); - globfree(&gl); - return (1); -} -/* - * determine size of remote file - */ -off_t -remotesize(const char *file, int noisy) -{ - int overbose; - off_t size; + if (timeout) { + (void)signal(SIGALRM, tooslow); + alarm(timeout); + } - overbose = verbose; - size = -1; -#ifndef SMALL - if (!debug) -#endif /* !SMALL */ - verbose = -1; - if (command("SIZE %s", file) == COMPLETE) { - char *cp, *ep; + for (res = res0; res; res = res->ai_next) { + if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, + sizeof hbuf, NULL, 0, NI_NUMERICHOST) != 0) + (void)strlcpy(hbuf, "(unknown)", sizeof hbuf); - cp = strchr(reply_string, ' '); - if (cp != NULL) { - cp++; - size = strtoll(cp, &ep, 10); - if (*ep != '\0' && !isspace((unsigned char)*ep)) - size = -1; + log_info("Trying %s...\n", hbuf); + s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s == -1) { + cause = "socket"; + continue; } - } else if (noisy -#ifndef SMALL - && !debug -#endif /* !SMALL */ - ) { - fputs(reply_string, ttyout); - fputc('\n', ttyout); - } - verbose = overbose; - return (size); -} -/* - * determine last modification time (in GMT) of remote file - */ -time_t -remotemodtime(const char *file, int noisy) -{ - int overbose; - time_t rtime; - int ocode; - - overbose = verbose; - ocode = code; - rtime = -1; -#ifndef SMALL - if (!debug) -#endif /* !SMALL */ - verbose = -1; - if (command("MDTM %s", file) == COMPLETE) { - struct tm timebuf; - int yy, mo, day, hour, min, sec; - /* - * time-val = 14DIGIT [ "." 1*DIGIT ] - * YYYYMMDDHHMMSS[.sss] - * mdtm-response = "213" SP time-val CRLF / error-response - */ - /* TODO: parse .sss as well, use timespecs. */ - char *timestr = reply_string; + for (error = connect(s, res->ai_addr, res->ai_addrlen); + error != 0 && errno == EINTR; error = connect_wait(s)) + continue; - /* Repair `19%02d' bug on server side */ - while (!isspace((unsigned char)*timestr)) - timestr++; - while (isspace((unsigned char)*timestr)) - timestr++; - if (strncmp(timestr, "191", 3) == 0) { - fprintf(ttyout, - "Y2K warning! Fixed incorrect time-val received from server.\n"); - timestr[0] = ' '; - timestr[1] = '2'; - timestr[2] = '0'; + if (error != 0) { + cause = "connect"; + save_errno = errno; + close(s); + errno = save_errno; + s = -1; + continue; } - sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo, - &day, &hour, &min, &sec); - memset(&timebuf, 0, sizeof(timebuf)); - timebuf.tm_sec = sec; - timebuf.tm_min = min; - timebuf.tm_hour = hour; - timebuf.tm_mday = day; - timebuf.tm_mon = mo - 1; - timebuf.tm_year = yy - 1900; - timebuf.tm_isdst = -1; - rtime = mktime(&timebuf); - if (rtime == -1 && (noisy -#ifndef SMALL - || debug -#endif /* !SMALL */ - )) - fprintf(ttyout, "Can't convert %s to a time.\n", reply_string); - else - rtime += timebuf.tm_gmtoff; /* conv. local -> GMT */ - } else if (noisy -#ifndef SMALL - && !debug -#endif /* !SMALL */ - ) { - fputs(reply_string, ttyout); - fputc('\n', ttyout); - } - verbose = overbose; - if (rtime == -1) - code = ocode; - return (rtime); -} -/* - * 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], *parentdir; - char realdir[PATH_MAX]; - size_t dirlen; + break; + } - /* determine parent directory of file */ - (void)strlcpy(parentdirbuf, file, sizeof(parentdirbuf)); - parentdir = dirname(parentdirbuf); - if (strcmp(parentdir, ".") == 0) - return 1; /* current directory is ok */ + freeaddrinfo(res0); + if (s == -1) { + warn("%s", cause); + return -1; + } - /* find the directory */ - if (realpath(parentdir, realdir) == NULL) { - warn("Unable to determine real path of `%s'", parentdir); - return 0; + if (timeout) { + signal(SIGALRM, SIG_DFL); + alarm(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; + return s; } - -/* - * Returns true if this is the controlling/foreground process, else false. - */ int -foregroundproc(void) -{ - static pid_t pgrp = -1; - int ctty_pgrp; - - if (pgrp == -1) - pgrp = getpgrp(); - - return((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && - ctty_pgrp == pgrp)); -} - -/* ARGSUSED */ -static void -updateprogressmeter(int signo) +fd_request(char *path, int flags, off_t *offset) { - int save_errno = errno; + struct imsg imsg; + off_t *poffset; + int fd, save_errno; - /* update progressmeter if foreground process or in -m mode */ - if (foregroundproc() || progress == -1) - progressmeter(0, NULL); - errno = save_errno; -} - -/* - * 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 timespec start; - -char *action; - -void -progressmeter(int flag, const char *filename) -{ - /* - * List of order of magnitude prefixes. - * The last is `P', as 2^64 = 16384 Petabytes - */ - static const char prefixes[] = " KMGTP"; - - static struct timespec lastupdate; - static off_t lastsize; - static char *title = NULL; - struct timespec now, td, wait; - off_t cursize, abbrevsize; - double elapsed; - int ratio, barlength, i, remaining, overhead = 30; - char buf[512]; - - if (flag == -1) { - clock_gettime(CLOCK_MONOTONIC, &start); - lastupdate = start; - lastsize = restart_point; - } - clock_gettime(CLOCK_MONOTONIC, &now); - if (!progress || filesize < 0) - return; - cursize = bytes + restart_point; - - if (filesize) - ratio = cursize * 100 / filesize; - else - ratio = 100; - ratio = MAXIMUM(ratio, 0); - ratio = MINIMUM(ratio, 100); - if (!verbose && flag == -1) { - filename = basename(filename); - if (filename != NULL) { - free(title); - title = strdup(filename); - } - } - - buf[0] = 0; - if (!verbose && action != NULL) { - int l = strlen(action); - char *dotdot = ""; - - if (l < 7) - l = 7; - else if (l > 12) { - l = 12; - dotdot = "..."; - overhead += 3; - } - snprintf(buf, sizeof(buf), "\r%-*.*s%s ", l, l, action, - dotdot); - overhead += l + 1; - } else - snprintf(buf, sizeof(buf), "\r"); - - if (!verbose && title != NULL) { - int l = strlen(title); - char *dotdot = ""; - - if (l < 12) - l = 12; - else if (l > 25) { - l = 22; - dotdot = "..."; - overhead += 3; - } - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%-*.*s%s %3d%% ", l, l, title, - dotdot, ratio); - overhead += l + 1; - } else - snprintf(buf, sizeof(buf), "\r%3d%% ", ratio); - - barlength = ttywidth - overhead; - if (barlength > 0) { - i = barlength * ratio / 100; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "|%.*s%*s|", i, - "*******************************************************" - "*******************************************************" - "*******************************************************" - "*******************************************************" - "*******************************************************" - "*******************************************************" - "*******************************************************", - barlength - i, ""); - } - - i = 0; - abbrevsize = cursize; - while (abbrevsize >= 100000 && i < sizeof(prefixes)-1) { - i++; - abbrevsize >>= 10; - } - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " %5lld %c%c ", (long long)abbrevsize, prefixes[i], - prefixes[i] == ' ' ? ' ' : 'B'); - - timespecsub(&now, &lastupdate, &wait); - if (cursize > lastsize) { - lastupdate = now; - lastsize = cursize; - if (wait.tv_sec >= STALLTIME) { /* fudge out stalled time */ - start.tv_sec += wait.tv_sec; - start.tv_nsec += wait.tv_nsec; - } - wait.tv_sec = 0; - } + send_message(&child_ibuf, IMSG_OPEN, flags, path, strlen(path) + 1, -1); + if (read_message(&child_ibuf, &imsg) == 0) + return -1; - timespecsub(&now, &start, &td); - elapsed = td.tv_sec + (td.tv_nsec / 1000000000.0); + if (imsg.hdr.type != IMSG_OPEN) + errx(1, "%s: IMSG_OPEN expected", __func__); - if (flag == 1) { - i = (int)elapsed / 3600; - if (i) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%2d:", i); - else - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " "); - i = (int)elapsed % 3600; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%02d:%02d ", i / 60, i % 60); - } else if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " --:-- ETA"); - } else if (wait.tv_sec >= STALLTIME) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " - stalled -"); - } else { - remaining = (int)((filesize - restart_point) / - (bytes / elapsed) - elapsed); - i = remaining / 3600; - if (i) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%2d:", i); - else - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - " "); - i = remaining % 3600; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - "%02d:%02d ETA", i / 60, i % 60); + fd = imsg.fd; + if (offset) { + poffset = imsg.data; + *offset = *poffset; } - (void)write(fileno(ttyout), buf, strlen(buf)); - if (flag == -1) { - (void)signal(SIGALRM, updateprogressmeter); - alarmtimer(1); /* set alarm timer for 1 Hz */ - } else if (flag == 1) { - alarmtimer(0); - (void)putc('\n', ttyout); - free(title); - title = NULL; - } - fflush(ttyout); + save_errno = imsg.hdr.peerid; + imsg_free(&imsg); + errno = save_errno; + return fd; } -/* - * 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) +send_message(struct imsgbuf *ibuf, int type, uint32_t peerid, + void *msg, size_t msglen, int fd) { - struct timespec now, td; - double elapsed; - off_t bs; - int meg, remaining, hh; - char buf[100]; - - if (!verbose && !siginfo) - return; - - clock_gettime(CLOCK_MONOTONIC, &now); - timespecsub(&now, &start, &td); - elapsed = td.tv_sec + (td.tv_nsec / 1000000000.0); - bs = bytes / (elapsed == 0.0 ? 1 : elapsed); - meg = 0; - if (bs > (1024 * 1024)) - meg = 1; + if (imsg_compose(ibuf, type, peerid, 0, fd, msg, msglen) != 1) + err(1, "imsg_compose"); - /* XXX floating point printf in signal handler */ - (void)snprintf(buf, sizeof(buf), - "%lld byte%s %s in %.2f seconds (%.2f %sB/s)\n", - (long long)bytes, bytes == 1 ? "" : "s", direction, elapsed, - bs / (1024.0 * (meg ? 1024.0 : 1.0)), meg ? "M" : "K"); - - if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0 && - bytes + restart_point <= filesize) { - remaining = (int)((filesize - restart_point) / - (bytes / elapsed) - elapsed); - hh = remaining / 3600; - remaining %= 3600; - - /* "buf+len(buf) -1" to overwrite \n */ - snprintf(buf + strlen(buf) - 1, sizeof(buf) - strlen(buf), - " ETA: %02d:%02d:%02d\n", hh, remaining / 60, - remaining % 60); - } - (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, strlen(buf)); + if (imsg_flush(ibuf) != 0) + err(1, "imsg_flush"); } -/* - * List words in stringlist, vertically arranged - */ -#ifndef SMALL -void -list_vertical(StringList *sl) +int +read_message(struct imsgbuf *ibuf, struct imsg *imsg) { - int i, j, w; - int columns, width, lines; - char *p; + int n; - width = 0; + if ((n = imsg_read(ibuf)) == -1) + err(1, "%s: imsg_read", __func__); + if (n == 0) + return 0; - for (i = 0 ; i < sl->sl_cur ; i++) { - w = strlen(sl->sl_str[i]); - if (w > width) - width = w; - } - width = (width + 8) &~ 7; + if ((n = imsg_get(ibuf, imsg)) == -1) + err(1, "%s: imsg_get", __func__); + if (n == 0) + return 0; - 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; - } - w = strlen(p); - while (w < width) { - w = (w + 8) &~ 7; - (void)putc('\t', ttyout); - } - } - } + return n; } -#endif /* !SMALL */ -/* - * Update the global ttywidth value, using TIOCGWINSZ. - */ -/* ARGSUSED */ void -setttywidth(int signo) +log_info(const char *fmt, ...) { - int save_errno = errno; - struct winsize winsize; + va_list ap; - if (ioctl(fileno(ttyout), TIOCGWINSZ, &winsize) != -1) - ttywidth = winsize.ws_col ? winsize.ws_col : 80; - else - ttywidth = 80; - errno = save_errno; -} - -/* - * Set the SIGALRM interval timer for wait seconds, 0 to disable. - */ -void -alarmtimer(int wait) -{ - int save_errno = errno; - struct itimerval itv; + if (verbose == 0) + return; - itv.it_value.tv_sec = wait; - itv.it_value.tv_usec = 0; - itv.it_interval = itv.it_value; - setitimer(ITIMER_REAL, &itv, NULL); - errno = save_errno; + va_start(ap, fmt); + vfprintf(msgout, fmt, ap); + va_end(ap); } -/* - * Setup or cleanup EditLine structures - */ -#ifndef SMALL void -controlediting(void) +copy_file(FILE *dst, FILE *src, off_t *offset) { - HistEvent hev; + char *tmp_buf; + size_t r; - if (editing && el == NULL && hist == NULL) { - el = el_init(__progname, stdin, ttyout, stderr); /* init editline */ - hist = history_init(); /* init the builtin history */ - history(hist, &hev, 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 function */ - - /* 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 */ - el_set(el, EL_SIGNAL, 1); - } else if (!editing) { - if (hist) { - history_end(hist); - hist = NULL; - } - if (el) { - el_end(el); - el = NULL; - } + tmp_buf = xmalloc(TMPBUF_LEN); + while ((r = fread(tmp_buf, 1, TMPBUF_LEN, src)) != 0 && !interrupted) { + *offset += r; + if (fwrite(tmp_buf, 1, r, dst) != r) + err(1, "%s: fwrite", __func__); } -} -#endif /* !SMALL */ -/* - * Wait for an asynchronous connect(2) attempt to finish. - */ -int -connect_wait(int s) -{ - struct pollfd pfd[1]; - int error = 0; - socklen_t len = sizeof(error); + if (interrupted) { + free(tmp_buf); + return; + } - pfd[0].fd = s; - pfd[0].events = POLLOUT; + if (!feof(src)) + errx(1, "%s: fread", __func__); - if (poll(pfd, 1, -1) == -1) - return -1; - if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) - return -1; - if (error != 0) { - errno = error; - return -1; - } - return 0; + free(tmp_buf); } diff --git a/usr.bin/ftp/xmalloc.c b/usr.bin/ftp/xmalloc.c new file mode 100644 index 00000000000..c934355ce96 --- /dev/null +++ b/usr.bin/ftp/xmalloc.c @@ -0,0 +1,147 @@ +/* $OpenBSD: xmalloc.c,v 1.1 2019/05/12 20:44:39 kmos Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * Versions of malloc and friends that check their results, and never return + * failure (they call fatalx if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "xmalloc.h" + +void * +xmalloc(size_t size) +{ + void *ptr; + + if (size == 0) + errx(1, "xmalloc: zero size"); + ptr = malloc(size); + if (ptr == NULL) + err(1, "xmalloc: allocating %zu bytes", size); + return ptr; +} + +void * +xcalloc(size_t nmemb, size_t size) +{ + void *ptr; + + if (size == 0 || nmemb == 0) + errx(1, "xcalloc: zero size"); + ptr = calloc(nmemb, size); + if (ptr == NULL) + err(1, "xcalloc: allocating %zu * %zu bytes", nmemb, size); + return ptr; +} + +void * +xrealloc(void *ptr, size_t size) +{ + return xreallocarray(ptr, 1, size); +} + +void * +xreallocarray(void *ptr, size_t nmemb, size_t size) +{ + void *new_ptr; + + if (nmemb == 0 || size == 0) + errx(1, "xreallocarray: zero size"); + new_ptr = reallocarray(ptr, nmemb, size); + if (new_ptr == NULL) + err(1, "xreallocarray: allocating %zu * %zu bytes", + nmemb, size); + return new_ptr; +} + +char * +xstrdup(const char *str) +{ + char *cp; + + if ((cp = strdup(str)) == NULL) + err(1, "xstrdup"); + return cp; +} + +char * +xstrndup(const char *str, size_t maxlen) +{ + char *cp; + + if ((cp = strndup(str, maxlen)) == NULL) + err(1, "xstrndup"); + return cp; +} + +int +xasprintf(char **ret, const char *fmt, ...) +{ + va_list ap; + int i; + + va_start(ap, fmt); + i = xvasprintf(ret, fmt, ap); + va_end(ap); + + return i; +} + +int +xvasprintf(char **ret, const char *fmt, va_list ap) +{ + int i; + + i = vasprintf(ret, fmt, ap); + + if (i < 0 || *ret == NULL) + err(1, "xasprintf"); + + return i; +} + +int +xsnprintf(char *str, size_t len, const char *fmt, ...) +{ + va_list ap; + int i; + + va_start(ap, fmt); + i = xvsnprintf(str, len, fmt, ap); + va_end(ap); + + return i; +} + +int +xvsnprintf(char *str, size_t len, const char *fmt, va_list ap) +{ + int i; + + if (len > INT_MAX) + errx(1, "xsnprintf: len > INT_MAX"); + + i = vsnprintf(str, len, fmt, ap); + + if (i < 0 || i >= (int)len) + errx(1, "xsnprintf: overflow"); + + return i; +} diff --git a/usr.bin/ftp/xmalloc.h b/usr.bin/ftp/xmalloc.h new file mode 100644 index 00000000000..8b6c2c0452e --- /dev/null +++ b/usr.bin/ftp/xmalloc.h @@ -0,0 +1,41 @@ +/* $OpenBSD: xmalloc.h,v 1.1 2019/05/12 20:44:39 kmos Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * Created: Mon Mar 20 22:09:17 1995 ylo + * + * Versions of malloc and friends that check their results, and never return + * failure (they call fatal if they encounter an error). + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef XMALLOC_H +#define XMALLOC_H + +void *xmalloc(size_t); +void *xcalloc(size_t, size_t); +void *xrealloc(void *, size_t); +void *xreallocarray(void *, size_t, size_t); +char *xstrdup(const char *); +char *xstrndup(const char *, size_t); +int xasprintf(char **, const char *, ...) + __attribute__((__format__ (printf, 2, 3))) + __attribute__((__nonnull__ (2))); +int xvasprintf(char **, const char *, va_list) + __attribute__((__nonnull__ (2))); +int xsnprintf(char *, size_t, const char *, ...) + __attribute__((__format__ (printf, 3, 4))) + __attribute__((__nonnull__ (3))) + __attribute__((__bounded__ (__string__, 1, 2))); +int xvsnprintf(char *, size_t, const char *, va_list) + __attribute__((__nonnull__ (3))) + __attribute__((__bounded__ (__string__, 1, 2))); + +#endif /* XMALLOC_H */ |