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*).
This commit is contained in:
Jordan K. Hubbard 1996-06-17 12:26:06 +00:00
parent baebc39ab5
commit 31caf7f20f
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/FREEBSD/; revision=16420
6 changed files with 1226 additions and 0 deletions

10
lib/libftpio/Makefile Normal file
View File

@ -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>

257
lib/libftpio/ftp_pkg.c Normal file
View File

@ -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;
}

35
lib/libftpio/ftp_pkg.h Normal file
View File

@ -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 */

185
lib/libftpio/ftpio.3 Normal file
View File

@ -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.

686
lib/libftpio/ftpio.c Normal file
View File

@ -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;
}

53
lib/libftpio/ftpio.h Normal file
View File

@ -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 */