From 880dd9d3c6fae1c0252b9a7ad8b0a3acd985acb6 Mon Sep 17 00:00:00 2001 From: jkh Date: Mon, 17 Jun 1996 12:26:06 +0000 Subject: 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*). --- lib/libftpio/Makefile | 10 + lib/libftpio/ftp_pkg.c | 257 ++++++++++++++++++ lib/libftpio/ftp_pkg.h | 35 +++ lib/libftpio/ftpio.3 | 185 +++++++++++++ lib/libftpio/ftpio.c | 686 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/libftpio/ftpio.h | 53 ++++ 6 files changed, 1226 insertions(+) create mode 100644 lib/libftpio/Makefile create mode 100644 lib/libftpio/ftp_pkg.c create mode 100644 lib/libftpio/ftp_pkg.h create mode 100644 lib/libftpio/ftpio.3 create mode 100644 lib/libftpio/ftpio.c create mode 100644 lib/libftpio/ftpio.h (limited to 'lib/libftpio') 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 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 +#include +#include +#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 +#include + +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 +.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): + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * 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 */ -- cgit v1.1