diff options
-rw-r--r-- | utils/ftpcp.c | 336 | ||||
-rw-r--r-- | utils/ftpdir.c | 328 |
2 files changed, 664 insertions, 0 deletions
diff --git a/utils/ftpcp.c b/utils/ftpcp.c new file mode 100644 index 00000000..92bcabf2 --- /dev/null +++ b/utils/ftpcp.c @@ -0,0 +1,336 @@ +/* Copy a file using the ftp protocol + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <error.h> +#include <argp.h> +#include <netdb.h> +#include <fcntl.h> + +#include <version.h> + +#include <ftpconn.h> + +#define COPY_SZ 65536 + +const char *argp_program_version = STANDARD_HURD_VERSION (ftpcp); + +#define OPT_SRC_U -3 +#define OPT_SRC_A -4 +#define OPT_SRC_P -5 +#define OPT_DST_U -6 +#define OPT_DST_A -7 +#define OPT_DST_P -8 + + +static struct argp_option options[] = +{ + {"user", 'u', "USER",0, "User to login as on both ftp servers"}, + {"password", 'p', "PWD", 0, "USER's password"}, + {"account", 'a', "ACCT",0, "Account to login as"}, + {"src-user", OPT_SRC_U, "USER",0, "User to login as on the src ftp server"}, + {"src-password",OPT_SRC_P, "PWD", 0, "The src USER's password"}, + {"src-account", OPT_SRC_A, "ACCT",0, "Account to login as on the source server"}, + {"dst-user", OPT_DST_U, "USER",0, "User to login as on the dst ftp server"}, + {"dst-password",OPT_DST_P, "PWD", 0, "The dst USER's password"}, + {"dst-account", OPT_DST_A, "ACCT",0, "Account to login as on the source server"}, + {"debug", 'D', 0, 0, "Turn on debugging output for ftp connections"}, + {0, 0} +}; +static char *args_doc = "SRC [DST]"; +static char *doc = "Copy file SRC over ftp to DST." +"\vBoth SRC and DST may have the form HOST:FILE, FILE, or -, where - is" +" standard input for SRC or standard output for DST, and FILE is a local" +" file. DST may be a directory, in which case the basename of SRC is" +" appended to make the actual destination filename."; + +/* customization hooks. */ +static struct ftp_conn_hooks conn_hooks = { 0 }; + +static void +cntl_debug (struct ftp_conn *conn, int type, const char *txt) +{ + char *type_str; + + switch (type) + { + case FTP_CONN_CNTL_DEBUG_CMD: type_str = "."; break; + case FTP_CONN_CNTL_DEBUG_REPLY: type_str = "="; break; + default: type_str = "?"; break; + } + + fprintf (stderr, "%s%s\n", type_str, txt); +} + +/* Return an ftp connection for the host NAME using PARAMS. If an error + occurrs, a message is printed the program exits. If CNAME is non-zero, + the host's canonical name, in mallocated storage, is returned in it. */ +struct ftp_conn * +get_host_conn (char *name, struct ftp_conn_params *params, char **cname) +{ + error_t err; + struct hostent *he; + struct ftp_conn *conn; + + he = gethostbyname (name); + if (! he) + error (10, 0, "%s: %s", name, hstrerror (h_errno)); + + params->addr = malloc (he->h_length); + if (! params->addr) + error (11, ENOMEM, "%s", name); + + bcopy (he->h_addr_list[0], params->addr, he->h_length); + params->addr_len = he->h_length; + params->addr_type = he->h_addrtype; + + err = ftp_conn_create (params, &conn_hooks, &conn); + if (err) + error (12, err, "%s", he->h_name); + + if (cname) + *cname = strdup (he->h_name); + + return conn; +} + +static void +cp (int src, const char *src_name, int dst, const char *dst_name) +{ + ssize_t rd; + static void *copy_buf = 0; + + if (! copy_buf) + { + copy_buf = valloc (COPY_SZ); + if (! copy_buf) + error (13, ENOMEM, "Cannot allocate copy buffer"); + } + + while ((rd = read (src, copy_buf, COPY_SZ)) > 0) + do + { + int wr = write (dst, copy_buf, rd); + if (wr < 0) + error (14, errno, "%s", dst_name); + rd -= wr; + } + while (rd > 0); + + if (rd != 0) + error (15, errno, "%s", src_name); +} + +struct epoint +{ + char *name; /* Name, of the form HOST:FILE, FILE, or -. */ + char *file; /* If remote, the FILE portion, or 0. */ + int fd; /* A file descriptor to use. */ + struct ftp_conn *conn; /* An ftp connection to use. */ + struct ftp_conn_params params; +}; + +static void +econnect (struct epoint *e, struct ftp_conn_params *def_params) +{ + char *rmt; + + if (! e->name) + e->name = "-"; + + rmt = strchr (e->name, ':'); + if (rmt) + { + error_t err; + + *rmt++ = 0; + + if (! e->params.user) + e->params.user = def_params->user; + if (! e->params.pass) + e->params.pass = def_params->pass; + if (! e->params.acct) + e->params.acct = def_params->acct; + + e->conn = get_host_conn (e->name, &e->params, &e->name); + e->name = realloc (e->name, strlen (e->name) + 1 + strlen (rmt) + 1); + if (! e->name) + error (22, ENOMEM, "Cannot allocate name storage"); + + err = ftp_conn_set_type (e->conn, "I"); + if (err) + error (23, err, "%s: Cannot set connection type to binary", + e->name); + + strcat (e->name, ":"); + strcat (e->name, rmt); + + e->file = rmt; + } + else if (e->params.user || e->params.pass || e->params.acct) + error (20, 0, + "%s: Ftp login parameter specified for a local endpoint (%s,%s,%s)", + e->name, e->params.user, e->params.pass, e->params.acct); + else + e->file = strdup (e->name); +} + +static error_t +eopen_wr (struct epoint *e, int *fd) +{ + if (e->conn) + return ftp_conn_start_store (e->conn, e->file, fd); + else if (strcmp (e->name, "-") == 0) + *fd = 1; + else + { + *fd = open (e->name, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (*fd < 0) + return errno; + } + return 0; +} + +static error_t +eopen_rd (struct epoint *e, int *fd) +{ + if (e->conn) + return ftp_conn_start_retrieve (e->conn, e->file, fd); + else if (strcmp (e->name, "-") == 0) + *fd = 0; + else + { + *fd = open (e->name, O_RDONLY, 0666); + if (*fd < 0) + return errno; + } + return 0; +} + +static void +efinish (struct epoint *e) +{ + if (e->conn) + { + error_t err = ftp_conn_finish_transfer (e->conn); + if (err) + error (31, err, "%s", e->name); + } +} + +int +main (int argc, char **argv) +{ + error_t err; + struct epoint rd = { 0 }, wr = { 0 }; + struct ftp_conn_params def_params = { 0 }; /* default params */ + + /* Parse our options... */ + error_t parse_opt (int key, char *arg, struct argp_state *state) + { + switch (key) + { + case ARGP_KEY_ARG: + switch (state->arg_num) + { + case 0: rd.name = arg; break; + case 1: wr.name = arg; break; + default: return ARGP_ERR_UNKNOWN; + } + break; + case ARGP_KEY_NO_ARGS: + argp_usage (state); + + case 'u': def_params.user = arg; break; + case 'p': def_params.pass = arg; break; + case 'a': def_params.acct = arg; break; + + case OPT_SRC_U: rd.params.user = arg; break; + case OPT_SRC_P: rd.params.pass = arg; break; + case OPT_SRC_A: rd.params.acct = arg; break; + + case OPT_DST_U: wr.params.user = arg; break; + case OPT_DST_P: wr.params.pass = arg; break; + case OPT_DST_A: wr.params.acct = arg; break; + + case 'D': conn_hooks.cntl_debug = cntl_debug; break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + struct argp argp = {options, parse_opt, args_doc, doc}; + + argp_parse (&argp, argc, argv, 0, 0, 0); + + econnect (&rd, &def_params); + econnect (&wr, &def_params); + + if (rd.conn && wr.conn) + { + err = ftp_conn_rmt_copy (rd.conn, rd.file, wr.conn, wr.file); + if (err) + error (30, err, "Remote copy"); + } + else + { + int rd_fd, wr_fd; + + err = eopen_rd (&rd, &rd_fd); + if (err) + error (31, err, "%s", rd.name); + + err = eopen_wr (&wr, &wr_fd); + if (err == EISDIR) + /* The destination name is a directory; try again with the source + basename appended. */ + { + char *bname = basename (rd.file); + size_t bname_len = strlen (bname); + char *dir = wr.file; + char *name = malloc (strlen (dir) + 1 + bname_len + 1); + + if (! name) + error (99, ENOMEM, "%s", dir); + + strcpy (name, dir); + strcat (name, "/"); + strcat (name, bname); + wr.file = name; + + err = eopen_wr (&wr, &wr_fd); + } + if (err) + error (32, err, "%s", wr.name); + + cp (rd_fd, rd.name, wr_fd, wr.name); + + close (rd_fd); + close (wr_fd); + + efinish (&rd); + efinish (&wr); + } + + exit (0); +} diff --git a/utils/ftpdir.c b/utils/ftpdir.c new file mode 100644 index 00000000..b7bcce2b --- /dev/null +++ b/utils/ftpdir.c @@ -0,0 +1,328 @@ +/* Get a directory listing using the ftp protocol + + Copyright (C) 1997 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <unistd.h> +#include <string.h> +#include <error.h> +#include <argp.h> +#include <time.h> +#include <netdb.h> + +#include <version.h> + +#include <ftpconn.h> + +#define COPY_SZ 65536 + +const char *argp_program_version = STANDARD_HURD_VERSION (ftpdir); + +static struct argp_option options[] = +{ + {"user", 'u', "USER",0, "User to login as on ftp server"}, + {"password", 'p', "PWD", 0, "USER's password"}, + {"account", 'a', "ACCT",0, "Account to login as"}, + {"separator",'S', "SEP", 0, "String to separate multiple listings"}, + {"prefix", 'P', "PFX", 0, "String to proceed listings; the first and second" + " occurances of %s are replace by HOST and DIR"}, + {"host", 'h', "HOST",0, "Use HOST as a default host"}, + {"debug", 'D', 0, 0, "Turn on debugging output for ftp connections"}, + {"intepret", 'i', 0, 0, "Parse the directory output"}, + {0, 0} +}; +static char *args_doc = "[([HOST:]DIR | HOST:)...]"; +static char *doc = "Get a directory listing over ftp from HOST:DIR." +"\vIf HOST is not supplied in an argument any default value set by --host is" +" used; if DIR is not supplied, the default directory of HOST is used." +"\nIf multiple DIRs are supplied on the command line, each listing is" +" prefixed by a newline (or SEP) and a line containing HOST:DIR: (or PFX)."; + +/* customization hooks. */ +static struct ftp_conn_hooks conn_hooks = { 0 }; + +static void +cntl_debug (struct ftp_conn *conn, int type, const char *txt) +{ + char *type_str; + + switch (type) + { + case FTP_CONN_CNTL_DEBUG_CMD: type_str = "."; break; + case FTP_CONN_CNTL_DEBUG_REPLY: type_str = "="; break; + default: type_str = "?"; break; + } + + fprintf (stderr, "%s%s\n", type_str, txt); +} + +struct ftpdir_host +{ + char *name; + struct ftp_conn_params params; + struct ftp_conn *conn; + struct ftpdir_host *next; +}; + +/* Return an ftp connection for the host NAME using PARAMS, and add an entry + for it to *HOSTS. If a connection already exists in HOSTS, it is returned + instead of making a new one. If an error occurrs, a message is printed and + 0 is returned. */ +static struct ftpdir_host * +get_host_conn (char *name, struct ftp_conn_params *params, + struct ftpdir_host **hosts) +{ + error_t err; + struct ftpdir_host *h; + struct hostent *he; + + for (h = *hosts; h; h = h->next) + if (strcmp (h->name, name) == 0) + return h; + + he = gethostbyname (name); + if (! he) + { + error (0, 0, "%s: %s", name, hstrerror (h_errno)); + return 0; + } + + for (h = *hosts; h; h = h->next) + if (he->h_addrtype == h->params.addr_type + && he->h_length == h->params.addr_len + && bcmp (he->h_addr_list[0], h->params.addr, he->h_length) == 0) + return h; + + h = malloc (sizeof (struct ftpdir_host)); + if (! h) + { + error (0, ENOMEM, "%s", name); + return 0; + } + + h->params = *params; + h->params.addr = malloc (he->h_length); + h->name = strdup (he->h_name); + + if (!h->name || !h->params.addr) + err = ENOMEM; + else + { + bcopy (he->h_addr_list[0], h->params.addr, he->h_length); + h->params.addr_len = he->h_length; + h->params.addr_type = he->h_addrtype; + err = ftp_conn_create (&h->params, &conn_hooks, &h->conn); + } + + if (err) + { + error (0, err, "%s", he->h_name); + if (h->name) + free (h->name); + if (h->params.addr) + free (h->params.addr); + free (h); + return 0; + } + + h->next = *hosts; + *hosts = h; + + return h; +} + +static int +ftpdir (char *dir, struct ftpdir_host *host) +{ + int data; + int rd; + error_t err; + static void *copy_buf = 0; + struct ftp_conn *conn = host->conn; + char *host_name = host->name; + + err = ftp_conn_start_dir (conn, dir, &data); + if (err) + { + error (0, err, "%s:%s", host_name, dir); + return err; + } + + if (! copy_buf) + { + copy_buf = valloc (COPY_SZ); + if (! copy_buf) + error (12, ENOMEM, "Cannot allocate copy buffer"); + } + + while ((rd = read (data, copy_buf, COPY_SZ)) > 0) + do + { + int wr = write (1, copy_buf, rd); + if (wr < 0) + error (13, errno, "stdout"); + rd -= wr; + } + while (rd > 0); + if (rd != 0) + { + error (0, errno, "%s:%s", host_name, dir); + return errno; + } + + close (data); + + err = ftp_conn_finish_transfer (conn); + if (err) + { + error (0, err, "%s:%s", host_name, dir); + return err; + } + + return 0; +} + +static error_t +pdirent (const char *name, const struct stat *st, const char *symlink_target, + void *hook) +{ + char timebuf[20]; + strftime (timebuf, sizeof timebuf, "%Y-%m-%d %H:%M", localtime (&st->st_mtime)); + printf ("%6o %2d %5d %5d %6ld %s %s\n", + st->st_mode, st->st_nlink, st->st_uid, st->st_gid, st->st_size, + timebuf, name); + if (symlink_target) + printf (" -> %s\n", + symlink_target); + return 0; +} + +static error_t +ftpdir2 (char *dir, struct ftpdir_host *host) +{ + error_t err = ftp_conn_get_stats (host->conn, dir, 1, pdirent, 0); + if (err == ENOTDIR) + err = ftp_conn_get_stats (host->conn, dir, 0, pdirent, 0); + if (err) + error (0, err, "%s:%s", host->name, dir); + return err; +} + +int +main (int argc, char **argv) +{ + struct ftpdir_host *hosts = 0; + char *default_host = 0; + int interpret = 0; + struct ftp_conn_params params = { 0 }; + char *sep = "\n"; + char *pfx = "%s:%s:\n"; + int use_pfx = 0; + int errs = 0; + + /* Parse our options... */ + error_t parse_opt (int key, char *arg, struct argp_state *state) + { + switch (key) + { + case ARGP_KEY_ARG: + { + char *host, *dir; + + if (state->next < state->argc) + use_pfx = 1; /* Multiple arguments. */ + + dir = index (arg, ':'); + if (dir) + { + host = arg; + *dir++ = '\0'; + if (*host == '\0') + /* An argument of `:' */ + host = default_host; + } + else + { + host = default_host; + dir = arg; + } + + if (host) + { + struct ftpdir_host *h = get_host_conn (host, ¶ms, &hosts); + if (h) + { + if (state->arg_num > 0) + fputs (sep, stdout); + if (use_pfx) + printf (pfx, h->name, dir); + if (interpret) + errs |= ftpdir2 (dir, h); + else + errs |= ftpdir (dir, h); + } + errs = 1; + } + else + { + error (0, 0, "%s: No default host", arg); + errs = 1; + } + } + break; + + case ARGP_KEY_NO_ARGS: + if (default_host) + { + struct ftpdir_host *h = + get_host_conn (default_host, ¶ms, &hosts); + if (h) + errs |= ftpdir (0, h); + } + else + { + error (0, 0, "No default host"); + errs = 1; + } + break; + + return EINVAL; + + case 'u': params.user = arg; break; + case 'p': params.pass = arg; break; + case 'a': params.acct = arg; break; + case 'h': default_host = arg; break; + case 'D': conn_hooks.cntl_debug = cntl_debug; break; + case 'P': pfx = arg; use_pfx = 1; break; + case 'S': sep = arg; break; + case 'i': interpret = 1; break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + struct argp argp = {options, parse_opt, args_doc, doc}; + + argp_parse (&argp, argc, argv, 0, 0, 0); + + if (errs) + exit (10); + else + exit (0); +} |