summaryrefslogtreecommitdiff
path: root/libftpconn
diff options
context:
space:
mode:
Diffstat (limited to 'libftpconn')
-rw-r--r--libftpconn/addr.c74
-rw-r--r--libftpconn/cmd.c168
-rw-r--r--libftpconn/create.c86
-rw-r--r--libftpconn/cwd.c107
-rw-r--r--libftpconn/errs.c32
-rw-r--r--libftpconn/names.c209
-rw-r--r--libftpconn/open.c239
-rw-r--r--libftpconn/priv.h89
-rw-r--r--libftpconn/reply.c212
-rw-r--r--libftpconn/rmt.c90
-rw-r--r--libftpconn/set-type.c59
-rw-r--r--libftpconn/stats.c85
-rw-r--r--libftpconn/xfer.c130
-rw-r--r--libftpconn/xinl.c24
14 files changed, 1604 insertions, 0 deletions
diff --git a/libftpconn/addr.c b/libftpconn/addr.c
new file mode 100644
index 00000000..c2ff01ac
--- /dev/null
+++ b/libftpconn/addr.c
@@ -0,0 +1,74 @@
+/* Send/receive data-connection addresses
+
+ 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 <errno.h>
+#include <netinet/in.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+error_t
+ftp_conn_get_pasv_addr (struct ftp_conn *conn, struct sockaddr **addr)
+{
+ int reply;
+ const char *txt;
+ error_t err = ftp_conn_cmd_reopen (conn, "pasv", 0, &reply, &txt);
+
+ if (! err)
+ if (reply == REPLY_PASV_OK)
+ err = (*(conn->syshooks.pasv_addr ?: ftp_conn_unix_pasv_addr)) (conn, txt, addr);
+ else
+ err = unexpected_reply (conn, reply, txt, 0);
+
+ return err;
+}
+
+error_t
+ftp_conn_send_actv_addr (struct ftp_conn *conn, struct sockaddr *addr)
+{
+ error_t err;
+
+ if (addr == 0)
+ err = EINVAL;
+ else if (addr->sa_family != AF_INET)
+ err = EAFNOSUPPORT;
+ else
+ {
+ char buf[50];
+ int reply;
+ unsigned char *a =
+ (unsigned char *)&((struct sockaddr_in *)addr)->sin_addr.s_addr;
+ unsigned char *p =
+ (unsigned char *)&((struct sockaddr_in *)addr)->sin_port;
+
+ snprintf (buf, sizeof buf, "%d,%d,%d,%d,%d,%d",
+ a[0], a[1], a[2], a[3], p[0], p[1]);
+ err = ftp_conn_cmd_reopen (conn, "port", buf, &reply, 0);
+
+ if (! err)
+ if (reply == REPLY_OK)
+ err = 0;
+ else
+ err = unexpected_reply (conn, reply, 0, 0);
+ }
+
+ return err;
+}
diff --git a/libftpconn/cmd.c b/libftpconn/cmd.c
new file mode 100644
index 00000000..1721264c
--- /dev/null
+++ b/libftpconn/cmd.c
@@ -0,0 +1,168 @@
+/* Send commands to the ftp server
+
+ 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 <errno.h>
+#include <arpa/telnet.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Version of write that writes all LEN bytes of BUF if possible to FD. */
+static error_t
+_write (int fd, const void *buf, size_t len)
+{
+ while (len > 0)
+ {
+ ssize_t wr = write (fd, buf, len);
+ if (wr < 0)
+ return errno;
+ else if (wr == 0)
+ return EPIPE;
+ buf += wr;
+ len -= wr;
+ }
+ return 0;
+}
+
+static error_t
+_skip_write (int fd, const void *buf, size_t len, size_t *skip)
+{
+ size_t sk = *skip;
+ error_t err = 0;
+
+ if (len > sk)
+ {
+ err = _write (fd, buf + sk, len - sk);
+ *skip = 0;
+ }
+ else
+ *skip = sk - len;
+
+ return err;
+}
+
+/* Ridiculous function to deal with the never-to-occur case of the ftp
+ command being too long for the buffer in ftp_conn_cmd; just writes the
+ portion of the command that wasn't written there. */
+static error_t
+_long_cmd (int fd, const char *cmd, const char *arg, size_t skip)
+{
+ error_t err = _skip_write (fd, cmd, strlen (cmd), &skip);
+ if (!err && arg)
+ {
+ err = _skip_write (fd, " ", 1, &skip);
+ if (! err)
+ err = _skip_write (fd, arg, strlen (arg), &skip);
+ }
+ if (! err)
+ err = _skip_write (fd, "\r\n", 2, &skip);
+ return err;
+}
+
+/* Send the ftp command CMD, with optional argument ARG (if non-zero) to
+ CONN's ftp server. If either of REPLY or REPLY_TXT is non-zero, then a
+ reply is waited for and returned as with ftp_conn_get_reply, otherwise
+ the next reply from the server is left unconsumed. */
+error_t
+ftp_conn_cmd (struct ftp_conn *conn, const char *cmd, const char *arg,
+ int *reply, const char **reply_txt)
+{
+ error_t err = 0;
+
+ if (conn->control < 0)
+ err = EPIPE;
+ else
+ /* (This used to try to call dprintf to output to conn->control, but that
+ function doesn't appear to work.) */
+ {
+ char buf[200];
+ size_t out =
+ snprintf (buf, sizeof buf, arg ? "%s %s\r\n" : "%s\r\n", cmd, arg);
+ err = _write (conn->control, buf, out);
+
+ if (!err && conn->hooks && conn->hooks->cntl_debug)
+ {
+ buf[out - 2] = '\0'; /* Stomp the CR & NL. */
+ (* conn->hooks->cntl_debug) (conn, FTP_CONN_CNTL_DEBUG_CMD, buf);
+ }
+
+ if (!err && out == sizeof buf)
+ err = _long_cmd (conn->control, cmd, arg, sizeof buf);
+ }
+
+ if (!err && (reply || reply_txt))
+ err = ftp_conn_get_reply (conn, reply, reply_txt);
+
+ return err;
+}
+
+/* Send an ftp command to CONN's server, and optionally await a reply as with
+ ftp_conn_cmd, but also open a new connection if it appears that the old
+ one has died (as when the ftp server times it out). */
+error_t
+ftp_conn_cmd_reopen (struct ftp_conn *conn, const char *cmd, const char *arg,
+ int *reply, const char **reply_txt)
+{
+ int _reply;
+ error_t err;
+
+ err = ftp_conn_cmd (conn, cmd, arg, &_reply, reply_txt);
+ if (err == EPIPE || (!err && _reply == REPLY_CLOSED))
+ /* Retry once after reopening the connection. */
+ {
+ err = ftp_conn_open (conn);
+ if (! err)
+ err = ftp_conn_cmd (conn, cmd, arg, reply, reply_txt);
+ }
+ else if (reply)
+ *reply = _reply;
+
+ return err;
+}
+
+/* Send an ftp ABOR command to CONN's server, aborting any transfer in
+ progress. */
+void
+ftp_conn_abort (struct ftp_conn *conn)
+{
+ if (conn->control >= 0)
+ {
+ static const char ip[] = { IAC, IP, IAC };
+ static const char abor[] = { DM, 'a', 'b', 'o', 'r', '\r', '\n' };
+
+ if (conn->hooks && conn->hooks->cntl_debug)
+ (* conn->hooks->cntl_debug) (conn, FTP_CONN_CNTL_DEBUG_CMD, "abor");
+
+ if (send (conn->control, ip, sizeof ip, MSG_OOB) == sizeof ip
+ && write (conn->control, abor, sizeof abor) == sizeof abor)
+ {
+ int reply;
+ error_t err;
+ do
+ err = ftp_conn_get_reply (conn, &reply, 0);
+ while (reply == REPLY_ABORTED);
+ if (reply != REPLY_TRANS_OK)
+ ftp_conn_close (conn);
+ }
+ else
+ ftp_conn_close (conn);
+ }
+}
diff --git a/libftpconn/create.c b/libftpconn/create.c
new file mode 100644
index 00000000..93d9e57e
--- /dev/null
+++ b/libftpconn/create.c
@@ -0,0 +1,86 @@
+/* Create a new ftp connection
+
+ 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 <errno.h>
+#include <string.h>
+
+#include <ftpconn.h>
+
+/* Create a new ftp connection as specified by PARAMS, and return it in CONN;
+ HOOKS contains customization hooks used by the connection. Neither PARAMS
+ nor HOOKS is copied, so a copy of it should be made if necessary before
+ calling this function; if it should be freed later, a FINI hook may be
+ used to do so. */
+error_t
+ftp_conn_create (const struct ftp_conn_params *params,
+ const struct ftp_conn_hooks *hooks,
+ struct ftp_conn **conn)
+{
+ error_t err;
+ struct ftp_conn *new = malloc (sizeof (struct ftp_conn));
+
+ if (! new)
+ return ENOMEM;
+
+ new->control = -1;
+ new->line = 0;
+ new->line_sz = 0;
+ new->line_offs = 0;
+ new->line_len = 0;
+ new->reply_txt = 0;
+ new->reply_txt_sz = 0;
+ new->params = params;
+ new->hooks = hooks;
+ new->cwd = 0;
+ new->type = 0;
+ bzero (&new->syshooks, sizeof new->syshooks);
+
+ if (new->hooks && new->hooks->init)
+ err = (*new->hooks->init) (new);
+ else
+ err = 0;
+
+ if (! err)
+ err = ftp_conn_open (new);
+
+ if (err)
+ ftp_conn_free (new);
+ else
+ *conn = new;
+
+ return err;
+
+}
+
+/* Free the ftp connection CONN, closing it first, and freeing all resources
+ it uses. */
+void
+ftp_conn_free (struct ftp_conn *conn)
+{
+ ftp_conn_close (conn);
+ if (conn->hooks && conn->hooks->fini)
+ (* conn->hooks->fini) (conn);
+ if (conn->line)
+ free (conn->line);
+ if (conn->reply_txt)
+ free (conn->reply_txt);
+ free (conn);
+}
diff --git a/libftpconn/cwd.c b/libftpconn/cwd.c
new file mode 100644
index 00000000..89156a58
--- /dev/null
+++ b/libftpconn/cwd.c
@@ -0,0 +1,107 @@
+/* Get/set connection current working directory
+
+ 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 <errno.h>
+#include <string.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+static error_t
+_cache_cwd (struct ftp_conn *conn, int reopen)
+{
+ int reply;
+ const char *txt;
+ error_t err =
+ (reopen ? ftp_conn_cmd_reopen : ftp_conn_cmd) (conn, "pwd", 0, &reply, &txt);
+
+ if (! err)
+ if (reply == REPLY_DIR_NAME)
+ {
+ char *cwd = malloc (strlen (txt));
+ if (! cwd)
+ err = ENOMEM;
+ else if (sscanf (txt, "\"%[^\"]\"", cwd) != 1)
+ err = EGRATUITOUS;
+ else
+ {
+ if (conn->cwd)
+ free (conn->cwd);
+ conn->cwd = cwd;
+ }
+ }
+ else
+ err = unexpected_reply (conn, reply, txt, 0);
+
+ return err;
+}
+
+/* Return a malloced string containing CONN's working directory in CWD. */
+error_t
+ftp_conn_get_cwd (struct ftp_conn *conn, char **cwd)
+{
+ error_t err = 0;
+ if (! conn->cwd)
+ err = _cache_cwd (conn, 1);
+ if (! err)
+ {
+ *cwd = strdup (conn->cwd);
+ if (! *cwd)
+ err = ENOMEM;
+ }
+ return err;
+}
+
+/* Return a malloced string containing CONN's working directory in CWD. */
+error_t
+ftp_conn_cwd (struct ftp_conn *conn, const char *cwd)
+{
+ error_t err = 0;
+ if (conn->cwd && strcmp (conn->cwd, cwd) == 0)
+ err = 0;
+ else
+ {
+ int reply;
+ const char *txt;
+ err = ftp_conn_cmd_reopen (conn, "cwd", cwd, &reply, &txt);
+ if (! err)
+ if (reply == REPLY_FCMD_OK)
+ err = _cache_cwd (conn, 0);
+ else
+ err = unexpected_reply (conn, reply, txt, ftp_conn_poss_file_errs);
+ }
+ return err;
+}
+
+/* Return a malloced string containing CONN's working directory in CWD. */
+error_t
+ftp_conn_cdup (struct ftp_conn *conn)
+{
+ int reply;
+ const char *txt;
+ error_t err = ftp_conn_cmd_reopen (conn, "cdup", 0, &reply, &txt);
+ if (! err)
+ if (reply == REPLY_OK)
+ err = _cache_cwd (conn, 0);
+ else
+ err = unexpected_reply (conn, reply, txt, ftp_conn_poss_file_errs);
+ return err;
+}
diff --git a/libftpconn/errs.c b/libftpconn/errs.c
new file mode 100644
index 00000000..5a93fb55
--- /dev/null
+++ b/libftpconn/errs.c
@@ -0,0 +1,32 @@
+/* Error codes we think may result from file operations we do
+
+ 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 <errno.h>
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Error codes we think may result from file operations we do. */
+const error_t
+ftp_conn_poss_file_errs[] =
+{
+ EIO, ENOENT, EPERM, EACCES, ENOTDIR, ENAMETOOLONG, ELOOP, EISDIR, EROFS,
+ EMFILE, ENFILE, ENXIO, EOPNOTSUPP, ENOSPC, EDQUOT, ETXTBSY, EEXIST,
+ 0
+};
diff --git a/libftpconn/names.c b/libftpconn/names.c
new file mode 100644
index 00000000..ced741f6
--- /dev/null
+++ b/libftpconn/names.c
@@ -0,0 +1,209 @@
+/* Fetch directory file names
+
+ 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 <ftpconn.h>
+
+struct get_names_state
+{
+ char *name; /* Last read (maybe partial) name. */
+ size_t name_len; /* Valid length of NAME, *not including* '\0'. */
+ size_t name_alloced; /* Allocated size of NAME (>= NAME_LEN). */
+ int name_partial; /* True if NAME isn't complete. */
+
+ size_t buf_len; /* Length of contents in BUF. */
+ char buf[7000];
+};
+
+/* Start an operation to get a list of filenames in the directory NAME, and
+ return a file-descriptor for reading on, and a state structure in STATE
+ suitable for passing to cont_get_names. */
+error_t
+ftp_conn_start_get_names (struct ftp_conn *conn,
+ const char *name, int *fd, void **state)
+{
+ error_t err;
+ struct get_names_state *s = malloc (sizeof (struct get_names_state));
+
+ if (! s)
+ return ENOMEM;
+
+ err = ftp_conn_start_list (conn, name, fd);
+
+ if (err)
+ free (s);
+ else
+ {
+ s->name = 0;
+ s->name_len = s->name_alloced = 0;
+ s->name_partial = 0;
+ s->buf_len = 0;
+ *state = s;
+ }
+
+ return err;
+}
+
+/* Read filenames from FD, calling ADD_NAME for each new NAME (HOOK is passed
+ to ADD_NAME). FD and STATE should be returned from start_get_names. If
+ this function returns EAGAIN, then it should be called again to finish the
+ job (possibly after calling select on FD); if it returns 0, then it is
+ finishe,d and FD and STATE are deallocated. */
+error_t
+ftp_conn_cont_get_names (struct ftp_conn *conn, int fd, void *state,
+ ftp_conn_add_name_fun_t add_name, void *hook)
+{
+ char *p, *nl;
+ ssize_t rd;
+ size_t name_len;
+ error_t err = 0;
+ struct get_names_state *s = state;
+
+ /* We always consume full lines, so we know that we have to read more when
+ we first get called. */
+ rd = read (fd, s->buf + s->buf_len, sizeof (s->buf) - s->buf_len);
+ if (rd < 0)
+ {
+ err = errno;
+ goto finished;
+ }
+
+ if (rd == 0)
+ /* EOF */
+ if (s->buf_len == 0)
+ /* We're done! Clean up and return the result in NAMES. */
+ goto finished;
+ else
+ /* Partial line at end of file? */
+ nl = s->buf + s->buf_len;
+ else
+ /* Look for a new line in what we read (we know that there weren't any in
+ the buffer before that). */
+ {
+ nl = memchr (s->buf + s->buf_len, '\n', rd);
+ s->buf_len += rd;
+ }
+
+ if (!nl && s->buf_len < sizeof (s->buf))
+ /* We didn't find any newlines (which implies we didn't hit EOF), and we
+ still have room to grow the buffer, so just wait until next time to do
+ anything. */
+ return EAGAIN;
+
+ /* Where we start parsing. */
+ p = s->buf;
+
+ do
+ {
+ /* Fill in S->name, possibly extending it from a previous buffer. */
+ name_len = (nl ? nl - p : s->buf + s->buf_len - p);
+ if (name_len > 0 && p[name_len - 1] == '\r')
+ name_len--;
+ if (name_len > 0)
+ /* Extending s->name. */
+ {
+ size_t old_len = s->name_len;
+ size_t total_len = old_len + name_len + 1;
+
+ if (total_len > s->name_alloced)
+ {
+ char *new_name = realloc (s->name, total_len);
+ if (! new_name)
+ goto enomem;
+ s->name = new_name;
+ s->name_alloced = total_len;
+ }
+
+ strncpy (s->name + old_len, p, name_len);
+ s->name[old_len + name_len] = '\0';
+ s->name_len = total_len - 1;
+ }
+
+ if (nl)
+ {
+ /* Call the callback function to process the current entry. */
+ err = (*add_name) (s->name, hook);
+ if (err)
+ goto finished;
+
+ s->name_len = 0;
+ s->name_partial = 0;
+
+ p = nl + 1;
+ nl = memchr (p, '\n', s->buf + s->buf_len - p);
+ }
+ else
+ /* We found no newline, so the name extends past what we read; we'll
+ try to read more next time. */
+ {
+ s->name_partial = 1;
+ /* Skip over the partial name for the next iteration. */
+ p += name_len;
+ }
+ }
+ while (nl);
+
+ /* Move any remaining characters in the buffer to the beginning for the
+ next call. */
+ s->buf_len -= (p - s->buf);
+ if (s->buf_len > 0)
+ memmove (s->buf, p, s->buf_len);
+
+ /* Try again later. */
+ return EAGAIN;
+
+enomem:
+ /* Some memory allocation failed. */
+ err = ENOMEM;
+
+finished:
+ /* We're finished (with an error if ERR != 0), deallocate everything &
+ return. */
+ if (s->name)
+ free (s->name);
+ free (s);
+ close (fd);
+
+ return err;
+}
+
+/* Get a list of names in the directory NAME, calling ADD_NAME for each one
+ (HOOK is passed to ADD_NAME). This function may block. */
+error_t
+ftp_conn_get_names (struct ftp_conn *conn, const char *name,
+ ftp_conn_add_name_fun_t add_name, void *hook)
+{
+ int fd;
+ void *state;
+ error_t err = ftp_conn_start_get_names (conn, name, &fd, &state);
+
+ if (err)
+ return err;
+
+ do
+ err = ftp_conn_cont_get_names (conn, fd, state, add_name, hook);
+ while (err == EAGAIN);
+
+ return err;
+}
+
diff --git a/libftpconn/open.c b/libftpconn/open.c
new file mode 100644
index 00000000..cfc93f2d
--- /dev/null
+++ b/libftpconn/open.c
@@ -0,0 +1,239 @@
+/* Connection initiation
+
+ 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 <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+static error_t
+ftp_conn_login (struct ftp_conn *conn)
+{
+ int reply;
+ error_t err = 0;
+ const struct ftp_conn_params *p = conn->params;
+
+ err = ftp_conn_cmd (conn, "user", p->user ?: "anonymous", &reply, 0);
+
+ if (!err && reply == REPLY_NEED_ACCT)
+ {
+ char *acct = p->acct;
+ if (!acct && conn->hooks && conn->hooks->get_login_param)
+ err = (* conn->hooks->get_login_param) (conn,
+ FTP_CONN_GET_LOGIN_PARAM_ACCT,
+ &acct);
+ if (! err)
+ err = acct ? ftp_conn_cmd (conn, "acct", acct, &reply, 0) : EACCES;
+ if (acct && !p->acct)
+ free (acct);
+ }
+
+ if (!err && reply == REPLY_NEED_PASS)
+ {
+ char *pass = p->pass;
+ if (!pass && conn->hooks && conn->hooks->get_login_param)
+ err = (* conn->hooks->get_login_param) (conn,
+ FTP_CONN_GET_LOGIN_PARAM_PASS,
+ &pass);
+ if (! err)
+ if (pass)
+ err = ftp_conn_cmd (conn, "pass", pass, &reply, 0);
+ else
+ {
+ pass = getenv ("USER");
+ if (pass)
+ pass = getenv ("LOGNAME");
+ if (pass)
+ {
+ struct passwd *pe = getpwuid (getuid ());
+ pass = pe ? pe->pw_name : "?";
+ }
+
+ /* Append a '@' */
+ pass = strdup (pass);
+ if (pass)
+ pass = realloc (pass, strlen (pass) + 1);
+ if (pass)
+ {
+ strcat (pass, "@");
+ err = ftp_conn_cmd (conn, "pass", pass, &reply, 0);
+ }
+ else
+ err = ENOMEM;
+ }
+ if (pass && !p->pass)
+ free (pass);
+ }
+
+ if (!err && reply != REPLY_LOGIN_OK)
+ if (REPLY_IS_FAILURE (reply))
+ err = EACCES;
+ else
+ err = unexpected_reply (conn, reply, 0, 0);
+
+ return err;
+}
+
+static error_t
+ftp_conn_hello (struct ftp_conn *conn)
+{
+ int reply;
+ error_t err;
+
+ do
+ err = ftp_conn_get_reply (conn, &reply, 0);
+ while (!err && reply == REPLY_DELAY);
+
+ if (err)
+ return err;
+
+ if (reply == REPLY_CLOSED)
+ return ECONNREFUSED;
+ if (reply != REPLY_HELLO)
+ return EGRATUITOUS;
+
+ return 0;
+}
+
+/* Sets CONN's syshooks to a copy of SYSHOOKS. */
+void
+ftp_conn_set_syshooks (struct ftp_conn *conn, struct ftp_conn_syshooks *syshooks)
+{
+ conn->syshooks = *syshooks;
+}
+
+void
+ftp_conn_choose_syshooks (struct ftp_conn *conn, const char *syst)
+{
+ if (!syst || (strncasecmp (syst, "UNIX", 4) == 0 && !isalnum (syst[4])))
+ ftp_conn_set_syshooks (conn, &ftp_conn_unix_syshooks);
+}
+
+/* Sets CONN's syshooks by querying the remote system to see what type it is. */
+static error_t
+ftp_conn_sysify (struct ftp_conn *conn)
+{
+ int reply;
+ const char *txt;
+ error_t err = ftp_conn_cmd (conn, "syst", 0, &reply, &txt);
+
+ if (! err)
+ if (reply == REPLY_SYSTYPE || reply == REPLY_BAD_CMD)
+ {
+ if (reply == REPLY_BAD_CMD)
+ txt = 0;
+ if (conn->hooks && conn->hooks->choose_syshooks)
+ (*conn->hooks->choose_syshooks) (conn, txt);
+ else
+ ftp_conn_choose_syshooks (conn, txt);
+ }
+ else
+ err = unexpected_reply (conn, reply, txt, 0);
+
+ return err;
+}
+
+error_t
+ftp_conn_open (struct ftp_conn *conn)
+{
+ static int ftp_port = 0;
+ int csock;
+ error_t err;
+ struct sockaddr_in ftp_addr;
+
+ if (conn->params->addr_type != AF_INET)
+ return EAFNOSUPPORT;
+
+ if (! ftp_port)
+ {
+ struct servent *se = getservbyname ("ftp", "tcp");
+ if (! se)
+ return EGRATUITOUS;
+ ftp_port = se->s_port;
+ }
+
+ if (conn->control >= 0)
+ {
+ close (conn->control);
+ conn->control = -1;
+ }
+ bzero (&conn->syshooks, sizeof conn->syshooks);
+
+ csock = socket (PF_INET, SOCK_STREAM, 0);
+ if (csock < 0)
+ return errno;
+
+ ftp_addr.sin_len = conn->params->addr_len;
+ ftp_addr.sin_family = conn->params->addr_type;
+ ftp_addr.sin_addr = *(struct in_addr *)conn->params->addr;
+ ftp_addr.sin_port = ftp_port;
+
+ if (connect (csock, &ftp_addr, sizeof ftp_addr) < 0)
+ {
+ err = errno;
+ close (csock);
+ return err;
+ }
+
+ conn->control = csock;
+
+ err = ftp_conn_hello (conn);
+
+ if (!err && conn->hooks && conn->hooks->opened)
+ (* conn->hooks->opened) (conn);
+
+ if (! err)
+ /* Make any machine-dependent customizations. */
+ err = ftp_conn_sysify (conn);
+
+ if (! err)
+ /* login */
+ err = ftp_conn_login (conn);
+
+ if (!err && conn->type)
+ /* Set the connection type. */
+ {
+ int reply;
+ err = ftp_conn_cmd (conn, "type", conn->type, &reply, 0);
+ if (!err && reply != REPLY_OK)
+ err = unexpected_reply (conn, reply, 0, 0);
+ }
+
+ if (err)
+ ftp_conn_close (conn);
+
+ return err;
+}
+
+void
+ftp_conn_close (struct ftp_conn *conn)
+{
+ if (conn->control >= 0)
+ close (conn->control);
+ conn->control = -1;
+ if (conn->hooks && conn->hooks->closed)
+ (* conn->hooks->closed) (conn);
+}
diff --git a/libftpconn/priv.h b/libftpconn/priv.h
new file mode 100644
index 00000000..c0725852
--- /dev/null
+++ b/libftpconn/priv.h
@@ -0,0 +1,89 @@
+/* libftpconn private definitions
+
+ 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. */
+
+#ifndef __FTPCONN_PRIV_H__
+#define __FTPCONN_PRIV_H__
+
+#ifndef FTP_CONN_EI
+# define FTP_CONN_EI extern inline
+#endif
+
+/* Ftp reply codes. */
+#define REPLY_DELAY 120 /* Service ready in nnn minutes */
+
+#define REPLY_OK 200 /* Command OK */
+#define REPLY_SYSTYPE 215 /* NAME version */
+#define REPLY_HELLO 220 /* Service ready for new user */
+#define REPLY_TRANS_OK 226 /* Closing data connection; requested file
+ action successful */
+#define REPLY_PASV_OK 227 /* Entering passive mode */
+#define REPLY_LOGIN_OK 230 /* User logged in, proceed */
+#define REPLY_FCMD_OK 250 /* Requested file action okay, completed */
+#define REPLY_DIR_NAME 257 /* "DIR" msg */
+
+#define REPLY_NEED_PASS 331 /* User name okay, need password */
+#define REPLY_NEED_ACCT 332 /* Need account for login */
+
+#define REPLY_CLOSED 421 /* Service not available, closing control connection */
+#define REPLY_ABORTED 426 /* Connection closed; transfer aborted */
+
+#define REPLY_BAD_CMD 500 /* Syntax error; command unrecognized */
+#define REPLY_BAD_ARG 501 /* Synax error in parameters or arguments */
+#define REPLY_UNIMP_CMD 502 /* Command not implemented */
+#define REPLY_UNIMP_ARG 504 /* Command not implemented for that parameter */
+
+#define REPLY_NO_LOGIN 530 /* Not logged in */
+#define REPLY_NO_ACCT 532 /* Need account for storing files */
+#define REPLY_NO_SPACE 552 /* Requested file action aborted
+ Exceeded storage allocation */
+
+#define REPLY_IS_PRELIM(rep) ((rep) >= 100 && (rep) < 200)
+#define REPLY_IS_SUCCESS(rep) ((rep) >= 200 && (rep) < 300)
+#define REPLY_IS_INCOMPLETE(rep) ((rep) >= 300 && (rep) < 400)
+#define REPLY_IS_TRANSIENT(rep) ((rep) >= 400 && (rep) < 500)
+#define REPLY_IS_FAILURE(rep) ((rep) >= 500 && (rep) < 600)
+
+FTP_CONN_EI error_t
+unexpected_reply (struct ftp_conn *conn, int reply, const char *reply_txt,
+ const error_t *poss_errs)
+{
+ if (reply == REPLY_CLOSED)
+ return EPIPE;
+ else if (reply == REPLY_UNIMP_CMD || reply == REPLY_UNIMP_ARG)
+ return EOPNOTSUPP;
+ else if (reply == REPLY_BAD_ARG)
+ return EINVAL;
+ else if (REPLY_IS_FAILURE (reply) && reply_txt
+ && conn->syshooks.interp_err && poss_errs)
+ return (*conn->syshooks.interp_err) (conn, reply_txt, poss_errs);
+ else if (REPLY_IS_TRANSIENT (reply))
+ return EAGAIN;
+ else
+ return EGRATUITOUS;
+}
+
+/* Error codes we think may result from file operations we do. */
+extern const error_t ftp_conn_poss_file_errs[];
+
+error_t ftp_conn_get_pasv_addr (struct ftp_conn *conn, struct sockaddr **addr);
+
+error_t ftp_conn_send_actv_addr (struct ftp_conn *conn, struct sockaddr *addr);
+
+#endif /* __FTPCONN_PRIV_H__ */
diff --git a/libftpconn/reply.c b/libftpconn/reply.c
new file mode 100644
index 00000000..a85f5a36
--- /dev/null
+++ b/libftpconn/reply.c
@@ -0,0 +1,212 @@
+/* Parse ftp server replies
+
+ 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 <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Add STR (of size LEN) to CONN's reply_txt buffer, at offset *OFFS,
+ updating *OFFS. */
+static error_t
+ftp_conn_add_reply_txt (struct ftp_conn *conn, size_t *offs,
+ const char *str, size_t len)
+{
+ if (*offs + len + 1 > conn->reply_txt_sz)
+ {
+ size_t new_sz = *offs + len + 50;
+ char *new = realloc (conn->reply_txt, new_sz);
+ if (! new)
+ return ENOMEM;
+ conn->reply_txt = new;
+ conn->reply_txt_sz = new_sz;
+ }
+
+ bcopy (str, conn->reply_txt + *offs, len);
+ conn->reply_txt[*offs + len] = '\0'; /* Make sure nul terminated. */
+
+ *offs += len;
+
+ return 0;
+}
+
+/* Return a new line read from CONN's control connection in LINE & LINE_LEN;
+ LINE points into storage allocated in CONN, and is only valid until the
+ next call to this function, or return an error code. (we used to just use
+ the stdio getline function, and keep a stdio stream for the control
+ connection, but interleaved I/O didn't work correctly.) */
+static error_t
+ftp_conn_getline (struct ftp_conn *conn, const char **line, size_t *line_len)
+{
+ char *l = conn->line;
+ size_t offs = conn->line_offs, len = conn->line_len, sz = conn->line_sz;
+
+ for (;;)
+ {
+ int rd;
+
+ if (offs < len)
+ /* See if there's a newline in the active part of the line buffer. */
+ {
+ char *nl = memchr (l + offs, '\n', len - offs);
+ if (nl)
+ /* There is! Consume and return the whole line we found. */
+ {
+ *line = l + offs;
+
+ offs = nl + 1 - l; /* Consume the line */
+
+ /* Null terminate the result by overwriting the newline; if
+ there's a CR preceeding it, get rid of that too. */
+ if (nl > *line && nl[-1] == '\r')
+ nl--;
+ *nl = '\0';
+
+ *line_len = nl - *line;
+
+ if (offs == len)
+ conn->line_offs = conn->line_len = 0;
+ else
+ conn->line_offs = offs;
+
+ return 0;
+ }
+ }
+
+ /* No newline yet, so read some more! */
+
+ if (offs > (len << 2) && offs < len)
+ /* Relocate the current contents of the buffer to the beginning. */
+ {
+ len -= offs;
+ bcopy (l + offs, l, len - offs);
+ offs = conn->line_offs = 0;
+ conn->line_len = len;
+ }
+ if (len == sz)
+ /* Grow the line buffer; there's no space left. */
+ {
+ sz = sz + len ?: 50;
+ l = realloc (l, sz);
+ if (! l)
+ return ENOMEM;
+ conn->line = l;
+ conn->line_sz = sz;
+ }
+
+ /* Actually read something. */
+ rd = read (conn->control, l + len, sz - len);
+ if (rd < 0)
+ return errno;
+ else if (rd == 0)
+ {
+ *line = l + offs;
+ *line_len = 0;
+ return 0;
+ }
+
+ len += rd;
+ conn->line_len = len;
+ }
+}
+
+/* Get the next reply from CONN's ftp server, returning the reply code in
+ REPLY, if REPLY is non-zero, and the text of the reply (not including the
+ reply code) in REPLY_TXT (if it isn't zero), or return an error code. If
+ the reply is multiple lines, all of them are included in REPLY_TXT,
+ separated by newlines. */
+error_t
+ftp_conn_get_reply (struct ftp_conn *conn, int *reply, const char **reply_txt)
+{
+ size_t reply_txt_offs = 0; /* End of a multi-line reply in accum buf. */
+ int multi = 0; /* If a multi-line reply, the reply code. */
+
+ if (!reply && !reply_txt)
+ return 0; /* nop */
+
+ do
+ {
+ const char *l;
+ size_t len;
+ error_t err = ftp_conn_getline (conn, &l, &len);
+
+ if (err)
+ return err;
+ if (!multi && len == 0)
+ return EPIPE;
+
+#define ACCUM(txt, len) \
+ do { \
+ if (reply_txt) /* Only accumulate if wanted. */ \
+ { \
+ error_t err = ftp_conn_add_reply_txt (conn, &reply_txt_offs, txt, len); \
+ if (err) \
+ return err; \
+ } \
+ } while (0)
+
+ if (conn->hooks && conn->hooks->cntl_debug)
+ (*conn->hooks->cntl_debug) (conn, FTP_CONN_CNTL_DEBUG_REPLY, l);
+
+ if (isdigit (l[0]) && isdigit (l[1]) && isdigit (l[2]))
+ /* A reply code. */
+ {
+ int code = (l[0] - '0')*100 + (l[1] - '0')*10 + (l[2] - '0');
+
+ if (multi && code != multi)
+ /* Two codes in a multi-line reply don't match. */
+ return EGRATUITOUS;
+
+ if (l[3] == '-')
+ /* The non-terminal line of a multi-line reply. RFC959 actually
+ claims there shouldn't be more than one multi-line code (other
+ lines in between the two shouldn't have a numeric code at
+ all), but real ftp servers don't obey this rule. */
+ multi = code;
+ else if (l[3] != ' ')
+ /* Some syntax error. */
+ return EGRATUITOUS;
+ else
+ /* The end of the reply (and perhaps the only line). */
+ {
+ multi = 0;
+ if (reply)
+ *reply = code;
+ }
+
+ ACCUM (l + 4, len - 4);
+ }
+ else if (multi)
+ /* The lines between the first and last in a multi-line reply may be
+ anything as long as they don't start with a digit. */
+ ACCUM (l, len);
+ else
+ return EGRATUITOUS;
+ }
+ while (multi);
+
+ if (reply_txt)
+ *reply_txt = conn->reply_txt;
+
+ return 0;
+}
diff --git a/libftpconn/rmt.c b/libftpconn/rmt.c
new file mode 100644
index 00000000..d7865672
--- /dev/null
+++ b/libftpconn/rmt.c
@@ -0,0 +1,90 @@
+/* Remote (server-to-server) transfer
+
+ 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 <errno.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Transfer the output of SRC_CMD/SRC_NAME on SRC_CONN to DST_NAME on
+ DST_CONN, moving the data directly between servers. */
+error_t
+ftp_conn_rmt_transfer (struct ftp_conn *src_conn,
+ const char *src_cmd, const char *src_name,
+ const int *src_poss_errs,
+ struct ftp_conn *dst_conn, const char *dst_name)
+{
+ struct sockaddr *src_addr;
+ error_t err = ftp_conn_get_pasv_addr (src_conn, &src_addr);
+
+ if (! err)
+ {
+ err = ftp_conn_send_actv_addr (dst_conn, src_addr);
+
+ if (! err)
+ {
+ int reply;
+ const char *txt;
+ err = ftp_conn_cmd (src_conn, src_cmd, src_name, 0, 0);
+
+ if (! err)
+ {
+ err = ftp_conn_cmd (dst_conn, "stor", dst_name, &reply, &txt);
+
+ if (! err)
+ if (REPLY_IS_PRELIM (reply))
+ {
+ err = ftp_conn_get_reply (src_conn, &reply, &txt);
+ if (!err && !REPLY_IS_PRELIM (reply))
+ err = unexpected_reply (src_conn, reply, txt, src_poss_errs);
+
+ if (err)
+ ftp_conn_abort (dst_conn);
+ else
+ err = ftp_conn_finish_transfer (dst_conn);
+ }
+ else
+ err = unexpected_reply (dst_conn, reply, txt,
+ ftp_conn_poss_file_errs);
+
+ if (err)
+ ftp_conn_abort (src_conn);
+ else
+ err = ftp_conn_finish_transfer (src_conn);
+ }
+ }
+
+ free (src_addr);
+ }
+
+ return err;
+}
+
+/* Copy the SRC_NAME on SRC_CONN to DST_NAME on DST_CONN, moving the data
+ directly between servers. */
+error_t
+ftp_conn_rmt_copy (struct ftp_conn *src_conn, const char *src_name,
+ struct ftp_conn *dst_conn, const char *dst_name)
+{
+ return
+ ftp_conn_rmt_transfer (src_conn, "retr", src_name, ftp_conn_poss_file_errs,
+ dst_conn, dst_name);
+}
diff --git a/libftpconn/set-type.c b/libftpconn/set-type.c
new file mode 100644
index 00000000..dba44c36
--- /dev/null
+++ b/libftpconn/set-type.c
@@ -0,0 +1,59 @@
+/* Set connection type
+
+ 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 <errno.h>
+#include <string.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Set the ftp connection type of CONN to TYPE, or return an error. */
+error_t
+ftp_conn_set_type (struct ftp_conn *conn, const char *type)
+{
+ error_t err = 0;
+
+ if (! type)
+ return EINVAL;
+
+ if (!conn->type || strcmp (type, conn->type) != 0)
+ {
+ type = strdup (type);
+ if (! type)
+ err = ENOMEM;
+ else
+ {
+ int reply;
+ error_t err = ftp_conn_cmd_reopen (conn, "type", type, &reply, 0);
+ if (! err)
+ if (reply == REPLY_OK)
+ {
+ if (conn->type)
+ free ((char *)conn->type);
+ conn->type = type;
+ }
+ else
+ err = unexpected_reply (conn, reply, 0, 0);
+ }
+ }
+
+ return err;
+}
diff --git a/libftpconn/stats.c b/libftpconn/stats.c
new file mode 100644
index 00000000..d6e6ebd1
--- /dev/null
+++ b/libftpconn/stats.c
@@ -0,0 +1,85 @@
+/* Fetch file stats
+
+ 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 <errno.h>
+
+#include <ftpconn.h>
+
+/* Start an operation to get a list of file-stat structures for NAME (this
+ is often similar to ftp_conn_start_dir, but with OS-specific flags), and
+ return a file-descriptor for reading on, and a state structure in STATE
+ suitable for passing to cont_get_stats. FORCE_DIR controls what happens if
+ NAME refers to a directory: if FORCE_DIR is false, STATS will contain
+ entries for all files *in* NAME, and if FORCE_DIR is true, it will
+ contain just a single entry for NAME itself (or an error will be
+ returned when this isn't possible). */
+error_t
+ftp_conn_start_get_stats (struct ftp_conn *conn,
+ const char *name, int force_dir,
+ int *fd, void **state)
+{
+ if (conn->syshooks.start_get_stats)
+ return
+ (*conn->syshooks.start_get_stats) (conn, name, force_dir, fd, state);
+ else
+ return EOPNOTSUPP;
+}
+
+/* Read stats information from FD, calling ADD_STAT for each new stat (HOOK
+ is passed to ADD_STAT). FD and STATE should be returned from
+ start_get_stats. If this function returns EAGAIN, then it should be
+ called again to finish the job (possibly after calling select on FD); if
+ it returns 0, then it is finishe,d and FD and STATE are deallocated. */
+error_t
+ftp_conn_cont_get_stats (struct ftp_conn *conn, int fd, void *state,
+ ftp_conn_add_stat_fun_t add_stat, void *hook)
+{
+ if (conn->syshooks.cont_get_stats)
+ return (*conn->syshooks.cont_get_stats) (conn, fd, state, add_stat, hook);
+ else
+ return EOPNOTSUPP;
+}
+
+/* Get a list of file-stat structures for NAME, calling ADD_STAT for each one
+ (HOOK is passed to ADD_STAT). If NAME refers to an ordinary file, a
+ single entry for it is returned for it; if NAME refers to a directory,
+ then if FORCE_DIR is false, STATS will contain entries for all files *in*
+ NAME, and if FORCE_DIR is true, it will contain just a single entry for
+ NAME itself (or an error will be returned when this isn't possible). This
+ function may block. */
+error_t
+ftp_conn_get_stats (struct ftp_conn *conn,
+ const char *name, int force_dir,
+ ftp_conn_add_stat_fun_t add_stat, void *hook)
+{
+ int fd;
+ void *state;
+ error_t err = ftp_conn_start_get_stats (conn, name, force_dir, &fd, &state);
+
+ if (err)
+ return err;
+
+ do
+ err = ftp_conn_cont_get_stats (conn, fd, state, add_stat, hook);
+ while (err == EAGAIN);
+
+ return err;
+}
diff --git a/libftpconn/xfer.c b/libftpconn/xfer.c
new file mode 100644
index 00000000..07985406
--- /dev/null
+++ b/libftpconn/xfer.c
@@ -0,0 +1,130 @@
+/* Start/stop data channel transfer
+
+ 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 <errno.h>
+
+#include <ftpconn.h>
+#include "priv.h"
+
+/* Open a data connection, returning the file descriptor in DATA. */
+static error_t
+ftp_conn_open_data (struct ftp_conn *conn, int *data)
+{
+ struct sockaddr *addr;
+ error_t err = ftp_conn_get_pasv_addr (conn, &addr);
+
+ if (! err)
+ {
+ int dsock = socket (PF_INET, SOCK_STREAM, 0);
+
+ if (dsock < 0)
+ err = errno;
+ else if (connect (dsock, addr, addr->sa_len) < 0)
+ {
+ err = errno;
+ close (dsock);
+ }
+ else
+ *data = dsock;
+
+ free (addr);
+ }
+
+ return err;
+}
+
+/* Start a transfer command CMD/ARG, returning a file descriptor in DATA.
+ POSS_ERRS is a list of errnos to try matching against any resulting error
+ text. */
+error_t
+ftp_conn_start_transfer (struct ftp_conn *conn,
+ const char *cmd, const char *arg,
+ const error_t *poss_errs,
+ int *data)
+{
+ error_t err = ftp_conn_open_data (conn, data);
+
+ if (! err)
+ {
+ int reply;
+ const char *txt;
+
+ err = ftp_conn_cmd (conn, cmd, arg, &reply, &txt);
+ if (!err && !REPLY_IS_PRELIM (reply))
+ err = unexpected_reply (conn, reply, txt, poss_errs);
+
+ if (err)
+ close (*data);
+ }
+
+ return err;
+}
+
+/* Wait for the reply signalling the end of a data transfer. */
+error_t
+ftp_conn_finish_transfer (struct ftp_conn *conn)
+{
+ int reply;
+ error_t err = ftp_conn_get_reply (conn, &reply, 0);
+ if (!err && reply != REPLY_TRANS_OK && reply != REPLY_FCMD_OK)
+ err = unexpected_reply (conn, reply, 0, 0);
+ return err;
+}
+
+/* Start retreiving file NAME over CONN, returning a file descriptor in DATA
+ over which the data can be read. */
+error_t
+ftp_conn_start_retrieve (struct ftp_conn *conn, const char *name, int *data)
+{
+ if (! name)
+ return EINVAL;
+ return
+ ftp_conn_start_transfer (conn, "retr", name, ftp_conn_poss_file_errs, data);
+}
+
+/* Start retreiving a list of files in NAME over CONN, returning a file
+ descriptor in DATA over which the data can be read. */
+error_t
+ftp_conn_start_list (struct ftp_conn *conn, const char *name, int *data)
+{
+ return
+ ftp_conn_start_transfer (conn, "nlst", name, ftp_conn_poss_file_errs, data);
+}
+
+/* Start retreiving a directory listing of NAME over CONN, returning a file
+ descriptor in DATA over which the data can be read. */
+error_t
+ftp_conn_start_dir (struct ftp_conn *conn, const char *name, int *data)
+{
+ return
+ ftp_conn_start_transfer (conn, "list", name, ftp_conn_poss_file_errs, data);
+}
+
+/* Start storing into file NAME over CONN, returning a file descriptor in DATA
+ into which the data can be written. */
+error_t
+ftp_conn_start_store (struct ftp_conn *conn, const char *name, int *data)
+{
+ if (! name)
+ return EINVAL;
+ return
+ ftp_conn_start_transfer (conn, "stor", name, ftp_conn_poss_file_errs, data);
+}
diff --git a/libftpconn/xinl.c b/libftpconn/xinl.c
new file mode 100644
index 00000000..a975b80d
--- /dev/null
+++ b/libftpconn/xinl.c
@@ -0,0 +1,24 @@
+/* Real definitions for extern inline functions in priv.h
+
+ Copyright (C) 1997 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#define FTP_CONN_EI
+#include <ftpconn.h>
+#include "priv.h"