#include "uucp.h"
#if USE_RCS_ID
const char protf_rcsid[] = "$Id: protf.c,v 1.36 2002/03/05 19:10:41 ian Rel $";
#endif
#include <ctype.h>
#include <errno.h>
#include "uudefs.h"
#include "uuconf.h"
#include "conn.h"
#include "trans.h"
#include "system.h"
#include "prot.h"
struct sfinfo
{
boolean (*psendfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon));
boolean (*precfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon,
const char *zdata, size_t cdata));
pointer pinfo;
char bsend;
};
static boolean ffprocess_data P((struct sdaemon *qdaemon,
boolean *pfexit, size_t *pcneed));
static boolean ffawait_ack P((struct stransfer *qtrans,
struct sdaemon *qdaemon,
const char *zdata, size_t cdata));
static boolean ffawait_cksum P((struct stransfer *qtrans,
struct sdaemon *qdaemon,
const char *zdata, size_t cdata));
static boolean ffsend_ack P((struct stransfer *qtrans,
struct sdaemon *qdaemon));
#define CFBUFSIZE (256)
static int cFtimeout = 120;
static int cFmaxretries = 2;
static char *zFbuf;
static boolean fFfile;
static unsigned int iFcheck;
static char bFspecial;
static int cFretries;
static boolean fFacked;
struct uuconf_cmdtab asFproto_params[] =
{
{ "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cFtimeout, NULL },
{ "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cFmaxretries, NULL },
{ NULL, 0, NULL, NULL }
};
static long cFsent_data;
static long cFsent_bytes;
static long cFrec_data;
static long cFrec_bytes;
static long cFsend_retries;
static long cFrec_retries;
boolean
ffstart (qdaemon, pzlog)
struct sdaemon *qdaemon;
char **pzlog;
{
*pzlog = NULL;
cFsent_data = 0;
cFsent_bytes = 0;
cFrec_data = 0;
cFrec_bytes = 0;
cFsend_retries = 0;
cFrec_retries = 0;
if (! fconn_set (qdaemon->qconn, PARITYSETTING_DEFAULT,
STRIPSETTING_SEVENBITS, XONXOFF_ON))
return FALSE;
usysdep_sleep (2);
return TRUE;
}
boolean
ffshutdown (qdaemon)
struct sdaemon *qdaemon ATTRIBUTE_UNUSED;
{
xfree ((pointer) zFbuf);
zFbuf = NULL;
ulog (LOG_NORMAL,
"Protocol 'f': sent %ld bytes for %ld, received %ld bytes for %ld",
cFsent_bytes, cFsent_data, cFrec_bytes, cFrec_data);
if (cFsend_retries != 0 || cFrec_retries != 0)
ulog (LOG_NORMAL, "Protocol 'f' file retries: %ld sending, %ld receiving",
cFsend_retries, cFrec_retries);
cFtimeout = 120;
cFmaxretries = 2;
return TRUE;
}
boolean
ffsendcmd (qdaemon, z, ilocal, iremote)
struct sdaemon *qdaemon;
const char *z;
int ilocal ATTRIBUTE_UNUSED;
int iremote ATTRIBUTE_UNUSED;
{
size_t clen;
char *zalc;
boolean fret;
DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "ffsendcmd: Sending command \"%s\"", z);
clen = strlen (z);
zalc = zbufalc (clen + 2);
memcpy (zalc, z, clen);
zalc[clen] = '\r';
zalc[clen + 1] = '\0';
fret = fsend_data (qdaemon->qconn, zalc, clen + 1, TRUE);
ubuffree (zalc);
return fret;
}
char *
zfgetspace (qdaemon, pclen)
struct sdaemon *qdaemon ATTRIBUTE_UNUSED;
size_t *pclen;
{
*pclen = CFBUFSIZE;
if (zFbuf == NULL)
zFbuf = (char *) xmalloc (CFBUFSIZE);
return zFbuf;
}
boolean
ffsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
struct sdaemon *qdaemon;
char *zdata;
size_t cdata;
int ilocal ATTRIBUTE_UNUSED;
int iremote ATTRIBUTE_UNUSED;
long ipos ATTRIBUTE_UNUSED;
{
char ab[CFBUFSIZE * 2];
char *ze;
register unsigned int itmpchk;
cFsent_data += cdata;
ze = ab;
itmpchk = iFcheck;
while (cdata-- > 0)
{
register int b;
if ((itmpchk & 0x8000) == 0)
itmpchk <<= 1;
else
{
itmpchk <<= 1;
++itmpchk;
}
b = *zdata++ & 0xff;
itmpchk += b;
if (b <= 0177)
{
if (b <= 037)
{
*ze++ = '\172';
*ze++ = (char) (b + 0100);
}
else if (b <= 0171)
*ze++ = (char) b;
else
{
*ze++ = '\173';
*ze++ = (char) (b - 0100);
}
}
else
{
if (b <= 0237)
{
*ze++ = '\174';
*ze++ = (char) (b - 0100);
}
else if (b <= 0371)
{
*ze++ = '\175';
*ze++ = (char) (b - 0200);
}
else
{
*ze++ = '\176';
*ze++ = (char) (b - 0300);
}
}
}
iFcheck = itmpchk;
cFsent_bytes += ze - ab;
return fsend_data (qdaemon->qconn, ab, (size_t) (ze - ab), FALSE);
}
static boolean
ffprocess_data (qdaemon, pfexit, pcneed)
struct sdaemon *qdaemon;
boolean *pfexit;
size_t *pcneed;
{
int i;
register unsigned int itmpchk;
*pfexit = FALSE;
if (pcneed != NULL)
*pcneed = 1;
if (! fFfile)
{
while (iPrecstart != iPrecend)
{
for (i = iPrecstart; i < CRECBUFLEN && i != iPrecend; i++)
{
abPrecbuf[i] &= 0x7f;
if (abPrecbuf[i] == '\r')
{
int istart;
DEBUG_MESSAGE1 (DEBUG_PROTO,
"ffprocess_data: Got %d command bytes",
i - iPrecstart + 1);
abPrecbuf[i] = '\0';
istart = iPrecstart;
iPrecstart = (i + 1) % CRECBUFLEN;
if (pcneed != NULL)
*pcneed = 0;
return fgot_data (qdaemon, abPrecbuf + istart,
(size_t) (i - istart + 1),
(const char *) NULL, (size_t) 0,
-1, -1, (long) -1, TRUE, pfexit);
}
}
DEBUG_MESSAGE1 (DEBUG_PROTO,
"ffprocess_data: Got %d command bytes",
i - iPrecstart);
if (! fgot_data (qdaemon, abPrecbuf + iPrecstart,
(size_t) (i - iPrecstart),
(const char *) NULL, (size_t) 0,
-1, -1, (long) -1, TRUE, pfexit))
return FALSE;
iPrecstart = i % CRECBUFLEN;
}
return TRUE;
}
itmpchk = iFcheck;
while (iPrecstart != iPrecend)
{
char *zstart, *zto, *zfrom;
int c;
zto = zfrom = zstart = abPrecbuf + iPrecstart;
c = iPrecend - iPrecstart;
if (c < 0)
c = CRECBUFLEN - iPrecstart;
while (c-- != 0)
{
int b;
b = *zfrom++ & 0x7f;
if (b < 040 || b > 0176)
{
ulog (LOG_ERROR, "Illegal byte %d", b);
continue;
}
if (b >= 0172)
{
if (bFspecial != 0)
{
if (bFspecial != 0176 || b != 0176)
{
ulog (LOG_ERROR, "Illegal bytes %d %d",
bFspecial, b);
bFspecial = 0;
continue;
}
if (zto != zstart)
{
cFrec_bytes += zfrom - zstart - 2;
cFrec_data += zto - zstart;
if (! fgot_data (qdaemon, zstart,
(size_t) (zto - zstart),
(const char *) NULL, (size_t) 0,
-1, -1, (long) -1, TRUE, pfexit))
return FALSE;
}
iPrecstart = (iPrecstart + zfrom - zstart) % CRECBUFLEN;
iFcheck = itmpchk;
if (pcneed != NULL)
*pcneed = 0;
return fgot_data (qdaemon, (const char *) NULL,
(size_t) 0, (const char *) NULL,
(size_t) 0, -1, -1, (long) -1,
TRUE, pfexit);
}
bFspecial = (char) b;
}
else
{
int bnext;
switch (bFspecial)
{
default:
bnext = b;
break;
case 0172:
bnext = b - 0100;
break;
case 0173:
case 0174:
bnext = b + 0100;
break;
case 0175:
bnext = b + 0200;
break;
case 0176:
bnext = b + 0300;
break;
}
*zto++ = (char) bnext;
bFspecial = 0;
if ((itmpchk & 0x8000) == 0)
itmpchk <<= 1;
else
{
itmpchk <<= 1;
++itmpchk;
}
itmpchk += bnext;
}
}
if (zto != zstart)
{
DEBUG_MESSAGE1 (DEBUG_PROTO,
"ffprocess_data: Got %d bytes",
zto - zstart);
cFrec_data += zto - zstart;
if (! fgot_data (qdaemon, zstart, (size_t) (zto - zstart),
(const char *) NULL, (size_t) 0,
-1, -1, (long) -1, TRUE, pfexit))
return FALSE;
}
cFrec_bytes += zfrom - zstart;
iPrecstart = (iPrecstart + zfrom - zstart) % CRECBUFLEN;
}
iFcheck = itmpchk;
if (pcneed != NULL)
{
if (bFspecial == 0176)
*pcneed = 6;
else
*pcneed = 7;
}
return TRUE;
}
boolean
ffwait (qdaemon)
struct sdaemon *qdaemon;
{
while (TRUE)
{
boolean fexit;
size_t cneed, crec;
if (! ffprocess_data (qdaemon, &fexit, &cneed))
return FALSE;
if (fexit)
return TRUE;
if (cneed > 0)
{
if (! freceive_data (qdaemon->qconn, cneed, &crec, cFtimeout, TRUE))
return FALSE;
if (crec == 0)
{
ulog (LOG_ERROR, "Timed out waiting for data");
return FALSE;
}
}
}
}
boolean
fffile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled)
struct sdaemon *qdaemon;
struct stransfer *qtrans;
boolean fstart;
boolean fsend;
long cbytes ATTRIBUTE_UNUSED;
boolean *pfhandled;
{
DEBUG_MESSAGE3 (DEBUG_PROTO, "fffile: fstart %s; fsend %s; fFacked %s",
fstart ? "true" : "false", fsend ? "true" : "false",
fFacked ? "true" : "false");
*pfhandled = FALSE;
if (fstart)
{
iFcheck = 0xffff;
cFretries = 0;
fFacked = FALSE;
if (! fsend)
{
bFspecial = 0;
fFfile = TRUE;
}
return TRUE;
}
else
{
struct sfinfo *qinfo;
if (fFacked)
{
fFacked = FALSE;
return TRUE;
}
if (fsend)
{
char ab[sizeof "\176\176ABCD\r"];
sprintf (ab, "\176\176%04x\r", iFcheck & 0xffff);
if (! fsend_data (qdaemon->qconn, ab, (size_t) 7, TRUE))
return FALSE;
fFfile = FALSE;
qinfo = (struct sfinfo *) xmalloc (sizeof (struct sfinfo));
qinfo->psendfn = qtrans->psendfn;
qinfo->precfn = qtrans->precfn;
qinfo->pinfo = qtrans->pinfo;
qtrans->psendfn = NULL;
qtrans->precfn = ffawait_ack;
qtrans->pinfo = (pointer) qinfo;
qtrans->fcmd = TRUE;
*pfhandled = TRUE;
return fqueue_receive (qdaemon, qtrans);
}
else
{
fFfile = FALSE;
qinfo = (struct sfinfo *) xmalloc (sizeof (struct sfinfo));
qinfo->psendfn = qtrans->psendfn;
qinfo->precfn = qtrans->precfn;
qinfo->pinfo = qtrans->pinfo;
qtrans->psendfn = NULL;
qtrans->precfn = ffawait_cksum;
qtrans->pinfo = (pointer) qinfo;
qtrans->fcmd = TRUE;
*pfhandled = TRUE;
return fqueue_receive (qdaemon, qtrans);
}
}
}
static boolean
ffawait_ack (qtrans, qdaemon, zdata, cdata)
struct stransfer *qtrans;
struct sdaemon *qdaemon;
const char *zdata;
size_t cdata ATTRIBUTE_UNUSED;
{
struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo;
qtrans->precfn = NULL;
if (*zdata == 'R')
{
if (! ffileisopen (qtrans->e))
{
ulog (LOG_ERROR, "Request to resent non-file");
return FALSE;
}
++cFretries;
if (cFretries > cFmaxretries)
{
ulog (LOG_ERROR, "Too many retries");
return FALSE;
}
ulog (LOG_NORMAL, "Resending file");
if (! ffilerewind (qtrans->e))
{
ulog (LOG_ERROR, "rewind: %s", strerror (errno));
return FALSE;
}
qtrans->ipos = (long) 0;
iFcheck = 0xffff;
++cFsend_retries;
qtrans->psendfn = qinfo->psendfn;
qtrans->precfn = qinfo->precfn;
qtrans->pinfo = qinfo->pinfo;
xfree ((pointer) qinfo);
qtrans->fsendfile = TRUE;
return fqueue_send (qdaemon, qtrans);
}
if (*zdata != 'G')
{
DEBUG_MESSAGE1 (DEBUG_PROTO, "fffile: Got \"%s\"", zdata);
ulog (LOG_ERROR, "File send failed");
return FALSE;
}
qtrans->psendfn = qinfo->psendfn;
qtrans->precfn = qinfo->precfn;
qtrans->pinfo = qinfo->pinfo;
xfree ((pointer) qinfo);
fFacked = TRUE;
return (*qtrans->psendfn) (qtrans, qdaemon);
}
static boolean
ffawait_cksum (qtrans, qdaemon, zdata, cdata)
struct stransfer *qtrans;
struct sdaemon *qdaemon;
const char *zdata;
size_t cdata ATTRIBUTE_UNUSED;
{
struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo;
unsigned int icheck;
qtrans->precfn = NULL;
if (! isxdigit (zdata[0])
|| ! isxdigit (zdata[1])
|| ! isxdigit (zdata[2])
|| ! isxdigit (zdata[3])
|| zdata[4] != '\0')
{
ulog (LOG_ERROR, "Bad checksum format");
xfree (qtrans->pinfo);
return FALSE;
}
icheck = (unsigned int) strtol ((char *) zdata, (char **) NULL, 16);
if (icheck != (iFcheck & 0xffff))
{
DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
"Checksum failed; calculated 0x%x, got 0x%x",
iFcheck & 0xffff, icheck);
if (! ffileisopen (qtrans->e))
{
ulog (LOG_ERROR, "Failed to get non-file");
return FALSE;
}
++cFretries;
if (cFretries > cFmaxretries)
{
ulog (LOG_ERROR, "Too many retries");
qinfo->bsend = 'Q';
}
else
{
ulog (LOG_NORMAL, "File being resent");
qtrans->e = esysdep_truncate (qtrans->e, qtrans->s.ztemp);
if (! ffileisopen (qtrans->e))
return FALSE;
qtrans->ipos = (long) 0;
iFcheck = 0xffff;
bFspecial = 0;
fFfile = TRUE;
++cFrec_retries;
qinfo->bsend = 'R';
}
}
else
{
qinfo->bsend = 'G';
}
qtrans->psendfn = ffsend_ack;
return fqueue_send (qdaemon, qtrans);
}
static boolean
ffsend_ack (qtrans, qdaemon)
struct stransfer *qtrans;
struct sdaemon *qdaemon;
{
struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo;
char ab[2];
ab[0] = qinfo->bsend;
ab[1] = '\0';
if (! ffsendcmd (qdaemon, ab, 0, 0))
return FALSE;
qtrans->psendfn = qinfo->psendfn;
qtrans->precfn = qinfo->precfn;
qtrans->pinfo = qinfo->pinfo;
xfree ((pointer) qinfo);
if (ab[0] == 'Q')
return FALSE;
if (ab[0] == 'R')
{
qtrans->frecfile = TRUE;
return fqueue_receive (qdaemon, qtrans);
}
fFacked = TRUE;
return (*qtrans->precfn) (qtrans, qdaemon, (const char *) NULL,
(size_t) 0);
}