summaryrefslogtreecommitdiffstats
path: root/lib/libftpio
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libftpio')
-rw-r--r--lib/libftpio/Makefile30
-rw-r--r--lib/libftpio/ftp.errors44
-rw-r--r--lib/libftpio/ftpio.3212
-rw-r--r--lib/libftpio/ftpio.c882
-rw-r--r--lib/libftpio/ftpio.h68
5 files changed, 1236 insertions, 0 deletions
diff --git a/lib/libftpio/Makefile b/lib/libftpio/Makefile
new file mode 100644
index 0000000..ecbc730
--- /dev/null
+++ b/lib/libftpio/Makefile
@@ -0,0 +1,30 @@
+# $FreeBSD$
+
+LIB= ftpio
+SHLIB_MAJOR= 5
+SHLIB_MINOR= 0
+
+SRCS= ftpio.c ftperr.c
+CFLAGS+= -I${.CURDIR} -Wall
+MAN3= ftpio.3
+CLEANFILES= ftperr.c
+
+beforeinstall:
+ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/ftpio.h \
+ ${DESTDIR}/usr/include
+
+ftperr.c: ftp.errors
+ @echo '#include <stdio.h>' > ${.TARGET}
+ @echo '#include "ftpio.h"' >> ${.TARGET}
+ @echo "struct ftperr ftpErrList[] = {" \ >> ${.TARGET}
+ @cat ${.ALLSRC} \
+ | grep -v ^# \
+ | sort \
+ | while read NUM STRING; do \
+ echo " { $${NUM}, \"$${STRING}\" },"; \
+ done >> ${.TARGET}
+ @echo "};" >> ${.TARGET}
+ @echo -n "int const ftpErrListLength = " >> ${.TARGET}
+ @echo "sizeof(ftpErrList) / sizeof(*ftpErrList);" >> ${.TARGET}
+
+.include <bsd.lib.mk>
diff --git a/lib/libftpio/ftp.errors b/lib/libftpio/ftp.errors
new file mode 100644
index 0000000..7ae4445
--- /dev/null
+++ b/lib/libftpio/ftp.errors
@@ -0,0 +1,44 @@
+# $FreeBSD$
+#
+# This list is taken from RFC 959.
+# It probably needs a going over.
+#
+110 Restart marker reply
+120 Service ready in a few minutes
+125 Data connection already open; transfer starting
+150 File status okay; about to open data connection
+200 Command okay
+202 Command not implemented, superfluous at this site
+211 System status, or system help reply
+212 Directory status
+213 File status
+214 Help message
+215 Set system type
+220 Service ready for new user
+221 Service closing control connection
+225 Data connection open; no transfer in progress
+226 Requested file action successful
+227 Entering Passive Mode
+230 User logged in, proceed
+250 Requested file action okay, completed
+257 File/directory created
+331 User name okay, need password
+332 Need account for login
+350 Requested file action pending further information
+421 Service not available, closing control connection
+425 Can't open data connection
+426 Connection closed; transfer aborted
+450 File unavailable (e.g., file busy)
+451 Requested action aborted: local error in processing
+452 Insufficient storage space in system
+500 Syntax error, command unrecognized
+501 Syntax error in parameters or arguments
+502 Command not implemented
+503 Bad sequence of commands
+504 Command not implemented for that parameter
+530 Not logged in
+532 Need account for storing files
+550 File unavailable (e.g., file not found, no access)
+551 Requested action aborted. Page type unknown
+552 Exceeded storage allocation
+553 File name not allowed
diff --git a/lib/libftpio/ftpio.3 b/lib/libftpio/ftpio.3
new file mode 100644
index 0000000..60948ce
--- /dev/null
+++ b/lib/libftpio/ftpio.3
@@ -0,0 +1,212 @@
+.\" Copyright (c) 1996 Jordan Hubbard (jkh@FreeBSD.org)
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 17, 1996
+.Dt FTPIO 3
+.Os
+.Sh NAME
+.Nm ftpLogin ,
+.Nm ftpChdir ,
+.Nm ftpErrno ,
+.Nm ftpGetModtime ,
+.Nm ftpGetSize ,
+.Nm ftpGet ,
+.Nm ftpPut ,
+.Nm ftpBinary ,
+.Nm ftpPassive ,
+.Nm ftpVerbose ,
+.Nm ftpGetURL ,
+.Nm ftpPutURL
+.Nd FTPIO User library
+.Sh SYNOPSIS
+.Fd #include <ftpio.h>
+.Ft FILE *
+.Fn ftpLogin "char *host" "char *user" "char *passwd" "int ftp_port" "int verbose" "int *retcode"
+.Ft int
+.Fn ftpChdir "FILE *stream, char *dirname"
+.Ft int
+.Fn ftpErrno "FILE *stream"
+.Ft const char *
+.Fn ftpErrString "int errno"
+.Ft time_t
+.Fn ftpGetModtime "FILE *stream, char *file"
+.Ft off_t
+.Fn ftpGetSize "FILE *stream, char *file"
+.Ft FILE *
+.Fn ftpGet "FILE *stream, char *file, off_t *seekto"
+.Ft FILE *
+.Fn ftpPut "FILE *stream, char *file"
+.Ft int
+.Fn ftpAscii "FILE *stream"
+.Ft int
+.Fn ftpBinary "FILE *stream"
+.Ft int
+.Fn ftpPassive "FILE *stream, int status"
+.Ft void
+.Fn ftpVerbose "FILE *stream, int status"
+.Ft FILE *
+.Fn ftpGetURL "char *url, char *user, char *passwd, int *retcode"
+.Ft FILE *
+.Fn ftpPutURL "char *url, char *user, char *passwd, int *retcode"
+
+.Sh DESCRIPTION
+These functions implement a high-level library for managing FTP connections.
+.Pp
+.Fn ftpLogin
+attempts to log in using the supplied
+.Fa user ,
+.Fa passwd,
+.Fa ftp_port
+(if passed as 0,
+.Fa ftp_port
+defaults to the standard ftp port of 21) and
+.Fa verbose
+fields. If it is successful, a
+standard stream descriptor is returned which should be passed to
+subsequent FTP operations. On failure, NULL is returned and
+.Fa retcode
+will have the error code returned by the foreign server.
+.Pp
+.Fn ftpChdir
+attempts to issue a server CD command to the directory named in
+.Fa dir.
+On success, zero is returned. On failure, the error code from the server.
+.Pp
+.Fn ftpErrno
+returns the server failure code for the last operation (useful for seeing
+more about what happened if you're familiar with FTP error codes).
+.Fn ftpErrString
+returns a human readable version of the supplied server failure code.
+.Pp
+.Fn ftpGet
+attempts to retreive the file named by the
+.Fa file
+argument (which is assumed to be relative to the FTP server's current directory,
+see
+.Fn ftpChdir )
+and returns a new FILE* pointer for the file or NULL on failure. If
+.Fa seekto
+is non-NULL, the contents of the integer it points to will be used
+as a restart point for the file, that is to say that the stream
+returned will point
+.Fa *seekto
+bytes into the file gotten (this is handy for restarting failed
+transfers efficiently). If the seek operation fails, the value
+of
+.Fa *seekto
+will be zero'd.
+.Pp
+.Fn ftpGetModtime
+returns the last modification time of the file named by the
+.Fa file
+argument. If the file could not be opened or stat'd, 0 is returned.
+.Pp
+.Fn ftpGetSize
+returns the size in bytes of the file named by the
+.Fa file
+argument. If the file could not be opened or stat'd, -1 is returned.
+.Pp
+.Fn ftpPut
+attempts to create a new file named by the
+.Fa file
+argument (which is assumed to be relative to the FTP server's current directory,
+see
+.Fn ftpChdir )
+and returns a new
+.Fa stream
+pointer for the file or NULL on failure.
+.Pp
+.Fn ftpAscii
+sets ASCII mode for the current server connection named by
+.Fa stream .
+.Pp
+.Fn ftpBinary
+sets binary mode for the current server connection named by
+.Fa stream .
+.Pp
+.Fn ftpPassive
+sets passive mode (for firewalls) for the current server connection named by
+.Fa stream
+to boolean value
+.Fa status .
+.Pp
+.Fn ftpVerbose
+sets the verbosity mode for the current server connection named by
+.Fa stream
+to boolean value
+.Fa status .
+.Pp
+.Fn ftpGetURL
+attempts to retreive the file named by the supplied
+.Fa URL
+and can be considered equivalent to the combined
+.Fn ftpLogin ,
+.Fn ftpChdir
+and
+.Fn ftpGet
+operations except that no server
+.Fa stream
+is ever returned - the connection to the server closes when
+the file has been completely read. Use the lower-level routines
+if multiple gets are required as it will be far more efficient.
+.Pp
+.Fn ftpPutURL
+attempts to create the file named by the supplied
+.Fa URL
+and can be considered equivalent to the combined
+.Fn ftpLogin ,
+.Fn ftpChdir
+and
+.Fn ftpPut
+operations except that no server stream is ever returned - the connection
+to the server closes when the file has been completely written. Use the
+lower-level routines if multiple puts are required as it will be far more
+efficient.
+.Sh ENVIRONMENT
+.Bl -tag -width FTP_PASSIVE_MODE -offset 123
+.It Ev FTP_TIMEOUT
+Maximum time, in seconds, to wait for a response
+from the peer before aborting an
+.Tn FTP
+connection.
+.It Ev FTP_PASSIVE_MODE
+Force the use of passive mode
+.Tn FTP .
+.El
+.Sh BUGS
+I'm sure you can get this thing's internal state machine confused if
+you really work at it, but so far it's proven itself pretty robust in
+all my tests.
+.Sh HISTORY
+Started life as Poul-Henning Kamp's ftp driver for the system installation
+utility, later significantly mutated into a more general form as an
+extension of stdio by Jordan Hubbard. Also incorporates some ideas and
+extensions from Jean-Marc Zucconi.
+.Sh AUTHORS
+.An Jordan Hubbard ,
+.An Poul-Henning Kamp
+and
+.An Jean-Marc Zucconi
diff --git a/lib/libftpio/ftpio.c b/lib/libftpio/ftpio.c
new file mode 100644
index 0000000..35f2b15
--- /dev/null
+++ b/lib/libftpio/ftpio.c
@@ -0,0 +1,882 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * Major Changelog:
+ *
+ * Jordan K. Hubbard
+ * 17 Jan 1996
+ *
+ * Turned inside out. Now returns xfers as new file ids, not as a special
+ * `state' of FTP_t
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <ftpio.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define SUCCESS 0
+#define FAILURE -1
+
+#ifndef TRUE
+#define TRUE (1)
+#define FALSE (0)
+#endif
+
+/* How to see by a given code whether or not the connection has timed out */
+#define FTP_TIMEOUT(code) (FtpTimedOut || code == FTP_TIMED_OUT)
+
+/* Internal routines - deal only with internal FTP_t type */
+static FTP_t ftp_new(void);
+static void check_passive(FILE *fp);
+static int ftp_read_method(void *n, char *buf, int nbytes);
+static int ftp_write_method(void *n, const char *buf, int nbytes);
+static int ftp_close_method(void *n);
+static int writes(int fd, char *s);
+static __inline char *get_a_line(FTP_t ftp);
+static int get_a_number(FTP_t ftp, char **q);
+static int botch(char *func, char *botch_state);
+static int cmd(FTP_t ftp, const char *fmt, ...);
+static int ftp_login_session(FTP_t ftp, char *host, char *user, char *passwd, int port, int verbose);
+static int ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode, off_t *seekto);
+static int ftp_close(FTP_t ftp);
+static int get_url_info(char *url_in, char *host_ret, int *port_ret, char *name_ret);
+static void ftp_timeout(int sig);
+static void ftp_set_timeout(void);
+static void ftp_clear_timeout(void);
+
+
+/* Global status variable - ick */
+int FtpTimedOut;
+
+/* FTP happy status codes */
+#define FTP_GENERALLY_HAPPY 200
+#define FTP_ASCII_HAPPY FTP_GENERALLY_HAPPY
+#define FTP_BINARY_HAPPY FTP_GENERALLY_HAPPY
+#define FTP_PORT_HAPPY FTP_GENERALLY_HAPPY
+#define FTP_HAPPY_COMMENT 220
+#define FTP_QUIT_HAPPY 221
+#define FTP_TRANSFER_HAPPY 226
+#define FTP_PASSIVE_HAPPY 227
+#define FTP_CHDIR_HAPPY 250
+
+/* FTP unhappy status codes */
+#define FTP_TIMED_OUT 421
+
+/*
+ * XXX
+ * gross! evil! bad! We really need an access primitive for cookie in stdio itself.
+ * it's too convenient a hook to bury and it's already exported through funopen as it is, so...
+ * XXX
+ */
+#define fcookie(fp) ((fp)->_cookie)
+
+/* Placeholder in case we want to do any pre-init stuff at some point */
+int
+networkInit()
+{
+ return SUCCESS; /* XXX dummy function for now XXX */
+}
+
+/* Check a return code with some lenience for back-dated garbage that might be in the buffer */
+static int
+check_code(FTP_t ftp, int var, int preferred)
+{
+ ftp->error = 0;
+ while (1) {
+ if (var == preferred)
+ return 0;
+ else if (var == FTP_TRANSFER_HAPPY) /* last operation succeeded */
+ var = get_a_number(ftp, NULL);
+ else if (var == FTP_HAPPY_COMMENT) /* chit-chat */
+ var = get_a_number(ftp, NULL);
+ else if (var == FTP_GENERALLY_HAPPY) /* general success code */
+ var = get_a_number(ftp, NULL);
+ else {
+ ftp->error = var;
+ return 1;
+ }
+ }
+}
+
+int
+ftpAscii(FILE *fp)
+{
+ FTP_t ftp = fcookie(fp);
+ int i;
+
+ if (!ftp->is_binary)
+ return SUCCESS;
+ i = cmd(ftp, "TYPE A");
+ if (i < 0 || check_code(ftp, i, FTP_ASCII_HAPPY))
+ return i;
+ ftp->is_binary = FALSE;
+ return SUCCESS;
+}
+
+int
+ftpBinary(FILE *fp)
+{
+ FTP_t ftp = fcookie(fp);
+ int i;
+
+ if (ftp->is_binary)
+ return SUCCESS;
+ i = cmd(ftp, "TYPE I");
+ if (i < 0 || check_code(ftp, i, FTP_BINARY_HAPPY))
+ return i;
+ ftp->is_binary = TRUE;
+ return SUCCESS;
+}
+void
+ftpVerbose(FILE *fp, int status)
+{
+ FTP_t ftp = fcookie(fp);
+ ftp->is_verbose = status;
+}
+
+int
+ftpChdir(FILE *fp, char *dir)
+{
+ int i;
+ FTP_t ftp = fcookie(fp);
+
+ i = cmd(ftp, "CWD %s", dir);
+ if (i < 0 || check_code(ftp, i, FTP_CHDIR_HAPPY))
+ return i;
+ return SUCCESS;
+}
+
+int
+ftpErrno(FILE *fp)
+{
+ FTP_t ftp = fcookie(fp);
+ return ftp->error;
+}
+
+const char *
+ftpErrString(int error)
+{
+ int k;
+
+ if (error == -1)
+ return("connection in wrong state");
+ if (error < 100)
+ /* XXX soon UNIX errnos will catch up with FTP protocol errnos */
+ return strerror(error);
+ for (k = 0; k < ftpErrListLength; k++)
+ if (ftpErrList[k].num == error)
+ return(ftpErrList[k].string);
+ return("Unknown error");
+}
+
+off_t
+ftpGetSize(FILE *fp, char *name)
+{
+ int i;
+ char p[BUFSIZ], *cp, *ep;
+ FTP_t ftp = fcookie(fp);
+ off_t size;
+
+ check_passive(fp);
+ sprintf(p, "SIZE %s\r\n", name);
+ if (ftp->is_verbose)
+ fprintf(stderr, "Sending %s", p);
+ if (writes(ftp->fd_ctrl, p))
+ return (off_t)-1;
+ i = get_a_number(ftp, &cp);
+ if (check_code(ftp, i, 213))
+ return (off_t)-1;
+
+ errno = 0; /* to check for ERANGE */
+ size = (off_t)strtoq(cp, &ep, 10);
+ if (*ep != '\0' || errno == ERANGE)
+ return (off_t)-1;
+ return size;
+}
+
+time_t
+ftpGetModtime(FILE *fp, char *name)
+{
+ char p[BUFSIZ], *cp;
+ struct tm t;
+ time_t t0 = time (0);
+ FTP_t ftp = fcookie(fp);
+ int i;
+
+ check_passive(fp);
+ sprintf(p, "MDTM %s\r\n", name);
+ if (ftp->is_verbose)
+ fprintf(stderr, "Sending %s", p);
+ if (writes(ftp->fd_ctrl, p))
+ return (time_t)0;
+ i = get_a_number(ftp, &cp);
+ if (check_code(ftp, i, 213))
+ return (time_t)0;
+ while (*cp && !isdigit(*cp))
+ cp++;
+ if (!*cp)
+ return (time_t)0;
+ t0 = localtime (&t0)->tm_gmtoff;
+ sscanf(cp, "%04d%02d%02d%02d%02d%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec);
+ t.tm_mon--;
+ t.tm_year -= 1900;
+ t.tm_isdst=-1;
+ t.tm_gmtoff = 0;
+ t0 += mktime (&t);
+ return t0;
+}
+
+FILE *
+ftpGet(FILE *fp, char *file, off_t *seekto)
+{
+ FILE *fp2;
+ FTP_t ftp = fcookie(fp);
+
+ check_passive(fp);
+ if (ftpBinary(fp) != SUCCESS)
+ return NULL;
+
+ if (ftp_file_op(ftp, "RETR", file, &fp2, "r", seekto) == SUCCESS)
+ return fp2;
+ return NULL;
+}
+
+/* Returns a standard FILE pointer type representing an open control connection */
+FILE *
+ftpLogin(char *host, char *user, char *passwd, int port, int verbose, int *retcode)
+{
+ FTP_t n;
+ FILE *fp;
+
+ if (retcode)
+ *retcode = 0;
+ if (networkInit() != SUCCESS)
+ return NULL;
+
+ n = ftp_new();
+ fp = NULL;
+ if (n && ftp_login_session(n, host, user, passwd, port, verbose) == SUCCESS) {
+ fp = funopen(n, ftp_read_method, ftp_write_method, NULL, ftp_close_method); /* BSD 4.4 function! */
+ fp->_file = n->fd_ctrl;
+ }
+ if (retcode) {
+ if (!n)
+ *retcode = (FtpTimedOut ? FTP_TIMED_OUT : -1);
+ /* Poor attempt at mapping real errnos to FTP error codes */
+ else switch(n->error) {
+ case EADDRNOTAVAIL:
+ *retcode = FTP_TIMED_OUT; /* Actually no such host, but we have no way of saying that. :-( */
+ break;
+
+ case ETIMEDOUT:
+ *retcode = FTP_TIMED_OUT;
+ break;
+
+ default:
+ *retcode = n->error;
+ break;
+ }
+ }
+ return fp;
+}
+
+FILE *
+ftpPut(FILE *fp, char *file)
+{
+ FILE *fp2;
+ FTP_t ftp = fcookie(fp);
+
+ check_passive(fp);
+ if (ftp_file_op(ftp, "STOR", file, &fp2, "w", NULL) == SUCCESS)
+ return fp2;
+ return NULL;
+}
+
+/* Unlike binary mode, passive mode is a toggle! :-( */
+int
+ftpPassive(FILE *fp, int st)
+{
+ FTP_t ftp = fcookie(fp);
+ int i;
+
+ if (ftp->is_passive == st)
+ return SUCCESS;
+ i = cmd(ftp, "PASV");
+ if (i < 0)
+ return i;
+ if (i != FTP_PASSIVE_HAPPY)
+ return FAILURE;
+ ftp->is_passive = !ftp->is_passive;
+ return SUCCESS;
+}
+
+FILE *
+ftpGetURL(char *url, char *user, char *passwd, int *retcode)
+{
+ char host[255], name[255];
+ int port;
+ FILE *fp2;
+ static FILE *fp = NULL;
+ static char *prev_host;
+
+ if (retcode)
+ *retcode = 0;
+ if (get_url_info(url, host, &port, name) == SUCCESS) {
+ if (fp && prev_host) {
+ if (!strcmp(prev_host, host)) {
+ /* Try to use cached connection */
+ fp2 = ftpGet(fp, name, NULL);
+ if (!fp2) {
+ /* Connection timed out or was no longer valid */
+ fclose(fp);
+ free(prev_host);
+ prev_host = NULL;
+ }
+ else
+ return fp2;
+ }
+ else {
+ /* It's a different host now, flush old */
+ fclose(fp);
+ free(prev_host);
+ prev_host = NULL;
+ }
+ }
+ fp = ftpLogin(host, user, passwd, port, 0, retcode);
+ if (fp) {
+ fp2 = ftpGet(fp, name, NULL);
+ if (!fp2) {
+ /* Connection timed out or was no longer valid */
+ if (retcode)
+ *retcode = ftpErrno(fp);
+ fclose(fp);
+ fp = NULL;
+ }
+ else
+ prev_host = strdup(host);
+ return fp2;
+ }
+ }
+ return NULL;
+}
+
+FILE *
+ftpPutURL(char *url, char *user, char *passwd, int *retcode)
+{
+ char host[255], name[255];
+ int port;
+ static FILE *fp = NULL;
+ FILE *fp2;
+
+ if (retcode)
+ *retcode = 0;
+ if (fp) { /* Close previous managed connection */
+ fclose(fp);
+ fp = NULL;
+ }
+ if (get_url_info(url, host, &port, name) == SUCCESS) {
+ fp = ftpLogin(host, user, passwd, port, 0, retcode);
+ if (fp) {
+ fp2 = ftpPut(fp, name);
+ if (!fp2) {
+ if (retcode)
+ *retcode = ftpErrno(fp);
+ fclose(fp);
+ fp = NULL;
+ }
+ return fp2;
+ }
+ }
+ return NULL;
+}
+
+/* Internal workhorse function for dissecting URLs. Takes a URL as the first argument and returns the
+ result of such disection in the host, user, passwd, port and name variables. */
+static int
+get_url_info(char *url_in, char *host_ret, int *port_ret, char *name_ret)
+{
+ char *name, *host, *cp, url[BUFSIZ];
+ int port;
+
+ name = host = NULL;
+ /* XXX add http:// here or somewhere reasonable at some point XXX */
+ if (strncmp("ftp://", url_in, 6) != 0)
+ return FAILURE;
+ /* We like to stomp a lot on the URL string in dissecting it, so copy it first */
+ strncpy(url, url_in, BUFSIZ);
+ host = url + 6;
+ if ((cp = index(host, ':')) != NULL) {
+ *(cp++) = '\0';
+ port = strtol(cp, 0, 0);
+ }
+ else
+ port = 0; /* use default */
+ if (port_ret)
+ *port_ret = port;
+
+ if ((name = index(cp ? cp : host, '/')) != NULL)
+ *(name++) = '\0';
+ if (host_ret)
+ strcpy(host_ret, host);
+ if (name && name_ret)
+ strcpy(name_ret, name);
+ return SUCCESS;
+}
+
+static FTP_t
+ftp_new(void)
+{
+ FTP_t ftp;
+
+ ftp = (FTP_t)malloc(sizeof *ftp);
+ if (!ftp)
+ return NULL;
+ memset(ftp, 0, sizeof *ftp);
+ ftp->fd_ctrl = -1;
+ ftp->con_state = init;
+ ftp->is_binary = FALSE;
+ ftp->is_passive = FALSE;
+ ftp->is_verbose = FALSE;
+ ftp->error = 0;
+ return ftp;
+}
+
+static int
+ftp_read_method(void *vp, char *buf, int nbytes)
+{
+ int i, fd;
+ FTP_t n = (FTP_t)vp;
+
+ fd = n->fd_ctrl;
+ i = (fd >= 0) ? read(fd, buf, nbytes) : EOF;
+ return i;
+}
+
+static int
+ftp_write_method(void *vp, const char *buf, int nbytes)
+{
+ int i, fd;
+ FTP_t n = (FTP_t)vp;
+
+ fd = n->fd_ctrl;
+ i = (fd >= 0) ? write(fd, buf, nbytes) : EOF;
+ return i;
+}
+
+static int
+ftp_close_method(void *n)
+{
+ int i;
+
+ i = ftp_close((FTP_t)n);
+ free(n);
+ return i;
+}
+
+static void
+check_passive(FILE *fp)
+{
+ if (getenv("FTP_PASSIVE_MODE"))
+ ftpPassive(fp, TRUE);
+}
+
+static void
+ftp_timeout(int sig)
+{
+ FtpTimedOut = TRUE;
+ /* Debug("ftp_pkg: ftp_timeout called - operation timed out"); */
+}
+
+static void
+ftp_set_timeout(void)
+{
+ struct sigaction new;
+ char *cp;
+ int ival;
+
+ FtpTimedOut = FALSE;
+ sigemptyset(&new.sa_mask);
+ new.sa_flags = 0;
+ new.sa_handler = ftp_timeout;
+ sigaction(SIGALRM, &new, NULL);
+ cp = getenv("FTP_TIMEOUT");
+ if (!cp || !(ival = atoi(cp)))
+ ival = 120;
+ alarm(ival);
+}
+
+static void
+ftp_clear_timeout(void)
+{
+ struct sigaction new;
+
+ alarm(0);
+ sigemptyset(&new.sa_mask);
+ new.sa_flags = 0;
+ new.sa_handler = SIG_DFL;
+ sigaction(SIGALRM, &new, NULL);
+}
+
+static int
+writes(int fd, char *s)
+{
+ int n, i = strlen(s);
+
+ ftp_set_timeout();
+ n = write(fd, s, i);
+ ftp_clear_timeout();
+ if (FtpTimedOut || i != n)
+ return TRUE;
+ return FALSE;
+}
+
+static __inline char *
+get_a_line(FTP_t ftp)
+{
+ static char buf[BUFSIZ];
+ int i,j;
+
+ /* Debug("ftp_pkg: trying to read a line from %d", ftp->fd_ctrl); */
+ for(i = 0; i < BUFSIZ;) {
+ ftp_set_timeout();
+ j = read(ftp->fd_ctrl, buf + i, 1);
+ ftp_clear_timeout();
+ if (FtpTimedOut || j != 1)
+ return NULL;
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ if (!i)
+ continue;
+ buf[i] = '\0';
+ if (ftp->is_verbose == TRUE)
+ fprintf(stderr, "%s\n",buf+4);
+ return buf;
+ }
+ i++;
+ }
+ /* Debug("ftp_pkg: read string \"%s\" from %d", buf, ftp->fd_ctrl); */
+ return buf;
+}
+
+static int
+get_a_number(FTP_t ftp, char **q)
+{
+ char *p;
+ int i = -1, j;
+
+ while(1) {
+ p = get_a_line(ftp);
+ if (!p) {
+ ftp_close(ftp);
+ if (FtpTimedOut)
+ return FTP_TIMED_OUT;
+ return FAILURE;
+ }
+ if (!(isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])))
+ continue;
+ if (i == -1 && p[3] == '-') {
+ i = strtol(p, 0, 0);
+ continue;
+ }
+ if (p[3] != ' ' && p[3] != '\t')
+ continue;
+ j = strtol(p, 0, 0);
+ if (i == -1) {
+ if (q) *q = p+4;
+ /* Debug("ftp_pkg: read reply %d from server (%s)", j, p); */
+ return j;
+ } else if (j == i) {
+ if (q) *q = p+4;
+ /* Debug("ftp_pkg: read reply %d from server (%s)", j, p); */
+ return j;
+ }
+ }
+}
+
+static int
+ftp_close(FTP_t ftp)
+{
+ int i, rcode;
+
+ rcode = FAILURE;
+ if (ftp->con_state == isopen) {
+ ftp->con_state = quit;
+ /* If last operation timed out, don't try to quit - just close */
+ if (ftp->error != FTP_TIMED_OUT)
+ i = cmd(ftp, "QUIT");
+ else
+ i = FTP_QUIT_HAPPY;
+ if (!check_code(ftp, i, FTP_QUIT_HAPPY))
+ rcode = SUCCESS;
+ close(ftp->fd_ctrl);
+ ftp->fd_ctrl = -1;
+ }
+ else if (ftp->con_state == quit)
+ rcode = SUCCESS;
+ return rcode;
+}
+
+static int
+botch(char *func, char *botch_state)
+{
+ /* Debug("ftp_pkg: botch: %s(%s)", func, botch_state); */
+ return FAILURE;
+}
+
+static int
+cmd(FTP_t ftp, const char *fmt, ...)
+{
+ char p[BUFSIZ];
+ int i;
+
+ va_list ap;
+ va_start(ap, fmt);
+ (void)vsnprintf(p, sizeof p, fmt, ap);
+ va_end(ap);
+
+ if (ftp->con_state == init)
+ return botch("cmd", "open");
+
+ strcat(p, "\r\n");
+ if (ftp->is_verbose)
+ fprintf(stderr, "Sending: %s", p);
+ if (writes(ftp->fd_ctrl, p)) {
+ if (FtpTimedOut)
+ return FTP_TIMED_OUT;
+ return FAILURE;
+ }
+ while ((i = get_a_number(ftp, NULL)) == FTP_HAPPY_COMMENT);
+ return i;
+}
+
+static int
+ftp_login_session(FTP_t ftp, char *host, char *user, char *passwd, int port, int verbose)
+{
+ struct hostent *he = NULL;
+ struct sockaddr_in sin;
+ int s;
+ unsigned long temp;
+ int i;
+
+ if (networkInit() != SUCCESS)
+ return FAILURE;
+
+ if (ftp->con_state != init) {
+ ftp_close(ftp);
+ ftp->error = -1;
+ return FAILURE;
+ }
+
+ if (!user)
+ user = "ftp";
+
+ if (!passwd)
+ passwd = "setup@";
+
+ if (!port)
+ port = 21;
+
+ temp = inet_addr(host);
+ if (temp != INADDR_NONE) {
+ ftp->addrtype = sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = temp;
+ }
+ else {
+ he = gethostbyname(host);
+ if (!he) {
+ ftp->error = 0;
+ return FAILURE;
+ }
+ ftp->addrtype = sin.sin_family = he->h_addrtype;
+ bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length);
+ }
+
+ sin.sin_port = htons(port);
+
+ if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0) {
+ ftp->error = -1;
+ return FAILURE;
+ }
+
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ (void)close(s);
+ ftp->error = errno;
+ return FAILURE;
+ }
+
+ ftp->fd_ctrl = s;
+ ftp->con_state = isopen;
+ ftp->is_verbose = verbose;
+
+ i = cmd(ftp, "USER %s", user);
+ if (i >= 300 && i < 400)
+ i = cmd(ftp, "PASS %s", passwd);
+ if (i >= 299 || i < 0) {
+ ftp_close(ftp);
+ if (i > 0)
+ ftp->error = i;
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static int
+ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode, off_t *seekto)
+{
+ int i,s;
+ char *q;
+ unsigned char addr[64];
+ struct sockaddr_in sin;
+ u_long a;
+
+ if (!fp)
+ return FAILURE;
+ *fp = NULL;
+
+ if (ftp->con_state != isopen)
+ return botch("ftp_file_op", "open");
+
+ if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0) {
+ ftp->error = errno;
+ return FAILURE;
+ }
+
+ if (ftp->is_passive) {
+ if (ftp->is_verbose)
+ fprintf(stderr, "Sending PASV\n");
+ if (writes(ftp->fd_ctrl, "PASV\r\n")) {
+ ftp_close(ftp);
+ if (FtpTimedOut)
+ ftp->error = FTP_TIMED_OUT;
+ return FTP_TIMED_OUT;
+ }
+ i = get_a_number(ftp, &q);
+ if (check_code(ftp, i, FTP_PASSIVE_HAPPY)) {
+ ftp_close(ftp);
+ return i;
+ }
+ while (*q && !isdigit(*q))
+ q++;
+ if (!*q) {
+ ftp_close(ftp);
+ return FAILURE;
+ }
+ q--;
+ for (i = 0; i < 6; i++) {
+ q++;
+ addr[i] = strtol(q, &q, 10);
+ }
+
+ sin.sin_family = ftp->addrtype;
+ bcopy(addr, (char *)&sin.sin_addr, 4);
+ bcopy(addr + 4, (char *)&sin.sin_port, 2);
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ (void)close(s);
+ return FAILURE;
+ }
+
+ if (seekto && *seekto) {
+ i = cmd(ftp, "REST %d", *seekto);
+ if (i < 0 || FTP_TIMEOUT(i)) {
+ close(s);
+ ftp->error = i;
+ *seekto = (off_t)0;
+ return i;
+ }
+ }
+ i = cmd(ftp, "%s %s", operation, file);
+ if (i < 0 || i > 299) {
+ close(s);
+ ftp->error = i;
+ return i;
+ }
+ *fp = fdopen(s, mode);
+ }
+ else {
+ int fd,portrange;
+
+#ifdef IP_PORTRANGE
+ portrange = IP_PORTRANGE_HIGH;
+ if (setsockopt(s, IPPROTO_IP, IP_PORTRANGE, (char *)
+ &portrange, sizeof(portrange)) < 0) {
+ close(s);
+ return FAILURE;
+ };
+#endif
+
+ i = sizeof sin;
+ getsockname(ftp->fd_ctrl, (struct sockaddr *)&sin, &i);
+ sin.sin_port = 0;
+ i = sizeof sin;
+ if (bind(s, (struct sockaddr *)&sin, i) < 0) {
+ close(s);
+ return FAILURE;
+ }
+ getsockname(s,(struct sockaddr *)&sin,&i);
+ if (listen(s, 1) < 0) {
+ close(s);
+ return FAILURE;
+ }
+ a = ntohl(sin.sin_addr.s_addr);
+ i = cmd(ftp, "PORT %d,%d,%d,%d,%d,%d",
+ (a >> 24) & 0xff,
+ (a >> 16) & 0xff,
+ (a >> 8) & 0xff,
+ a & 0xff,
+ (ntohs(sin.sin_port) >> 8) & 0xff,
+ ntohs(sin.sin_port) & 0xff);
+ if (check_code(ftp, i, FTP_PORT_HAPPY)) {
+ close(s);
+ return i;
+ }
+ if (seekto && *seekto) {
+ i = cmd(ftp, "REST %d", *seekto);
+ if (i < 0 || FTP_TIMEOUT(i)) {
+ close(s);
+ ftp->error = i;
+ return i;
+ }
+ else if (i != 350)
+ *seekto = (off_t)0;
+ }
+ i = cmd(ftp, "%s %s", operation, file);
+ if (i < 0 || i > 299) {
+ close(s);
+ ftp->error = i;
+ return FAILURE;
+ }
+ fd = accept(s, 0, 0);
+ if (fd < 0) {
+ close(s);
+ ftp->error = 401;
+ return FAILURE;
+ }
+ close(s);
+ *fp = fdopen(fd, mode);
+ }
+ if (*fp)
+ return SUCCESS;
+ else
+ return FAILURE;
+}
diff --git a/lib/libftpio/ftpio.h b/lib/libftpio/ftpio.h
new file mode 100644
index 0000000..26d4f90
--- /dev/null
+++ b/lib/libftpio/ftpio.h
@@ -0,0 +1,68 @@
+#ifndef _FTP_H_INCLUDE
+#define _FTP_H_INCLUDE
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <stdio.h>
+#include <time.h>
+
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * Major Changelog:
+ *
+ * Jordan K. Hubbard
+ * 17 Jan 1996
+ *
+ * Turned inside out. Now returns xfers as new file ids, not as a special
+ * `state' of FTP_t
+ *
+ * $FreeBSD$
+ */
+
+/* Internal housekeeping data structure for FTP sessions */
+typedef struct {
+ enum { init, isopen, quit } con_state;
+ int fd_ctrl;
+ int addrtype;
+ char *host;
+ char *file;
+ int error;
+ int is_binary;
+ int is_passive;
+ int is_verbose;
+} *FTP_t;
+
+/* Structure we use to match FTP error codes with readable strings */
+struct ftperr {
+ const int num;
+ const char *string;
+};
+
+__BEGIN_DECLS
+extern struct ftperr ftpErrList[];
+extern int const ftpErrListLength;
+
+/* Exported routines - deal only with FILE* type */
+extern FILE *ftpLogin(char *host, char *user, char *passwd, int port, int verbose, int *retcode);
+extern int ftpChdir(FILE *fp, char *dir);
+extern int ftpErrno(FILE *fp);
+extern off_t ftpGetSize(FILE *fp, char *file);
+extern FILE *ftpGet(FILE *fp, char *file, off_t *seekto);
+extern FILE *ftpPut(FILE *fp, char *file);
+extern int ftpAscii(FILE *fp);
+extern int ftpBinary(FILE *fp);
+extern int ftpPassive(FILE *fp, int status);
+extern void ftpVerbose(FILE *fp, int status);
+extern FILE *ftpGetURL(char *url, char *user, char *passwd, int *retcode);
+extern FILE *ftpPutURL(char *url, char *user, char *passwd, int *retcode);
+extern time_t ftpGetModtime(FILE *fp, char *s);
+extern const char *ftpErrString(int error);
+__END_DECLS
+
+#endif /* _FTP_H_INCLUDE */
OpenPOWER on IntegriCloud