BBN-Vax-TCP/src/ftp/ftp_lib.c
/* This package provides the data transmission routines used by both the
user and server ftp for data connections. It also provides the routines
for dealing with performance statistics on the data transmissions.
It is designed to meet the specifications of RFC 765 (aka IEN 149) - File
Transfer Protocol by J. Postel, June, 1980.
The current version supports the following combinations:
mode - only stream mode is supported
type - ASCII non-print, image, and local (8 bit byte size)
structure - file, record (if also in ASCII mode)
The following external routines are defined within this package:
inittime() - sets up an assortment of timers and counters
dumptime(logfn) - calls logfn with a printable string of performance
statistics
rcvdata(NetFile, LocalFile, logfn) - receive data from the network
senddata(LocalFile, NetFile, logfn) - send data to network
The following externals are tested by these routines:
Each of the following variables should really be an enumeration type, but
too much of the rest of the code would have to be changed to support this
bit of cleanliness for now.
type - TYPEA,TYPEAN - ASCII Non-print
TYPEAT - ASCII Telnet format effectors (not supported)
TYPEAC - ASCII Carriage Control characters (not supported)
TYPEEN - EBCDIC Non-print (not supported)
TYPEET - EBCDIC Telnet format effectors (not supported)
TYPEEC - EBCDIC Carriage control characters (not supported)
TYPEI - Image mode
TYPEL - Local mode (only 8 bit byte supported)
stru - STRUF - file structure
STRUR - record structure
STRUP - page structure (not supported)
mode - MODES - stream mode
MODEB - block mode (not supported)
MODEC - compressed mode (not supported)
abrtxfer - this variable will cause a data transfer to be stopped if it
becomes true (1) during the transfer. Intended for use by signal
handlers. It is set false (0) on entry to the data transfer
routines.
Initial coding - Alan G. Nemeth, BBN, May 21, 1981
*/
#include "ftp_lib.h"
#include "sys/types.h"
#include "sys/timeb.h"
#include "stdio.h"
#include "errno.h"
#include "ftp.h"
struct timeb starttime;
long bytesmoved = 0; /* # of bytes on net connection */
time_t totaltime = 0; /* elapsed time of transmission */
extern int errno;
#define FTPESC '\0377'
#define FTPEOR '\01'
#define FTPEOF '\02'
/*
rcvdata (NetHandle, file, logfn) is used to receive data from the
network and write it into a file. When entered, the network file and
local file are already open and hooked up. Upon completion, the files
remain connected and the user of this routine should see to closing
them. logfn is a function which may be called with a string in case
of failures during the transfer. The string is formatted with an
appropriate FTP reply code so that the usual case will be to call
with logfn set to netreply during srvrftp, and printf during usrftp.
rcvdata returns 0 if the transfer completes entirely successfully and
-1 in case of any difficulty at all.
*/
rcvdata (NetHandle, file, logfn)
struct net_stuff *NetHandle;
FILE *file;
int logfn ();
{
register int cnt;
int escflg,
retflg;
char buf[BUFSIZ];
register char *ptr;
register char c;
char msgbuf[128];
errno = 0;
escflg = retflg = 0;
abrtxfer = 0;
timeon ();
switch (type)
{
case TYPEL:
case TYPEI:
{
int fid = fileno (file);
if (mode != MODES || stru != STRUF)
{
timeoff ();
logfn (
"554 Unimplemented mode/type/structure combination\n");
return (-1);
}
while ((cnt = net_read (NetHandle, buf, BUFSIZ)) > 0)
{
bytesmoved += cnt;
if (write (fid, buf, cnt) != cnt)
{
timeoff ();
sprintf (msgbuf, "552 Write failed: %s\n",
errmsg (0));
logfn (msgbuf);
return (-1);
}
if (abrtxfer)
{
timeoff ();
return (-1);
}
}
timeoff ();
if (cnt == 0)
return (0);
else
{
sprintf (msgbuf, "426 Data connection failure: %s\n",
errmsg (0));
logfn (msgbuf);
return (-1);
}
}
case TYPEA:
{
if ((mode != MODES) ||
((stru != STRUF) && (stru != STRUR)))
{
timeoff ();
logfn (
"554 Unimplemented mode/type/structure combination\n");
return (-1);
}
for (;;)
{
if (abrtxfer)
{
timeoff ();
return (-1);
}
if (ferror (file))
{
timeoff ();
sprintf (msgbuf, "552 Write failed: %s\n",
errmsg (0));
logfn (msgbuf);
return (-1);
}
cnt = net_read (NetHandle, buf, BUFSIZ);
if (cnt <= 0)
break;
bytesmoved += cnt;
for (ptr = buf; ptr < buf + cnt; ptr++)
switch (c = *ptr)
{
case '\r':
if (escflg)
{
putc (FTPESC, file);
escflg = 0;
}
if (retflg++)
putc (c, file);
continue;
case '\n':
retflg = 0;
putc (c, file);
continue;
case '\0':
if (retflg)
{
retflg = 0;
putc ('\r', file);
}
else
putc (c, file);
continue;
case FTPESC:
if (retflg)
{
putc ('\r', file);
retflg = 0;
}
if (stru != STRUR || escflg++)
putc (c, file);
continue;
case FTPEOR:
if (escflg)
{
putc ('\n', file);
escflg = 0;
}
else
putc (c, file);
continue;
case FTPEOF:
if (escflg)
{
timeoff ();
return (0);
}
else
putc (c, file);
continue;
default:
if (retflg)
{
putc ('\r', file);
retflg = 0;
}
if (escflg)
{
putc (FTPESC, file);
escflg = 0;
}
putc (c, file);
continue;
}
}
timeoff ();
if (cnt == 0)
return (0);
else
{
sprintf (msgbuf, "426 Data connection failure: %s\n",
errmsg (0));
logfn (msgbuf);
return (-1);
}
}
default:
timeoff ();
logfn (
"554 Unimplemented mode/type/structure combination\n");
return (-1);
}
/* NOTREACHED */
}
/*
senddata(file, NetHandle, logfn) is the reverse of rcvdata - it copies
data from the local file to the network. Otherwise, it's calling
conventions are identical with rcvdata.
*/
senddata (file, NetHandle, logfn)
struct net_stuff *NetHandle;
register FILE *file;
int logfn ();
{
register int cnt;
char buf[BUFSIZ];
register char *ptr;
register int c;
char msgbuf[128];
errno = 0;
abrtxfer = 0;
timeon ();
switch (type)
{
case TYPEL:
case TYPEI:
{
int fid = fileno (file);
if (mode != MODES || stru != STRUF)
{
timeoff ();
logfn (
"554 Unimplemented mode/type/structure combination\n");
return (-1);
}
while ((cnt = read (fid, buf, BUFSIZ)) > 0)
{
bytesmoved += cnt;
if (dat_write (NetHandle, buf, cnt) != cnt)
{
timeoff ();
sprintf (msgbuf,
"426 Data connection failure: %s\n",
errmsg (0));
logfn (msgbuf);
return (-1);
}
if (abrtxfer)
{
timeoff ();
return (-1);
}
}
timeoff ();
if (cnt == 0)
return (0);
else
{
sprintf (msgbuf, "552 Read of file failed: %s\n",
errmsg (0));
logfn (msgbuf);
return (-1);
}
}
case TYPEA:
{
if ((mode != MODES) ||
((stru != STRUF) && (stru != STRUR)))
{
timeoff ();
logfn (
"554 Unimplemented mode/type/structure combination\n");
return (-1);
}
#define myputc(chr) { *ptr++ = chr; \
if (ptr == &buf[BUFSIZ]) \
{ \
if ((cnt = dat_write (NetHandle, buf, BUFSIZ)) != BUFSIZ) \
goto finishup; \
bytesmoved += BUFSIZ; \
ptr = buf; \
} \
}
ptr = buf;
cnt = 0;
for (;;)
{
if (abrtxfer)
{
timeoff ();
return (-1);
}
if (ferror (file))
{
timeoff ();
sprintf (msgbuf, "552 Read failed: %s\n",
errmsg (0));
logfn (msgbuf);
return (-1);
}
switch (c = getc (file))
{
case '\r':
myputc (c);
myputc ('\0');
continue;
case '\n':
if (stru == STRUR)
{
myputc (FTPESC);
myputc (FTPEOR);
}
else
myputc (c);
continue;
case (FTPESC & 0377):
if (stru == STRUR)
myputc (FTPESC);
myputc (FTPESC);
continue;
case EOF:
if (stru == STRUR)
{
myputc (FTPESC);
myputc (FTPEOF);
}
if ((cnt =
dat_write (NetHandle, buf, ptr - buf))
!= (ptr - buf))
goto finishup;
bytesmoved += ptr - buf;
timeoff ();
return (0);
default:
myputc (c);
continue;
}
}
finishup:
timeoff ();
if (cnt == 0)
return (0);
else
{
sprintf (msgbuf, "426 Data connection failure: %s\n",
errmsg (0));
logfn (msgbuf);
return (-1);
}
}
default:
timeoff ();
logfn (
"554 Unimplemented mode/type/structure combination\n");
return (-1);
}
/* NOTREACHED */
}
/*
inittime() - establishes a base by zeroing a count of total bytes
moved, and the totaltime spent moving them.
timeon() - internal routine. Starts the clock.
timeoff() - internal routine. Stops the clock and adds accumulated
time to totaltime.
dumptime() - provides a formatted string describing the accumulated
performance statistics.
*/
inittime ()
{
bytesmoved = 0;
totaltime = 0;
}
static timeon ()
{
ftime (&starttime);
}
static timeoff ()
{
struct timeb endtime;
ftime (&endtime);
totaltime += endtime.time - starttime.time;
}
char *dumptime ()
{
static char msgbuf[128];
msgbuf[0] = '\0';
if (bytesmoved > 0 && totaltime != 0)
sprintf (msgbuf,
"Transferred %D bytes in %D seconds ( %D bps, %D bytes/sec )\n",
bytesmoved,
totaltime,
(8 * bytesmoved) / totaltime,
bytesmoved / totaltime);
else
if (bytesmoved > 0)
sprintf (msgbuf,
"Transferred %D bytes in %D seconds\n",
bytesmoved,
totaltime);
return (msgbuf);
}
dat_write(handle, buff, count)
struct net_stuff *handle; /* stuff block pointer */
char *buff;
int count;
{
int i;
errno = 0;
while ((i = net_write(handle, buff, count)) < 0)
{
if (
(errno == ENETSTAT) &&
(
(ioctl(handle -> fds, NETGETS, &(handle -> ns))),
(handle -> ns.n_state & URXTIMO)
)
)
{
errno = 0;
fprintf (stderr, "!");
}
else break;
}
return i;
}