summaryrefslogtreecommitdiffstats
path: root/lib/libftpio
diff options
context:
space:
mode:
authorjkh <jkh@FreeBSD.org>1996-06-17 12:26:06 +0000
committerjkh <jkh@FreeBSD.org>1996-06-17 12:26:06 +0000
commit880dd9d3c6fae1c0252b9a7ad8b0a3acd985acb6 (patch)
tree8f5d310b7f8f5268b7d1524b01546840e5a9b6f1 /lib/libftpio
parentd296466eaa9e2d412ac79232f8f0308f0488b67d (diff)
downloadFreeBSD-src-880dd9d3c6fae1c0252b9a7ad8b0a3acd985acb6.zip
FreeBSD-src-880dd9d3c6fae1c0252b9a7ad8b0a3acd985acb6.tar.gz
Bring in a new library `libftpio', so named to avoid clashes with older
packages and also sort of give the (correct) impression that this basically sits on top of stdio and deals with stream pointers (FILE*).
Diffstat (limited to 'lib/libftpio')
-rw-r--r--lib/libftpio/Makefile10
-rw-r--r--lib/libftpio/ftp_pkg.c257
-rw-r--r--lib/libftpio/ftp_pkg.h35
-rw-r--r--lib/libftpio/ftpio.3185
-rw-r--r--lib/libftpio/ftpio.c686
-rw-r--r--lib/libftpio/ftpio.h53
6 files changed, 1226 insertions, 0 deletions
diff --git a/lib/libftpio/Makefile b/lib/libftpio/Makefile
new file mode 100644
index 0000000..8e6b9c6
--- /dev/null
+++ b/lib/libftpio/Makefile
@@ -0,0 +1,10 @@
+LIB= ftpio
+CFLAGS+= -I${.CURDIR}
+SRCS= ftpio.c
+MAN3= ftpio.3
+
+beforeinstall:
+ cd ${.CURDIR}; cmp -s ftpio.h ${DESTDIR}/usr/include/ftpio.h || \
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ftpio.h ${DESTDIR}/usr/include
+
+.include <bsd.lib.mk>
diff --git a/lib/libftpio/ftp_pkg.c b/lib/libftpio/ftp_pkg.c
new file mode 100644
index 0000000..5622126
--- /dev/null
+++ b/lib/libftpio/ftp_pkg.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c)1995, 1996 Jordan Hubbard
+ *
+ * All rights reserved.
+ *
+ * This source code may be used, modified, copied, distributed, and
+ * sold, in both source and binary form provided that the above
+ * copyright and these terms are retained, verbatim, as the first
+ * lines of this file. Under no circumstances is the author
+ * responsible for the proper functioning of the software nor does
+ * the author assume any responsibility for damages incurred with
+ * its use.
+ *
+ * $Id$
+ *
+ * TCL Interface code for functions provided by the ftp library.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ftpio.h>
+#include "ftp_pkg.h"
+
+#ifndef TRUE
+#define TRUE (1)
+#define FALSE (0)
+#endif
+
+#define CHECK_ARGS(cnt, myname, str) \
+if (argc <= (cnt)) { sprintf(interp->result, "usage: %s %s", myname, str); return TCL_ERROR; }
+
+#define USAGE(myname, msg) \
+{ fprintf(stderr, "%s: %s\n", myname, msg); return TCL_ERROR; }
+
+
+/* Registration function */
+int
+Ftp_Init(Tcl_Interp *interp)
+{
+ Tcl_CreateCommand (interp, "ftp_login", Ftp_login, NULL, NULL);
+ Tcl_CreateCommand (interp, "ftp_chdir", Ftp_chdir, NULL, NULL);
+ Tcl_CreateCommand (interp, "ftp_getsize", Ftp_getsize, NULL, NULL);
+ Tcl_CreateCommand (interp, "ftp_get", Ftp_get, NULL, NULL);
+ Tcl_CreateCommand (interp, "ftp_put", Ftp_put, NULL, NULL);
+ Tcl_CreateCommand (interp, "ftp_binary", Ftp_binary, NULL, NULL);
+ Tcl_CreateCommand (interp, "ftp_passive", Ftp_passive, NULL, NULL);
+ Tcl_CreateCommand (interp, "ftp_get_url", Ftp_get_url, NULL, NULL);
+ Tcl_CreateCommand (interp, "ftp_put_url", Ftp_put_url, NULL, NULL);
+ return TCL_OK;
+}
+
+/*
+ * ftp_login host user passwd port
+ * -- returns new fileId
+ *
+ */
+int
+Ftp_login(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
+{
+ FILE *fp;
+ char *user, *pass;
+ int port;
+
+ CHECK_ARGS(1, argv[0], "host [user] [passwd] [port]");
+
+ user = (argc > 2) ? argv[2] : "ftp";
+ pass = (argc > 3) ? argv[3] : "setup@";
+ port = (argc > 4) ? atoi(argv[4]) : 21;
+ /* Debug("ftp_pkg: attempt login to host %s using %s/%s (port %d)", argv[1], user, pass, port); */
+ fp = ftpLogin(argv[1], user, pass, port);
+ if (fp) {
+ /* Debug("ftp_pkg: logged successfully into host %s", argv[1]); */
+ Tcl_EnterFile(interp, fp, TCL_FILE_READABLE | TCL_FILE_WRITABLE);
+ return TCL_OK;
+ }
+ /* Debug("ftp_pkg: login operation failed for host %s", argv[1]); */
+ return TCL_ERROR;
+}
+
+/*
+ * ftp_chdir file-handle newdir
+ * -- returns status
+ */
+int
+Ftp_chdir(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
+{
+ FILE *fp;
+
+ CHECK_ARGS(2, argv[0], "fileId directory");
+ if (Tcl_GetOpenFile(interp, argv[1], TRUE, TRUE, &fp) != TCL_OK)
+ return TCL_ERROR;
+ /* Debug("ftp_pkg: attempt chdir to dir %s", argv[2]); */
+ if (!ftpChdir(fp, argv[2])) {
+ /* Debug("ftp_pkg: chdir successful"); */
+ return TCL_OK;
+ }
+ /* Debug("ftp_pkg: chdir failed"); */
+ return TCL_ERROR;
+}
+
+/*
+ * ftp_getsize file-handle filename
+ * -- returns size
+ */
+int
+Ftp_getsize(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
+{
+ FILE *fp;
+ int sz;
+
+ CHECK_ARGS(2, argv[0], "fileId filename");
+ if (Tcl_GetOpenFile(interp, argv[1], TRUE, TRUE, &fp) != TCL_OK)
+ return TCL_ERROR;
+ /* Debug("ftp_pkg: attempt to get size of %s", argv[2]); */
+ if ((sz = ftpGetSize(fp, argv[2])) >= 0) {
+ /* Debug("ftp_pkg: getsize successful (%d)", sz); */
+ sprintf(interp->result, "%d", sz);
+ return TCL_OK;
+ }
+ /* Debug("ftp_pkg: chdir failed"); */
+ return TCL_ERROR;
+}
+
+/*
+ * ftp_get fileId filename
+ * -- returns new fileId for filename
+ *
+ */
+int
+Ftp_get(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
+{
+ FILE *fp, *fp2;
+
+ CHECK_ARGS(2, argv[0], "fileId filename");
+ if (Tcl_GetOpenFile(interp, argv[1], TRUE, TRUE, &fp) != TCL_OK)
+ return TCL_ERROR;
+ /* Debug("ftp_pkg: attempt to get file %s", argv[2]); */
+ fp2 = ftpGet(fp, argv[2]);
+ if (fp2) {
+ /* Debug("ftp_pkg: get operation successful for: %s", argv[2]); */
+ Tcl_EnterFile(interp, fp2, TCL_FILE_READABLE);
+ return TCL_OK;
+ }
+ /* Debug("ftp_pkg: get operation failed for file %s", argv[2]); */
+ return TCL_ERROR;
+}
+
+/*
+ * ftp_put fileId filename
+ * -- returns new fileId for filename
+ *
+ */
+int
+Ftp_put(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
+{
+ FILE *fp, *fp2;
+
+ CHECK_ARGS(2, argv[0], "fileId filename");
+ if (Tcl_GetOpenFile(interp, argv[1], TRUE, TRUE, &fp) != TCL_OK)
+ return TCL_ERROR;
+ /* Debug("ftp_pkg: attempt to put file %s", argv[2]); */
+ fp2 = ftpPut(fp, argv[2]);
+ if (fp2) {
+ /* Debug("ftp_pkg: put operation successful for: %s", argv[2]); */
+ Tcl_EnterFile(interp, fp2, TCL_FILE_READABLE);
+ return TCL_OK;
+ }
+ /* Debug("ftp_pkg: put operation failed for file %s", argv[2]); */
+ return TCL_ERROR;
+}
+
+/*
+ * ftp_binary fileId value
+ * -- Set binary mode to truth value for FTP session represented by fileId
+ *
+ */
+int
+Ftp_binary(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
+{
+ FILE *fp;
+
+ CHECK_ARGS(2, argv[0], "fileId bool");
+ if (Tcl_GetOpenFile(interp, argv[1], TRUE, TRUE, &fp) != TCL_OK)
+ return TCL_ERROR;
+ /* Debug("ftp_pkg: set binary mode to %d", atoi(argv[2])); */
+ ftpBinary(fp, atoi(argv[2]));
+ return TCL_OK;
+}
+
+/*
+ * ftp_passive fileId value
+ * -- Set passive mode to truth value for FTP session represented by fileId
+ *
+ */
+int
+Ftp_passive(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
+{
+ FILE *fp;
+
+ CHECK_ARGS(2, argv[0], "fileId bool");
+ if (Tcl_GetOpenFile(interp, argv[1], TRUE, TRUE, &fp) != TCL_OK)
+ return TCL_ERROR;
+ /* Debug("ftp_pkg: set passive mode to %d", atoi(argv[2])); */
+ ftpPassive(fp, atoi(argv[2]));
+ return TCL_OK;
+}
+
+/*
+ * ftp_get_url URL user pass
+ * -- Return new fileId for open URL (using user and pass to log in)
+ *
+ */
+int
+Ftp_get_url(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
+{
+ FILE *fp;
+ char *user, *pass;
+
+ CHECK_ARGS(1, argv[0], "URL [username] [password]");
+ user = (argc > 2) ? argv[2] : "ftp";
+ pass = (argc > 3) ? argv[3] : "setup@";
+ /* Debug("ftp_pkg: attempt to get URL %s as %s/%s", argv[1], user, pass); */
+ fp = ftpGetURL(argv[1], user, pass);
+ if (fp) {
+ /* Debug("ftp_pkg: get URL successful"); */
+ Tcl_EnterFile(interp, fp, TCL_FILE_READABLE);
+ return TCL_OK;
+ }
+ /* Debug("ftp_pkg: get URL failed"); */
+ return TCL_ERROR;
+}
+
+/*
+ * ftp_put_url URL user pass
+ * -- Return new fileId for open url (using user and pass to log in)
+ *
+ */
+int
+Ftp_put_url(ClientData clientData, Tcl_Interp *interp, int argc, char **argv)
+{
+ FILE *fp;
+ char *user, *pass;
+
+ CHECK_ARGS(1, argv[0], "URL [username] [password]");
+ user = (argc > 2) ? argv[2] : "ftp";
+ pass = (argc > 3) ? argv[3] : "setup@";
+ /* Debug("ftp_pkg: attempt to put URL %s as %s/%s", argv[1], user, pass); */
+ fp = ftpPutURL(argv[1], user, pass);
+ if (fp) {
+ /* Debug("ftp_pkg: put URL successful"); */
+ Tcl_EnterFile(interp, fp, TCL_FILE_READABLE);
+ return TCL_OK;
+ }
+ /* Debug("ftp_pkg: put URL failed"); */
+ return TCL_ERROR;
+}
diff --git a/lib/libftpio/ftp_pkg.h b/lib/libftpio/ftp_pkg.h
new file mode 100644
index 0000000..7b5982c
--- /dev/null
+++ b/lib/libftpio/ftp_pkg.h
@@ -0,0 +1,35 @@
+#ifndef _FTP_PKG_H
+#define _FTP_PKG_H
+
+/*
+ * Copyright (c)1995, 1996 Jordan Hubbard
+ *
+ * All rights reserved.
+ *
+ * This source code may be used, modified, copied, distributed, and
+ * sold, in both source and binary form provided that the above
+ * copyright and these terms are retained, verbatim, as the first
+ * lines of this file. Under no circumstances is the author
+ * responsible for the proper functioning of the software nor does
+ * the author assume any responsibility for damages incurred with
+ * its use.
+ *
+ * $Id$
+ *
+ * TCL Interface code for functions provided by the ftp library.
+ */
+
+#include <tcl.h>
+#include <ftpio.h>
+
+extern int Ftp_login (ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
+extern int Ftp_chdir (ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
+extern int Ftp_getsize (ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
+extern int Ftp_get (ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
+extern int Ftp_put (ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
+extern int Ftp_binary (ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
+extern int Ftp_passive (ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
+extern int Ftp_get_url (ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
+extern int Ftp_put_url (ClientData clientData, Tcl_Interp *interp, int argc, char **argv);
+
+#endif /* _FTP_PKG_H */
diff --git a/lib/libftpio/ftpio.3 b/lib/libftpio/ftpio.3
new file mode 100644
index 0000000..a53532e
--- /dev/null
+++ b/lib/libftpio/ftpio.3
@@ -0,0 +1,185 @@
+.\" 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.
+.\"
+.\"
+.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 ftpRestart ,
+.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"
+.Ft int
+.Fn ftpChdir "FILE *fp, char *dirname"
+.Ft int
+.Fn ftpErrno "FILE *fp"
+.Ft time_t
+.Fn ftpGetModtime "FILE *fp, char *file"
+.Ft size_t
+.Fn ftpGetSize "FILE *fp, char *file"
+.Ft FILE *
+.Fn ftpGet "FILE *fp, char *file"
+.Ft FILE *
+.Fn ftpPut "FILE *fp, char *file"
+.Ft int
+.Fn ftpBinary "FILE *fp, int status"
+.Ft int
+.Fn ftpPassive "FILE *fp, int status"
+.Ft int
+.Fn ftpRestart "FILE *fp, int where"
+.Ft FILE *
+.Fn ftpGetURL "char *url, char *user, char *passwd"
+.Ft FILE *
+.Fn ftpPutURL "char *url, char *user, char *passwd"
+
+.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
+and
+.Fa ftp_port
+fields (if passed as 0,
+.Fa ftp_port
+defaults to the standard ftp port of 21). If it is successful, a
+standard
+.Fa stream
+descriptor is returned which should be passed to subsequent FTP
+operations. On failure, NULL is returned and
+.Fn ftpErrno
+will return the error code returned by the foreign server.
+.Pp
+.Fn ftpChdir
+attempts to issue a server CD command to the the directory named in
+.Fa dir.
+On success, zero is returned. On failure, -1.
+.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).
+.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
+.Fa stream
+pointer for the file or NULL on failure.
+.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 ftpBinary
+sets binary mode for the current server connection named by
+.Fa stream
+to boolean value
+.Fa status .
+.Pp
+.Fn ftpPassive
+sets passive mode (for firewalls) for the current server connection named by
+.Fa stream
+to boolean value
+.Fa status .
+.Pp
+.Fn ftpRestart
+requests that if the remote server supports restart operations, the offset
+in bytes specified in
+.Fa where
+should be used in the next file get operation to resume transferring from
+that location. This is handy for restarting long get operations which have
+aborted in the middle without re-transferring wasted bytes. Returns the
+old seek value, if any.
+.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
+.Fa 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 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 and given a TCL interface (not enabled by default)
+by Jordan Hubbard. Also incorporates some ideas and extensions from
+Jean-Marc Zucconi.
diff --git a/lib/libftpio/ftpio.c b/lib/libftpio/ftpio.c
new file mode 100644
index 0000000..b5d58b8
--- /dev/null
+++ b/lib/libftpio/ftpio.c
@@ -0,0 +1,686 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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
+ *
+ * $Id: ftp.c,v 1.14 1995/06/11 19:29:55 rgrimes Exp $
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ftpio.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) (code == 421)
+
+/* Internal routines - deal only with internal FTP_t type */
+static FTP_t ftp_new(void);
+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);
+static int ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode);
+static int ftp_close(FTP_t ftp);
+static int get_url_info(char *url_in, char *host_ret, int *port_ret, char *name_ret);
+
+/* Global status variable - ick */
+int FtpTimedOut;
+
+/* FTP status codes */
+#define FTP_BINARY_HAPPY 200
+#define FTP_PORT_HAPPY 200
+#define FTP_QUIT_HAPPY 221
+#define FTP_TRANSFER_HAPPY 226
+#define FTP_PASSIVE_HAPPY 227
+#define FTP_CHDIR_HAPPY 250
+
+/*
+ * 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->errno = 0;
+ while (1) {
+ if (var == preferred)
+ return 0;
+ else if (var == 226) /* last operation succeeded */
+ var = get_a_number(ftp, NULL);
+ else if (var == 220) /* chit-chat */
+ var = get_a_number(ftp, NULL);
+ else if (var == 200) /* success codes */
+ var = get_a_number(ftp, NULL);
+ else {
+ ftp->errno = var;
+ return 1;
+ }
+ }
+}
+
+/* Returns a standard FILE pointer type representing an open control connection */
+FILE *
+ftpLogin(char *host, char *user, char *passwd, int port)
+{
+ FTP_t n;
+ FILE *fp;
+
+ if (networkInit() != SUCCESS)
+ return NULL;
+
+ n = ftp_new();
+ fp = NULL;
+ if (n && ftp_login_session(n, host, user, passwd, port) == SUCCESS) {
+ fp = funopen(n, ftp_read_method, ftp_write_method, NULL, ftp_close_method); /* BSD 4.4 function! */
+ /* Yuck, but can't use fdopen() because that also allocates an fp. Sigh! */
+ fp->_file = n->fd_ctrl;
+ }
+ if (n && !fp)
+ free(n);
+ return fp;
+}
+
+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 -1;
+ return SUCCESS;
+}
+
+int
+ftpErrno(FILE *fp)
+{
+ FTP_t ftp = fcookie(fp);
+ return ftp->errno;
+}
+
+int
+ftpRestart(FILE *fp, int where)
+{
+ FTP_t ftp = fcookie(fp);
+ int old = ftp->seek;
+
+ ftp->seek = where;
+ return old;
+}
+
+size_t
+ftpGetSize(FILE *fp, char *name)
+{
+ int i;
+ char p[BUFSIZ], *cp;
+ FTP_t ftp = fcookie(fp);
+
+ sprintf(p, "SIZE %s\r\n", name);
+ i = writes(ftp->fd_ctrl, p);
+ if (i)
+ return (size_t)-1;
+ i = get_a_number(ftp, &cp);
+ if (check_code(ftp, i, 213))
+ return (size_t)-1;
+ return (size_t)atoi(cp);
+}
+
+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;
+
+ sprintf(p, "MDTM %s\r\n", name);
+ i = writes(ftp->fd_ctrl, p);
+ if (i)
+ 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)
+{
+ FILE *fp2;
+ FTP_t ftp = fcookie(fp);
+
+ if (ftp_file_op(ftp, "RETR", file, &fp2, "r") == SUCCESS)
+ return fp2;
+ return NULL;
+}
+
+FILE *
+ftpPut(FILE *fp, char *file)
+{
+ FILE *fp2;
+ FTP_t ftp = fcookie(fp);
+
+ if (ftp_file_op(ftp, "STOR", file, &fp2, "w") == SUCCESS)
+ return fp2;
+ return NULL;
+}
+
+int
+ftpBinary(FILE *fp, int st)
+{
+ FTP_t ftp = fcookie(fp);
+
+ ftp->binary = st;
+ return SUCCESS;
+}
+
+int
+ftpPassive(FILE *fp, int st)
+{
+ FTP_t ftp = fcookie(fp);
+
+ ftp->passive = st;
+ return SUCCESS;
+}
+
+FILE *
+ftpGetURL(char *url, char *user, char *passwd)
+{
+ char host[255], name[255];
+ int port;
+ FILE *fp, *fp2;
+
+ if (get_url_info(url, host, &port, name) == SUCCESS) {
+ fp = ftpLogin(host, user, passwd, port);
+ if (fp) {
+ fp2 = ftpGet(fp, name);
+ fclose(fp);
+ return fp2;
+ }
+ }
+ return NULL;
+}
+
+FILE *
+ftpPutURL(char *url, char *user, char *passwd)
+{
+ char host[255], name[255];
+ int port;
+ FILE *fp, *fp2;
+
+ if (get_url_info(url, host, &port, name) == SUCCESS) {
+ fp = ftpLogin(host, user, passwd, port);
+ if (fp) {
+ fp2 = ftpPut(fp, name);
+ fclose(fp);
+ 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) != NULL)
+ 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->errno = 0;
+ ftp->seek = 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)
+{
+ return ftp_close((FTP_t)n);
+}
+
+static void
+ftp_timeout()
+{
+ FtpTimedOut = TRUE;
+ /* Debug("ftp_pkg: ftp_timeout called - operation timed out"); */
+}
+
+static int
+writes(int fd, char *s)
+{
+ int n, i = strlen(s);
+
+ /* Set the timer */
+ FtpTimedOut = FALSE;
+ signal(SIGALRM, ftp_timeout);
+ alarm(120);
+ /* Debug("ftp_pkg: writing \"%s\" to ftp connection %d", s, fd); */
+ n = write(fd, s, i);
+ alarm(0);
+ if (i != n)
+ return FAILURE;
+ return SUCCESS;
+}
+
+static __inline char *
+get_a_line(FTP_t ftp)
+{
+ static char buf[BUFSIZ];
+ int i,j;
+
+ /* Set the timer */
+ FtpTimedOut = FALSE;
+ signal(SIGALRM, ftp_timeout);
+
+ /* Debug("ftp_pkg: trying to read a line from %d", ftp->fd_ctrl); */
+ for(i = 0; i < BUFSIZ;) {
+ alarm(120);
+ j = read(ftp->fd_ctrl, buf + i, 1);
+ alarm(0);
+ if (j != 1)
+ return NULL;
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ if (!i)
+ continue;
+ buf[i] = '\0';
+ 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)
+ 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;
+
+ if (ftp->con_state == isopen) {
+ /* Debug("ftp_pkg: in ftp_close(), sending QUIT"); */
+ i = cmd(ftp, "QUIT");
+ close(ftp->fd_ctrl);
+ ftp->fd_ctrl = -1;
+ ftp->con_state = init;
+ if (check_code(ftp, i, FTP_QUIT_HAPPY)) {
+ ftp->errno = i;
+ return FAILURE;
+ }
+ /* Debug("ftp_pkg: ftp_close() - proper shutdown"); */
+ return SUCCESS;
+ }
+ /* Debug("ftp_pkg: ftp_close() - improper shutdown"); */
+ return FAILURE;
+}
+
+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 != isopen)
+ return botch("cmd", "open");
+
+ strcat(p, "\r\n");
+ i = writes(ftp->fd_ctrl, p);
+ if (i)
+ return FAILURE;
+ while ((i = get_a_number(ftp, NULL)) == 220);
+ return i;
+}
+
+static int
+ftp_login_session(FTP_t ftp, char *host, char *user, char *passwd, int port)
+{
+ 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);
+
+ 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)
+ 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)
+ return FAILURE;
+
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ (void)close(s);
+ return FAILURE;
+ }
+
+ ftp->fd_ctrl = s;
+ ftp->con_state = isopen;
+
+ 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);
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+static int
+ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode)
+{
+ 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 (ftp->binary) {
+ i = cmd(ftp, "TYPE I");
+ if (check_code(ftp, i, FTP_BINARY_HAPPY)) {
+ ftp_close(ftp);
+ return i;
+ }
+ }
+
+ if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0)
+ return FAILURE;
+
+ if (ftp->passive) {
+ if (writes(ftp->fd_ctrl, "PASV\r\n")) {
+ ftp_close(ftp);
+ return FAILURE;
+ }
+ i = get_a_number(ftp, &q);
+ if (check_code(ftp, i, FTP_PASSIVE_HAPPY))
+ 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 (!strcmp(operation, "RETR") && ftp->seek) {
+ i = cmd(ftp, "RETR %d", ftp->seek);
+ if (i < 0 || FTP_TIMEOUT(i)) {
+ close(s);
+ ftp->errno = i;
+ return i;
+ }
+ else if (i != 350)
+ ftp->seek = 0;
+ }
+ i = cmd(ftp, "%s %s", operation, file);
+ if (i < 0 || i > 299) {
+ close(s);
+ ftp->errno = i;
+ return i;
+ }
+ *fp = fdopen(s, mode);
+ }
+ else {
+ int fd;
+
+ 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 (!strcmp(operation, "RETR") && ftp->seek) {
+ i = cmd(ftp, "RETR %d", ftp->seek);
+ if (i < 0 || FTP_TIMEOUT(i)) {
+ close(s);
+ ftp->errno = i;
+ return i;
+ }
+ else if (i != 350)
+ ftp->seek = 0;
+ }
+ i = cmd(ftp, "%s %s", operation, file);
+ if (i < 0 || i > 299) {
+ close(s);
+ ftp->errno = i;
+ return FAILURE;
+ }
+ fd = accept(s, 0, 0);
+ if (fd < 0) {
+ close(s);
+ ftp->errno = 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..fc24ad7
--- /dev/null
+++ b/lib/libftpio/ftpio.h
@@ -0,0 +1,53 @@
+#ifndef _FTP_H_INCLUDE
+#define _FTP_H_INCLUDE
+
+#include <sys/types.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
+ *
+ * $Id$
+ */
+
+/* Internal housekeeping data structure for FTP sessions */
+typedef struct {
+ enum { init, isopen } con_state;
+ int fd_ctrl;
+ int binary;
+ int passive;
+ int addrtype;
+ char *host;
+ char *file;
+ int errno;
+ int seek;
+} *FTP_t;
+
+/* Exported routines - deal only with FILE* type */
+extern FILE *ftpLogin(char *host, char *user, char *passwd, int port);
+extern int ftpChdir(FILE *fp, char *dir);
+extern int ftpErrno(FILE *fp);
+extern size_t ftpGetSize(FILE *fp, char *file);
+extern FILE *ftpGet(FILE *fp, char *file);
+extern FILE *ftpPut(FILE *fp, char *file);
+extern int ftpBinary(FILE *fp, int status);
+extern int ftpPassive(FILE *fp, int status);
+extern int ftpRestart(FILE *fp, int where);
+extern FILE *ftpGetURL(char *url, char *user, char *passwd);
+extern FILE *ftpPutURL(char *url, char *user, char *passwd);
+extern time_t ftpModtime(FILE *fp, char *s);
+
+#endif /* _FTP_H_INCLUDE */
OpenPOWER on IntegriCloud