summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiles Bader <miles@gnu.org>1997-05-07 20:58:48 +0000
committerMiles Bader <miles@gnu.org>1997-05-07 20:58:48 +0000
commit6abfa49b04ea7b5d4721871296959ad620be0c80 (patch)
treeb3fdd7d4c157c619b69409a0a60e4e0a135c7baa
parent08e5b48c21ea0e6ae7a3131a5546e272de8c30e4 (diff)
Split into separate files
-rw-r--r--libftpconn/ftpconn.c1068
1 files changed, 0 insertions, 1068 deletions
diff --git a/libftpconn/ftpconn.c b/libftpconn/ftpconn.c
deleted file mode 100644
index fc851163..00000000
--- a/libftpconn/ftpconn.c
+++ /dev/null
@@ -1,1068 +0,0 @@
-/* Manage an 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 <string.h>
-#include <ctype.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <pwd.h>
-
-#include <arpa/telnet.h>
-
-#include <ftpconn.h>
-
-/* 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)
-
-static 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;
-}
-
-/* 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;
-}
-
-/* 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);
- }
-}
-
-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);
-}
-
-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;
-
-}
-
-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);
-}
-
-static 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;
-}
-
-static 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;
-}
-
-/* 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;
-}
-
-static const error_t poss_file_errs[] = {
- EIO, ENOENT, EPERM, EACCES, ENOTDIR, ENAMETOOLONG, ELOOP, EISDIR, EROFS,
- EMFILE, ENFILE, ENXIO, EOPNOTSUPP, ENOSPC, EDQUOT, ETXTBSY, EEXIST,
- 0
-};
-
-/* 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, 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, 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, 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, poss_file_errs, data);
-}
-
-/* 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, 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, poss_file_errs,
- dst_conn, dst_name);
-}
-
-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, 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, poss_file_errs);
- return err;
-}
-
-/* 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;
-}
-
-/* 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;
-}
-