Initial import of FreeBSD libftpio.

Placed here (under pkg_install) to make it private to that tool.
This commit is contained in:
agc 1997-06-05 09:11:59 +00:00
parent 5d4508e153
commit 5d78b65530
5 changed files with 1200 additions and 7 deletions

View File

@ -1,10 +1,28 @@
LIB= install
SRCS= file.c msg.c plist.c str.c exec.c global.c pen.c
CFLAGS+= ${DEBUG}
NOPROFILE= yes
NOPIC= yes
LIB= ftpio
CFLAGS+= -I${.CURDIR} -Wall
SRCS= ftpio.c ftperr.c
MAN3= ftpio.3
CLEANFILES+= ftperr.c
install:
@echo -n
SHLIB_MAJOR= 4
SHLIB_MINOR= 0
beforeinstall:
${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/ftpio.h \
${DESTDIR}/usr/include
ftperr.c: ftp.errors
@echo '#include <stdio.h>' > ${.TARGET}
@echo '#include "ftpio.h"' >> ${.TARGET}
@echo "struct ftperr ftpErrList[] = {" \ >> ${.TARGET}
@cat ${.ALLSRC} \
| grep -v ^# \
| sort \
| while read NUM STRING; do \
echo " { $${NUM}, \"$${STRING}\" },"; \
done >> ${.TARGET}
@echo "};" >> ${.TARGET}
@echo -n "int const ftpErrListLength = " >> ${.TARGET}
@echo "sizeof(ftpErrList) / sizeof(*ftpErrList);" >> ${.TARGET}
.include <bsd.lib.mk>

View File

@ -0,0 +1,44 @@
# FreeBSD - Id: ftp.errors,v 1.3 1997/02/22 15:06:47 peter Exp
#
# This list is taken from RFC 959.
# It probably needs a going over.
#
110 Restart marker reply
120 Service ready in a few minutes
125 Data connection already open; transfer starting
150 File status okay; about to open data connection
200 Command okay
202 Command not implemented, superfluous at this site
211 System status, or system help reply
212 Directory status
213 File status
214 Help message
215 Set system type
220 Service ready for new user
221 Service closing control connection
225 Data connection open; no transfer in progress
226 Requested file action successful
227 Entering Passive Mode
230 User logged in, proceed
250 Requested file action okay, completed
257 File/directory created
331 User name okay, need password
332 Need account for login
350 Requested file action pending further information
421 Service not available, closing control connection
425 Can't open data connection
426 Connection closed; transfer aborted
450 File unavailable (e.g., file busy)
451 Requested action aborted: local error in processing
452 Insufficient storage space in system
500 Syntax error, command unrecognized
501 Syntax error in parameters or arguments
502 Command not implemented
503 Bad sequence of commands
504 Command not implemented for that parameter
530 Not logged in
532 Need account for storing files
550 File unavailable (e.g., file not found, no access)
551 Requested action aborted. Page type unknown
552 Exceeded storage allocation
553 File name not allowed

View File

@ -0,0 +1,198 @@
.\" Copyright (c) 1996 Jordan Hubbard (jkh@FreeBSD.org)
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" FreeBSD Id: ftpio.3,v 1.15 1997/04/19 15:57:15 bde Exp
.\"
.Dd June 17, 1996
.Dt ftpio 3
.Os
.Sh NAME
.Nm ftpLogin ,
.Nm ftpChdir ,
.Nm ftpErrno ,
.Nm ftpGetModtime ,
.Nm ftpGetSize ,
.Nm ftpGet ,
.Nm ftpPut ,
.Nm ftpBinary ,
.Nm ftpPassive ,
.Nm ftpVerbose ,
.Nm ftpGetURL ,
.Nm ftpPutURL
.Nd FTPIO User library
.Sh SYNOPSIS
.Fd #include <ftpio.h>
.Ft FILE *
.Fn ftpLogin "char *host" "char *user" "char *passwd" "int ftp_port" "int verbose" "int *retcode"
.Ft int
.Fn ftpChdir "FILE *stream, char *dirname"
.Ft int
.Fn ftpErrno "FILE *stream"
.Ft const char *
.Fn ftpErrString "int errno"
.Ft time_t
.Fn ftpGetModtime "FILE *stream, char *file"
.Ft off_t
.Fn ftpGetSize "FILE *stream, char *file"
.Ft FILE *
.Fn ftpGet "FILE *stream, char *file, off_t *seekto"
.Ft FILE *
.Fn ftpPut "FILE *stream, char *file"
.Ft int
.Fn ftpAscii "FILE *stream"
.Ft int
.Fn ftpBinary "FILE *stream"
.Ft int
.Fn ftpPassive "FILE *stream, int status"
.Ft void
.Fn ftpVerbose "FILE *stream, int status"
.Ft FILE *
.Fn ftpGetURL "char *url, char *user, char *passwd, int *retcode"
.Ft FILE *
.Fn ftpPutURL "char *url, char *user, char *passwd, int *retcode"
.Sh DESCRIPTION
These functions implement a high-level library for managing FTP connections.
.Pp
.Fn ftpLogin
attempts to log in using the supplied
.Fa user ,
.Fa passwd,
.Fa ftp_port
(if passed as 0,
.Fa ftp_port
defaults to the standard ftp port of 21) and
.Fa verbose
fields. If it is successful, a
standard stream descriptor is returned which should be passed to
subsequent FTP operations. On failure, NULL is returned and
.Fa retcode
will have the error code returned by the foreign server.
.Pp
.Fn ftpChdir
attempts to issue a server CD command to the directory named in
.Fa dir.
On success, zero is returned. On failure, the error code from the server.
.Pp
.Fn ftpErrno
returns the server failure code for the last operation (useful for seeing
more about what happened if you're familiar with FTP error codes).
.Fn ftpErrString
returns a human readable version of the supplied server failure code.
.Pp
.Fn ftpGet
attempts to retreive the file named by the
.Fa file
argument (which is assumed to be relative to the FTP server's current directory,
see
.Fn ftpChdir )
and returns a new FILE* pointer for the file or NULL on failure. If
.Fa seekto
is non-NULL, the contents of the integer it points to will be used
as a restart point for the file, that is to say that the stream
returned will point
.Fa *seekto
bytes into the file gotten (this is handy for restarting failed
transfers efficiently). If the seek operation fails, the value
of
.Fa *seekto
will be zero'd.
.Pp
.Fn ftpGetModtime
returns the last modification time of the file named by the
.Fa file
argument. If the file could not be opened or stat'd, 0 is returned.
.Pp
.Fn ftpGetSize
returns the size in bytes of the file named by the
.Fa file
argument. If the file could not be opened or stat'd, -1 is returned.
.Pp
.Fn ftpPut
attempts to create a new file named by the
.Fa file
argument (which is assumed to be relative to the FTP server's current directory,
see
.Fn ftpChdir )
and returns a new
.Fa stream
pointer for the file or NULL on failure.
.Pp
.Fn ftpAscii
sets ascii mode for the current server connection named by
.Fa stream .
.Pp
.Fn ftpBinary
sets binary mode for the current server connection named by
.Fa stream .
.Pp
.Fn ftpPassive
sets passive mode (for firewalls) for the current server connection named by
.Fa stream
to boolean value
.Fa status .
.Pp
.Fn ftpVerbose
sets the verbosity mode for the current server connection named by
.Fa stream
to boolean value
.Fa status .
.Pp
.Fn ftpGetURL
attempts to retreive the file named by the supplied
.Fa URL
and can be considered equivalent to the combined
.Fn ftpLogin ,
.Fn ftpChdir
and
.Fn ftpGet
operations except that no server
.Fa stream
is ever returned - the connection to the server closes when
the file has been completely read. Use the lower-level routines
if multiple gets are required as it will be far more efficient.
.Pp
.Fn ftpPutURL
attempts to create the file named by the supplied
.Fa URL
and can be considered equivalent to the combined
.Fn ftpLogin ,
.Fn ftpChdir
and
.Fn ftpPut
operations except that no server stream is ever returned - the connection
to the server closes when the file has been completely written. Use the
lower-level routines if multiple puts are required as it will be far more
efficient.
.Sh BUGS
I'm sure you can get this thing's internal state machine confused if
you really work at it, but so far it's proven itself pretty robust in
all my tests.
.Sh HISTORY
Started life as Poul-Henning Kamp's ftp driver for the system installation
utility, later significantly mutated into a more general form as an
extension of stdio by Jordan Hubbard. Also incorporates some ideas and
extensions from Jean-Marc Zucconi.
.Sh AUTHORS
Jordan Hubbard, Poul-Henning Kamp and Jean-Marc Zucconi

View File

@ -0,0 +1,865 @@
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*
* Major Changelog:
*
* Jordan K. Hubbard
* 17 Jan 1996
*
* Turned inside out. Now returns xfers as new file ids, not as a special
* `state' of FTP_t
*
* FreeBSD Id: ftpio.c,v 1.25 1997/02/22 15:06:50 peter Exp
*
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <ftpio.h>
#include <netdb.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define SUCCESS 0
#define FAILURE -1
#ifndef TRUE
#define TRUE (1)
#define FALSE (0)
#endif
/* How to see by a given code whether or not the connection has timed out */
#define FTP_TIMEOUT(code) (FtpTimedOut || code == FTP_TIMED_OUT)
/* Internal routines - deal only with internal FTP_t type */
static FTP_t ftp_new(void);
static void check_passive(FILE *fp);
static int ftp_read_method(void *n, char *buf, int nbytes);
static int ftp_write_method(void *n, const char *buf, int nbytes);
static int ftp_close_method(void *n);
static int writes(int fd, char *s);
static __inline char *get_a_line(FTP_t ftp);
static int get_a_number(FTP_t ftp, char **q);
static int botch(char *func, char *botch_state);
static int cmd(FTP_t ftp, const char *fmt, ...);
static int ftp_login_session(FTP_t ftp, char *host, char *user, char *passwd, int port, int verbose);
static int ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode, off_t *seekto);
static int ftp_close(FTP_t ftp);
static int get_url_info(char *url_in, char *host_ret, int *port_ret, char *name_ret);
static void ftp_timeout(int sig);
static void ftp_set_timeout(void);
static void ftp_clear_timeout(void);
/* Global status variable - ick */
int FtpTimedOut;
/* FTP happy status codes */
#define FTP_GENERALLY_HAPPY 200
#define FTP_ASCII_HAPPY FTP_GENERALLY_HAPPY
#define FTP_BINARY_HAPPY FTP_GENERALLY_HAPPY
#define FTP_PORT_HAPPY FTP_GENERALLY_HAPPY
#define FTP_HAPPY_COMMENT 220
#define FTP_QUIT_HAPPY 221
#define FTP_TRANSFER_HAPPY 226
#define FTP_PASSIVE_HAPPY 227
#define FTP_CHDIR_HAPPY 250
/* FTP unhappy status codes */
#define FTP_TIMED_OUT 421
/*
* XXX
* gross! evil! bad! We really need an access primitive for cookie in stdio itself.
* it's too convenient a hook to bury and it's already exported through funopen as it is, so...
* XXX
*/
#define fcookie(fp) ((fp)->_cookie)
/* Placeholder in case we want to do any pre-init stuff at some point */
int
networkInit()
{
return SUCCESS; /* XXX dummy function for now XXX */
}
/* Check a return code with some lenience for back-dated garbage that might be in the buffer */
static int
check_code(FTP_t ftp, int var, int preferred)
{
ftp->errno = 0;
while (1) {
if (var == preferred)
return 0;
else if (var == FTP_TRANSFER_HAPPY) /* last operation succeeded */
var = get_a_number(ftp, NULL);
else if (var == FTP_HAPPY_COMMENT) /* chit-chat */
var = get_a_number(ftp, NULL);
else if (var == FTP_GENERALLY_HAPPY) /* general success code */
var = get_a_number(ftp, NULL);
else {
ftp->errno = var;
return 1;
}
}
}
int
ftpAscii(FILE *fp)
{
FTP_t ftp = fcookie(fp);
int i;
if (!ftp->is_binary)
return SUCCESS;
i = cmd(ftp, "TYPE A");
if (i < 0 || check_code(ftp, i, FTP_ASCII_HAPPY))
return i;
ftp->is_binary = FALSE;
return SUCCESS;
}
int
ftpBinary(FILE *fp)
{
FTP_t ftp = fcookie(fp);
int i;
if (ftp->is_binary)
return SUCCESS;
i = cmd(ftp, "TYPE I");
if (i < 0 || check_code(ftp, i, FTP_BINARY_HAPPY))
return i;
ftp->is_binary = TRUE;
return SUCCESS;
}
void
ftpVerbose(FILE *fp, int status)
{
FTP_t ftp = fcookie(fp);
ftp->is_verbose = status;
}
int
ftpChdir(FILE *fp, char *dir)
{
int i;
FTP_t ftp = fcookie(fp);
i = cmd(ftp, "CWD %s", dir);
if (i < 0 || check_code(ftp, i, FTP_CHDIR_HAPPY))
return i;
return SUCCESS;
}
int
ftpErrno(FILE *fp)
{
FTP_t ftp = fcookie(fp);
return ftp->errno;
}
const char *
ftpErrString(int errno)
{
int k;
if (errno == -1)
return("connection in wrong state");
for (k = 0; k < ftpErrListLength; k++)
if (ftpErrList[k].num == errno)
return(ftpErrList[k].string);
return("Unknown error");
}
off_t
ftpGetSize(FILE *fp, char *name)
{
int i;
char p[BUFSIZ], *cp, *ep;
FTP_t ftp = fcookie(fp);
off_t size;
check_passive(fp);
sprintf(p, "SIZE %s\r\n", name);
if (ftp->is_verbose)
fprintf(stderr, "Sending %s", p);
if (writes(ftp->fd_ctrl, p))
return (off_t)-1;
i = get_a_number(ftp, &cp);
if (check_code(ftp, i, 213))
return (off_t)-1;
errno = 0; /* to check for ERANGE */
size = (off_t)strtoq(cp, &ep, 10);
if (*ep != '\0' || errno == ERANGE)
return (off_t)-1;
return size;
}
time_t
ftpGetModtime(FILE *fp, char *name)
{
char p[BUFSIZ], *cp;
struct tm t;
time_t t0 = time (0);
FTP_t ftp = fcookie(fp);
int i;
check_passive(fp);
sprintf(p, "MDTM %s\r\n", name);
if (ftp->is_verbose)
fprintf(stderr, "Sending %s", p);
if (writes(ftp->fd_ctrl, p))
return (time_t)0;
i = get_a_number(ftp, &cp);
if (check_code(ftp, i, 213))
return (time_t)0;
while (*cp && !isdigit(*cp))
cp++;
if (!*cp)
return (time_t)0;
t0 = localtime (&t0)->tm_gmtoff;
sscanf(cp, "%04d%02d%02d%02d%02d%02d", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec);
t.tm_mon--;
t.tm_year -= 1900;
t.tm_isdst=-1;
t.tm_gmtoff = 0;
t0 += mktime (&t);
return t0;
}
FILE *
ftpGet(FILE *fp, char *file, off_t *seekto)
{
FILE *fp2;
FTP_t ftp = fcookie(fp);
check_passive(fp);
if (ftpBinary(fp) != SUCCESS)
return NULL;
if (ftp_file_op(ftp, "RETR", file, &fp2, "r", seekto) == SUCCESS)
return fp2;
return NULL;
}
/* Returns a standard FILE pointer type representing an open control connection */
FILE *
ftpLogin(char *host, char *user, char *passwd, int port, int verbose, int *retcode)
{
FTP_t n;
FILE *fp;
if (retcode)
*retcode = 0;
if (networkInit() != SUCCESS)
return NULL;
n = ftp_new();
fp = NULL;
if (n && ftp_login_session(n, host, user, passwd, port, verbose) == SUCCESS) {
fp = funopen(n, ftp_read_method, ftp_write_method, NULL, ftp_close_method); /* BSD 4.4 function! */
fp->_file = n->fd_ctrl;
}
if (retcode) {
if (!n)
*retcode = (FtpTimedOut ? FTP_TIMED_OUT : -1);
/* Poor attempt at mapping real errnos to FTP error codes */
else switch(n->errno) {
case EADDRNOTAVAIL:
*retcode = FTP_TIMED_OUT; /* Actually no such host, but we have no way of saying that. :-( */
break;
case ETIMEDOUT:
*retcode = FTP_TIMED_OUT;
break;
default:
*retcode = n->errno;
break;
}
}
return fp;
}
FILE *
ftpPut(FILE *fp, char *file)
{
FILE *fp2;
FTP_t ftp = fcookie(fp);
check_passive(fp);
if (ftp_file_op(ftp, "STOR", file, &fp2, "w", NULL) == SUCCESS)
return fp2;
return NULL;
}
/* Unlike binary mode, passive mode is a toggle! :-( */
int
ftpPassive(FILE *fp, int st)
{
FTP_t ftp = fcookie(fp);
int i;
if (ftp->is_passive == st)
return SUCCESS;
i = cmd(ftp, "PASV");
if (i < 0)
return i;
ftp->is_passive = !ftp->is_passive;
return SUCCESS;
}
FILE *
ftpGetURL(char *url, char *user, char *passwd, int *retcode)
{
char host[255], name[255];
int port;
FILE *fp2;
static FILE *fp = NULL;
static char *prev_host;
if (retcode)
*retcode = 0;
if (get_url_info(url, host, &port, name) == SUCCESS) {
if (fp && prev_host) {
if (!strcmp(prev_host, host)) {
/* Try to use cached connection */
fp2 = ftpGet(fp, name, NULL);
if (!fp2) {
/* Connection timed out or was no longer valid */
fclose(fp);
free(prev_host);
prev_host = NULL;
}
else
return fp2;
}
else {
/* It's a different host now, flush old */
fclose(fp);
free(prev_host);
prev_host = NULL;
}
}
fp = ftpLogin(host, user, passwd, port, 0, retcode);
if (fp) {
fp2 = ftpGet(fp, name, NULL);
if (!fp2) {
/* Connection timed out or was no longer valid */
if (retcode)
*retcode = ftpErrno(fp);
fclose(fp);
fp = NULL;
}
else
prev_host = strdup(host);
return fp2;
}
}
return NULL;
}
FILE *
ftpPutURL(char *url, char *user, char *passwd, int *retcode)
{
char host[255], name[255];
int port;
static FILE *fp = NULL;
FILE *fp2;
if (retcode)
*retcode = 0;
if (fp) { /* Close previous managed connection */
fclose(fp);
fp = NULL;
}
if (get_url_info(url, host, &port, name) == SUCCESS) {
fp = ftpLogin(host, user, passwd, port, 0, retcode);
if (fp) {
fp2 = ftpPut(fp, name);
if (!fp2) {
if (retcode)
*retcode = ftpErrno(fp);
fclose(fp);
fp = NULL;
}
return fp2;
}
}
return NULL;
}
/* Internal workhorse function for dissecting URLs. Takes a URL as the first argument and returns the
result of such disection in the host, user, passwd, port and name variables. */
static int
get_url_info(char *url_in, char *host_ret, int *port_ret, char *name_ret)
{
char *name, *host, *cp, url[BUFSIZ];
int port;
name = host = NULL;
/* XXX add http:// here or somewhere reasonable at some point XXX */
if (strncmp("ftp://", url_in, 6) != 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->is_binary = FALSE;
ftp->is_passive = FALSE;
ftp->is_verbose = FALSE;
ftp->errno = 0;
return ftp;
}
static int
ftp_read_method(void *vp, char *buf, int nbytes)
{
int i, fd;
FTP_t n = (FTP_t)vp;
fd = n->fd_ctrl;
i = (fd >= 0) ? read(fd, buf, nbytes) : EOF;
return i;
}
static int
ftp_write_method(void *vp, const char *buf, int nbytes)
{
int i, fd;
FTP_t n = (FTP_t)vp;
fd = n->fd_ctrl;
i = (fd >= 0) ? write(fd, buf, nbytes) : EOF;
return i;
}
static int
ftp_close_method(void *n)
{
int i;
i = ftp_close((FTP_t)n);
free(n);
return i;
}
static void
check_passive(FILE *fp)
{
if (getenv("FTP_PASSIVE_MODE"))
ftpPassive(fp, TRUE);
}
static void
ftp_timeout(int sig)
{
FtpTimedOut = TRUE;
/* Debug("ftp_pkg: ftp_timeout called - operation timed out"); */
}
static struct sigaction new;
static void
ftp_set_timeout(void)
{
char *cp;
int ival;
FtpTimedOut = FALSE;
new.sa_handler = ftp_timeout;
sigaction(SIGALRM, &new, NULL);
cp = getenv("FTP_TIMEOUT");
if (!cp || !(ival = atoi(cp)))
ival = 120;
alarm(ival);
}
static void
ftp_clear_timeout(void)
{
alarm(0);
new.sa_handler = SIG_DFL;
sigaction(SIGALRM, &new, NULL);
}
static int
writes(int fd, char *s)
{
int n, i = strlen(s);
ftp_set_timeout();
n = write(fd, s, i);
ftp_clear_timeout();
if (FtpTimedOut || i != n)
return TRUE;
return FALSE;
}
static __inline char *
get_a_line(FTP_t ftp)
{
static char buf[BUFSIZ];
int i,j;
/* Debug("ftp_pkg: trying to read a line from %d", ftp->fd_ctrl); */
for(i = 0; i < BUFSIZ;) {
ftp_set_timeout();
j = read(ftp->fd_ctrl, buf + i, 1);
ftp_clear_timeout();
if (FtpTimedOut || j != 1)
return NULL;
if (buf[i] == '\r' || buf[i] == '\n') {
if (!i)
continue;
buf[i] = '\0';
if (ftp->is_verbose == TRUE)
fprintf(stderr, "%s\n",buf+4);
return buf;
}
i++;
}
/* Debug("ftp_pkg: read string \"%s\" from %d", buf, ftp->fd_ctrl); */
return buf;
}
static int
get_a_number(FTP_t ftp, char **q)
{
char *p;
int i = -1, j;
while(1) {
p = get_a_line(ftp);
if (!p) {
ftp_close(ftp);
if (FtpTimedOut)
return FTP_TIMED_OUT;
return FAILURE;
}
if (!(isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])))
continue;
if (i == -1 && p[3] == '-') {
i = strtol(p, 0, 0);
continue;
}
if (p[3] != ' ' && p[3] != '\t')
continue;
j = strtol(p, 0, 0);
if (i == -1) {
if (q) *q = p+4;
/* Debug("ftp_pkg: read reply %d from server (%s)", j, p); */
return j;
} else if (j == i) {
if (q) *q = p+4;
/* Debug("ftp_pkg: read reply %d from server (%s)", j, p); */
return j;
}
}
}
static int
ftp_close(FTP_t ftp)
{
int i;
if (ftp->con_state == isopen) {
ftp->con_state = quit;
/* If last operation timed out, don't try to quit - just close */
if (ftp->errno != FTP_TIMED_OUT)
i = cmd(ftp, "QUIT");
else
i = FTP_QUIT_HAPPY;
close(ftp->fd_ctrl);
ftp->fd_ctrl = -1;
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 == init)
return botch("cmd", "open");
strcat(p, "\r\n");
if (ftp->is_verbose)
fprintf(stderr, "Sending: %s", p);
if (writes(ftp->fd_ctrl, p)) {
if (FtpTimedOut)
return FTP_TIMED_OUT;
return FAILURE;
}
while ((i = get_a_number(ftp, NULL)) == FTP_HAPPY_COMMENT);
return i;
}
static int
ftp_login_session(FTP_t ftp, char *host, char *user, char *passwd, int port, int verbose)
{
struct hostent *he = NULL;
struct sockaddr_in sin;
int s;
unsigned long temp;
int i;
if (networkInit() != SUCCESS)
return FAILURE;
if (ftp->con_state != init) {
ftp_close(ftp);
ftp->errno = -1;
return FAILURE;
}
if (!user)
user = "ftp";
if (!passwd)
passwd = "setup@";
if (!port)
port = 21;
temp = inet_addr(host);
if (temp != INADDR_NONE) {
ftp->addrtype = sin.sin_family = AF_INET;
sin.sin_addr.s_addr = temp;
}
else {
he = gethostbyname(host);
if (!he) {
ftp->errno = 0;
return FAILURE;
}
ftp->addrtype = sin.sin_family = he->h_addrtype;
bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length);
}
sin.sin_port = htons(port);
if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0) {
ftp->errno = -1;
return FAILURE;
}
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
(void)close(s);
ftp->errno = errno;
return FAILURE;
}
ftp->fd_ctrl = s;
ftp->con_state = isopen;
ftp->is_verbose = verbose;
i = cmd(ftp, "USER %s", user);
if (i >= 300 && i < 400)
i = cmd(ftp, "PASS %s", passwd);
if (i >= 299 || i < 0) {
ftp_close(ftp);
if (i > 0)
ftp->errno = i;
return FAILURE;
}
return SUCCESS;
}
static int
ftp_file_op(FTP_t ftp, char *operation, char *file, FILE **fp, char *mode, off_t *seekto)
{
int i,s;
char *q;
unsigned char addr[64];
struct sockaddr_in sin;
u_long a;
if (!fp)
return FAILURE;
*fp = NULL;
if (ftp->con_state != isopen)
return botch("ftp_file_op", "open");
if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0) {
ftp->errno = errno;
return FAILURE;
}
if (ftp->is_passive) {
if (ftp->is_verbose)
fprintf(stderr, "Sending PASV\n");
if (writes(ftp->fd_ctrl, "PASV\r\n")) {
ftp_close(ftp);
if (FtpTimedOut)
ftp->errno = FTP_TIMED_OUT;
return FTP_TIMED_OUT;
}
i = get_a_number(ftp, &q);
if (check_code(ftp, i, FTP_PASSIVE_HAPPY)) {
ftp_close(ftp);
return i;
}
while (*q && !isdigit(*q))
q++;
if (!*q) {
ftp_close(ftp);
return FAILURE;
}
q--;
for (i = 0; i < 6; i++) {
q++;
addr[i] = strtol(q, &q, 10);
}
sin.sin_family = ftp->addrtype;
bcopy(addr, (char *)&sin.sin_addr, 4);
bcopy(addr + 4, (char *)&sin.sin_port, 2);
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
(void)close(s);
return FAILURE;
}
if (seekto && *seekto) {
i = cmd(ftp, "REST %d", *seekto);
if (i < 0 || FTP_TIMEOUT(i)) {
close(s);
ftp->errno = i;
*seekto = (off_t)0;
return i;
}
}
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 (seekto && *seekto) {
i = cmd(ftp, "REST %d", *seekto);
if (i < 0 || FTP_TIMEOUT(i)) {
close(s);
ftp->errno = i;
return i;
}
else if (i != 350)
*seekto = (off_t)0;
}
i = cmd(ftp, "%s %s", operation, file);
if (i < 0 || i > 299) {
close(s);
ftp->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;
}

View File

@ -0,0 +1,68 @@
#ifndef _FTP_H_INCLUDE
#define _FTP_H_INCLUDE
#include <sys/types.h>
#include <sys/cdefs.h>
#include <stdio.h>
#include <time.h>
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
* ----------------------------------------------------------------------------
*
* Major Changelog:
*
* Jordan K. Hubbard
* 17 Jan 1996
*
* Turned inside out. Now returns xfers as new file ids, not as a special
* `state' of FTP_t
*
* FreeBSD Id: ftpio.h,v 1.13 1997/05/05 11:18:55 jkh Exp
*/
/* Internal housekeeping data structure for FTP sessions */
typedef struct {
enum { init, isopen, quit } con_state;
int fd_ctrl;
int addrtype;
char *host;
char *file;
int errno;
int is_binary;
int is_passive;
int is_verbose;
} *FTP_t;
/* Structure we use to match FTP error codes with readable strings */
struct ftperr {
const int num;
const char *string;
};
__BEGIN_DECLS
extern struct ftperr ftpErrList[];
extern int const ftpErrListLength;
/* Exported routines - deal only with FILE* type */
extern FILE *ftpLogin(char *host, char *user, char *passwd, int port, int verbose, int *retcode);
extern int ftpChdir(FILE *fp, char *dir);
extern int ftpErrno(FILE *fp);
extern off_t ftpGetSize(FILE *fp, char *file);
extern FILE *ftpGet(FILE *fp, char *file, off_t *seekto);
extern FILE *ftpPut(FILE *fp, char *file);
extern int ftpAscii(FILE *fp);
extern int ftpBinary(FILE *fp);
extern int ftpPassive(FILE *fp, int status);
extern void ftpVerbose(FILE *fp, int status);
extern FILE *ftpGetURL(char *url, char *user, char *passwd, int *retcode);
extern FILE *ftpPutURL(char *url, char *user, char *passwd, int *retcode);
extern time_t ftpGetModtime(FILE *fp, char *s);
extern const char *ftpErrString(int errno);
__END_DECLS
#endif /* _FTP_H_INCLUDE */