summaryrefslogtreecommitdiff
path: root/gnu/libexec/uucp/uucico
diff options
context:
space:
mode:
authorsvn2git <svn2git@FreeBSD.org>1994-05-01 08:00:00 +0000
committersvn2git <svn2git@FreeBSD.org>1994-05-01 08:00:00 +0000
commita16f65c7d117419bd266c28a1901ef129a337569 (patch)
tree2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /gnu/libexec/uucp/uucico
parent8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff)
Diffstat (limited to 'gnu/libexec/uucp/uucico')
-rw-r--r--gnu/libexec/uucp/uucico/Makefile20
-rw-r--r--gnu/libexec/uucp/uucico/prote.c387
-rw-r--r--gnu/libexec/uucp/uucico/protf.c842
-rw-r--r--gnu/libexec/uucp/uucico/protg.c1933
-rw-r--r--gnu/libexec/uucp/uucico/proti.c1563
-rw-r--r--gnu/libexec/uucp/uucico/protj.c671
-rw-r--r--gnu/libexec/uucp/uucico/prott.c330
-rw-r--r--gnu/libexec/uucp/uucico/protz.c2626
-rw-r--r--gnu/libexec/uucp/uucico/rec.c1162
-rw-r--r--gnu/libexec/uucp/uucico/send.c1273
-rw-r--r--gnu/libexec/uucp/uucico/time.c130
-rw-r--r--gnu/libexec/uucp/uucico/trans.c1439
-rw-r--r--gnu/libexec/uucp/uucico/uucico.8225
-rw-r--r--gnu/libexec/uucp/uucico/uucico.c2618
-rw-r--r--gnu/libexec/uucp/uucico/xcmd.c396
15 files changed, 15615 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/uucico/Makefile b/gnu/libexec/uucp/uucico/Makefile
new file mode 100644
index 0000000000000..6cd270934bbf3
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/Makefile
@@ -0,0 +1,20 @@
+# Makefile for uucico
+# $Id: Makefile,v 1.2 1993/08/06 23:38:23 rgrimes Exp $
+
+BINDIR= $(sbindir)
+BINOWN= $(owner)
+BINMODE= 4555
+
+PROG= uucico
+SRCS= uucico.c trans.c send.c rec.c xcmd.c prot.c protg.c protf.c \
+ prott.c prote.c proti.c protj.c protz.c time.c chat.c \
+ conn.c copy.c log.c tcp.c tli.c util.c
+LDADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP)
+DPADD+= $(LIBUNIX) $(LIBUUCONF) $(LIBUUCP)
+CFLAGS+= -I$(.CURDIR)/../common_sources\
+ -DVERSION=\"$(VERSION)\"
+
+MAN8= uucico.8
+
+.include <bsd.prog.mk>
+.PATH: $(.CURDIR)/../common_sources
diff --git a/gnu/libexec/uucp/uucico/prote.c b/gnu/libexec/uucp/uucico/prote.c
new file mode 100644
index 0000000000000..9b824651c2f77
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/prote.c
@@ -0,0 +1,387 @@
+/* prote.c
+ The 'e' protocol.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char prote_rcsid[] = "$Id: prote.c,v 1.1 1993/08/05 18:27:09 conklin Exp $";
+#endif
+
+#include "uudefs.h"
+#include "uuconf.h"
+#include "conn.h"
+#include "trans.h"
+#include "system.h"
+#include "prot.h"
+
+/* This implementation is based on my implementation of the 't'
+ protocol, which is fairly similar. The main difference between the
+ protocols seems to be that 't' breaks the file into packets and
+ transmits the size of the packet with each packet, whereas 'e'
+ sends the size of the entire file and then sends all the data in a
+ single enormous packet.
+
+ The 'e' protocol does no error checking whatsoever and thus
+ requires an end-to-end verified eight bit communication line, such
+ as is provided by TCP. Using it with a modem is inadvisable, since
+ errors can occur between the modem and the computer. */
+
+/* The buffer size we use. */
+#define CEBUFSIZE (CRECBUFLEN / 2)
+
+/* The size of the initial file size message. */
+#define CEFRAMELEN (20)
+
+/* A pointer to the buffer we will use. */
+static char *zEbuf;
+
+/* True if we are receiving a file. */
+static boolean fEfile;
+
+/* The number of bytes we have left to send or receive. */
+static long cEbytes;
+
+/* The timeout we use. */
+static int cEtimeout = 120;
+
+struct uuconf_cmdtab asEproto_params[] =
+{
+ { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cEtimeout, NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+/* Local function. */
+
+static boolean feprocess_data P((struct sdaemon *qdaemon, boolean *pfexit,
+ size_t *pcneed));
+
+/* Start the protocol. */
+
+boolean
+festart (qdaemon, pzlog)
+ struct sdaemon *qdaemon;
+ char **pzlog;
+{
+ *pzlog = NULL;
+ if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
+ STRIPSETTING_EIGHTBITS, XONXOFF_OFF))
+ return FALSE;
+ zEbuf = (char *) xmalloc (CEBUFSIZE);
+ fEfile = FALSE;
+ usysdep_sleep (2);
+ return TRUE;
+}
+
+/* Stop the protocol. */
+
+/*ARGSUSED*/
+boolean
+feshutdown (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ xfree ((pointer) zEbuf);
+ zEbuf = NULL;
+ cEtimeout = 120;
+ return TRUE;
+}
+
+/* Send a command string. We send everything up to and including the
+ null byte. */
+
+/*ARGSUSED*/
+boolean
+fesendcmd (qdaemon, z, ilocal, iremote)
+ struct sdaemon *qdaemon;
+ const char *z;
+ int ilocal;
+ int iremote;
+{
+ DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fesendcmd: Sending command \"%s\"", z);
+
+ return fsend_data (qdaemon->qconn, z, strlen (z) + 1, TRUE);
+}
+
+/* Get space to be filled with data. We provide a buffer which has
+ 20 bytes at the start available to hold the length. */
+
+/*ARGSUSED*/
+char *
+zegetspace (qdaemon, pclen)
+ struct sdaemon *qdaemon;
+ size_t *pclen;
+{
+ *pclen = CEBUFSIZE;
+ return zEbuf;
+}
+
+/* Send out some data. We are allowed to modify the 20 bytes
+ preceding the buffer. This allows us to send the entire block with
+ header bytes in a single call. */
+
+/*ARGSIGNORED*/
+boolean
+fesenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
+ struct sdaemon *qdaemon;
+ char *zdata;
+ size_t cdata;
+ int ilocal;
+ int iremote;
+ long ipos;
+{
+#if DEBUG > 0
+ /* Keep track of the number of bytes we send out to make sure it all
+ adds up. */
+ cEbytes -= cdata;
+ if (cEbytes < 0)
+ {
+ ulog (LOG_ERROR, "Protocol 'e' internal error");
+ return FALSE;
+ }
+#endif
+
+ /* We pass FALSE to fsend_data since we don't expect the other side
+ to be sending us anything just now. */
+ return fsend_data (qdaemon->qconn, zdata, cdata, FALSE);
+}
+
+/* Process data and return the amount we need in *pfneed. */
+
+static boolean
+feprocess_data (qdaemon, pfexit, pcneed)
+ struct sdaemon *qdaemon;
+ boolean *pfexit;
+ size_t *pcneed;
+{
+ int cinbuf, cfirst, clen;
+
+ *pfexit = FALSE;
+
+ cinbuf = iPrecend - iPrecstart;
+ if (cinbuf < 0)
+ cinbuf += CRECBUFLEN;
+
+ if (! fEfile)
+ {
+ /* We are not receiving a file. Commands continue up to a null
+ byte. */
+ while (cinbuf > 0)
+ {
+ char *pnull;
+
+ cfirst = CRECBUFLEN - iPrecstart;
+ if (cfirst > cinbuf)
+ cfirst = cinbuf;
+
+ pnull = memchr (abPrecbuf + iPrecstart, '\0', (size_t) cfirst);
+ if (pnull != NULL)
+ cfirst = pnull - (abPrecbuf + iPrecstart) + 1;
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "feprocess_data: Got %d command bytes",
+ cfirst);
+
+ if (! fgot_data (qdaemon, abPrecbuf + iPrecstart,
+ (size_t) cfirst, (const char *) NULL, (size_t) 0,
+ -1, -1, (long) -1, TRUE, pfexit))
+ return FALSE;
+
+ iPrecstart = (iPrecstart + cfirst) % CRECBUFLEN;
+
+ if (*pfexit)
+ return TRUE;
+
+ cinbuf = iPrecend - iPrecstart;
+ if (cinbuf < 0)
+ cinbuf += CRECBUFLEN;
+ }
+
+ if (pcneed != NULL)
+ *pcneed = 1;
+
+ return TRUE;
+ }
+
+ /* Here we are receiving a file. We want cEbytes in total. If we
+ don't have cEbytes yet, we have to get it first. */
+
+ if (cEbytes == -1)
+ {
+ char ab[CEFRAMELEN + 1];
+
+ if (cinbuf < CEFRAMELEN)
+ {
+ if (pcneed != NULL)
+ *pcneed = CEFRAMELEN - cinbuf;
+ return TRUE;
+ }
+
+ cfirst = CRECBUFLEN - iPrecstart;
+ if (cfirst >= CEFRAMELEN)
+ memcpy (ab, abPrecbuf + iPrecstart, (size_t) CEFRAMELEN);
+ else
+ {
+ memcpy (ab, abPrecbuf + iPrecstart, (size_t) cfirst);
+ memcpy (ab + cfirst, abPrecbuf, (size_t) CEFRAMELEN - cfirst);
+ }
+
+ ab[CEFRAMELEN] = '\0';
+ cEbytes = strtol (ab, (char **) NULL, 10);
+
+ iPrecstart = (iPrecstart + CEFRAMELEN) % CRECBUFLEN;
+
+ cinbuf = iPrecend - iPrecstart;
+ if (cinbuf < 0)
+ cinbuf += CRECBUFLEN;
+ }
+
+ /* Here we can read real data for the file. */
+
+ while (cinbuf > 0)
+ {
+ clen = cinbuf;
+ if ((long) clen > cEbytes)
+ clen = (int) cEbytes;
+
+ cfirst = CRECBUFLEN - iPrecstart;
+ if (cfirst > clen)
+ cfirst = clen;
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "feprocess_data: Got %d data bytes",
+ clen);
+
+ if (! fgot_data (qdaemon, abPrecbuf + iPrecstart,
+ (size_t) cfirst, abPrecbuf, (size_t) (clen - cfirst),
+ -1, -1, (long) -1, TRUE, pfexit))
+ return FALSE;
+
+ iPrecstart = (iPrecstart + clen) % CRECBUFLEN;
+ cEbytes -= clen;
+
+ if (cEbytes == 0)
+ {
+ if (! fgot_data (qdaemon, abPrecbuf, (size_t) 0,
+ (const char *) NULL, (size_t) 0,
+ -1, -1, (long) -1, TRUE, pfexit))
+ return FALSE;
+ if (*pfexit)
+ return TRUE;
+ }
+
+ cinbuf -= clen;
+ }
+
+ if (pcneed != NULL)
+ {
+ if (cEbytes > CRECBUFLEN / 2)
+ *pcneed = CRECBUFLEN / 2;
+ else
+ *pcneed = (int) cEbytes;
+ }
+
+ return TRUE;
+}
+
+/* Wait for data to come in and process it until we've reached the end
+ of a command or a file. */
+
+boolean
+fewait (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ while (TRUE)
+ {
+ boolean fexit;
+ size_t cneed, crec;
+
+ if (! feprocess_data (qdaemon, &fexit, &cneed))
+ return FALSE;
+ if (fexit)
+ return TRUE;
+
+ if (! freceive_data (qdaemon->qconn, cneed, &crec, cEtimeout, TRUE))
+ return FALSE;
+
+ if (crec == 0)
+ {
+ ulog (LOG_ERROR, "Timed out waiting for data");
+ return FALSE;
+ }
+ }
+}
+
+/* File level routine, to handle transferring the amount of data and
+ to set fEfile correctly. */
+
+boolean
+fefile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+ boolean fstart;
+ boolean fsend;
+ long cbytes;
+ boolean *pfhandled;
+{
+ *pfhandled = FALSE;
+
+ if (fstart)
+ {
+ if (fsend)
+ {
+ char ab[CEFRAMELEN];
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "Protocol 'e' starting to send %ld bytes",
+ cbytes);
+
+ bzero (ab, (size_t) CEFRAMELEN);
+ sprintf (ab, "%ld", cbytes);
+ if (! fsend_data (qdaemon->qconn, ab, (size_t) CEFRAMELEN, TRUE))
+ return FALSE;
+ cEbytes = cbytes;
+ }
+ else
+ {
+ cEbytes = -1;
+ fEfile = TRUE;
+ }
+ }
+ else
+ {
+ if (! fsend)
+ fEfile = FALSE;
+#if DEBUG > 0
+ if (cEbytes != 0)
+ {
+ ulog (LOG_ERROR,
+ "Protocol 'e' internal error: %ld bytes left over",
+ cEbytes);
+ return FALSE;
+ }
+#endif
+ }
+
+ return TRUE;
+}
diff --git a/gnu/libexec/uucp/uucico/protf.c b/gnu/libexec/uucp/uucico/protf.c
new file mode 100644
index 0000000000000..96b213157cee0
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/protf.c
@@ -0,0 +1,842 @@
+/* protf.c
+ The 'f' protocol.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char protf_rcsid[] = "$Id: protf.c,v 1.1 1993/08/05 18:27:10 conklin Exp $";
+#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"
+
+/* This implementation is based on code by Piet Beertema, CWI,
+ Amsterdam, Sep 1984.
+
+ This code implements the 'f' protocol, which requires a
+ flow-controlled error-free seven-bit data path. It does check for
+ errors, but only at the end of each file transmission, so a noisy
+ line without error correcting modems will be unusable.
+
+ The conversion to seven bit data is done as follows, where b
+ represents the character to convert:
+
+ 0 <= b <= 037: 0172, b + 0100 (0100 to 0137)
+ 040 <= b <= 0171: b ( 040 to 0171)
+ 0172 <= b <= 0177: 0173, b - 0100 ( 072 to 077)
+ 0200 <= b <= 0237: 0174, b - 0100 (0100 to 0137)
+ 0240 <= b <= 0371: 0175, b - 0200 ( 040 to 0171)
+ 0372 <= b <= 0377: 0176, b - 0300 ( 072 to 077)
+
+ This causes all output bytes to be in the range 040 to 0176; these
+ are the printable ASCII characters. */
+
+/* This structure is used to hold information when dealing with the
+ end of file acknowledgement. */
+
+struct sfinfo
+{
+ /* The functions from the generic code. */
+ boolean (*psendfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon));
+ boolean (*precfn) P((struct stransfer *qtrans, struct sdaemon *qdaemon,
+ const char *zdata, size_t cdata));
+ /* The info pointer from the generic code. */
+ pointer pinfo;
+ /* The character to send after receiving the checksum. */
+ char bsend;
+};
+
+/* Internal functions. */
+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));
+
+/* The size of the buffer we allocate to store outgoing data in. */
+#define CFBUFSIZE (256)
+
+/* The timeout to wait for data to arrive before giving up. */
+static int cFtimeout = 120;
+
+/* The maximum number of retries. */
+static int cFmaxretries = 2;
+
+/* The buffer we allocate for outgoing data. */
+static char *zFbuf;
+
+/* TRUE if we are receiving a file rather than a command. */
+static boolean fFfile;
+
+/* The checksum so far. */
+static unsigned int iFcheck;
+
+/* The last special byte (0172 to 0176) or 0 if none. */
+static char bFspecial;
+
+/* The number of times we have retried this file. */
+static int cFretries;
+
+/* Whether this file has been acknowledged. */
+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 }
+};
+
+/* Statistics. */
+
+/* The number of data bytes sent in files. */
+static long cFsent_data;
+
+/* The number of actual bytes sent in files. */
+static long cFsent_bytes;
+
+/* The number of data bytes received in files. */
+static long cFrec_data;
+
+/* The number of actual bytes received in files. */
+static long cFrec_bytes;
+
+/* The number of file retries when sending. */
+static long cFsend_retries;
+
+/* The number of file retries when receiving. */
+static long cFrec_retries;
+
+/* Start the protocol. */
+
+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;
+
+ /* Use XON/XOFF handshaking. */
+ if (! fconn_set (qdaemon->qconn, PARITYSETTING_DEFAULT,
+ STRIPSETTING_SEVENBITS, XONXOFF_ON))
+ return FALSE;
+
+ /* We sleep to allow the other side to reset the terminal; this is
+ what Mr. Beertema's code does. */
+ usysdep_sleep (2);
+
+ return TRUE;
+}
+
+/* Shutdown the protocol. */
+
+/*ARGSIGNORED*/
+boolean
+ffshutdown (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ 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;
+}
+
+/* Send a command string. We just send the string followed by a carriage
+ return. */
+
+/*ARGSUSED*/
+boolean
+ffsendcmd (qdaemon, z, ilocal, iremote)
+ struct sdaemon *qdaemon;
+ const char *z;
+ int ilocal;
+ int iremote;
+{
+ 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;
+}
+
+/* Get space to be filled with data. We allocate the space from the
+ heap. */
+
+/*ARGSIGNORED*/
+char *
+zfgetspace (qdaemon, pclen)
+ struct sdaemon *qdaemon;
+ size_t *pclen;
+{
+ *pclen = CFBUFSIZE;
+ if (zFbuf == NULL)
+ zFbuf = (char *) xmalloc (CFBUFSIZE);
+ return zFbuf;
+}
+
+/* Send out a data packet. We have to encode the data into seven bits
+ and accumulate a checksum. */
+
+/*ARGSIGNORED*/
+boolean
+ffsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
+ struct sdaemon *qdaemon;
+ char *zdata;
+ size_t cdata;
+ int ilocal;
+ int iremote;
+ long ipos;
+{
+ char ab[CFBUFSIZE * 2];
+ char *ze;
+ register unsigned int itmpchk;
+
+ cFsent_data += cdata;
+
+ ze = ab;
+ itmpchk = iFcheck;
+ while (cdata-- > 0)
+ {
+ register int b;
+
+ /* Rotate the checksum left. */
+ if ((itmpchk & 0x8000) == 0)
+ itmpchk <<= 1;
+ else
+ {
+ itmpchk <<= 1;
+ ++itmpchk;
+ }
+
+ /* Add the next byte into the checksum. */
+ b = *zdata++ & 0xff;
+ itmpchk += b;
+
+ /* Encode the byte. */
+ 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;
+
+ /* Passing FALSE tells fsend_data not to bother looking for incoming
+ information, since we really don't expect any. */
+ return fsend_data (qdaemon->qconn, ab, (size_t) (ze - ab), FALSE);
+}
+
+/* Process data and return the amount of data we are looking for in
+ *pcneed. The 'f' protocol doesn't really reveal this, but when
+ transferring file we know that we need at least seven characters
+ for the checksum. */
+
+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)
+ {
+ /* A command continues until a '\r' character, which we turn
+ into '\0' before calling fgot_data. */
+ while (iPrecstart != iPrecend)
+ {
+ for (i = iPrecstart; i < CRECBUFLEN && i != iPrecend; i++)
+ {
+ 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;
+ }
+
+ /* Here the data is destined for a file, and we must decode it. */
+
+ 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++ & 0xff;
+ if (b < 040 || b > 0176)
+ {
+ ulog (LOG_ERROR, "Illegal byte %d", b);
+ continue;
+ }
+
+ /* Characters >= 0172 are always special characters. The
+ only legal pair of consecutive special characters
+ are 0176 0176 which immediately precede the four
+ digit checksum. */
+ if (b >= 0172)
+ {
+ if (bFspecial != 0)
+ {
+ if (bFspecial != 0176 || b != 0176)
+ {
+ ulog (LOG_ERROR, "Illegal bytes %d %d",
+ bFspecial, b);
+ bFspecial = 0;
+ continue;
+ }
+
+ /* Pass any initial data. */
+ if (zto != zstart)
+ {
+ /* Don't count the checksum in the received bytes. */
+ 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;
+ }
+
+ /* The next characters we want to read are the
+ checksum, so skip the second 0176. */
+ iPrecstart = (iPrecstart + zfrom - zstart) % CRECBUFLEN;
+
+ iFcheck = itmpchk;
+
+ /* Tell fgot_data that we've read the entire file by
+ passing 0 length data. This will wind up calling
+ fffile to verify the checksum. We set *pcneed to
+ 0 because we don't want to read any more data
+ from the port, since we may have already read the
+ checksum. */
+ 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);
+ }
+
+ /* Here we have encountered a special character that
+ does not follow another special character. */
+ bFspecial = (char) b;
+ }
+ else
+ {
+ int bnext;
+
+ /* Here we have encountered a nonspecial character. */
+
+ 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;
+
+ /* Rotate the checksum left. */
+ if ((itmpchk & 0x8000) == 0)
+ itmpchk <<= 1;
+ else
+ {
+ itmpchk <<= 1;
+ ++itmpchk;
+ }
+
+ /* Add the next byte into the checksum. */
+ 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)
+ {
+ /* At this point we may have seen the first 0176 in the checksum
+ but not the second. The checksum is at least seven
+ characters long (0176 0176 a b c d \r). This won't help
+ much, but reading seven characters is a lot better than
+ reading two, which is what I saw in a 2400 baud log file. */
+ if (bFspecial == 0176)
+ *pcneed = 6;
+ else
+ *pcneed = 7;
+ }
+
+ return TRUE;
+}
+
+/* Wait for data to come in and process it until we've finished a
+ command or a file. */
+
+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)
+ {
+ /* We really want to do something like get all available
+ characters, then sleep for half a second and get all
+ available characters again, and keep this up until we
+ don't get anything after sleeping. */
+ if (! freceive_data (qdaemon->qconn, cneed, &crec, cFtimeout, TRUE))
+ return FALSE;
+ if (crec == 0)
+ {
+ ulog (LOG_ERROR, "Timed out waiting for data");
+ return FALSE;
+ }
+ }
+ }
+}
+
+/* File level operations. Reset the checksums when starting to send
+ or receive a file, and output the checksum when we've finished
+ sending a file. */
+
+/*ARGSUSED*/
+boolean
+fffile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+ boolean fstart;
+ boolean fsend;
+ long cbytes;
+ 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;
+
+ /* We need to handle the checksum and the acknowledgement. If
+ we get a successful ACK, we set fFacked to TRUE and call the
+ send or receive function by hand. This will wind up calling
+ here again, so if fFacked is TRUE we just return out and let
+ the send or receive function do whatever it does. This is a
+ bit of a hack. */
+ if (fFacked)
+ {
+ fFacked = FALSE;
+ return TRUE;
+ }
+
+ if (fsend)
+ {
+ char ab[sizeof "\176\176ABCD\r"];
+
+ /* Send the final checksum. */
+ sprintf (ab, "\176\176%04x\r", iFcheck & 0xffff);
+ if (! fsend_data (qdaemon->qconn, ab, (size_t) 7, TRUE))
+ return FALSE;
+
+ /* Now wait for the acknowledgement. */
+ 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
+ {
+ /* Wait for the checksum. */
+ 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);
+ }
+ }
+}
+
+/* Wait for the ack after sending a file and the checksum. */
+
+static boolean
+ffawait_ack (qtrans, qdaemon, zdata, cdata)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+ const char *zdata;
+ size_t cdata;
+{
+ struct sfinfo *qinfo = (struct sfinfo *) qtrans->pinfo;
+
+ qtrans->precfn = NULL;
+
+ /* An R means to retry sending the file. */
+ 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);
+
+ /* Now call the send function by hand after setting fFacked to TRUE.
+ Since fFacked is true fffile will simply return out, and the send
+ function can do whatever it what was going to do. */
+ fFacked = TRUE;
+ return (*qtrans->psendfn) (qtrans, qdaemon);
+}
+
+/* This function is called when the checksum arrives. */
+
+/*ARGSUSED*/
+static boolean
+ffawait_cksum (qtrans, qdaemon, zdata, cdata)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+ const char *zdata;
+ size_t cdata;
+{
+ 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");
+
+ /* This bit of code relies on the receive code setting
+ qtrans->s.ztemp to the full name of the temporary file
+ being used. */
+ 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;
+
+ /* Send an R to tell the other side to resend the file. */
+ qinfo->bsend = 'R';
+ }
+ }
+ else
+ {
+ /* Send a G to tell the other side the file was received
+ correctly. */
+ qinfo->bsend = 'G';
+ }
+
+ qtrans->psendfn = ffsend_ack;
+
+ return fqueue_send (qdaemon, qtrans);
+}
+
+/* Send the acknowledgement, and then possible wait for the resent
+ file. */
+
+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);
+}
diff --git a/gnu/libexec/uucp/uucico/protg.c b/gnu/libexec/uucp/uucico/protg.c
new file mode 100644
index 0000000000000..39a79212cf771
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/protg.c
@@ -0,0 +1,1933 @@
+/* protg.c
+ The 'g' protocol.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char protg_rcsid[] = "$Id: protg.c,v 1.1 1993/08/05 18:27:11 conklin Exp $";
+#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"
+
+/* Each 'g' protocol packet begins with six bytes. They are:
+
+ <DLE><k><c0><c1><C><x>
+
+ <DLE> is the ASCII DLE character (^P or '\020').
+ if 1 <= <k> <= 8, the packet is followed by 2 ** (k + 4) bytes of data;
+ if <k> == 9, these six bytes are a complete control packet;
+ other value of <k> are illegal.
+ <c0> is the low byte of a checksum.
+ <c1> is the high byte of a checksum.
+ <C> is a control byte (see below).
+ <x> is <k> ^ <c0> ^ <c1> ^ <C>.
+
+ The control byte <C> is divided into three bitfields:
+
+ t t x x x y y y
+
+ The two bit field tt is the packet type.
+ The three bit field xxx is the control type for a control packet, or
+ the sequence number for a data packet.
+ The three bit field yyy is a value for a control packet, or the
+ sequence number of the last packet received for a data packet.
+
+ For all successfully recieved packets, the control byte is stored
+ into iGpacket_control. */
+
+/* Names for the bytes in the frame header. */
+#define IFRAME_DLE (0)
+#define IFRAME_K (1)
+#define IFRAME_CHECKLOW (2)
+#define IFRAME_CHECKHIGH (3)
+#define IFRAME_CONTROL (4)
+#define IFRAME_XOR (5)
+
+/* Length of the frame header. */
+#define CFRAMELEN (6)
+
+/* Macros to break apart the control bytes. */
+#define CONTROL_TT(b) ((int)(((b) >> 6) & 03))
+#define CONTROL_XXX(b) ((int)(((b) >> 3) & 07))
+#define CONTROL_YYY(b) ((int)((b) & 07))
+
+/* DLE value. */
+#define DLE ('\020')
+
+/* Get the length of a packet given a pointer to the header. */
+#define CPACKLEN(z) ((size_t) (1 << ((z)[IFRAME_K] + 4)))
+
+/* <k> field value for a control message. */
+#define KCONTROL (9)
+
+/* Get the next sequence number given a sequence number. */
+#define INEXTSEQ(i) ((i + 1) & 07)
+
+/* Compute i1 - i2 modulo 8. */
+#define CSEQDIFF(i1, i2) (((i1) + 8 - (i2)) & 07)
+
+/* Packet types. These are from the tt field.
+ CONTROL -- control packet
+ ALTCHAN -- alternate channel; not used by UUCP
+ DATA -- full data segment
+ SHORTDATA -- less than full data segment (all the bytes specified by
+ the packet length <k> are always transferred). Let <u> be the number
+ of bytes in the data segment not to be used. If <u> <= 0x7f, the first
+ byte of the data segment is <u> and the data follows. If <u> > 0x7f,
+ the first byte of the data segment is 0x80 | (<u> & 0x7f), the second
+ byte of the data segment is <u> >> 7, and the data follows. The
+ maximum possible data segment size is 2**12, so this handles all
+ possible cases. */
+#define CONTROL (0)
+#define ALTCHAN (1)
+#define DATA (2)
+#define SHORTDATA (3)
+
+/* Control types. These are from the xxx field if the type (tt field)
+ is CONTROL.
+
+ CLOSE -- close the connection
+ RJ -- reject; packet yyy last to be received correctly
+ SRJ -- selective reject; reject only packet yyy (not used by UUCP)
+ RR -- receiver ready; packet yyy received correctly
+ INITC -- third step of initialization; yyy holds window size
+ INITB -- second step of initialization; yyy holds maximum <k> value - 1
+ INITA -- first step of initialization; yyy holds window size.
+
+ The yyy value for RR is the same as the yyy value for an ordinary
+ data packet. */
+#define CLOSE (1)
+#define RJ (2)
+#define SRJ (3)
+#define RR (4)
+#define INITC (5)
+#define INITB (6)
+#define INITA (7)
+
+/* Maximum amount of data in a single packet. This is set by the <k>
+ field in the header; the amount of data in a packet is
+ 2 ** (<k> + 4). <k> ranges from 1 to 8. */
+
+#define CMAXDATAINDEX (8)
+
+#define CMAXDATA (1 << (CMAXDATAINDEX + 4))
+
+/* Maximum window size. */
+#define CMAXWINDOW (7)
+
+/* Defaults for the protocol parameters. These may all be changed by
+ using the ``protocol-parameter g'' command, so there is no
+ particular reason to change the values given here. */
+
+/* The desired window size. This is what we tell the other system to
+ use. It must be between 1 and 7, and there's no reason to use less
+ than 7. Protocol parameter ``window''. */
+#define IWINDOW (7)
+
+/* The desired packet size. Many implementations only support 64 byte
+ packets. Protocol parameter ``packet-size''. */
+#define IPACKSIZE (64)
+
+/* The number of times to retry the exchange of INIT packets when
+ starting the protocol. Protocol parameter ``startup-retries''. */
+#define CSTARTUP_RETRIES (8)
+
+/* The timeout to use when waiting for an INIT packet when starting up
+ the protocol. Protocol parameter ``init-timeout''. */
+#define CEXCHANGE_INIT_TIMEOUT (10)
+
+/* The number of times to retry sending and waiting for a single INIT
+ packet when starting the protocol. This controls a single INIT
+ packet, while CSTARTUP_RETRIES controls how many times to try the
+ entire INIT sequence. Protocol parameter ``init-retries''. */
+#define CEXCHANGE_INIT_RETRIES (4)
+
+/* The timeout to use when waiting for a packet. Protocol parameter
+ ``timeout''. */
+#define CTIMEOUT (10)
+
+/* The number of times to retry waiting for a packet. Each time the
+ timeout fails we send a copy of our last data packet or a reject
+ message for the packet we expect from the other side, depending on
+ whether we are waiting for an acknowledgement or a data packet.
+ This is the number of times we try doing that and then waiting
+ again. Protocol parameter ``retries''. */
+#define CRETRIES (6)
+
+/* If we see more than this much unrecognized data, we drop the
+ connection. This must be larger than a single packet size, which
+ means it must be larger than 4096 (the largest possible packet
+ size). Protocol parameter ``garbage''. */
+#define CGARBAGE (10000)
+
+/* If we see more than this many protocol errors, we drop the
+ connection. Protocol parameter ``errors''. */
+#define CERRORS (100)
+
+/* Default decay rate. Each time we send or receive this many packets
+ succesfully, we decrement the error level by one (protocol
+ parameter ``error-decay''). */
+#define CERROR_DECAY (10)
+
+/* If this value is non-zero, it will be used as the remote window
+ size regardless of what the other side requested. This can be
+ useful for dealing with some particularly flawed packages. This
+ default value should always be 0, and protocol parameter
+ ``remote-window'' should be used for the affected systems. */
+#define IREMOTE_WINDOW (0)
+
+/* If this value is non-zero, it will be used as the packet size to
+ send to the remote system regardless of what it requested. It's
+ difficult to imagine any circumstances where you would want to set
+ this. Protocol parameter ``remote-packet-size''. */
+#define IREMOTE_PACKSIZE (0)
+
+/* Local variables. */
+
+/* Next sequence number to send. */
+static int iGsendseq;
+
+/* Last sequence number that has been acked. */
+static int iGremote_ack;
+
+/* Last sequence number to be retransmitted. */
+static int iGretransmit_seq;
+
+/* Last sequence number we have received. */
+static int iGrecseq;
+
+/* Last sequence number we have acked. */
+static int iGlocal_ack;
+
+/* Window size to request (protocol parameter ``window''). */
+static int iGrequest_winsize = IWINDOW;
+
+/* Packet size to request (protocol parameter ``packet-size''). */
+static int iGrequest_packsize = IPACKSIZE;
+
+/* Remote window size (set during handshake). */
+static int iGremote_winsize;
+
+/* Forced remote window size (protocol parameter ``remote-window''). */
+static int iGforced_remote_winsize = IREMOTE_WINDOW;
+
+/* Remote segment size (set during handshake). This is one less than
+ the value in a packet header. */
+static int iGremote_segsize;
+
+/* Remote packet size (set based on iGremote_segsize). */
+static size_t iGremote_packsize;
+
+/* Forced remote packet size (protocol parameter
+ ``remote-packet-size''). */
+static int iGforced_remote_packsize = IREMOTE_PACKSIZE;
+
+/* Recieved control byte. */
+static int iGpacket_control;
+
+/* Number of times to retry the initial handshake. Protocol parameter
+ ``startup-retries''. */
+static int cGstartup_retries = CSTARTUP_RETRIES;
+
+/* Number of times to retry sending an initial control packet.
+ Protocol parameter ``init-retries''. */
+static int cGexchange_init_retries = CEXCHANGE_INIT_RETRIES;
+
+/* Timeout (seconds) for receiving an initial control packet.
+ Protocol parameter ``init-timeout''. */
+static int cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT;
+
+/* Timeout (seconds) for receiving a data packet. Protocol parameter
+ ``timeout''. */
+static int cGtimeout = CTIMEOUT;
+
+/* Maximum number of timeouts when receiving a data packet or
+ acknowledgement. Protocol parameter ``retries''. */
+static int cGretries = CRETRIES;
+
+/* Amount of garbage data we are prepared to see before giving up.
+ Protocol parameter ``garbage''. */
+static int cGgarbage_data = CGARBAGE;
+
+/* Maximum number of errors we are prepared to see before giving up.
+ Protocol parameter ``errors''. */
+static int cGmax_errors = CERRORS;
+
+/* Each time we receive this many packets succesfully, we decrement
+ the error level by one (protocol parameter ``error-decay''). */
+static int cGerror_decay = CERROR_DECAY;
+
+/* Whether to use shorter packets when possible. Protocol parameter
+ ``short-packets''. */
+static boolean fGshort_packets = TRUE;
+
+/* Protocol parameter commands. */
+struct uuconf_cmdtab asGproto_params[] =
+{
+ { "window", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_winsize, NULL },
+ { "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iGrequest_packsize,
+ NULL },
+ { "startup-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGstartup_retries,
+ NULL },
+ { "init-timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_timeout,
+ NULL },
+ { "init-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGexchange_init_retries,
+ NULL },
+ { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cGtimeout, NULL },
+ { "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cGretries, NULL },
+ { "garbage", UUCONF_CMDTABTYPE_INT, (pointer) &cGgarbage_data, NULL },
+ { "errors", UUCONF_CMDTABTYPE_INT, (pointer) &cGmax_errors, NULL },
+ { "error-decay", UUCONF_CMDTABTYPE_INT, (pointer) &cGerror_decay, NULL },
+ { "remote-window", UUCONF_CMDTABTYPE_INT,
+ (pointer) &iGforced_remote_winsize, NULL },
+ { "remote-packet-size", UUCONF_CMDTABTYPE_INT,
+ (pointer) &iGforced_remote_packsize, NULL },
+ { "short-packets", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) &fGshort_packets,
+ NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+/* Statistics. */
+
+/* Number of packets we have sent. */
+static long cGsent_packets;
+
+/* Number of packets we have resent (these are not included in
+ cGsent_packets). */
+static long cGresent_packets;
+
+/* Number of packets we have delayed sending (these should not be
+ counted in cGresent_packets). */
+static long cGdelayed_packets;
+
+/* Number of packets we have received. */
+static long cGrec_packets;
+
+/* Number of packets rejected because the header was bad. */
+static long cGbad_hdr;
+
+/* Number of packets rejected because the checksum was bad. */
+static long cGbad_checksum;
+
+/* Number of packets received out of order. */
+static long cGbad_order;
+
+/* Number of packets rejected by receiver (number of RJ packets
+ received). */
+static long cGremote_rejects;
+
+/* The error level. This is the total number of errors as adjusted by
+ cGerror_decay. */
+static long cGerror_level;
+
+/* Each time we send an RJ, we can expect several out of order of
+ packets, because the other side will probably have sent a full
+ window by the time it sees the RJ. This variable keeps track of
+ the number of out of order packets we expect to see. We don't
+ count expected out of order packets against the error level. This
+ is reset to 0 when an in order packet is received. */
+static int cGexpect_bad_order;
+
+#if DEBUG > 1
+/* Control packet names used for debugging. */
+static const char * const azGcontrol[] =
+{"?0?", "CLOSE", "RJ", "SRJ", "RR", "INITC", "INITB", "INITA"};
+#endif
+
+/* Local functions. */
+static boolean fgexchange_init P((struct sdaemon *qdaemon, int ictl,
+ int ival, int *piset));
+static boolean fgsend_control P((struct sdaemon *qdaemon, int ictl,
+ int ival));
+static char *zgadjust_ack P((int iseq));
+static boolean fgwait_for_packet P((struct sdaemon *qdaemon,
+ boolean freturncontrol, int ctimeout,
+ int cretries));
+static boolean fgsend_acks P((struct sdaemon *qdaemon));
+static boolean fggot_ack P((struct sdaemon *qdaemon, int iack));
+static boolean fgprocess_data P((struct sdaemon *qdaemon, boolean fdoacks,
+ boolean freturncontrol,
+ boolean *pfexit, size_t *pcneed,
+ boolean *pffound));
+static boolean fginit_sendbuffers P((boolean fallocate));
+static boolean fgcheck_errors P((struct sdaemon *qdaemon));
+static int igchecksum P((const char *zdata, size_t clen));
+static int igchecksum2 P((const char *zfirst, size_t cfirst,
+ const char *zsecond, size_t csecond));
+
+/* Start the protocol. This requires a three way handshake. Both sides
+ must send and receive an INITA packet, an INITB packet, and an INITC
+ packet. The INITA and INITC packets contain the window size, and the
+ INITB packet contains the packet size. */
+
+boolean
+fgstart (qdaemon, pzlog)
+ struct sdaemon *qdaemon;
+ char **pzlog;
+{
+ int iseg;
+ int i;
+ boolean fgota, fgotb;
+
+ *pzlog = NULL;
+
+ /* The 'g' protocol requires a full eight bit interface. */
+ if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
+ STRIPSETTING_EIGHTBITS, XONXOFF_OFF))
+ return FALSE;
+
+ iGsendseq = 1;
+ iGremote_ack = 0;
+ iGretransmit_seq = -1;
+ iGrecseq = 0;
+ iGlocal_ack = 0;
+ cGsent_packets = 0;
+ cGresent_packets = 0;
+ cGdelayed_packets = 0;
+ cGrec_packets = 0;
+ cGbad_hdr = 0;
+ cGbad_checksum = 0;
+ cGbad_order = 0;
+ cGremote_rejects = 0;
+ cGerror_level = 0;
+ cGexpect_bad_order = 0;
+
+ /* We must determine the segment size based on the packet size
+ which may have been modified by a protocol parameter command.
+ A segment size of 2^n is passed as n - 5. */
+ i = iGrequest_packsize;
+ iseg = -1;
+ while (i > 0)
+ {
+ ++iseg;
+ i >>= 1;
+ }
+ iseg -= 5;
+ if (iseg < 0 || iseg > 7)
+ {
+ ulog (LOG_ERROR, "Illegal packet size %d for '%c' protocol",
+ iGrequest_packsize, qdaemon->qproto->bname);
+ iseg = 1;
+ }
+
+ fgota = FALSE;
+ fgotb = FALSE;
+ for (i = 0; i < cGstartup_retries; i++)
+ {
+ if (fgota)
+ {
+ if (! fgsend_control (qdaemon, INITA, iGrequest_winsize))
+ return FALSE;
+ }
+ else
+ {
+ if (! fgexchange_init (qdaemon, INITA, iGrequest_winsize,
+ &iGremote_winsize))
+ continue;
+ }
+ fgota = TRUE;
+
+ if (fgotb)
+ {
+ if (! fgsend_control (qdaemon, INITB, iseg))
+ return FALSE;
+ }
+ else
+ {
+ if (! fgexchange_init (qdaemon, INITB, iseg, &iGremote_segsize))
+ continue;
+ }
+ fgotb = TRUE;
+
+ if (! fgexchange_init (qdaemon, INITC, iGrequest_winsize,
+ &iGremote_winsize))
+ continue;
+
+ /* We have succesfully connected. Determine the remote packet
+ size. */
+ iGremote_packsize = 1 << (iGremote_segsize + 5);
+
+ /* If the user requested us to force specific remote window and
+ packet sizes, do so now. */
+ if (iGforced_remote_winsize > 0
+ && iGforced_remote_winsize <= CMAXWINDOW)
+ iGremote_winsize = iGforced_remote_winsize;
+
+ if (iGforced_remote_packsize >= 32
+ && iGforced_remote_packsize <= 4096)
+ {
+ /* Force the value to a power of two. */
+ i = iGforced_remote_packsize;
+ iseg = -1;
+ while (i > 0)
+ {
+ ++iseg;
+ i >>= 1;
+ }
+ iGremote_packsize = 1 << iseg;
+ iGremote_segsize = iseg - 5;
+ }
+
+ /* Set up packet buffers to use. We don't do this until we know
+ the maximum packet size we are going to send. */
+ if (! fginit_sendbuffers (TRUE))
+ return FALSE;
+
+ *pzlog = zbufalc (sizeof "protocol '' packet size window " + 50);
+ sprintf (*pzlog, "protocol '%c' packet size %d window %d",
+ qdaemon->qproto->bname, (int) iGremote_packsize,
+ (int) iGremote_winsize);
+
+ return TRUE;
+ }
+
+ DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgstart: Protocol startup failed");
+
+ return FALSE;
+}
+
+/* The 'G' protocol is identical to the 'g' protocol, except that
+ short packets are never supported. */
+
+boolean
+fbiggstart (qdaemon, pzlog)
+ struct sdaemon *qdaemon;
+ char **pzlog;
+{
+ fGshort_packets = FALSE;
+ return fgstart (qdaemon, pzlog);
+}
+
+/* Exchange initialization messages with the other system.
+
+ A problem:
+
+ We send INITA; it gets received
+ We receive INITA
+ We send INITB; it gets garbled
+ We receive INITB
+
+ We have seen and sent INITB, so we start to send INITC. The other
+ side as sent INITB but not seen it, so it times out and resends
+ INITB. We will continue sending INITC and the other side will
+ continue sending INITB until both sides give up and start again
+ with INITA.
+
+ It might seem as though if we are sending INITC and receive INITB,
+ we should resend our INITB, but this could cause infinite echoing
+ of INITB on a long-latency line. Rather than risk that, I have
+ implemented a fast drop-back procedure. If we are sending INITB and
+ receive INITC, the other side has gotten ahead of us. We immediately
+ fail and begin again with INITA. For the other side, if we are
+ sending INITC and see INITA, we also immediately fail back to INITA.
+
+ Unfortunately, this doesn't work for the other case, in which we
+ are sending INITB but the other side has not yet seen INITA. As
+ far as I can see, if this happens we just have to wait until we
+ time out and resend INITA. */
+
+static boolean
+fgexchange_init (qdaemon, ictl, ival, piset)
+ struct sdaemon *qdaemon;
+ int ictl;
+ int ival;
+ int *piset;
+{
+ int i;
+
+ /* The three-way handshake should be independent of who initializes
+ it, but it seems that some versions of uucico assume that the
+ caller sends first and the callee responds. This only matters if
+ we are the callee and the first packet is garbled. If we send a
+ packet, the other side will assume that we must have seen the
+ packet they sent and will never time out and send it again.
+ Therefore, if we are the callee we don't send a packet the first
+ time through the loop. This can still fail, but should usually
+ work, and, after all, if the initialization packets are received
+ correctly there will be no problem no matter what we do. */
+ for (i = 0; i < cGexchange_init_retries; i++)
+ {
+ long itime;
+ int ctimeout;
+
+ if (qdaemon->fcaller || i > 0)
+ {
+ if (! fgsend_control (qdaemon, ictl, ival))
+ return FALSE;
+ }
+
+ itime = ixsysdep_time ((long *) NULL);
+ ctimeout = cGexchange_init_timeout;
+
+ do
+ {
+ long inewtime;
+
+ /* We pass 0 as the retry count to fgwait_for_packet because
+ we want to handle retries here and because if it retried
+ it would send a packet, which would be bad. */
+ if (! fgwait_for_packet (qdaemon, TRUE, ctimeout, 0))
+ break;
+
+ if (CONTROL_TT (iGpacket_control) == CONTROL)
+ {
+ if (CONTROL_XXX (iGpacket_control) == ictl)
+ {
+ *piset = CONTROL_YYY (iGpacket_control);
+
+ /* If we didn't already send our initialization
+ packet, send it now. */
+ if (! qdaemon->fcaller && i == 0)
+ {
+ if (! fgsend_control (qdaemon, ictl, ival))
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ /* If the other side is farther along than we are,
+ we have lost a packet. Fail immediately back to
+ INITA (but don't fail if we are already doing INITA,
+ since that would count against cStart_retries more
+ than it should). */
+ if (CONTROL_XXX (iGpacket_control) < ictl && ictl != INITA)
+ return FALSE;
+
+ /* If we are sending INITC and we receive an INITA, the other
+ side has failed back (we know this because we have
+ seen an INITB from them). Fail back ourselves to
+ start the whole handshake over again. */
+ if (CONTROL_XXX (iGpacket_control) == INITA && ictl == INITC)
+ return FALSE;
+
+ /* As a special hack, if we are sending INITC and we
+ receive INITB, we update the segment size from the
+ packet. This permits a second INITB to override the
+ first one. It would be nice to do this in a cleaner
+ way. */
+ if (CONTROL_XXX (iGpacket_control) == INITB && ictl == INITC)
+ iGremote_segsize = CONTROL_YYY (iGpacket_control);
+ }
+
+ inewtime = ixsysdep_time ((long *) NULL);
+ ctimeout -= inewtime - itime;
+ }
+ while (ctimeout > 0);
+ }
+
+ return FALSE;
+}
+
+/* Shut down the protocol. */
+
+boolean
+fgshutdown (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ (void) fgsend_control (qdaemon, CLOSE, 0);
+ (void) fgsend_control (qdaemon, CLOSE, 0);
+ (void) fginit_sendbuffers (FALSE);
+
+ /* The count of sent packets may not be accurate, because some of
+ them may have not been sent yet if the connection failed in the
+ middle (the ones that counted for cGdelayed_packets). I don't
+ think it's worth being precise. */
+ ulog (LOG_NORMAL,
+ "Protocol '%c' packets: sent %ld, resent %ld, received %ld",
+ qdaemon->qproto->bname, cGsent_packets,
+ cGresent_packets - cGdelayed_packets, cGrec_packets);
+ if (cGbad_hdr != 0
+ || cGbad_checksum != 0
+ || cGbad_order != 0
+ || cGremote_rejects != 0)
+ ulog (LOG_NORMAL,
+ "Errors: header %ld, checksum %ld, order %ld, remote rejects %ld",
+ cGbad_hdr, cGbad_checksum, cGbad_order, cGremote_rejects);
+
+ /* Reset all the parameters to their default values, so that the
+ protocol parameters used for this connection do not affect the
+ next one. */
+ iGrequest_winsize = IWINDOW;
+ iGrequest_packsize = IPACKSIZE;
+ cGstartup_retries = CSTARTUP_RETRIES;
+ cGexchange_init_timeout = CEXCHANGE_INIT_TIMEOUT;
+ cGexchange_init_retries = CEXCHANGE_INIT_RETRIES;
+ cGtimeout = CTIMEOUT;
+ cGretries = CRETRIES;
+ cGgarbage_data = CGARBAGE;
+ cGmax_errors = CERRORS;
+ cGerror_decay = CERROR_DECAY;
+ iGforced_remote_winsize = IREMOTE_WINDOW;
+ iGforced_remote_packsize = IREMOTE_PACKSIZE;
+ fGshort_packets = TRUE;
+
+ return TRUE;
+}
+
+/* Send a command string. We send packets containing the string until
+ the entire string has been sent. Each packet is full. */
+
+/*ARGSUSED*/
+boolean
+fgsendcmd (qdaemon, z, ilocal, iremote)
+ struct sdaemon *qdaemon;
+ const char *z;
+ int ilocal;
+ int iremote;
+{
+ size_t clen;
+ boolean fagain;
+
+ DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fgsendcmd: Sending command \"%s\"", z);
+
+ clen = strlen (z);
+
+ do
+ {
+ char *zpacket;
+ size_t cdummy;
+
+ zpacket = zggetspace (qdaemon, &cdummy);
+
+ if (clen < iGremote_packsize)
+ {
+ size_t csize;
+
+ /* If the remote packet size is larger than 64 (the default,
+ which may indicate an older UUCP package), try to fit
+ this command into a smaller packet. We still always send
+ a complete packet, though. */
+ if (iGremote_packsize <= 64 || ! fGshort_packets)
+ csize = iGremote_packsize;
+ else
+ {
+ csize = 32;
+ while (csize <= clen)
+ csize <<= 1;
+ }
+
+ memcpy (zpacket, z, clen);
+ bzero (zpacket + clen, csize - clen);
+ fagain = FALSE;
+
+ if (! fgsenddata (qdaemon, zpacket, csize, 0, 0, (long) 0))
+ return FALSE;
+ }
+ else
+ {
+ memcpy (zpacket, z, iGremote_packsize);
+ z += iGremote_packsize;
+ clen -= iGremote_packsize;
+ fagain = TRUE;
+
+ if (! fgsenddata (qdaemon, zpacket, iGremote_packsize,
+ 0, 0, (long) 0))
+ return FALSE;
+ }
+ }
+ while (fagain);
+
+ return TRUE;
+}
+
+/* We keep an array of buffers to retransmit as necessary. Rather
+ than waste static space on large buffer sizes, we allocate the
+ buffers once we know how large the other system expects them to be.
+ The sequence numbers used in the 'g' protocol are only three bits
+ long, so we allocate eight buffers and maintain a correspondence
+ between buffer index and sequence number. This always wastes some
+ buffer space, but it's easy to implement.
+
+ We leave room at the front of the buffer for the frame header and
+ two additional bytes. The two extra bytes are used for short
+ packets, which essentially use a longer header and shorter data.
+ We do this to avoid moving the data. We zero out any unused bytes
+ before the frame, so we can locate the real header given a buffer
+ by finding the first non-zero byte (which will be one of the first
+ three bytes in the buffer). */
+
+#define CSENDBUFFERS (CMAXWINDOW + 1)
+
+static char *azGsendbuffers[CSENDBUFFERS];
+
+static boolean
+fginit_sendbuffers (fallocate)
+ boolean fallocate;
+{
+ int i;
+
+ /* Free up any remaining old buffers. */
+ for (i = 0; i < CSENDBUFFERS; i++)
+ {
+ xfree ((pointer) azGsendbuffers[i]);
+ if (fallocate)
+ {
+ azGsendbuffers[i] = (char *) malloc (CFRAMELEN + 2
+ + iGremote_packsize);
+ if (azGsendbuffers[i] == NULL)
+ return FALSE;
+
+ /* This bzero might not seem necessary, since before we send
+ out each packet we zero out any non-data bytes. However,
+ if we receive an SRJ at the start of the conversation, we
+ will send out the packet before it has been set to
+ anything, thus sending the contents of our heap. We
+ avoid this by using bzero. */
+ bzero (azGsendbuffers[i], CFRAMELEN + 2 + iGremote_packsize);
+ }
+ else
+ azGsendbuffers[i] = NULL;
+ }
+ return TRUE;
+}
+
+/* Allocate a packet to send out. The return value of this function
+ must be filled in and passed to fgsenddata, or discarded. This
+ will ensure that the buffers and iGsendseq stay in synch. Set
+ *pclen to the amount of data to place in the buffer. */
+
+/*ARGSUSED*/
+char *
+zggetspace (qdaemon, pclen)
+ struct sdaemon *qdaemon;
+ size_t *pclen;
+{
+ *pclen = iGremote_packsize;
+ return azGsendbuffers[iGsendseq] + CFRAMELEN + 2;
+}
+
+/* Send out a data packet. This computes the checksum, sets up the
+ header, and sends the packet out. The argument zdata should point
+ to the return value of zggetspace. */
+
+/*ARGSIGNORED*/
+boolean
+fgsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
+ struct sdaemon *qdaemon;
+ char *zdata;
+ size_t cdata;
+ int ilocal;
+ int iremote;
+ long ipos;
+{
+ char *z;
+ int itt, iseg;
+ size_t csize;
+ int iclr1, iclr2;
+ unsigned short icheck;
+
+ /* Set the initial length bytes. See the description at the definition
+ of SHORTDATA, above. */
+ itt = DATA;
+ csize = iGremote_packsize;
+ iseg = iGremote_segsize + 1;
+
+#if DEBUG > 0
+ if (cdata > csize)
+ ulog (LOG_FATAL, "fgsend_packet: Packet size too large");
+#endif
+
+ iclr1 = -1;
+ iclr2 = -2;
+ if (cdata < csize)
+ {
+ /* If the remote packet size is larger than 64, the default, we
+ can assume they can handle a smaller packet as well, which
+ will be more efficient to send. */
+ if (iGremote_packsize > 64 && fGshort_packets)
+ {
+ /* The packet size is 1 << (iseg + 4). */
+ iseg = 1;
+ csize = 32;
+ while (csize < cdata)
+ {
+ csize <<= 1;
+ ++iseg;
+ }
+ }
+
+ if (csize != cdata)
+ {
+ size_t cshort;
+
+ /* We have to add bytes which indicate how short the packet
+ is. We do this by pushing the header backward, which we
+ can do because we allocated two extra bytes for this
+ purpose. */
+ iclr2 = 0;
+ itt = SHORTDATA;
+ cshort = csize - cdata;
+ if (cshort <= 127)
+ {
+ --zdata;
+ zdata[0] = (char) cshort;
+ zdata[-1] = '\0';
+ bzero (zdata + cdata + 1, cshort - 1);
+ }
+ else
+ {
+ zdata -= 2;
+ zdata[0] = (char) (0x80 | (cshort & 0x7f));
+ zdata[1] = (char) (cshort >> 7);
+ bzero (zdata + cdata + 2, cshort - 2);
+ iclr1 = 0;
+ }
+ }
+ }
+
+ z = zdata - CFRAMELEN;
+
+ /* Zero out the preceding bytes, in case the last time this buffer
+ was used those bytes were used. We need to zero out the initial
+ bytes so that we can find the true start of the packet in
+ zgadjust_ack. */
+ z[iclr1] = '\0';
+ z[iclr2] = '\0';
+
+ z[IFRAME_DLE] = DLE;
+ z[IFRAME_K] = (char) iseg;
+
+ icheck = (unsigned short) igchecksum (zdata, csize);
+
+ /* We're just about ready to go. Wait until there is room in the
+ receiver's window for us to send the packet. We do this now so
+ that we send the correct value for the last packet received.
+ Note that if iGsendseq == iGremote_ack, this means that the
+ sequence numbers are actually 8 apart, since the packet could not
+ have been acknowledged before it was sent; this can happen when
+ the window size is 7. */
+ while (iGsendseq == iGremote_ack
+ || CSEQDIFF (iGsendseq, iGremote_ack) > iGremote_winsize)
+ {
+ if (! fgwait_for_packet (qdaemon, TRUE, cGtimeout, cGretries))
+ return FALSE;
+ }
+
+ /* Ack all packets up to the next one, since the UUCP protocol
+ requires that all packets be acked in order. */
+ while (CSEQDIFF (iGrecseq, iGlocal_ack) > 1)
+ {
+ iGlocal_ack = INEXTSEQ (iGlocal_ack);
+ if (! fgsend_control (qdaemon, RR, iGlocal_ack))
+ return FALSE;
+ }
+ iGlocal_ack = iGrecseq;
+
+ z[IFRAME_CONTROL] = (char) ((itt << 6) | (iGsendseq << 3) | iGrecseq);
+
+ iGsendseq = INEXTSEQ (iGsendseq);
+
+ icheck = ((unsigned short)
+ ((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff));
+ z[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
+ z[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
+
+ z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW]
+ ^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]);
+
+ /* If we're waiting for acks of retransmitted packets, then don't
+ send this packet yet. The other side may not be ready for it
+ yet. Instead, code in fggot_ack will send the outstanding
+ packets when an ack is received. */
+ ++cGsent_packets;
+
+ if (iGretransmit_seq != -1)
+ {
+ ++cGdelayed_packets;
+ return TRUE;
+ }
+
+ DEBUG_MESSAGE2 (DEBUG_PROTO,
+ "fgsenddata: Sending packet %d (%d bytes)",
+ CONTROL_XXX (z[IFRAME_CONTROL]), cdata);
+
+ return fsend_data (qdaemon->qconn, z, CFRAMELEN + csize, TRUE);
+}
+
+/* Recompute the control byte and checksum of a packet so that it
+ includes the correct packet acknowledgement. This is called when a
+ packet is retransmitted to make sure the retransmission does not
+ confuse the other side. It returns a pointer to the start of the
+ packet, skipping the bytes that may be unused at the start of
+ azGsendbuffers[iseq]. */
+
+static char *
+zgadjust_ack (iseq)
+ int iseq;
+{
+ register char *z;
+ unsigned short icheck;
+
+ z = azGsendbuffers[iseq];
+ if (*z == '\0')
+ ++z;
+ if (*z == '\0')
+ ++z;
+
+ /* If the received packet number is the same, there is nothing
+ to do. */
+ if (CONTROL_YYY (z[IFRAME_CONTROL]) == iGrecseq)
+ return z;
+
+ /* Get the old checksum. */
+ icheck = (unsigned short) (((z[IFRAME_CHECKHIGH] & 0xff) << 8)
+ | (z[IFRAME_CHECKLOW] & 0xff));
+ icheck = ((unsigned short)
+ (((0xaaaa - icheck) ^ (z[IFRAME_CONTROL] & 0xff)) & 0xffff));
+
+ /* Update the control byte. */
+ z[IFRAME_CONTROL] = (char) ((z[IFRAME_CONTROL] &~ 07) | iGrecseq);
+
+ /* Create the new checksum. */
+ icheck = ((unsigned short)
+ ((0xaaaa - (icheck ^ (z[IFRAME_CONTROL] & 0xff))) & 0xffff));
+ z[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
+ z[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
+
+ /* Update the XOR byte. */
+ z[IFRAME_XOR] = (char) (z[IFRAME_K] ^ z[IFRAME_CHECKLOW]
+ ^ z[IFRAME_CHECKHIGH] ^ z[IFRAME_CONTROL]);
+
+ return z;
+}
+
+/* Send a control packet. These are fairly simple to construct. It
+ seems reasonable to me that we should be able to send a control
+ packet at any time, even if the receive window is closed. In
+ particular, we don't want to delay when sending a CLOSE control
+ message. If I'm wrong, it can be changed easily enough. */
+
+static boolean
+fgsend_control (qdaemon, ixxx, iyyy)
+ struct sdaemon *qdaemon;
+ int ixxx;
+ int iyyy;
+{
+ char ab[CFRAMELEN];
+ int ictl;
+ unsigned short icheck;
+
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_PROTO) ||
+ (FDEBUGGING (DEBUG_ABNORMAL) && ixxx != RR))
+ ulog (LOG_DEBUG, "fgsend_control: Sending control %s %d",
+ azGcontrol[ixxx], iyyy);
+#endif
+
+ ab[IFRAME_DLE] = DLE;
+ ab[IFRAME_K] = KCONTROL;
+
+ ictl = (CONTROL << 6) | (ixxx << 3) | iyyy;
+ icheck = (unsigned short) (0xaaaa - ictl);
+ ab[IFRAME_CHECKLOW] = (char) (icheck & 0xff);
+ ab[IFRAME_CHECKHIGH] = (char) (icheck >> 8);
+
+ ab[IFRAME_CONTROL] = (char) ictl;
+
+ ab[IFRAME_XOR] = (char) (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW]
+ ^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL]);
+
+ return fsend_data (qdaemon->qconn, ab, (size_t) CFRAMELEN, TRUE);
+}
+
+/* Wait for data to come in. This continues processing until a
+ complete file or command has been received. */
+
+boolean
+fgwait (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ return fgwait_for_packet (qdaemon, FALSE, cGtimeout, cGretries);
+}
+
+/* Get a packet. This is called when we have nothing to send, but
+ want to wait for a packet to come in. If freturncontrol is TRUE,
+ this will return after getting any control packet. Otherwise, it
+ will continue to receive packets until a complete file or a
+ complete command has been received. The timeout and the number of
+ retries are specified as arguments. The function returns FALSE if
+ an error occurs or if cretries timeouts of ctimeout seconds were
+ exceeded. */
+
+static boolean
+fgwait_for_packet (qdaemon, freturncontrol, ctimeout, cretries)
+ struct sdaemon *qdaemon;
+ boolean freturncontrol;
+ int ctimeout;
+ int cretries;
+{
+ int ctimeouts;
+ int cgarbage;
+ int cshort;
+
+ ctimeouts = 0;
+ cgarbage = 0;
+ cshort = 0;
+
+ while (TRUE)
+ {
+ boolean fexit;
+ size_t cneed;
+ boolean ffound;
+ size_t crec;
+
+ if (! fgprocess_data (qdaemon, TRUE, freturncontrol, &fexit,
+ &cneed, &ffound))
+ return FALSE;
+
+ if (fexit)
+ return TRUE;
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fgwait_for_packet: Need %lu bytes",
+ (unsigned long) cneed);
+
+ if (ffound)
+ {
+ ctimeouts = 0;
+ cgarbage = 0;
+ }
+ else
+ {
+ if (cgarbage > cGgarbage_data)
+ {
+ ulog (LOG_ERROR, "Too much unrecognized data");
+ return FALSE;
+ }
+ }
+
+ if (! freceive_data (qdaemon->qconn, cneed, &crec, ctimeout, TRUE))
+ return FALSE;
+
+ cgarbage += crec;
+
+ if (crec != 0)
+ {
+ /* If we don't get enough data twice in a row, we may have
+ dropped some data and still be looking for the end of a
+ large packet. Incrementing iPrecstart will force
+ fgprocess_data to skip that packet and look through the
+ rest of the data. In some situations, this will be a
+ mistake. */
+ if (crec >= cneed)
+ cshort = 0;
+ else
+ {
+ ++cshort;
+ if (cshort > 1)
+ {
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ cshort = 0;
+ }
+ }
+ }
+ else
+ {
+ /* The read timed out. If we have an unacknowledged packet,
+ send it again. Otherwise, send an RJ with the last
+ packet we received correctly. */
+ ++ctimeouts;
+ if (ctimeouts > cretries)
+ {
+ if (cretries > 0)
+ ulog (LOG_ERROR, "Timed out waiting for packet");
+ return FALSE;
+ }
+
+ if (INEXTSEQ (iGremote_ack) != iGsendseq)
+ {
+ int inext;
+ char *zsend;
+
+ inext = INEXTSEQ (iGremote_ack);
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgwait_for_packet: Resending packet %d",
+ inext);
+
+ ++cGresent_packets;
+ zsend = zgadjust_ack (inext);
+ if (! fsend_data (qdaemon->qconn, zsend,
+ CFRAMELEN + CPACKLEN (zsend), TRUE))
+ return FALSE;
+ iGretransmit_seq = inext;
+ }
+ else
+ {
+ /* Send all pending acks first, to avoid confusing
+ the other side. */
+ if (iGlocal_ack != iGrecseq)
+ {
+ if (! fgsend_acks (qdaemon))
+ return FALSE;
+ }
+ if (! fgsend_control (qdaemon, RJ, iGrecseq))
+ return FALSE;
+ }
+ }
+ }
+}
+
+/* Send acks for all packets we haven't acked yet. */
+
+static boolean
+fgsend_acks (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ while (iGlocal_ack != iGrecseq)
+ {
+ iGlocal_ack = INEXTSEQ (iGlocal_ack);
+ if (! fgsend_control (qdaemon, RR, iGlocal_ack))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Handle an ack of a packet. According to Hanrahan's paper, this
+ acknowledges all previous packets. If this is an ack for a
+ retransmitted packet, continue by resending up to two more packets
+ following the retransmitted one. This should recover quickly from
+ a line glitch, while avoiding the problem of continual
+ retransmission. */
+
+static boolean
+fggot_ack (qdaemon, iack)
+ struct sdaemon *qdaemon;
+ int iack;
+{
+ int inext;
+ char *zsend;
+
+ /* We only decrement the error level if we are not retransmitting
+ packets. We want to catch a sudden downgrade in line quality as
+ fast as possible. */
+ if (cGerror_level > 0
+ && iGretransmit_seq == -1
+ && cGsent_packets % cGerror_decay == 0)
+ --cGerror_level;
+ cGexpect_bad_order = 0;
+
+ /* Each time packet 0 is acknowledged, we call uwindow_acked since a
+ new window has been acked. */
+ if (iack < iGremote_ack)
+ uwindow_acked (qdaemon, FALSE);
+
+ iGremote_ack = iack;
+
+ if (iGretransmit_seq == -1)
+ return TRUE;
+
+ inext = INEXTSEQ (iGretransmit_seq);
+ if (inext == iGsendseq)
+ iGretransmit_seq = -1;
+ else
+ {
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fggot_ack: Sending packet %d", inext);
+
+ ++cGresent_packets;
+ zsend = zgadjust_ack (inext);
+ if (! fsend_data (qdaemon->qconn, zsend, CFRAMELEN + CPACKLEN (zsend),
+ TRUE))
+ return FALSE;
+ inext = INEXTSEQ (inext);
+ if (inext == iGsendseq)
+ iGretransmit_seq = -1;
+ else
+ {
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fggot_ack: Sending packet %d", inext);
+
+ ++cGresent_packets;
+ zsend = zgadjust_ack (inext);
+ if (! fsend_data (qdaemon->qconn, zsend,
+ CFRAMELEN + CPACKLEN (zsend), TRUE))
+ return FALSE;
+ iGretransmit_seq = inext;
+ }
+ }
+
+ return TRUE;
+}
+
+/* See if we've received more than the permitted number of errors. If
+ we receive a bad packet, we can expect a window full (less one) of
+ out of order packets to follow, so we discount cGbad_order
+ accordingly. */
+
+static boolean
+fgcheck_errors (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ if (cGerror_level > cGmax_errors && cGmax_errors >= 0)
+ {
+ ulog (LOG_ERROR, "Too many '%c' protocol errors",
+ qdaemon->qproto->bname);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Process the receive buffer into a data packet, if possible. All
+ control packets are handled here. When a data packet is received,
+ fgprocess_data calls fgot_data with the data; if that sets its
+ pfexit argument to TRUE fgprocess_data will set *pfexit to TRUE and
+ return TRUE. Also, if the freturncontrol argument is TRUE
+ fgprocess_data will set *pfexit to TRUE and return TRUE. Otherwise
+ fgprocess_data will continue trying to process data. If some error
+ occurs, fgprocess_data will return FALSE. If there is not enough
+ data to form a complete packet, then *pfexit will be set to FALSE,
+ *pcneed will be set to the number of bytes needed to form a
+ complete packet (unless pcneed is NULL) and fgprocess_data will
+ return TRUE. If this function found a data packet, and pffound is
+ not NULL, it will set *pffound to TRUE; this can be used to tell
+ valid data from an endless stream of garbage and control packets.
+ If fdoacks is TRUE, received packets will be acknowledged;
+ otherwise they must be acknowledged later. */
+
+static boolean
+fgprocess_data (qdaemon, fdoacks, freturncontrol, pfexit, pcneed, pffound)
+ struct sdaemon *qdaemon;
+ boolean fdoacks;
+ boolean freturncontrol;
+ boolean *pfexit;
+ size_t *pcneed;
+ boolean *pffound;
+{
+ *pfexit = FALSE;
+ if (pffound != NULL)
+ *pffound = FALSE;
+
+ while (iPrecstart != iPrecend)
+ {
+ char ab[CFRAMELEN];
+ int i, iget, cwant;
+ unsigned short ihdrcheck, idatcheck;
+ const char *zfirst, *zsecond;
+ int cfirst, csecond;
+ boolean fduprr;
+
+ /* Look for the DLE which must start a packet. */
+ if (abPrecbuf[iPrecstart] != DLE)
+ {
+ char *zdle;
+
+ cfirst = iPrecend - iPrecstart;
+ if (cfirst < 0)
+ cfirst = CRECBUFLEN - iPrecstart;
+
+ zdle = memchr (abPrecbuf + iPrecstart, DLE, (size_t) cfirst);
+
+ if (zdle == NULL)
+ {
+ iPrecstart = (iPrecstart + cfirst) % CRECBUFLEN;
+ continue;
+ }
+
+ /* We don't need % CRECBUFLEN here because zdle - (abPrecbuf
+ + iPrecstart) < cfirst <= CRECBUFLEN - iPrecstart. */
+ iPrecstart += zdle - (abPrecbuf + iPrecstart);
+ }
+
+ /* Get the first six bytes into ab. */
+ for (i = 0, iget = iPrecstart;
+ i < CFRAMELEN && iget != iPrecend;
+ i++, iget = (iget + 1) % CRECBUFLEN)
+ ab[i] = abPrecbuf[iget];
+
+ /* If there aren't six bytes, there is no packet. */
+ if (i < CFRAMELEN)
+ {
+ if (pcneed != NULL)
+ *pcneed = CFRAMELEN - i;
+ return TRUE;
+ }
+
+ /* Make sure these six bytes start a packet. The check on
+ IFRAME_DLE is basically a debugging check, since the above
+ code should have ensured that it will never fail. If this is
+ not the start of a packet, bump iPrecstart and loop around to
+ look for another DLE. */
+ if (ab[IFRAME_DLE] != DLE
+ || ab[IFRAME_K] < 1
+ || ab[IFRAME_K] > 9
+ || ab[IFRAME_XOR] != (ab[IFRAME_K] ^ ab[IFRAME_CHECKLOW]
+ ^ ab[IFRAME_CHECKHIGH] ^ ab[IFRAME_CONTROL])
+ || CONTROL_TT (ab[IFRAME_CONTROL]) == ALTCHAN)
+ {
+ ++cGbad_hdr;
+ ++cGerror_level;
+
+ DEBUG_MESSAGE4 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Bad header: K %d TT %d XOR byte %d calc %d",
+ ab[IFRAME_K] & 0xff,
+ CONTROL_TT (ab[IFRAME_CONTROL]),
+ ab[IFRAME_XOR] & 0xff,
+ (ab[IFRAME_K]
+ ^ ab[IFRAME_CHECKLOW]
+ ^ ab[IFRAME_CHECKHIGH]
+ ^ ab[IFRAME_CONTROL]) & 0xff);
+
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ continue;
+ }
+
+ /* The zfirst and cfirst pair point to the first set of data for
+ this packet; the zsecond and csecond point to the second set,
+ in case the packet wraps around the end of the buffer. */
+ zfirst = abPrecbuf + iPrecstart + CFRAMELEN;
+ cfirst = 0;
+ zsecond = NULL;
+ csecond = 0;
+
+ if (ab[IFRAME_K] == KCONTROL)
+ {
+ /* This is a control packet. It should not have any data. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
+ {
+ ++cGbad_hdr;
+ ++cGerror_level;
+
+ DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Bad header: control packet with data");
+
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ continue;
+ }
+
+ idatcheck = (unsigned short) (0xaaaa - ab[IFRAME_CONTROL]);
+ cwant = 0;
+ }
+ else
+ {
+ int cinbuf;
+ unsigned short icheck;
+
+ /* This is a data packet. It should not be type CONTROL. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL)
+ {
+ ++cGbad_hdr;
+ ++cGerror_level;
+
+ DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Bad header: data packet is type CONTROL");
+
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ continue;
+ }
+
+ cinbuf = iPrecend - iPrecstart;
+ if (cinbuf < 0)
+ cinbuf += CRECBUFLEN;
+ cinbuf -= CFRAMELEN;
+
+ /* Make sure we have enough data. If we don't, wait for
+ more. */
+
+ cwant = (int) CPACKLEN (ab);
+ if (cinbuf < cwant)
+ {
+ if (pcneed != NULL)
+ *pcneed = cwant - cinbuf;
+ return TRUE;
+ }
+
+ /* Set up the data pointers and compute the checksum. */
+ if (iPrecend >= iPrecstart)
+ cfirst = cwant;
+ else
+ {
+ cfirst = CRECBUFLEN - (iPrecstart + CFRAMELEN);
+ if (cfirst >= cwant)
+ cfirst = cwant;
+ else if (cfirst > 0)
+ {
+ zsecond = abPrecbuf;
+ csecond = cwant - cfirst;
+ }
+ else
+ {
+ /* Here cfirst is non-positive, so subtracting from
+ abPrecbuf will actually skip the appropriate number
+ of bytes at the start of abPrecbuf. */
+ zfirst = abPrecbuf - cfirst;
+ cfirst = cwant;
+ }
+ }
+
+ if (csecond == 0)
+ icheck = (unsigned short) igchecksum (zfirst, (size_t) cfirst);
+ else
+ icheck = (unsigned short) igchecksum2 (zfirst, (size_t) cfirst,
+ zsecond,
+ (size_t) csecond);
+
+ idatcheck = ((unsigned short)
+ (((0xaaaa - (icheck ^ (ab[IFRAME_CONTROL] & 0xff)))
+ & 0xffff)));
+ }
+
+ ihdrcheck = (unsigned short) (((ab[IFRAME_CHECKHIGH] & 0xff) << 8)
+ | (ab[IFRAME_CHECKLOW] & 0xff));
+
+ if (ihdrcheck != idatcheck)
+ {
+ DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Bad checksum: header 0x%x, data 0x%x",
+ ihdrcheck, idatcheck);
+
+ ++cGbad_checksum;
+ ++cGerror_level;
+
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+
+ /* If the checksum failed for a data packet, then if it was
+ the one we were expecting send an RJ, otherwise ignore
+ it. Previously if this code got the wrong packet number
+ it would send an RR, but that may confuse some Telebit
+ modems and it doesn't help in any case since the receiver
+ will probably just ignore the RR as a duplicate (that's
+ basically what this code does). If we totally missed the
+ packet we will time out and send an RJ in the function
+ fgwait_for_packet above. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
+ {
+ /* Make sure we've acked everything up to this point. */
+ if (iGrecseq != iGlocal_ack)
+ {
+ if (! fgsend_acks (qdaemon))
+ return FALSE;
+ }
+
+ /* If this is the packet we wanted, tell the sender that
+ it failed. */
+ if (CONTROL_XXX (ab[IFRAME_CONTROL]) == INEXTSEQ (iGrecseq))
+ {
+ if (! fgsend_control (qdaemon, RJ, iGrecseq))
+ return FALSE;
+ cGexpect_bad_order += iGrequest_winsize - 1;
+ }
+ }
+
+ /* We can't skip the packet data after this, because if we
+ have lost incoming bytes the next DLE will be somewhere
+ in what we thought was the packet data. */
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ continue;
+ }
+
+ /* We have a packet; remove the processed bytes from the receive
+ buffer. */
+ iPrecstart = (iPrecstart + cwant + CFRAMELEN) % CRECBUFLEN;
+
+ /* Store the control byte for the handshake routines. */
+ iGpacket_control = ab[IFRAME_CONTROL] & 0xff;
+
+ /* Annoyingly, some UUCP packages appear to send an RR packet
+ rather than an RJ packet when they want a packet to be
+ resent. If we get a duplicate RR, we treat it as an RJ. */
+ fduprr = FALSE;
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) == CONTROL
+ && CONTROL_XXX (ab[IFRAME_CONTROL]) == RR
+ && iGremote_ack == CONTROL_YYY (ab[IFRAME_CONTROL])
+ && INEXTSEQ (iGremote_ack) != iGsendseq)
+ {
+ DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Treating duplicate RR as RJ");
+ fduprr = TRUE;
+ }
+
+ /* Update the received sequence number from the yyy field of a
+ data packet or an RR control packet. If we've been delaying
+ sending packets until we received an ack, this may send out
+ some packets. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL
+ || CONTROL_XXX (ab[IFRAME_CONTROL]) == RR)
+ {
+ if (! fggot_ack (qdaemon, CONTROL_YYY (ab[IFRAME_CONTROL])))
+ return FALSE;
+ }
+
+ /* If this isn't a control message, make sure we have received
+ the expected packet sequence number, acknowledge the packet
+ if it's the right one, and process the data. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) != CONTROL)
+ {
+ if (CONTROL_XXX (ab[IFRAME_CONTROL]) != INEXTSEQ (iGrecseq))
+ {
+ /* We got the wrong packet number. */
+ DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Got packet %d; expected %d",
+ CONTROL_XXX (ab[IFRAME_CONTROL]),
+ INEXTSEQ (iGrecseq));
+
+ if (cGexpect_bad_order > 0)
+ --cGexpect_bad_order;
+ else
+ {
+ ++cGbad_order;
+ ++cGerror_level;
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+ }
+
+ /* This code used to send an RR to encourage the other
+ side to get back in synch, but that may confuse some
+ Telebit modems and does little good in any case,
+ since the other side will probably just ignore it
+ anyhow (that's what this code does). */
+ continue;
+ }
+
+ /* We got the packet we expected. */
+ ++cGrec_packets;
+ if (cGerror_level > 0
+ && cGrec_packets % cGerror_decay == 0)
+ --cGerror_level;
+ cGexpect_bad_order = 0;
+
+ iGrecseq = INEXTSEQ (iGrecseq);
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fgprocess_data: Got packet %d", iGrecseq);
+
+ /* Tell the caller that we found something. */
+ if (pffound != NULL)
+ *pffound = TRUE;
+
+ /* If we are supposed to do acknowledgements here, send back
+ an RR packet. */
+ if (fdoacks)
+ {
+ if (! fgsend_acks (qdaemon))
+ return FALSE;
+ }
+
+ /* If this is a short data packet, adjust the data pointers
+ and lengths. */
+ if (CONTROL_TT (ab[IFRAME_CONTROL]) == SHORTDATA)
+ {
+ int cshort, cmove;
+
+ if ((zfirst[0] & 0x80) == 0)
+ {
+ cshort = zfirst[0] & 0xff;
+ cmove = 1;
+ }
+ else
+ {
+ int cbyte2;
+
+ if (cfirst > 1)
+ cbyte2 = zfirst[1] & 0xff;
+ else
+ cbyte2 = zsecond[0] & 0xff;
+ cshort = (zfirst[0] & 0x7f) + (cbyte2 << 7);
+ cmove = 2;
+ }
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fgprocess_data: Packet short by %d",
+ cshort);
+
+ /* Adjust the start of the buffer for the bytes used
+ by the count. */
+ if (cfirst > cmove)
+ {
+ zfirst += cmove;
+ cfirst -= cmove;
+ }
+ else
+ {
+ zfirst = zsecond + (cmove - cfirst);
+ cfirst = csecond - (cmove - cfirst);
+ csecond = 0;
+ }
+
+ /* Adjust the length of the buffer for the bytes we are
+ not supposed to consider. */
+ cshort -= cmove;
+ if (csecond >= cshort)
+ csecond -= cshort;
+ else
+ {
+ cfirst -= cshort - csecond;
+ csecond = 0;
+ }
+
+#if DEBUG > 0
+ /* This should not happen, but just in case. */
+ if (cfirst < 0)
+ cfirst = 0;
+#endif
+ }
+
+ if (! fgot_data (qdaemon, zfirst, (size_t) cfirst,
+ zsecond, (size_t) csecond,
+ -1, -1, (long) -1,
+ INEXTSEQ (iGremote_ack) == iGsendseq,
+ pfexit))
+ return FALSE;
+
+ /* If fgot_data told us that we were finished, get out. */
+ if (*pfexit)
+ return TRUE;
+
+ /* If we've been asked to return control packets, get out
+ now. */
+ if (freturncontrol)
+ {
+ *pfexit = TRUE;
+ return TRUE;
+ }
+
+ continue;
+ }
+
+ /* Handle control messages here. */
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_PROTO)
+ || (FDEBUGGING (DEBUG_ABNORMAL)
+ && CONTROL_XXX (ab[IFRAME_CONTROL]) != RR))
+ ulog (LOG_DEBUG, "fgprocess_data: Got control %s %d",
+ azGcontrol[CONTROL_XXX (ab[IFRAME_CONTROL])],
+ CONTROL_YYY (ab[IFRAME_CONTROL]));
+#endif
+
+ switch (CONTROL_XXX (ab[IFRAME_CONTROL]))
+ {
+ case CLOSE:
+ /* The other side has closed the connection. */
+ if (fLog_sighup)
+ {
+ ulog (LOG_ERROR, "Received unexpected CLOSE packet");
+ (void) fgsend_control (qdaemon, CLOSE, 0);
+ }
+ return FALSE;
+ case RR:
+ /* Acknowledge receipt of a packet. This was already handled
+ above, unless we are treating it as RJ. */
+ if (! fduprr)
+ break;
+ /* Fall through. */
+ case RJ:
+ /* The other side dropped a packet. Begin retransmission with
+ the packet following the one acknowledged. We don't
+ retransmit the packets immediately, but instead wait
+ for the first one to be acked. This prevents us from
+ sending an entire window several times if we get several
+ RJ packets. */
+ iGremote_ack = CONTROL_YYY (ab[IFRAME_CONTROL]);
+ iGretransmit_seq = INEXTSEQ (iGremote_ack);
+ if (iGretransmit_seq == iGsendseq)
+ iGretransmit_seq = -1;
+ else
+ {
+ char *zpack;
+
+ DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Remote reject: next %d resending %d",
+ iGsendseq, iGretransmit_seq);
+
+ ++cGresent_packets;
+ ++cGremote_rejects;
+ ++cGerror_level;
+ if (! fgcheck_errors (qdaemon))
+ return FALSE;
+ zpack = zgadjust_ack (iGretransmit_seq);
+ if (! fsend_data (qdaemon->qconn, zpack,
+ CFRAMELEN + CPACKLEN (zpack),
+ TRUE))
+ return FALSE;
+ }
+ break;
+ case SRJ:
+ /* Selectively reject a particular packet. This is not used
+ by UUCP, but it's easy to support. */
+ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fgprocess_data: Selective reject of %d",
+ CONTROL_YYY (ab[IFRAME_CONTROL]));
+ {
+ char *zpack;
+
+ ++cGresent_packets;
+ ++cGremote_rejects;
+ ++cGerror_level;
+ zpack = zgadjust_ack (CONTROL_YYY (ab[IFRAME_CONTROL]));
+ if (! fsend_data (qdaemon->qconn, zpack,
+ CFRAMELEN + CPACKLEN (zpack),
+ TRUE))
+ return FALSE;
+ }
+ break;
+ case INITC:
+ case INITB:
+ case INITA:
+ /* Ignore attempts to reinitialize. */
+ break;
+ }
+
+ /* If we've been asked to return control packets, get out. */
+ if (freturncontrol)
+ {
+ *pfexit = TRUE;
+ return TRUE;
+ }
+
+ /* Loop around to look for the next packet, if any. */
+ }
+
+ /* There is no data left in the receive buffer. */
+ if (pcneed != NULL)
+ *pcneed = CFRAMELEN;
+ return TRUE;
+}
+
+/* Compute the 'g' protocol checksum. This is unfortunately rather
+ awkward. This is the most time consuming code in the entire
+ program. It's also not a great checksum, since it can be fooled
+ by some single bit errors. */
+
+/* Sorry about this knavery, but it speeds up the VAX code
+ significantly. It would be better to rewrite the whole routine in
+ assembler. */
+#ifdef __GNUC__
+#ifdef __vax__
+#define VAX_ASM 1
+#endif
+#endif
+
+#if VAX_ASM
+#define ROTATE(i) \
+ asm ("cvtwl %1,%0\n\trotl $1,%0,%0" : "=g" (i) : "g" (i))
+#else
+#define ROTATE(i) i += i + ((i & 0x8000) >> 15)
+#endif
+
+#define ITERATION \
+ /* Rotate ichk1 left. */ \
+ ROTATE (ichk1); \
+ \
+ /* The guts of the checksum. */ \
+ b = BUCHAR (*z++); \
+ if (b != 0) \
+ { \
+ ichk1 &= 0xffff; \
+ ichk1 += b; \
+ ichk2 += ichk1 ^ c; \
+ if ((ichk1 >> 16) != 0) \
+ ichk1 ^= ichk2; \
+ } \
+ else \
+ { \
+ ichk2 += ichk1 ^ c; \
+ ichk1 ^= ichk2; \
+ } \
+ \
+ --c
+
+static int
+igchecksum (z, c)
+ register const char *z;
+ register size_t c;
+{
+ register unsigned long ichk1, ichk2;
+
+ ichk1 = 0xffff;
+ ichk2 = 0;
+
+ do
+ {
+ register unsigned int b;
+
+ ITERATION;
+ ITERATION;
+ ITERATION;
+ ITERATION;
+ }
+ while (c > 0);
+
+ return ichk1 & 0xffff;
+}
+
+/* We use a separate function compute the checksum if the block is
+ split around the end of the receive buffer since it occurs much
+ less frequently and the checksum is already high up in the
+ profiles. These functions are almost identical, and this one
+ actually only has a few more instructions in the inner loop. */
+
+static int
+igchecksum2 (zfirst, cfirst, zsecond, csecond)
+ const char *zfirst;
+ size_t cfirst;
+ const char *zsecond;
+ size_t csecond;
+{
+ register unsigned long ichk1, ichk2;
+ register const char *z;
+ register size_t c;
+
+ z = zfirst;
+ c = cfirst + csecond;
+
+ ichk1 = 0xffff;
+ ichk2 = 0;
+
+ do
+ {
+ register unsigned int b;
+
+ ITERATION;
+
+ /* If the first buffer has been finished, switch to the second. */
+ --cfirst;
+ if (cfirst == 0)
+ z = zsecond;
+ }
+ while (c > 0);
+
+ return ichk1 & 0xffff;
+}
diff --git a/gnu/libexec/uucp/uucico/proti.c b/gnu/libexec/uucp/uucico/proti.c
new file mode 100644
index 0000000000000..d14d42b42db07
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/proti.c
@@ -0,0 +1,1563 @@
+/* proti.c
+ The 'i' protocol.
+
+ Copyright (C) 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char proti_rcsid[] = "$Id: proti.c,v 1.1 1993/08/05 18:27:12 conklin Exp $";
+#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"
+
+/* The 'i' protocol is a simple sliding window protocol, created by
+ me. It is in many ways similar to the 'g' protocol. Several ideas
+ are also taken from the paper ``A High-Throughput Message Transport
+ System'' by P. Lauder. I don't know where the paper was published,
+ but the author's e-mail address is piers@cs.su.oz.au. However, I
+ haven't adopted his main idea, which is to dispense with windows
+ entirely. This is because some links still do require flow control
+ and, more importantly, because I want to have a limit to the amount
+ of data I must be able to resend upon request. To reduce the costs
+ of window acknowledgements, I use a large window and only require
+ an ack at the halfway point.
+
+ Each packet starts with a header containing the following
+ information:
+
+ Intro byte 8 bits byte 1
+ Packet number 5 bits byte 2
+ Local channel 3 bits
+ Packet ack 5 bits byte 3
+ Remote channel 3 bits
+ Packet type 3 bits bytes 4-5
+ Direction 1 bit
+ Data length 12 bits
+ Header check 8 bits byte 6
+
+ If the data length is not 0, this is followed by the data and a 32
+ bit CRC checksum.
+
+ The following packet types are defined:
+
+ SYNC Initialize the connection
+ DATA Data packet
+ ACK Simple acknowledgement with no data
+ NAK Negative acknowledgement; requests resend of single packet
+ SPOS Set file position
+ CLOSE Close the connection
+ */
+
+/* The offsets of the bytes in the packet header. */
+
+#define IHDR_INTRO (0)
+#define IHDR_LOCAL (1)
+#define IHDR_REMOTE (2)
+#define IHDR_CONTENTS1 (3)
+#define IHDR_CONTENTS2 (4)
+#define IHDR_CHECK (5)
+
+/* Macros to set and extract values of IHDR_LOCAL and IHDR_REMOTE. */
+#define IHDRWIN_SET(iseq, ichan) (((iseq) << 3) | (ichan))
+#define IHDRWIN_GETSEQ(ival) (((ival) >> 3) & 0x1f)
+#define IHDRWIN_GETCHAN(ival) ((ival) & 0x07)
+
+/* Macros to set and extract values of IHDR_CONTENTS fields. */
+#define IHDRCON_SET1(ttype, fcaller, cbytes) \
+ (((ttype) << 5) | ((fcaller) ? (1 << 4) : 0) | (((cbytes) >> 8) & 0x0f))
+#define IHDRCON_SET2(ttype, fcaller, cbytes) ((cbytes) & 0xff)
+#define THDRCON_GETTYPE(i1, i2) (((i1) >> 5) & 0x07)
+#define FHDRCON_GETCALLER(i1, i2) (((i1) & (1 << 4)) != 0)
+#define CHDRCON_GETBYTES(i1, i2) ((((i1) & 0x0f) << 8) | ((i2) & 0xff))
+
+/* Macros for the IHDR_CHECK field. */
+#define IHDRCHECK_VAL(zhdr) \
+ ((zhdr[IHDR_LOCAL] \
+ ^ zhdr[IHDR_REMOTE] \
+ ^ zhdr[IHDR_CONTENTS1] \
+ ^ zhdr[IHDR_CONTENTS2]) \
+ & 0xff)
+
+/* Length of the packet header. */
+#define CHDRLEN (6)
+
+/* Amount of space to skip between start of packet and actual data.
+ This is used to make the actual data longword aligned, to encourage
+ good performance when copying data into the buffer. */
+#define CHDRSKIPLEN (CHDRLEN + (sizeof (long) - CHDRLEN % sizeof (long)))
+
+/* Amount of space to skip between memory buffer and header. */
+#define CHDROFFSET (CHDRSKIPLEN - CHDRLEN)
+
+/* Length of the trailing checksum. */
+#define CCKSUMLEN (4)
+
+/* Macros to set and get the checksum. These multiply evaluate their
+ arguments. */
+#define ICKSUM_GET(z) \
+ ((((((((unsigned long) ((z)[0] & 0xff)) << 8) \
+ | (unsigned long) ((z)[1] & 0xff)) << 8) \
+ | (unsigned long) ((z)[2] & 0xff)) << 8) \
+ | (unsigned long) ((z)[3] & 0xff))
+#define UCKSUM_SET(z, i) \
+ (void) ((z)[0] = (((i) >> 24) & 0xff), \
+ (z)[1] = (((i) >> 16) & 0xff), \
+ (z)[2] = (((i) >> 8) & 0xff), \
+ (z)[3] = ((i) & 0xff))
+
+/* The header introduction character. */
+#define IINTRO ('\007')
+
+/* The packet types. */
+
+#define DATA (0)
+#define SYNC (1)
+#define ACK (2)
+#define NAK (3)
+#define SPOS (4)
+#define CLOSE (5)
+
+/* Largest possible packet size (plus 1). */
+#define IMAXPACKSIZE (1 << 12)
+
+/* Largest possible sequence number (plus 1). */
+#define IMAXSEQ 32
+
+/* Get the next sequence number given a sequence number. */
+#define INEXTSEQ(i) ((i + 1) & (IMAXSEQ - 1))
+
+/* Compute i1 - i2 in sequence space (i.e., the number of packets from
+ i2 to i1). */
+#define CSEQDIFF(i1, i2) (((i1) + IMAXSEQ - (i2)) & (IMAXSEQ - 1))
+
+/* Largest possible channel number (plus 1). */
+#define IMAXICHAN (8)
+
+/* Default packet size to request (protocol parameter
+ ``packet-size''). */
+#define IREQUEST_PACKSIZE (1024)
+
+/* Default window size to request (protocol parameter ``window''). */
+#define IREQUEST_WINSIZE (16)
+
+/* Default timeout to use when sending the SYNC packet (protocol
+ parameter ``sync-timeout''). */
+#define CSYNC_TIMEOUT (10)
+
+/* Default number of times to retry sending the SYNC packet (protocol
+ parameter ``sync-retries''). */
+#define CSYNC_RETRIES (6)
+
+/* Default timeout to use when waiting for a packet (protocol
+ parameter ``timeout''). */
+#define CTIMEOUT (10)
+
+/* Default number of times to retry sending a packet before giving up
+ (protocol parameter ``retries''). */
+#define CRETRIES (6)
+
+/* Default maximum level of errors to accept before giving up
+ (protocol parameter ``errors''). */
+#define CERRORS (100)
+
+/* Default decay rate. Each time we receive this many packets
+ succesfully, we decrement the error level by one (protocol
+ parameter ``error-decay''). */
+#define CERROR_DECAY (10)
+
+/* The default list of characters to avoid: XON and XOFF. This string
+ is processed as an escape sequence. This is 'j' protocol parameter
+ ``avoid''; it is defined in this file because the 'i' and 'j'
+ protocols share protocol parameters. */
+#define ZAVOID "\\021\\023"
+
+/* Local variables. */
+
+/* Packet size to request (protocol parameter ``packet-size''). */
+static int iIrequest_packsize = IREQUEST_PACKSIZE;
+
+/* Window size to request (protocol parameter ``window''). */
+static int iIrequest_winsize = IREQUEST_WINSIZE;
+
+/* Remote packet size (set from SYNC packet or from
+ iIforced_remote_packsize). */
+static int iIremote_packsize;
+
+/* Size which buffers were allocated for. */
+static int iIalc_packsize;
+
+/* Forced remote packet size, used if non-zero (protocol parameter
+ ``remote-packet-size''). */
+static int iIforced_remote_packsize = 0;
+
+/* Remote window size (set from SYNC packet or from
+ iIforced_remote_winsize). */
+static int iIremote_winsize;
+
+/* Forced remote window size, used if non-zero (protocol parameter
+ ``remote-window''). */
+static int iIforced_remote_winsize = 0;
+
+/* Timeout to use when sending the SYNC packet (protocol
+ parameter ``sync-timeout''). */
+int cIsync_timeout = CSYNC_TIMEOUT;
+
+/* Number of times to retry sending the SYNC packet (protocol
+ parameter ``sync-retries''). */
+static int cIsync_retries = CSYNC_RETRIES;
+
+/* Timeout to use when waiting for a packet (protocol parameter
+ ``timeout''). */
+static int cItimeout = CTIMEOUT;
+
+/* Number of times to retry sending a packet before giving up
+ (protocol parameter ``retries''). */
+static int cIretries = CRETRIES;
+
+/* Maximum level of errors to accept before giving up (protocol
+ parameter ``errors''). */
+static int cIerrors = CERRORS;
+
+/* Each time we receive this many packets succesfully, we decrement
+ the error level by one (protocol parameter ``error-decay''). */
+static int cIerror_decay = CERROR_DECAY;
+
+/* The set of characters to avoid (protocol parameter ``avoid'').
+ This is actually part of the 'j' protocol; it is defined in this
+ file because the 'i' and 'j' protocols use the same protocol
+ parameters. */
+const char *zJavoid_parameter = ZAVOID;
+
+/* Routine to use when sending data. This is a hook for the 'j'
+ protocol. */
+static boolean (*pfIsend) P((struct sconnection *qconn, const char *zsend,
+ size_t csend, boolean fdoread));
+
+/* Routine to use to use when reading data. This is a hook for the
+ 'j' protocol. */
+static boolean (*pfIreceive) P((struct sconnection *qconn, size_t cneed,
+ size_t *pcrec, int ctimeout,
+ boolean freport));
+
+/* Next sequence number to send. */
+static int iIsendseq;
+
+/* Last sequence number received. */
+static int iIrecseq;
+
+/* Last sequence number we have acknowledged. */
+static int iIlocal_ack;
+
+/* Last sequence number remote system has acknowledged. */
+static int iIremote_ack;
+
+/* File position we are sending from. */
+static long iIsendpos;
+
+/* File position we are receiving to. */
+static long iIrecpos;
+
+/* TRUE if closing the connection. */
+static boolean fIclosing;
+
+/* Array of sent packets indexed by packet number. */
+static char *azIsendbuffers[IMAXSEQ];
+
+/* Array of received packets that we aren't ready to process yet,
+ indexed by packet number. */
+static char *azIrecbuffers[IMAXSEQ];
+
+/* For each packet sequence number, record whether we sent a NAK for
+ the packet. */
+static boolean afInaked[IMAXSEQ];
+
+/* Number of SYNC packets received (used only to detect whether one
+ was received). */
+static int cIsyncs;
+
+/* Number of packets sent. */
+static long cIsent_packets;
+
+/* Number of packets received. */
+static long cIreceived_packets;
+
+/* Number of packets resent. */
+static long cIresent_packets;
+
+/* Number of bad packet headers received. */
+static long cIbad_hdr;
+
+/* Number of out of order packets received. */
+static long cIbad_order;
+
+/* Number of bad checksums received. */
+static long cIbad_cksum;
+
+/* Number of packets rejected by remote system. */
+static long cIremote_rejects;
+
+/* Protocol parameter commands. */
+
+struct uuconf_cmdtab asIproto_params[] =
+{
+ { "packet-size", UUCONF_CMDTABTYPE_INT, (pointer) &iIrequest_packsize,
+ NULL },
+ { "window", UUCONF_CMDTABTYPE_INT, (pointer) &iIrequest_winsize, NULL },
+ { "remote-packet-size", UUCONF_CMDTABTYPE_INT,
+ (pointer) &iIforced_remote_packsize, NULL },
+ { "remote-window", UUCONF_CMDTABTYPE_INT,
+ (pointer) &iIforced_remote_winsize, NULL },
+ { "sync-timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cIsync_timeout,
+ NULL },
+ { "sync-retries", UUCONF_CMDTABTYPE_INT, (pointer) &cIsync_retries,
+ NULL },
+ { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cItimeout, NULL },
+ { "retries", UUCONF_CMDTABTYPE_INT, (pointer) &cIretries, NULL },
+ { "errors", UUCONF_CMDTABTYPE_INT, (pointer) &cIerrors, NULL },
+ { "error-decay", UUCONF_CMDTABTYPE_INT, (pointer) &cIerror_decay, NULL },
+ /* The ``avoid'' protocol parameter is part of the 'j' protocol, but
+ it is convenient for the 'i' and 'j' protocols to share the same
+ protocol parameter table. */
+ { "avoid", UUCONF_CMDTABTYPE_STRING, (pointer) &zJavoid_parameter, NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+/* Local functions. */
+
+static boolean finak P((struct sdaemon *qdaemon, int iseq));
+static boolean firesend P((struct sdaemon *qdaemon));
+static boolean fiwindow_wait P((struct sdaemon *qdaemon));
+static boolean fiwait_for_packet P((struct sdaemon *qdaemon,
+ int ctimeout, int cretries,
+ boolean fone, boolean *ftimedout));
+static boolean ficheck_errors P((struct sdaemon *qdaemon));
+static boolean fiprocess_data P((struct sdaemon *qdaemon,
+ boolean *pfexit, boolean *pffound,
+ size_t *pcneed));
+static boolean fiprocess_packet P((struct sdaemon *qdaemon,
+ const char *zhdr,
+ const char *zfirst, int cfirst,
+ const char *zsecond, int csecond,
+ boolean *pfexit));
+
+/* The 'i' protocol start routine. The work is done in a routine
+ which is also called by the 'j' protocol start routine. */
+
+boolean
+fistart (qdaemon, pzlog)
+ struct sdaemon *qdaemon;
+ char **pzlog;
+{
+ return fijstart (qdaemon, pzlog, IMAXPACKSIZE, fsend_data, freceive_data);
+}
+
+/* Start the protocol. This routine is called by both the 'i' and 'j'
+ protocol start routines. We keep transmitting a SYNC packet
+ containing the window and packet size we would like to receive
+ until we receive a SYNC packet from the remote system. The first
+ two bytes of the data contents of a SYNC packet are the maximum
+ packet size we want to receive (high byte, low byte), and the next
+ byte is the maximum window size we want to use. */
+
+boolean
+fijstart (qdaemon, pzlog, imaxpacksize, pfsend, pfreceive)
+ struct sdaemon *qdaemon;
+ char **pzlog;
+ int imaxpacksize;
+ boolean (*pfsend) P((struct sconnection *qconn, const char *zsend,
+ size_t csend, boolean fdoread));
+ boolean (*pfreceive) P((struct sconnection *qconn, size_t cneed,
+ size_t *pcrec, int ctimeout, boolean freport));
+{
+ char ab[CHDRLEN + 3 + CCKSUMLEN];
+ unsigned long icksum;
+ int ctries;
+ int csyncs;
+
+ *pzlog = NULL;
+
+ pfIsend = pfsend;
+ pfIreceive = pfreceive;
+
+ if (iIforced_remote_packsize <= 0
+ || iIforced_remote_packsize >= imaxpacksize)
+ iIforced_remote_packsize = 0;
+ else
+ iIremote_packsize = iIforced_remote_packsize;
+ iIalc_packsize = 0;
+ if (iIforced_remote_winsize <= 0 || iIforced_remote_winsize >= IMAXSEQ)
+ iIforced_remote_winsize = 0;
+ else
+ iIremote_winsize = iIforced_remote_winsize;
+
+ iIsendseq = 1;
+ iIrecseq = 0;
+ iIlocal_ack = 0;
+ iIremote_ack = 0;
+ iIsendpos = 0;
+ iIrecpos = 0;
+ fIclosing = FALSE;
+
+ cIsent_packets = 0;
+ cIreceived_packets = 0;
+ cIresent_packets = 0;
+ cIbad_hdr = 0;
+ cIbad_order = 0;
+ cIbad_cksum = 0;
+ cIremote_rejects = 0;
+
+ ab[IHDR_INTRO] = IINTRO;
+ ab[IHDR_LOCAL] = ab[IHDR_REMOTE] = IHDRWIN_SET (0, 0);
+ ab[IHDR_CONTENTS1] = IHDRCON_SET1 (SYNC, qdaemon->fcaller, 3);
+ ab[IHDR_CONTENTS2] = IHDRCON_SET2 (SYNC, qdaemon->fcaller, 3);
+ ab[IHDR_CHECK] = IHDRCHECK_VAL (ab);
+ ab[CHDRLEN + 0] = (iIrequest_packsize >> 8) & 0xff;
+ ab[CHDRLEN + 1] = iIrequest_packsize & 0xff;
+ ab[CHDRLEN + 2] = iIrequest_winsize;
+ icksum = icrc (ab + CHDRLEN, 3, ICRCINIT);
+ UCKSUM_SET (ab + CHDRLEN + 3, icksum);
+
+ /* The static cIsyncs is incremented each time a SYNC packet is
+ received. */
+ csyncs = cIsyncs;
+ ctries = 0;
+
+ while (TRUE)
+ {
+ boolean ftimedout;
+
+ DEBUG_MESSAGE2 (DEBUG_PROTO,
+ "fistart: Sending SYNC packsize %d winsize %d",
+ iIrequest_packsize, iIrequest_winsize);
+
+ if (! (*pfIsend) (qdaemon->qconn, ab, CHDRLEN + 3 + CCKSUMLEN,
+ TRUE))
+ return FALSE;
+
+ if (fiwait_for_packet (qdaemon, cIsync_timeout, 0, FALSE,
+ &ftimedout))
+ {
+ if (csyncs != cIsyncs)
+ break;
+ }
+ else
+ {
+ if (! ftimedout)
+ return FALSE;
+
+ ++ctries;
+ if (ctries > cIsync_retries)
+ {
+ ulog (LOG_ERROR, "Protocol startup failed");
+ return FALSE;
+ }
+ }
+ }
+
+ /* We got a SYNC packet; set up packet buffers to use. */
+ if (iIremote_packsize > imaxpacksize)
+ iIremote_packsize = imaxpacksize;
+ do
+ {
+ int iseq;
+
+ for (iseq = 0; iseq < IMAXSEQ; iseq++)
+ {
+ azIrecbuffers[iseq] = NULL;
+ afInaked[iseq] = FALSE;
+ azIsendbuffers[iseq] = (char *) malloc (iIremote_packsize
+ + CHDRSKIPLEN
+ + CCKSUMLEN);
+ if (azIsendbuffers[iseq] == NULL)
+ {
+ int ifree;
+
+ for (ifree = 0; ifree < iseq; ifree++)
+ free ((pointer) azIsendbuffers[ifree]);
+ break;
+ }
+ }
+
+ if (iseq >= IMAXSEQ)
+ {
+ *pzlog = zbufalc (sizeof "protocol 'i' packet size %d window %d"
+ + 50);
+ sprintf (*pzlog, "protocol '%c' packet size %d window %d",
+ qdaemon->qproto->bname, iIremote_packsize,
+ iIremote_winsize);
+ iIalc_packsize = iIremote_packsize;
+
+ return TRUE;
+ }
+
+ iIremote_packsize >>= 1;
+ }
+ while (iIremote_packsize > 200);
+
+ ulog (LOG_ERROR,
+ "'%c' protocol startup failed; insufficient memory for packets",
+ qdaemon->qproto->bname);
+
+ return FALSE;
+}
+
+/* Shut down the protocol. We can be fairly informal about this,
+ since we know that the upper level protocol has already exchanged
+ hangup messages. If we didn't know that, we would have to make
+ sure that all packets before the CLOSE had been received. */
+
+boolean
+fishutdown (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ char *z;
+ size_t clen;
+
+ fIclosing = TRUE;
+
+ z = zigetspace (qdaemon, &clen) - CHDRLEN;
+
+ z[IHDR_INTRO] = IINTRO;
+ z[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, 0);
+ z[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0);
+ iIlocal_ack = iIrecseq;
+ z[IHDR_CONTENTS1] = IHDRCON_SET1 (CLOSE, qdaemon->fcaller, 0);
+ z[IHDR_CONTENTS2] = IHDRCON_SET2 (CLOSE, qdaemon->fcaller, 0);
+ z[IHDR_CHECK] = IHDRCHECK_VAL (z);
+
+ DEBUG_MESSAGE0 (DEBUG_PROTO, "fishutdown: Sending CLOSE");
+
+ if (! (*pfIsend) (qdaemon->qconn, z, CHDRLEN, FALSE))
+ return FALSE;
+
+ ulog (LOG_NORMAL,
+ "Protocol '%c' packets: sent %ld, resent %ld, received %ld",
+ qdaemon->qproto->bname, cIsent_packets, cIresent_packets,
+ cIreceived_packets);
+ if (cIbad_hdr != 0
+ || cIbad_cksum != 0
+ || cIbad_order != 0
+ || cIremote_rejects != 0)
+ ulog (LOG_NORMAL,
+ "Errors: header %ld, checksum %ld, order %ld, remote rejects %ld",
+ cIbad_hdr, cIbad_cksum, cIbad_order, cIremote_rejects);
+
+ /* Reset the protocol parameters to their default values. */
+ iIrequest_packsize = IREQUEST_PACKSIZE;
+ iIrequest_winsize = IREQUEST_WINSIZE;
+ iIforced_remote_packsize = 0;
+ iIforced_remote_winsize = 0;
+ cIsync_timeout = CSYNC_TIMEOUT;
+ cIsync_retries = CSYNC_RETRIES;
+ cItimeout = CTIMEOUT;
+ cIretries = CRETRIES;
+ cIerrors = CERRORS;
+ cIerror_decay = CERROR_DECAY;
+ zJavoid_parameter = ZAVOID;
+
+ return TRUE;
+}
+
+/* Send a command string. These are just sent as normal packets,
+ ending in a packet containing a null byte. */
+
+boolean
+fisendcmd (qdaemon, z, ilocal, iremote)
+ struct sdaemon *qdaemon;
+ const char *z;
+ int ilocal;
+ int iremote;
+{
+ size_t clen;
+
+ DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fisendcmd: Sending command \"%s\"", z);
+
+ clen = strlen (z);
+
+ while (TRUE)
+ {
+ char *zpacket;
+ size_t csize;
+
+ zpacket = zigetspace (qdaemon, &csize);
+
+ if (clen < csize)
+ {
+ memcpy (zpacket, z, clen + 1);
+ return fisenddata (qdaemon, zpacket, clen + 1, ilocal, iremote,
+ (long) -1);
+ }
+
+ memcpy (zpacket, z, csize);
+ z += csize;
+ clen -= csize;
+
+ if (! fisenddata (qdaemon, zpacket, csize, ilocal, iremote, (long) -1))
+ return FALSE;
+ }
+ /*NOTREACHED*/
+}
+
+/* Send a NAK. */
+
+static boolean
+finak (qdaemon, iseq)
+ struct sdaemon *qdaemon;
+ int iseq;
+{
+ char abnak[CHDRLEN];
+
+ abnak[IHDR_INTRO] = IINTRO;
+ abnak[IHDR_LOCAL] = IHDRWIN_SET (iseq, 0);
+ abnak[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0);
+ iIlocal_ack = iIrecseq;
+ abnak[IHDR_CONTENTS1] = IHDRCON_SET1 (NAK, qdaemon->fcaller, 0);
+ abnak[IHDR_CONTENTS2] = IHDRCON_SET2 (NAK, qdaemon->fcaller, 0);
+ abnak[IHDR_CHECK] = IHDRCHECK_VAL (abnak);
+
+ afInaked[iseq] = TRUE;
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "finak: Sending NAK %d", iseq);
+
+ return (*pfIsend) (qdaemon->qconn, abnak, CHDRLEN, TRUE);
+}
+
+/* Resend the latest packet the remote has not acknowledged. */
+
+static boolean
+firesend (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ int iseq;
+ char *zhdr;
+ size_t clen;
+
+ iseq = INEXTSEQ (iIremote_ack);
+ if (iseq == iIsendseq)
+ {
+ /* Everything has been ACKed and there is nothing to resend. */
+ return TRUE;
+ }
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "firesend: Resending packet %d", iseq);
+
+ /* Update the received sequence number. */
+ zhdr = azIsendbuffers[iseq] + CHDROFFSET;
+ if (IHDRWIN_GETSEQ (zhdr[IHDR_REMOTE]) != iIrecseq)
+ {
+ int iremote;
+
+ iremote = IHDRWIN_GETCHAN (zhdr[IHDR_REMOTE]);
+ zhdr[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote);
+ zhdr[IHDR_CHECK] = IHDRCHECK_VAL (zhdr);
+ iIlocal_ack = iIrecseq;
+ }
+
+ ++cIresent_packets;
+
+ clen = CHDRCON_GETBYTES (zhdr[IHDR_CONTENTS1],
+ zhdr[IHDR_CONTENTS2]);
+
+ return (*pfIsend) (qdaemon->qconn, zhdr,
+ CHDRLEN + clen + (clen > 0 ? CCKSUMLEN : 0),
+ TRUE);
+}
+
+/* Wait until there is an opening in the receive window of the remote
+ system. */
+
+static boolean
+fiwindow_wait (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ /* iIsendseq is the sequence number we are sending, and iIremote_ack
+ is the last sequence number acknowledged by the remote. */
+ while (CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize)
+ {
+ /* If a NAK is lost, it is possible for the other side to be
+ sending a stream of packets while we are waiting for an ACK.
+ This should be caught in fiprocess_data; if it is about to
+ send an ACK, but it has an unacknowledged packet to send, it
+ sends the entire packet. Hopefully that will trigger an ACK
+ or a NAK and get us going again. */
+ DEBUG_MESSAGE0 (DEBUG_PROTO, "fiwindow_wait: Waiting for ACK");
+ if (! fiwait_for_packet (qdaemon, cItimeout, cIretries,
+ TRUE, (boolean *) NULL))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Get buffer space to use for packet data. We return a pointer to
+ the space to be used for the actual data, leaving room for the
+ header. */
+
+/*ARGSUSED*/
+char *
+zigetspace (qdaemon, pclen)
+ struct sdaemon *qdaemon;
+ size_t *pclen;
+{
+ *pclen = iIremote_packsize;
+ return azIsendbuffers[iIsendseq] + CHDRSKIPLEN;
+}
+
+/* Send a data packet. The zdata argument will always point to value
+ returned by zigetspace, so we know that we have room before it for
+ the header information. */
+
+boolean
+fisenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
+ struct sdaemon *qdaemon;
+ char *zdata;
+ size_t cdata;
+ int ilocal;
+ int iremote;
+ long ipos;
+{
+ char *zhdr;
+ unsigned long icksum;
+ boolean fret;
+
+#if DEBUG > 0
+ if (ilocal < 0 || ilocal >= IMAXICHAN
+ || iremote < 0 || iremote >= IMAXICHAN)
+ ulog (LOG_FATAL, "fisenddata: ilocal %d iremote %d", ilocal, iremote);
+#endif
+
+ /* If we are changing the file position, we must send an SPOS
+ packet. */
+ if (ipos != iIsendpos && ipos != (long) -1)
+ {
+ int inext;
+ char *zspos;
+
+ /* We need to get a buffer to hold the SPOS packet, and it needs
+ to be next sequence number. However, the data we have been
+ given is currently in the next sequence number buffer. So we
+ shuffle the buffers around. */
+ inext = INEXTSEQ (iIsendseq);
+ zspos = azIsendbuffers[inext];
+ azIsendbuffers[inext] = zdata - CHDRSKIPLEN;
+ azIsendbuffers[iIsendseq] = zspos;
+ zspos += CHDROFFSET;
+
+ zspos[IHDR_INTRO] = IINTRO;
+ zspos[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, 0);
+ zspos[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0);
+ iIlocal_ack = iIrecseq;
+ zspos[IHDR_CONTENTS1] = IHDRCON_SET1 (SPOS, qdaemon->fcaller,
+ CCKSUMLEN);
+ zspos[IHDR_CONTENTS2] = IHDRCON_SET2 (SPOS, qdaemon->fcaller,
+ CCKSUMLEN);
+ zspos[IHDR_CHECK] = IHDRCHECK_VAL (zspos);
+ UCKSUM_SET (zspos + CHDRLEN, (unsigned long) ipos);
+ icksum = icrc (zspos + CHDRLEN, CCKSUMLEN, ICRCINIT);
+ UCKSUM_SET (zspos + CHDRLEN + CCKSUMLEN, icksum);
+
+ /* Wait for an opening in the window. */
+ if (iIremote_winsize > 0
+ && CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize)
+ {
+ if (! fiwindow_wait (qdaemon))
+ return FALSE;
+ }
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO, "fisenddata: Sending SPOS %ld",
+ ipos);
+
+ if (! (*pfIsend) (qdaemon->qconn, zspos,
+ CHDRLEN + CCKSUMLEN + CCKSUMLEN, TRUE))
+ return FALSE;
+
+ iIsendseq = INEXTSEQ (iIsendseq);
+ iIsendpos = ipos;
+ }
+
+ zhdr = zdata - CHDRLEN;
+ zhdr[IHDR_INTRO] = IINTRO;
+ zhdr[IHDR_LOCAL] = IHDRWIN_SET (iIsendseq, ilocal);
+ zhdr[IHDR_CONTENTS1] = IHDRCON_SET1 (DATA, qdaemon->fcaller, cdata);
+ zhdr[IHDR_CONTENTS2] = IHDRCON_SET2 (DATA, qdaemon->fcaller, cdata);
+
+ /* Compute and set the checksum. */
+ if (cdata > 0)
+ {
+ icksum = icrc (zdata, cdata, ICRCINIT);
+ UCKSUM_SET (zdata + cdata, icksum);
+ }
+
+ /* Wait until there is an opening in the window (we hope to not have
+ to wait here at all, actually; ideally the window should be large
+ enough to avoid a wait). */
+ if (iIremote_winsize > 0
+ && CSEQDIFF (iIsendseq, iIremote_ack) > iIremote_winsize)
+ {
+ if (! fiwindow_wait (qdaemon))
+ return FALSE;
+ }
+
+ /* We only fill in IHDR_REMOTE now, since only now do know the
+ correct value of iIrecseq. */
+ zhdr[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote);
+ iIlocal_ack = iIrecseq;
+ zhdr[IHDR_CHECK] = IHDRCHECK_VAL (zhdr);
+
+ DEBUG_MESSAGE2 (DEBUG_PROTO, "fisenddata: Sending packet %d (%d bytes)",
+ iIsendseq, (int) cdata);
+
+ iIsendseq = INEXTSEQ (iIsendseq);
+ ++cIsent_packets;
+
+ fret = (*pfIsend) (qdaemon->qconn, zhdr,
+ cdata + CHDRLEN + (cdata > 0 ? CCKSUMLEN : 0),
+ TRUE);
+
+ iIsendpos += cdata;
+
+ if (fret && iPrecstart != iPrecend)
+ {
+ boolean fexit;
+
+ fret = fiprocess_data (qdaemon, &fexit, (boolean *) NULL,
+ (size_t *) NULL);
+ }
+
+ return fret;
+}
+
+/* Wait for data to come in. */
+
+boolean
+fiwait (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ return fiwait_for_packet (qdaemon, cItimeout, cIretries,
+ FALSE, (boolean *) NULL);
+}
+
+/* Wait for a packet. Either there is no data to send, or the remote
+ window is full. */
+
+static boolean
+fiwait_for_packet (qdaemon, ctimeout, cretries, fone, pftimedout)
+ struct sdaemon *qdaemon;
+ int ctimeout;
+ int cretries;
+ boolean fone;
+ boolean *pftimedout;
+{
+ int cshort;
+ int ctimeouts;
+
+ if (pftimedout != NULL)
+ *pftimedout = FALSE;
+
+ cshort = 0;
+ ctimeouts = 0;
+
+ while (TRUE)
+ {
+ boolean fexit, ffound;
+ size_t cneed;
+ size_t crec;
+
+ if (! fiprocess_data (qdaemon, &fexit, &ffound, &cneed))
+ return FALSE;
+
+ if (fexit || (fone && ffound))
+ return TRUE;
+
+ if (cneed == 0)
+ continue;
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO, "fiwait_for_packet: Need %d bytes",
+ (int) cneed);
+
+ if (! (*pfIreceive) (qdaemon->qconn, cneed, &crec, ctimeout, TRUE))
+ return FALSE;
+
+ if (crec != 0)
+ {
+ /* If we didn't get enough data twice in a row, we may have
+ dropped some data and be waiting for the end of a large
+ packet. Incrementing iPrecstart will force
+ fiprocess_data to skip the current packet and try to find
+ the next one. */
+ if (crec >= cneed)
+ cshort = 0;
+ else
+ {
+ ++cshort;
+ if (cshort > 1)
+ {
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ cshort = 0;
+ }
+ }
+ }
+ else
+ {
+ int i;
+
+ /* We timed out on the read. */
+ ++ctimeouts;
+ if (ctimeouts > cretries)
+ {
+ if (cretries > 0)
+ ulog (LOG_ERROR, "Timed out waiting for packet");
+ if (pftimedout != NULL)
+ *pftimedout = TRUE;
+ return FALSE;
+ }
+
+ /* Clear out the list of packets we have sent NAKs for. We
+ should have seen some sort of response by now. */
+ for (i = 0; i < IMAXSEQ; i++)
+ afInaked[i] = FALSE;
+
+ /* Send a NAK for the packet we want, and, if we have an
+ unacknowledged packet, send it again. */
+ if (! finak (qdaemon, INEXTSEQ (iIrecseq))
+ || ! firesend (qdaemon))
+ return FALSE;
+ }
+ }
+ /*NOTREACHED*/
+}
+
+/* Make sure we haven't overflowed the permissible error level. */
+
+static boolean
+ficheck_errors (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ if (cIerrors < 0)
+ return TRUE;
+
+ if (((cIbad_order + cIbad_hdr + cIbad_cksum + cIremote_rejects)
+ - (cIreceived_packets / cIerror_decay))
+ > cIerrors)
+ {
+ /* Try shrinking the packet size. */
+ if (iIrequest_packsize > 400)
+ {
+ char absync[CHDRLEN + 3 + CCKSUMLEN];
+ unsigned long icksum;
+
+ iIrequest_packsize /= 2;
+ absync[IHDR_INTRO] = IINTRO;
+ absync[IHDR_LOCAL] = IHDRWIN_SET (0, 0);
+ absync[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0);
+ iIlocal_ack = iIrecseq;
+ absync[IHDR_CONTENTS1] = IHDRCON_SET1 (SYNC, qdaemon->fcaller, 3);
+ absync[IHDR_CONTENTS2] = IHDRCON_SET2 (SYNC, qdaemon->fcaller, 3);
+ absync[IHDR_CHECK] = IHDRCHECK_VAL (absync);
+ absync[CHDRLEN + 0] = (iIrequest_packsize >> 8) & 0xff;
+ absync[CHDRLEN + 1] = iIrequest_packsize & 0xff;
+ absync[CHDRLEN + 2] = iIrequest_winsize;
+ icksum = icrc (absync + CHDRLEN, 3, ICRCINIT);
+ UCKSUM_SET (absync + CHDRLEN + 3, icksum);
+
+ cIerrors *= 2;
+
+ DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "ficheck_errors: Sending SYNC packsize %d winsize %d",
+ iIrequest_packsize, iIrequest_winsize);
+
+ return (*pfIsend) (qdaemon->qconn, absync,
+ CHDRLEN + 3 + CCKSUMLEN, TRUE);
+ }
+
+ ulog (LOG_ERROR, "Too many '%c' protocol errors",
+ qdaemon->qproto->bname);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Process data waiting in the receive buffer, passing to the
+ fgot_data function. */
+
+static boolean
+fiprocess_data (qdaemon, pfexit, pffound, pcneed)
+ struct sdaemon *qdaemon;
+ boolean *pfexit;
+ boolean *pffound;
+ size_t *pcneed;
+{
+ boolean fbadhdr;
+
+ if (pfexit != NULL)
+ *pfexit = FALSE;
+ if (pffound != NULL)
+ *pffound = FALSE;
+
+ fbadhdr = FALSE;
+
+ while (iPrecstart != iPrecend)
+ {
+ char ab[CHDRLEN];
+ int cfirst, csecond;
+ char *zfirst, *zsecond;
+ int i;
+ int iget;
+ int ttype;
+ int iseq;
+ int csize;
+ int iack;
+
+ /* If we're closing the connection, ignore any data remaining in
+ the input buffer. */
+ if (fIclosing)
+ {
+ if (pfexit != NULL)
+ *pfexit = TRUE;
+ if (pcneed != NULL)
+ *pcneed = 0;
+ return TRUE;
+ }
+
+ /* Look for the IINTRO character. */
+ if (abPrecbuf[iPrecstart] != IINTRO)
+ {
+ char *zintro;
+ int cintro;
+
+ cintro = iPrecend - iPrecstart;
+ if (cintro < 0)
+ cintro = CRECBUFLEN - iPrecstart;
+
+ zintro = memchr (abPrecbuf + iPrecstart, IINTRO, (size_t) cintro);
+
+ if (zintro == NULL)
+ {
+ iPrecstart = (iPrecstart + cintro) % CRECBUFLEN;
+ continue;
+ }
+
+ /* We don't need % CRECBUFLEN here because zintro - (abPrecbuf
+ + iPrecstart) < cintro <= CRECBUFLEN - iPrecstart. */
+ iPrecstart += zintro - (abPrecbuf + iPrecstart);
+ }
+
+ /* Get the header into ab. */
+ for (i = 0, iget = iPrecstart;
+ i < CHDRLEN && iget != iPrecend;
+ i++, iget = (iget + 1) % CRECBUFLEN)
+ ab[i] = abPrecbuf[iget];
+
+ if (i < CHDRLEN)
+ {
+ if (pcneed != NULL)
+ *pcneed = CHDRLEN - i;
+ return TRUE;
+ }
+
+ if ((ab[IHDR_CHECK] & 0xff) != IHDRCHECK_VAL (ab)
+ || (FHDRCON_GETCALLER (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2])
+ ? qdaemon->fcaller : ! qdaemon->fcaller))
+ {
+ /* We only report a single bad header message per call, to
+ avoid generating many errors if we get many INTRO bytes
+ in a row. */
+ if (! fbadhdr)
+ {
+ DEBUG_MESSAGE0 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fiprocess_data: Bad header");
+
+ ++cIbad_hdr;
+ if (! ficheck_errors (qdaemon))
+ return FALSE;
+
+ fbadhdr = TRUE;
+ }
+
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ continue;
+ }
+
+ zfirst = zsecond = NULL;
+ cfirst = csecond = 0;
+
+ ttype = THDRCON_GETTYPE (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2]);
+ if (ttype == DATA || ttype == SPOS || ttype == CLOSE)
+ iseq = IHDRWIN_GETSEQ (ab[IHDR_LOCAL]);
+ else
+ iseq = -1;
+ csize = CHDRCON_GETBYTES (ab[IHDR_CONTENTS1], ab[IHDR_CONTENTS2]);
+
+ if (iseq != -1)
+ {
+ /* Make sure this packet is in our receive window. The last
+ packet we have acked is iIlocal_ack. */
+ if (iIrequest_winsize > 0
+ && CSEQDIFF (iseq, iIlocal_ack) > iIrequest_winsize)
+ {
+ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fiprocess_data: Out of order packet %d",
+ iseq);
+
+ ++cIbad_order;
+ if (! ficheck_errors (qdaemon))
+ return FALSE;
+
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+
+ continue;
+ }
+ }
+
+ if (csize > 0)
+ {
+ int cinbuf;
+ char abcksum[CCKSUMLEN];
+ unsigned long ickdata;
+
+ cinbuf = iPrecend - iPrecstart;
+ if (cinbuf < 0)
+ cinbuf += CRECBUFLEN;
+ if (cinbuf < CHDRLEN + csize + CCKSUMLEN)
+ {
+ if (pcneed != NULL)
+ *pcneed = CHDRLEN + csize + CCKSUMLEN - cinbuf;
+ return TRUE;
+ }
+
+ if (iPrecend > iPrecstart)
+ {
+ cfirst = csize;
+ zfirst = abPrecbuf + iPrecstart + CHDRLEN;
+ }
+ else
+ {
+ cfirst = CRECBUFLEN - (iPrecstart + CHDRLEN);
+ if (cfirst <= 0)
+ {
+ /* Here cfirst is non-positive, so subtracting from
+ abPrecbuf will actually skip the appropriate number
+ of bytes at the start of abPrecbuf. */
+ zfirst = abPrecbuf - cfirst;
+ cfirst = csize;
+ }
+ else
+ {
+ if (cfirst >= csize)
+ cfirst = csize;
+ else
+ {
+ zsecond = abPrecbuf;
+ csecond = csize - cfirst;
+ }
+ zfirst = abPrecbuf + iPrecstart + CHDRLEN;
+ }
+ }
+
+ /* Get the checksum into abcksum. */
+ for (i = 0, iget = (iPrecstart + CHDRLEN + csize) % CRECBUFLEN;
+ i < CCKSUMLEN;
+ i++, iget = (iget + 1) % CRECBUFLEN)
+ abcksum[i] = abPrecbuf[iget];
+
+ ickdata = icrc (zfirst, (size_t) cfirst, ICRCINIT);
+ if (csecond > 0)
+ ickdata = icrc (zsecond, (size_t) csecond, ickdata);
+
+ if (ICKSUM_GET (abcksum) != ickdata)
+ {
+ DEBUG_MESSAGE2 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fiprocess_data: Bad checksum; data %lu, frame %lu",
+ ickdata, ICKSUM_GET (abcksum));
+
+ ++cIbad_cksum;
+ if (! ficheck_errors (qdaemon))
+ return FALSE;
+
+ /* If this sequence number is in our receive window,
+ send a NAK. iIrecseq is the last sequence number we
+ have succesfully received. */
+ if (iseq != -1
+ && iseq != iIrecseq
+ && (iIrequest_winsize <= 0
+ || CSEQDIFF (iseq, iIrecseq) <= iIrequest_winsize)
+ && azIrecbuffers[iseq] == NULL)
+ {
+ if (! finak (qdaemon, iseq))
+ return FALSE;
+ }
+
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
+ continue;
+ }
+ }
+
+ /* Here we know that this is a valid packet, so we can adjust
+ iPrecstart accordingly. */
+ if (csize == 0)
+ iPrecstart = (iPrecstart + CHDRLEN) % CRECBUFLEN;
+ else
+ {
+ iPrecstart = ((iPrecstart + CHDRLEN + csize + CCKSUMLEN)
+ % CRECBUFLEN);
+ ++cIreceived_packets;
+ }
+
+ /* Get the ack from the packet, if appropriate. iIsendseq is
+ the next sequence number we are going to send, and
+ iIremote_ack is the last sequence number acknowledged by the
+ remote system. */
+ iack = IHDRWIN_GETSEQ (ab[IHDR_REMOTE]);
+ if (iIremote_winsize > 0
+ && iack != iIsendseq
+ && CSEQDIFF (iack, iIremote_ack) <= iIremote_winsize
+ && CSEQDIFF (iIsendseq, iack) <= iIremote_winsize)
+ {
+ /* Call uwindow_acked each time packet 0 is acked. */
+ if (iack < iIremote_ack)
+ uwindow_acked (qdaemon, FALSE);
+ iIremote_ack = iack;
+ }
+
+ if (iseq != -1)
+ {
+ afInaked[iseq] = FALSE;
+
+ /* If we haven't handled all previous packets, we must save
+ off this packet and deal with it later. */
+ if (iseq != INEXTSEQ (iIrecseq))
+ {
+ if (iseq == iIrecseq
+ || (iIrequest_winsize > 0
+ && CSEQDIFF (iseq, iIrecseq) > iIrequest_winsize))
+ {
+ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fiprocess_data: Ignoring out of order packet %d",
+ iseq);
+ continue;
+ }
+ else
+ {
+ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fiprocess_data: Saving unexpected packet %d",
+ iseq);
+
+ if (azIrecbuffers[iseq] == NULL)
+ {
+ azIrecbuffers[iseq] = zbufalc ((size_t) (CHDRLEN
+ + csize));
+ memcpy (azIrecbuffers[iseq], ab, CHDRLEN);
+ if (csize > 0)
+ {
+ memcpy (azIrecbuffers[iseq] + CHDRLEN, zfirst,
+ (size_t) cfirst);
+ if (csecond > 0)
+ memcpy (azIrecbuffers[iseq] + CHDRLEN + cfirst,
+ zsecond, (size_t) csecond);
+ }
+ }
+ }
+
+ /* Send NAK's for each packet between the last one we
+ received and this one, avoiding any packets for which
+ we've already sent NAK's or which we've already
+ received. */
+ for (i = INEXTSEQ (iIrecseq);
+ i != iseq;
+ i = INEXTSEQ (i))
+ {
+ if (! afInaked[i]
+ && azIrecbuffers[i] == NULL)
+ {
+ if (! finak (qdaemon, i))
+ return FALSE;
+ }
+ }
+
+ continue;
+ }
+
+ iIrecseq = iseq;
+ }
+
+ if (pffound != NULL)
+ *pffound = TRUE;
+
+ if (! fiprocess_packet (qdaemon, ab, zfirst, cfirst, zsecond, csecond,
+ pfexit))
+ return FALSE;
+
+ if (iseq != -1)
+ {
+ int inext;
+
+ /* If we've already received the next packet(s), process
+ them. */
+ inext = INEXTSEQ (iIrecseq);
+ while (azIrecbuffers[inext] != NULL)
+ {
+ char *z;
+ int c;
+
+ z = azIrecbuffers[inext];
+ c = CHDRCON_GETBYTES (z[IHDR_CONTENTS1], z[IHDR_CONTENTS2]);
+ iIrecseq = inext;
+ if (! fiprocess_packet (qdaemon, z, z + CHDRLEN, c,
+ (char *) NULL, 0, pfexit))
+ return FALSE;
+ ubuffree (azIrecbuffers[inext]);
+ azIrecbuffers[inext] = NULL;
+ inext = INEXTSEQ (inext);
+ }
+ }
+
+ /* If we have received half of our window size or more since the
+ last ACK, send one now. Sending an ACK for half the window
+ at a time should significantly cut the acknowledgement
+ traffic when only one side is sending. We should normally
+ not have to send an ACK if we have data to send, since each
+ packet sent will ACK the most recently received packet.
+ However, it can happen if we receive a burst of short
+ packets, such as a set of command acknowledgements. */
+ if (iIrequest_winsize > 0
+ && CSEQDIFF (iIrecseq, iIlocal_ack) >= iIrequest_winsize / 2)
+ {
+ char aback[CHDRLEN];
+
+ aback[IHDR_INTRO] = IINTRO;
+ aback[IHDR_LOCAL] = IHDRWIN_SET (0, 0);
+ aback[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, 0);
+ iIlocal_ack = iIrecseq;
+ aback[IHDR_CONTENTS1] = IHDRCON_SET1 (ACK, qdaemon->fcaller, 0);
+ aback[IHDR_CONTENTS2] = IHDRCON_SET2 (ACK, qdaemon->fcaller, 0);
+ aback[IHDR_CHECK] = IHDRCHECK_VAL (aback);
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_data: Sending ACK %d",
+ iIrecseq);
+
+ if (! (*pfIsend) (qdaemon->qconn, aback, CHDRLEN, TRUE))
+ return FALSE;
+ }
+ }
+
+ if (pcneed != NULL)
+ *pcneed = CHDRLEN;
+
+ return TRUE;
+}
+
+/* Process a single packet. */
+
+static boolean
+fiprocess_packet (qdaemon, zhdr, zfirst, cfirst, zsecond, csecond, pfexit)
+ struct sdaemon *qdaemon;
+ const char *zhdr;
+ const char *zfirst;
+ int cfirst;
+ const char *zsecond;
+ int csecond;
+ boolean *pfexit;
+{
+ int ttype;
+
+ ttype = THDRCON_GETTYPE (zhdr[IHDR_CONTENTS1], zhdr[IHDR_CONTENTS2]);
+ switch (ttype)
+ {
+ case DATA:
+ {
+ int iseq;
+ boolean fret;
+
+ iseq = IHDRWIN_GETSEQ (zhdr[IHDR_LOCAL]);
+ DEBUG_MESSAGE2 (DEBUG_PROTO,
+ "fiprocess_packet: Got DATA packet %d size %d",
+ iseq, cfirst + csecond);
+ fret = fgot_data (qdaemon, zfirst, (size_t) cfirst,
+ zsecond, (size_t) csecond,
+ IHDRWIN_GETCHAN (zhdr[IHDR_REMOTE]),
+ IHDRWIN_GETCHAN (zhdr[IHDR_LOCAL]),
+ iIrecpos,
+ INEXTSEQ (iIremote_ack) == iIsendseq,
+ pfexit);
+ iIrecpos += cfirst + csecond;
+ return fret;
+ }
+
+ case SYNC:
+ {
+ int ipack, iwin;
+
+ /* We accept a SYNC packet to adjust the packet and window
+ sizes at any time. */
+ if (cfirst + csecond < 3)
+ {
+ ulog (LOG_ERROR, "Bad SYNC packet");
+ return FALSE;
+ }
+ ipack = (zfirst[0] & 0xff) << 8;
+ if (cfirst > 1)
+ ipack |= zfirst[1] & 0xff;
+ else
+ ipack |= zsecond[0];
+ if (cfirst > 2)
+ iwin = zfirst[2];
+ else
+ iwin = zsecond[2 - cfirst];
+
+ DEBUG_MESSAGE2 (DEBUG_PROTO,
+ "fiprocess_packet: Got SYNC packsize %d winsize %d",
+ ipack, iwin);
+
+ if (iIforced_remote_packsize == 0
+ && (iIalc_packsize == 0
+ || ipack <= iIalc_packsize))
+ iIremote_packsize = ipack;
+ if (iIforced_remote_winsize == 0)
+ iIremote_winsize = iwin;
+
+ /* We increment a static variable to tell the initialization
+ code that a SYNC was received, and we set *pfexit to TRUE
+ to get out to the initialization code (this will do no harm
+ if we are called from elsewhere). */
+ ++cIsyncs;
+ *pfexit = TRUE;
+ return TRUE;
+ }
+
+ case ACK:
+ /* There is nothing to do here, since the ack was already
+ handled in fiprocess_data. */
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fiprocess_packet: Got ACK %d",
+ IHDRWIN_GETSEQ (zhdr[IHDR_REMOTE]));
+ return TRUE;
+
+ case NAK:
+ /* We must resend the requested packet. */
+ {
+ int iseq;
+ char *zsend;
+ size_t clen;
+
+ ++cIremote_rejects;
+ if (! ficheck_errors (qdaemon))
+ return FALSE;
+
+ iseq = IHDRWIN_GETSEQ (zhdr[IHDR_LOCAL]);
+
+ /* The timeout code will send a NAK for the packet the remote
+ side wants. So we may see a NAK here for the packet we are
+ about to send. */
+ if (iseq == iIsendseq
+ || (iIremote_winsize > 0
+ && (CSEQDIFF (iseq, iIremote_ack) > iIremote_winsize
+ || CSEQDIFF (iIsendseq, iseq) > iIremote_winsize)))
+ {
+ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fiprocess_packet: Ignoring out of order NAK %d",
+ iseq);
+ return TRUE;
+ }
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO | DEBUG_ABNORMAL,
+ "fiprocess_packet: Got NAK %d; resending packet",
+ iseq);
+
+ /* Update the received sequence number. */
+ zsend = azIsendbuffers[iseq] + CHDROFFSET;
+ if (IHDRWIN_GETSEQ (zsend[IHDR_REMOTE]) != iIrecseq)
+ {
+ int iremote;
+
+ iremote = IHDRWIN_GETCHAN (zsend[IHDR_REMOTE]);
+ zsend[IHDR_REMOTE] = IHDRWIN_SET (iIrecseq, iremote);
+ zsend[IHDR_CHECK] = IHDRCHECK_VAL (zsend);
+ iIlocal_ack = iIrecseq;
+ }
+
+ ++cIresent_packets;
+
+ clen = CHDRCON_GETBYTES (zsend[IHDR_CONTENTS1],
+ zsend[IHDR_CONTENTS2]);
+
+ return (*pfIsend) (qdaemon->qconn, zsend,
+ CHDRLEN + clen + (clen > 0 ? CCKSUMLEN : 0),
+ TRUE);
+ }
+
+ case SPOS:
+ /* Set the file position. */
+ {
+ char abpos[CCKSUMLEN];
+ const char *zpos;
+
+ if (cfirst >= CCKSUMLEN)
+ zpos = zfirst;
+ else
+ {
+ memcpy (abpos, zfirst, (size_t) cfirst);
+ memcpy (abpos + cfirst, zsecond, (size_t) (CCKSUMLEN - cfirst));
+ zpos = abpos;
+ }
+ iIrecpos = (long) ICKSUM_GET (zpos);
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fiprocess_packet: Got SPOS %ld", iIrecpos);
+ return TRUE;
+ }
+
+ case CLOSE:
+ {
+ boolean fexpected;
+
+ fexpected = ! fLog_sighup || fIclosing;
+ if (! fexpected)
+ ulog (LOG_ERROR, "Received unexpected CLOSE packet");
+ else
+ DEBUG_MESSAGE0 (DEBUG_PROTO, "fiprocess_packet: Got CLOSE packet");
+
+ fIclosing = TRUE;
+ *pfexit = TRUE;
+ return fexpected;
+ }
+
+ default:
+ /* Just ignore unrecognized packet types, for future protocol
+ enhancements. */
+ DEBUG_MESSAGE1 (DEBUG_PROTO, "fiprocess_packet: Got packet type %d",
+ ttype);
+ return TRUE;
+ }
+ /*NOTREACHED*/
+}
diff --git a/gnu/libexec/uucp/uucico/protj.c b/gnu/libexec/uucp/uucico/protj.c
new file mode 100644
index 0000000000000..5717bb916dfb9
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/protj.c
@@ -0,0 +1,671 @@
+/* protj.c
+ The 'j' protocol.
+
+ Copyright (C) 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char protj_rcsid[] = "$Id: protj.c,v 1.1 1993/08/05 18:27:14 conklin Exp $";
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+
+#include "uudefs.h"
+#include "conn.h"
+#include "trans.h"
+#include "system.h"
+#include "prot.h"
+
+/* The 'j' protocol.
+
+ The 'j' protocol is a wrapper around the 'i' protocol, which avoids
+ the use of certain characters, such as XON and XOFF.
+
+ Each 'j' protocol packet begins with a '^' character, followed by a
+ two byte encoded size giving the total number of bytes in the
+ packet. The first byte is HIGH, the second byte is LOW, and the
+ number of bytes is (HIGH - 32) * 64 + (LOW - 32), where 32 <= HIGH
+ < 127 and 32 <= LOW < 96 (i.e., HIGH and LOW are printable ASCII
+ characters). This is followed by a '=' character. The next two
+ bytes are the number of data bytes in the packet, using the same
+ encoding. This is followed by a '@' character, and then that
+ number of data bytes. The remaining bytes in the packet are
+ indices of bytes which must be transformed, followed by a trailing
+ '~' character. The indices are encoded in the following overly
+ complex format.
+
+ Each byte index is two bytes long. The first byte in the index is
+ INDEX-HIGH and the second is INDEX-LOW. If 32 <= INDEX-HIGH < 126,
+ the byte index refers to the byte at position (INDEX-HIGH - 32) *
+ 32 + INDEX-LOW % 32 in the actual data, where 32 <= INDEX-LOW <
+ 127. If 32 <= INDEX-LOW < 64, then 128 must be added to the
+ indexed byte. If 64 <= INDEX-LOW < 96, then the indexed byte must
+ be exclusive or'red with 32. If 96 <= INDEX-LOW < 127, both
+ operations must be performed. If INDEX-HIGH == 126, then the byte
+ index refers to the byte at position (INDEX-LOW - 32) * 32 + 31,
+ where 32 <= INDEX-LOW < 126. 128 must be added to the byte, and it
+ must be exclusive or'red with 32. This unfortunately requires a
+ special test (when encoding INDEX-LOW must be checked for 127; when
+ decoding INDEX-HIGH must be checked for 126). It does, however,
+ permit the byte indices field to consist exclusively of printable
+ ASCII characters.
+
+ The maximum value for a byte index is (125 - 32) * 32 + 31 == 3007,
+ so the is the maximum number of data bytes permitted. Since it is
+ convenient to have each 'j' protocol packet correspond to each 'i'
+ protocol packet, we restrict the 'i' protocol accordingly.
+
+ Note that this encoding method assumes that we can send all
+ printable ASCII characters. */
+
+/* The first byte of each packet. I just picked these values
+ randomly, trying to get characters that were perhaps slightly less
+ likely to appear in normal text. */
+#define FIRST '\136'
+
+/* The fourth byte of each packet. */
+#define FOURTH '\075'
+
+/* The seventh byte of each packet. */
+#define SEVENTH '\100'
+
+/* The trailing byte of each packet. */
+#define TRAILER '\176'
+
+/* The length of the header. */
+#define CHDRLEN (7)
+
+/* Get a number of bytes encoded in a two byte length at the start of
+ a packet. */
+#define CGETLENGTH(b1, b2) (((b1) - 32) * 64 + ((b2) - 32))
+
+/* Set the high and low bytes of a two byte length at the start of a
+ packet. */
+#define ISETLENGTH_FIRST(i) ((i) / 64 + 32)
+#define ISETLENGTH_SECOND(i) ((i) % 64 + 32)
+
+/* The maximum packet size we support, as determined by the byte
+ indices. */
+#define IMAXPACKSIZE ((125 - 32) * 32 + 31)
+
+/* Amount to offset the bytes in the byte index by. */
+#define INDEX_OFFSET (32)
+
+/* Maximum value of INDEX-LOW, before offsetting. */
+#define INDEX_MAX_LOW (32)
+
+/* Maximum value of INDEX-HIGH, before offsetting. */
+#define INDEX_MAX_HIGH (94)
+
+/* The set of characters to avoid. */
+static char *zJavoid;
+
+/* The number of characters to avoid. */
+static size_t cJavoid;
+
+/* A buffer used when sending data. */
+static char *zJbuf;
+
+/* The end of the undecoded data in abPrecbuf. */
+static int iJrecend;
+
+/* Local functions. */
+static boolean fjsend_data P((struct sconnection *qconn, const char *zsend,
+ size_t csend, boolean fdoread));
+static boolean fjreceive_data P((struct sconnection *qconn, size_t cneed,
+ size_t *pcrec, int ctimeout,
+ boolean freport));
+static boolean fjprocess_data P((size_t *pcneed));
+
+/* Start the protocol. We first send over the list of characters to
+ avoid as an escape sequence, starting with FIRST and ending with
+ TRAILER. There is no error checking done on this string. */
+
+boolean
+fjstart (qdaemon, pzlog)
+ struct sdaemon *qdaemon;
+ char **pzlog;
+{
+ size_t clen;
+ char *zsend;
+ int b;
+ size_t cbuf, cgot;
+ char *zbuf;
+ int i;
+
+ /* Send the characters we want to avoid to the other side. */
+ clen = strlen (zJavoid_parameter);
+ zsend = zbufalc (clen + 3);
+ zsend[0] = FIRST;
+ memcpy (zsend + 1, zJavoid_parameter, clen);
+ zsend[clen + 1] = TRAILER;
+ zsend[clen + 2] = '\0';
+ if (! fsend_data (qdaemon->qconn, zsend, clen + 2, TRUE))
+ {
+ ubuffree (zsend);
+ return FALSE;
+ }
+ ubuffree (zsend);
+
+ /* Read the characters the other side wants to avoid. */
+ while ((b = breceive_char (qdaemon->qconn, cIsync_timeout, TRUE))
+ != FIRST)
+ {
+ if (b < 0)
+ {
+ if (b == -1)
+ ulog (LOG_ERROR, "Timed out in 'j' protocol startup");
+ return FALSE;
+ }
+ }
+
+ cbuf = 20;
+ zbuf = zbufalc (cbuf);
+ cgot = 0;
+ while ((b = breceive_char (qdaemon->qconn, cIsync_timeout, TRUE))
+ != TRAILER)
+ {
+ if (b < 0)
+ {
+ ubuffree (zbuf);
+ if (b == -1)
+ ulog (LOG_ERROR, "Timed out in 'j' protocol startup");
+ return FALSE;
+ }
+ if (cgot + 1 >= cbuf)
+ {
+ char *znew;
+
+ cbuf += 20;
+ znew = zbufalc (cbuf);
+ memcpy (znew, zbuf, cgot);
+ ubuffree (zbuf);
+ zbuf = znew;
+ }
+ zbuf[cgot] = b;
+ ++cgot;
+ }
+ zbuf[cgot] = '\0';
+
+ /* Merge the local and remote avoid bytes into one list, translated
+ into bytes. */
+ cgot = cescape (zbuf);
+
+ clen = strlen (zJavoid_parameter);
+ zJavoid = zbufalc (clen + cgot + 1);
+ memcpy (zJavoid, zJavoid_parameter, clen + 1);
+ cJavoid = cescape (zJavoid);
+
+ for (i = 0; i < cgot; i++)
+ {
+ if (memchr (zJavoid, zbuf[i], cJavoid) == NULL)
+ {
+ zJavoid[cJavoid] = zbuf[i];
+ ++cJavoid;
+ }
+ }
+
+ ubuffree (zbuf);
+
+ /* We can't avoid ASCII printable characters, since the encoding
+ method assumes that they can always be sent. If it ever turns
+ out to be important, a different encoding method could be used,
+ perhaps keyed by a different FIRST character. */
+ if (cJavoid == 0)
+ {
+ ulog (LOG_ERROR, "No characters to avoid in 'j' protocol");
+ return FALSE;
+ }
+ for (i = 0; i < cJavoid; i++)
+ {
+ if (zJavoid[i] >= 32 && zJavoid[i] <= 126)
+ {
+ ulog (LOG_ERROR, "'j' protocol can't avoid character '\\%03o'",
+ zJavoid[i]);
+ return FALSE;
+ }
+ }
+
+ /* If we are avoiding XON and XOFF, use XON/XOFF handshaking. */
+ if (memchr (zJavoid, '\021', cJavoid) != NULL
+ && memchr (zJavoid, '\023', cJavoid) != NULL)
+ {
+ if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
+ STRIPSETTING_EIGHTBITS, XONXOFF_ON))
+ return FALSE;
+ }
+
+ /* Let the port settle. */
+ usysdep_sleep (2);
+
+ /* Allocate a buffer we use when sending data. We will probably
+ never actually need one this big; if this code is ported to a
+ computer with small amounts of memory, this should be changed to
+ increase the buffer size as needed. */
+ zJbuf = zbufalc (CHDRLEN + IMAXPACKSIZE * 3 + 1);
+ zJbuf[0] = FIRST;
+ zJbuf[3] = FOURTH;
+ zJbuf[6] = SEVENTH;
+
+ /* iJrecend is the end of the undecoded data, and iPrecend is the
+ end of the decoded data. At this point there is no decoded data,
+ and we must initialize the variables accordingly. */
+ iJrecend = iPrecend;
+ iPrecend = iPrecstart;
+
+ /* Now do the 'i' protocol startup. */
+ return fijstart (qdaemon, pzlog, IMAXPACKSIZE, fjsend_data,
+ fjreceive_data);
+}
+
+/* Shut down the protocol. */
+
+boolean
+fjshutdown (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ boolean fret;
+
+ fret = fishutdown (qdaemon);
+ ubuffree (zJavoid);
+ ubuffree (zJbuf);
+ return fret;
+}
+
+/* Encode a packet of data and send it. This copies the data, which
+ is a waste of time, but calling fsend_data three times (for the
+ header, the body, and the trailer) would waste even more time. */
+
+static boolean
+fjsend_data (qconn, zsend, csend, fdoread)
+ struct sconnection *qconn;
+ const char *zsend;
+ size_t csend;
+ boolean fdoread;
+{
+ char *zput, *zindex;
+ const char *zfrom, *zend;
+ char bfirst, bsecond;
+ int iprecendhold;
+ boolean fret;
+
+ zput = zJbuf + CHDRLEN;
+ zindex = zput + csend;
+ zfrom = zsend;
+ zend = zsend + csend;
+
+ /* Optimize for the common case of avoiding two characters. */
+ bfirst = zJavoid[0];
+ if (cJavoid <= 1)
+ bsecond = bfirst;
+ else
+ bsecond = zJavoid[1];
+ while (zfrom < zend)
+ {
+ char b;
+ boolean f128, f32;
+ int i, ihigh, ilow;
+
+ b = *zfrom++;
+ if (b != bfirst && b != bsecond)
+ {
+ int ca;
+ char *za;
+
+ if (cJavoid <= 2)
+ {
+ *zput++ = b;
+ continue;
+ }
+
+ ca = cJavoid - 2;
+ za = zJavoid + 2;
+ while (ca-- != 0)
+ if (*za++ == b)
+ break;
+
+ if (ca < 0)
+ {
+ *zput++ = b;
+ continue;
+ }
+ }
+
+ if ((b & 0x80) == 0)
+ f128 = FALSE;
+ else
+ {
+ b &=~ 0x80;
+ f128 = TRUE;
+ }
+ if (b >= 32 && b != 127)
+ f32 = FALSE;
+ else
+ {
+ b ^= 0x20;
+ f32 = TRUE;
+ }
+
+ /* We must now put the byte index into the buffer. The byte
+ index is encoded similarly to the length of the actual data,
+ but the byte index also encodes the operations that must be
+ performed on the byte. The first byte in the index is the
+ most significant bits. If we only had to subtract 128 from
+ the byte, we use the second byte directly. If we had to xor
+ the byte with 32, we add 32 to the second byte index. If we
+ had to perform both operations, we add 64 to the second byte
+ index. However, if we had to perform both operations, and
+ the second byte index was 31, then after adding 64 and
+ offsetting by 32 we would come up with 127, which we are not
+ permitted to use. Therefore, in this special case we set the
+ first byte of the index to 126 and put the original first
+ byte into the second byte position instead. This is why we
+ could not permit the high byte of the length of the actual
+ data to be 126. We can get away with the switch because both
+ the value of the second byte index (31) and the operations to
+ perform (both) are known. */
+ i = zput - (zJbuf + CHDRLEN);
+ ihigh = i / INDEX_MAX_LOW;
+ ilow = i % INDEX_MAX_LOW;
+
+ if (f128 && ! f32)
+ ;
+ else if (f32 && ! f128)
+ ilow += INDEX_MAX_LOW;
+ else
+ {
+ /* Both operations had to be performed. */
+ if (ilow != INDEX_MAX_LOW - 1)
+ ilow += 2 * INDEX_MAX_LOW;
+ else
+ {
+ ilow = ihigh;
+ ihigh = INDEX_MAX_HIGH;
+ }
+ }
+
+ *zindex++ = ihigh + INDEX_OFFSET;
+ *zindex++ = ilow + INDEX_OFFSET;
+ *zput++ = b;
+ }
+
+ *zindex++ = TRAILER;
+
+ /* Set the lengths into the buffer. zJbuf[0,3,6] were set when
+ zJbuf was allocated, and are never changed thereafter. */
+ zJbuf[1] = ISETLENGTH_FIRST (zindex - zJbuf);
+ zJbuf[2] = ISETLENGTH_SECOND (zindex - zJbuf);
+ zJbuf[4] = ISETLENGTH_FIRST (csend);
+ zJbuf[5] = ISETLENGTH_SECOND (csend);
+
+ /* Send the data over the line. We must preserve iPrecend as
+ discussed in fjreceive_data. */
+ iprecendhold = iPrecend;
+ iPrecend = iJrecend;
+ fret = fsend_data (qconn, zJbuf, (size_t) (zindex - zJbuf), fdoread);
+ iJrecend = iPrecend;
+ iPrecend = iprecendhold;
+
+ /* Process any bytes that may have been placed in abPrecbuf. */
+ if (fret && iPrecend != iJrecend)
+ {
+ if (! fjprocess_data ((size_t *) NULL))
+ return FALSE;
+ }
+
+ return fret;
+}
+
+/* Receive and decode data. This is called by fiwait_for_packet. We
+ need to be able to return decoded data between iPrecstart and
+ iPrecend, while not losing any undecoded partial packets we may
+ have read. We use iJrecend as a pointer to the end of the
+ undecoded data, and set iPrecend for the decoded data. iPrecend
+ points to the start of the undecoded data. */
+
+static boolean
+fjreceive_data (qconn, cineed, pcrec, ctimeout, freport)
+ struct sconnection *qconn;
+ size_t cineed;
+ size_t *pcrec;
+ int ctimeout;
+ boolean freport;
+{
+ int iprecendstart;
+ size_t cjneed;
+ size_t crec;
+ int cnew;
+
+ iprecendstart = iPrecend;
+
+ /* Figure out how many bytes we need to decode the next packet. */
+ if (! fjprocess_data (&cjneed))
+ return FALSE;
+
+ /* As we long as we read some data but don't have enough to decode a
+ packet, we try to read some more. We decrease the timeout each
+ time so that we will not wait forever if the connection starts
+ dribbling data. */
+ do
+ {
+ int iprecendhold;
+ size_t cneed;
+
+ if (cjneed > cineed)
+ cneed = cjneed;
+ else
+ cneed = cineed;
+
+ /* We are setting iPrecend to the end of the decoded data for
+ the 'i' protocol. When we do the actual read, we have to set
+ it to the end of the undecoded data so that any undecoded
+ data we have received is not overwritten. */
+ iprecendhold = iPrecend;
+ iPrecend = iJrecend;
+ if (! freceive_data (qconn, cneed, &crec, ctimeout, freport))
+ return FALSE;
+ iJrecend = iPrecend;
+ iPrecend = iprecendhold;
+
+ /* Process any data we have received. This will set iPrecend to
+ the end of the new decoded data. */
+ if (! fjprocess_data (&cjneed))
+ return FALSE;
+
+ cnew = iPrecend - iprecendstart;
+ if (cnew < 0)
+ cnew += CRECBUFLEN;
+
+ if (cnew > cineed)
+ cineed = 0;
+ else
+ cineed -= cnew;
+
+ --ctimeout;
+ }
+ while (cnew == 0 && crec > 0 && ctimeout > 0);
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO, "fjreceive_data: Got %d decoded bytes",
+ cnew);
+
+ *pcrec = cnew;
+ return TRUE;
+}
+
+/* Decode the data in the buffer, optionally returning the number of
+ bytes needed to complete the next packet. */
+
+static boolean
+fjprocess_data (pcneed)
+ size_t *pcneed;
+{
+ int istart;
+
+ istart = iPrecend;
+ while (istart != iJrecend)
+ {
+ int i, iget;
+ char ab[CHDRLEN];
+ int cpacket, cdata, chave;
+ int iindex, iendindex;
+
+ /* Find the next occurrence of FIRST. If we have to skip some
+ garbage bytes to get to it, zero them out (so they don't
+ confuse the 'i' protocol) and advance iPrecend. This will
+ save us from looking at them again. */
+ if (abPrecbuf[istart] != FIRST)
+ {
+ int cintro;
+ char *zintro;
+ size_t cskipped;
+
+ cintro = iJrecend - istart;
+ if (cintro < 0)
+ cintro = CRECBUFLEN - istart;
+
+ zintro = memchr (abPrecbuf + istart, FIRST, (size_t) cintro);
+ if (zintro == NULL)
+ {
+ bzero (abPrecbuf + istart, (size_t) cintro);
+ istart = (istart + cintro) % CRECBUFLEN;
+ iPrecend = istart;
+ continue;
+ }
+
+ cskipped = zintro - (abPrecbuf + istart);
+ bzero (abPrecbuf + istart, cskipped);
+ istart += cskipped;
+ iPrecend = istart;
+ }
+
+ for (i = 0, iget = istart;
+ i < CHDRLEN && iget != iJrecend;
+ ++i, iget = (iget + 1) % CRECBUFLEN)
+ ab[i] = abPrecbuf[iget];
+
+ if (i < CHDRLEN)
+ {
+ if (pcneed != NULL)
+ *pcneed = CHDRLEN - i;
+ return TRUE;
+ }
+
+ cpacket = CGETLENGTH (ab[1], ab[2]);
+ cdata = CGETLENGTH (ab[4], ab[5]);
+
+ /* Make sure the header has the right magic characters, that the
+ data is not larger than the packet, and that we have an even
+ number of byte index characters. */
+ if (ab[3] != FOURTH
+ || ab[6] != SEVENTH
+ || cdata > cpacket - CHDRLEN - 1
+ || (cpacket - cdata - CHDRLEN - 1) % 2 == 1)
+ {
+ istart = (istart + 1) % CRECBUFLEN;
+ continue;
+ }
+
+ chave = iJrecend - istart;
+ if (chave < 0)
+ chave += CRECBUFLEN;
+
+ if (chave < cpacket)
+ {
+ if (pcneed != NULL)
+ *pcneed = cpacket - chave;
+ return TRUE;
+ }
+
+ /* Figure out where the byte indices start and end. */
+ iindex = (istart + CHDRLEN + cdata) % CRECBUFLEN;
+ iendindex = (istart + cpacket - 1) % CRECBUFLEN;
+
+ /* Make sure the magic trailer character is there. */
+ if (abPrecbuf[iendindex] != TRAILER)
+ {
+ istart = (istart + 1) % CRECBUFLEN;
+ continue;
+ }
+
+ /* We have a packet to decode. The decoding process is simpler
+ than the encoding process, since all we have to do is examine
+ the byte indices. We zero out the byte indices as we go, so
+ that they will not confuse the 'i' protocol. */
+ while (iindex != iendindex)
+ {
+ int ihigh, ilow;
+ boolean f32, f128;
+ int iset;
+
+ ihigh = abPrecbuf[iindex] - INDEX_OFFSET;
+ abPrecbuf[iindex] = 0;
+ iindex = (iindex + 1) % CRECBUFLEN;
+ ilow = abPrecbuf[iindex] - INDEX_OFFSET;
+ abPrecbuf[iindex] = 0;
+ iindex = (iindex + 1) % CRECBUFLEN;
+
+ /* Now we must undo the encoding, by adding 128 and xoring
+ with 32 as appropriate. Which to do is encoded in the
+ low byte, except that if the high byte is the special
+ value 126, then the low byte is actually the high byte
+ and both operations are performed. */
+ f128 = TRUE;
+ f32 = TRUE;
+ if (ihigh == INDEX_MAX_HIGH)
+ iset = ilow * INDEX_MAX_LOW + INDEX_MAX_LOW - 1;
+ else
+ {
+ iset = ihigh * INDEX_MAX_LOW + ilow % INDEX_MAX_LOW;
+ if (ilow < INDEX_MAX_LOW)
+ f32 = FALSE;
+ else if (ilow < 2 * INDEX_MAX_LOW)
+ f128 = FALSE;
+ }
+
+ /* Now iset is the index from the start of the data to the
+ byte to modify; adjust it to an index in abPrecbuf. */
+ iset = (istart + CHDRLEN + iset) % CRECBUFLEN;
+
+ if (f128)
+ abPrecbuf[iset] |= 0x80;
+ if (f32)
+ abPrecbuf[iset] ^= 0x20;
+ }
+
+ /* Zero out the header and trailer to avoid confusing the 'i'
+ protocol, and update iPrecend to the end of decoded data. */
+ for (i = 0, iget = istart;
+ i < CHDRLEN && iget != iJrecend;
+ ++i, iget = (iget + 1) % CRECBUFLEN)
+ abPrecbuf[iget] = 0;
+ abPrecbuf[iendindex] = 0;
+ iPrecend = (iendindex + 1) % CRECBUFLEN;
+ istart = iPrecend;
+ }
+
+ if (pcneed != NULL)
+ *pcneed = CHDRLEN + 1;
+ return TRUE;
+}
diff --git a/gnu/libexec/uucp/uucico/prott.c b/gnu/libexec/uucp/uucico/prott.c
new file mode 100644
index 0000000000000..44763958349ae
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/prott.c
@@ -0,0 +1,330 @@
+/* prott.c
+ The 't' protocol.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char prott_rcsid[] = "$Id: prott.c,v 1.1 1993/08/05 18:27:15 conklin Exp $";
+#endif
+
+#include "uudefs.h"
+#include "uuconf.h"
+#include "conn.h"
+#include "trans.h"
+#include "system.h"
+#include "prot.h"
+
+/* This implementation is based on code written by Rick Adams.
+
+ This code implements the 't' protocol, which does no error checking
+ whatsoever and thus requires an end-to-end verified eight bit
+ communication line, such as is provided by TCP. Using it with a
+ modem is unadvisable, since errors can occur between the modem and
+ the computer. */
+
+/* The buffer size we use. */
+#define CTBUFSIZE (1024)
+
+/* The offset in the buffer to the data. */
+#define CTFRAMELEN (4)
+
+/* Commands are sent in multiples of this size. */
+#define CTPACKSIZE (512)
+
+/* A pointer to the buffer we will use. */
+static char *zTbuf;
+
+/* True if we are receiving a file. */
+static boolean fTfile;
+
+/* The timeout we use. */
+static int cTtimeout = 120;
+
+struct uuconf_cmdtab asTproto_params[] =
+{
+ { "timeout", UUCONF_CMDTABTYPE_INT, (pointer) &cTtimeout, NULL },
+ { NULL, 0, NULL, NULL }
+};
+
+/* Local function. */
+
+static boolean ftprocess_data P((struct sdaemon *qdaemon, boolean *pfexit,
+ size_t *pcneed));
+
+/* Start the protocol. */
+
+boolean
+ftstart (qdaemon, pzlog)
+ struct sdaemon *qdaemon;
+ char **pzlog;
+{
+ *pzlog = NULL;
+ if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
+ STRIPSETTING_EIGHTBITS, XONXOFF_OFF))
+ return FALSE;
+ zTbuf = (char *) xmalloc (CTBUFSIZE + CTFRAMELEN);
+ /* The first two bytes of the buffer are always zero. */
+ zTbuf[0] = 0;
+ zTbuf[1] = 0;
+ fTfile = FALSE;
+ usysdep_sleep (2);
+ return TRUE;
+}
+
+/* Stop the protocol. */
+
+/*ARGSUSED*/
+boolean
+ftshutdown (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ xfree ((pointer) zTbuf);
+ zTbuf = NULL;
+ cTtimeout = 120;
+ return TRUE;
+}
+
+/* Send a command string. We send everything up to and including the
+ null byte. The number of bytes we send must be a multiple of
+ TPACKSIZE. */
+
+/*ARGSUSED*/
+boolean
+ftsendcmd (qdaemon, z, ilocal, iremote)
+ struct sdaemon *qdaemon;
+ const char *z;
+ int ilocal;
+ int iremote;
+{
+ size_t clen, csend;
+ char *zalc;
+ boolean fret;
+
+ DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "ftsendcmd: Sending command \"%s\"", z);
+
+ clen = strlen (z);
+
+ /* We need to send the smallest multiple of CTPACKSIZE which is
+ greater than clen (not equal to clen, since we need room for the
+ null byte). */
+ csend = ((clen / CTPACKSIZE) + 1) * CTPACKSIZE;
+
+ zalc = zbufalc (csend);
+ memcpy (zalc, z, clen);
+ bzero (zalc + clen, csend - clen);
+
+ fret = fsend_data (qdaemon->qconn, zalc, csend, TRUE);
+ ubuffree (zalc);
+ return fret;
+}
+
+/* Get space to be filled with data. We provide a buffer which has
+ four bytes at the start available to hold the length. */
+
+/*ARGSIGNORED*/
+char *
+ztgetspace (qdaemon, pclen)
+ struct sdaemon *qdaemon;
+ size_t *pclen;
+{
+ *pclen = CTBUFSIZE;
+ return zTbuf + CTFRAMELEN;
+}
+
+/* Send out some data. We are allowed to modify the four bytes
+ preceding the buffer. This allows us to send the entire block with
+ header bytes in a single call. */
+
+/*ARGSIGNORED*/
+boolean
+ftsenddata (qdaemon, zdata, cdata, ilocal, iremote, ipos)
+ struct sdaemon *qdaemon;
+ char *zdata;
+ size_t cdata;
+ int ilocal;
+ int iremote;
+ long ipos;
+{
+ /* Here we do htonl by hand, since it doesn't exist everywhere. We
+ know that the amount of data cannot be greater than CTBUFSIZE, so
+ the first two bytes of this value will always be 0. They were
+ set to 0 in ftstart so we don't touch them here. This is useful
+ because we cannot portably right shift by 24 or 16, since we
+ might be dealing with sixteen bit integers. */
+ zdata[-2] = (char) ((cdata >> 8) & 0xff);
+ zdata[-1] = (char) (cdata & 0xff);
+
+ /* We pass FALSE to fsend_data since we don't expect the other side
+ to be sending us anything just now. */
+ return fsend_data (qdaemon->qconn, zdata - CTFRAMELEN, cdata + CTFRAMELEN,
+ FALSE);
+}
+
+/* Process data and return the amount we need in *pfneed. */
+
+static boolean
+ftprocess_data (qdaemon, pfexit, pcneed)
+ struct sdaemon *qdaemon;
+ boolean *pfexit;
+ size_t *pcneed;
+{
+ int cinbuf, cfirst, clen;
+
+ *pfexit = FALSE;
+
+ cinbuf = iPrecend - iPrecstart;
+ if (cinbuf < 0)
+ cinbuf += CRECBUFLEN;
+
+ if (! fTfile)
+ {
+ /* We are not receiving a file. Commands are read in chunks of
+ CTPACKSIZE. */
+ while (cinbuf >= CTPACKSIZE)
+ {
+ cfirst = CRECBUFLEN - iPrecstart;
+ if (cfirst > CTPACKSIZE)
+ cfirst = CTPACKSIZE;
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "ftprocess_data: Got %d command bytes",
+ cfirst);
+
+ if (! fgot_data (qdaemon, abPrecbuf + iPrecstart,
+ (size_t) cfirst, abPrecbuf,
+ (size_t) CTPACKSIZE - cfirst,
+ -1, -1, (long) -1, TRUE, pfexit))
+ return FALSE;
+
+ iPrecstart = (iPrecstart + CTPACKSIZE) % CRECBUFLEN;
+
+ if (*pfexit)
+ return TRUE;
+
+ cinbuf -= CTPACKSIZE;
+ }
+
+ if (pcneed != NULL)
+ *pcneed = CTPACKSIZE - cinbuf;
+
+ return TRUE;
+ }
+
+ /* Here we are receiving a file. The data comes in blocks. The
+ first four bytes contain the length, followed by that amount of
+ data. */
+
+ while (cinbuf >= CTFRAMELEN)
+ {
+ /* The length is stored in network byte order, MSB first. */
+
+ clen = (((((((abPrecbuf[iPrecstart] & 0xff) << 8)
+ + (abPrecbuf[(iPrecstart + 1) % CRECBUFLEN] & 0xff)) << 8)
+ + (abPrecbuf[(iPrecstart + 2) % CRECBUFLEN] & 0xff)) << 8)
+ + (abPrecbuf[(iPrecstart + 3) % CRECBUFLEN] & 0xff));
+
+ if (cinbuf < clen + CTFRAMELEN)
+ {
+ if (pcneed != NULL)
+ *pcneed = clen + CTFRAMELEN - cinbuf;
+ return TRUE;
+ }
+
+ iPrecstart = (iPrecstart + CTFRAMELEN) % CRECBUFLEN;
+
+ cfirst = CRECBUFLEN - iPrecstart;
+ if (cfirst > clen)
+ cfirst = clen;
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "ftprocess_data: Got %d data bytes",
+ clen);
+
+ if (! fgot_data (qdaemon, abPrecbuf + iPrecstart,
+ (size_t) cfirst, abPrecbuf, (size_t) (clen - cfirst),
+ -1, -1, (long) -1, TRUE, pfexit))
+ return FALSE;
+
+ iPrecstart = (iPrecstart + clen) % CRECBUFLEN;
+
+ if (*pfexit)
+ return TRUE;
+
+ cinbuf -= clen + CTFRAMELEN;
+ }
+
+ if (pcneed != NULL)
+ *pcneed = CTFRAMELEN - cinbuf;
+
+ return TRUE;
+}
+
+/* Wait for data to come in and process it until we've reached the end
+ of a command or a file. */
+
+boolean
+ftwait (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ while (TRUE)
+ {
+ boolean fexit;
+ size_t cneed, crec;
+
+ if (! ftprocess_data (qdaemon, &fexit, &cneed))
+ return FALSE;
+ if (fexit)
+ return TRUE;
+
+ if (! freceive_data (qdaemon->qconn, cneed, &crec, cTtimeout, TRUE))
+ return FALSE;
+
+ if (crec == 0)
+ {
+ ulog (LOG_ERROR, "Timed out waiting for data");
+ return FALSE;
+ }
+ }
+}
+
+/* File level routine, to set fTfile correctly. */
+
+/*ARGSUSED*/
+boolean
+ftfile (qdaemon, qtrans, fstart, fsend, cbytes, pfhandled)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+ boolean fstart;
+ boolean fsend;
+ long cbytes;
+ boolean *pfhandled;
+{
+ *pfhandled = FALSE;
+
+ if (! fsend)
+ fTfile = fstart;
+
+ return TRUE;
+}
diff --git a/gnu/libexec/uucp/uucico/protz.c b/gnu/libexec/uucp/uucico/protz.c
new file mode 100644
index 0000000000000..6510df7d798e7
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/protz.c
@@ -0,0 +1,2626 @@
+/* protz.c Version 1.5, 92Apr24 */
+/* Modified by Ian Lance Taylor for Taylor UUCP 1.04 92Aug4. */
+
+/*
+ * Doug Evans, dje@sspiff.UUCP or dje@ersys.edmonton.ab.ca
+ *
+ * This file provides the Zmodem protocol (by Chuck Forsberg) for
+ * Ian Taylor's UUCP package.
+ *
+ * It was originally developed to establish a uucp link between myself and my
+ * employer: Ivation Datasystems, Inc. of Ottawa.
+ *
+ * My thanks to Ivation for letting me release this to the public. Given that
+ * Zmodem is in the public domain, no additional copyrights have been added.
+ *
+ *****************************************************************************
+ *
+ * It's been difficult fitting Zmodem into the UUCP world. I have been guided
+ * mostly by trying to plug it into Taylor UUCP. Where "the Zmodem way of doing
+ * things" conflicted with "the UUCP way of doing things", I have err'd on the
+ * side of UUCP. At the end of it all, I have achieved something that will plug
+ * into Taylor UUCP very easily, but some might argue that I have corrupted Z
+ * too much. At any rate, compatibility with sz/rz was sacrificed to achieve a
+ * clean UUCP protocol. Given that, I took the opportunity to start from
+ * scratch when defining protocol constants (EG: ZBIN).
+ *
+ * 1) I wasn't quite sure how to enhance Zmodem to handle send+receive in one
+ * session, so I added a 'g' protocol like initialization sequence. This
+ * also gets this stuff out of the way, in case we ever try to support
+ * full-duplex.
+ *
+ * Caller Callee
+ * ------ ------
+ * ZINIT --> <-- ZINIT
+ * ZDATA (ZCRCF) --> <-- ZDATA (ZCRCF)
+ * ZACK --> <-- ZACK
+ * ZINITEND --> <-- ZINITEND
+ *
+ * ZINIT is a combination of ZRINIT and ZSINIT and is intended to exchange
+ * simple protocol information (flags) and the protocol version number.
+ * ZDATA is intended to include window size information as well as the
+ * "Myattn" string (although at the moment it doesn't contain anything).
+ * ZDATA may contain at most 1k bytes of data and is sent out as one ZCRCF
+ * packet. Two ack's (ZACK + ZINITEND) are needed to ensure both sides have
+ * received ZDATA.
+ *
+ * 2) I've hardcoded several protocol parameters, like 32 bit CRC's for data.
+ * Others are not supported (we don't need them).
+ *
+ * 3) ZHEX headers use 32 bit CRC's.
+ *
+ * 4) Zmodem sends the ZFILE message "in one chunk". If there are errors, the
+ * entire string is resent. I have continued this practice. All UUCP
+ * commands are sent "in one chunk". This can be changed down the road if
+ * necessary.
+ *
+ * 5) The ZEOF message has been replaced with a new ZCRCx value: ZCRCF. ZCRCF
+ * is identical to ZCRCW except that it indicates the end of the message.
+ * The protocol here is *not* a file transfer protocol. It is an end to end
+ * transport protocol (that preserves message boundaries).
+ *
+ * 6) Zmodem handles restarting a file transfer, but as best as I can tell UUCP
+ * does not. At least Taylor UUCP doesn't. And if UUCP does start handling
+ * file restart, can it be plugged into the existing Zmodem way with zero
+ * changes? Beats me. Therefore I have removed this part of the code. One
+ * can always put it back in if and when UUCP handles it. Ditto for other
+ * pieces of removed code: there's no point in overly complicating this code
+ * when supporting all the bells and whistles requires enhancements to UUCP
+ * itself.
+ *
+ * *** It is easier to put code back in in an upward compatible manner ***
+ * *** than it is to correct for misunderstood code or poorly merged ***
+ * *** (Zmodem vs UUCP) code. ***
+ *
+ * 7) For the character in the initial "protocol selection" sequence, I have
+ * chosen 'a'. I'm told 'z' is already in use for something that isn't
+ * Zmodem. It's entirely reasonable to believe that if Zmodem ever becomes a
+ * standard UUCP protocol, this won't be it (so I'll leave z/Z for them).
+ * Publicly, this is the 'a' protocol. Internally, it is refered to as 'z'.
+ * A little confusing, I know. Maybe in time I'll refer to it internally as
+ * 'a', or maybe in time this will be *the* 'z' protocol.
+ *
+ * 8) Since we are writing a transport protocol, which isn't supposed to know
+ * anything about what is being transfered or where it is coming from, the
+ * header data value has changed meaning. It no longer means "file position"
+ * but instead means "window position". It is a running counter of the bytes
+ * transfered. Each "message" begins on a 1k boundary so the count isn't a
+ * precise byte count. The counter wraps every 4 gigabytes, although this
+ * wrapping isn't supported yet.
+ *
+ * FIXME: At present the max data transfered per session is 4 gigabytes.
+ *
+ ****************************************************************************
+ *
+ * A typical message sequence is (master sending file to slave):
+ *
+ * Master Slave
+ * ------ -----
+ * ZDATA (S, ZCRCF) -->
+ * <-- ZACK
+ * <-- ZDATA (SY, ZCRCF)
+ * ZACK -->
+ * ZDATA -->
+ * ... <-- ZACK/ZRPOS
+ * ZDATA (ZCRCF) -->
+ * <-- ZACK
+ * <-- ZDATA (CY, ZCRCF)
+ * ZACK -->
+ *
+ * A typical message sequence is (master receiving file from slave):
+ *
+ * Master Slave
+ * ------ -----
+ * ZDATA (R, ZCRCF) -->
+ * <-- ZACK
+ * <-- ZDATA (RY, ZCRCF)
+ * ZACK -->
+ * <-- ZDATA
+ * ZACK/ZRPOS ... -->
+ * <-- ZDATA (ZCRCF)
+ * ZACK -->
+ * ZDATA (CY, ZCRCF) -->
+ * <-- ZACK
+ *
+ *****************************************************************************
+ *
+ * Notes:
+ * 1) For future bidirectional concerns, keep packet types "unidirectional".
+ * Sender always uses: ZDATA, ZNAK
+ * Receiver always uses: ZRPOS, ZACK
+ * There is no intersection.
+ *
+ * I'm not sure if this is necessary or even useful, but it seems to be.
+ *
+ * 2) I use to store the byte count / 32 in the data header. This left 5 bits
+ * unused for future concerns. I removed this because of the following
+ * situation when sending a file:
+ *
+ * ZDATA (ZCRCG, xx bytes) - received ok
+ * ZDATA (ZCRCF, 0 bytes) - corrupted
+ *
+ * At this point the receiver would like to send back a ZRPOS with a value
+ * of the size of the file. However, it can't because the value is divided
+ * by 32, and it would have to round up to the next multiple of 32. This
+ * seemed a little ugly, so I went with using the entire header to store
+ * the byte count.
+ *
+ *****************************************************************************
+ *
+ * Source version:
+ *
+ * 1.1,2,3
+ * Protocol version 0
+ * Early attempts, completely rewritten later.
+ *
+ * 1.4 Protocol version 1
+ * Beta test sent to Ian for analysis 92Apr18.
+ *
+ * 1.5 Protocol version 1
+ * Released 92Apr24.
+ *
+ *****************************************************************************
+ *
+ * Protocol version:
+ *
+ * A version number is exchanged in the ZINIT message, so it is possible to
+ * correct or enhance the protocol, without breaking existing versions.
+ * The purpose of this section is to document these versions as they come out.
+ * Remember, this is the protocol version, not the source version.
+ *
+ * 0 Initial version.
+ * Zmodem controlled file transfer. This was more of a "plug Z
+ * into UUCP as is" port.
+ *
+ * 1 Complete rewrite.
+ * Made Z more of a transport protocol. UUCP now controls transfer and Z
+ * is on the same footing as the other UUCP protocols.
+ * Theoretically, there will be little pain when UUCP goes bidirectional.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char protz_rcsid[] = "$Id: protz.c,v 1.1 1993/08/05 18:27:16 conklin Exp $";
+#endif
+
+#include <errno.h>
+
+#include "uudefs.h"
+#include "uuconf.h"
+#include "conn.h"
+#include "trans.h"
+#include "system.h"
+#include "prot.h"
+
+#define ZPROTOCOL_VERSION 1
+
+/*
+ * Control message characters ...
+ */
+
+#define ZPAD '*' /* Padding character begins frames */
+#define ZDLE 030 /* Ctrl-X Zmodem escape - `ala BISYNC DLE */
+#define ZBIN 'A' /* Binary frame indicator */
+#define ZHEX 'B' /* HEX frame indicator */
+
+/*
+ * Frame types (see array "frametypes" in zm.c) ...
+ *
+ * Note that the numbers here have been reorganized, as we don't support
+ * all of them (nor do we need to).
+ *
+ * WARNING: The init sequence assumes ZINIT < ZDATA < ZACK < ZINITEND.
+ */
+
+#define ZINIT 0 /* Init (contains protocol version, flags) */
+#define ZDATA 1 /* Data packet(s) follow */
+#define ZRPOS 2 /* Resume data trans at this position */
+#define ZACK 3 /* ACK to above */
+#define ZNAK 4 /* Last packet was garbled */
+#define Zreserved 5 /* reserved (for future concerns) */
+#define ZINITEND 6 /* end of init sequence */
+#define ZFIN 7 /* Finish session */
+
+/*
+ * ZDLE sequences ...
+ *
+ * Note addition of ZCRCF: "end of message".
+ */
+
+#define ZCRCE 'h' /* CRC next, frame ends, header packet follows */
+#define ZCRCG 'i' /* CRC next, frame continues nonstop */
+#define ZCRCQ 'j' /* CRC next, frame continues, ZACK expected */
+#define ZCRCW 'k' /* CRC next, ZACK expected, end of frame */
+#define ZCRCF 'l' /* CRC next, ZACK expected, end of message */
+
+#define ZRUB0 'm' /* Translate to rubout 0177 */
+#define ZRUB1 'n' /* Translate to rubout 0377 */
+
+
+/*
+ * zdlread return values (internal) ...
+ * Other values are ZM_ERROR, ZM_TIMEOUT, ZM_RCDO.
+ */
+
+#define GOTOR 0400
+#define GOTCRCE (ZCRCE | GOTOR) /* ZDLE-ZCRCE received */
+#define GOTCRCG (ZCRCG | GOTOR) /* ZDLE-ZCRCG received */
+#define GOTCRCQ (ZCRCQ | GOTOR) /* ZDLE-ZCRCQ received */
+#define GOTCRCW (ZCRCW | GOTOR) /* ZDLE-ZCRCW received */
+#define GOTCRCF (ZCRCF | GOTOR) /* ZDLE-ZCRCF received */
+
+/*
+ * Byte positions within header array ...
+ */
+
+#define ZF0 3 /* First flags byte */
+#define ZF1 2
+#define ZF2 1
+#define ZF3 0
+
+#define ZP0 0 /* Low order 8 bits of position */
+#define ZP1 1
+#define ZP2 2
+#define ZP3 3 /* High order 8 bits of position */
+
+/*
+ * Bit Masks for ZRQINIT flags byte ZF0 ...
+ */
+
+#define TX_ESCCTL 1 /* Tx will escape control chars */
+
+/*
+ * Possible errors when running ZMODEM ...
+ */
+
+#define ZM_ERROR (-1) /* crc error, etc. */
+#define ZM_TIMEOUT (-2)
+#define ZM_RCDO (-3) /* Carrier Lost */
+
+/*
+ * ASCII characters ...
+ */
+
+#define LF 012
+#define CR 015
+#define XON 021
+#define XOFF 023
+
+#define XON_WAIT 10 /* seconds */
+
+/*
+ * Packet sizes ...
+ *
+ * FIXME: CPACKETSIZE is hardcoded in a lot of places.
+ * It's not clear to me whether changing it's value would be a
+ * "good thing" or not. But of course that doesn't excuse the hardcoding.
+ */
+
+#define CPACKETSIZE 1024 /* max packet size (data only) */
+#define CFRAMELEN 12 /* header size */
+#define CSUFFIXLEN 10 /* suffix at end of data packets */
+#define CEXCHANGE_INIT_RETRIES 4
+
+/* The header CRC value. */
+
+#if ANSI_C
+#define IHDRCRC 0xDEBB20E3UL
+#else
+#define IHDRCRC ((unsigned long) 0xDEBB20E3L)
+#endif
+
+/* packet buffer size */
+#define CPACKBUFSIZE (CFRAMELEN + 2 * CPACKETSIZE + CSUFFIXLEN + 42 /*slop*/)
+
+/*
+ * Data types ...
+ */
+
+typedef unsigned char achdrval_t[4];
+typedef unsigned long hdrval_t;
+typedef unsigned long winpos_t;
+
+/*
+ * Configurable parms ...
+ *
+ * FIXME: <cZrx_buf_len> isn't used yet. It may not be needed.
+ */
+
+#define CTIMEOUT 10
+#define CRETRIES 10
+#define CSTARTUP_RETRIES 4
+#define CGARBAGE 2400
+#define CSEND_WINDOW 16384
+#define FESCAPE_CONTROL FALSE
+
+static int cZtimeout = CTIMEOUT; /* (seconds) */
+static int cZretries = CRETRIES;
+static int cZstartup_retries = CSTARTUP_RETRIES;
+static int cZmax_garbage = CGARBAGE; /* max garbage before header */
+static int cZtx_window = CSEND_WINDOW; /* our transmission window */
+static int cZrx_buf_len = 0; /* our reception buffer size */
+static boolean fZesc_ctl = FESCAPE_CONTROL; /* escape control chars */
+
+struct uuconf_cmdtab asZproto_params[] =
+{
+ {"timeout", UUCONF_CMDTABTYPE_INT, (pointer) & cZtimeout, NULL},
+ {"retries", UUCONF_CMDTABTYPE_INT, (pointer) & cZretries, NULL},
+ {"startup-retries", UUCONF_CMDTABTYPE_INT,
+ (pointer) & cZstartup_retries, NULL},
+ {"garbage", UUCONF_CMDTABTYPE_INT, (pointer) & cZmax_garbage, NULL},
+ {"send-window", UUCONF_CMDTABTYPE_INT, (pointer) & cZtx_window, NULL},
+ {"escape-control", UUCONF_CMDTABTYPE_BOOLEAN, (pointer) & fZesc_ctl,
+ NULL},
+ {NULL, 0, NULL, NULL}
+};
+
+/*
+ * Variables for statistic gathering ...
+ *
+ * We use <wpZtxpos, wpZrxbytes> to record the number of "packets"
+ * sent/received. Packets is in double quotes because some of them aren't full.
+ */
+
+static unsigned long cZheaders_sent;
+static unsigned long cZheaders_received;
+static unsigned long cZbytes_resent;
+static unsigned long cZtimeouts;
+static unsigned long cZerrors;
+
+/*
+ * Data buffers ...
+ */
+
+static char *zZtx_buf; /* transmit buffer */
+
+static char *zZtx_packet_buf; /* raw outgoing packet data */
+static char *zZrx_packet_buf; /* raw incoming packet data */
+
+/*
+ * Transmitter state variables ...
+ */
+
+static unsigned cZblklen; /* data length in sent/received packets */
+static unsigned cZtxwspac; /* spacing between ZCRCQ requests */
+/*static unsigned cZblklen_override;*//* override value for <cZblklen> */
+static unsigned cZtxwcnt; /* counter used to space ack requests */
+static unsigned cZrxwcnt; /* counter used to watch receiver's buf size */
+static winpos_t wpZtxstart; /* <wpZtxpos> when message started */
+static winpos_t wpZtxpos; /* transmitter position */
+static winpos_t wpZlastsync; /* last offset to which we got a ZRPOS */
+static winpos_t wpZlrxpos; /* receiver's last reported offset */
+static winpos_t wpZrxpos; /* receiver file position */
+
+static int iZlast_tx_data_packet; /* type of last ZDATA packet (ZCRCx) */
+static int iZjunk_count; /* amount of garbage characters received */
+static int iZtleft; /* for dynamic packet resizing */
+
+static int iZbeenhereb4; /* times we've been ZRPOS'd to same place */
+
+/*
+ * Receiver state variables ...
+ */
+
+static winpos_t wpZrxbytes; /* receiver byte count */
+static int iZlast_rx_data_packet; /* last successfully received ZCRCx packet */
+
+/*
+ * Misc. globals ...
+ */
+
+static char xon = XON;
+
+#ifdef DJE_TESTING
+int uucptest = -1;
+int uucptest2;
+int uucptestseed;
+#endif
+
+/*
+ * Kludge!!!
+ * See fzfinish_tx(). Basically the next two globals are used to record the
+ * fact that we got a ZDATA, but aren't quite ready to process it.
+ */
+
+static int iZpkt_rcvd_kludge; /* -1 if not valid */
+static hdrval_t hvZpkt_hdrval_kludge;
+
+/*
+ * Packet types ...
+ */
+
+static const char *azZframe_types[] = {
+ "Carrier Lost", /* -3 */
+ "Timeout", /* -2 */
+ "Error", /* -1 */
+#define FTOFFSET 3
+ "ZINIT",
+ "ZDATA",
+ "ZRPOS",
+ "ZACK",
+ "ZNAK",
+ "Zreserved",
+ "ZINITEND",
+ "ZFIN",
+ "UNKNOWN!!!"
+};
+#define FTNUMBER (sizeof(azZframe_types) / sizeof(char *))
+
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+#define ZZHEADER_NAME(itype) \
+ azZframe_types[min((itype) + FTOFFSET, FTNUMBER - 1)]
+
+/*
+ * Local functions ...
+ */
+
+static boolean fzsend_data P((struct sdaemon *qdaemon, char *zdata,
+ size_t cdata, boolean fendofmessage));
+static boolean fzprocess P((struct sdaemon *qdaemon));
+static boolean fzstart_proto P((struct sdaemon *qdaemon));
+static int izexchange_init P((struct sdaemon *qdaemon, int send_type,
+ achdrval_t send_val, achdrval_t recv_val));
+static boolean fzshutdown_proto P((struct sdaemon *qdaemon));
+static boolean fzstart_tx P((void));
+static boolean fzfinish_tx P((struct sdaemon *qdaemon, long *plredo));
+static boolean fzstart_rx P((void));
+static boolean fzfinish_rx P((struct sdaemon *qdaemon));
+static boolean fzsend_hdr P((struct sdaemon *qdaemon, int ipkttype,
+ int ihdrtype, hdrval_t hdrval,
+ boolean fcheckreceive));
+static boolean fzsend_data_packet P((struct sdaemon *qdaemon, char *zdata,
+ size_t cdata, int frameend,
+ boolean fcheckreceive));
+static int czbuild_header P((char *zresult, int ipkttype, int ihdrtype,
+ hdrval_t hdrval));
+static int czbuild_data_packet P((char *zresult, const char *zdata,
+ size_t cdata, int frameend));
+/*
+ * The rest of the functions do not follow Ian's naming style. I have left
+ * the names the same as the original zm source. Over time, they may change.
+ */
+static int izrecv_hdr P((struct sdaemon *qdaemon, achdrval_t hdr));
+static int zrbhdr32 P((struct sdaemon *qdaemon, achdrval_t hdr));
+static int zrhhdr P((struct sdaemon *qdaemon, achdrval_t hdr));
+static int zrdat32 P((struct sdaemon *qdaemon, char *buf, int length,
+ int *iprxcount));
+static int getinsync P((struct sdaemon *qdaemon, boolean flag));
+static char *zputhex P((char *p, int ch));
+static char *zputchar P((char *p, int ch));
+static int zgethex P((struct sdaemon *qdaemon));
+static int zdlread P((struct sdaemon *qdaemon));
+static int noxrd7 P((struct sdaemon *qdaemon));
+static int realreadchar P((struct sdaemon *qdaemon, int timeout));
+static boolean fzreceive_ready P((void));
+static void stohdr P((hdrval_t pos, achdrval_t hdr));
+static hdrval_t rclhdr P((achdrval_t hdr));
+static hdrval_t hvzencode_data_hdr P((winpos_t cbytes));
+static void zdecode_data_hdr P((hdrval_t hdrval, winpos_t *pcbytes));
+static winpos_t lzupdate_rxpos P((achdrval_t rx_hdr, winpos_t rxpos,
+ winpos_t lrxpos, winpos_t txpos));
+
+/*
+ * This macro replaces readchar() because it achieves a noticable speed up. The
+ * readchar() function has been renamed realreadchar(). Thanks to Ian for
+ * running this stuff through a profiler to find this out. Ian suggests further
+ * speed ups may be obtained by doing a similar thing in zrdat32().
+ */
+
+/* Assign the next character to b. */
+#define READCHAR(qdaemon, b, i) \
+ (iPrecstart != iPrecend \
+ ? ((b) = BUCHAR (abPrecbuf[iPrecstart]), \
+ iPrecstart = (iPrecstart + 1) % CRECBUFLEN) \
+ : ((b) = realreadchar ((qdaemon), (i))))
+
+/************************************************************************/
+
+
+/*
+ * Start the protocol ...
+ */
+
+boolean
+fzstart(qdaemon, pzlog)
+struct sdaemon *qdaemon;
+char **pzlog;
+{
+ *pzlog = zbufalc (sizeof "protocol 'a' starting: , , , , , " + 100);
+ sprintf (*pzlog, "protocol 'a' starting: %d, %d, %d, %d, %d, %d",
+ cZtimeout, cZretries, cZstartup_retries,
+ cZmax_garbage, cZtx_window, fZesc_ctl);
+
+ if (! fconn_set (qdaemon->qconn, PARITYSETTING_NONE,
+ STRIPSETTING_EIGHTBITS, XONXOFF_OFF))
+ return FALSE;
+
+ /*
+ * For now, we place tight restrictions on the size of the transmit
+ * window. This might be relaxed in the future. If it is relaxed,
+ * some of these tests will stay, some will go. That is why it is
+ * coded like it is.
+ */
+
+ if (cZtx_window % 1024 != 0 ||
+ cZtx_window < 4096 || cZtx_window > 65536 ||
+ 65536 % cZtx_window != 0
+ ) {
+ ulog (LOG_ERROR,
+ "fzstart: cZtx_window not one of 4096, 8192, 16384, 32768, 65536");
+ return FALSE;
+ }
+
+ zZtx_buf = (char *) xmalloc (CPACKETSIZE);
+ zZtx_packet_buf = (char *) xmalloc (CPACKBUFSIZE);
+ zZrx_packet_buf = (char *) xmalloc (CPACKBUFSIZE);
+
+ iZlast_tx_data_packet = -1;
+ iZlast_rx_data_packet = -1;
+
+ wpZtxpos = wpZlrxpos = wpZrxpos = wpZrxbytes = 0;
+ cZtxwspac = cZtx_window / 4;
+
+ cZheaders_sent = cZheaders_received = cZbytes_resent = 0;
+ cZtimeouts = cZerrors = 0;
+
+ iZpkt_rcvd_kludge = -1;
+
+#if 0
+ /*
+ * We ensure <cZtx_window> is at least 4k, so the following is
+ * unnecessary. It can be put back in later if needed.
+ */
+ if (cZblklen_override > cZtxwspac
+ || (!cZblklen_override && cZtxwspac < 1024))
+ cZblklen_override = cZtxwspac;
+#endif
+
+#ifdef DJE_TESTING
+ {
+ extern int uucptest,uucptest2,uucptestseed;
+ FILE *f;
+
+ if (uucptest == -1) {
+ f = fopen ("/usr/local/src/bin/uucp/uucptest", "r");
+ if (f != NULL) {
+ fscanf (f, "%d %d %d",
+ &uucptestseed, &uucptest, &uucptest2);
+ fclose (f);
+ }
+ srand (uucptestseed);
+ }
+ }
+#endif
+
+ /*
+ * Fire up the protocol (exchange init messages) ...
+ */
+
+ if (!fzstart_proto (qdaemon))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Stop the protocol ...
+ */
+
+boolean
+fzshutdown(qdaemon)
+struct sdaemon *qdaemon;
+{
+ (void) fzshutdown_proto (qdaemon);
+
+ xfree ((pointer) zZtx_buf);
+ xfree ((pointer) zZtx_packet_buf);
+ xfree ((pointer) zZrx_packet_buf);
+ zZtx_buf = NULL;
+ zZtx_packet_buf = NULL;
+ zZrx_packet_buf = NULL;
+
+ /*
+ * Print some informative statistics ...
+ *
+ * I use the word "messages" here instead of "headers" because the
+ * latter is jargonese.
+ */
+
+ ulog (LOG_NORMAL,
+ "Protocol 'a' messages: sent %lu, received %lu",
+ cZheaders_sent, cZheaders_received);
+ ulog (LOG_NORMAL,
+ "Protocol 'a' packets: sent %lu, received %lu",
+ wpZtxpos / 1024, wpZrxbytes / 1024);
+ if (cZbytes_resent != 0 || cZtimeouts != 0 || cZerrors != 0)
+ ulog (LOG_NORMAL,
+ "Protocol 'a' errors: bytes resent %lu, timeouts %lu, errors %lu",
+ cZbytes_resent, cZtimeouts, cZerrors);
+
+ /*
+ * Reset all the parameters to their default values, so that the
+ * protocol parameters used for this connection do not affect the
+ * next one.
+ */
+
+ cZtimeout = CTIMEOUT;
+ cZretries = CRETRIES;
+ cZstartup_retries = CSTARTUP_RETRIES;
+ cZmax_garbage = CGARBAGE;
+ cZtx_window = CSEND_WINDOW;
+ fZesc_ctl = FESCAPE_CONTROL;
+
+ cZheaders_sent = cZheaders_received = cZbytes_resent = 0;
+ cZtimeouts = cZerrors = 0;
+
+ return TRUE;
+}
+
+/*
+ * Send a command string ...
+ * We send everything up to and including the null byte.
+ *
+ * We assume the command will fit in the outgoing data buffer.
+ * FIXME: A valid assumption?
+ */
+
+/*ARGSUSED*/
+boolean
+fzsendcmd(qdaemon, z, ilocal, iremote)
+struct sdaemon *qdaemon;
+const char *z;
+int ilocal;
+int iremote;
+{
+ size_t n,clen;
+ long lredo;
+ char *zbuf;
+
+ clen = strlen (z) + 1;
+
+ DEBUG_MESSAGE1 (DEBUG_PROTO, "fzsendcmd: sending command %s", z);
+
+ if (!fzstart_tx ()) /* must be called before zzgetspace() */
+ return FALSE;
+
+ if ((zbuf = zzgetspace (qdaemon, &n)) == NULL)
+ return FALSE;
+
+#if DEBUG > 0
+ if (clen > n)
+ ulog (LOG_FATAL, "fzsendcmd: clen > n");
+#endif
+
+ strcpy (zbuf, z);
+
+ /*
+ * Send it out ...
+ */
+
+ do {
+ if (!fzsend_data (qdaemon, zbuf, clen, TRUE))
+ return FALSE;
+ if (!fzfinish_tx (qdaemon, &lredo))
+ return FALSE;
+ } while (lredo >= 0);
+
+ return fzprocess (qdaemon);
+}
+
+/*
+ * Allocate a packet to send out ...
+ *
+ * Note that 'z' has dynamic packet resizing and that <cZblklen> will range
+ * from 32 to 1024, in multiples of 2.
+ */
+
+/*ARGSUSED*/
+char *
+zzgetspace(qdaemon, pclen)
+struct sdaemon *qdaemon;
+size_t *pclen;
+{
+ *pclen = cZblklen;
+ return zZtx_buf;
+}
+
+/*
+ * Send a block of data ...
+ *
+ * If (cdata == 0) then the end of the file has been reached.
+ */
+
+/*ARGSUSED*/
+boolean
+fzsenddata(qdaemon, zdata, cdata, ilocal, iremote, ipos)
+struct sdaemon *qdaemon;
+char *zdata;
+size_t cdata;
+int ilocal;
+int iremote;
+long ipos;
+{
+ DEBUG_MESSAGE1 (DEBUG_PROTO, "fzsenddata: %d bytes", cdata);
+
+ if (! fzsend_data (qdaemon, zdata, cdata, cdata == 0))
+ return FALSE;
+ return fzprocess (qdaemon);
+}
+
+/*
+ * Send a block of data (command or file) ...
+ */
+
+/* This should buffer the data internally. Until it does, it needs to
+ be able to reset the file position when it is called. This is
+ really ugly. */
+extern struct stransfer *qTsend;
+
+static boolean
+fzsend_data(qdaemon, zdata, cdata, fendofmessage)
+struct sdaemon *qdaemon;
+char *zdata;
+size_t cdata;
+boolean fendofmessage;
+{
+ size_t n;
+
+ if (iZlast_tx_data_packet == -1 || iZlast_tx_data_packet == ZCRCW) {
+ cZtxwcnt = cZrxwcnt = 0;
+ iZjunk_count = 0;
+ if (!fzsend_hdr (qdaemon, ZBIN, ZDATA,
+ hvzencode_data_hdr (wpZtxpos), TRUE))
+ return FALSE;
+ }
+
+ n = cdata;
+
+ if (fendofmessage)
+ iZlast_tx_data_packet = ZCRCF;
+ else if (iZjunk_count > 3)
+ iZlast_tx_data_packet = ZCRCW;
+ else if (wpZtxpos == wpZlastsync)
+ iZlast_tx_data_packet = ZCRCW;
+ else if (cZrx_buf_len && (cZrxwcnt += n) >= cZrx_buf_len)
+ iZlast_tx_data_packet = ZCRCW;
+ else if ((cZtxwcnt += n) >= cZtxwspac) {
+ iZlast_tx_data_packet = ZCRCQ;
+ cZtxwcnt = 0;
+ } else
+ iZlast_tx_data_packet = ZCRCG;
+
+ if (++iZtleft > 3) {
+ iZtleft = 0;
+ if (cZblklen < 1024)
+ cZblklen *= 2;
+#if 0 /* <cZblklen_override> is currently unnecessary */
+ if (cZblklen_override && cZblklen > cZblklen_override)
+ cZblklen = cZblklen_override;
+#endif
+ if (cZblklen > 1024)
+ cZblklen = 1024;
+ if (cZrx_buf_len && cZblklen > cZrx_buf_len)
+ cZblklen = cZrx_buf_len;
+ }
+
+#if DEBUG > 1
+ if (FDEBUGGING(DEBUG_PROTO)) {
+ const char *type;
+
+ switch (iZlast_tx_data_packet) {
+ case ZCRCW: type = "ZCRCW"; break;
+ case ZCRCG: type = "ZCRCG"; break;
+ case ZCRCQ: type = "ZCRCQ"; break;
+ case ZCRCE: type = "ZCRCE"; break;
+ case ZCRCF: type = "ZCRCF"; break;
+ default : type = "UNKNOWN!!!"; break;
+ }
+ DEBUG_MESSAGE3 (DEBUG_PROTO,
+ "fzsend_data: %s, pos 0x%lx, %d bytes",
+ type, wpZtxpos, n);
+ }
+#endif
+
+ if (!fzsend_data_packet (qdaemon, zdata, n, iZlast_tx_data_packet,
+ TRUE))
+ return FALSE;
+
+ wpZtxpos += n;
+
+ if (iZlast_tx_data_packet == ZCRCW) {
+ /*
+ * FIXME: Ideally this would be done in fzprocess. However, it
+ * is only called if there is data pending which there
+ * may not be yet. I could have patched fploop() a bit but
+ * for now, I've done it like this.
+ */
+ switch (getinsync (qdaemon, FALSE)) {
+ case ZACK:
+ break;
+ case ZRPOS:
+ if (qTsend == NULL
+ || ! ffileisopen (qTsend->e)) {
+ ulog (LOG_ERROR, "Can't reset non-file");
+ return FALSE;
+ }
+ iZlast_tx_data_packet = -1; /* trigger ZDATA */
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fzsend_data: Seeking to %ld",
+ (long) (wpZrxpos - wpZtxstart));
+ if (!ffileseek (qTsend->e, wpZrxpos - wpZtxstart)) {
+ ulog (LOG_ERROR, "seek: %s", strerror (errno));
+ return FALSE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ /*
+ * If we've reached the maximum transmit window size, let the
+ * receiver catch up ...
+ *
+ * I use (cZtx_window - 2048) to play it safe.
+ */
+
+ while (wpZtxpos - wpZlrxpos >= cZtx_window - 2048) {
+ if (iZlast_tx_data_packet != ZCRCQ) {
+ if (!fzsend_data_packet (qdaemon, zdata, (size_t) 0,
+ iZlast_tx_data_packet = ZCRCQ,
+ TRUE))
+ return FALSE;
+ }
+ /*
+ * FIXME: I'd rather not call ffileseek() in this file. When we
+ * start buffering the outgoing data, the following
+ * ffileseek() will disappear.
+ */
+ switch (getinsync (qdaemon, TRUE)) {
+ case ZACK:
+ break;
+ case ZRPOS:
+ if (qTsend == NULL
+ || ! ffileisopen (qTsend->e)) {
+ ulog (LOG_ERROR, "Can't reset non-file");
+ return FALSE;
+ }
+ iZlast_tx_data_packet = -1; /* trigger ZDATA */
+ DEBUG_MESSAGE1 (DEBUG_PROTO,
+ "fzsend_data: Seeking to %ld",
+ (long) (wpZrxpos - wpZtxstart));
+ if (!ffileseek (qTsend->e, wpZrxpos - wpZtxstart)) {
+ ulog (LOG_ERROR, "seek: %s", strerror (errno));
+ return FALSE;
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * Process existing data ...
+ */
+
+static boolean
+fzprocess(qdaemon)
+struct sdaemon *qdaemon;
+{
+ int c,ch;
+
+ while (fzreceive_ready ()) {
+ READCHAR (qdaemon, ch, 1);
+ switch (ch) {
+ case ZPAD:
+ /* see if we're detecting ZRPOS packets quickly */
+ DEBUG_MESSAGE0 (DEBUG_PROTO,
+ "fzprocess: possible ZRPOS packet");
+ /* We just ate the ZPAD char that getinsync
+ expects, so put it back. */
+ iPrecstart = ((iPrecstart + CRECBUFLEN - 1)
+ % CRECBUFLEN);
+ c = getinsync (qdaemon, TRUE);
+ if (c == ZACK)
+ break;
+ /* FIXME: sz does a TCFLSH here */
+#if 0 /* FIXME: Not sure if this is needed, or where to put it. */
+ /* ZCRCE - dinna wanna starta ping-pong game */
+ if (!fzsend_data_packet (qdaemon, zZtx_packet_buf,
+ 0, ZCRCE, TRUE))
+ return FALSE;
+#endif
+ if (c == ZRPOS) {
+ if (qTsend == NULL
+ || ! ffileisopen (qTsend->e)) {
+ ulog (LOG_ERROR,
+ "Attempt to back up non-file");
+ return FALSE;
+ }
+ if (! ffileseek (qTsend->e,
+ wpZrxpos - wpZtxstart)) {
+ ulog (LOG_ERROR,
+ "seek: %s", strerror (errno));
+ return FALSE;
+ }
+ iZlast_tx_data_packet = -1; /* trigger ZDATA */
+ break; /* not returning is intentional */
+ }
+ return FALSE;
+ case XOFF:
+ case XOFF | 0200:
+ READCHAR (qdaemon, ch, XON_WAIT);
+ break;
+ case CR:
+ break;
+ default:
+ iZjunk_count++;
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * Wait for data to come in.
+ *
+ * This continues processing until a complete file or command has been
+ * received.
+ */
+
+boolean
+fzwait(qdaemon)
+struct sdaemon *qdaemon;
+{
+ int c,cerr,rxcount;
+ boolean fexit;
+ achdrval_t rx_hdr;
+
+ if (!fzstart_rx ())
+ return FALSE;
+
+ cerr = cZretries;
+
+ goto nxthdr;
+
+ for (;;) {
+ if (!fzsend_hdr (qdaemon, ZHEX, ZRPOS,
+ hvzencode_data_hdr (wpZrxbytes), FALSE))
+ return FALSE;
+nxthdr:
+ c = izrecv_hdr (qdaemon, rx_hdr);
+
+ switch (c) {
+ case ZM_TIMEOUT:
+ case ZNAK:
+ if (--cerr < 0) {
+ ulog (LOG_ERROR, "fzwait: retries exhausted");
+ return FALSE;
+ }
+ continue;
+ case ZM_ERROR:
+ if (--cerr < 0) {
+ ulog (LOG_ERROR, "fzwait: retries exhausted");
+ return FALSE;
+ }
+ /*fport_break ();*/
+ continue;
+ case ZM_RCDO:
+ case ZFIN:
+ return FALSE;
+ case ZRPOS:
+ case ZACK:
+ goto nxthdr; /* ignore, partner is out of sync */
+ case ZDATA: {
+ winpos_t rx_bytes;
+
+ zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes);
+ DEBUG_MESSAGE2 (DEBUG_PROTO,
+ "fzwait: bytes(us,them) 0x%lx,0x%lx",
+ wpZrxbytes, rx_bytes);
+ if (rx_bytes != wpZrxbytes) {
+ if (--cerr < 0) {
+ ulog (LOG_ERROR,
+ "fzwait: retries exhausted");
+ return FALSE;
+ }
+ (void) zrdat32 (qdaemon, zZrx_packet_buf,
+ 1024, &rxcount);
+ /*fport_break ();*/
+ /*
+ * FIXME: Seems to me we should ignore this one
+ * and go for a timeout, the theory being
+ * that the appropriate ZRPOS has already
+ * been sent. We're obviously out of sync.
+ * /dje 92Mar10
+ */
+ continue; /* goto nxthdr? */
+ }
+moredata:
+ /*
+ * Do not call fgot_data() with (rxcount == 0) if it's
+ * not ZCRCF. fgot_data() will erroneously think this
+ * is the end of the message.
+ */
+ c = zrdat32 (qdaemon, zZrx_packet_buf, 1024,
+ &rxcount);
+#if DEBUG > 1
+ if (FDEBUGGING(DEBUG_PROTO)) {
+ const char *msg;
+
+ if (c < 0) {
+ msg = ZZHEADER_NAME(c);
+ } else {
+ switch (c) {
+ case GOTCRCW: msg = "ZCRCW"; break;
+ case GOTCRCG: msg = "ZCRCG"; break;
+ case GOTCRCQ: msg = "ZCRCQ"; break;
+ case GOTCRCE: msg = "ZCRCE"; break;
+ case GOTCRCF: msg = "ZCRCF"; break;
+ default : msg = NULL; break;
+ }
+ }
+ if (msg != NULL)
+ DEBUG_MESSAGE2 (DEBUG_PROTO,
+ "fzwait: zrdat32: %s, %d bytes",
+ msg, rxcount);
+ else
+ DEBUG_MESSAGE2 (DEBUG_PROTO,
+ "fzwait: zrdat32: %d, %d bytes",
+ c, rxcount);
+ }
+#endif
+ switch (c) {
+ case ZM_ERROR: /* CRC error */
+ cZerrors++;
+ if (--cerr < 0) {
+ ulog (LOG_ERROR,
+ "fzwait: retries exhausted");
+ return FALSE;
+ }
+ /*fport_break ();*/
+ continue;
+ case ZM_TIMEOUT:
+ cZtimeouts++;
+ if (--cerr < 0) {
+ ulog (LOG_ERROR,
+ "fzwait: retries exhausted");
+ return FALSE;
+ }
+ continue;
+ case ZM_RCDO:
+ return FALSE;
+ case GOTCRCW:
+ iZlast_rx_data_packet = ZCRCW;
+ cerr = cZretries;
+ if (rxcount != 0
+ && !fgot_data (qdaemon, zZrx_packet_buf,
+ (size_t) rxcount,
+ (const char *) NULL,
+ (size_t) 0,
+ -1, -1, (long) -1,
+ TRUE, &fexit))
+ return FALSE;
+ wpZrxbytes += rxcount;
+ if (!fzsend_hdr (qdaemon, ZHEX, ZACK,
+ hvzencode_data_hdr (wpZrxbytes),
+ FALSE))
+ return FALSE;
+ if (! fsend_data (qdaemon->qconn, &xon,
+ (size_t) 1, FALSE))
+ return FALSE;
+ goto nxthdr;
+ case GOTCRCQ:
+ iZlast_rx_data_packet = ZCRCQ;
+ cerr = cZretries;
+ if (rxcount != 0
+ && !fgot_data (qdaemon, zZrx_packet_buf,
+ (size_t) rxcount,
+ (const char *) NULL,
+ (size_t) 0,
+ -1, -1, (long) -1,
+ TRUE, &fexit))
+ return FALSE;
+ wpZrxbytes += rxcount;
+ if (!fzsend_hdr (qdaemon, ZHEX, ZACK,
+ hvzencode_data_hdr (wpZrxbytes),
+ FALSE))
+ return FALSE;
+ goto moredata;
+ case GOTCRCG:
+ iZlast_rx_data_packet = ZCRCG;
+ cerr = cZretries;
+ if (rxcount != 0
+ && !fgot_data (qdaemon, zZrx_packet_buf,
+ (size_t) rxcount,
+ (const char *) NULL,
+ (size_t) 0,
+ -1, -1, (long) -1,
+ TRUE, &fexit))
+ return FALSE;
+ wpZrxbytes += rxcount;
+ goto moredata;
+ case GOTCRCE:
+ iZlast_rx_data_packet = ZCRCE;
+ cerr = cZretries;
+ if (rxcount != 0
+ && !fgot_data (qdaemon, zZrx_packet_buf,
+ (size_t) rxcount,
+ (const char *) NULL,
+ (size_t) 0,
+ -1, -1, (long) -1,
+ TRUE, &fexit))
+ return FALSE;
+ wpZrxbytes += rxcount;
+ goto nxthdr;
+ case GOTCRCF:
+ iZlast_rx_data_packet = ZCRCF;
+ /*
+ * fzfinish_rx() must be called before
+ * fgot_data() because fgot_data() will send
+ * out a UUCP-command but the sender won't be
+ * ready for it until it receives our final
+ * ZACK.
+ */
+ cerr = cZretries;
+ wpZrxbytes += rxcount;
+ if (!fzfinish_rx (qdaemon))
+ return FALSE;
+ if (!fgot_data (qdaemon, zZrx_packet_buf,
+ (size_t) rxcount,
+ (const char *) NULL,
+ (size_t) 0, -1, -1,
+ (long) -1, TRUE, &fexit))
+ return FALSE;
+ /*
+ * FIXME: Examine <fexit>?
+ * Or maybe ensure it's TRUE?
+ */
+ return TRUE;
+ }
+ return FALSE;
+ }
+ default:
+ ulog (LOG_FATAL, "fzwait: received header %s",
+ ZZHEADER_NAME(c));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * File level routine. Called when initiating/terminating file transfers.
+ *
+ * When starting to send a file: (TRUE, TRUE, cbytes)
+ * When starting to receive a file: (TRUE, FALSE, -1)
+ * When send EOF, check resend: (FALSE, TRUE, -1)
+ * When receive EOF, check re-receive: (FALSE, FALSE, -1)
+ */
+
+boolean
+fzfile(qdaemon, qtrans, fstart, fsend, cbytes, pfhandled)
+struct sdaemon *qdaemon;
+struct stransfer *qtrans;
+boolean fstart;
+boolean fsend;
+long cbytes;
+boolean *pfhandled;
+{
+ long iredo;
+
+ *pfhandled = FALSE;
+
+ DEBUG_MESSAGE2 (DEBUG_PROTO, "fzfile: fstart=%d, fsend=%d", fstart,
+ fsend);
+
+ if (fsend) {
+ if (fstart)
+ return fzstart_tx ();
+ if (! fzfinish_tx (qdaemon, &iredo))
+ return FALSE;
+ if (iredo >= 0) {
+ if (! ffileisopen (qtrans->e)) {
+ ulog (LOG_ERROR,
+ "Attempt to back up non-file");
+ return FALSE;
+ }
+ if (! ffileseek (qtrans->e, iredo)) {
+ ulog (LOG_ERROR,
+ "seek: %s", strerror (errno));
+ return FALSE;
+ }
+ *pfhandled = TRUE;
+ qtrans->fsendfile = TRUE;
+ return fqueue_send (qdaemon, qtrans);
+ }
+ }
+
+ return TRUE;
+}
+
+/****************************************************************************/
+
+
+#if 0 /* not used, we only use 32 bit crc's */
+/*
+ * crctab calculated by Mark G. Mendel, Network Systems Corporation
+ */
+
+static unsigned short crctab[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
+};
+#endif /* crctab */
+
+/*
+ * Copyright (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+
+/* First, the polynomial itself and its table of feedback terms. The */
+/* polynomial is */
+/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
+/* Note that we take it "backwards" and put the highest-order term in */
+/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */
+/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */
+/* the MSB being 1. */
+
+/* Note that the usual hardware shift register implementation, which */
+/* is what we're using (we're merely optimizing it by doing eight-bit */
+/* chunks at a time) shifts bits into the lowest-order term. In our */
+/* implementation, that means shifting towards the right. Why do we */
+/* do it this way? Because the calculated CRC must be transmitted in */
+/* order from highest-order term to lowest-order term. UARTs transmit */
+/* characters in order from LSB to MSB. By storing the CRC this way, */
+/* we hand it to the UART in the order low-byte to high-byte; the UART */
+/* sends each low-bit to hight-bit; and the result is transmission bit */
+/* by bit from highest- to lowest-order term without requiring any bit */
+/* shuffling on our part. Reception works similarly. */
+
+/* The feedback terms table consists of 256, 32-bit entries. Notes: */
+/* */
+/* The table can be generated at runtime if desired; code to do so */
+/* is shown later. It might not be obvious, but the feedback */
+/* terms simply represent the results of eight shift/xor opera- */
+/* tions for all combinations of data and CRC register values. */
+/* */
+/* The values must be right-shifted by eight bits by the "updcrc" */
+/* logic; the shift must be unsigned (bring in zeroes). On some */
+/* hardware you could probably optimize the shift in assembler by */
+/* using byte-swap instructions. */
+
+static unsigned long crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
+ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL,
+ 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L,
+ 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L,
+ 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L,
+ 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,
+ 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL,
+ 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L,
+ 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L,
+ 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L,
+ 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L,
+ 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L,
+ 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL,
+ 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL,
+ 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL,
+ 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,
+ 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L,
+ 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL,
+ 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L,
+ 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL,
+ 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,
+ 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL,
+ 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L,
+ 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L,
+ 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L,
+ 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L,
+ 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL,
+ 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL,
+ 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L,
+ 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,
+ 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL,
+ 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L,
+ 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL,
+ 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L,
+ 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,
+ 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L,
+ 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L,
+ 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL,
+ 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L,
+ 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,
+ 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL,
+ 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L,
+ 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L,
+ 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL,
+ 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,
+ 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L,
+ 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L,
+ 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL,
+ 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L,
+ 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L,
+ 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL,
+ 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L,
+ 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL
+};
+
+/*
+ * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell.
+ * NOTE: First argument must be in range 0 to 255.
+ * Second argument is referenced twice.
+ *
+ * Programmers may incorporate any or all code into their programs,
+ * giving proper credit within the source. Publication of the
+ * source routines is permitted so long as proper credit is given
+ * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg,
+ * Omen Technology.
+ */
+
+#define updcrc(cp, crc) (crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)
+
+#define UPDC32(b, crc) \
+ (crc_32_tab[((unsigned)(crc) ^ (unsigned)(b)) & 0xff] \
+ ^ (((crc) >> 8) & 0x00ffffffL))
+
+/****************************************************************************/
+
+/*
+ * This section contains the guts of the Zmodem protocol. The intention
+ * is to leave as much of it alone as possible at the start. Overtime it
+ * will be cleaned up (EG: I'd like to clean up the naming of the globals).
+ * Also, Zmodem has a different coding style. Over time this will be converted
+ * to the Taylor UUCP coding style.
+ */
+
+/*
+ * Start the protocol (exchange init packets) ...
+ *
+ * UUCP can transfer files in both directions in one session. Therefore the
+ * init sequence is a little different.
+ *
+ * 1) ZINIT packets are exchanged
+ * - contains protocol version and protocol flags
+ * 2) ZDATA packets are exchanged
+ * - is intended to contain various numeric and string information
+ * 3) ZACK packets are exchanged
+ * 4) ZINITEND packets are exchanged
+ */
+
+static boolean
+fzstart_proto(qdaemon)
+struct sdaemon *qdaemon;
+{
+ int i;
+ achdrval_t tx_hdr,rx_hdr;
+
+ for (i = 0; i < cZstartup_retries; i++) {
+ stohdr (0L, tx_hdr);
+ tx_hdr[ZF0] = ZPROTOCOL_VERSION;
+ if (fZesc_ctl)
+ tx_hdr[ZF1] |= TX_ESCCTL;
+ switch (izexchange_init (qdaemon, ZINIT, tx_hdr, rx_hdr)) {
+ case -1: return FALSE;
+ case 0: continue;
+ case 1: break;
+ }
+#if 0 /* can't work, but kept for documentation */
+ if (rx_hdr[ZF0] == 0) {
+ ulog (LOG_ERROR, "Old protocol version, init failed");
+ return FALSE;
+ }
+#endif
+ fZesc_ctl = fZesc_ctl || (rx_hdr[ZF1] & TX_ESCCTL) != 0;
+
+ stohdr (0L, tx_hdr);
+ switch (izexchange_init (qdaemon, ZDATA, tx_hdr, rx_hdr)) {
+ case -1: return FALSE;
+ case 0: continue;
+ case 1: break;
+ }
+
+ stohdr (0L, tx_hdr);
+ switch (izexchange_init (qdaemon, ZACK, tx_hdr, rx_hdr)) {
+ case -1: return FALSE;
+ case 0: continue;
+ case 1: break;
+ }
+
+ stohdr (0L, tx_hdr);
+ switch (izexchange_init (qdaemon, ZINITEND, tx_hdr, rx_hdr)) {
+ case -1: return FALSE;
+ case 0: continue;
+ case 1: break;
+ }
+
+ DEBUG_MESSAGE0 (DEBUG_PROTO,
+ "fzstart_proto: Protocol started");
+ return TRUE;
+
+ /* FIXME: see protg.c regarding sequencing here. */
+ }
+
+ ulog (LOG_ERROR, "Protocol init failed");
+ return FALSE;
+}
+
+/*
+ * Exchange init messages. This is based on 'g'.
+ * See the comments concerning fgexchange_init() in protg.c.
+ *
+ * We return 1 for success, 0 for restart, -1 for comm failure (terminate).
+ */
+
+static int
+izexchange_init(qdaemon, send_type, send_val, recv_val)
+struct sdaemon *qdaemon;
+int send_type;
+achdrval_t send_val;
+achdrval_t recv_val;
+{
+ int i,recv_type,count;
+
+ for (i = 0; i < CEXCHANGE_INIT_RETRIES; i++) {
+ if (!fzsend_hdr (qdaemon, send_type == ZDATA ? ZBIN : ZHEX,
+ send_type, rclhdr (send_val), FALSE))
+ return -1;
+
+ /*
+ * The ZDATA packet is intended to contain the <Attn> string
+ * (eventually, if it's ever usable) and allow for anything
+ * else that will need to be thrown in.
+ */
+
+ if (send_type == ZDATA) {
+ count = czbuild_data_packet (zZtx_packet_buf, "",
+ (size_t) 1, ZCRCF);
+ if (!fsend_data (qdaemon->qconn, zZtx_packet_buf,
+ (size_t) count, FALSE))
+ return -1;
+ }
+
+ recv_type = izrecv_hdr (qdaemon, recv_val);
+
+ switch (recv_type) {
+ case ZM_TIMEOUT:
+ case ZM_ERROR:
+ continue;
+ case ZM_RCDO:
+ case ZFIN:
+ return -1;
+ case ZINIT:
+ case ZACK:
+ case ZINITEND:
+ break;
+ case ZDATA:
+ if (zrdat32 (qdaemon, zZrx_packet_buf, 1024, &count)
+ == GOTCRCF)
+ break;
+ continue;
+ default:
+ continue;
+ }
+
+ if (recv_type == send_type)
+ return 1;
+
+ /*
+ * If the other side is farther along than we are, we have lost
+ * a packet. Fall immediately back to ZINIT (but don't fail
+ * if we are already doing ZINIT, since that would count
+ * against cStart_retries more than it should).
+ *
+ * FIXME: The ">" test is "<" in protg.c. Check who's right.
+ */
+
+ if (recv_type > send_type && send_type != ZINIT)
+ return 0;
+
+ /*
+ * If we are sending ZINITEND and we receive an ZINIT, the
+ * other side has falled back (we know this because we have
+ * seen a ZINIT from them). Fall back ourselves to start
+ * the whole handshake over again.
+ */
+
+ if (recv_type == ZINIT && send_type == ZINITEND)
+ return 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Shut down the protocol ...
+ */
+
+static boolean
+fzshutdown_proto(qdaemon)
+struct sdaemon *qdaemon;
+{
+ (void) fzsend_hdr (qdaemon, ZHEX, ZFIN, 0L, FALSE);
+ return TRUE;
+}
+
+/*
+ * Reset the transmitter side for sending a new message ...
+ */
+
+static boolean
+fzstart_tx()
+{
+ iZlast_tx_data_packet = -1;
+
+ /*
+ * <wpZlastsync> is set to -1L to suppress ZCRCW request otherwise
+ * triggered by (wpZlastsync == wpZtxpos).
+ */
+
+ cZblklen = 1024;
+ wpZlastsync = -1L;
+ iZbeenhereb4 = 0;
+ iZtleft = 0;
+ iZjunk_count = 0;
+
+ wpZtxpos = (wpZtxpos + 1024L) & ~1023L; /* next packet boundary */
+ wpZlrxpos = wpZrxpos = wpZtxpos;
+
+ wpZtxstart = wpZtxpos; /* so we can compute the "file offset" */
+
+ return TRUE;
+}
+
+/*
+ * Finish the sending of a message ...
+ *
+ * Basically, we wait for some indication that the receiver received our last
+ * message. If the receiver tells us to restart from some point, we set
+ * *plredo to that point.
+ *
+ * FIXME: This function is a major kludge at the moment. It is taken from
+ * getinsync(). It is necessary because I don't yet buffer outgoing data.
+ * It will go away when we do (buffer outgoing data).
+ */
+
+static boolean
+fzfinish_tx(qdaemon, plredo)
+struct sdaemon *qdaemon;
+long *plredo;
+{
+ int c,cerr,ctimeouts;
+ achdrval_t rx_hdr;
+ winpos_t rx_bytes;
+
+ *plredo = -1;
+ cerr = cZretries;
+ ctimeouts = 0;
+
+ DEBUG_MESSAGE4 (DEBUG_PROTO,
+ "fzfinish_tx: txpos=0x%lx, rxpos=0x%lx, lrxpos=0x%lx, rxbytes=0x%lx",
+ wpZtxpos, wpZrxpos, wpZlrxpos, wpZrxbytes);
+
+ for (;;) {
+ c = izrecv_hdr (qdaemon, rx_hdr);
+
+ switch (c) {
+ case ZRPOS:
+ wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos,
+ wpZlrxpos, wpZtxpos);
+ /*
+ * If the receiver sends a ZRPOS for the 1k block after
+ * the one we're currently at, we lost the final ZACK.
+ * We cheat and ignore this ZRPOS. Remember: the theory
+ * is that this entire function will go away when we
+ * begin buffering the outgoing data. Of course, one
+ * can reword the protocol definition and say this
+ * isn't cheating at all.
+ */
+ if (((wpZtxpos + 1024) & ~1023) == wpZrxpos)
+ return TRUE;
+ cZbytes_resent += wpZtxpos - wpZrxpos;
+ wpZlrxpos = wpZtxpos = wpZrxpos;
+ if (wpZlastsync == wpZrxpos) {
+ if (++iZbeenhereb4 > 4)
+ if (cZblklen > 32)
+ cZblklen /= 2;
+ /* FIXME: shouldn't we reset iZbeenhereb4? */
+ }
+ wpZlastsync = wpZrxpos;
+ iZlast_tx_data_packet = ZCRCW; /* force a timeout */
+ *plredo = wpZrxpos - wpZtxstart;
+ return TRUE;
+ case ZACK:
+ wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos,
+ wpZlrxpos, wpZtxpos);
+ wpZlrxpos = wpZrxpos;
+ if (wpZtxpos == wpZrxpos) /* the ACK we want? */
+ return TRUE;
+ break;
+ case ZDATA:
+ /*
+ * We cheat here and take advantage of UUCP's current
+ * half duplex nature. If we get a ZDATA starting on
+ * the next 1k boundary, we lost the ZACK. We cheat and
+ * tuck it away so that izrecv_hdr() can later detect
+ * it. Remember: see above.
+ */
+ zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes);
+ if (((wpZrxbytes + 1024L) & ~1023L) == rx_bytes) {
+ iZpkt_rcvd_kludge = ZDATA;
+ hvZpkt_hdrval_kludge = rclhdr (rx_hdr);
+ return TRUE;
+ }
+ break; /* ignore, out of sync (old) */
+ case ZNAK:
+ /*
+ * We cheat here and take advantage of UUCP's current
+ * half duplex nature. If we get a ZNAK starting on
+ * the next 1k boundary, we lost the ZACK. We cheat and
+ * throw the ZNAK away. Remember: see above.
+ *
+ * On the other hand, if (rx_bytes == wpZrxbytes) then
+ * the other side is also in fzfinish_tx(). He must
+ * have lost our ZACK, so we send him another.
+ */
+ zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes);
+ if (((wpZrxbytes + 1024L) & ~1023L) == rx_bytes)
+ return TRUE;
+ if (rx_bytes == wpZrxbytes) {
+ if (!fzsend_hdr (qdaemon, ZHEX, ZACK,
+ hvzencode_data_hdr (wpZrxbytes),
+ TRUE))
+ return FALSE;
+ }
+ break; /* ignore, out of sync (old) */
+ case ZFIN:
+ case ZM_RCDO:
+ return FALSE;
+ case ZM_TIMEOUT:
+ if (--cerr < 0) {
+ ulog (LOG_ERROR,
+ "fzfinish_tx: retries exhausted");
+ return FALSE;
+ }
+ /*
+ * Normally the sender doesn't send NAK's for timeouts.
+ * We have to here because of the following scenario:
+ *
+ * - We send ZDATA/ZCRCF
+ * - They send ZACK (corrupted)
+ * - They send ZDATA/ZCRCF (corrupted)
+ *
+ * At this point, both sides are in fzfinish_tx().
+ * We only send ZNAK every second timeout to increase
+ * our timeout delay vs. our partner. This tries to
+ * avoid ZRPOS and ZNAK "passing in transit".
+ */
+ if (++ctimeouts % 2 == 0)
+ if (!fzsend_hdr (qdaemon, ZHEX, ZNAK,
+ hvzencode_data_hdr (wpZtxpos),
+ TRUE))
+ return FALSE;
+ break;
+ case ZM_ERROR:
+ default:
+ if (--cerr < 0) {
+ ulog (LOG_ERROR,
+ "fzfinish_tx: retries exhausted");
+ return FALSE;
+ }
+ if (!fzsend_hdr (qdaemon, ZHEX, ZNAK,
+ hvzencode_data_hdr (wpZtxpos),
+ TRUE))
+ return FALSE;
+ break;
+ }
+ }
+}
+
+/*
+ * Initialize the receiver ...
+ */
+
+static boolean
+fzstart_rx()
+{
+ wpZrxbytes = (wpZrxbytes + 1024L) & ~1023L; /* next packet boundary */
+
+ return TRUE;
+}
+
+/*
+ * Terminate the receiver ...
+ *
+ * Acknowledge the last packet received.
+ */
+
+static boolean
+fzfinish_rx(qdaemon)
+struct sdaemon *qdaemon;
+{
+ DEBUG_MESSAGE0 (DEBUG_PROTO, "fzfinish_rx: message/file received");
+
+ return fzsend_hdr (qdaemon, ZHEX, ZACK,
+ hvzencode_data_hdr (wpZrxbytes), FALSE);
+}
+
+/*
+ * Send a Zmodem header to our partner ...
+ */
+
+static boolean
+fzsend_hdr(qdaemon, ipkttype, ihdrtype, hdrval, fcheckreceive)
+struct sdaemon *qdaemon;
+int ipkttype;
+int ihdrtype;
+hdrval_t hdrval;
+boolean fcheckreceive;
+{
+ int cpacketlen;
+
+ DEBUG_MESSAGE2 (DEBUG_PROTO, "fzsend_hdr: %s, data = 0x%lx",
+ ZZHEADER_NAME(ihdrtype), hdrval);
+
+ cpacketlen = czbuild_header (zZtx_packet_buf, ipkttype,
+ ihdrtype, hdrval);
+
+#ifdef DJE_TESTING
+#if 0
+ if (ihdrtype == ZACK && rand () % 100 < uucptest2) {
+ cZheaders_sent++;
+ return TRUE;
+ }
+#else
+ if (ihdrtype == ZACK || ihdrtype == ZDATA) {
+ boolean fresult;
+ int old;
+ extern int uucptest,uucptest2;
+
+ old = uucptest;
+ uucptest = uucptest2;
+ cZheaders_sent++;
+ fresult = fsend_data (qdaemon->qconn, zZtx_packet_buf,
+ (size_t) cpacketlen, fcheckreceive);
+ uucptest = old;
+ return fresult;
+ }
+#endif
+#endif
+ cZheaders_sent++;
+ return fsend_data (qdaemon->qconn, zZtx_packet_buf,
+ (size_t) cpacketlen, fcheckreceive);
+}
+
+/*
+ * Send a data packet to our partner ...
+ * <frameend> is one of ZCRCx.
+ */
+
+static boolean
+fzsend_data_packet(qdaemon, zdata, cdata, frameend, fcheckreceive)
+struct sdaemon *qdaemon;
+char *zdata;
+size_t cdata;
+int frameend;
+boolean fcheckreceive;
+{
+ int cpacketlen;
+
+ cpacketlen = czbuild_data_packet (zZtx_packet_buf, zdata, cdata,
+ frameend);
+
+ return fsend_data (qdaemon->qconn, zZtx_packet_buf,
+ (size_t) cpacketlen, fcheckreceive);
+}
+
+/*
+ * Build Zmodem headers ...
+ *
+ * Note that we use 32 bit CRC's for ZHEX headers.
+ *
+ * This function is a combination of zm fns: zsbhdr(), zsbh32(), and zshhdr().
+ */
+
+static int
+czbuild_header(zresult, ipkttype, ihdrtype, hdrval)
+char *zresult;
+int ipkttype;
+int ihdrtype;
+hdrval_t hdrval;
+{
+ char *p;
+ int i;
+ unsigned long crc;
+ achdrval_t achdrval;
+
+ p = zresult;
+
+ switch (ipkttype) {
+ case ZBIN:
+ *p++ = ZPAD;
+ *p++ = ZDLE;
+ *p++ = ZBIN;
+ p = zputchar (p, ihdrtype);
+ crc = ICRCINIT;
+ crc = UPDC32 (ihdrtype, crc);
+ stohdr (hdrval, achdrval);
+ for (i = 0; i < 4; i++) {
+ p = zputchar (p, achdrval[i]);
+ crc = UPDC32 (achdrval[i], crc);
+ }
+ crc = ~crc;
+ for (i = 0; i < 4; i++) {
+ p = zputchar (p, (char) crc);
+ crc >>= 8;
+ }
+ break;
+ case ZHEX: /* build hex header */
+ *p++ = ZPAD;
+ *p++ = ZPAD;
+ *p++ = ZDLE;
+ *p++ = ZHEX;
+ p = zputhex (p, ihdrtype);
+ crc = ICRCINIT;
+ crc = UPDC32 (ihdrtype, crc);
+ stohdr (hdrval, achdrval);
+ for (i = 0; i < 4; i++) {
+ p = zputhex (p, achdrval[i]);
+ crc = UPDC32 (achdrval[i], crc);
+ }
+ crc = ~crc;
+ for (i = 0; i < 4; i++) {
+ p = zputhex (p, (char) crc);
+ crc >>= 8;
+ }
+ *p++ = CR;
+ /*
+ * Uncork the remote in case a fake XOFF has stopped data flow.
+ */
+ if (ihdrtype != ZFIN && ihdrtype != ZACK) /* FIXME: why? */
+ *p++ = XON;
+ break;
+ default:
+ ulog (LOG_FATAL, "czbuild_header: ipkttype == %d", ipkttype);
+ break;
+ }
+
+ return p - zresult;
+}
+
+/*
+ * Build Zmodem data packets ...
+ *
+ * This function is zsdata() and zsda32() from the zm source.
+ */
+
+static int
+czbuild_data_packet(zresult, zdata, cdata, frameend)
+char *zresult;
+const char *zdata;
+size_t cdata;
+int frameend;
+{
+ char *p;
+ unsigned long crc;
+
+ p = zresult;
+
+ crc = ICRCINIT;
+ for ( ; cdata-- != 0; zdata++) {
+ char c;
+
+ c = *zdata;
+ if (c & 0140)
+ *p++ = c;
+ else
+ p = zputchar (p, c);
+ crc = UPDC32 ((unsigned char) c, crc);
+ }
+ *p++ = ZDLE;
+ *p++ = frameend;
+ crc = UPDC32 (frameend, crc);
+ crc = ~crc;
+ for (cdata = 0; cdata < 4; cdata++) {
+ p = zputchar (p, (char) crc);
+ crc >>= 8;
+ }
+ if (frameend == ZCRCW || frameend == ZCRCE || frameend == ZCRCF) {
+ *p++ = CR;
+ *p++ = XON;
+ }
+
+ return p - zresult;
+}
+
+/*
+ * Read in a header ...
+ *
+ * This is function zgethdr() from the Zmodem source.
+ */
+
+static int
+izrecv_hdr(qdaemon, hdr)
+struct sdaemon *qdaemon;
+achdrval_t hdr;
+{
+ int c,cerr;
+
+ /*
+ * Kludge alert! If another part of the program received a packet but
+ * wasn't ready to handle it, it is tucked away for us to handle now.
+ */
+
+ if (iZpkt_rcvd_kludge != -1) {
+ c = iZpkt_rcvd_kludge;
+ iZpkt_rcvd_kludge = -1;
+ stohdr (hvZpkt_hdrval_kludge, hdr);
+ DEBUG_MESSAGE2 (DEBUG_PROTO,
+ "izrecv_hdr: queued %s, data = 0x%lx",
+ ZZHEADER_NAME(c), rclhdr (hdr));
+ cZheaders_received++;
+ return c;
+ }
+
+ cerr = cZmax_garbage; /* Max bytes before start of frame */
+
+again:
+ switch (c = noxrd7 (qdaemon)) {
+ case ZM_TIMEOUT:
+ case ZM_ERROR:
+ case ZM_RCDO:
+ goto fifi;
+ case ZPAD: /* This is what we want */
+ break;
+ case CR: /* padding at end of previous header */
+ default:
+ if (--cerr < 0) {
+ c = ZM_ERROR;
+ goto fifi;
+ }
+ goto again;
+ }
+
+splat:
+ switch (c = noxrd7 (qdaemon)) {
+ case ZPAD:
+ if (--cerr < 0) {
+ c = ZM_ERROR;
+ goto fifi;
+ }
+ goto splat;
+ case ZM_TIMEOUT:
+ case ZM_RCDO:
+ goto fifi;
+ case ZDLE: /* This is what we want */
+ break;
+ default:
+ if (--cerr < 0) {
+ c = ZM_ERROR;
+ goto fifi;
+ }
+ goto again;
+ }
+
+ switch (c = noxrd7 (qdaemon)) {
+ case ZM_TIMEOUT:
+ case ZM_RCDO:
+ goto fifi;
+ case ZBIN:
+ c = zrbhdr32 (qdaemon, hdr);
+ break;
+ case ZHEX:
+ c = zrhhdr (qdaemon, hdr);
+ break;
+ default:
+ if (--cerr < 0) {
+ c = ZM_ERROR;
+ goto fifi;
+ }
+ goto again;
+ }
+
+fifi:
+ switch (c) {
+ case ZM_TIMEOUT:
+ cZtimeouts++;
+ break;
+ case ZM_ERROR:
+ cZerrors++;
+ break;
+ case ZM_RCDO:
+ break;
+ default:
+ cZheaders_received++;
+ break;
+ }
+ DEBUG_MESSAGE2 (DEBUG_PROTO, "izrecv_hdr: %s, data = 0x%x",
+ ZZHEADER_NAME(c), rclhdr (hdr));
+
+ return c;
+}
+
+/*
+ * Receive a binary style header (type and position) with 32 bit FCS ...
+ */
+
+static int
+zrbhdr32(qdaemon, hdr)
+struct sdaemon *qdaemon;
+achdrval_t hdr;
+{
+ int c,i,type;
+ unsigned long crc;
+
+ if ((c = zdlread (qdaemon)) & ~0377)
+ return c;
+ type = c;
+ crc = ICRCINIT;
+ crc = UPDC32 (c, crc);
+
+ for (i = 0; i < 4; i++) {
+ if ((c = zdlread (qdaemon)) & ~0377)
+ return c;
+ crc = UPDC32 (c, crc);
+ hdr[i] = (char) c;
+ }
+ for (i = 0; i < 4; i++) {
+ if ((c = zdlread (qdaemon)) & ~0377)
+ return c;
+ crc = UPDC32 (c, crc);
+ }
+ if (crc != IHDRCRC)
+ return ZM_ERROR;
+
+ return type;
+}
+
+/*
+ * Receive a hex style header (type and position) ...
+ */
+
+static int
+zrhhdr(qdaemon, hdr)
+struct sdaemon *qdaemon;
+achdrval_t hdr;
+{
+ int c,i,type;
+ unsigned long crc;
+
+ if ((c = zgethex (qdaemon)) < 0)
+ return c;
+ type = c;
+ crc = ICRCINIT;
+ crc = UPDC32 (c, crc);
+
+ for (i = 0; i < 4; i++) {
+ if ((c = zgethex (qdaemon)) < 0)
+ return c;
+ crc = UPDC32 (c, crc);
+ hdr[i] = (char) c;
+ }
+ for (i = 0; i < 4; i++) {
+ if ((c = zgethex (qdaemon)) < 0)
+ return c;
+ crc = UPDC32 (c, crc);
+ }
+ if (crc != IHDRCRC)
+ return ZM_ERROR;
+
+ return type;
+}
+
+/*
+ * Receive a data packet ...
+ */
+
+static int
+zrdat32(qdaemon, buf, length, iprxcount)
+struct sdaemon *qdaemon;
+char *buf;
+int length;
+int *iprxcount;
+{
+ int c,d;
+ unsigned long crc;
+ char *end;
+
+ crc = ICRCINIT;
+ *iprxcount = 0;
+ end = buf + length;
+ while (buf <= end) {
+ if ((c = zdlread (qdaemon)) & ~0377) {
+crcfoo:
+ switch (c) {
+ case GOTCRCE:
+ case GOTCRCG:
+ case GOTCRCQ:
+ case GOTCRCW:
+ case GOTCRCF:
+ d = c;
+ c &= 0377;
+ crc = UPDC32 (c, crc);
+ if ((c = zdlread (qdaemon)) & ~0377)
+ goto crcfoo;
+ crc = UPDC32 (c, crc);
+ if ((c = zdlread (qdaemon)) & ~0377)
+ goto crcfoo;
+ crc = UPDC32 (c, crc);
+ if ((c = zdlread (qdaemon)) & ~0377)
+ goto crcfoo;
+ crc = UPDC32 (c, crc);
+ if ((c = zdlread (qdaemon)) & ~0377)
+ goto crcfoo;
+ crc = UPDC32 (c, crc);
+ if (crc != IHDRCRC)
+ return ZM_ERROR;
+ *iprxcount = length - (end - buf);
+ return d;
+ case ZM_TIMEOUT:
+ case ZM_RCDO:
+ return c;
+ default:
+ return ZM_ERROR;
+ }
+ }
+ *buf++ = (char) c;
+ crc = UPDC32 (c, crc);
+ }
+
+ return ZM_ERROR; /* bad packet, too long */
+}
+
+/*
+ * Respond to receiver's complaint, get back in sync with receiver ...
+ */
+
+static int
+getinsync(qdaemon, flag)
+struct sdaemon *qdaemon;
+boolean flag;
+{
+ int c,cerr;
+ achdrval_t rx_hdr;
+
+ cerr = cZretries;
+
+ for (;;) {
+ c = izrecv_hdr (qdaemon, rx_hdr);
+
+ switch (c) {
+ case ZRPOS:
+ wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos,
+ wpZlrxpos, wpZtxpos);
+ cZbytes_resent += wpZtxpos - wpZrxpos;
+ wpZlrxpos = wpZtxpos = wpZrxpos;
+ if (wpZlastsync == wpZrxpos) {
+ if (++iZbeenhereb4 > 4)
+ if (cZblklen > 32)
+ cZblklen /= 2;
+ /* FIXME: shouldn't we reset iZbeenhereb4? */
+ }
+ wpZlastsync = wpZrxpos;
+ return ZRPOS;
+ case ZACK:
+ wpZrxpos = lzupdate_rxpos (rx_hdr, wpZrxpos,
+ wpZlrxpos, wpZtxpos);
+ wpZlrxpos = wpZrxpos;
+ if (flag || wpZtxpos == wpZrxpos)
+ return ZACK;
+ break;
+ case ZNAK: {
+ winpos_t rx_bytes;
+ /*
+ * Our partner is in fzfinish_tx() and is waiting
+ * for ZACK ...
+ */
+ zdecode_data_hdr (rclhdr (rx_hdr), &rx_bytes);
+ if (rx_bytes == wpZrxbytes) {
+ if (!fzsend_hdr (qdaemon, ZHEX, ZACK,
+ hvzencode_data_hdr (wpZrxbytes),
+ TRUE))
+ return FALSE;
+ }
+ break;
+ }
+ case ZFIN:
+ case ZM_RCDO:
+ return c;
+ case ZM_TIMEOUT:
+ if (--cerr < 0) {
+ ulog (LOG_ERROR,
+ "getinsync: retries exhausted");
+ return ZM_ERROR;
+ }
+ break; /* sender doesn't send ZNAK for timeout */
+ case ZM_ERROR:
+ default:
+ if (--cerr < 0) {
+ ulog (LOG_ERROR,
+ "getinsync: retries exhausted");
+ return ZM_ERROR;
+ }
+ if (!fzsend_hdr (qdaemon, ZHEX, ZNAK,
+ hvzencode_data_hdr (wpZtxpos),
+ TRUE))
+ return ZM_ERROR;
+ break;
+ }
+ }
+}
+
+/*
+ * Send a byte as two hex digits ...
+ */
+
+static char *
+zputhex(p, ch)
+char *p;
+int ch;
+{
+ static char digits[] = "0123456789abcdef";
+
+ *p++ = digits[(ch & 0xF0) >> 4];
+ *p++ = digits[ch & 0xF];
+ return p;
+}
+
+/*
+ * Send character c with ZMODEM escape sequence encoding ...
+ *
+ * Escape XON, XOFF.
+ * FIXME: Escape CR following @ (Telenet net escape) ... disabled for now
+ * Will need to put back references to <lastsent>.
+ */
+
+static char *
+zputchar(p, ch)
+char *p;
+int ch;
+{
+ char c = ch;
+
+ /* Quick check for non control characters */
+
+ if (c & 0140) {
+ *p++ = c;
+ } else {
+ switch (c & 0377) {
+ case ZDLE:
+ *p++ = ZDLE;
+ *p++ = c ^ 0100;
+ break;
+ case CR:
+#if 0
+ if (!fZesc_ctl && (lastsent & 0177) != '@')
+ goto sendit;
+#endif
+ /* fall through */
+ case 020: /* ^P */
+ case XON:
+ case XOFF:
+ *p++ = ZDLE;
+ c ^= 0100;
+/*sendit:*/
+ *p++ = c;
+ break;
+ default:
+ if (fZesc_ctl && !(c & 0140)) {
+ *p++ = ZDLE;
+ c ^= 0100;
+ }
+ *p++ = c;
+ break;
+ }
+ }
+
+ return p;
+}
+
+/*
+ * Decode two lower case hex digits into an 8 bit byte value ...
+ */
+
+static int
+zgethex(qdaemon)
+struct sdaemon *qdaemon;
+{
+ int c,n;
+
+ if ((c = noxrd7 (qdaemon)) < 0)
+ return c;
+ n = c - '0';
+ if (n > 9)
+ n -= ('a' - ':');
+ if (n & ~0xF)
+ return ZM_ERROR;
+ if ((c = noxrd7 (qdaemon)) < 0)
+ return c;
+ c -= '0';
+ if (c > 9)
+ c -= ('a' - ':');
+ if (c & ~0xF)
+ return ZM_ERROR;
+ c += (n << 4);
+
+ return c;
+}
+
+/*
+ * Read a byte, checking for ZMODEM escape encoding ...
+ */
+
+static int
+zdlread(qdaemon)
+struct sdaemon *qdaemon;
+{
+ int c;
+
+again:
+ READCHAR (qdaemon, c, cZtimeout);
+ if (c < 0)
+ return c;
+ if (c & 0140) /* quick check for non control characters */
+ return c;
+ switch (c) {
+ case ZDLE:
+ break;
+ case XON:
+ goto again;
+ case XOFF:
+ READCHAR (qdaemon, c, XON_WAIT);
+ goto again;
+ default:
+ if (fZesc_ctl && !(c & 0140))
+ goto again;
+ return c;
+ }
+
+again2:
+ READCHAR (qdaemon, c, cZtimeout);
+ if (c < 0)
+ return c;
+ switch (c) {
+ case ZCRCE:
+ case ZCRCG:
+ case ZCRCQ:
+ case ZCRCW:
+ case ZCRCF:
+ return c | GOTOR;
+ case ZRUB0: /* FIXME: This is never generated. */
+ return 0177;
+ case ZRUB1: /* FIXME: This is never generated. */
+ return 0377;
+ case XON:
+ goto again2;
+ case XOFF:
+ READCHAR (qdaemon, c, XON_WAIT);
+ goto again2;
+ default:
+ if (fZesc_ctl && !(c & 0140))
+ goto again2; /* FIXME: why again2? */
+ if ((c & 0140) == 0100)
+ return c ^ 0100;
+ break;
+ }
+
+ return ZM_ERROR;
+}
+
+/*
+ * Read a character from the modem line with timeout ...
+ * Eat parity bit, XON and XOFF characters.
+ */
+
+static int
+noxrd7(qdaemon)
+struct sdaemon *qdaemon;
+{
+ int c;
+
+ for (;;) {
+ READCHAR (qdaemon, c, cZtimeout);
+ if (c < 0)
+ return c;
+ switch (c &= 0177) {
+ case XON:
+ continue;
+ case XOFF:
+ READCHAR (qdaemon, c, XON_WAIT);
+ continue;
+ case CR:
+ case ZDLE:
+ return c;
+ default:
+ if (fZesc_ctl && !(c & 0140))
+ continue;
+ return c;
+ }
+ }
+}
+
+/*
+ * Read a character from the receive buffer, or from the line if empty ...
+ *
+ * <timeout> is in seconds (maybe make it tenths of seconds like in Zmodem?)
+ */
+
+static int
+realreadchar(qdaemon, timeout)
+struct sdaemon *qdaemon;
+int timeout;
+{
+ int c;
+
+ if ((c = breceive_char (qdaemon->qconn, timeout, TRUE)) >= 0)
+ return c;
+
+ switch (c) {
+ case -1:
+ return ZM_TIMEOUT;
+ case -2:
+ return ZM_RCDO;
+ }
+
+ ulog (LOG_FATAL, "realreadchar: breceive_char() returned %d", c);
+ return ZM_ERROR;
+}
+
+
+/*
+ * Check if the receive channel has any characters in it.
+ *
+ * At present we can only test the receive buffer. No mechanism is available
+ * to go to the hardware. This should not be a problem though, as long as all
+ * appropriate calls to fsend_data() set <fdoread> to TRUE.
+ */
+
+static boolean
+fzreceive_ready()
+{
+ return iPrecstart != iPrecend;
+}
+
+/*
+ * Store integer value in an achdrval_t ...
+ */
+
+static void
+stohdr(val, hdr)
+hdrval_t val;
+achdrval_t hdr;
+{
+ hdr[ZP0] = (char) val;
+ hdr[ZP1] = (char) (val >> 8);
+ hdr[ZP2] = (char) (val >> 16);
+ hdr[ZP3] = (char) (val >> 24);
+}
+
+/*
+ * Recover an integer from a header ...
+ */
+
+static hdrval_t
+rclhdr(hdr)
+achdrval_t hdr;
+{
+ hdrval_t v;
+
+ v = hdr[ZP3] & 0377;
+ v = (v << 8) | (hdr[ZP2] & 0377);
+ v = (v << 8) | (hdr[ZP1] & 0377);
+ v = (v << 8) | (hdr[ZP0] & 0377);
+
+ return v;
+}
+
+/*
+ * Encode a <hdrval_t> from the byte count ...
+ *
+ * We use to store the byte count / 32 and a message sequence number which
+ * made this function very useful. Don't remove it.
+ * FIXME: Well, maybe remove it later.
+ */
+
+static hdrval_t
+hvzencode_data_hdr(cbytes)
+winpos_t cbytes;
+{
+ return (hdrval_t) cbytes;
+}
+
+/*
+ * Decode a <hdrval_t> into a byte count ...
+ *
+ * We use to store the byte count / 32 and a message sequence number which
+ * made this function very useful. Don't remove it.
+ * FIXME: Well, maybe remove it later.
+ */
+
+static void
+zdecode_data_hdr(hdrval, pcbytes)
+hdrval_t hdrval;
+winpos_t *pcbytes;
+{
+ *pcbytes = hdrval;
+}
+
+/*
+ * Update <wpZrxpos> from the received data header value ...
+ *
+ * FIXME: Here is where we'd handle wrapping around at 4 gigabytes.
+ */
+
+static winpos_t
+lzupdate_rxpos(rx_hdr, rxpos, lrxpos, txpos)
+achdrval_t rx_hdr;
+winpos_t rxpos,lrxpos,txpos;
+{
+ winpos_t rx_pktpos;
+
+ zdecode_data_hdr (rclhdr (rx_hdr), &rx_pktpos);
+
+ DEBUG_MESSAGE4 (DEBUG_PROTO,
+ "lzupdate_rxpos: rx_pktpos=0x%lx, rxpos=0x%lx, lrxpos=0x%lx, txpos=0x%lx",
+ rx_pktpos, rxpos, lrxpos, txpos);
+
+ /*
+ * Check if <rx_pktpos> valid. It could be old.
+ */
+
+ if (rx_pktpos < wpZlrxpos
+ || rx_pktpos > ((wpZtxpos + 1024L) & ~1023L))
+ return rxpos;
+
+ return rx_pktpos;
+}
diff --git a/gnu/libexec/uucp/uucico/rec.c b/gnu/libexec/uucp/uucico/rec.c
new file mode 100644
index 0000000000000..b55d7f2c401f2
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/rec.c
@@ -0,0 +1,1162 @@
+/* rec.c
+ Routines to receive a file.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char rec_rcsid[] = "$Id: rec.c,v 1.1 1993/08/05 18:27:18 conklin Exp $";
+#endif
+
+#include <errno.h>
+
+#include "uudefs.h"
+#include "uuconf.h"
+#include "system.h"
+#include "prot.h"
+#include "trans.h"
+
+/* If the other side does not tell us the size of a file it wants to
+ send us, we assume it is this long. This is only used for free
+ space checking. */
+#define CASSUMED_FILE_SIZE (10240)
+
+/* We keep this information in the pinfo field of the stransfer
+ structure. */
+struct srecinfo
+{
+ /* Local user to send mail to (may be NULL). */
+ char *zmail;
+ /* Full file name. */
+ char *zfile;
+ /* Temporary file name. */
+ char *ztemp;
+ /* TRUE if this is a spool directory file. */
+ boolean fspool;
+ /* TRUE if this was a local request. */
+ boolean flocal;
+ /* TRUE if the file has been completely received. */
+ boolean freceived;
+ /* TRUE if remote request has been replied to. */
+ boolean freplied;
+ /* TRUE if we moved the file to the final destination. */
+ boolean fmoved;
+};
+
+/* This structure is kept in the pinfo field if we are refusing a
+ remote request. */
+struct srecfailinfo
+{
+ /* Reason for refusal. */
+ enum tfailure twhy;
+ /* TRUE if we have sent the reason for refusal. */
+ boolean fsent;
+ /* TRUE if we have seen the end of the file. */
+ boolean freceived;
+};
+
+/* Local functions. */
+
+static void urrec_free P((struct stransfer *qtrans));
+static boolean flocal_rec_fail P((struct stransfer *qtrans,
+ struct scmd *qcmd,
+ const struct uuconf_system *qsys,
+ const char *zwhy));
+static boolean flocal_rec_send_request P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static boolean flocal_rec_await_reply P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon,
+ const char *zdata,
+ size_t cdata));
+static boolean fremote_send_reply P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static boolean fremote_send_fail P((struct sdaemon *qdaemon,
+ struct scmd *qcmd,
+ enum tfailure twhy,
+ int iremote));
+static boolean fremote_send_fail_send P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static boolean fremote_discard P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon,
+ const char *zdata, size_t cdata));
+static boolean frec_file_end P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon,
+ const char *zdata, size_t cdata));
+static boolean frec_file_send_confirm P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+
+/* Free up a receive stransfer structure. */
+
+static void
+urrec_free (qtrans)
+ struct stransfer *qtrans;
+{
+ struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo;
+
+ if (qinfo != NULL)
+ {
+ ubuffree (qinfo->zmail);
+ ubuffree (qinfo->zfile);
+ ubuffree (qinfo->ztemp);
+ xfree (qtrans->pinfo);
+ }
+
+ utransfree (qtrans);
+}
+
+/* Set up a request for a file from the remote system. This may be
+ called before the remote system has been called.
+
+ This is the order of function calls:
+
+ flocal_rec_file_init --> fqueue_local
+ flocal_rec_send_request (send R ...) --> fqueue_receive
+ flocal_rec_await_reply (open file, call pffile) --> fqueue_receive
+ receive file
+ frec_file_end (close and move file, call pffile) --> fqueue_send
+ frec_file_send_confirm (send CY)
+ */
+
+boolean
+flocal_rec_file_init (qdaemon, qcmd)
+ struct sdaemon *qdaemon;
+ struct scmd *qcmd;
+{
+ const struct uuconf_system *qsys;
+ boolean fspool;
+ char *zfile;
+ struct srecinfo *qinfo;
+ struct stransfer *qtrans;
+
+ qsys = qdaemon->qsys;
+
+ /* Make sure we are permitted to transfer files. */
+ if (qdaemon->fcaller
+ ? ! qsys->uuconf_fcall_transfer
+ : ! qsys->uuconf_fcalled_transfer)
+ {
+ /* This case will have been checked by uucp or uux, but it could
+ have changed. */
+ if (! qsys->uuconf_fcall_transfer
+ && ! qsys->uuconf_fcalled_transfer)
+ return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys,
+ "not permitted to request files");
+ return TRUE;
+ }
+
+ fspool = fspool_file (qcmd->zto);
+
+ if (fspool)
+ {
+ pointer puuconf;
+ int iuuconf;
+ const char *zlocalname;
+ struct uuconf_system slocalsys;
+
+ /* Normal users are not allowed to request files to be received
+ into the spool directory. To support uux forwarding, we use
+ the special option '9'. This permits a file to be received
+ into the spool directory for the local system only without
+ the usual checking. This is only done for local requests, of
+ course. */
+ if (qcmd->zto[0] != 'D'
+ || strchr (qcmd->zoptions, '9') == NULL)
+ return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys,
+ "not permitted to receive");
+
+ puuconf = qdaemon->puuconf;
+ iuuconf = uuconf_localname (puuconf, &zlocalname);
+ if (iuuconf == UUCONF_NOT_FOUND)
+ {
+ zlocalname = zsysdep_localname ();
+ if (zlocalname == NULL)
+ return FALSE;
+ }
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ return FALSE;
+ }
+
+ iuuconf = uuconf_system_info (puuconf, zlocalname, &slocalsys);
+ if (iuuconf == UUCONF_NOT_FOUND)
+ {
+ iuuconf = uuconf_system_local (puuconf, &slocalsys);
+ if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ return FALSE;
+ }
+ }
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ return FALSE;
+ }
+
+ zfile = zsysdep_spool_file_name (&slocalsys, qcmd->zto, qcmd->pseq);
+
+ (void) uuconf_system_free (puuconf, &slocalsys);
+
+ if (zfile == NULL)
+ return FALSE;
+ }
+ else
+ {
+ zfile = zsysdep_add_base (qcmd->zto, qcmd->zfrom);
+ if (zfile == NULL)
+ return FALSE;
+
+ /* Check permissions. */
+ if (! fin_directory_list (zfile, qsys->uuconf_pzlocal_receive,
+ qsys->uuconf_zpubdir, TRUE,
+ FALSE, qcmd->zuser))
+ {
+ ubuffree (zfile);
+ return flocal_rec_fail ((struct stransfer *) NULL, qcmd, qsys,
+ "not permitted to receive");
+ }
+
+ /* The 'f' option means that directories should not
+ be created if they do not already exist. */
+ if (strchr (qcmd->zoptions, 'f') == NULL)
+ {
+ if (! fsysdep_make_dirs (zfile, TRUE))
+ {
+ ubuffree (zfile);
+ return flocal_rec_fail ((struct stransfer *) NULL, qcmd,
+ qsys, "cannot create directories");
+ }
+ }
+ }
+
+ qinfo = (struct srecinfo *) xmalloc (sizeof (struct srecinfo));
+ if (strchr (qcmd->zoptions, 'm') == NULL)
+ qinfo->zmail = NULL;
+ else
+ qinfo->zmail = zbufcpy (qcmd->zuser);
+ qinfo->zfile = zfile;
+ qinfo->ztemp = NULL;
+ qinfo->fspool = fspool;
+ qinfo->flocal = TRUE;
+ qinfo->freceived = FALSE;
+ qinfo->freplied = TRUE;
+
+ qtrans = qtransalc (qcmd);
+ qtrans->psendfn = flocal_rec_send_request;
+ qtrans->pinfo = (pointer) qinfo;
+
+ return fqueue_local (qdaemon, qtrans);
+}
+
+/* Report an error for a local receive request. */
+
+static boolean
+flocal_rec_fail (qtrans, qcmd, qsys, zwhy)
+ struct stransfer *qtrans;
+ struct scmd *qcmd;
+ const struct uuconf_system *qsys;
+ const char *zwhy;
+{
+ if (zwhy != NULL)
+ {
+ ulog (LOG_ERROR, "%s: %s", qcmd->zfrom, zwhy);
+ (void) fmail_transfer (FALSE, qcmd->zuser, (const char *) NULL, zwhy,
+ qcmd->zfrom, qsys->uuconf_zname,
+ qcmd->zto, (const char *) NULL,
+ (const char *) NULL);
+ (void) fsysdep_did_work (qcmd->pseq);
+ }
+ if (qtrans != NULL)
+ urrec_free (qtrans);
+ return TRUE;
+}
+
+/* This is called when we are ready to send the actual request to the
+ other system. */
+
+static boolean
+flocal_rec_send_request (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo;
+ long cbytes, cbytes2;
+ size_t clen;
+ char *zsend;
+ boolean fret;
+
+ qinfo->ztemp = zsysdep_receive_temp (qdaemon->qsys, qinfo->zfile,
+ (const char *) NULL);
+ if (qinfo->ztemp == NULL)
+ {
+ urrec_free (qtrans);
+ return FALSE;
+ }
+
+ /* Check the amount of free space available for both the temporary
+ file and the real file. */
+ cbytes = csysdep_bytes_free (qinfo->ztemp);
+ cbytes2 = csysdep_bytes_free (qinfo->zfile);
+ if (cbytes < cbytes2)
+ cbytes = cbytes2;
+ if (cbytes != -1)
+ {
+ cbytes -= qdaemon->qsys->uuconf_cfree_space;
+ if (cbytes < 0)
+ cbytes = 0;
+ }
+
+ if (qdaemon->clocal_size != -1
+ && (cbytes == -1 || qdaemon->clocal_size < cbytes))
+ cbytes = qdaemon->clocal_size;
+
+ /* We send the string
+ R from to user options
+
+ We put a dash in front of options. If we are talking to a
+ counterpart, we also send the maximum size file we are prepared
+ to accept, as returned by esysdep_open_receive. */
+ clen = (strlen (qtrans->s.zfrom) + strlen (qtrans->s.zto)
+ + strlen (qtrans->s.zuser) + strlen (qtrans->s.zoptions) + 30);
+ zsend = zbufalc (clen);
+ if ((qdaemon->ifeatures & FEATURE_SIZES) == 0)
+ sprintf (zsend, "R %s %s %s -%s", qtrans->s.zfrom, qtrans->s.zto,
+ qtrans->s.zuser, qtrans->s.zoptions);
+ else if ((qdaemon->ifeatures & FEATURE_V103) == 0)
+ sprintf (zsend, "R %s %s %s -%s 0x%lx", qtrans->s.zfrom, qtrans->s.zto,
+ qtrans->s.zuser, qtrans->s.zoptions, (unsigned long) cbytes);
+ else
+ sprintf (zsend, "R %s %s %s -%s %ld", qtrans->s.zfrom, qtrans->s.zto,
+ qtrans->s.zuser, qtrans->s.zoptions, cbytes);
+
+ fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, qtrans->ilocal,
+ qtrans->iremote);
+ ubuffree (zsend);
+ if (! fret)
+ {
+ urrec_free (qtrans);
+ return FALSE;
+ }
+
+ qtrans->fcmd = TRUE;
+ qtrans->precfn = flocal_rec_await_reply;
+
+ return fqueue_receive (qdaemon, qtrans);
+}
+
+/* This is called when a reply is received for the request. */
+
+/*ARGSUSED*/
+static boolean
+flocal_rec_await_reply (qtrans, qdaemon, zdata, cdata)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+ const char *zdata;
+ size_t cdata;
+{
+ struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo;
+ long crestart;
+ const char *zlog;
+
+ if (zdata[0] != 'R'
+ || (zdata[1] != 'Y' && zdata[1] != 'N'))
+ {
+ ulog (LOG_ERROR, "%s: bad response to receive request: \"%s\"",
+ qtrans->s.zfrom, zdata);
+ urrec_free (qtrans);
+ return FALSE;
+ }
+
+ if (zdata[1] == 'N')
+ {
+ boolean fnever;
+ const char *zerr;
+
+ fnever = TRUE;
+ if (zdata[2] == '2')
+ zerr = "no such file";
+ else if (zdata[2] == '6')
+ {
+ /* We sent over the maximum file size we were prepared to
+ receive, and the remote system is telling us that the
+ file is larger than that. Try again later. It would be
+ better if we could know whether there will ever be enough
+ room. */
+ zerr = "too large to receive now";
+ fnever = FALSE;
+ }
+ else
+ zerr = "unknown reason";
+
+ if (fnever)
+ return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys, zerr);
+
+ ulog (LOG_ERROR, "%s: %s", qtrans->s.zfrom, zerr);
+
+ urrec_free (qtrans);
+
+ return TRUE;
+ }
+
+ /* The mode should have been sent as "RY 0%o". If it wasn't, we use
+ 0666. */
+ qtrans->s.imode = (unsigned int) strtol ((char *) (zdata + 2),
+ (char **) NULL, 8);
+ if (qtrans->s.imode == 0)
+ qtrans->s.imode = 0666;
+
+ /* Open the file to receive into. We just ignore any restart count,
+ since we have no way to tell it to the other side. SVR4 may have
+ some way to do this, but I don't know what it is. */
+ qtrans->e = esysdep_open_receive (qdaemon->qsys, qinfo->zfile,
+ (const char *) NULL, qinfo->ztemp,
+ &crestart);
+ if (! ffileisopen (qtrans->e))
+ return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys,
+ "cannot open file");
+
+ if (qinfo->fspool)
+ zlog = qtrans->s.zto;
+ else
+ zlog = qinfo->zfile;
+ qtrans->zlog = zbufalc (sizeof "Receiving " + strlen (zlog));
+ sprintf (qtrans->zlog, "Receiving %s", zlog);
+
+ if (qdaemon->qproto->pffile != NULL)
+ {
+ boolean fhandled;
+
+ if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, FALSE,
+ (long) -1, &fhandled))
+ {
+ (void) ffileclose (qtrans->e);
+ return flocal_rec_fail (qtrans, &qtrans->s, qdaemon->qsys,
+ (const char *) NULL);
+ }
+ if (fhandled)
+ return TRUE;
+ }
+
+ qtrans->frecfile = TRUE;
+ qtrans->psendfn = frec_file_send_confirm;
+ qtrans->precfn = frec_file_end;
+
+ return fqueue_receive (qdaemon, qtrans);
+}
+
+/* Make sure there is still enough disk space available to receive a
+ file. */
+
+boolean
+frec_check_free (qtrans, cfree_space)
+ struct stransfer *qtrans;
+ long cfree_space;
+{
+ struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo;
+ long cfree1, cfree2;
+
+ cfree1 = csysdep_bytes_free (qinfo->ztemp);
+ cfree2 = csysdep_bytes_free (qinfo->zfile);
+ if (cfree1 < cfree2)
+ cfree1 = cfree2;
+ if (cfree1 != -1 && cfree1 < cfree_space)
+ {
+ ulog (LOG_ERROR, "%s: too big to receive now", qinfo->zfile);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* A remote request to send a file to the local system, meaning that
+ we are going to receive a file.
+
+ If we are using a protocol which does not support multiple
+ channels, the remote system will not start sending us the file
+ until it has received our confirmation. In that case, the order of
+ functions is as follows:
+
+ fremote_send_file_init (open file) --> fqueue_remote
+ fremote_send_reply (send SY, call pffile) --> fqueue_receive
+ receive file
+ frec_file_end (close and move file, call pffile) --> fqueue_send
+ frec_file_send_confirm (send CY)
+
+ If the protocol supports multiple channels, then the remote system
+ will start sending the file immediately after the send request.
+ That means that the data may come in before remote_send_reply is
+ called, so frec_file_end may be called before fremote_send_reply.
+ Note that this means the pffile entry points may be called in
+ reverse order for such a protocol.
+
+ If the send request is rejected, via fremote_send_fail, and the
+ protocol supports multiple channels, we must accept and discard
+ data until a zero byte buffer is received from the other side,
+ indicating that it has received our rejection.
+
+ This code also handles execution requests, which are very similar
+ to send requests. */
+
+boolean
+fremote_send_file_init (qdaemon, qcmd, iremote)
+ struct sdaemon *qdaemon;
+ struct scmd *qcmd;
+ int iremote;
+{
+ const struct uuconf_system *qsys;
+ boolean fspool;
+ char *zfile;
+ openfile_t e;
+ char *ztemp;
+ long cbytes, cbytes2;
+ long crestart;
+ struct srecinfo *qinfo;
+ struct stransfer *qtrans;
+ const char *zlog;
+
+ qsys = qdaemon->qsys;
+
+ if (! qsys->uuconf_frec_request)
+ {
+ ulog (LOG_ERROR, "%s: not permitted to receive files from remote",
+ qcmd->zfrom);
+ return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote);
+ }
+
+ fspool = fspool_file (qcmd->zto);
+
+ /* We don't accept remote command files. An execution request may
+ only send a simple data file. */
+ if ((fspool && qcmd->zto[0] == 'C')
+ || (qcmd->bcmd == 'E'
+ && (! fspool || qcmd->zto[0] != 'D')))
+ {
+ ulog (LOG_ERROR, "%s: not permitted to receive", qcmd->zfrom);
+ return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote);
+ }
+
+ /* See if we have already received this file in a previous
+ conversation. */
+ if (fsysdep_already_received (qsys, qcmd->zto, qcmd->ztemp))
+ return fremote_send_fail (qdaemon, qcmd, FAILURE_RECEIVED, iremote);
+
+ if (fspool)
+ {
+ zfile = zsysdep_spool_file_name (qsys, qcmd->zto, (pointer) NULL);
+ if (zfile == NULL)
+ return FALSE;
+ }
+ else
+ {
+ zfile = zsysdep_local_file (qcmd->zto, qsys->uuconf_zpubdir);
+ if (zfile != NULL)
+ {
+ char *zadd;
+
+ zadd = zsysdep_add_base (zfile, qcmd->zfrom);
+ ubuffree (zfile);
+ zfile = zadd;
+ }
+ if (zfile == NULL)
+ return FALSE;
+
+ /* Check permissions. */
+ if (! fin_directory_list (zfile, qsys->uuconf_pzremote_receive,
+ qsys->uuconf_zpubdir, TRUE,
+ FALSE, (const char *) NULL))
+ {
+ ulog (LOG_ERROR, "%s: not permitted to receive", zfile);
+ ubuffree (zfile);
+ return fremote_send_fail (qdaemon, qcmd, FAILURE_PERM, iremote);
+ }
+
+ if (strchr (qcmd->zoptions, 'f') == NULL)
+ {
+ if (! fsysdep_make_dirs (zfile, TRUE))
+ {
+ ubuffree (zfile);
+ return fremote_send_fail (qdaemon, qcmd, FAILURE_OPEN,
+ iremote);
+ }
+ }
+ }
+
+ ztemp = zsysdep_receive_temp (qsys, zfile, qcmd->ztemp);
+
+ /* Adjust the number of bytes we are prepared to receive according
+ to the amount of free space we are supposed to leave available
+ and the maximum file size we are permitted to transfer. */
+ cbytes = csysdep_bytes_free (ztemp);
+ cbytes2 = csysdep_bytes_free (zfile);
+ if (cbytes < cbytes2)
+ cbytes = cbytes2;
+
+ if (cbytes != -1)
+ {
+ cbytes -= qsys->uuconf_cfree_space;
+ if (cbytes < 0)
+ cbytes = 0;
+ }
+
+ if (qdaemon->cremote_size != -1
+ && (cbytes == -1 || qdaemon->cremote_size < cbytes))
+ cbytes = qdaemon->cremote_size;
+
+ /* If the number of bytes we are prepared to receive is less than
+ the file size, we must fail. If the remote did not tell us the
+ file size, arbitrarily assumed that it is 10240 bytes. */
+ if (cbytes != -1)
+ {
+ long csize;
+
+ csize = qcmd->cbytes;
+ if (csize == -1)
+ csize = CASSUMED_FILE_SIZE;
+ if (cbytes < csize)
+ {
+ ulog (LOG_ERROR, "%s: too big to receive", zfile);
+ ubuffree (ztemp);
+ ubuffree (zfile);
+ return fremote_send_fail (qdaemon, qcmd, FAILURE_SIZE, iremote);
+ }
+ }
+
+ /* Open the file to receive into. This may find an old copy of the
+ file, which will be used for file restart if the other side
+ supports it. */
+ e = esysdep_open_receive (qsys, zfile, qcmd->ztemp, ztemp, &crestart);
+ if (! ffileisopen (e))
+ {
+ ubuffree (ztemp);
+ ubuffree (zfile);
+ return fremote_send_fail (qdaemon, qcmd, FAILURE_OPEN, iremote);
+ }
+
+ if (crestart > 0)
+ {
+ if ((qdaemon->ifeatures & FEATURE_RESTART) == 0)
+ crestart = -1;
+ else
+ {
+ DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO,
+ "fremote_send_file_init: Restarting receive from %ld",
+ crestart);
+ if (! ffileseek (e, crestart))
+ {
+ ulog (LOG_ERROR, "seek: %s", strerror (errno));
+ (void) ffileclose (e);
+ ubuffree (ztemp);
+ ubuffree (zfile);
+ return FALSE;
+ }
+ }
+ }
+
+ qinfo = (struct srecinfo *) xmalloc (sizeof (struct srecinfo));
+ if (strchr (qcmd->zoptions, 'n') == NULL)
+ qinfo->zmail = NULL;
+ else
+ qinfo->zmail = zbufcpy (qcmd->znotify);
+ qinfo->zfile = zfile;
+ qinfo->ztemp = ztemp;
+ qinfo->fspool = fspool;
+ qinfo->flocal = FALSE;
+ qinfo->freceived = FALSE;
+ qinfo->freplied = FALSE;
+
+ qtrans = qtransalc (qcmd);
+ qtrans->psendfn = fremote_send_reply;
+ qtrans->precfn = frec_file_end;
+ qtrans->iremote = iremote;
+ qtrans->pinfo = (pointer) qinfo;
+ qtrans->frecfile = TRUE;
+ qtrans->e = e;
+ if (crestart > 0)
+ qtrans->ipos = crestart;
+
+ if (qcmd->bcmd == 'E')
+ zlog = qcmd->zcmd;
+ else
+ {
+ if (qinfo->fspool)
+ zlog = qcmd->zto;
+ else
+ zlog = qinfo->zfile;
+ }
+ qtrans->zlog = zbufalc (sizeof "Receiving " + strlen (zlog));
+ sprintf (qtrans->zlog, "Receiving %s", zlog);
+
+ return fqueue_remote (qdaemon, qtrans);
+}
+
+/* Reply to a send request, and prepare to receive the file. */
+
+static boolean
+fremote_send_reply (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo;
+ char ab[50];
+
+ ab[0] = qtrans->s.bcmd;
+ ab[1] = 'Y';
+ if (qtrans->ipos <= 0)
+ ab[2] = '\0';
+ else
+ sprintf (ab + 2, " 0x%lx", (unsigned long) qtrans->ipos);
+
+ if (! (*qdaemon->qproto->pfsendcmd) (qdaemon, ab, qtrans->ilocal,
+ qtrans->iremote))
+ {
+ (void) ffileclose (qtrans->e);
+ (void) remove (qinfo->ztemp);
+ urrec_free (qtrans);
+ return FALSE;
+ }
+
+ qinfo->freplied = TRUE;
+
+ if (qdaemon->qproto->pffile != NULL)
+ {
+ boolean fhandled;
+
+ if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, FALSE,
+ (long) -1, &fhandled))
+ {
+ (void) ffileclose (qtrans->e);
+ (void) remove (qinfo->ztemp);
+ urrec_free (qtrans);
+ return FALSE;
+ }
+ if (fhandled)
+ return TRUE;
+ }
+
+ /* If the file has been completely received, we just want to send
+ the final confirmation. Otherwise, we must wait for the file
+ first. */
+ qtrans->psendfn = frec_file_send_confirm;
+ if (qinfo->freceived)
+ return fqueue_send (qdaemon, qtrans);
+ else
+ return fqueue_receive (qdaemon, qtrans);
+}
+
+/* If we can't receive a file, queue up a response to the remote
+ system. */
+
+static boolean
+fremote_send_fail (qdaemon, qcmd, twhy, iremote)
+ struct sdaemon *qdaemon;
+ struct scmd *qcmd;
+ enum tfailure twhy;
+ int iremote;
+{
+ struct srecfailinfo *qinfo;
+ struct stransfer *qtrans;
+
+ qinfo = (struct srecfailinfo *) xmalloc (sizeof (struct srecfailinfo));
+ qinfo->twhy = twhy;
+ qinfo->fsent = FALSE;
+
+ /* If the protocol does not support multiple channels (cchans <= 1),
+ then we have essentially already received the entire file. */
+ qinfo->freceived = qdaemon->qproto->cchans <= 1;
+
+ qtrans = qtransalc (qcmd);
+ qtrans->psendfn = fremote_send_fail_send;
+ qtrans->precfn = fremote_discard;
+ qtrans->iremote = iremote;
+ qtrans->pinfo = (pointer) qinfo;
+
+ return fqueue_remote (qdaemon, qtrans);
+}
+
+/* Send a failure string for a send command to the remote system;
+ this is called when we are ready to reply to the command. */
+
+static boolean
+fremote_send_fail_send (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ struct srecfailinfo *qinfo = (struct srecfailinfo *) qtrans->pinfo;
+ char ab[4];
+ boolean fret;
+
+ ab[0] = qtrans->s.bcmd;
+ ab[1] = 'N';
+
+ switch (qinfo->twhy)
+ {
+ case FAILURE_PERM:
+ ab[2] = '2';
+ break;
+ case FAILURE_OPEN:
+ ab[2] = '4';
+ break;
+ case FAILURE_SIZE:
+ ab[2] = '6';
+ break;
+ case FAILURE_RECEIVED:
+ /* Remember this file as though we successfully received it;
+ when the other side acknowledges our rejection, we know that
+ we no longer have to remember that we received this file. */
+ usent_receive_ack (qdaemon, qtrans);
+ ab[2] = '8';
+ break;
+ default:
+ ab[2] = '\0';
+ break;
+ }
+
+ ab[3] = '\0';
+
+ fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, ab, qtrans->ilocal,
+ qtrans->iremote);
+
+ qinfo->fsent = TRUE;
+
+ /* Wait for the end of file marker if we haven't gotten it yet. */
+ if (! qinfo->freceived)
+ {
+ if (! fqueue_receive (qdaemon, qtrans))
+ fret = FALSE;
+ }
+ else
+ {
+ xfree (qtrans->pinfo);
+ utransfree (qtrans);
+ }
+
+ return fret;
+}
+
+/* Discard data until we reach the end of the file. This is used for
+ a protocol with multiple channels, since the remote system may
+ start sending the file before the confirmation is sent. If we
+ refuse the file, the remote system will get us back in synch by
+ sending an empty buffer, which is what we look for here. */
+
+/*ARGSUSED*/
+static boolean
+fremote_discard (qtrans, qdaemon, zdata, cdata)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+ const char *zdata;
+ size_t cdata;
+{
+ struct srecfailinfo *qinfo = (struct srecfailinfo *) qtrans->pinfo;
+
+ DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO,
+ "fremote_discard: Discarding %lu bytes",
+ (unsigned long) cdata);
+
+ if (cdata != 0)
+ return TRUE;
+
+ qinfo->freceived = TRUE;
+
+ /* If we have already sent the denial, we are done. */
+ if (qinfo->fsent)
+ {
+ xfree (qtrans->pinfo);
+ utransfree (qtrans);
+ }
+
+ return TRUE;
+}
+
+/* This is called when a file has been completely received. It sends
+ a response to the remote system. */
+
+/*ARGSUSED*/
+static boolean
+frec_file_end (qtrans, qdaemon, zdata, cdata)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+ const char *zdata;
+ size_t cdata;
+{
+ struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo;
+ const char *zerr;
+ boolean fnever;
+
+ DEBUG_MESSAGE3 (DEBUG_UUCP_PROTO, "frec_file_end: %s to %s (freplied %s)",
+ qtrans->s.zfrom, qtrans->s.zto,
+ qinfo->freplied ? "TRUE" : "FALSE");
+
+ if (qdaemon->qproto->pffile != NULL)
+ {
+ boolean fhandled;
+
+ if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, FALSE, FALSE,
+ (long) -1, &fhandled))
+ {
+ (void) ffileclose (qtrans->e);
+ (void) remove (qinfo->ztemp);
+ urrec_free (qtrans);
+ return FALSE;
+ }
+ if (fhandled)
+ return TRUE;
+ }
+
+ qinfo->freceived = TRUE;
+
+ fnever = FALSE;
+
+ if (! ffileclose (qtrans->e))
+ {
+ zerr = strerror (errno);
+ ulog (LOG_ERROR, "%s: close: %s", qtrans->s.zto, zerr);
+ }
+ else if (! fsysdep_move_file (qinfo->ztemp, qinfo->zfile, qinfo->fspool,
+ FALSE, ! qinfo->fspool,
+ (qinfo->flocal
+ ? qtrans->s.zuser
+ : (const char *) NULL)))
+ {
+ zerr = "could not move to final location";
+ ulog (LOG_ERROR, "%s: %s", qinfo->zfile, zerr);
+ fnever = TRUE;
+ }
+ else
+ {
+ if (! qinfo->fspool)
+ {
+ unsigned int imode;
+
+ /* Unless we can change the ownership of the file, the only
+ choice to make about these bits is whether to set the
+ execute bit or not. */
+ if ((qtrans->s.imode & 0111) != 0)
+ imode = 0777;
+ else
+ imode = 0666;
+ (void) fsysdep_change_mode (qinfo->zfile, imode);
+ }
+
+ zerr = NULL;
+ }
+
+ if (zerr != NULL)
+ (void) remove (qinfo->ztemp);
+
+ ustats (zerr == NULL, qtrans->s.zuser, qdaemon->qsys->uuconf_zname,
+ FALSE, qtrans->cbytes, qtrans->isecs, qtrans->imicros,
+ qdaemon->fmaster);
+
+ if (zerr == NULL)
+ {
+ if (qinfo->zmail != NULL && *qinfo->zmail != '\0')
+ (void) fmail_transfer (TRUE, qtrans->s.zuser, qinfo->zmail,
+ (const char *) NULL,
+ qtrans->s.zfrom, qdaemon->qsys->uuconf_zname,
+ qtrans->s.zto, (const char *) NULL,
+ (const char *) NULL);
+
+ if (qtrans->s.pseq != NULL)
+ (void) fsysdep_did_work (qtrans->s.pseq);
+
+ if (! qinfo->flocal)
+ {
+ /* Remember that we have received this file, so that if the
+ connection drops at this point we won't receive it again.
+ We could check the return value here, but if we return
+ FALSE we couldn't do anything but drop the connection,
+ which would hardly be reasonable. Instead we trust that
+ the administrator will notice and handle any error
+ messages, which are very unlikely to occur if everything
+ is set up correctly. */
+ (void) fsysdep_remember_reception (qdaemon->qsys, qtrans->s.zto,
+ qtrans->s.ztemp);
+ }
+ }
+ else
+ {
+ /* If the transfer failed, we send mail if it was requested
+ locally and if it can never succeed. */
+ if (qinfo->flocal && fnever)
+ {
+ (void) fmail_transfer (FALSE, qtrans->s.zuser, qinfo->zmail,
+ zerr, qtrans->s.zfrom,
+ qdaemon->qsys->uuconf_zname,
+ qtrans->s.zto, (const char *) NULL,
+ (const char *) NULL);
+ (void) fsysdep_did_work (qtrans->s.pseq);
+ }
+ }
+
+ /* If this is an execution request, we must create the execution
+ file itself. */
+ if (qtrans->s.bcmd == 'E' && zerr == NULL)
+ {
+ char *zxqt, *zxqtfile, *ztemp;
+ FILE *e;
+ boolean fbad;
+
+ /* We get an execution file name by simply replacing the leading
+ D in the received file name with an X. This pretty much
+ always has to work since we can always receive a file name
+ starting with X, so the system dependent code must be
+ prepared to see one. */
+ zxqt = zbufcpy (qtrans->s.zto);
+ zxqt[0] = 'X';
+ zxqtfile = zsysdep_spool_file_name (qdaemon->qsys, zxqt,
+ (pointer) NULL);
+ ubuffree (zxqt);
+
+ if (zxqtfile == NULL)
+ {
+ urrec_free (qtrans);
+ return FALSE;
+ }
+
+ /* We have to write via a temporary file, because otherwise
+ uuxqt might pick up the file before we have finished writing
+ it. */
+ e = NULL;
+ ztemp = zsysdep_receive_temp (qdaemon->qsys, zxqtfile, "D.0");
+ if (ztemp != NULL)
+ e = esysdep_fopen (ztemp, FALSE, FALSE, TRUE);
+
+ if (e == NULL)
+ {
+ ubuffree (zxqtfile);
+ ubuffree (ztemp);
+ urrec_free (qtrans);
+ return FALSE;
+ }
+
+ fprintf (e, "U %s %s\n", qtrans->s.zuser, qdaemon->qsys->uuconf_zname);
+ fprintf (e, "F %s\n", qtrans->s.zto);
+ fprintf (e, "I %s\n", qtrans->s.zto);
+ if (strchr (qtrans->s.zoptions, 'N') != NULL)
+ fprintf (e, "N\n");
+ if (strchr (qtrans->s.zoptions, 'Z') != NULL)
+ fprintf (e, "Z\n");
+ if (strchr (qtrans->s.zoptions, 'R') != NULL)
+ fprintf (e, "R %s\n", qtrans->s.znotify);
+ if (strchr (qtrans->s.zoptions, 'e') != NULL)
+ fprintf (e, "e\n");
+ fprintf (e, "C %s\n", qtrans->s.zcmd);
+
+ fbad = FALSE;
+ if (fclose (e) == EOF)
+ {
+ ulog (LOG_ERROR, "fclose: %s", strerror (errno));
+ (void) remove (ztemp);
+ fbad = TRUE;
+ }
+
+ if (! fbad)
+ {
+ if (! fsysdep_move_file (ztemp, zxqtfile, TRUE, FALSE, FALSE,
+ (const char *) NULL))
+ fbad = TRUE;
+ }
+
+ ubuffree (zxqtfile);
+ ubuffree (ztemp);
+
+ if (fbad)
+ {
+ urrec_free (qtrans);
+ return FALSE;
+ }
+ }
+
+ /* Prepare to send the completion string to the remote system. If
+ we have not yet replied to the remote send request, we leave the
+ transfer structure on the remote queue. Otherwise we add it to
+ the send queue. The psendfn field will already be set. */
+ qinfo->fmoved = zerr == NULL;
+ if (qinfo->freplied)
+ return fqueue_send (qdaemon, qtrans);
+
+ return TRUE;
+}
+
+/* Send the final confirmation string to the remote system. */
+
+static boolean
+frec_file_send_confirm (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo;
+ const char *zsend;
+ boolean fret;
+
+ if (! qinfo->fmoved)
+ zsend = "CN5";
+ else if (! qdaemon->frequest_hangup)
+ zsend = "CY";
+ else
+ {
+#if DEBUG > 0
+ if (qdaemon->fmaster)
+ ulog (LOG_FATAL, "frec_file_send_confirm: Can't happen");
+#endif
+
+ DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO,
+ "frec_send_file_confirm: Requesting remote to transfer control");
+ zsend = "CYM";
+ }
+
+ fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend,
+ qtrans->ilocal, qtrans->iremote);
+
+ /* Now, if that was a remote command, then when the confirmation
+ message is acked we no longer have to remember that we received
+ that file. */
+ if (! qinfo->flocal && qinfo->fmoved)
+ usent_receive_ack (qdaemon, qtrans);
+
+ urrec_free (qtrans);
+ return fret;
+}
+
+/* Discard a temporary file if it is not useful. A temporary file is
+ useful if it could be used to restart a receive. This is called if
+ the connection is lost. It is only called if qtrans->frecfile is
+ TRUE. */
+
+boolean
+frec_discard_temp (qdaemon, qtrans)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+{
+ struct srecinfo *qinfo = (struct srecinfo *) qtrans->pinfo;
+
+ if ((qdaemon->ifeatures & FEATURE_RESTART) == 0
+ || qtrans->s.ztemp == NULL
+ || qtrans->s.ztemp[0] != 'D'
+ || strcmp (qtrans->s.ztemp, "D.0") == 0)
+ (void) remove (qinfo->ztemp);
+ return TRUE;
+}
diff --git a/gnu/libexec/uucp/uucico/send.c b/gnu/libexec/uucp/uucico/send.c
new file mode 100644
index 0000000000000..8e2c55ef37244
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/send.c
@@ -0,0 +1,1273 @@
+/* send.c
+ Routines to send a file.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char send_rcsid[] = "$Id: send.c,v 1.1 1993/08/05 18:27:19 conklin Exp $";
+#endif
+
+#include <errno.h>
+
+#include "uudefs.h"
+#include "uuconf.h"
+#include "system.h"
+#include "prot.h"
+#include "trans.h"
+
+/* We keep this information in the pinfo field of the stransfer
+ structure. */
+struct ssendinfo
+{
+ /* Local user to send mail to (may be NULL). */
+ char *zmail;
+ /* Full file name. */
+ char *zfile;
+ /* Number of bytes in file. */
+ long cbytes;
+ /* TRUE if this was a local request. */
+ boolean flocal;
+ /* TRUE if this is a spool directory file. */
+ boolean fspool;
+ /* TRUE if the file has been completely sent. */
+ boolean fsent;
+ /* Execution file for sending an unsupported E request. */
+ char *zexec;
+};
+
+/* Local functions. */
+
+static void usfree_send P((struct stransfer *qtrans));
+static boolean flocal_send_fail P((struct stransfer *qtrans,
+ struct scmd *qcmd,
+ const struct uuconf_system *qsys,
+ const char *zwhy));
+static boolean flocal_send_request P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static boolean flocal_send_await_reply P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon,
+ const char *zdata, size_t cdata));
+static boolean flocal_send_cancelled P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static boolean flocal_send_open_file P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static boolean fremote_rec_fail P((struct sdaemon *qdaemon,
+ enum tfailure twhy, int iremote));
+static boolean fremote_rec_fail_send P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static boolean fremote_rec_reply P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static boolean fsend_file_end P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static boolean fsend_await_confirm P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon,
+ const char *zdata, size_t cdata));
+static boolean fsend_exec_file_init P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static void usadd_exec_line P((char **pz, size_t *pcalc, size_t *pclen,
+ int bcmd, const char *z1, const char *z2));
+static boolean fsend_exec_file P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+
+/* Free up a send stransfer structure. */
+
+static void
+usfree_send (qtrans)
+ struct stransfer *qtrans;
+{
+ struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo;
+
+ if (qinfo != NULL)
+ {
+ ubuffree (qinfo->zmail);
+ ubuffree (qinfo->zfile);
+ ubuffree (qinfo->zexec);
+ xfree (qtrans->pinfo);
+ }
+
+ utransfree (qtrans);
+}
+
+/* Set up a local request to send a file. This may be called before
+ we have even tried to call the remote system.
+
+ If we are using a traditional protocol, which doesn't support
+ channel numbers and doesn't permit the file to be sent until an
+ acknowledgement has been received, the sequence of function calls
+ looks like this:
+
+ flocal_send_file_init --> fqueue_local
+ flocal_send_request (sends S request) --> fqueue_receive
+ flocal_send_await_reply (waits for SY) --> fqueue_send
+ flocal_send_open_file (opens file, calls pffile) --> fqueue_send
+ send file
+ fsend_file_end (calls pffile) --> fqueue_receive
+ fsend_await_confirm (waits for CY)
+
+ If flocal_send_await_reply gets an SN, it deletes the request. If
+ the SY reply contains a file position at which to start sending,
+ flocal_send_await_reply sets qinfo->ipos.
+
+ This gets more complex if the protocol supports channels. In that
+ case, we want to start sending the file data immediately, to avoid
+ the round trip delay between flocal_send_request and
+ flocal_send_await_reply. To do this, flocal_send_request calls
+ fqueue_send rather than fqueue_receive. The main execution
+ sequence looks like this:
+
+ flocal_send_file_init --> fqueue_local
+ flocal_send_request (sends S request) --> fqueue_send
+ flocal_send_open_file (opens file, calls pffile) --> fqueue_send
+ send file
+ fsend_file_end (calls pffile) --> fqueue_receive
+ sometime: flocal_send_await_reply (waits for SY)
+ fsend_await_confirm (waits for CY)
+
+ In this case flocal_send_await_reply must be run before
+ fsend_await_confirm; it may be run anytime after
+ flocal_send_request.
+
+ If flocal_send_await_reply is called before the entire file has
+ been sent: if it gets an SN, it calls flocal_send_cancelled to send
+ an empty data block to inform the remote system that the file
+ transfer has stopped. If it gets a file position request, it must
+ adjust the file position accordingly.
+
+ If flocal_send_await_reply is called after the entire file has been
+ sent: if it gets an SN, it can simply delete the request. It can
+ ignore any file position request.
+
+ If the request is not deleted, flocal_send_await_reply must arrange
+ for the next string to be passed to fsend_await_confirm.
+ Presumably fsend_await_confirm will only be called after the entire
+ file has been sent.
+
+ Just to make things even more complex, these same routines support
+ sending execution requests, since that is much like sending a file.
+ For an execution request, the bcmd character will be E rather than
+ S. If an execution request is being sent to a system which does
+ not support them, it must be sent as two S requests instead. The
+ second one will be the execution file, but no actual file is
+ created; instead the zexec and znext fields in the ssendinfo
+ structure are used. So if the bcmd character is E, then if the
+ zexec field is NULL, the data file is being sent, otherwise the
+ fake execution file is being sent. */
+
+boolean
+flocal_send_file_init (qdaemon, qcmd)
+ struct sdaemon *qdaemon;
+ struct scmd *qcmd;
+{
+ const struct uuconf_system *qsys;
+ boolean fspool;
+ char *zfile;
+ long cbytes;
+ struct ssendinfo *qinfo;
+ struct stransfer *qtrans;
+
+ qsys = qdaemon->qsys;
+
+ if (qdaemon->fcaller
+ ? ! qsys->uuconf_fcall_transfer
+ : ! qsys->uuconf_fcalled_transfer)
+ {
+ /* uux or uucp should have already made sure that the transfer
+ is possible, but it might have changed since then. */
+ if (! qsys->uuconf_fcall_transfer
+ && ! qsys->uuconf_fcalled_transfer)
+ return flocal_send_fail ((struct stransfer *) NULL, qcmd, qsys,
+ "not permitted to transfer files");
+
+ /* We can't do the request now, but it may get done later. */
+ return TRUE;
+ }
+
+ /* The 'C' option means that the file has been copied to the spool
+ directory. */
+ if (strchr (qcmd->zoptions, 'C') == NULL
+ && ! fspool_file (qcmd->zfrom))
+ {
+ fspool = FALSE;
+ if (! fin_directory_list (qcmd->zfrom,
+ qsys->uuconf_pzlocal_send,
+ qsys->uuconf_zpubdir, TRUE,
+ TRUE, qcmd->zuser))
+ return flocal_send_fail ((struct stransfer *) NULL, qcmd, qsys,
+ "not permitted to send");
+ zfile = zbufcpy (qcmd->zfrom);
+ }
+ else
+ {
+ fspool = TRUE;
+ zfile = zsysdep_spool_file_name (qsys, qcmd->ztemp, qcmd->pseq);
+ if (zfile == NULL)
+ return FALSE;
+ }
+
+ /* Make sure we meet any local size restrictions. The connection
+ may not have been opened at this point, so we can't check remote
+ size restrictions. */
+ cbytes = csysdep_size (zfile);
+ if (cbytes < 0)
+ {
+ ubuffree (zfile);
+ if (cbytes != -1)
+ return FALSE;
+ /* A cbytes value of -1 means that the file does not exist.
+ This can happen legitimately if it has already been sent from
+ the spool directory. */
+ if (! fspool)
+ return flocal_send_fail ((struct stransfer *) NULL, qcmd, qsys,
+ "does not exist");
+ (void) fsysdep_did_work (qcmd->pseq);
+ return TRUE;
+ }
+
+ if (qdaemon->clocal_size != -1
+ && qdaemon->clocal_size < cbytes)
+ {
+ ubuffree (zfile);
+
+ if (qdaemon->cmax_ever == -2)
+ {
+ long c1, c2;
+
+ c1 = cmax_size_ever (qsys->uuconf_qcall_local_size);
+ c2 = cmax_size_ever (qsys->uuconf_qcalled_local_size);
+ if (c1 > c2)
+ qdaemon->cmax_ever = c1;
+ else
+ qdaemon->cmax_ever = c2;
+ }
+
+ if (qdaemon->cmax_ever != -1
+ && qdaemon->cmax_ever < qcmd->cbytes)
+ return flocal_send_fail ((struct stransfer *) NULL, qcmd, qsys,
+ "too large to send");
+
+ return TRUE;
+ }
+
+ /* We are now prepared to send the command to the remote system. We
+ queue up a transfer request to send the command when we are
+ ready. */
+ qinfo = (struct ssendinfo *) xmalloc (sizeof (struct ssendinfo));
+ if (strchr (qcmd->zoptions, 'm') == NULL)
+ qinfo->zmail = NULL;
+ else
+ qinfo->zmail = zbufcpy (qcmd->zuser);
+ qinfo->zfile = zfile;
+ qinfo->cbytes = cbytes;
+ qinfo->flocal = TRUE;
+ qinfo->fspool = fspool;
+ qinfo->fsent = FALSE;
+ qinfo->zexec = NULL;
+
+ qtrans = qtransalc (qcmd);
+ qtrans->psendfn = flocal_send_request;
+ qtrans->pinfo = (pointer) qinfo;
+
+ return fqueue_local (qdaemon, qtrans);
+}
+
+/* Clean up after a failing local send request. If zwhy is not NULL,
+ this reports an error to the log file and to the user. */
+
+static boolean
+flocal_send_fail (qtrans, qcmd, qsys, zwhy)
+ struct stransfer *qtrans;
+ struct scmd *qcmd;
+ const struct uuconf_system *qsys;
+ const char *zwhy;
+{
+ if (zwhy != NULL)
+ {
+ char *zfree;
+
+ if (qcmd->bcmd != 'E')
+ zfree = NULL;
+ else
+ {
+ zfree = zbufalc (sizeof "Execution of \"\": "
+ + strlen (qcmd->zcmd)
+ + strlen (zwhy));
+ sprintf (zfree, "Execution of \"%s\": %s", qcmd->zcmd, zwhy);
+ zwhy = zfree;
+ }
+
+ ulog (LOG_ERROR, "%s: %s", qcmd->zfrom, zwhy);
+ (void) fmail_transfer (FALSE, qcmd->zuser, (const char *) NULL,
+ zwhy, qcmd->zfrom, (const char *) NULL,
+ qcmd->zto, qsys->uuconf_zname,
+ zsysdep_save_temp_file (qcmd->pseq));
+
+ ubuffree (zfree);
+ }
+
+ (void) fsysdep_did_work (qcmd->pseq);
+
+ if (qtrans != NULL)
+ usfree_send (qtrans);
+
+ return TRUE;
+}
+
+/* This is called when we are ready to send the request to the remote
+ system. We form the request and send it over. If the protocol
+ does not support multiple channels, we start waiting for the
+ response; otherwise we can start sending the file immediately. */
+
+static boolean
+flocal_send_request (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo;
+ char *zsend;
+ const char *znotify;
+ char absize[20];
+ boolean fret;
+
+ /* Make sure the file meets any remote size restrictions. */
+ if (qdaemon->cmax_receive != -1
+ && qdaemon->cmax_receive < qinfo->cbytes)
+ return flocal_send_fail (qtrans, &qtrans->s, qdaemon->qsys,
+ "too large for receiver");
+
+ /* Construct the notify string to send. If we are going to send a
+ size or an execution command, it must be non-empty. */
+ znotify = qtrans->s.znotify;
+ if (znotify == NULL)
+ znotify = "";
+ if ((qdaemon->ifeatures & FEATURE_SIZES) != 0
+ || (qtrans->s.bcmd == 'E'
+ && (qdaemon->ifeatures & FEATURE_EXEC) != 0))
+ {
+ if (*znotify == '\0')
+ znotify = "\"\"";
+ }
+ else
+ {
+ /* We don't need a notify string. Some crufty UUCP code can't
+ handle a pair of double quotes. */
+ if (strcmp (znotify, "\"\"") == 0)
+ znotify = "";
+ }
+
+ /* Construct the size string to send. */
+ if ((qdaemon->ifeatures & FEATURE_SIZES) == 0
+ && (qtrans->s.bcmd != 'E'
+ || (qdaemon->ifeatures & FEATURE_EXEC) == 0))
+ absize[0] = '\0';
+ else if ((qdaemon->ifeatures & FEATURE_V103) == 0)
+ sprintf (absize, "0x%lx", (unsigned long) qinfo->cbytes);
+ else
+ sprintf (absize, "%ld", qinfo->cbytes);
+
+ zsend = zbufalc (strlen (qtrans->s.zfrom) + strlen (qtrans->s.zto)
+ + strlen (qtrans->s.zuser) + strlen (qtrans->s.zoptions)
+ + strlen (qtrans->s.ztemp) + strlen (znotify)
+ + strlen (absize)
+ + (qtrans->s.zcmd != NULL ? strlen (qtrans->s.zcmd) : 0)
+ + 50);
+
+ /* If this an execution request and the other side supports
+ execution requests, we send an E command. Otherwise we send an S
+ command. The case of an execution request when we are sending
+ the fake execution file is handled just like an S request at this
+ point. */
+ if (qtrans->s.bcmd == 'E'
+ && (qdaemon->ifeatures & FEATURE_EXEC) != 0)
+ {
+ /* Send the string
+ E zfrom zto zuser zoptions ztemp imode znotify size zcmd
+ to the remote system. We put a '-' in front of the (possibly
+ empty) options and a '0' in front of the mode. */
+ sprintf (zsend, "E %s %s %s -%s %s 0%o %s %s %s", qtrans->s.zfrom,
+ qtrans->s.zto, qtrans->s.zuser, qtrans->s.zoptions,
+ qtrans->s.ztemp, qtrans->s.imode, znotify, absize,
+ qtrans->s.zcmd);
+ }
+ else
+ {
+ const char *zoptions, *zdummy;
+
+ /* Send the string
+ S zfrom zto zuser zoptions ztemp imode znotify
+ to the remote system. We put a '-' in front of the (possibly
+ empty) options and a '0' in front of the mode. If size
+ negotiation is supported, we also send the size; in this case
+ if znotify is empty we must send it as "". If this is really
+ an execution request, we have to simplify the options string
+ to remove the various execution options which may confuse the
+ remote system. SVR4 expects a string "dummy" between the
+ notify string and the size; I don't know why. */
+ if (qtrans->s.bcmd != 'E')
+ zoptions = qtrans->s.zoptions;
+ else if (strchr (qtrans->s.zoptions, 'C') != NULL)
+ {
+ /* This should set zoptions to "C", but at least one UUCP
+ program gets confused by it. That means that it will
+ fail in certain cases, but I suppose we might as well
+ kowtow to compatibility. This shouldn't matter to any
+ other program, I hope. */
+ zoptions = "";
+ }
+ else
+ zoptions = "c";
+
+ if ((qdaemon->ifeatures & FEATURE_SVR4) != 0)
+ zdummy = " dummy ";
+ else
+ zdummy = " ";
+
+ sprintf (zsend, "S %s %s %s -%s %s 0%o %s%s%s", qtrans->s.zfrom,
+ qtrans->s.zto, qtrans->s.zuser, zoptions,
+ qtrans->s.ztemp, qtrans->s.imode, znotify, zdummy,
+ absize);
+ }
+
+ fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, qtrans->ilocal,
+ qtrans->iremote);
+ ubuffree (zsend);
+ if (! fret)
+ {
+ usfree_send (qtrans);
+ return FALSE;
+ }
+
+ /* If we are using a protocol which can make multiple channels, then
+ we can open and send the file whenever we are ready. This is
+ because we will be able to distinguish the response by the
+ channel it is directed to. This assumes that every protocol
+ which supports multiple channels also supports sending the file
+ position in mid-stream, since otherwise we would not be able to
+ restart files. */
+ qtrans->fcmd = TRUE;
+ qtrans->psendfn = flocal_send_open_file;
+ qtrans->precfn = flocal_send_await_reply;
+
+ if (qdaemon->qproto->cchans > 1)
+ return fqueue_send (qdaemon, qtrans);
+ else
+ return fqueue_receive (qdaemon, qtrans);
+}
+
+/* This is called when a reply is received for the send request. As
+ described at length above, if the protocol supports multiple
+ channels we may be in the middle of sending the file, or we may
+ even finished sending the file. */
+
+static boolean
+flocal_send_await_reply (qtrans, qdaemon, zdata, cdata)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+ const char *zdata;
+ size_t cdata;
+{
+ struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo;
+ char bcmd;
+
+ if (qtrans->s.bcmd == 'E'
+ && (qdaemon->ifeatures & FEATURE_EXEC) != 0)
+ bcmd = 'E';
+ else
+ bcmd = 'S';
+ if (zdata[0] != bcmd
+ || (zdata[1] != 'Y' && zdata[1] != 'N'))
+ {
+ ulog (LOG_ERROR, "%s: Bad response to %c request: \"%s\"",
+ qtrans->s.zfrom, bcmd, zdata);
+ usfree_send (qtrans);
+ return FALSE;
+ }
+
+ if (zdata[1] == 'N')
+ {
+ const char *zerr;
+ boolean fnever;
+
+ fnever = TRUE;
+ if (zdata[2] == '2')
+ zerr = "permission denied by remote";
+ else if (zdata[2] == '4')
+ {
+ zerr = "remote cannot create work files";
+ fnever = FALSE;
+ }
+ else if (zdata[2] == '6')
+ {
+ zerr = "too large for remote now";
+ fnever = FALSE;
+ }
+ else if (zdata[2] == '7')
+ {
+ /* The file is too large to ever send. */
+ zerr = "too large for remote";
+ }
+ else if (zdata[2] == '8')
+ {
+ /* The file was already received by the remote system. This
+ is not an error, it just means that the ack from the
+ remote was lost in the previous conversation, and there
+ is no need to resend the file. */
+ zerr = NULL;
+ }
+ else
+ zerr = "unknown reason";
+
+ if (! fnever)
+ {
+ if (qtrans->s.bcmd == 'E')
+ ulog (LOG_ERROR, "Execution of \"%s\": %s", qtrans->s.zcmd,
+ zerr);
+ else
+ ulog (LOG_ERROR, "%s: %s", qtrans->s.zfrom, zerr);
+ }
+ else
+ {
+ if (! flocal_send_fail ((struct stransfer *) NULL, &qtrans->s,
+ qdaemon->qsys, zerr))
+ return FALSE;
+ }
+
+ /* If the protocol does not support multiple channels, we can
+ simply remove the transaction. Otherwise we must make sure
+ the remote side knows that we have finished sending the file
+ data. If we have already sent the entire file, there will be
+ no confusion. */
+ if (qdaemon->qproto->cchans == 1 || qinfo->fsent)
+ {
+ usfree_send (qtrans);
+ return TRUE;
+ }
+ else
+ {
+ qtrans->psendfn = flocal_send_cancelled;
+ qtrans->precfn = NULL;
+ qtrans->fsendfile = FALSE;
+ return fqueue_send (qdaemon, qtrans);
+ }
+ }
+
+ /* A number following the SY or EY is the file position to start
+ sending from. If we are already sending the file, we must set
+ the position accordingly. */
+ if (zdata[2] != '\0')
+ {
+ long cskip;
+
+ cskip = strtol ((char *) (zdata + 2), (char **) NULL, 0);
+ if (cskip > 0 && qtrans->ipos < cskip)
+ {
+ if (qtrans->fsendfile && ! qinfo->fsent)
+ {
+ if (! ffileseek (qtrans->e, cskip))
+ {
+ ulog (LOG_ERROR, "seek: %s", strerror (errno));
+ usfree_send (qtrans);
+ return FALSE;
+ }
+ }
+ qtrans->ipos = cskip;
+ }
+ }
+
+ /* Now queue up to send the file or to wait for the confirmation.
+ We already set psendfn at the end of flocal_send_request. If the
+ protocol supports multiple channels, we have already called
+ fqueue_send; calling it again would move the request in the
+ queue, which would make the log file a bit confusing. */
+ qtrans->precfn = fsend_await_confirm;
+ if (qinfo->fsent)
+ return fqueue_receive (qdaemon, qtrans);
+ else if (qdaemon->qproto->cchans <= 1)
+ return fqueue_send (qdaemon, qtrans);
+ else
+ return TRUE;
+}
+
+/* Open the file, if any, and prepare to send it. */
+
+static boolean
+flocal_send_open_file (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo;
+ const char *zuser;
+
+ /* If this is not a fake execution file, open it. */
+ if (qinfo->zexec == NULL)
+ {
+ /* If there is an ! in the user name, this is a remote request
+ queued up by fremote_xcmd_init. */
+ zuser = qtrans->s.zuser;
+ if (strchr (zuser, '!') != NULL)
+ zuser = NULL;
+
+ qtrans->e = esysdep_open_send (qdaemon->qsys, qinfo->zfile,
+ ! qinfo->fspool, zuser);
+ if (! ffileisopen (qtrans->e))
+ {
+ (void) fmail_transfer (FALSE, qtrans->s.zuser,
+ (const char *) NULL,
+ "cannot open file",
+ qtrans->s.zfrom, (const char *) NULL,
+ qtrans->s.zto,
+ qdaemon->qsys->uuconf_zname,
+ zsysdep_save_temp_file (qtrans->s.pseq));
+ (void) fsysdep_did_work (qtrans->s.pseq);
+ usfree_send (qtrans);
+
+ /* Unfortunately, there is no way to cancel a file send
+ after we've already put it in progress. So we have to
+ return FALSE to drop the connection. */
+ return FALSE;
+ }
+ }
+
+ /* If flocal_send_await_reply has received a reply with a file
+ position, it will have set qtrans->ipos to the position at which
+ to start. */
+ if (qtrans->ipos > 0)
+ {
+ if (qinfo->zexec != NULL)
+ {
+ if (qtrans->ipos > qtrans->cbytes)
+ qtrans->ipos = qtrans->cbytes;
+ }
+ else
+ {
+ if (! ffileseek (qtrans->e, qtrans->ipos))
+ {
+ ulog (LOG_ERROR, "seek: %s", strerror (errno));
+ usfree_send (qtrans);
+ return FALSE;
+ }
+ }
+ }
+
+ /* We don't bother to log sending the execution file. */
+ if (qinfo->zexec == NULL)
+ {
+ const char *zsend;
+ char *zalc;
+
+ if (qtrans->s.bcmd != 'E')
+ {
+ zsend = qtrans->s.zfrom;
+ zalc = NULL;
+ }
+ else
+ {
+ zalc = zbufalc (strlen (qtrans->s.zcmd) + sizeof " ()"
+ + strlen (qtrans->s.zfrom));
+ sprintf (zalc, "%s (%s)", qtrans->s.zcmd, qtrans->s.zfrom);
+ zsend = zalc;
+ }
+ qtrans->zlog = zbufalc (sizeof "Sending " + strlen (zsend));
+ sprintf (qtrans->zlog, "Sending %s", zsend);
+ ubuffree (zalc);
+ }
+
+ if (qdaemon->qproto->pffile != NULL)
+ {
+ boolean fhandled;
+
+ if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, TRUE,
+ qinfo->cbytes, &fhandled))
+ {
+ usfree_send (qtrans);
+ return FALSE;
+ }
+
+ if (fhandled)
+ return TRUE;
+ }
+
+ if (qinfo->zexec != NULL)
+ qtrans->psendfn = fsend_exec_file;
+ else
+ {
+ qtrans->fsendfile = TRUE;
+ qtrans->psendfn = fsend_file_end;
+ }
+
+ return fqueue_send (qdaemon, qtrans);
+}
+
+/* Cancel a file send by sending an empty buffer. This is only called
+ for a protocol which supports multiple channels. It is needed
+ so that both systems agree as to when a channel is no longer
+ needed. */
+
+static boolean
+flocal_send_cancelled (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ char *zdata;
+ size_t cdata;
+ boolean fret;
+
+ zdata = (*qdaemon->qproto->pzgetspace) (qdaemon, &cdata);
+ if (zdata == NULL)
+ {
+ usfree_send (qtrans);
+ return FALSE;
+ }
+
+ fret = (*qdaemon->qproto->pfsenddata) (qdaemon, zdata, (size_t) 0,
+ qtrans->ilocal, qtrans->iremote,
+ qtrans->ipos);
+ usfree_send (qtrans);
+ return fret;
+}
+
+/* A remote request to receive a file (meaning that we have to send a
+ file). The sequence of functions calls is as follows:
+
+ fremote_rec_file_init (open file) --> fqueue_remote
+ fremote_rec_reply (send RY, call pffile) --> fqueue_send
+ send file
+ fsend_file_end (calls pffile) --> fqueue_receive
+ fsend_await_confirm (waits for CY)
+ */
+
+boolean
+fremote_rec_file_init (qdaemon, qcmd, iremote)
+ struct sdaemon *qdaemon;
+ struct scmd *qcmd;
+ int iremote;
+{
+ const struct uuconf_system *qsys;
+ char *zfile;
+ long cbytes;
+ unsigned int imode;
+ openfile_t e;
+ struct ssendinfo *qinfo;
+ struct stransfer *qtrans;
+
+ qsys = qdaemon->qsys;
+
+ if (! qsys->uuconf_fsend_request)
+ {
+ ulog (LOG_ERROR, "%s: not permitted to send files to remote",
+ qcmd->zfrom);
+ return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote);
+ }
+
+ if (fspool_file (qcmd->zfrom))
+ {
+ ulog (LOG_ERROR, "%s: not permitted to send", qcmd->zfrom);
+ return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote);
+ }
+
+ zfile = zsysdep_local_file (qcmd->zfrom, qsys->uuconf_zpubdir);
+ if (zfile != NULL)
+ {
+ char *zbased;
+
+ zbased = zsysdep_add_base (zfile, qcmd->zto);
+ ubuffree (zfile);
+ zfile = zbased;
+ }
+ if (zfile == NULL)
+ return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote);
+
+ if (! fin_directory_list (zfile, qsys->uuconf_pzremote_send,
+ qsys->uuconf_zpubdir, TRUE, TRUE,
+ (const char *) NULL))
+ {
+ ulog (LOG_ERROR, "%s: not permitted to send", zfile);
+ ubuffree (zfile);
+ return fremote_rec_fail (qdaemon, FAILURE_PERM, iremote);
+ }
+
+ /* If the file is larger than the amount of space the other side
+ reported, we can't send it. Should we adjust this check based on
+ the restart position? */
+ cbytes = csysdep_size (zfile);
+ if (cbytes != -1
+ && ((qcmd->cbytes != -1 && qcmd->cbytes < cbytes)
+ || (qdaemon->cremote_size != -1
+ && qdaemon->cremote_size < cbytes)
+ || (qdaemon->cmax_receive != -1
+ && qdaemon->cmax_receive < cbytes)))
+ {
+ ulog (LOG_ERROR, "%s: too large to send", zfile);
+ ubuffree (zfile);
+ return fremote_rec_fail (qdaemon, FAILURE_SIZE, iremote);
+ }
+
+ imode = ixsysdep_file_mode (zfile);
+
+ e = esysdep_open_send (qsys, zfile, TRUE, (const char *) NULL);
+ if (! ffileisopen (e))
+ {
+ ubuffree (zfile);
+ return fremote_rec_fail (qdaemon, FAILURE_OPEN, iremote);
+ }
+
+ /* If the remote requested that the file send start from a
+ particular position, arrange to do so. */
+ if (qcmd->ipos > 0)
+ {
+ if (! ffileseek (e, qcmd->ipos))
+ {
+ ulog (LOG_ERROR, "seek: %s", strerror (errno));
+ ubuffree (zfile);
+ return FALSE;
+ }
+ }
+
+ qinfo = (struct ssendinfo *) xmalloc (sizeof (struct ssendinfo));
+ qinfo->zmail = NULL;
+ qinfo->zfile = zfile;
+ qinfo->cbytes = cbytes;
+ qinfo->flocal = FALSE;
+ qinfo->fspool = FALSE;
+ qinfo->fsent = FALSE;
+ qinfo->zexec = NULL;
+
+ qtrans = qtransalc (qcmd);
+ qtrans->psendfn = fremote_rec_reply;
+ qtrans->iremote = iremote;
+ qtrans->pinfo = (pointer) qinfo;
+ qtrans->e = e;
+ qtrans->ipos = qcmd->ipos;
+ qtrans->s.imode = imode;
+
+ return fqueue_remote (qdaemon, qtrans);
+}
+
+/* Reply to a receive request from the remote system, and prepare to
+ start sending the file. */
+
+static boolean
+fremote_rec_reply (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo;
+ char absend[50];
+
+ sprintf (absend, "RY 0%o 0x%lx", qtrans->s.imode,
+ (unsigned long) qinfo->cbytes);
+ if (! (*qdaemon->qproto->pfsendcmd) (qdaemon, absend, qtrans->ilocal,
+ qtrans->iremote))
+ {
+ (void) ffileclose (qtrans->e);
+ usfree_send (qtrans);
+ return FALSE;
+ }
+
+ qtrans->zlog = zbufalc (sizeof "Sending " + strlen (qtrans->s.zfrom));
+ sprintf (qtrans->zlog, "Sending %s", qtrans->s.zfrom);
+
+ if (qdaemon->qproto->pffile != NULL)
+ {
+ boolean fhandled;
+
+ if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, TRUE, TRUE,
+ qinfo->cbytes, &fhandled))
+ {
+ usfree_send (qtrans);
+ return FALSE;
+ }
+
+ if (fhandled)
+ return TRUE;
+ }
+
+ qtrans->fsendfile = TRUE;
+ qtrans->psendfn = fsend_file_end;
+ qtrans->precfn = fsend_await_confirm;
+
+ return fqueue_send (qdaemon, qtrans);
+}
+
+/* If we can't send a file as requested by the remote system, queue up
+ a failure reply which will be sent when possible. */
+
+static boolean
+fremote_rec_fail (qdaemon, twhy, iremote)
+ struct sdaemon *qdaemon;
+ enum tfailure twhy;
+ int iremote;
+{
+ enum tfailure *ptinfo;
+ struct stransfer *qtrans;
+
+ ptinfo = (enum tfailure *) xmalloc (sizeof (enum tfailure));
+ *ptinfo = twhy;
+
+ qtrans = qtransalc ((struct scmd *) NULL);
+ qtrans->psendfn = fremote_rec_fail_send;
+ qtrans->iremote = iremote;
+ qtrans->pinfo = (pointer) ptinfo;
+
+ return fqueue_remote (qdaemon, qtrans);
+}
+
+/* Send a failure string for a receive command to the remote system;
+ this is called when we are ready to reply to the command. */
+
+static boolean
+fremote_rec_fail_send (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ enum tfailure *ptinfo = (enum tfailure *) qtrans->pinfo;
+ const char *z;
+ boolean fret;
+
+ switch (*ptinfo)
+ {
+ case FAILURE_PERM:
+ case FAILURE_OPEN:
+ z = "RN2";
+ break;
+ case FAILURE_SIZE:
+ z = "RN6";
+ break;
+ default:
+ z = "RN";
+ break;
+ }
+
+ fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, z, qtrans->ilocal,
+ qtrans->iremote);
+ xfree (qtrans->pinfo);
+ utransfree (qtrans);
+ return fret;
+}
+
+/* This is called when the main loop has finished sending a file. It
+ prepares to wait for a response from the remote system. Note that
+ if this is a local request and the protocol supports multiple
+ channels, we may not even have received a confirmation of the send
+ request. */
+
+static boolean
+fsend_file_end (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo;
+
+ if (qdaemon->qproto->pffile != NULL)
+ {
+ boolean fhandled;
+
+ if (! (*qdaemon->qproto->pffile) (qdaemon, qtrans, FALSE, TRUE,
+ (long) -1, &fhandled))
+ {
+ usfree_send (qtrans);
+ return FALSE;
+ }
+
+ if (fhandled)
+ return TRUE;
+ }
+
+ qinfo->fsent = TRUE;
+
+ /* qtrans->precfn should have been set by a previous function. */
+ qtrans->fcmd = TRUE;
+ return fqueue_receive (qdaemon, qtrans);
+}
+
+/* Handle the confirmation string received after sending a file. */
+
+/*ARGSUSED*/
+static boolean
+fsend_await_confirm (qtrans, qdaemon, zdata, cdata)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+ const char *zdata;
+ size_t cdata;
+{
+ struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo;
+ boolean fnever;
+ const char *zerr;
+
+ if (qinfo->zexec == NULL)
+ (void) ffileclose (qtrans->e);
+
+ fnever = FALSE;
+ if (zdata[0] != 'C'
+ || (zdata[1] != 'Y' && zdata[1] != 'N'))
+ {
+ zerr = "bad confirmation from remote";
+ ulog (LOG_ERROR, "%s: %s \"%s\"", qtrans->s.zfrom, zerr, zdata);
+ }
+ else if (zdata[1] == 'N')
+ {
+ fnever = TRUE;
+ if (zdata[2] == '5')
+ {
+ zerr = "file could not be stored in final location";
+ ulog (LOG_ERROR, "%s: %s", qtrans->s.zfrom, zerr);
+ }
+ else
+ {
+ zerr = "file send failed for unknown reason";
+ ulog (LOG_ERROR, "%s: %s \"%s\"", qtrans->s.zfrom, zerr, zdata);
+ }
+ }
+ else
+ {
+ zerr = NULL;
+
+ /* If we receive CYM, it means that the other side wants us to
+ hang up so that they can send us something. The
+ fhangup_requested field is checked in the main loop. */
+ if (zdata[2] == 'M' && qdaemon->fmaster)
+ {
+ DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO,
+ "fsend_await_confirm: Remote has requested transfer of control");
+ qdaemon->fhangup_requested = TRUE;
+ }
+ }
+
+ ustats (zerr == NULL, qtrans->s.zuser, qdaemon->qsys->uuconf_zname,
+ TRUE, qtrans->cbytes, qtrans->isecs, qtrans->imicros,
+ qdaemon->fmaster);
+
+ if (zerr == NULL)
+ {
+ /* If this is an execution request, and the remote system
+ doesn't support execution requests, we have to set up the
+ fake execution file and loop around again. */
+ if (qtrans->s.bcmd == 'E'
+ && (qdaemon->ifeatures & FEATURE_EXEC) == 0
+ && qinfo->zexec == NULL)
+ return fsend_exec_file_init (qtrans, qdaemon);
+
+ /* Send mail about the transfer if requested. */
+ if (qinfo->zmail != NULL && *qinfo->zmail != '\0')
+ (void) fmail_transfer (TRUE, qtrans->s.zuser, qinfo->zmail,
+ (const char *) NULL,
+ qtrans->s.zfrom, (const char *) NULL,
+ qtrans->s.zto, qdaemon->qsys->uuconf_zname,
+ (const char *) NULL);
+
+ if (qtrans->s.pseq != NULL)
+ (void) fsysdep_did_work (qtrans->s.pseq);
+ }
+ else
+ {
+ /* If the file send failed, we only try to save the file and
+ send mail if it was requested locally and it will never
+ succeed. We send mail to qinfo->zmail if set, otherwise to
+ qtrans->s.zuser. I hope this is reasonable. */
+ if (fnever && qinfo->flocal)
+ {
+ (void) fmail_transfer (FALSE, qtrans->s.zuser, qinfo->zmail,
+ zerr, qtrans->s.zfrom, (const char *) NULL,
+ qtrans->s.zto, qdaemon->qsys->uuconf_zname,
+ zsysdep_save_temp_file (qtrans->s.pseq));
+ (void) fsysdep_did_work (qtrans->s.pseq);
+ }
+ }
+
+ usfree_send (qtrans);
+
+ return TRUE;
+}
+
+/* Prepare to send an execution file to a system which does not
+ support execution requests. We build the execution file in memory,
+ and then call flocal_send_request as though we were sending a real
+ file. Instead of sending a file, the code in flocal_send_open_file
+ will arrange to call fsend_exec_file which will send data out of
+ the buffer we have created. */
+
+static boolean
+fsend_exec_file_init (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo;
+ char *zxqtfile;
+ char abtname[CFILE_NAME_LEN];
+ char abxname[CFILE_NAME_LEN];
+ char *z;
+ size_t calc, clen;
+
+ z = NULL;
+ calc = 0;
+ clen = 0;
+
+ usadd_exec_line (&z, &calc, &clen, 'U', qtrans->s.zuser,
+ qdaemon->zlocalname);
+ usadd_exec_line (&z, &calc, &clen, 'F', qtrans->s.zto, "");
+ usadd_exec_line (&z, &calc, &clen, 'I', qtrans->s.zto, "");
+ if (strchr (qtrans->s.zoptions, 'N') != NULL)
+ usadd_exec_line (&z, &calc, &clen, 'N', "", "");
+ if (strchr (qtrans->s.zoptions, 'Z') != NULL)
+ usadd_exec_line (&z, &calc, &clen, 'Z', "", "");
+ if (strchr (qtrans->s.zoptions, 'R') != NULL)
+ usadd_exec_line (&z, &calc, &clen, 'R', qtrans->s.znotify, "");
+ if (strchr (qtrans->s.zoptions, 'e') != NULL)
+ usadd_exec_line (&z, &calc, &clen, 'e', "", "");
+ usadd_exec_line (&z, &calc, &clen, 'C', qtrans->s.zcmd, "");
+
+ qinfo->zexec = z;
+ qinfo->cbytes = clen;
+
+ zxqtfile = zsysdep_data_file_name (qdaemon->qsys, qdaemon->zlocalname,
+ BDEFAULT_UUX_GRADE, TRUE, abtname,
+ (char *) NULL, abxname);
+ if (zxqtfile == NULL)
+ {
+ usfree_send (qtrans);
+ return FALSE;
+ }
+ ubuffree (zxqtfile);
+
+ ubuffree ((char *) qtrans->s.zfrom);
+ qtrans->s.zfrom = zbufcpy (abtname);
+ ubuffree ((char *) qtrans->s.zto);
+ qtrans->s.zto = zbufcpy (abxname);
+ ubuffree ((char *) qtrans->s.zoptions);
+ qtrans->s.zoptions = zbufcpy ("C");
+ ubuffree ((char *) qtrans->s.ztemp);
+ qtrans->s.ztemp = zbufcpy (abtname);
+
+ qtrans->psendfn = flocal_send_request;
+ qtrans->precfn = NULL;
+ qtrans->ipos = 0;
+ qtrans->cbytes = 0;
+ qtrans->isecs = 0;
+ qtrans->imicros = 0;
+ qinfo->fsent = FALSE;
+
+ return fqueue_send (qdaemon, qtrans);
+}
+
+/* Add a line to the fake execution file. */
+
+static void
+usadd_exec_line (pz, pcalc, pclen, bcmd, z1, z2)
+ char **pz;
+ size_t *pcalc;
+ size_t *pclen;
+ int bcmd;
+ const char *z1;
+ const char *z2;
+{
+ size_t c1, c2;
+ char *znew;
+
+ c1 = strlen (z1);
+ c2 = strlen (z2);
+
+ if (*pclen + c1 + c2 + 4 >= *pcalc)
+ {
+ *pcalc += c1 + c2 + 100;
+ znew = zbufalc (*pcalc);
+ if (*pclen > 0)
+ {
+ memcpy (znew, *pz, *pclen);
+ ubuffree (*pz);
+ }
+ *pz = znew;
+ }
+
+ znew = *pz + *pclen;
+ *znew++ = bcmd;
+ if (*z1 != '\0')
+ {
+ *znew++ = ' ';
+ memcpy (znew, z1, c1);
+ znew += c1;
+ if (*z2 != '\0')
+ {
+ *znew++ = ' ';
+ memcpy (znew, z2, c2);
+ znew += c2;
+ }
+ }
+
+ /* In some bizarre non-Unix case we might have to worry about the
+ newline here. We don't know how a newline is normally written
+ out to a file, but whatever is written to a file is what we will
+ normally transfer. If that is not simply \n then this fake
+ execution file will not look like other execution files. */
+ *znew++ = '\n';
+
+ *pclen = znew - *pz;
+}
+
+/* This routine is called to send the contents of the fake execution
+ file. Normally file data is sent by the floop routine in trans.c,
+ but since we don't have an actual file we must do it here. This
+ routine sends the complete buffer, followed by a zero length
+ packet, and then calls fsend_file_end. */
+
+static boolean
+fsend_exec_file (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ struct ssendinfo *qinfo = (struct ssendinfo *) qtrans->pinfo;
+ char *zdata;
+ size_t cdata;
+ size_t csend;
+
+ zdata = (*qdaemon->qproto->pzgetspace) (qdaemon, &cdata);
+ if (zdata == NULL)
+ {
+ usfree_send (qtrans);
+ return FALSE;
+ }
+
+ csend = qinfo->cbytes - qtrans->ipos;
+ if (csend > cdata)
+ csend = cdata;
+
+ memcpy (zdata, qinfo->zexec + qtrans->ipos, csend);
+
+ if (! (*qdaemon->qproto->pfsenddata) (qdaemon, zdata, csend,
+ qtrans->ilocal, qtrans->iremote,
+ qtrans->ipos))
+ {
+ usfree_send (qtrans);
+ return FALSE;
+ }
+
+ qtrans->cbytes += csend;
+ qtrans->ipos += csend;
+
+ if (csend == 0)
+ return fsend_file_end (qtrans, qdaemon);
+
+ /* Leave the job on the send queue. */
+
+ return TRUE;
+}
diff --git a/gnu/libexec/uucp/uucico/time.c b/gnu/libexec/uucp/uucico/time.c
new file mode 100644
index 0000000000000..4305e990e2453
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/time.c
@@ -0,0 +1,130 @@
+/* time.c
+ Routines to deal with UUCP time spans.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char time_rcsid[] = "$Id: time.c,v 1.1 1993/08/05 18:27:20 conklin Exp $";
+#endif
+
+#include <ctype.h>
+
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+
+#include "uudefs.h"
+#include "uuconf.h"
+
+/* External functions. */
+#ifndef time
+extern time_t time ();
+#endif
+#ifndef localtime
+extern struct tm *localtime ();
+#endif
+
+/* See if the current time matches a time span. If it does, return
+ TRUE, set *pival to the value for the matching span, and set
+ *pcretry to the retry for the matching span. Otherwise return
+ FALSE. */
+
+boolean
+ftimespan_match (qspan, pival, pcretry)
+ const struct uuconf_timespan *qspan;
+ long *pival;
+ int *pcretry;
+{
+ time_t inow;
+ struct tm *qtm;
+ int itm;
+ const struct uuconf_timespan *q;
+
+ if (qspan == NULL)
+ return FALSE;
+
+ time (&inow);
+ qtm = localtime (&inow);
+
+ /* Get the number of minutes since Sunday for the time. */
+ itm = qtm->tm_wday * 24 * 60 + qtm->tm_hour * 60 + qtm->tm_min;
+
+ for (q = qspan; q != NULL; q = q->uuconf_qnext)
+ {
+ if (q->uuconf_istart <= itm && itm <= q->uuconf_iend)
+ {
+ if (pival != NULL)
+ *pival = q->uuconf_ival;
+ if (pcretry != NULL)
+ *pcretry = q->uuconf_cretry;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Determine the maximum size that may ever be transferred, according
+ to a timesize span. This returns -1 if there is no limit. */
+
+long
+cmax_size_ever (qtimesize)
+ const struct uuconf_timespan *qtimesize;
+{
+ long imax;
+ const struct uuconf_timespan *q;
+
+ if (qtimesize == NULL)
+ return -1;
+
+ /* Look through the list of spans. If there is any gap larger than
+ 1 hour, we assume there are no restrictions. Otherwise we keep
+ track of the largest value we see. I picked 1 hour arbitrarily,
+ on the theory that a 1 hour span to transfer large files might
+ actually occur, and is probably not an accident. */
+ if (qtimesize->uuconf_istart >= 60)
+ return -1;
+
+ imax = -1;
+
+ for (q = qtimesize; q != NULL; q = q->uuconf_qnext)
+ {
+ if (q->uuconf_qnext == NULL)
+ {
+ if (q->uuconf_iend <= 6 * 24 * 60 + 23 * 60)
+ return -1;
+ }
+ else
+ {
+ if (q->uuconf_iend + 60 <= q->uuconf_qnext->uuconf_istart)
+ return -1;
+ }
+
+ if (imax < q->uuconf_ival)
+ imax = q->uuconf_ival;
+ }
+
+ return imax;
+}
diff --git a/gnu/libexec/uucp/uucico/trans.c b/gnu/libexec/uucp/uucico/trans.c
new file mode 100644
index 0000000000000..2bb4d228195ca
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/trans.c
@@ -0,0 +1,1439 @@
+/* trans.c
+ Routines to handle file transfers.
+
+ Copyright (C) 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char trans_rcsid[] = "$Id: trans.c,v 1.1 1993/08/05 18:27:21 conklin Exp $";
+#endif
+
+#include <errno.h>
+
+#include "uudefs.h"
+#include "uuconf.h"
+#include "prot.h"
+#include "system.h"
+#include "trans.h"
+
+/* Local functions. */
+
+static void utqueue P((struct stransfer **, struct stransfer *,
+ boolean fhead));
+static void utdequeue P((struct stransfer *));
+static void utchanalc P((struct sdaemon *qdaemon, struct stransfer *qtrans));
+__inline__ static struct stransfer *qtchan P((int ichan));
+__inline__ static void utchanfree P((struct stransfer *qtrans));
+static boolean ftcharge P((struct sdaemon *qdaemon,
+ struct stransfer *qtrans,
+ boolean fsend, boolean fforce));
+static boolean fcheck_queue P((struct sdaemon *qdaemon));
+static boolean ftadd_cmd P((struct sdaemon *qdaemon, const char *z,
+ size_t cdata, int iremote, boolean flast));
+static boolean fremote_hangup_reply P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static boolean flocal_poll_file P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+
+/* Queue of transfer structures that are ready to start which have
+ been requested by the local system. These are only permitted to
+ start when the local system is the master. */
+static struct stransfer *qTlocal;
+
+/* Queue of transfer structures that are ready to start which have
+ been requested by the remote system. These are responses to
+ commands received from the remote system, and should be started as
+ soon as possible. */
+static struct stransfer *qTremote;
+
+/* Queue of transfer structures that have been started and want to
+ send information. This should be static, but the 'a' protocol
+ looks at it, at least for now. */
+struct stransfer *qTsend;
+
+/* Queue of transfer structures that have been started and are waiting
+ to receive information. */
+static struct stransfer *qTreceive;
+
+/* Queue of free transfer structures. */
+static struct stransfer *qTavail;
+
+/* Array of transfer structures indexed by local channel number. This
+ is maintained for local jobs. */
+static struct stransfer *aqTchan[IMAX_CHAN + 1];
+
+/* Number of local channel numbers currently allocated. */
+static int cTchans;
+
+/* Next channel number to allocate. */
+static int iTchan;
+
+/* Array of transfer structures indexed by remote channel number.
+ This is maintained for remote jobs. */
+static struct stransfer *aqTremote[IMAX_CHAN + 1];
+
+/* A structure used to charge time to file transfers. */
+struct scharge
+{
+ /* The transfer we are currently charging. */
+ struct stransfer *qtrans;
+ /* The time at the last update. */
+ long isecs;
+ long imicros;
+};
+
+/* We are always charging one send and one receive. */
+static struct scharge sTsend;
+static struct scharge sTreceive;
+
+/* The minimum amount of time, in seconds, to wait between times we
+ check the spool directory, if we are busy transferring data. If we
+ have nothing to do, we will check the spool directory regardless of
+ how long ago the last check was. This should probably be
+ configurable. */
+#define CCHECKWAIT (600)
+
+/* The time we last checked the spool directory for work. This is set
+ from the return value of ixsysdep_process_time, not ixsysdep_time,
+ for convenience in the routines which use it. */
+static long iTchecktime;
+
+/* The size of the command we have read so far in ftadd_cmd. */
+static size_t cTcmdlen;
+
+/* The structure we use when waiting for an acknowledgement of a
+ confirmed received file in fsent_receive_ack, and a list of those
+ structures. */
+
+struct sreceive_ack
+{
+ struct sreceive_ack *qnext;
+ char *zto;
+ char *ztemp;
+ boolean fmarked;
+};
+
+static struct sreceive_ack *qTreceive_ack;
+
+/* Queue up a transfer structure before *pq. This puts it at the head
+ or the tail of the list headed by *pq. */
+
+static void
+utqueue (pq, q, fhead)
+ struct stransfer **pq;
+ struct stransfer *q;
+ boolean fhead;
+{
+ if (*pq == NULL)
+ {
+ *pq = q;
+ q->qprev = q->qnext = q;
+ }
+ else
+ {
+ q->qnext = *pq;
+ q->qprev = (*pq)->qprev;
+ q->qprev->qnext = q;
+ q->qnext->qprev = q;
+ if (fhead)
+ *pq = q;
+ }
+ q->pqqueue = pq;
+}
+
+/* Dequeue a transfer structure. */
+
+static void
+utdequeue (q)
+ struct stransfer *q;
+{
+ if (q->pqqueue != NULL)
+ {
+ if (*(q->pqqueue) == q)
+ {
+ if (q->qnext == q)
+ *(q->pqqueue) = NULL;
+ else
+ *(q->pqqueue) = q->qnext;
+ }
+ q->pqqueue = NULL;
+ }
+ if (q->qprev != NULL)
+ q->qprev->qnext = q->qnext;
+ if (q->qnext != NULL)
+ q->qnext->qprev = q->qprev;
+ q->qprev = NULL;
+ q->qnext = NULL;
+}
+
+/* Queue up a transfer structure requested by the local system. */
+
+/*ARGSIGNORED*/
+boolean
+fqueue_local (qdaemon, qtrans)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+{
+ utdequeue (qtrans);
+ utqueue (&qTlocal, qtrans, FALSE);
+ return TRUE;
+}
+
+/* Queue up a transfer structure requested by the remote system. The
+ stransfer structure should have the iremote field set. We need to
+ record it, so that any subsequent data associated with this
+ channel can be routed to the right place. */
+
+boolean
+fqueue_remote (qdaemon, qtrans)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+{
+ DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "fqueue_remote: Channel %d",
+ qtrans->iremote);
+ if (qtrans->iremote > 0)
+ aqTremote[qtrans->iremote] = qtrans;
+ utdequeue (qtrans);
+ utqueue (&qTremote, qtrans, FALSE);
+
+ /* We just received data for this transfer, so start charging. */
+ return ftcharge (qdaemon, qtrans, FALSE, FALSE);
+}
+
+/* Queue up a transfer with something to send. */
+
+boolean
+fqueue_send (qdaemon, qtrans)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+{
+#if DEBUG > 0
+ if (qtrans->psendfn == NULL)
+ ulog (LOG_FATAL, "fqueue_send: Bad call");
+#endif
+ utdequeue (qtrans);
+ utqueue (&qTsend, qtrans, FALSE);
+
+ /* Since we're now going to wait to send data, don't charge this
+ transfer for receive time. */
+ if (qtrans == sTreceive.qtrans)
+ return ftcharge (qdaemon, (struct stransfer *) NULL, FALSE, FALSE);
+ return TRUE;
+}
+
+/* Queue up a transfer with something to receive. */
+
+boolean
+fqueue_receive (qdaemon, qtrans)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+{
+#if DEBUG > 0
+ if (qtrans->precfn == NULL)
+ ulog (LOG_FATAL, "fqueue_receive: Bad call");
+#endif
+ utdequeue (qtrans);
+ utqueue (&qTreceive, qtrans, FALSE);
+
+ /* Since we are now going to wait to receive data, don't charge this
+ transfer for send time. */
+ if (qtrans == sTsend.qtrans)
+ return ftcharge (qdaemon, (struct stransfer *) NULL, TRUE, FALSE);
+ return TRUE;
+}
+
+/* Get a new local channel number. */
+
+static void
+utchanalc (qdaemon, qtrans)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+{
+ do
+ {
+ ++iTchan;
+ if (iTchan > qdaemon->qproto->cchans)
+ iTchan = 1;
+ }
+ while (aqTchan[iTchan] != NULL);
+
+ qtrans->ilocal = iTchan;
+ aqTchan[iTchan] = qtrans;
+ ++cTchans;
+}
+
+/* Return the transfer for a channel number. */
+
+__inline__
+static struct stransfer *
+qtchan (ic)
+ int ic;
+{
+ return aqTchan[ic];
+}
+
+/* Clear the channel number for a transfer. */
+
+__inline__
+static void
+utchanfree (qt)
+ struct stransfer *qt;
+{
+ if (qt->ilocal != 0)
+ {
+ aqTchan[qt->ilocal] = NULL;
+ qt->ilocal = 0;
+ --cTchans;
+ }
+}
+
+/* Allocate a new transfer structure. */
+
+struct stransfer *
+qtransalc (qcmd)
+ struct scmd *qcmd;
+{
+ register struct stransfer *q;
+
+ q = qTavail;
+ if (q != NULL)
+ utdequeue (q);
+ else
+ q = (struct stransfer *) xmalloc (sizeof (struct stransfer));
+ q->qnext = NULL;
+ q->qprev = NULL;
+ q->pqqueue = NULL;
+ q->psendfn = NULL;
+ q->precfn = NULL;
+ q->pinfo = NULL;
+ q->fsendfile = FALSE;
+ q->frecfile = FALSE;
+ q->e = EFILECLOSED;
+ q->ipos = 0;
+ q->fcmd = FALSE;
+ q->zcmd = NULL;
+ q->ccmd = 0;
+ q->ilocal = 0;
+ q->iremote = 0;
+ if (qcmd != NULL)
+ {
+ q->s = *qcmd;
+ q->s.zfrom = zbufcpy (qcmd->zfrom);
+ q->s.zto = zbufcpy (qcmd->zto);
+ q->s.zuser = zbufcpy (qcmd->zuser);
+ q->s.zoptions = zbufcpy (qcmd->zoptions);
+ q->s.ztemp = zbufcpy (qcmd->ztemp);
+ q->s.znotify = zbufcpy (qcmd->znotify);
+ q->s.zcmd = zbufcpy (qcmd->zcmd);
+ }
+ else
+ {
+ q->s.zfrom = NULL;
+ q->s.zto = NULL;
+ q->s.zuser = NULL;
+ q->s.zoptions = NULL;
+ q->s.ztemp = NULL;
+ q->s.znotify = NULL;
+ q->s.zcmd = NULL;
+ }
+ q->isecs = 0;
+ q->imicros = 0;
+ q->cbytes = 0;
+
+ return q;
+}
+
+/* Free a transfer structure. This does not free any pinfo
+ information that may have been allocated. */
+
+void
+utransfree (q)
+ struct stransfer *q;
+{
+ ubuffree (q->zcmd);
+ ubuffree ((char *) q->s.zfrom);
+ ubuffree ((char *) q->s.zto);
+ ubuffree ((char *) q->s.zuser);
+ ubuffree ((char *) q->s.zoptions);
+ ubuffree ((char *) q->s.ztemp);
+ ubuffree ((char *) q->s.znotify);
+ ubuffree ((char *) q->s.zcmd);
+
+ utchanfree (q);
+ if (q->iremote > 0)
+ {
+ aqTremote[q->iremote] = NULL;
+ q->iremote = 0;
+ }
+
+#if DEBUG > 0
+ q->zcmd = NULL;
+ q->s.zfrom = NULL;
+ q->s.zto = NULL;
+ q->s.zuser = NULL;
+ q->s.zoptions = NULL;
+ q->s.ztemp = NULL;
+ q->s.znotify = NULL;
+ q->s.zcmd = NULL;
+ q->psendfn = NULL;
+ q->precfn = NULL;
+#endif
+
+ /* Don't try to charge time to this structure any longer. */
+ if (sTsend.qtrans == q)
+ sTsend.qtrans = NULL;
+ if (sTreceive.qtrans == q)
+ sTreceive.qtrans = NULL;
+
+ utdequeue (q);
+ utqueue (&qTavail, q, FALSE);
+}
+
+/* Handle timing of file tranfers. This is called when processing
+ starts for a transfer structure. All time up to the next call to
+ this function is charged to that transfer structure. Sending time
+ and receiving time are charged separately. Normally if we are
+ about to start charging the same structure we are already charging,
+ we do nothing; but if the fforce argument is TRUE, we charge the
+ time anyhow. */
+
+static boolean
+ftcharge (qdaemon, qtrans, fsend, fforce)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+ boolean fsend;
+ boolean fforce;
+{
+ struct scharge *qcharge, *qother;
+ long inextsecs, inextmicros;
+
+ if (fsend)
+ {
+ qcharge = &sTsend;
+ qother = &sTreceive;
+ }
+ else
+ {
+ qcharge = &sTreceive;
+ qother = &sTsend;
+ }
+
+ if (! fforce && qtrans == qcharge->qtrans)
+ return TRUE;
+
+ inextsecs = ixsysdep_process_time (&inextmicros);
+ if (qcharge->qtrans != NULL)
+ {
+ qcharge->qtrans->isecs += inextsecs - qcharge->isecs;
+ qcharge->qtrans->imicros += inextmicros - qcharge->imicros;
+
+ /* If we are charging the same structure for both send and
+ receive, update the time we are not currently charging so
+ that we don't charge twice for the same time. */
+ if (qcharge->qtrans == qother->qtrans)
+ {
+ qother->isecs = inextsecs;
+ qother->imicros = inextmicros;
+ }
+ }
+
+ qcharge->qtrans = qtrans;
+ qcharge->isecs = inextsecs;
+ qcharge->imicros = inextmicros;
+
+ /* If enough time has elapsed since the last time we checked the
+ queue, check it again. We do this here because we have already
+ gone to the trouble of getting the time. */
+ if (inextsecs - iTchecktime >= CCHECKWAIT)
+ {
+ if (! fcheck_queue (qdaemon))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* Gather local commands and queue them up for later processing. Also
+ recompute time based control values. */
+
+boolean
+fqueue (qdaemon, pfany)
+ struct sdaemon *qdaemon;
+ boolean *pfany;
+{
+ const struct uuconf_system *qsys;
+ int bgrade;
+ struct uuconf_timespan *qlocal_size, *qremote_size;
+
+ if (pfany != NULL)
+ *pfany = FALSE;
+
+ qsys = qdaemon->qsys;
+
+ /* If we are not the caller, the grade will be set during the
+ initial handshake. */
+ if (! qdaemon->fcaller)
+ bgrade = qdaemon->bgrade;
+ else
+ {
+ long ival;
+
+ if (! ftimespan_match (qsys->uuconf_qtimegrade, &ival,
+ (int *) NULL))
+ bgrade = '\0';
+ else
+ bgrade = (char) ival;
+ }
+
+ /* Determine the maximum sizes we can send and receive. */
+ if (qdaemon->fcaller)
+ {
+ qlocal_size = qsys->uuconf_qcall_local_size;
+ qremote_size = qsys->uuconf_qcall_remote_size;
+ }
+ else
+ {
+ qlocal_size = qsys->uuconf_qcalled_local_size;
+ qremote_size = qsys->uuconf_qcalled_remote_size;
+ }
+
+ if (! ftimespan_match (qlocal_size, &qdaemon->clocal_size, (int *) NULL))
+ qdaemon->clocal_size = (long) -1;
+ if (! ftimespan_match (qremote_size, &qdaemon->cremote_size, (int *) NULL))
+ qdaemon->cremote_size = (long) -1;
+
+ if (bgrade == '\0')
+ return TRUE;
+
+ if (! fsysdep_get_work_init (qsys, bgrade))
+ return FALSE;
+
+ while (TRUE)
+ {
+ struct scmd s;
+
+ if (! fsysdep_get_work (qsys, bgrade, &s))
+ return FALSE;
+
+ if (s.bcmd == 'H')
+ {
+ ulog_user ((const char *) NULL);
+ break;
+ }
+
+ if (s.bcmd == 'P')
+ {
+ struct stransfer *qtrans;
+
+ /* A poll file. */
+ ulog_user ((const char *) NULL);
+ qtrans = qtransalc (&s);
+ qtrans->psendfn = flocal_poll_file;
+ if (! fqueue_local (qdaemon, qtrans))
+ return FALSE;
+ continue;
+ }
+
+ ulog_user (s.zuser);
+
+ switch (s.bcmd)
+ {
+ case 'S':
+ case 'E':
+ if (! flocal_send_file_init (qdaemon, &s))
+ return FALSE;
+ break;
+ case 'R':
+ if (! flocal_rec_file_init (qdaemon, &s))
+ return FALSE;
+ break;
+ case 'X':
+ if (! flocal_xcmd_init (qdaemon, &s))
+ return FALSE;
+ break;
+#if DEBUG > 0
+ default:
+ ulog (LOG_FATAL, "fqueue: Can't happen");
+ break;
+#endif
+ }
+ }
+
+ if (pfany != NULL)
+ *pfany = qTlocal != NULL;
+
+ iTchecktime = ixsysdep_process_time ((long *) NULL);
+
+ return TRUE;
+}
+
+/* Clear everything off the work queue. This is used when the call is
+ complete, or if the call is never made. */
+
+void
+uclear_queue (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ int i;
+
+ usysdep_get_work_free (qdaemon->qsys);
+
+ qTlocal = NULL;
+ qTremote = NULL;
+ qTsend = NULL;
+ qTreceive = NULL;
+ cTchans = 0;
+ iTchan = 0;
+ sTsend.qtrans = NULL;
+ sTreceive.qtrans = NULL;
+ cTcmdlen = 0;
+ qTreceive_ack = NULL;
+ for (i = 0; i < IMAX_CHAN + 1; i++)
+ {
+ aqTchan[i] = NULL;
+ aqTremote[i] = NULL;
+ }
+}
+
+/* Recheck the work queue during a conversation. This is only called
+ if it's been more than CCHECKWAIT seconds since the last time the
+ queue was checked. */
+
+static boolean
+fcheck_queue (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ int cchans;
+
+ /* Only check if we are the master, or if there are multiple
+ channels, or if we aren't already trying to get the other side to
+ hang up. Otherwise, there's nothing we can do with any new jobs
+ we might find. */
+ if ((qdaemon->ireliable & UUCONF_RELIABLE_FULLDUPLEX) == 0)
+ cchans = 1;
+ else
+ cchans = qdaemon->qproto->cchans;
+ if (qdaemon->fmaster
+ || cchans > 1
+ || ! qdaemon->frequest_hangup)
+ {
+ boolean fany;
+
+ DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO,
+ "fcheck_queue: Rechecking work queue");
+ if (! fqueue (qdaemon, &fany))
+ return FALSE;
+
+ /* If we found something to do, and we're not the master, and we
+ don't have multiple channels to send new jobs over, try to
+ get the other side to hang up. */
+ if (fany && ! qdaemon->fmaster && cchans <= 1)
+ qdaemon->frequest_hangup = TRUE;
+ }
+
+ return TRUE;
+}
+
+/* The main transfer loop. The uucico daemon spends essentially all
+ its time in this function. */
+
+boolean
+floop (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ int cchans;
+ boolean fret;
+
+ /* If we are using a half-duplex line, act as though we have only a
+ single channel; otherwise we might start a send and a receive at
+ the same time. */
+ if ((qdaemon->ireliable & UUCONF_RELIABLE_FULLDUPLEX) == 0)
+ cchans = 1;
+ else
+ cchans = qdaemon->qproto->cchans;
+
+ fret = TRUE;
+
+ while (! qdaemon->fhangup)
+ {
+ register struct stransfer *q;
+
+#if DEBUG > 1
+ /* If we're doing any debugging, close the log and debugging
+ files regularly. This will let people copy them off and
+ remove them while the conversation is in progresss. */
+ if (iDebug != 0)
+ {
+ ulog_close ();
+ ustats_close ();
+ }
+#endif
+
+ if (qdaemon->fmaster)
+ {
+ boolean fhangup;
+
+ /* We've managed to become the master, so we no longer want
+ to request a hangup. */
+ qdaemon->frequest_hangup = FALSE;
+
+ fhangup = FALSE;
+
+ if (qdaemon->fhangup_requested
+ && qTsend == NULL)
+ {
+ /* The remote system has requested that we transfer
+ control by sending CYM after receiving a file. */
+ DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO,
+ "floop: Transferring control at remote request");
+ fhangup = TRUE;
+ }
+ else if (qTremote == NULL
+ && qTlocal == NULL
+ && qTsend == NULL
+ && qTreceive == NULL)
+ {
+ /* We don't have anything to do. Try to find some new
+ jobs. If we can't, transfer control. */
+ if (! fqueue (qdaemon, (boolean *) NULL))
+ {
+ fret = FALSE;
+ break;
+ }
+ if (qTlocal == NULL)
+ {
+ DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO,
+ "floop: No work for master");
+ fhangup = TRUE;
+ }
+ }
+
+ if (fhangup)
+ {
+ if (! (*qdaemon->qproto->pfsendcmd) (qdaemon, "H", 0, 0))
+ {
+ fret = FALSE;
+ break;
+ }
+ qdaemon->fmaster = FALSE;
+ }
+ }
+
+ /* If we are no long the master, clear any requested hangup. We
+ may have already hung up before checking this variable in the
+ block above. */
+ if (! qdaemon->fmaster)
+ qdaemon->fhangup_requested = FALSE;
+
+ /* Immediately queue up any remote jobs. We don't need local
+ channel numbers for them, since we can disambiguate based on
+ the remote channel number. */
+ while (qTremote != NULL)
+ {
+ q = qTremote;
+ utdequeue (q);
+ utqueue (&qTsend, q, TRUE);
+ }
+
+ /* If we are the master, or if we have multiple channels, try to
+ queue up additional local jobs. */
+ if (qdaemon->fmaster || cchans > 1)
+ {
+ while (qTlocal != NULL && cTchans < cchans)
+ {
+ /* We have room for an additional channel. */
+ q = qTlocal;
+ if (! fqueue_send (qdaemon, q))
+ {
+ fret = FALSE;
+ break;
+ }
+ utchanalc (qdaemon, q);
+ }
+ if (! fret)
+ break;
+ }
+
+ q = qTsend;
+
+ if (q == NULL)
+ {
+ ulog_user ((const char *) NULL);
+ DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "floop: Waiting for data");
+ if (! (*qdaemon->qproto->pfwait) (qdaemon))
+ {
+ fret = FALSE;
+ break;
+ }
+ }
+ else
+ {
+ ulog_user (q->s.zuser);
+
+ if (! q->fsendfile)
+ {
+ if (! ftcharge (qdaemon, q, TRUE, TRUE))
+ {
+ fret = FALSE;
+ break;
+ }
+
+ if (! (*q->psendfn) (q, qdaemon))
+ {
+ fret = FALSE;
+ break;
+ }
+ }
+ else
+ {
+ if (! ftcharge (qdaemon, q, TRUE, FALSE))
+ {
+ fret = FALSE;
+ break;
+ }
+
+ if (q->zlog != NULL)
+ {
+ ulog (LOG_NORMAL, "%s", q->zlog);
+ ubuffree (q->zlog);
+ q->zlog = NULL;
+ }
+
+ /* We can read the file in a tight loop until qTremote
+ changes or until we have transferred the entire file.
+ We can disregard any changes to qTlocal since we
+ already have something to send anyhow. */
+ while (qTremote == NULL)
+ {
+ char *zdata;
+ size_t cdata;
+ long ipos;
+
+ zdata = (*qdaemon->qproto->pzgetspace) (qdaemon, &cdata);
+ if (zdata == NULL)
+ {
+ fret = FALSE;
+ break;
+ }
+
+ if (ffileeof (q->e))
+ cdata = 0;
+ else
+ {
+ cdata = cfileread (q->e, zdata, cdata);
+ if (ffilereaderror (q->e, cdata))
+ {
+ /* There is no way to report a file reading
+ error, so we just drop the connection. */
+ ulog (LOG_ERROR, "read: %s", strerror (errno));
+ fret = FALSE;
+ break;
+ }
+ }
+
+ ipos = q->ipos;
+ q->ipos += cdata;
+ q->cbytes += cdata;
+
+ if (! (*qdaemon->qproto->pfsenddata) (qdaemon, zdata,
+ cdata, q->ilocal,
+ q->iremote, ipos))
+ {
+ fret = FALSE;
+ break;
+ }
+
+ /* It is possible that this transfer has just been
+ cancelled. */
+ if (q != qTsend || ! q->fsendfile)
+ break;
+
+ if (cdata == 0)
+ {
+ /* We must update the time now, because this
+ call may make an entry in the statistics
+ file. */
+ if (! ftcharge (qdaemon, q, TRUE, TRUE))
+ fret = FALSE;
+ q->fsendfile = FALSE;
+ if (! (*q->psendfn) (q, qdaemon))
+ fret = FALSE;
+ break;
+ }
+ }
+
+ if (! fret)
+ break;
+ }
+ }
+ }
+
+ ulog_user ((const char *) NULL);
+
+ (void) (*qdaemon->qproto->pfshutdown) (qdaemon);
+
+ if (fret)
+ uwindow_acked (qdaemon, TRUE);
+ else
+ ufailed (qdaemon);
+
+ return fret;
+}
+
+/* This is called by the protocol routines when they have received
+ some data. If pfexit is not NULL, *pfexit should be set to TRUE if
+ the protocol receive loop should exit back to the main floop
+ routine, above. It is only important to set *pfexit to TRUE if the
+ main loop called the pfwait entry point, so we need never set it to
+ TRUE if we just receive data for a file. This routine never sets
+ *pfexit to FALSE. */
+
+boolean
+fgot_data (qdaemon, zfirst, cfirst, zsecond, csecond, ilocal, iremote, ipos,
+ fallacked, pfexit)
+ struct sdaemon *qdaemon;
+ const char *zfirst;
+ size_t cfirst;
+ const char *zsecond;
+ size_t csecond;
+ int ilocal;
+ int iremote;
+ long ipos;
+ boolean fallacked;
+ boolean *pfexit;
+{
+ struct stransfer *q;
+ int cwrote;
+ boolean fret;
+
+ if (fallacked && qTreceive_ack != NULL)
+ uwindow_acked (qdaemon, TRUE);
+
+ /* Now we have to decide which transfer structure gets the data. If
+ ilocal is -1, it means that the protocol does not know where to
+ route the data. In that case we route it to the first transfer
+ that is waiting for data, or, if none, as a new command. If
+ ilocal is 0, we either select based on the remote channel number
+ or we have a new command. */
+ if (ilocal == -1 && qTreceive != NULL)
+ q = qTreceive;
+ else if (ilocal == 0 && iremote > 0 && aqTremote[iremote] != NULL)
+ q = aqTremote[iremote];
+ else if (ilocal <= 0)
+ {
+ const char *znull;
+
+ ulog_user ((const char *) NULL);
+
+ /* This data is part of a command. If there is no null
+ character in the data, this string will be continued by the
+ next packet. Otherwise this must be the last string in the
+ command, and we don't care about what comes after the null
+ byte. */
+ znull = (const char *) memchr (zfirst, '\0', cfirst);
+ if (znull != NULL)
+ fret = ftadd_cmd (qdaemon, zfirst, (size_t) (znull - zfirst),
+ iremote, TRUE);
+ else
+ {
+ fret = ftadd_cmd (qdaemon, zfirst, cfirst, iremote, FALSE);
+ if (fret && csecond > 0)
+ {
+ znull = (const char *) memchr (zsecond, '\0', csecond);
+ if (znull != NULL)
+ fret = ftadd_cmd (qdaemon, zsecond,
+ (size_t) (znull - zsecond), iremote, TRUE);
+ else
+ fret = ftadd_cmd (qdaemon, zsecond, csecond, iremote, FALSE);
+ }
+ }
+
+ if (pfexit != NULL && (qdaemon->fhangup || qTremote != NULL))
+ *pfexit = TRUE;
+
+ return fret;
+ }
+ else
+ {
+ /* Get the transfer structure this data is intended for. */
+
+ q = qtchan (ilocal);
+ }
+
+#if DEBUG > 0
+ if (q == NULL || q->precfn == NULL)
+ {
+ ulog (LOG_ERROR, "Protocol error: %lu bytes remote %d local %d",
+ (unsigned long) (cfirst + csecond),
+ iremote, ilocal);
+ return FALSE;
+ }
+#endif
+
+ ulog_user (q->s.zuser);
+
+ fret = TRUE;
+
+ /* If we're receiving a command, then accumulate it up to the null
+ byte. */
+ if (q->fcmd)
+ {
+ const char *znull;
+
+ znull = NULL;
+ while (cfirst > 0)
+ {
+ size_t cnew;
+ char *znew;
+
+ znull = (const char *) memchr (zfirst, '\0', cfirst);
+ if (znull != NULL)
+ cnew = znull - zfirst;
+ else
+ cnew = cfirst;
+ znew = zbufalc (q->ccmd + cnew + 1);
+ memcpy (znew, q->zcmd, q->ccmd);
+ memcpy (znew + q->ccmd, zfirst, cnew);
+ znew[q->ccmd + cnew] = '\0';
+ ubuffree (q->zcmd);
+ q->zcmd = znew;
+ q->ccmd += cnew;
+
+ if (znull != NULL)
+ break;
+
+ zfirst = zsecond;
+ cfirst = csecond;
+ csecond = 0;
+ }
+
+ if (znull != NULL)
+ {
+ char *zcmd;
+ size_t ccmd;
+
+ if (! ftcharge (qdaemon, q, FALSE, TRUE))
+ fret = FALSE;
+ zcmd = q->zcmd;
+ ccmd = q->ccmd;
+ q->fcmd = FALSE;
+ q->zcmd = NULL;
+ q->ccmd = 0;
+ if (! (*q->precfn) (q, qdaemon, zcmd, ccmd + 1))
+ fret = FALSE;
+ ubuffree (zcmd);
+ }
+ else
+ {
+ if (! ftcharge (qdaemon, q, FALSE, FALSE))
+ fret = FALSE;
+ }
+
+ if (pfexit != NULL
+ && (qdaemon->fhangup
+ || qdaemon->fmaster
+ || qTsend != NULL))
+ *pfexit = TRUE;
+ }
+ else if (! q->frecfile || cfirst == 0)
+ {
+ /* We're either not receiving a file or the file transfer is
+ complete. */
+ if (! ftcharge (qdaemon, q, FALSE, TRUE))
+ fret = FALSE;
+ q->frecfile = FALSE;
+ if (! (*q->precfn) (q, qdaemon, zfirst, cfirst))
+ fret = FALSE;
+ if (fret && csecond > 0)
+ return fgot_data (qdaemon, zsecond, csecond,
+ (const char *) NULL, (size_t) 0,
+ ilocal, iremote, ipos + (long) cfirst,
+ FALSE, pfexit);
+ if (pfexit != NULL
+ && (qdaemon->fhangup
+ || qdaemon->fmaster
+ || qTsend != NULL))
+ *pfexit = TRUE;
+ }
+ else
+ {
+ if (! ftcharge (qdaemon, q, FALSE, FALSE))
+ fret = FALSE;
+
+ if (q->zlog != NULL)
+ {
+ ulog (LOG_NORMAL, "%s", q->zlog);
+ ubuffree (q->zlog);
+ q->zlog = NULL;
+ }
+
+ if (ipos != -1 && ipos != q->ipos)
+ {
+ DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO,
+ "fgot_data: Seeking to %ld", ipos);
+ if (! ffileseek (q->e, ipos))
+ {
+ ulog (LOG_ERROR, "seek: %s", strerror (errno));
+ fret = FALSE;
+ }
+ q->ipos = ipos;
+ }
+
+ if (fret)
+ {
+ while (cfirst > 0)
+ {
+ cwrote = cfilewrite (q->e, (char *) zfirst, cfirst);
+ if (cwrote == cfirst)
+ {
+#if FREE_SPACE_DELTA > 0
+ long cfree_space;
+
+ /* Check that there is still enough space on the
+ disk. If there isn't, we drop the connection,
+ because we have no way to abort a file transfer
+ in progress. */
+ cfree_space = qdaemon->qsys->uuconf_cfree_space;
+ if (cfree_space > 0
+ && ((q->cbytes / FREE_SPACE_DELTA)
+ != (q->cbytes + cfirst) / FREE_SPACE_DELTA)
+ && ! frec_check_free (q, cfree_space))
+ {
+ fret = FALSE;
+ break;
+ }
+#endif
+ q->cbytes += cfirst;
+ q->ipos += cfirst;
+ }
+ else
+ {
+ if (cwrote < 0)
+ ulog (LOG_ERROR, "write: %s", strerror (errno));
+ else
+ ulog (LOG_ERROR,
+ "Wrote %d to file when trying to write %lu",
+ cwrote, (unsigned long) cfirst);
+
+ /* Any write error is almost certainly a temporary
+ condition, or else UUCP would not be functioning
+ at all. If we continue to accept the file, we
+ will wind up rejecting it at the end (what else
+ could we do?) and the remote system will throw
+ away the request. We're better off just dropping
+ the connection, which is what happens when we
+ return FALSE, and trying again later. */
+ fret = FALSE;
+ break;
+ }
+
+ zfirst = zsecond;
+ cfirst = csecond;
+ csecond = 0;
+ }
+ }
+
+ if (pfexit != NULL && qdaemon->fhangup)
+ *pfexit = TRUE;
+ }
+
+ return fret;
+}
+
+/* Accumulate a string into a command. If the command is complete,
+ start up a new transfer. */
+
+static boolean
+ftadd_cmd (qdaemon, z, clen, iremote, flast)
+ struct sdaemon *qdaemon;
+ const char *z;
+ size_t clen;
+ int iremote;
+ boolean flast;
+{
+ static char *zbuf;
+ static size_t cbuf;
+ size_t cneed;
+ struct scmd s;
+
+ cneed = cTcmdlen + clen + 1;
+ if (cneed > cbuf)
+ {
+ zbuf = (char *) xrealloc ((pointer) zbuf, cneed);
+ cbuf = cneed;
+ }
+
+ memcpy (zbuf + cTcmdlen, z, clen);
+ zbuf[cTcmdlen + clen] = '\0';
+
+ if (! flast)
+ {
+ cTcmdlen += clen;
+ return TRUE;
+ }
+
+ /* Don't save this string for next time. */
+ cTcmdlen = 0;
+
+ DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO,
+ "ftadd_cmd: Got command \"%s\"", zbuf);
+
+ if (! fparse_cmd (zbuf, &s))
+ {
+ ulog (LOG_ERROR, "Received garbled command \"%s\"", zbuf);
+ return TRUE;
+ }
+
+ if (s.bcmd != 'H' && s.bcmd != 'Y' && s.bcmd != 'N')
+ ulog_user (s.zuser);
+ else
+ ulog_user ((const char *) NULL);
+
+ switch (s.bcmd)
+ {
+ case 'S':
+ case 'E':
+ return fremote_send_file_init (qdaemon, &s, iremote);
+ case 'R':
+ return fremote_rec_file_init (qdaemon, &s, iremote);
+ case 'X':
+ return fremote_xcmd_init (qdaemon, &s, iremote);
+ case 'H':
+ /* This is a remote request for a hangup. We close the log
+ files so that they may be moved at this point. */
+ ulog_close ();
+ ustats_close ();
+ {
+ struct stransfer *q;
+
+ q = qtransalc ((struct scmd *) NULL);
+ q->psendfn = fremote_hangup_reply;
+ q->iremote = iremote;
+ return fqueue_remote (qdaemon, q);
+ }
+ case 'N':
+ /* This means a hangup request is being denied; we just ignore
+ this and wait for further commands. */
+ return TRUE;
+ case 'Y':
+ /* This is a remote confirmation of a hangup. We reconfirm. */
+ if (qdaemon->fhangup)
+ return TRUE;
+#if DEBUG > 0
+ if (qdaemon->fmaster)
+ ulog (LOG_ERROR, "Got hangup reply as master");
+#endif
+ /* Don't check errors rigorously here, since the other side
+ might jump the gun and hang up. The fLog_sighup variable
+ will get set TRUE again when the port is closed. */
+ fLog_sighup = FALSE;
+ (void) (*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, iremote);
+ qdaemon->fhangup = TRUE;
+ return TRUE;
+#if DEBUG > 0
+ default:
+ ulog (LOG_FATAL, "ftadd_cmd: Can't happen");
+ return FALSE;
+#endif
+ }
+}
+
+/* The remote system is requesting a hang up. If we have something to
+ do, send an HN. Otherwise send two HY commands (the other side is
+ presumed to send an HY command between the first and second, but we
+ don't bother to wait for it) and hang up. */
+
+static boolean
+fremote_hangup_reply (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ boolean fret;
+
+ utransfree (qtrans);
+
+ if (qTremote == NULL
+ && qTlocal == NULL
+ && qTsend == NULL
+ && qTreceive == NULL)
+ {
+ if (! fqueue (qdaemon, (boolean *) NULL))
+ return FALSE;
+
+ if (qTlocal == NULL)
+ {
+ DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fremote_hangup_reply: No work");
+ fret = ((*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, 0)
+ && (*qdaemon->qproto->pfsendcmd) (qdaemon, "HY", 0, 0));
+ qdaemon->fhangup = TRUE;
+ return fret;
+ }
+ }
+
+ DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fremote_hangup_reply: Found work");
+ fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, "HN", 0, 0);
+ qdaemon->fmaster = TRUE;
+ return fret;
+}
+
+/* As described in system.h, we need to keep track of which files have
+ been successfully received for which we do not know that the other
+ system has received our acknowledgement. This routine is called to
+ keep a list of such files. */
+
+static struct sreceive_ack *qTfree_receive_ack;
+
+void
+usent_receive_ack (qdaemon, qtrans)
+ struct sdaemon *qdaemon;
+ struct stransfer *qtrans;
+{
+ struct sreceive_ack *q;
+
+ if (qTfree_receive_ack == NULL)
+ q = (struct sreceive_ack *) xmalloc (sizeof (struct sreceive_ack));
+ else
+ {
+ q = qTfree_receive_ack;
+ qTfree_receive_ack = q->qnext;
+ }
+
+ q->qnext = qTreceive_ack;
+ q->zto = zbufcpy (qtrans->s.zto);
+ q->ztemp = zbufcpy (qtrans->s.ztemp);
+ q->fmarked = FALSE;
+
+ qTreceive_ack = q;
+}
+
+/* This routine is called by the protocol code when either all
+ outstanding data has been acknowledged or one complete window has
+ passed. It may be called directly by the protocol, or it may be
+ called via fgot_data. If one complete window has passed, then all
+ unmarked receives are marked, and we know that all marked ones have
+ been acked. */
+
+void
+uwindow_acked (qdaemon, fallacked)
+ struct sdaemon *qdaemon;
+ boolean fallacked;
+{
+ register struct sreceive_ack **pq;
+
+ pq = &qTreceive_ack;
+ while (*pq != NULL)
+ {
+ if (fallacked || (*pq)->fmarked)
+ {
+ struct sreceive_ack *q;
+
+ q = *pq;
+ (void) fsysdep_forget_reception (qdaemon->qsys, q->zto,
+ q->ztemp);
+ ubuffree (q->zto);
+ ubuffree (q->ztemp);
+ *pq = q->qnext;
+ q->qnext = qTfree_receive_ack;
+ qTfree_receive_ack = q;
+ }
+ else
+ {
+ (*pq)->fmarked = TRUE;
+ pq = &(*pq)->qnext;
+ }
+ }
+}
+
+/* This routine is called when an error occurred and we are crashing
+ out of the connection. It is used to report statistics on failed
+ transfers to the statistics file, and it also discards useless
+ temporary files for file receptions. Note that the number of bytes
+ we report as having been sent has little or nothing to do with the
+ number of bytes the remote site actually received. */
+
+void
+ufailed (qdaemon)
+ struct sdaemon *qdaemon;
+{
+ register struct stransfer *q;
+
+ /* Update the transfer times, but avoid looking in the queue. */
+ iTchecktime = ixsysdep_process_time ((long *) NULL);
+ (void) ftcharge (qdaemon, (struct stransfer *) NULL, TRUE, TRUE);
+ (void) ftcharge (qdaemon, (struct stransfer *) NULL, FALSE, TRUE);
+
+ if (qTsend != NULL)
+ {
+ q = qTsend;
+ do
+ {
+ if ((q->fsendfile || q->frecfile)
+ && q->cbytes > 0)
+ ustats (FALSE, q->s.zuser, qdaemon->qsys->uuconf_zname,
+ q->fsendfile, q->cbytes, q->isecs, q->imicros,
+ FALSE);
+ if (q->frecfile)
+ (void) frec_discard_temp (qdaemon, q);
+ q = q->qnext;
+ }
+ while (q != qTsend);
+ }
+
+ if (qTreceive != NULL)
+ {
+ q = qTreceive;
+ do
+ {
+ if ((q->fsendfile || q->frecfile)
+ && q->cbytes > 0)
+ ustats (FALSE, q->s.zuser, qdaemon->qsys->uuconf_zname,
+ q->fsendfile, q->cbytes, q->isecs, q->imicros,
+ FALSE);
+ if (q->frecfile)
+ (void) frec_discard_temp (qdaemon, q);
+ q = q->qnext;
+ }
+ while (q != qTreceive);
+ }
+}
+
+/* When a local poll file is found, it is entered on the queue like
+ any other job. When it is pulled off the queue, this function is
+ called. It just calls fsysdep_did_work, which will remove the poll
+ file. This ensures that poll files are only removed if the system
+ is actually called. */
+
+/*ARGSUSED*/
+static boolean
+flocal_poll_file (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ boolean fret;
+
+ fret = fsysdep_did_work (qtrans->s.pseq);
+ utransfree (qtrans);
+ return fret;
+}
diff --git a/gnu/libexec/uucp/uucico/uucico.8 b/gnu/libexec/uucp/uucico/uucico.8
new file mode 100644
index 0000000000000..ccc93afab5633
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/uucico.8
@@ -0,0 +1,225 @@
+''' $Id: uucico.8,v 1.1.2.1 1994/05/01 16:02:47 jkh Exp $
+.TH uucico 8 "Taylor UUCP 1.04"
+.SH NAME
+uucico \- UUCP file transfer daemon
+.SH SYNOPSIS
+.B uucico
+[ options ]
+.SH DESCRIPTION
+The
+.I uucico
+daemon processes file transfer requests queued by
+.I uucp
+(1) and
+.I uux
+(1). It is started when
+.I uucp
+or
+.I uux
+is run (unless they are given the
+.B \-r
+option). It is also typically started periodically using
+entries in the
+.I crontab
+table(s).
+
+When invoked with the
+.B \-r1
+option or the
+.B \-s
+or
+.B \-S
+option, the daemon will place a call to a remote system, running in
+master mode.
+Otherwise the daemon will start in slave mode, accepting a
+call from a remote system. Typically a special login name will be set
+up for UUCP which automatically invokes
+.I uucico
+when a call is made.
+
+When
+.I uucico
+terminates, it invokes the
+.I uuxqt
+(8) daemon, unless the
+.B \-q
+option is given;
+.I uuxqt
+(8) executes any work orders created by
+.I uux
+(1) on a remote system, and any work orders created locally which have
+received remote files for which they were waiting.
+
+If a call fails,
+.I uucico
+will normally refuse to retry the
+call until a certain (configurable) amount of time
+has passed. This may be overridden by the
+.B -f
+or the
+.B -S
+option.
+
+The
+.B \-l
+or
+.B \-e
+option may be used to force
+.I uucico
+to produce its own prompts of "login: " and "Password:". When another
+daemon calls in, it will see these prompts and log in as usual; the
+login name and password will be checked against a separate list kept
+specially for
+.I uucico
+rather than the
+.I /etc/passwd
+file. The
+.B \-l
+option will prompt once and then exit. The
+.B \-e
+option will prompt again after the first session is over; in this mode
+.I uucico
+will permanently control a port.
+
+If
+.I uucico
+receives a SIGQUIT, SIGTERM or SIGPIPE signal, it will cleanly abort
+any current conversation with a remote system and exit. If it
+receives a SIGHUP signal it will abort any current conversation, but
+will continue to place calls to (if invoked with
+.B \-r1)
+and accept calls from (if invoked with
+.B \-e)
+other systems. If it receives a
+SIGINT signal it will finish the current conversation, but will not
+place or accept any more calls.
+.SH OPTIONS
+The following options may be given to
+.I uucico.
+.TP 5
+.B \-r1
+Start in master mode (call out to a system); implied by
+.B \-s
+or
+.B \-S.
+If no system is specified, call any system for which work is waiting
+to be done.
+.TP 5
+.B \-r0
+Start in slave mode. This is the default.
+.TP 5
+.B \-s system
+Call the named system.
+.TP 5
+.B \-S system
+Call the named system, ignoring any required wait.
+.TP 5
+.B \-f
+Ignore any required wait for any systems to be called.
+.TP 5
+.B \-l
+Prompt for login name and password using "login: " and "Password:".
+This allows
+.I uucico
+to be easily run from
+.I inetd
+(8). The login name and password are checked against the UUCP
+password file, which has no connection to the file
+.I /etc/passwd.
+.TP 5
+.B \-p port
+Specify a port to call out on or to listen to. In slave mode, this
+implies
+.B \-e.
+.TP 5
+.B \-e
+Enter endless loop of login/password prompts and slave mode daemon
+execution. The program will not stop by itself; you must use
+.I kill
+(1) to shut it down.
+.TP 5
+.B \-w
+After calling out (to a particular system when
+.B \-s
+or
+.B \-S
+is specified, or to all systems which have work when
+.B \-r1
+is specified), begin an endless loop as with
+.B \-e.
+.TP 5
+.B \-q
+Do not start the
+.I uuxqt
+(8) daemon when finished.
+.TP 5
+.B \-c
+If no calls are permitted at this time, then don't make the call, but
+also do not put an error message in the log file and do not update the
+system status (as reported by
+.I uustat
+(1)). This can be convenient for automated polling scripts, which may
+want to simply attempt to call every system rather than worry about
+which particular systems may be called at the moment.
+.TP 5
+.B \-D
+Do not detach from the controlling terminal. Normally
+.I uucico
+detaches from the terminal before each call out to another system and
+before invoking
+.I uuxqt.
+This option prevents this.
+.TP 5
+.B \-x type, \-X type
+Turn on particular debugging types. The following types are
+recognized: abnormal, chat, handshake, uucp-proto, proto, port,
+config, spooldir, execute, incoming, outgoing.
+
+Multiple types may be given, separated by commas, and the
+.B \-x
+option may appear multiple times. A number may also be given, which
+will turn on that many types from the foregoing list; for example,
+.B \-x 2
+is equivalent to
+.B \-x abnormal,chat.
+
+The debugging output is sent to the debugging file, usually one of
+/usr/spool/uucp/Debug, /usr/spool/uucp/DEBUG, or
+/usr/spool/uucp/.Admin/audit.local.
+.TP 5
+.B \-I file
+Set configuration file to use. This option may not be available,
+depending upon how
+.I uucico
+was compiled.
+.TP 5
+.B \-u login
+This option is ignored. It is only included because some versions of
+uucpd invoke
+.I uucico
+with it.
+.SH FILES
+The file names may be changed at compilation time or by the
+configuration file, so these are only approximations.
+
+.br
+/usr/lib/uucp/config - Configuration file.
+.br
+/usr/lib/uucp/passwd - Default UUCP password file.
+.br
+/usr/spool/uucp -
+UUCP spool directory.
+.br
+/usr/spool/uucp/Log -
+UUCP log file.
+.br
+/usr/spool/uucppublic -
+Default UUCP public directory.
+.br
+/usr/spool/uucp/Debug -
+Debugging file.
+.SH SEE ALSO
+kill(1), uucp(1), uux(1), uustat(1), uuxqt(8)
+.SH AUTHOR
+Ian Lance Taylor
+(ian@airs.com or uunet!airs!ian)
diff --git a/gnu/libexec/uucp/uucico/uucico.c b/gnu/libexec/uucp/uucico/uucico.c
new file mode 100644
index 0000000000000..01717c4bcf4fa
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/uucico.c
@@ -0,0 +1,2618 @@
+/* uucico.c
+ This is the main UUCP communication program.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char uucico_rcsid[] = "$Id: uucico.c,v 1.1 1993/08/05 18:27:24 conklin Exp $";
+#endif
+
+#include <ctype.h>
+
+#if HAVE_LIMITS_H
+#include <limits.h>
+#else
+#define LONG_MAX 2147483647L
+#endif
+
+#include "getopt.h"
+
+#include "uudefs.h"
+#include "uuconf.h"
+#include "conn.h"
+#include "prot.h"
+#include "trans.h"
+#include "system.h"
+
+/* The program name. */
+char abProgram[] = "uucico";
+
+/* Define the known protocols. */
+
+#define TCP_PROTO \
+ (UUCONF_RELIABLE_ENDTOEND \
+ | UUCONF_RELIABLE_RELIABLE \
+ | UUCONF_RELIABLE_EIGHT)
+
+static const struct sprotocol asProtocols[] =
+{
+ { 't', TCP_PROTO, 1,
+ asTproto_params, ftstart, ftshutdown, ftsendcmd, ztgetspace,
+ ftsenddata, ftwait, ftfile },
+ { 'e', TCP_PROTO, 1,
+ asEproto_params, festart, feshutdown, fesendcmd, zegetspace,
+ fesenddata, fewait, fefile },
+ { 'i', UUCONF_RELIABLE_EIGHT, 7,
+ asIproto_params, fistart, fishutdown, fisendcmd, zigetspace,
+ fisenddata, fiwait, NULL },
+ { 'a', UUCONF_RELIABLE_EIGHT, 1,
+ asZproto_params, fzstart, fzshutdown, fzsendcmd, zzgetspace,
+ fzsenddata, fzwait, fzfile },
+ { 'g', UUCONF_RELIABLE_EIGHT, 1,
+ asGproto_params, fgstart, fgshutdown, fgsendcmd, zggetspace,
+ fgsenddata, fgwait, NULL },
+ { 'G', UUCONF_RELIABLE_EIGHT, 1,
+ asGproto_params, fbiggstart, fgshutdown, fgsendcmd, zggetspace,
+ fgsenddata, fgwait, NULL },
+ { 'j', UUCONF_RELIABLE_EIGHT, 7,
+ asIproto_params, fjstart, fjshutdown, fisendcmd, zigetspace,
+ fisenddata, fiwait, NULL },
+ { 'f', UUCONF_RELIABLE_RELIABLE, 1,
+ asFproto_params, ffstart, ffshutdown, ffsendcmd, zfgetspace,
+ ffsenddata, ffwait, fffile },
+};
+
+#define CPROTOCOLS (sizeof asProtocols / sizeof asProtocols[0])
+
+/* Locked system. */
+static boolean fLocked_system;
+static struct uuconf_system sLocked_system;
+
+/* Daemon structure holding information about the remote system (must
+ be global so the error handler can see it. */
+static struct sdaemon sDaemon;
+
+/* Open connection. */
+static struct sconnection *qConn;
+
+/* uuconf global pointer; need to close the connection after a fatal
+ error. */
+static pointer pUuconf;
+
+/* This structure is passed to iuport_lock via uuconf_find_port. */
+struct spass
+{
+ boolean fmatched;
+ boolean flocked;
+ struct sconnection *qconn;
+};
+
+/* Local functions. */
+
+static void uusage P((void));
+static void uabort P((void));
+static boolean fcall P((pointer puuconf,
+ const struct uuconf_system *qsys,
+ struct uuconf_port *qport, boolean fifwork,
+ boolean fforce, boolean fdetach,
+ boolean ftimewarn));
+static boolean fconn_call P((struct sdaemon *qdaemon,
+ struct uuconf_port *qport,
+ struct sstatus *qstat, int cretry,
+ boolean *pfcalled));
+static boolean fdo_call P((struct sdaemon *qdaemon,
+ struct sstatus *qstat,
+ const struct uuconf_dialer *qdialer,
+ boolean *pfcalled, enum tstatus_type *pterr));
+static int iuport_lock P((struct uuconf_port *qport, pointer pinfo));
+static boolean flogin_prompt P((pointer puuconf,
+ struct sconnection *qconn));
+static boolean faccept_call P((pointer puuconf, const char *zlogin,
+ struct sconnection *qconn,
+ const char **pzsystem));
+static void uapply_proto_params P((pointer puuconf, int bproto,
+ struct uuconf_cmdtab *qcmds,
+ struct uuconf_proto_param *pas));
+static boolean fsend_uucp_cmd P((struct sconnection *qconn,
+ const char *z));
+static char *zget_uucp_cmd P((struct sconnection *qconn,
+ boolean frequired));
+static char *zget_typed_line P((struct sconnection *qconn));
+
+/* Long getopt options. */
+static const struct option asLongopts[] = { { NULL, 0, NULL, 0 } };
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ /* -c: Whether to warn if a call is attempted at a bad time. */
+ boolean ftimewarn = TRUE;
+ /* -D: don't detach from controlling terminal. */
+ boolean fdetach = TRUE;
+ /* -e: Whether to do an endless loop of accepting calls. */
+ boolean fendless = FALSE;
+ /* -f: Whether to force a call despite status of previous call. */
+ boolean fforce = FALSE;
+ /* -I file: configuration file name. */
+ const char *zconfig = NULL;
+ /* -l: Whether to give a single login prompt. */
+ boolean flogin = FALSE;
+ /* -P port: port to use; in master mode, call out on this port. In
+ slave mode, accept logins on this port. If port not specified,
+ then in master mode figure it out for each system, and in slave
+ mode use stdin and stdout. */
+ const char *zport = NULL;
+ /* -q: Whether to start uuxqt when done. */
+ boolean fuuxqt = TRUE;
+ /* -r1: Whether we are the master. */
+ boolean fmaster = FALSE;
+ /* -s,-S system: system to call. */
+ const char *zsystem = NULL;
+ /* -w: Whether to wait for a call after doing one. */
+ boolean fwait = FALSE;
+ int iopt;
+ struct uuconf_port *qport;
+ struct uuconf_port sport;
+ boolean fret = TRUE;
+ pointer puuconf;
+ int iuuconf;
+#if DEBUG > 1
+ int iholddebug;
+#endif
+
+ while ((iopt = getopt_long (argc, argv,
+ "cDefI:lp:qr:s:S:u:x:X:w",
+ asLongopts, (int *) NULL)) != EOF)
+ {
+ switch (iopt)
+ {
+ case 'c':
+ /* Don't warn if a call is attempted at a bad time. */
+ ftimewarn = FALSE;
+ break;
+
+ case 'D':
+ /* Don't detach from controlling terminal. */
+ fdetach = FALSE;
+ break;
+
+ case 'e':
+ /* Do an endless loop of accepting calls. */
+ fendless = TRUE;
+ break;
+
+ case 'f':
+ /* Force a call even if it hasn't been long enough since the last
+ failed call. */
+ fforce = TRUE;
+ break;
+
+ case 'I':
+ /* Set configuration file name (default is in sysdep.h). */
+ if (fsysdep_other_config (optarg))
+ zconfig = optarg;
+ break;
+
+ case 'l':
+ /* Prompt for login name and password. */
+ flogin = TRUE;
+ break;
+
+ case 'p':
+ /* Port to use */
+ zport = optarg;
+ break;
+
+ case 'q':
+ /* Don't start uuxqt. */
+ fuuxqt = FALSE;
+ break;
+
+ case 'r':
+ /* Set mode: -r1 for master, -r0 for slave (default) */
+ if (strcmp (optarg, "1") == 0)
+ fmaster = TRUE;
+ else if (strcmp (optarg, "0") == 0)
+ fmaster = FALSE;
+ else
+ uusage ();
+ break;
+
+ case 's':
+ /* Set system name */
+ zsystem = optarg;
+ fmaster = TRUE;
+ break;
+
+ case 'S':
+ /* Set system name and force call like -f */
+ zsystem = optarg;
+ fforce = TRUE;
+ fmaster = TRUE;
+ break;
+
+ case 'u':
+ /* Some versions of uucpd invoke uucico with a -u argument
+ specifying the login name. I'm told it is safe to ignore
+ this value, although perhaps we should use it rather than
+ zsysdep_login_name (). */
+ break;
+
+ case 'x':
+ case 'X':
+#if DEBUG > 1
+ /* Set debugging level */
+ iDebug |= idebug_parse (optarg);
+#endif
+ break;
+
+ case 'w':
+ /* Call out and then wait for a call in */
+ fwait = TRUE;
+ break;
+
+ case 0:
+ /* Long option found, and flag value set. */
+ break;
+
+ default:
+ uusage ();
+ break;
+ }
+ }
+
+ if (optind != argc)
+ uusage ();
+
+ if (fwait && zport == NULL)
+ {
+ ulog (LOG_ERROR, "-w requires -e");
+ uusage ();
+ }
+
+ iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig);
+ if (iuuconf != UUCONF_SUCCESS)
+ ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
+ pUuconf = puuconf;
+
+#if DEBUG > 1
+ {
+ const char *zdebug;
+
+ iuuconf = uuconf_debuglevel (puuconf, &zdebug);
+ if (iuuconf != UUCONF_SUCCESS)
+ ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
+ if (zdebug != NULL)
+ iDebug |= idebug_parse (zdebug);
+ }
+#endif
+
+ /* If a port was named, get its information. */
+ if (zport == NULL)
+ qport = NULL;
+ else
+ {
+ iuuconf = uuconf_find_port (puuconf, zport, (long) 0, (long) 0,
+ (int (*) P((struct uuconf_port *,
+ pointer))) NULL,
+ (pointer) NULL, &sport);
+ if (iuuconf == UUCONF_NOT_FOUND)
+ ulog (LOG_FATAL, "%s: Port not found", zport);
+ else if (iuuconf != UUCONF_SUCCESS)
+ ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
+ qport = &sport;
+ }
+
+#ifdef SIGINT
+ usysdep_signal (SIGINT);
+#endif
+#ifdef SIGHUP
+ usysdep_signal (SIGHUP);
+#endif
+#ifdef SIGQUIT
+ usysdep_signal (SIGQUIT);
+#endif
+#ifdef SIGTERM
+ usysdep_signal (SIGTERM);
+#endif
+#ifdef SIGPIPE
+ usysdep_signal (SIGPIPE);
+#endif
+
+ usysdep_initialize (puuconf, INIT_SUID);
+
+ ulog_to_file (puuconf, TRUE);
+ ulog_fatal_fn (uabort);
+
+ if (fmaster)
+ {
+ if (zsystem != NULL)
+ {
+ /* A system was named. Call it. */
+ iuuconf = uuconf_system_info (puuconf, zsystem,
+ &sLocked_system);
+ if (iuuconf == UUCONF_NOT_FOUND)
+ ulog (LOG_FATAL, "%s: System not found", zsystem);
+ else if (iuuconf != UUCONF_SUCCESS)
+ ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
+
+ /* Detach from the controlling terminal for the call. This
+ probably makes sense only on Unix. We want the modem
+ line to become the controlling terminal. */
+ if (fdetach &&
+ (qport == NULL
+ || qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN))
+ usysdep_detach ();
+
+ ulog_system (sLocked_system.uuconf_zname);
+
+#if DEBUG > 1
+ iholddebug = iDebug;
+ if (sLocked_system.uuconf_zdebug != NULL)
+ iDebug |= idebug_parse (sLocked_system.uuconf_zdebug);
+#endif
+
+ if (! fsysdep_lock_system (&sLocked_system))
+ {
+ ulog (LOG_ERROR, "System already locked");
+ fret = FALSE;
+ }
+ else
+ {
+ fLocked_system = TRUE;
+ fret = fcall (puuconf, &sLocked_system, qport, FALSE,
+ fforce, fdetach, ftimewarn);
+ if (fLocked_system)
+ {
+ (void) fsysdep_unlock_system (&sLocked_system);
+ fLocked_system = FALSE;
+ }
+ }
+#if DEBUG > 1
+ iDebug = iholddebug;
+#endif
+ ulog_system ((const char *) NULL);
+ (void) uuconf_system_free (puuconf, &sLocked_system);
+ }
+ else
+ {
+ char **pznames, **pz;
+ int c, i;
+ boolean fdidone;
+
+ /* Call all systems which have work to do. */
+ fret = TRUE;
+ fdidone = FALSE;
+
+ iuuconf = uuconf_system_names (puuconf, &pznames, 0);
+ if (iuuconf != UUCONF_SUCCESS)
+ ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
+
+ /* Randomize the order in which we call the systems. */
+ c = 0;
+ for (pz = pznames; *pz != NULL; pz++)
+ c++;
+
+ srand ((unsigned int) ixsysdep_time ((long *) NULL));
+ for (i = c - 1; i > 0; i--)
+ {
+ int iuse;
+ char *zhold;
+
+ iuse = rand () % (i + 1);
+ zhold = pznames[i];
+ pznames[i] = pznames[iuse];
+ pznames[iuse] = zhold;
+ }
+
+ for (pz = pznames; *pz != NULL && ! FGOT_SIGNAL (); pz++)
+ {
+ iuuconf = uuconf_system_info (puuconf, *pz,
+ &sLocked_system);
+ if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ xfree ((pointer) *pz);
+ continue;
+ }
+
+ if (fsysdep_has_work (&sLocked_system))
+ {
+ fdidone = TRUE;
+
+ /* Detach from the controlling terminal. On Unix
+ this means that we will wind up forking a new
+ process for each system we call. */
+ if (fdetach
+ && (qport == NULL
+ || qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN))
+ usysdep_detach ();
+
+ ulog_system (sLocked_system.uuconf_zname);
+
+#if DEBUG > 1
+ iholddebug = iDebug;
+ if (sLocked_system.uuconf_zdebug != NULL)
+ iDebug |= idebug_parse (sLocked_system.uuconf_zdebug);
+#endif
+
+ if (! fsysdep_lock_system (&sLocked_system))
+ {
+ ulog (LOG_ERROR, "System already locked");
+ fret = FALSE;
+ }
+ else
+ {
+ fLocked_system = TRUE;
+ if (! fcall (puuconf, &sLocked_system, qport, TRUE,
+ fforce, fdetach, ftimewarn))
+ fret = FALSE;
+
+ /* Now ignore any SIGHUP that we got. */
+ afSignal[INDEXSIG_SIGHUP] = FALSE;
+
+ if (fLocked_system)
+ {
+ (void) fsysdep_unlock_system (&sLocked_system);
+ fLocked_system = FALSE;
+ }
+ }
+#if DEBUG > 1
+ iDebug = iholddebug;
+#endif
+ ulog_system ((const char *) NULL);
+ }
+
+ (void) uuconf_system_free (puuconf, &sLocked_system);
+ xfree ((pointer) *pz);
+ }
+
+ xfree ((pointer) pznames);
+
+ if (! fdidone)
+ ulog (LOG_NORMAL, "No work");
+ }
+
+ /* If requested, wait for calls after dialing out. */
+ if (fwait)
+ {
+ fendless = TRUE;
+ fmaster = FALSE;
+ }
+ }
+
+ if (! fmaster)
+ {
+ struct sconnection sconn;
+ boolean flocked;
+
+ /* If a port was specified by name, we go into endless loop
+ mode. In this mode, we wait for calls and prompt them with
+ "login:" and "Password:", so that they think we are a regular
+ UNIX system. If we aren't in endless loop mode, we have been
+ called by some other system. If flogin is TRUE, we prompt
+ with "login:" and "Password:" a single time. */
+
+ fret = TRUE;
+ zsystem = NULL;
+
+ if (! fconn_init (qport, &sconn))
+ fret = FALSE;
+
+ if (qport != NULL)
+ {
+ /* We are not using standard input. Detach from the
+ controlling terminal, so that the port we are about to
+ use becomes our controlling terminal. */
+ if (fdetach
+ && qport->uuconf_ttype != UUCONF_PORTTYPE_STDIN)
+ usysdep_detach ();
+
+ /* If a port was given, we loop forever. */
+ fendless = TRUE;
+ }
+
+ if (fconn_lock (&sconn, TRUE))
+ flocked = TRUE;
+ else
+ {
+ flocked = FALSE;
+ ulog (LOG_ERROR, "%s: Port already locked",
+ qport->uuconf_zname);
+ fret = FALSE;
+ }
+
+ if (fret)
+ {
+ if (! fconn_open (&sconn, (long) 0, (long) 0, TRUE))
+ fret = FALSE;
+ qConn = &sconn;
+ }
+
+ if (fret)
+ {
+ if (fendless)
+ {
+ while (! FGOT_SIGNAL ()
+ && flogin_prompt (puuconf, &sconn))
+ {
+ /* Now ignore any SIGHUP that we got. */
+ afSignal[INDEXSIG_SIGHUP] = FALSE;
+
+ if (fLocked_system)
+ {
+ (void) fsysdep_unlock_system (&sLocked_system);
+ fLocked_system = FALSE;
+ }
+ if (! fconn_reset (&sconn))
+ break;
+ }
+ fret = FALSE;
+ }
+ else
+ {
+ if (flogin)
+ fret = flogin_prompt (puuconf, &sconn);
+ else
+ {
+#if DEBUG > 1
+ iholddebug = iDebug;
+#endif
+ fret = faccept_call (puuconf, zsysdep_login_name (),
+ &sconn, &zsystem);
+#if DEBUG > 1
+ iDebug = iholddebug;
+#endif
+ }
+ }
+ }
+
+ if (qConn != NULL)
+ {
+ if (! fconn_close (&sconn, puuconf, (struct uuconf_dialer *) NULL,
+ fret))
+ fret = FALSE;
+ qConn = NULL;
+ }
+
+ if (flocked)
+ (void) fconn_unlock (&sconn);
+
+ if (fLocked_system)
+ {
+ (void) fsysdep_unlock_system (&sLocked_system);
+ fLocked_system = FALSE;
+ }
+
+ uconn_free (&sconn);
+ }
+
+ ulog_close ();
+ ustats_close ();
+
+ /* If we got a SIGTERM, perhaps because the system is going down,
+ don't run uuxqt. We go ahead and run it for any other signal,
+ since I think they indicate more temporary conditions. */
+ if (afSignal[INDEXSIG_SIGTERM])
+ fuuxqt = FALSE;
+
+ if (fuuxqt)
+ {
+ /* Detach from the controlling terminal before starting up uuxqt,
+ so that it runs as a true daemon. */
+ if (fdetach)
+ usysdep_detach ();
+ if (zsystem == NULL)
+ {
+ if (! fsysdep_run ("uuxqt", (const char *) NULL,
+ (const char *) NULL))
+ fret = FALSE;
+ }
+ else
+ {
+ if (! fsysdep_run ("uuxqt", "-s", zsystem))
+ fret = FALSE;
+ }
+ }
+
+ usysdep_exit (fret);
+
+ /* Avoid complaints about not returning. */
+ return 0;
+}
+
+/* Print out a usage message. */
+
+static void
+uusage ()
+{
+ fprintf (stderr,
+ "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n",
+ VERSION);
+ fprintf (stderr,
+ "Usage: uucico [options]\n");
+ fprintf (stderr,
+ " -s,-S system: Call system (-S implies -f)\n");
+ fprintf (stderr,
+ " -f: Force call despite system status\n");
+ fprintf (stderr,
+ " -r state: 1 for master, 0 for slave (default)\n");
+ fprintf (stderr,
+ " -p port: Specify port (implies -e)\n");
+ fprintf (stderr,
+ " -l: prompt for login name and password\n");
+ fprintf (stderr,
+ " -e: Endless loop of login prompts and daemon execution\n");
+ fprintf (stderr,
+ " -w: After calling out, wait for incoming calls\n");
+ fprintf (stderr,
+ " -q: Don't start uuxqt when done\n");
+ fprintf (stderr,
+ " -x,-X debug: Set debugging level\n");
+#if HAVE_TAYLOR_CONFIG
+ fprintf (stderr,
+ " -I file: Set configuration file to use\n");
+#endif /* HAVE_TAYLOR_CONFIG */
+
+ exit (EXIT_FAILURE);
+}
+
+/* This function is called when a LOG_FATAL error occurs. */
+
+static void
+uabort ()
+{
+ if (fLocked_system)
+ ufailed (&sDaemon);
+
+ ulog_user ((const char *) NULL);
+
+ if (qConn != NULL)
+ {
+ (void) fconn_close (qConn, pUuconf, (struct uuconf_dialer *) NULL,
+ FALSE);
+ (void) fconn_unlock (qConn);
+ uconn_free (qConn);
+ }
+
+ if (fLocked_system)
+ {
+ (void) fsysdep_unlock_system (&sLocked_system);
+ fLocked_system = FALSE;
+ }
+
+ ulog_system ((const char *) NULL);
+
+ ulog_close ();
+ ustats_close ();
+
+ usysdep_exit (FALSE);
+}
+
+/* Call another system, trying all the possible sets of calling
+ instructions. The qsys argument is the system to call. The qport
+ argument is the port to use, and may be NULL. If the fifwork
+ argument is TRUE, the call is only placed if there is work to be
+ done. If the fforce argument is TRUE, a call is forced even if not
+ enough time has passed since the last failed call. If the
+ ftimewarn argument is TRUE (the normal case), then a warning is
+ given if calls are not permitted at this time. */
+
+static boolean
+fcall (puuconf, qorigsys, qport, fifwork, fforce, fdetach, ftimewarn)
+ pointer puuconf;
+ const struct uuconf_system *qorigsys;
+ struct uuconf_port *qport;
+ boolean fifwork;
+ boolean fforce;
+ boolean fdetach;
+ boolean ftimewarn;
+{
+ struct sstatus sstat;
+ long inow;
+ boolean fbadtime, fnevertime;
+ const struct uuconf_system *qsys;
+
+ if (! fsysdep_get_status (qorigsys, &sstat, (boolean *) NULL))
+ return FALSE;
+
+ /* Make sure it's been long enough since the last failed call, and
+ that we haven't exceeded the maximum number of retries. Even if
+ we are over the limit on retries, we permit a call to be made if
+ 24 hours have passed. This 24 hour limit is still controlled by
+ the retry time. */
+ inow = ixsysdep_time ((long *) NULL);
+ if (! fforce)
+ {
+ if (qorigsys->uuconf_cmax_retries > 0
+ && sstat.cretries >= qorigsys->uuconf_cmax_retries
+ && sstat.ilast + 24 * 60 * 60 < inow)
+ {
+ ulog (LOG_ERROR, "Too many retries");
+ return FALSE;
+ }
+
+ if (sstat.ttype == STATUS_COMPLETE
+ ? sstat.ilast + qorigsys->uuconf_csuccess_wait > inow
+ : sstat.ilast + sstat.cwait > inow)
+ {
+ ulog (LOG_NORMAL, "Retry time not reached");
+ return FALSE;
+ }
+ }
+
+ sDaemon.puuconf = puuconf;
+ sDaemon.qsys = NULL;
+ sDaemon.zlocalname = NULL;
+ sDaemon.qconn = NULL;
+ sDaemon.qproto = NULL;
+ sDaemon.clocal_size = -1;
+ sDaemon.cremote_size = -1;
+ sDaemon.cmax_ever = -2;
+ sDaemon.cmax_receive = -1;
+ sDaemon.ifeatures = 0;
+ sDaemon.frequest_hangup = FALSE;
+ sDaemon.fhangup_requested = FALSE;
+ sDaemon.fhangup = FALSE;
+ sDaemon.fmaster = TRUE;
+ sDaemon.fcaller = TRUE;
+ sDaemon.ireliable = 0;
+ sDaemon.bgrade = '\0';
+
+ fbadtime = TRUE;
+ fnevertime = TRUE;
+
+ for (qsys = qorigsys; qsys != NULL; qsys = qsys->uuconf_qalternate)
+ {
+ int cretry;
+ boolean fany, fret, fcalled;
+
+ if (FGOT_SIGNAL ())
+ return FALSE;
+
+ if (! qsys->uuconf_fcall || qsys->uuconf_qtimegrade == NULL)
+ continue;
+
+ fnevertime = FALSE;
+
+ /* Make sure this is a legal time to call. */
+ if (! ftimespan_match (qsys->uuconf_qtimegrade, (long *) NULL,
+ &cretry))
+ continue;
+
+ sDaemon.qsys = qsys;
+
+ /* Queue up any work there is to do. */
+ if (! fqueue (&sDaemon, &fany))
+ return FALSE;
+
+ /* If we are only supposed to call if there is work, and there
+ isn't any work, check the next alternates. We can't give up
+ at this point because there might be some other alternates
+ with fewer restrictions on grade or file transfer size. */
+ if (fifwork && ! fany)
+ {
+ uclear_queue (&sDaemon);
+ continue;
+ }
+
+ fbadtime = FALSE;
+
+ fret = fconn_call (&sDaemon, qport, &sstat, cretry, &fcalled);
+
+ uclear_queue (&sDaemon);
+
+ if (fret)
+ return TRUE;
+ if (fcalled)
+ return FALSE;
+
+ /* Now we have to dump that port so that we can aquire a new
+ one. On Unix this means that we will fork and get a new
+ process ID, so we must unlock and relock the system. */
+ if (fdetach)
+ {
+ (void) fsysdep_unlock_system (&sLocked_system);
+ fLocked_system = FALSE;
+ usysdep_detach ();
+ if (! fsysdep_lock_system (&sLocked_system))
+ return FALSE;
+ fLocked_system = TRUE;
+ }
+ }
+
+ if (fbadtime && ftimewarn)
+ {
+ ulog (LOG_NORMAL, "Wrong time to call");
+
+ /* Update the status, unless the system can never be called. If
+ the system can never be called, there is little point to
+ putting in a ``wrong time to call'' message. We don't change
+ the number of retries, although we do set the wait until the
+ next retry to 0. */
+ if (! fnevertime)
+ {
+ sstat.ttype = STATUS_WRONG_TIME;
+ sstat.ilast = inow;
+ sstat.cwait = 0;
+ (void) fsysdep_set_status (qorigsys, &sstat);
+ }
+ }
+
+ return FALSE;
+}
+
+/* Find a port to use when calling a system, open a connection, and
+ dial the system. The actual call is done in fdo_call. This
+ routine is responsible for opening and closing the connection. */
+
+static boolean
+fconn_call (qdaemon, qport, qstat, cretry, pfcalled)
+ struct sdaemon *qdaemon;
+ struct uuconf_port *qport;
+ struct sstatus *qstat;
+ int cretry;
+ boolean *pfcalled;
+{
+ pointer puuconf;
+ const struct uuconf_system *qsys;
+ struct uuconf_port sport;
+ struct sconnection sconn;
+ enum tstatus_type terr;
+ boolean fret;
+
+ puuconf = qdaemon->puuconf;
+ qsys = qdaemon->qsys;
+
+ *pfcalled = FALSE;
+
+ /* Ignore any SIGHUP signal we may have received up to this point.
+ This is needed on Unix because we may have gotten one from the
+ shell before we detached from the controlling terminal. */
+ afSignal[INDEXSIG_SIGHUP] = FALSE;
+
+ /* If no port was specified on the command line, use any port
+ defined for the system. To select the system port: 1) see if
+ port information was specified directly; 2) see if a port was
+ named; 3) get an available port given the baud rate. We don't
+ change the system status if a port is unavailable; i.e. we don't
+ force the system to wait for the retry time. */
+ if (qport == NULL)
+ qport = qsys->uuconf_qport;
+ if (qport != NULL)
+ {
+ if (! fconn_init (qport, &sconn))
+ return FALSE;
+ if (! fconn_lock (&sconn, FALSE))
+ {
+ ulog (LOG_ERROR, "%s: Port already locked",
+ qport->uuconf_zname);
+ return FALSE;
+ }
+ }
+ else
+ {
+ struct spass s;
+ int iuuconf;
+
+ s.fmatched = FALSE;
+ s.flocked = FALSE;
+ s.qconn = &sconn;
+ iuuconf = uuconf_find_port (puuconf, qsys->uuconf_zport,
+ qsys->uuconf_ibaud,
+ qsys->uuconf_ihighbaud,
+ iuport_lock, (pointer) &s,
+ &sport);
+ if (iuuconf == UUCONF_NOT_FOUND)
+ {
+ if (s.fmatched)
+ ulog (LOG_ERROR, "All matching ports in use");
+ else
+ ulog (LOG_ERROR, "No matching ports");
+ return FALSE;
+ }
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ if (s.flocked)
+ {
+ (void) fconn_unlock (&sconn);
+ uconn_free (&sconn);
+ }
+ return FALSE;
+ }
+ }
+
+ if (! fconn_open (&sconn, qsys->uuconf_ibaud, qsys->uuconf_ihighbaud,
+ FALSE))
+ {
+ terr = STATUS_PORT_FAILED;
+ fret = FALSE;
+ }
+ else
+ {
+ struct uuconf_dialer *qdialer;
+ struct uuconf_dialer sdialer;
+ enum tdialerfound tdialer;
+
+ if (qsys->uuconf_zalternate == NULL)
+ ulog (LOG_NORMAL, "Calling system %s (port %s)", qsys->uuconf_zname,
+ zLdevice == NULL ? (char *) "unknown" : zLdevice);
+ else
+ ulog (LOG_NORMAL, "Calling system %s (alternate %s, port %s)",
+ qsys->uuconf_zname, qsys->uuconf_zalternate,
+ zLdevice == NULL ? (char *) "unknown" : zLdevice);
+
+ qdialer = NULL;
+
+ if (! fconn_dial (&sconn, puuconf, qsys, qsys->uuconf_zphone,
+ &sdialer, &tdialer))
+ {
+ terr = STATUS_DIAL_FAILED;
+ fret = FALSE;
+ }
+ else
+ {
+ qdaemon->qconn = &sconn;
+ if (tdialer == DIALERFOUND_FALSE)
+ qdialer = NULL;
+ else
+ qdialer = &sdialer;
+ fret = fdo_call (qdaemon, qstat, qdialer, pfcalled, &terr);
+ }
+
+ (void) fconn_close (&sconn, puuconf, qdialer, fret);
+
+ if (tdialer == DIALERFOUND_FREE)
+ (void) uuconf_dialer_free (puuconf, &sdialer);
+ }
+
+ if (! fret)
+ {
+ DEBUG_MESSAGE2 (DEBUG_HANDSHAKE, "Call failed: %d (%s)",
+ (int) terr, azStatus[(int) terr]);
+ qstat->ttype = terr;
+ qstat->cretries++;
+ qstat->ilast = ixsysdep_time ((long *) NULL);
+ if (cretry == 0)
+ qstat->cwait = CRETRY_WAIT (qstat->cretries);
+ else
+ qstat->cwait = cretry * 60;
+ (void) fsysdep_set_status (qsys, qstat);
+ }
+
+ (void) fconn_unlock (&sconn);
+ uconn_free (&sconn);
+
+ if (qport == NULL)
+ (void) uuconf_port_free (puuconf, &sport);
+
+ return fret;
+}
+
+/* Do the actual work of calling another system. The qsys argument is
+ the system to call, the qconn argument is the connection to use,
+ the qstat argument holds the current status of the ssystem, and the
+ qdialer argument holds the dialer being used (it may be NULL). If
+ we log in successfully, set *pfcalled to TRUE; this is used to
+ distinguish a failed dial from a failure during the call. If an
+ error occurs *pterr is set to the status type to record. */
+
+static boolean
+fdo_call (qdaemon, qstat, qdialer, pfcalled, pterr)
+ struct sdaemon *qdaemon;
+ struct sstatus *qstat;
+ const struct uuconf_dialer *qdialer;
+ boolean *pfcalled;
+ enum tstatus_type *pterr;
+{
+ pointer puuconf;
+ const struct uuconf_system *qsys;
+ struct sconnection *qconn;
+ const char *zport;
+ int iuuconf;
+ char *zstr;
+ long istart_time;
+ char *zlog;
+
+ puuconf = qdaemon->puuconf;
+ qsys = qdaemon->qsys;
+ qconn = qdaemon->qconn;
+
+ *pterr = STATUS_LOGIN_FAILED;
+
+ if (qconn->qport == NULL)
+ zport = "unknown";
+ else
+ zport = qconn->qport->uuconf_zname;
+ if (! fchat (qconn, puuconf, &qsys->uuconf_schat, qsys,
+ (const struct uuconf_dialer *) NULL,
+ (const char *) NULL, FALSE, zport,
+ iconn_baud (qconn)))
+ return FALSE;
+
+ *pfcalled = TRUE;
+ istart_time = ixsysdep_time ((long *) NULL);
+
+ *pterr = STATUS_HANDSHAKE_FAILED;
+
+ /* We should now see "Shere" from the other system. Newer systems
+ send "Shere=foo" where foo is the remote name. */
+ zstr = zget_uucp_cmd (qconn, TRUE);
+ if (zstr == NULL)
+ return FALSE;
+
+ if (strncmp (zstr, "Shere", 5) != 0)
+ {
+ ulog (LOG_ERROR, "Bad initialization string");
+ ubuffree (zstr);
+ return FALSE;
+ }
+
+ ulog (LOG_NORMAL, "Login successful");
+
+ qstat->ttype = STATUS_TALKING;
+ qstat->ilast = ixsysdep_time ((long *) NULL);
+ qstat->cretries = 0;
+ qstat->cwait = 0;
+ if (! fsysdep_set_status (qsys, qstat))
+ return FALSE;
+
+ if (zstr[5] == '=')
+ {
+ const char *zheresys;
+ size_t clen;
+ int icmp;
+
+ /* Some UUCP packages only provide seven characters in the Shere
+ machine name. Others only provide fourteen. */
+ zheresys = zstr + 6;
+ clen = strlen (zheresys);
+ if (clen == 7 || clen == 14)
+ icmp = strncmp (zheresys, qsys->uuconf_zname, clen);
+ else
+ icmp = strcmp (zheresys, qsys->uuconf_zname);
+ if (icmp != 0)
+ {
+ if (qsys->uuconf_pzalias != NULL)
+ {
+ char **pz;
+
+ for (pz = qsys->uuconf_pzalias; *pz != NULL; pz++)
+ {
+ if (clen == 7 || clen == 14)
+ icmp = strncmp (zheresys, *pz, clen);
+ else
+ icmp = strcmp (zheresys, *pz);
+ if (icmp == 0)
+ break;
+ }
+ }
+ if (icmp != 0)
+ {
+ ulog (LOG_ERROR, "Called wrong system (%s)", zheresys);
+ ubuffree (zstr);
+ return FALSE;
+ }
+ }
+ }
+#if DEBUG > 1
+ else if (zstr[5] != '\0')
+ DEBUG_MESSAGE1 (DEBUG_HANDSHAKE,
+ "fdo_call: Strange Shere: %s", zstr);
+#endif
+
+ ubuffree (zstr);
+
+ /* We now send "S" name switches, where name is our UUCP name. If
+ we are using sequence numbers with this system, we send a -Q
+ argument with the sequence number. If the call-timegrade command
+ was used, we send a -p argument and a -vgrade= argument with the
+ grade to send us (we send both argument to make it more likely
+ that one is recognized). We always send a -N (for new) switch
+ indicating what new features we support. */
+ {
+ long ival;
+ char bgrade;
+ char *zsend;
+ boolean fret;
+
+ /* Determine the grade we should request of the other system. A
+ '\0' means that no restrictions have been made. */
+ if (! ftimespan_match (qsys->uuconf_qcalltimegrade, &ival,
+ (int *) NULL))
+ bgrade = '\0';
+ else
+ bgrade = (char) ival;
+
+ /* Determine the name we will call ourselves. */
+ if (qsys->uuconf_zlocalname != NULL)
+ qdaemon->zlocalname = qsys->uuconf_zlocalname;
+ else
+ {
+ iuuconf = uuconf_localname (puuconf, &qdaemon->zlocalname);
+ if (iuuconf == UUCONF_NOT_FOUND)
+ {
+ qdaemon->zlocalname = zsysdep_localname ();
+ if (qdaemon->zlocalname == NULL)
+ return FALSE;
+ }
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ return FALSE;
+ }
+ }
+
+ zsend = zbufalc (strlen (qdaemon->zlocalname) + 70);
+ if (! qsys->uuconf_fsequence)
+ {
+ if (bgrade == '\0')
+ sprintf (zsend, "S%s -R -N0%o", qdaemon->zlocalname,
+ (unsigned int) (FEATURE_SIZES
+ | FEATURE_EXEC
+ | FEATURE_RESTART));
+ else
+ sprintf (zsend, "S%s -p%c -vgrade=%c -R -N0%o",
+ qdaemon->zlocalname, bgrade, bgrade,
+ (unsigned int) (FEATURE_SIZES
+ | FEATURE_EXEC
+ | FEATURE_RESTART));
+ }
+ else
+ {
+ long iseq;
+
+ iseq = ixsysdep_get_sequence (qsys);
+ if (iseq < 0)
+ return FALSE;
+ if (bgrade == '\0')
+ sprintf (zsend, "S%s -Q%ld -R -N0%o", qdaemon->zlocalname, iseq,
+ (unsigned int) (FEATURE_SIZES
+ | FEATURE_EXEC
+ | FEATURE_RESTART));
+ else
+ sprintf (zsend, "S%s -Q%ld -p%c -vgrade=%c -R -N0%o",
+ qdaemon->zlocalname, iseq, bgrade, bgrade,
+ (unsigned int) (FEATURE_SIZES
+ | FEATURE_EXEC
+ | FEATURE_RESTART));
+ }
+
+ fret = fsend_uucp_cmd (qconn, zsend);
+ ubuffree (zsend);
+ if (! fret)
+ return FALSE;
+ }
+
+ /* Now we should see ROK or Rreason where reason gives a cryptic
+ reason for failure. If we are talking to a counterpart, we will
+ get back ROKN, possibly with a feature bitfield attached. */
+ zstr = zget_uucp_cmd (qconn, TRUE);
+ if (zstr == NULL)
+ return FALSE;
+
+ if (zstr[0] != 'R')
+ {
+ ulog (LOG_ERROR, "Bad reponse to handshake string (%s)",
+ zstr);
+ ubuffree (zstr);
+ return FALSE;
+ }
+
+ if (strncmp (zstr + 1, "OKN", sizeof "OKN" - 1) == 0)
+ {
+ if (zstr[sizeof "ROKN" - 1] == '\0')
+ qdaemon->ifeatures |= FEATURE_SIZES | FEATURE_V103;
+ else
+ qdaemon->ifeatures |= (int) strtol (zstr + sizeof "ROKN" - 1,
+ (char **) NULL, 0);
+ }
+ else if (strncmp (zstr + 1, "OK", sizeof "OK" - 1) == 0)
+ {
+ if (zstr[sizeof "ROK" - 1] != '\0')
+ {
+ char *zopt;
+
+ /* SVR4 UUCP returns options following the ROK string. */
+ zopt = zstr + sizeof "ROK" - 1;
+ while (*zopt != '\0')
+ {
+ char b;
+ long c;
+ char *zend;
+
+ b = *zopt++;
+ if (isspace (b) || b != '-')
+ continue;
+ switch (*zopt)
+ {
+ case 'R':
+ qdaemon->ifeatures |= (FEATURE_RESTART
+ | FEATURE_SVR4
+ | FEATURE_SIZES);
+ break;
+ case 'U':
+ c = strtol (zopt, &zend, 0);
+ if (c > 0 && c <= LONG_MAX / (long) 512)
+ qdaemon->cmax_receive = c * (long) 512;
+ zopt = zend;
+ break;
+ }
+ while (*zopt != '\0' && ! isspace (*zopt))
+ ++zopt;
+ }
+ }
+ }
+ else if (strcmp (zstr + 1, "CB") == 0)
+ {
+ ulog (LOG_NORMAL, "Remote system will call back");
+ qstat->ttype = STATUS_COMPLETE;
+ (void) fsysdep_set_status (qsys, qstat);
+ ubuffree (zstr);
+ return TRUE;
+ }
+ else
+ {
+ ulog (LOG_ERROR, "Handshake failed (%s)", zstr + 1);
+ ubuffree (zstr);
+ return FALSE;
+ }
+
+ ubuffree (zstr);
+
+ /* The slave should now send \020Pprotos\0 where protos is a list of
+ supported protocols. Each protocol is a single character. */
+ zstr = zget_uucp_cmd (qconn, TRUE);
+ if (zstr == NULL)
+ return FALSE;
+
+ if (zstr[0] != 'P')
+ {
+ ulog (LOG_ERROR, "Bad protocol handshake (%s)", zstr);
+ ubuffree (zstr);
+ return FALSE;
+ }
+
+ /* Determine the reliability characteristics of the connection by
+ combining information for the port and the dialer. If we have no
+ information, default to a reliable eight-bit full-duplex
+ connection. */
+ if (qconn->qport != NULL
+ && (qconn->qport->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0)
+ qdaemon->ireliable = qconn->qport->uuconf_ireliable;
+ if (qdialer != NULL
+ && (qdialer->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0)
+ {
+ if (qdaemon->ireliable != 0)
+ qdaemon->ireliable &= qdialer->uuconf_ireliable;
+ else
+ qdaemon->ireliable = qdialer->uuconf_ireliable;
+ }
+ if (qdaemon->ireliable == 0)
+ qdaemon->ireliable = (UUCONF_RELIABLE_RELIABLE
+ | UUCONF_RELIABLE_EIGHT
+ | UUCONF_RELIABLE_FULLDUPLEX
+ | UUCONF_RELIABLE_SPECIFIED);
+
+ /* Now decide which protocol to use. The system and the port may
+ have their own list of protocols. */
+ {
+ int i;
+ char ab[5];
+
+ i = CPROTOCOLS;
+ if (qsys->uuconf_zprotocols != NULL
+ || (qconn->qport != NULL
+ && qconn->qport->uuconf_zprotocols != NULL))
+ {
+ const char *zproto;
+
+ if (qsys->uuconf_zprotocols != NULL)
+ zproto = qsys->uuconf_zprotocols;
+ else
+ zproto = qconn->qport->uuconf_zprotocols;
+ for (; *zproto != '\0'; zproto++)
+ {
+ if (strchr (zstr + 1, *zproto) != NULL)
+ {
+ for (i = 0; i < CPROTOCOLS; i++)
+ if (asProtocols[i].bname == *zproto)
+ break;
+ if (i < CPROTOCOLS)
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* If neither the system nor the port specified a list of
+ protocols, we want only protocols that match the known
+ reliability of the dialer and the port. */
+ for (i = 0; i < CPROTOCOLS; i++)
+ {
+ int ipr;
+
+ ipr = asProtocols[i].ireliable;
+ if ((ipr & qdaemon->ireliable) != ipr)
+ continue;
+ if (strchr (zstr + 1, asProtocols[i].bname) != NULL)
+ break;
+ }
+ }
+
+ ubuffree (zstr);
+
+ if (i >= CPROTOCOLS)
+ {
+ (void) fsend_uucp_cmd (qconn, "UN");
+ ulog (LOG_ERROR, "No mutually supported protocols");
+ return FALSE;
+ }
+
+ qdaemon->qproto = &asProtocols[i];
+
+ sprintf (ab, "U%c", qdaemon->qproto->bname);
+ if (! fsend_uucp_cmd (qconn, ab))
+ return FALSE;
+ }
+
+ /* Run any protocol parameter commands. */
+ if (qdaemon->qproto->qcmds != NULL)
+ {
+ if (qsys->uuconf_qproto_params != NULL)
+ uapply_proto_params (puuconf, qdaemon->qproto->bname,
+ qdaemon->qproto->qcmds,
+ qsys->uuconf_qproto_params);
+ if (qconn->qport != NULL
+ && qconn->qport->uuconf_qproto_params != NULL)
+ uapply_proto_params (puuconf, qdaemon->qproto->bname,
+ qdaemon->qproto->qcmds,
+ qconn->qport->uuconf_qproto_params);
+ if (qdialer != NULL
+ && qdialer->uuconf_qproto_params != NULL)
+ uapply_proto_params (puuconf, qdaemon->qproto->bname,
+ qdaemon->qproto->qcmds,
+ qdialer->uuconf_qproto_params);
+ }
+
+ /* Turn on the selected protocol. */
+ if (! (*qdaemon->qproto->pfstart) (qdaemon, &zlog))
+ return FALSE;
+ if (zlog == NULL)
+ {
+ zlog = zbufalc (sizeof "protocol ''" + 1);
+ sprintf (zlog, "protocol '%c'", qdaemon->qproto->bname);
+ }
+ ulog (LOG_NORMAL, "Handshake successful (%s)", zlog);
+ ubuffree (zlog);
+
+ *pterr = STATUS_FAILED;
+
+ {
+ boolean fret;
+ long iend_time;
+
+ fret = floop (qdaemon);
+
+ /* Now send the hangup message. As the caller, we send six O's
+ and expect to receive seven O's. We send the six O's twice to
+ help the other side. We don't worry about errors here. */
+ if (fsend_uucp_cmd (qconn, "OOOOOO")
+ && fsend_uucp_cmd (qconn, "OOOOOO"))
+ {
+ int i, fdone;
+
+ /* We look for the remote hangup string to ensure that the
+ modem has sent out our hangup string. This is only
+ necessary because some versions of UUCP complain if they
+ don't get the hangup string. The remote site should send 7
+ O's, but some versions of UUCP only send 6. We look for
+ the string several times because supposedly some
+ implementations send some garbage after the last packet but
+ before the hangup string. */
+ for (i = 0; i < 25; i++)
+ {
+ zstr = zget_uucp_cmd (qconn, FALSE);
+ if (zstr == NULL)
+ break;
+ fdone = strstr (zstr, "OOOOOO") != NULL;
+ ubuffree (zstr);
+ if (fdone)
+ break;
+ }
+ }
+
+ iend_time = ixsysdep_time ((long *) NULL);
+
+ ulog (LOG_NORMAL, "Call complete (%ld seconds)",
+ iend_time - istart_time);
+
+ if (fret)
+ {
+ qstat->ttype = STATUS_COMPLETE;
+ qstat->ilast = iend_time;
+ (void) fsysdep_set_status (qsys, qstat);
+ }
+
+ return fret;
+ }
+}
+
+/* This routine is called via uuconf_find_port when a matching port is
+ found. It tries to lock the port. If it fails, it returns
+ UUCONF_NOT_FOUND to force uuconf_find_port to continue searching
+ for the next matching port. */
+
+static int
+iuport_lock (qport, pinfo)
+ struct uuconf_port *qport;
+ pointer pinfo;
+{
+ struct spass *q = (struct spass *) pinfo;
+
+ q->fmatched = TRUE;
+
+ if (! fconn_init (qport, q->qconn))
+ return UUCONF_NOT_FOUND;
+ else if (! fconn_lock (q->qconn, FALSE))
+ {
+ uconn_free (q->qconn);
+ return UUCONF_NOT_FOUND;
+ }
+ else
+ {
+ q->flocked = TRUE;
+ return UUCONF_SUCCESS;
+ }
+}
+
+/* Prompt for a login name and a password, and run as the slave. */
+
+static boolean
+flogin_prompt (puuconf, qconn)
+ pointer puuconf;
+ struct sconnection *qconn;
+{
+ char *zuser, *zpass;
+ boolean fret;
+ int iuuconf;
+
+ DEBUG_MESSAGE0 (DEBUG_HANDSHAKE, "flogin_prompt: Waiting for login");
+
+ zuser = NULL;
+ do
+ {
+ ubuffree (zuser);
+ if (! fconn_write (qconn, "login: ", sizeof "login: " - 1))
+ return FALSE;
+ zuser = zget_typed_line (qconn);
+ }
+ while (zuser != NULL && *zuser == '\0');
+
+ if (zuser == NULL)
+ return TRUE;
+
+ if (! fconn_write (qconn, "Password:", sizeof "Password:" - 1))
+ {
+ ubuffree (zuser);
+ return FALSE;
+ }
+
+ zpass = zget_typed_line (qconn);
+ if (zpass == NULL)
+ {
+ ubuffree (zuser);
+ return TRUE;
+ }
+
+ fret = TRUE;
+
+ iuuconf = uuconf_callin (puuconf, zuser, zpass);
+ ubuffree (zpass);
+ if (iuuconf == UUCONF_NOT_FOUND)
+ ulog (LOG_ERROR, "Bad login");
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ fret = FALSE;
+ }
+ else
+ {
+#if DEBUG > 1
+ int iholddebug;
+#endif
+
+ /* We ignore the return value of faccept_call because we really
+ don't care whether the call succeeded or not. We are going
+ to reset the port anyhow. */
+#if DEBUG > 1
+ iholddebug = iDebug;
+#endif
+ (void) faccept_call (puuconf, zuser, qconn, (const char **) NULL);
+#if DEBUG > 1
+ iDebug = iholddebug;
+#endif
+ }
+
+ ubuffree (zuser);
+
+ return fret;
+}
+
+/* Accept a call from a remote system. If pqsys is not NULL, *pqsys
+ will be set to the system that called in if known. */
+
+static boolean
+faccept_call (puuconf, zlogin, qconn, pzsystem)
+ pointer puuconf;
+ const char *zlogin;
+ struct sconnection *qconn;
+ const char **pzsystem;
+{
+ long istart_time;
+ const char *zport;
+ struct uuconf_port *qport;
+ struct uuconf_port sport;
+ int iuuconf;
+ struct uuconf_dialer *qdialer;
+ struct uuconf_dialer sdialer;
+ boolean ftcp_port;
+ char *zsend, *zspace;
+ boolean fret;
+ char *zstr;
+ struct uuconf_system ssys;
+ const struct uuconf_system *qsys;
+ const struct uuconf_system *qany;
+ char *zloc;
+ struct sstatus sstat;
+ boolean fgotseq, fgotn;
+ int i;
+ char *zlog;
+ char *zgrade;
+
+ if (pzsystem != NULL)
+ *pzsystem = NULL;
+
+ ulog (LOG_NORMAL, "Incoming call (login %s port %s)", zlogin,
+ zLdevice == NULL ? (char *) "unknown" : zLdevice);
+
+ istart_time = ixsysdep_time ((long *) NULL);
+
+ /* Figure out protocol parameters determined by the port. If no
+ port was specified we're reading standard input, so try to get
+ the port name and read information from the port file. We only
+ use the port information to get protocol parameters; we don't
+ want to start treating the port as though it were a modem, for
+ example. */
+ if (qconn->qport != NULL)
+ {
+ qport = qconn->qport;
+ zport = qport->uuconf_zname;
+ ftcp_port = FALSE;
+ }
+ else
+ {
+ zport = zsysdep_port_name (&ftcp_port);
+ if (zport == NULL)
+ {
+ qport = NULL;
+ zport = "unknown";
+ }
+ else
+ {
+ iuuconf = uuconf_find_port (puuconf, zport, (long) 0, (long) 0,
+ (int (*) P((struct uuconf_port *,
+ pointer pinfo))) NULL,
+ (pointer) NULL,
+ &sport);
+ if (iuuconf == UUCONF_NOT_FOUND)
+ qport = NULL;
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ return FALSE;
+ }
+ else
+ qport = &sport;
+ }
+ }
+
+ /* If we've managed to figure out that this is a modem port, now try
+ to get protocol parameters from the dialer. */
+ qdialer = NULL;
+ if (qport != NULL)
+ {
+ if (qport->uuconf_ttype == UUCONF_PORTTYPE_MODEM)
+ {
+ if (qport->uuconf_u.uuconf_smodem.uuconf_pzdialer != NULL)
+ {
+ const char *zdialer;
+
+ zdialer = qport->uuconf_u.uuconf_smodem.uuconf_pzdialer[0];
+ iuuconf = uuconf_dialer_info (puuconf, zdialer, &sdialer);
+ if (iuuconf == UUCONF_SUCCESS)
+ qdialer = &sdialer;
+ }
+ else
+ qdialer = qport->uuconf_u.uuconf_smodem.uuconf_qdialer;
+ }
+ else if (qport->uuconf_ttype == UUCONF_PORTTYPE_TCP
+ || (qport->uuconf_ttype == UUCONF_PORTTYPE_TLI
+ && (qport->uuconf_ireliable
+ & UUCONF_RELIABLE_SPECIFIED) == 0))
+ ftcp_port = TRUE;
+ }
+
+ sDaemon.puuconf = puuconf;
+ sDaemon.qsys = NULL;
+ sDaemon.zlocalname = NULL;
+ sDaemon.qconn = qconn;
+ sDaemon.qproto = NULL;
+ sDaemon.clocal_size = -1;
+ sDaemon.cremote_size = -1;
+ sDaemon.cmax_ever = -2;
+ sDaemon.cmax_receive = -1;
+ sDaemon.ifeatures = 0;
+ sDaemon.frequest_hangup = FALSE;
+ sDaemon.fhangup_requested = FALSE;
+ sDaemon.fhangup = FALSE;
+ sDaemon.fmaster = FALSE;
+ sDaemon.fcaller = FALSE;
+ sDaemon.ireliable = 0;
+ sDaemon.bgrade = UUCONF_GRADE_LOW;
+
+ /* Get the local name to use. If uuconf_login_localname returns a
+ value, it is not always freed up, although it should be. */
+ iuuconf = uuconf_login_localname (puuconf, zlogin, &zloc);
+ if (iuuconf == UUCONF_SUCCESS)
+ sDaemon.zlocalname = zloc;
+ else if (iuuconf == UUCONF_NOT_FOUND)
+ {
+ sDaemon.zlocalname = zsysdep_localname ();
+ if (sDaemon.zlocalname == NULL)
+ return FALSE;
+ }
+ else
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ return FALSE;
+ }
+
+ /* Tell the remote system who we are. */
+ zsend = zbufalc (strlen (sDaemon.zlocalname) + sizeof "Shere=");
+ sprintf (zsend, "Shere=%s", sDaemon.zlocalname);
+ fret = fsend_uucp_cmd (qconn, zsend);
+ ubuffree (zsend);
+ if (! fret)
+ return FALSE;
+
+ zstr = zget_uucp_cmd (qconn, TRUE);
+ if (zstr == NULL)
+ return FALSE;
+
+ if (zstr[0] != 'S')
+ {
+ ulog (LOG_ERROR, "Bad introduction string");
+ ubuffree (zstr);
+ return FALSE;
+ }
+
+ zspace = strchr (zstr, ' ');
+ if (zspace != NULL)
+ *zspace = '\0';
+
+ iuuconf = uuconf_system_info (puuconf, zstr + 1, &ssys);
+ if (iuuconf == UUCONF_NOT_FOUND)
+ {
+ char *zscript;
+
+ /* Run the remote.unknown script, if appropriate. */
+ iuuconf = uuconf_remote_unknown (puuconf, &zscript);
+ if (iuuconf == UUCONF_SUCCESS)
+ {
+ if (! fsysdep_unknown_caller (zscript, zstr + 1))
+ {
+ xfree ((pointer) zscript);
+ (void) fsend_uucp_cmd (qconn, "RYou are unknown to me");
+ ubuffree (zstr);
+ return FALSE;
+ }
+ xfree ((pointer) zscript);
+ }
+ else if (iuuconf != UUCONF_NOT_FOUND)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ ubuffree (zstr);
+ return FALSE;
+ }
+
+ if (! funknown_system (puuconf, zstr + 1, &ssys))
+ {
+ (void) fsend_uucp_cmd (qconn, "RYou are unknown to me");
+ ulog (LOG_ERROR, "Call from unknown system %s", zstr + 1);
+ ubuffree (zstr);
+ return FALSE;
+ }
+ }
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ ubuffree (zstr);
+ return FALSE;
+ }
+
+ qany = NULL;
+ for (qsys = &ssys; qsys != NULL; qsys = qsys->uuconf_qalternate)
+ {
+ if (! qsys->uuconf_fcalled)
+ continue;
+
+ if (qsys->uuconf_zcalled_login == NULL
+ || strcmp (qsys->uuconf_zcalled_login, "ANY") == 0)
+ {
+ if (qany == NULL)
+ qany = qsys;
+ }
+ else if (strcmp (qsys->uuconf_zcalled_login, zlogin) == 0)
+ break;
+ }
+
+ if (qsys == NULL && qany != NULL)
+ {
+ iuuconf = uuconf_validate (puuconf, qany, zlogin);
+ if (iuuconf == UUCONF_SUCCESS)
+ qsys = qany;
+ else if (iuuconf != UUCONF_NOT_FOUND)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ ubuffree (zstr);
+ return FALSE;
+ }
+ }
+
+ if (qsys == NULL)
+ {
+ (void) fsend_uucp_cmd (qconn, "RLOGIN");
+ ulog (LOG_ERROR, "System %s used wrong login name %s",
+ zstr + 1, zlogin);
+ ubuffree (zstr);
+ return FALSE;
+ }
+
+ sDaemon.qsys = qsys;
+
+ if (pzsystem != NULL)
+ *pzsystem = zbufcpy (qsys->uuconf_zname);
+
+ ulog_system (qsys->uuconf_zname);
+
+#if DEBUG > 1
+ if (qsys->uuconf_zdebug != NULL)
+ iDebug |= idebug_parse (qsys->uuconf_zdebug);
+#endif
+
+ /* See if we are supposed to call the system back. This will queue
+ up an empty command. It would be better to actually call back
+ directly at this point as well. */
+ if (qsys->uuconf_fcallback)
+ {
+ (void) fsend_uucp_cmd (qconn, "RCB");
+ ulog (LOG_NORMAL, "Will call back");
+
+ /* Clear any existing status. */
+ sstat.ttype = STATUS_COMPLETE;
+ sstat.cretries = 0;
+ sstat.ilast = ixsysdep_time ((long *) NULL);
+ sstat.cwait = 0;
+ (void) fsysdep_set_status (qsys, &sstat);
+
+ ubuffree (zsysdep_spool_commands (qsys, UUCONF_GRADE_HIGH, 0,
+ (const struct scmd *) NULL));
+ ubuffree (zstr);
+ return TRUE;
+ }
+
+ /* We only permit one call at a time from a remote system. Lock it. */
+ if (! fsysdep_lock_system (qsys))
+ {
+ (void) fsend_uucp_cmd (qconn, "RLCK");
+ ulog (LOG_ERROR, "System already locked");
+ ubuffree (zstr);
+ return FALSE;
+ }
+ sLocked_system = *qsys;
+ fLocked_system = TRUE;
+
+ /* Set the system status. We don't care what the status was before.
+ We also don't want to kill the conversation just because we can't
+ output the .Status file, so we ignore any errors. */
+ sstat.ttype = STATUS_TALKING;
+ sstat.cretries = 0;
+ sstat.ilast = ixsysdep_time ((long *) NULL);
+ sstat.cwait = 0;
+ (void) fsysdep_set_status (qsys, &sstat);
+
+ /* Check the arguments of the remote system, if any. */
+ fgotseq = FALSE;
+ fgotn = FALSE;
+ if (zspace != NULL)
+ {
+ char **paz;
+ char **pzset;
+
+ ++zspace;
+
+ /* Break the introduction line up into arguments. */
+ paz = (char **) xmalloc ((strlen (zspace) / 2 + 2) * sizeof (char *));
+ pzset = paz;
+ *pzset++ = NULL;
+ while (TRUE)
+ {
+ while (*zspace != '\0' && isspace (BUCHAR (*zspace)))
+ ++zspace;
+ if (*zspace == '\0')
+ break;
+ *pzset++ = zspace;
+ ++zspace;
+ while (*zspace != '\0' && ! isspace (BUCHAR (*zspace)))
+ ++zspace;
+ if (*zspace == '\0')
+ break;
+ *zspace++ = '\0';
+ }
+
+ if (pzset != paz + 1)
+ {
+ int iopt;
+
+ *pzset = NULL;
+
+ /* We are going to use getopt to parse the arguments. We
+ must clear optind to force getopt to reinitialize, and
+ clear opterr to prevent getopt from printing an error
+ message. This approach assumes we are using the GNU
+ getopt, which is distributed with the program anyhow. */
+ optind = 0;
+ opterr = 0;
+
+ while ((iopt = getopt (pzset - paz, paz,
+ "N::p:Q:RU:v:x:")) != EOF)
+ {
+ long iseq;
+ long c;
+ char b;
+ int iwant;
+
+ switch (iopt)
+ {
+ case 'N':
+ /* This is used to indicate support for Taylor UUCP
+ extensions. An plain -N mean support for size
+ negotiation. If -N is followed by a number (with
+ no intervening space), the number is a bit field
+ of feature flags as defined in trans.h. Note
+ that the argument may start with 0x for hex or 0
+ for octal. */
+ fgotn = TRUE;
+ if (optarg == NULL)
+ sDaemon.ifeatures |= FEATURE_SIZES | FEATURE_V103;
+ else
+ sDaemon.ifeatures |= (int) strtol (optarg,
+ (char **) NULL,
+ 0);
+ break;
+
+ case 'p':
+ /* The argument is the lowest grade of work the
+ local system should send. */
+ if (UUCONF_GRADE_LEGAL (optarg[0]))
+ sDaemon.bgrade = optarg[0];
+ break;
+
+ case 'Q':
+ /* The conversation sequence number. */
+ iseq = strtol (optarg, (char **) NULL, 10);
+ if (qsys->uuconf_fsequence
+ && iseq != ixsysdep_get_sequence (qsys))
+ {
+ (void) fsend_uucp_cmd (qconn, "RBADSEQ");
+ ulog (LOG_ERROR, "Out of sequence call rejected");
+ sstat.ttype = STATUS_FAILED;
+ (void) fsysdep_set_status (qsys, &sstat);
+ xfree ((pointer) paz);
+ ubuffree (zstr);
+ return FALSE;
+ }
+ fgotseq = TRUE;
+ break;
+
+ case 'R':
+ /* The remote system supports file restart. */
+ sDaemon.ifeatures |= FEATURE_RESTART;
+ break;
+
+ case 'U':
+ /* The maximum file size the remote system is
+ prepared to received, in blocks where each block
+ is 512 bytes. */
+ c = strtol (optarg, (char **) NULL, 0);
+ if (c > 0 && c < LONG_MAX / (long) 512)
+ sDaemon.cmax_receive = c * (long) 512;
+ break;
+
+ case 'v':
+ /* -vgrade=X can be used to set the lowest grade of
+ work the local system should send. */
+ if (strncmp (optarg, "grade=", sizeof "grade=" - 1) == 0)
+ {
+ b = optarg[sizeof "grade=" - 1];
+ if (UUCONF_GRADE_LEGAL (b))
+ sDaemon.bgrade = b;
+ }
+ break;
+
+ case 'x':
+ iwant = (int) strtol (optarg, (char **) NULL, 10);
+#if DEBUG > 1
+ if (iwant <= 9)
+ iwant = (1 << iwant) - 1;
+ if (qsys->uuconf_zmax_remote_debug != NULL)
+ iwant &= idebug_parse (qsys->uuconf_zmax_remote_debug);
+ if ((iDebug | iwant) != iDebug)
+ {
+ iDebug |= iwant;
+ ulog (LOG_NORMAL, "Setting debugging mode to 0%o",
+ iDebug);
+ }
+#endif
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ xfree ((pointer) paz);
+ }
+
+ ubuffree (zstr);
+
+ if (qsys->uuconf_fsequence && ! fgotseq)
+ {
+ (void) fsend_uucp_cmd (qconn, "RBADSEQ");
+ ulog (LOG_ERROR, "No sequence number (call rejected)");
+ sstat.ttype = STATUS_FAILED;
+ (void) fsysdep_set_status (qsys, &sstat);
+ return FALSE;
+ }
+
+ /* We recognized the system, and the sequence number (if any) was
+ OK. Send an ROK, and send a list of protocols. If we got the -N
+ switch, send ROKN to confirm it; if the -N switch was followed by
+ a feature bitfield, return our own feature bitfield. */
+ {
+ char ab[20];
+ const char *zreply;
+
+ if (! fgotn)
+ {
+ if ((sDaemon.ifeatures & FEATURE_RESTART) == 0)
+ zreply = "ROK";
+ else
+ {
+ /* We got -R without -N, so assume that this is SVR4 UUCP.
+ SVR4 UUCP expects ROK -R to signal support for file
+ restart. */
+ sDaemon.ifeatures |= FEATURE_SVR4 | FEATURE_SIZES;
+ zreply = "ROK -R";
+ }
+ }
+ else if ((sDaemon.ifeatures & FEATURE_V103) != 0)
+ zreply = "ROKN";
+ else
+ {
+ sprintf (ab, "ROKN0%o",
+ (unsigned int) (FEATURE_SIZES
+ | FEATURE_EXEC
+ | FEATURE_RESTART));
+ zreply = ab;
+ }
+ if (! fsend_uucp_cmd (qconn, zreply))
+ {
+ sstat.ttype = STATUS_FAILED;
+ (void) fsysdep_set_status (qsys, &sstat);
+ return FALSE;
+ }
+ }
+
+ /* Determine the reliability of the connection based on the
+ reliability of the port and the dialer. If we have no
+ information, default to a reliable eight-bit full-duplex
+ connection. */
+ if (ftcp_port)
+ sDaemon.ireliable = (UUCONF_RELIABLE_SPECIFIED
+ | UUCONF_RELIABLE_ENDTOEND
+ | UUCONF_RELIABLE_RELIABLE
+ | UUCONF_RELIABLE_EIGHT
+ | UUCONF_RELIABLE_FULLDUPLEX);
+ else
+ {
+ if (qport != NULL
+ && (qport->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0)
+ sDaemon.ireliable = qport->uuconf_ireliable;
+ if (qdialer != NULL
+ && (qdialer->uuconf_ireliable & UUCONF_RELIABLE_SPECIFIED) != 0)
+ {
+ if (sDaemon.ireliable != 0)
+ sDaemon.ireliable &= qdialer->uuconf_ireliable;
+ else
+ sDaemon.ireliable = qdialer->uuconf_ireliable;
+ }
+ if (sDaemon.ireliable == 0)
+ sDaemon.ireliable = (UUCONF_RELIABLE_RELIABLE
+ | UUCONF_RELIABLE_EIGHT
+ | UUCONF_RELIABLE_FULLDUPLEX
+ | UUCONF_RELIABLE_SPECIFIED);
+ }
+
+ if (qsys->uuconf_zprotocols != NULL ||
+ (qport != NULL && qport->uuconf_zprotocols != NULL))
+ {
+ const char *zprotos;
+
+ if (qsys->uuconf_zprotocols != NULL)
+ zprotos = qsys->uuconf_zprotocols;
+ else
+ zprotos = qport->uuconf_zprotocols;
+ zsend = zbufalc (strlen (zprotos) + 2);
+ sprintf (zsend, "P%s", zprotos);
+ }
+ else
+ {
+ char *zset;
+
+ zsend = zbufalc (CPROTOCOLS + 2);
+ zset = zsend;
+ *zset++ = 'P';
+
+ /* If the system did not specify a list of protocols, we want
+ only protocols that match the known reliability of the dialer
+ and the port. */
+ for (i = 0; i < CPROTOCOLS; i++)
+ {
+ int ipr;
+
+ ipr = asProtocols[i].ireliable;
+ if ((ipr & sDaemon.ireliable) != ipr)
+ continue;
+ *zset++ = asProtocols[i].bname;
+ }
+ *zset = '\0';
+ }
+
+ fret = fsend_uucp_cmd (qconn, zsend);
+ ubuffree (zsend);
+ if (! fret)
+ {
+ sstat.ttype = STATUS_FAILED;
+ (void) fsysdep_set_status (qsys, &sstat);
+ return FALSE;
+ }
+
+ /* The master will now send back the selected protocol. */
+ zstr = zget_uucp_cmd (qconn, TRUE);
+ if (zstr == NULL)
+ {
+ sstat.ttype = STATUS_FAILED;
+ (void) fsysdep_set_status (qsys, &sstat);
+ return FALSE;
+ }
+
+ if (zstr[0] != 'U' || zstr[2] != '\0')
+ {
+ ulog (LOG_ERROR, "Bad protocol response string");
+ sstat.ttype = STATUS_FAILED;
+ (void) fsysdep_set_status (qsys, &sstat);
+ ubuffree (zstr);
+ return FALSE;
+ }
+
+ if (zstr[1] == 'N')
+ {
+ ulog (LOG_ERROR, "No supported protocol");
+ sstat.ttype = STATUS_FAILED;
+ (void) fsysdep_set_status (qsys, &sstat);
+ ubuffree (zstr);
+ return FALSE;
+ }
+
+ for (i = 0; i < CPROTOCOLS; i++)
+ if (asProtocols[i].bname == zstr[1])
+ break;
+
+ ubuffree (zstr);
+
+ if (i >= CPROTOCOLS)
+ {
+ ulog (LOG_ERROR, "No supported protocol");
+ sstat.ttype = STATUS_FAILED;
+ (void) fsysdep_set_status (qsys, &sstat);
+ return FALSE;
+ }
+
+ sDaemon.qproto = &asProtocols[i];
+
+ /* Run the chat script for when a call is received. */
+ if (! fchat (qconn, puuconf, &qsys->uuconf_scalled_chat, qsys,
+ (const struct uuconf_dialer *) NULL, (const char *) NULL,
+ FALSE, zport, iconn_baud (qconn)))
+ {
+ sstat.ttype = STATUS_FAILED;
+ sstat.ilast = ixsysdep_time ((long *) NULL);
+ (void) fsysdep_set_status (qsys, &sstat);
+ return FALSE;
+ }
+
+ /* Run any protocol parameter commands. */
+ if (sDaemon.qproto->qcmds != NULL)
+ {
+ if (qsys->uuconf_qproto_params != NULL)
+ uapply_proto_params (puuconf, sDaemon.qproto->bname,
+ sDaemon.qproto->qcmds,
+ qsys->uuconf_qproto_params);
+ if (qport != NULL
+ && qport->uuconf_qproto_params != NULL)
+ uapply_proto_params (puuconf, sDaemon.qproto->bname,
+ sDaemon.qproto->qcmds,
+ qport->uuconf_qproto_params);
+ if (qdialer != NULL
+ && qdialer->uuconf_qproto_params != NULL)
+ uapply_proto_params (puuconf, sDaemon.qproto->bname,
+ sDaemon.qproto->qcmds,
+ qdialer->uuconf_qproto_params);
+ }
+
+ /* We don't need the dialer information any more. */
+ if (qdialer == &sdialer)
+ (void) uuconf_dialer_free (puuconf, &sdialer);
+
+ /* Get any jobs queued for the system, and turn on the selected
+ protocol. */
+ if (! fqueue (&sDaemon, (boolean *) NULL)
+ || ! (*sDaemon.qproto->pfstart) (&sDaemon, &zlog))
+ {
+ uclear_queue (&sDaemon);
+ sstat.ttype = STATUS_FAILED;
+ sstat.ilast = ixsysdep_time ((long *) NULL);
+ (void) fsysdep_set_status (qsys, &sstat);
+ return FALSE;
+ }
+
+ if (zlog == NULL)
+ {
+ zlog = zbufalc (sizeof "protocol ''" + 1);
+ sprintf (zlog, "protocol '%c'", sDaemon.qproto->bname);
+ }
+
+ zgrade = zbufalc (sizeof "grade " + 1);
+ if (sDaemon.bgrade == UUCONF_GRADE_LOW)
+ *zgrade = '\0';
+ else
+ sprintf (zgrade, "grade %c ", sDaemon.bgrade);
+
+ /* If we are using HAVE_HDB_LOGGING, then the previous ``incoming
+ call'' message went to the general log, since we didn't know the
+ system name at that point. In that case, we repeat the port and
+ login names. */
+#if HAVE_HDB_LOGGING
+ ulog (LOG_NORMAL, "Handshake successful (login %s port %s %s%s)",
+ zlogin,
+ zLdevice == NULL ? "unknown" : zLdevice,
+ zgrade, zlog);
+#else /* ! HAVE_HDB_LOGGING */
+ ulog (LOG_NORMAL, "Handshake successful (%s%s)", zgrade, zlog);
+#endif /* ! HAVE_HDB_LOGGING */
+
+ ubuffree (zlog);
+ ubuffree (zgrade);
+
+ {
+ long iend_time;
+
+ fret = floop (&sDaemon);
+
+ /* Hangup. As the answerer, we send seven O's and expect to
+ receive six O's. We send the seven O's twice to help the other
+ side. We don't worry about errors here. */
+ if (fsend_uucp_cmd (qconn, "OOOOOOO")
+ && fsend_uucp_cmd (qconn, "OOOOOOO"))
+ {
+ int fdone;
+
+ /* We look for the remote hangup string to ensure that the
+ modem has sent out our hangup string. This is only
+ necessary because some versions of UUCP complain if they
+ don't get the hangup string. We look for the string
+ several times because supposedly some implementations send
+ some garbage after the last packet but before the hangup
+ string. */
+ for (i = 0; i < 25; i++)
+ {
+ zstr = zget_uucp_cmd (qconn, FALSE);
+ if (zstr == NULL)
+ break;
+ fdone = strstr (zstr, "OOOOOO") != NULL;
+ ubuffree (zstr);
+ if (fdone)
+ break;
+ }
+ }
+
+ iend_time = ixsysdep_time ((long *) NULL);
+
+ ulog (LOG_NORMAL, "Call complete (%ld seconds)",
+ iend_time - istart_time);
+
+ uclear_queue (&sDaemon);
+
+ if (fret)
+ sstat.ttype = STATUS_COMPLETE;
+ else
+ sstat.ttype = STATUS_FAILED;
+ sstat.ilast = iend_time;
+ (void) fsysdep_set_status (qsys, &sstat);
+
+ (void) uuconf_system_free (puuconf, &ssys);
+ if (qport == &sport)
+ (void) uuconf_port_free (puuconf, &sport);
+ xfree ((pointer) zloc);
+
+ return fret;
+ }
+}
+
+/* Apply protocol parameters, once we know the protocol. */
+
+static void
+uapply_proto_params (puuconf, bproto, qcmds, pas)
+ pointer puuconf;
+ int bproto;
+ struct uuconf_cmdtab *qcmds;
+ struct uuconf_proto_param *pas;
+{
+ struct uuconf_proto_param *qp;
+
+ for (qp = pas; qp->uuconf_bproto != '\0'; qp++)
+ {
+ if (qp->uuconf_bproto == bproto)
+ {
+ struct uuconf_proto_param_entry *qe;
+
+ for (qe = qp->uuconf_qentries; qe->uuconf_cargs > 0; qe++)
+ {
+ int iuuconf;
+
+ iuuconf = uuconf_cmd_args (puuconf, qe->uuconf_cargs,
+ qe->uuconf_pzargs, qcmds,
+ (pointer) NULL,
+ (uuconf_cmdtabfn) NULL, 0,
+ (pointer) NULL);
+ if (UUCONF_ERROR_VALUE (iuuconf) != UUCONF_SUCCESS)
+ {
+ ulog (LOG_ERROR, "Error in %c protocol parameters",
+ bproto);
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+/* Send a string to the other system beginning with a DLE
+ character and terminated with a null byte. This is only
+ used when no protocol is in force. */
+
+static boolean
+fsend_uucp_cmd (qconn, z)
+ struct sconnection *qconn;
+ const char *z;
+{
+ size_t cwrite;
+ char *zalc;
+ boolean fret;
+
+ DEBUG_MESSAGE1 (DEBUG_HANDSHAKE, "fsend_uucp_cmd: Sending \"%s\"", z);
+
+ cwrite = strlen (z) + 2;
+
+ zalc = zbufalc (cwrite);
+ zalc[0] = '\020';
+ memcpy (zalc + 1, z, cwrite - 1);
+
+ fret = fconn_write (qconn, zalc, cwrite);
+ ubuffree (zalc);
+ return fret;
+}
+
+/* Get a UUCP command beginning with a DLE character and ending with a
+ null byte. This is only used when no protocol is in force. This
+ implementation has the potential of being seriously slow. It also
+ doesn't have any real error recovery. The frequired argument is
+ passed as TRUE if we need the string; we don't care that much if
+ we're closing down the connection anyhow. */
+
+#define CTIMEOUT (120)
+#define CSHORTTIMEOUT (10)
+#define CINCREMENT (100)
+
+static char *
+zget_uucp_cmd (qconn, frequired)
+ struct sconnection *qconn;
+ boolean frequired;
+{
+ char *zalc;
+ size_t calc;
+ size_t cgot;
+ boolean fintro;
+ long iendtime;
+ int ctimeout;
+#if DEBUG > 1
+ int cchars;
+ int iolddebug;
+#endif
+
+ iendtime = ixsysdep_time ((long *) NULL);
+ if (frequired)
+ iendtime += CTIMEOUT;
+ else
+ iendtime += CSHORTTIMEOUT;
+
+#if DEBUG > 1
+ cchars = 0;
+ iolddebug = iDebug;
+ if (FDEBUGGING (DEBUG_HANDSHAKE))
+ {
+ ulog (LOG_DEBUG_START, "zget_uucp_cmd: Got \"");
+ iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT);
+ }
+#endif
+
+ zalc = NULL;
+ calc = 0;
+ cgot = 0;
+ fintro = FALSE;
+ while ((ctimeout = (int) (iendtime - ixsysdep_time ((long *) NULL))) > 0)
+ {
+ int b;
+
+ b = breceive_char (qconn, ctimeout, frequired);
+ /* Now b == -1 on timeout, -2 on error. */
+ if (b < 0)
+ {
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_HANDSHAKE))
+ {
+ ulog (LOG_DEBUG_END, "\" (%s)",
+ b == -1 ? "timeout" : "error");
+ iDebug = iolddebug;
+ }
+#endif
+ if (b == -1 && frequired)
+ ulog (LOG_ERROR, "Timeout");
+ ubuffree (zalc);
+ return NULL;
+ }
+
+ /* Apparently some systems use parity on these strings, so we
+ strip the parity bit. This may need to be configurable at
+ some point, although only if system names can have eight bit
+ characters. */
+ if (! isprint (BUCHAR (b)))
+ b &= 0x7f;
+
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_HANDSHAKE))
+ {
+ char ab[5];
+
+ ++cchars;
+ if (cchars > 60)
+ {
+ ulog (LOG_DEBUG_END, "\"");
+ ulog (LOG_DEBUG_START, "zget_uucp_cmd: Got \"");
+ cchars = 0;
+ }
+ (void) cdebug_char (ab, b);
+ ulog (LOG_DEBUG_CONTINUE, "%s", ab);
+ }
+#endif
+
+ if (! fintro)
+ {
+ if (b == '\020')
+ fintro = TRUE;
+ continue;
+ }
+
+ /* If we see another DLE, something has gone wrong; continue
+ as though this were the first one we saw. */
+ if (b == '\020')
+ {
+ cgot = 0;
+ continue;
+ }
+
+ /* Some systems send a trailing \n on the Shere line. As far as
+ I can tell this line can never contain a \n, so this
+ modification should be safe enough. */
+ if (b == '\r' || b == '\n')
+ b = '\0';
+
+ if (cgot >= calc)
+ {
+ char *znew;
+
+ calc += CINCREMENT;
+ znew = zbufalc (calc);
+ memcpy (znew, zalc, cgot);
+ ubuffree (zalc);
+ zalc = znew;
+ }
+
+ zalc[cgot] = (char) b;
+ ++cgot;
+
+ if (b == '\0')
+ {
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_HANDSHAKE))
+ {
+ ulog (LOG_DEBUG_END, "\"");
+ iDebug = iolddebug;
+ }
+#endif
+ return zalc;
+ }
+ }
+
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_HANDSHAKE))
+ {
+ ulog (LOG_DEBUG_END, "\" (timeout)");
+ iDebug = iolddebug;
+ }
+#endif
+
+ ubuffree (zalc);
+
+ if (frequired)
+ ulog (LOG_ERROR, "Timeout");
+ return NULL;
+}
+
+/* Read a sequence of characters up to a newline or carriage return, and
+ return the line without the line terminating character. */
+
+static char *
+zget_typed_line (qconn)
+ struct sconnection *qconn;
+{
+ char *zalc;
+ size_t calc;
+ size_t cgot;
+
+#if DEBUG > 1
+ int cchars;
+ int iolddebug;
+
+ cchars = 0;
+ iolddebug = iDebug;
+ if (FDEBUGGING (DEBUG_CHAT))
+ {
+ ulog (LOG_DEBUG_START, "zget_typed_line: Got \"");
+ iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT);
+ }
+#endif
+
+ zalc = NULL;
+ calc = 0;
+ cgot = 0;
+ while (TRUE)
+ {
+ int b;
+
+ b = breceive_char (qconn, CTIMEOUT, FALSE);
+
+ /* Now b == -1 on timeout, -2 on error. */
+
+ if (b == -2 || FGOT_SIGNAL ())
+ {
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_CHAT))
+ {
+ ulog (LOG_DEBUG_END, "\" (error)");
+ iDebug = iolddebug;
+ }
+#endif
+ ubuffree (zalc);
+ return NULL;
+ }
+
+ if (b == -1)
+ continue;
+
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_CHAT))
+ {
+ char ab[5];
+
+ ++cchars;
+ if (cchars > 60)
+ {
+ ulog (LOG_DEBUG_END, "\"");
+ ulog (LOG_DEBUG_START, "zget_typed_line: Got \"");
+ cchars = 0;
+ }
+ (void) cdebug_char (ab, b);
+ ulog (LOG_DEBUG_CONTINUE, "%s", ab);
+ }
+#endif
+
+ if (cgot >= calc)
+ {
+ char *znew;
+
+ calc += CINCREMENT;
+ znew = zbufalc (calc);
+ memcpy (znew, zalc, cgot);
+ ubuffree (zalc);
+ zalc = znew;
+ }
+
+ if (b == '\r' || b == '\n')
+ b = '\0';
+
+ zalc[cgot] = (char) b;
+ ++cgot;
+
+ if (b == '\0')
+ {
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_CHAT))
+ {
+ ulog (LOG_DEBUG_END, "\"");
+ iDebug = iolddebug;
+ }
+#endif
+ return zalc;
+ }
+ }
+}
diff --git a/gnu/libexec/uucp/uucico/xcmd.c b/gnu/libexec/uucp/uucico/xcmd.c
new file mode 100644
index 0000000000000..cadf6383dff91
--- /dev/null
+++ b/gnu/libexec/uucp/uucico/xcmd.c
@@ -0,0 +1,396 @@
+/* xcmd.c
+ Routines to handle work requests.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char xcmd_rcsid[] = "$Id: xcmd.c,v 1.1 1993/08/05 18:27:25 conklin Exp $";
+#endif
+
+#include <errno.h>
+
+#include "uudefs.h"
+#include "uuconf.h"
+#include "system.h"
+#include "prot.h"
+#include "trans.h"
+
+/* Local functions. */
+
+static boolean flocal_xcmd_request P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+static boolean flocal_xcmd_await_reply P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon,
+ const char *zdata, size_t cdata));
+static boolean fremote_xcmd_reply P((struct stransfer *qtrans,
+ struct sdaemon *qdaemon));
+
+/* Handle a local work request. We just set up the request for
+ transmission. */
+
+boolean
+flocal_xcmd_init (qdaemon, qcmd)
+ struct sdaemon *qdaemon;
+ struct scmd *qcmd;
+{
+ struct stransfer *qtrans;
+
+ qtrans = qtransalc (qcmd);
+ qtrans->psendfn = flocal_xcmd_request;
+
+ return fqueue_local (qdaemon, qtrans);
+}
+
+/* Send the execution request to the remote system. */
+
+static boolean
+flocal_xcmd_request (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ size_t clen;
+ char *zsend;
+ boolean fret;
+
+ ulog (LOG_NORMAL, "Requesting work: %s to %s", qtrans->s.zfrom,
+ qtrans->s.zto);
+
+ /* We send the string
+ X from to user options
+ We put a dash in front of options. */
+ clen = (strlen (qtrans->s.zfrom) + strlen (qtrans->s.zto)
+ + strlen (qtrans->s.zuser) + strlen (qtrans->s.zoptions) + 7);
+ zsend = zbufalc (clen);
+ sprintf (zsend, "X %s %s %s -%s", qtrans->s.zfrom, qtrans->s.zto,
+ qtrans->s.zuser, qtrans->s.zoptions);
+
+ fret = (*qdaemon->qproto->pfsendcmd) (qdaemon, zsend, qtrans->ilocal,
+ qtrans->iremote);
+ ubuffree (zsend);
+ if (! fret)
+ {
+ utransfree (qtrans);
+ return FALSE;
+ }
+
+ qtrans->fcmd = TRUE;
+ qtrans->precfn = flocal_xcmd_await_reply;
+
+ return fqueue_receive (qdaemon, qtrans);
+}
+
+/* Get a reply to an execution request from the remote system. */
+
+/*ARGSUSED*/
+static boolean
+flocal_xcmd_await_reply (qtrans, qdaemon, zdata, cdata)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+ const char *zdata;
+ size_t cdata;
+{
+ qtrans->precfn = NULL;
+
+ if (zdata[0] != 'X'
+ || (zdata[1] != 'Y' && zdata[1] != 'N'))
+ {
+ ulog (LOG_ERROR, "Bad response to work request");
+ utransfree (qtrans);
+ return FALSE;
+ }
+
+ if (zdata[1] == 'N')
+ {
+ ulog (LOG_ERROR, "%s: work request denied", qtrans->s.zfrom);
+ (void) fmail_transfer (FALSE, qtrans->s.zuser, (const char *) NULL,
+ "work request denied",
+ qtrans->s.zfrom, qdaemon->qsys->uuconf_zname,
+ qtrans->s.zto, (const char *) NULL,
+ (const char *) NULL);
+ }
+
+ (void) fsysdep_did_work (qtrans->s.pseq);
+ utransfree (qtrans);
+
+ return TRUE;
+}
+
+/* Handle a remote work request. This just queues up the requests for
+ later processing. */
+
+boolean
+fremote_xcmd_init (qdaemon, qcmd, iremote)
+ struct sdaemon *qdaemon;
+ struct scmd *qcmd;
+ int iremote;
+{
+ const struct uuconf_system *qsys;
+ const char *zexclam;
+ const struct uuconf_system *qdestsys;
+ struct uuconf_system sdestsys;
+ char *zdestfile;
+ boolean fmkdirs;
+ struct stransfer *qtrans;
+ char *zuser;
+ char aboptions[5];
+ char *zfrom;
+ boolean fret;
+ char *zfile;
+
+ ulog (LOG_NORMAL, "Work requested: %s to %s", qcmd->zfrom,
+ qcmd->zto);
+
+ qsys = qdaemon->qsys;
+
+ zexclam = strchr (qcmd->zto, '!');
+ if (zexclam == NULL
+ || zexclam == qcmd->zto
+ || strncmp (qdaemon->zlocalname, qcmd->zto,
+ (size_t) (zexclam - qcmd->zto)) == 0)
+ {
+ const char *zconst;
+
+ /* The files are supposed to be copied to the local system. */
+ qdestsys = NULL;
+ if (zexclam == NULL)
+ zconst = qcmd->zto;
+ else
+ zconst = zexclam + 1;
+
+ zdestfile = zsysdep_local_file (zconst, qsys->uuconf_zpubdir);
+ if (zdestfile == NULL)
+ return FALSE;
+
+ zuser = NULL;
+ fmkdirs = strchr (qcmd->zoptions, 'f') != NULL;
+ }
+ else
+ {
+ size_t clen;
+ char *zcopy;
+ int iuuconf;
+ char *zoptions;
+
+ clen = zexclam - qcmd->zto;
+ zcopy = zbufalc (clen + 1);
+ memcpy (zcopy, qcmd->zto, clen);
+ zcopy[clen] = '\0';
+
+ iuuconf = uuconf_system_info (qdaemon->puuconf, zcopy, &sdestsys);
+ if (iuuconf == UUCONF_NOT_FOUND)
+ {
+ if (! funknown_system (qdaemon->puuconf, zcopy, &sdestsys))
+ {
+ ulog (LOG_ERROR, "%s: System not found", zcopy);
+ ubuffree (zcopy);
+ qtrans = qtransalc (qcmd);
+ qtrans->psendfn = fremote_xcmd_reply;
+ qtrans->pinfo = (pointer) "XN";
+ qtrans->iremote = iremote;
+ return fqueue_remote (qdaemon, qtrans);
+ }
+ }
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, qdaemon->puuconf, iuuconf);
+ ubuffree (zcopy);
+ return FALSE;
+ }
+
+ ubuffree (zcopy);
+
+ qdestsys = &sdestsys;
+ zdestfile = zbufcpy (zexclam + 1);
+
+ zuser = zbufalc (strlen (qdestsys->uuconf_zname)
+ + strlen (qcmd->zuser) + sizeof "!");
+ sprintf (zuser, "%s!%s", qdestsys->uuconf_zname, qcmd->zuser);
+ zoptions = aboptions;
+ *zoptions++ = 'C';
+ if (strchr (qcmd->zoptions, 'd') != NULL)
+ *zoptions++ = 'd';
+ if (strchr (qcmd->zoptions, 'm') != NULL)
+ *zoptions++ = 'm';
+ *zoptions = '\0';
+ fmkdirs = TRUE;
+ }
+
+ /* At this point we prepare to confirm the remote request. We could
+ actually fork here and let the child spool up the requests. */
+ qtrans = qtransalc (qcmd);
+ qtrans->psendfn = fremote_xcmd_reply;
+ qtrans->pinfo = (pointer) "XY";
+ qtrans->iremote = iremote;
+ if (! fqueue_remote (qdaemon, qtrans))
+ {
+ ubuffree (zdestfile);
+ ubuffree (zuser);
+ return FALSE;
+ }
+
+ /* Now we have to process each source file. The source
+ specification may or may use wildcards. */
+ zfrom = zsysdep_local_file (qcmd->zfrom, qsys->uuconf_zpubdir);
+ if (zfrom == NULL)
+ {
+ ubuffree (zdestfile);
+ ubuffree (zuser);
+ return FALSE;
+ }
+
+ if (! fsysdep_wildcard_start (zfrom))
+ {
+ ubuffree (zfrom);
+ ubuffree (zdestfile);
+ ubuffree (zuser);
+ return FALSE;
+ }
+
+ fret = TRUE;
+
+ while ((zfile = zsysdep_wildcard (zfrom)) != NULL)
+ {
+ char *zto;
+ char abtname[CFILE_NAME_LEN];
+
+ if (! fsysdep_file_exists (zfile))
+ {
+ ulog (LOG_ERROR, "%s: no such file", zfile);
+ continue;
+ }
+
+ /* Make sure the remote system is permitted to read the
+ specified file. */
+ if (! fin_directory_list (zfile, qsys->uuconf_pzremote_send,
+ qsys->uuconf_zpubdir, TRUE, TRUE,
+ (const char *) NULL))
+ {
+ ulog (LOG_ERROR, "%s: not permitted to send", zfile);
+ break;
+ }
+
+ if (qdestsys != NULL)
+ {
+ /* We really should get the original grade here. */
+ zto = zsysdep_data_file_name (qdestsys, qdaemon->zlocalname,
+ BDEFAULT_UUCP_GRADE, FALSE,
+ abtname, (char *) NULL,
+ (char *) NULL);
+ if (zto == NULL)
+ {
+ fret = FALSE;
+ break;
+ }
+ }
+ else
+ {
+ zto = zsysdep_add_base (zdestfile, zfile);
+ if (zto == NULL)
+ {
+ fret = FALSE;
+ break;
+ }
+ /* We only accept a local destination if the remote system
+ has the right to create files there. */
+ if (! fin_directory_list (zto, qsys->uuconf_pzremote_receive,
+ qsys->uuconf_zpubdir, TRUE, FALSE,
+ (const char *) NULL))
+ {
+ ulog (LOG_ERROR, "%s: not permitted to receive", zto);
+ ubuffree (zto);
+ break;
+ }
+ }
+
+ /* Copy the file either to the final destination or to the
+ spool directory. */
+ if (! fcopy_file (zfile, zto, qdestsys == NULL, fmkdirs))
+ {
+ ubuffree (zto);
+ break;
+ }
+
+ ubuffree (zto);
+
+ /* If there is a destination system, queue it up. */
+ if (qdestsys != NULL)
+ {
+ struct scmd ssend;
+ char *zjobid;
+
+ ssend.bcmd = 'S';
+ ssend.pseq = NULL;
+ ssend.zfrom = zfile;
+ ssend.zto = zdestfile;
+ ssend.zuser = zuser;
+ ssend.zoptions = aboptions;
+ ssend.ztemp = abtname;
+ ssend.imode = ixsysdep_file_mode (zfile);
+ ssend.znotify = "";
+ ssend.cbytes = -1;
+ ssend.zcmd = NULL;
+ ssend.ipos = 0;
+
+ zjobid = zsysdep_spool_commands (qdestsys, BDEFAULT_UUCP_GRADE,
+ 1, &ssend);
+ if (zjobid == NULL)
+ break;
+ ubuffree (zjobid);
+ }
+
+ ubuffree (zfile);
+ }
+
+ if (zfile != NULL)
+ ubuffree (zfile);
+
+ (void) fsysdep_wildcard_end ();
+
+ ubuffree (zdestfile);
+ if (qdestsys != NULL)
+ (void) uuconf_system_free (qdaemon->puuconf, &sdestsys);
+
+ ubuffree (zfrom);
+ ubuffree (zuser);
+
+ return fret;
+}
+
+/* Reply to a remote work request. */
+
+static boolean
+fremote_xcmd_reply (qtrans, qdaemon)
+ struct stransfer *qtrans;
+ struct sdaemon *qdaemon;
+{
+ boolean fret;
+
+ fret = (*qdaemon->qproto->pfsendcmd) (qdaemon,
+ (const char *) qtrans->pinfo,
+ qtrans->ilocal,
+ qtrans->iremote);
+ utransfree (qtrans);
+ return fret;
+}