summaryrefslogtreecommitdiff
path: root/usr.bin/ftp
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/ftp')
-rw-r--r--usr.bin/ftp/Makefile9
-rw-r--r--usr.bin/ftp/cmd.c637
-rw-r--r--usr.bin/ftp/cmds.c1688
-rw-r--r--usr.bin/ftp/cmds.h83
-rw-r--r--usr.bin/ftp/cmdtab.c215
-rw-r--r--usr.bin/ftp/complete.c381
-rw-r--r--usr.bin/ftp/cookie.c231
-rw-r--r--usr.bin/ftp/domacro.c149
-rw-r--r--usr.bin/ftp/extern.h150
-rw-r--r--usr.bin/ftp/fetch.c1668
-rw-r--r--usr.bin/ftp/file.c55
-rw-r--r--usr.bin/ftp/ftp.11634
-rw-r--r--usr.bin/ftp/ftp.c2302
-rw-r--r--usr.bin/ftp/ftp.h116
-rw-r--r--usr.bin/ftp/ftp_var.h231
-rw-r--r--usr.bin/ftp/http.c784
-rw-r--r--usr.bin/ftp/list.c86
-rw-r--r--usr.bin/ftp/main.c1174
-rw-r--r--usr.bin/ftp/pathnames.h37
-rw-r--r--usr.bin/ftp/progressmeter.c353
-rw-r--r--usr.bin/ftp/ruserpass.c317
-rw-r--r--usr.bin/ftp/small.c730
-rw-r--r--usr.bin/ftp/small.h35
-rw-r--r--usr.bin/ftp/stringlist.c97
-rw-r--r--usr.bin/ftp/stringlist.h56
-rw-r--r--usr.bin/ftp/url.c417
-rw-r--r--usr.bin/ftp/util.c1160
-rw-r--r--usr.bin/ftp/xmalloc.c147
-rw-r--r--usr.bin/ftp/xmalloc.h41
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 < &current_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 */