[BACK]Return to cmd.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ftp

File: [local] / src / usr.bin / ftp / Attic / cmd.c (download)

Revision 1.3, Wed May 15 13:42:40 2019 UTC (5 years ago) by florian
Branch: MAIN
Changes since 1.2: +7 -3 lines

Display "bytes received" like the csrg ftp used to when the progressbar
is disabled.
Difference pointed out by deraadt
OK kurtm, sunil

/*	$OpenBSD: cmd.c,v 1.3 2019/05/15 13:42:40 florian Exp $ */

/*
 * 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;
	}

	init_stats(file_sz, &offset);
	if (progressmeter) {
		p = basename(remote_fname);
		start_progress_meter(p, NULL);
	}

	copy_file(dst_fp, data_fp, &offset);
	if (progressmeter)
		stop_progress_meter();
	finish_stats();

	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;
	}

	init_stats(file_sz, &offset);
	if (progressmeter) {
		p = basename(remote_fname);
		start_progress_meter(p, NULL);
	}

	copy_file(data_fp, src_fp, &offset);
	if (progressmeter)
		stop_progress_meter();
	finish_stats();

	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);
	}
}