1256 lines
31 KiB
C
1256 lines
31 KiB
C
/* prot.c
|
||
Protocol support routines to move commands and data around.
|
||
|
||
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 AIRS, P.O. Box 520, Waltham, MA 02254.
|
||
|
||
$Log: prot.c,v $
|
||
Revision 1.1.1.1 1993/03/21 09:45:37 cgd
|
||
initial import of 386bsd-0.1 sources
|
||
|
||
Revision 1.21 1992/04/02 22:51:09 ian
|
||
Add gcc 2.0 format checking to ulog, and fixed discovered problems
|
||
|
||
Revision 1.20 1992/03/30 04:49:10 ian
|
||
Niels Baggesen: added debugging types abnormal and uucp-proto
|
||
|
||
Revision 1.19 1992/03/30 04:07:13 ian
|
||
Dirk Musstopf: remove temporary file if receive fails
|
||
|
||
Revision 1.18 1992/03/13 22:59:25 ian
|
||
Have breceive_char go through freceive_data
|
||
|
||
Revision 1.17 1992/03/12 19:56:10 ian
|
||
Debugging based on types rather than number
|
||
|
||
Revision 1.16 1992/03/11 01:18:15 ian
|
||
Niels Baggesen: drop the connection on a write failure
|
||
|
||
Revision 1.15 1992/03/11 00:18:50 ian
|
||
Save temporary file if file send fails
|
||
|
||
Revision 1.14 1992/02/09 05:21:55 ian
|
||
Bob Denny: call fmail_transfer before fsysdep_did_work
|
||
|
||
Revision 1.13 1992/02/08 19:41:24 ian
|
||
Simplify pffile calls for ancient stupid compilers
|
||
|
||
Revision 1.12 1992/02/08 03:54:18 ian
|
||
Include <string.h> only in <uucp.h>, added 1992 copyright
|
||
|
||
Revision 1.11 1992/01/18 22:48:53 ian
|
||
Reworked sending of mail and general handling of failed transfers
|
||
|
||
Revision 1.10 1992/01/16 18:16:58 ian
|
||
Niels Baggesen: add some debugging messages
|
||
|
||
Revision 1.9 1991/12/31 19:34:19 ian
|
||
Added number of bytes to pffile protocol entry point
|
||
|
||
Revision 1.8 1991/12/30 04:28:30 ian
|
||
John Theus: check for EOF to work around bug in fread
|
||
|
||
Revision 1.7 1991/12/21 23:10:43 ian
|
||
Terry Gardner: record failed file transfers in statistics file
|
||
|
||
Revision 1.6 1991/12/13 04:33:38 ian
|
||
Franc,ois Pinard: don't bother to warn if the final HY doesn't come in
|
||
|
||
Revision 1.5 1991/11/15 21:00:59 ian
|
||
Efficiency hacks for 'f' and 't' protocols
|
||
|
||
Revision 1.4 1991/11/11 19:32:03 ian
|
||
Added breceive_char to read characters through protocol buffering
|
||
|
||
Revision 1.3 1991/11/11 04:21:16 ian
|
||
Added 'f' protocol
|
||
|
||
Revision 1.2 1991/11/10 19:24:22 ian
|
||
Added pffile protocol entry point for file level control
|
||
|
||
Revision 1.1 1991/11/09 18:51:50 ian
|
||
Initial revision
|
||
|
||
*/
|
||
|
||
#include "uucp.h"
|
||
|
||
#if USE_RCS_ID
|
||
char prot_rcsid[] = "$Id: prot.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $";
|
||
#endif
|
||
|
||
#include <errno.h>
|
||
|
||
#include "system.h"
|
||
#include "port.h"
|
||
#include "prot.h"
|
||
|
||
/* This file implements the generic UUCP protocol for making and
|
||
confirming file transfer requests. This involves sending ASCII
|
||
strings back and forth between the communicating daemons. It would
|
||
be possible to use a different scheme when designing a new
|
||
protocol, but this scheme is used by all traditional UUCP
|
||
protocols. */
|
||
|
||
/* Local functions. */
|
||
|
||
static boolean fpsendfile_confirm P((void));
|
||
static boolean fprecfile_confirm P((void));
|
||
static boolean fploop P((void));
|
||
static void upadd_cmd P((const char *z, int clen, boolean flast));
|
||
|
||
/* Variables visible to the protocol-specific routines. */
|
||
|
||
/* Protocol structure. */
|
||
const struct sprotocol *qProto;
|
||
|
||
/* Buffer to hold received data. */
|
||
char abPrecbuf[CRECBUFLEN];
|
||
|
||
/* Index of start of data in abPrecbuf. */
|
||
int iPrecstart;
|
||
|
||
/* Index of end of data (first byte not included in data) in abPrecbuf. */
|
||
int iPrecend;
|
||
|
||
/* Whether an unexpected shutdown is OK now; this is used to avoid
|
||
giving a warning for systems that hang up in a hurry. */
|
||
boolean fPerror_ok;
|
||
|
||
/* Amount of data sent for current send file; -1 means there is no
|
||
current send file. */
|
||
static long cPsent_bytes = -1;
|
||
|
||
/* Amount of data received for current receive file; -1 means there is
|
||
no current receive file. */
|
||
static long cPreceived_bytes = -1;
|
||
|
||
/* Send a file. If we are the master, we must send a command to
|
||
transfer the file and wait for a confirmation that we can begin
|
||
sending the file. If we are the slave, the master has sent us a
|
||
command and is waiting for a reply; we must confirm that we will
|
||
send the file. Either way, we begin transferring data.
|
||
|
||
This function returns FALSE if there is a communication failure.
|
||
It returns TRUE otherwise, even if the file transfer failed. */
|
||
|
||
boolean
|
||
fsend_file (fmaster, e, qcmd, zmail, ztosys, fnew)
|
||
boolean fmaster;
|
||
openfile_t e;
|
||
const struct scmd *qcmd;
|
||
const char *zmail;
|
||
const char *ztosys;
|
||
boolean fnew;
|
||
{
|
||
if (fmaster)
|
||
{
|
||
int clen;
|
||
char *zsend;
|
||
const char *zrec;
|
||
|
||
/* 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. The remote
|
||
system will ignore ztemp, but it is supposed to be sent anyhow.
|
||
If fnew is TRUE, we also send the size; in this case if ztemp
|
||
is empty we must send it as "". */
|
||
clen = (strlen (qcmd->zfrom) + strlen (qcmd->zto)
|
||
+ strlen (qcmd->zuser) + strlen (qcmd->zoptions)
|
||
+ strlen (qcmd->ztemp) + strlen (qcmd->znotify)
|
||
+ 50);
|
||
zsend = (char *) alloca (clen);
|
||
if (! fnew)
|
||
sprintf (zsend, "S %s %s %s -%s %s 0%o %s", qcmd->zfrom, qcmd->zto,
|
||
qcmd->zuser, qcmd->zoptions, qcmd->ztemp, qcmd->imode,
|
||
qcmd->znotify);
|
||
else
|
||
{
|
||
const char *znotify;
|
||
|
||
if (qcmd->znotify[0] != '\0')
|
||
znotify = qcmd->znotify;
|
||
else
|
||
znotify = "\"\"";
|
||
sprintf (zsend, "S %s %s %s -%s %s 0%o %s %ld", qcmd->zfrom,
|
||
qcmd->zto, qcmd->zuser, qcmd->zoptions, qcmd->ztemp,
|
||
qcmd->imode, znotify, qcmd->cbytes);
|
||
}
|
||
|
||
if (! (qProto->pfsendcmd) (zsend))
|
||
{
|
||
(void) ffileclose (e);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Now we must await a reply. */
|
||
|
||
zrec = zgetcmd ();
|
||
if (zrec == NULL)
|
||
{
|
||
(void) ffileclose (e);
|
||
return FALSE;
|
||
}
|
||
|
||
if (zrec[0] != 'S'
|
||
|| (zrec[1] != 'Y' && zrec[1] != 'N'))
|
||
{
|
||
ulog (LOG_ERROR, "Bad response to send request");
|
||
(void) ffileclose (e);
|
||
return FALSE;
|
||
}
|
||
|
||
if (zrec[1] == 'N')
|
||
{
|
||
const char *zerr;
|
||
boolean fnever;
|
||
|
||
fnever = TRUE;
|
||
if (zrec[2] == '2')
|
||
zerr = "permission denied";
|
||
else if (zrec[2] == '4')
|
||
{
|
||
zerr = "remote cannot create work files";
|
||
fnever = FALSE;
|
||
}
|
||
else if (zrec[2] == '6')
|
||
{
|
||
zerr = "too large for receiver now";
|
||
fnever = FALSE;
|
||
}
|
||
else if (zrec[2] == '7')
|
||
{
|
||
/* The file is too large to ever send. */
|
||
zerr = "too large for receiver";
|
||
}
|
||
else
|
||
{
|
||
char *zset;
|
||
|
||
zset = (char *) alloca (sizeof "unknown reason: "
|
||
+ strlen (zrec));
|
||
sprintf (zset, "unknown reason: %s", zrec);
|
||
zerr = zset;
|
||
}
|
||
|
||
ulog (LOG_ERROR, "Can't send %s: %s", qcmd->zfrom, zerr);
|
||
(void) ffileclose (e);
|
||
if (fnever)
|
||
{
|
||
(void) fmail_transfer (FALSE, qcmd->zuser, zmail, zerr,
|
||
qcmd->zfrom, zLocalname,
|
||
qcmd->zto, ztosys,
|
||
zsysdep_save_temp_file (qcmd->pseq));
|
||
(void) fsysdep_did_work (qcmd->pseq);
|
||
}
|
||
return TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
char absend[20];
|
||
|
||
/* We are the slave; confirm that we will send the file. We
|
||
send the file mode in the confirmation string. */
|
||
|
||
sprintf (absend, "RY 0%o", qcmd->imode);
|
||
|
||
if (! (qProto->pfsendcmd) (absend))
|
||
{
|
||
(void) ffileclose (e);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
/* Record the file we are sending, and let the protocol take over. */
|
||
|
||
if (! fstore_sendfile (e, qcmd->pseq, qcmd->zfrom, qcmd->zto, ztosys,
|
||
qcmd->zuser, zmail))
|
||
return FALSE;
|
||
|
||
cPsent_bytes = 0;
|
||
|
||
/* Tell the protocol that we are starting to send a file. */
|
||
if (qProto->pffile != NULL)
|
||
{
|
||
boolean (*pffile) P((boolean, boolean, boolean *, long));
|
||
|
||
/* Simplify expression for ancient compilers. */
|
||
pffile = qProto->pffile;
|
||
if (! pffile (TRUE, TRUE, (boolean *) NULL, qcmd->cbytes))
|
||
return FALSE;
|
||
}
|
||
|
||
return fploop ();
|
||
}
|
||
|
||
/* Confirm that a file has been received correctly by the other side.
|
||
Return FALSE for a communication error. We expect the receiving
|
||
system to send back CY; if an error occurred while moving the
|
||
received file into its final location, the receiving system will
|
||
send back CN5. */
|
||
|
||
static boolean
|
||
fpsendfile_confirm ()
|
||
{
|
||
const char *zrec;
|
||
long cbytes;
|
||
const char *zerr;
|
||
|
||
zrec = zgetcmd ();
|
||
if (zrec == NULL)
|
||
return FALSE;
|
||
|
||
cbytes = cPsent_bytes;
|
||
cPsent_bytes = -1;
|
||
|
||
if (zrec[0] != 'C'
|
||
|| (zrec[1] != 'Y' && zrec[1] != 'N'))
|
||
{
|
||
zerr = "Bad confirmation for sent file";
|
||
ulog (LOG_ERROR, zerr);
|
||
(void) fsent_file (FALSE, cbytes, zerr, FALSE);
|
||
}
|
||
else if (zrec[1] == 'N')
|
||
{
|
||
if (zrec[2] == '5')
|
||
zerr = "File could not be stored in final location";
|
||
else
|
||
{
|
||
char *zset;
|
||
|
||
zset = (char *) alloca (sizeof "File send failed: " + strlen (zrec));
|
||
sprintf (zset, "File send failed: %s", zrec);
|
||
zerr = zset;
|
||
}
|
||
ulog (LOG_ERROR, zerr);
|
||
(void) fsent_file (FALSE, cbytes, zerr, TRUE);
|
||
}
|
||
else
|
||
(void) fsent_file (TRUE, cbytes, (const char *) NULL, FALSE);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Receive a file. If we are the master, we must set up a file
|
||
request and wait for the other side to confirm it. If we are the
|
||
slave, we must confirm a request made by the other side. We then
|
||
start receiving the file.
|
||
|
||
This function must return FALSE if there is a communication error
|
||
and TRUE otherwise. We return TRUE even if the file transfer
|
||
fails. */
|
||
|
||
boolean
|
||
freceive_file (fmaster, e, qcmd, zmail, zfromsys, fnew)
|
||
boolean fmaster;
|
||
openfile_t e;
|
||
const struct scmd *qcmd;
|
||
const char *zmail;
|
||
const char *zfromsys;
|
||
boolean fnew;
|
||
{
|
||
unsigned int imode;
|
||
|
||
if (fmaster)
|
||
{
|
||
int clen;
|
||
char *zsend;
|
||
const char *zrec;
|
||
|
||
/* 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 (qcmd->zfrom) + strlen (qcmd->zto)
|
||
+ strlen (qcmd->zuser) + strlen (qcmd->zoptions) + 30);
|
||
zsend = (char *) alloca (clen);
|
||
|
||
if (! fnew)
|
||
sprintf (zsend, "R %s %s %s -%s", qcmd->zfrom, qcmd->zto,
|
||
qcmd->zuser, qcmd->zoptions);
|
||
else
|
||
sprintf (zsend, "R %s %s %s -%s %ld", qcmd->zfrom, qcmd->zto,
|
||
qcmd->zuser, qcmd->zoptions, qcmd->cbytes);
|
||
|
||
if (! (qProto->pfsendcmd) (zsend))
|
||
{
|
||
(void) ffileclose (e);
|
||
(void) remove (qcmd->ztemp);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Wait for a reply. */
|
||
|
||
zrec = zgetcmd ();
|
||
if (zrec == NULL)
|
||
{
|
||
(void) ffileclose (e);
|
||
(void) remove (qcmd->ztemp);
|
||
return FALSE;
|
||
}
|
||
|
||
if (zrec[0] != 'R'
|
||
|| (zrec[1] != 'Y' && zrec[1] != 'N'))
|
||
{
|
||
ulog (LOG_ERROR, "Bad response to receive request");
|
||
(void) ffileclose (e);
|
||
(void) remove (qcmd->ztemp);
|
||
return FALSE;
|
||
}
|
||
|
||
if (zrec[1] == 'N')
|
||
{
|
||
const char *zerr;
|
||
|
||
if (zrec[2] == '2')
|
||
zerr = "no such file";
|
||
else if (zrec[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. */
|
||
ulog (LOG_ERROR, "Can't receive %s: too large",
|
||
qcmd->zfrom);
|
||
(void) ffileclose (e);
|
||
(void) remove (qcmd->ztemp);
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
char *zset;
|
||
|
||
zset = (char *) alloca (sizeof "unknown reason: "
|
||
+ strlen (zrec));
|
||
sprintf (zset, "unknown reason: %s", zrec);
|
||
zerr = zset;
|
||
}
|
||
ulog (LOG_ERROR, "Can't receive %s: %s", qcmd->zfrom, zerr);
|
||
(void) ffileclose (e);
|
||
(void) remove (qcmd->ztemp);
|
||
(void) fmail_transfer (FALSE, qcmd->zuser, zmail, zerr,
|
||
qcmd->zfrom, zfromsys,
|
||
qcmd->zto, zLocalname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (qcmd->pseq);
|
||
return TRUE;
|
||
}
|
||
|
||
/* The mode should have been sent as "RY 0%o". If it wasn't,
|
||
we use 0666. */
|
||
imode = (unsigned int) strtol (zrec + 2, (char **) NULL, 8);
|
||
if (imode == 0)
|
||
imode = 0666;
|
||
}
|
||
else
|
||
{
|
||
/* Tell the other system to go ahead and send. */
|
||
|
||
if (! (qProto->pfsendcmd) ("SY"))
|
||
{
|
||
(void) ffileclose (e);
|
||
(void) remove (qcmd->ztemp);
|
||
return FALSE;
|
||
}
|
||
imode = qcmd->imode;
|
||
}
|
||
|
||
if (! fstore_recfile (e, qcmd->pseq, qcmd->zfrom, qcmd->zto, zfromsys,
|
||
qcmd->zuser, imode, zmail, qcmd->ztemp))
|
||
return FALSE;
|
||
|
||
cPreceived_bytes = 0;
|
||
|
||
/* Tell the protocol that we are starting to receive a file. */
|
||
if (qProto->pffile != NULL)
|
||
{
|
||
boolean (*pffile) P((boolean, boolean, boolean *, long));
|
||
|
||
/* Simplify expression for ancient compilers. */
|
||
pffile = qProto->pffile;
|
||
if (! pffile (TRUE, FALSE, (boolean *) NULL, (long) -1))
|
||
return FALSE;
|
||
}
|
||
|
||
return fploop ();
|
||
}
|
||
|
||
/* Confirm that a file was received correctly. */
|
||
|
||
static boolean
|
||
fprecfile_confirm ()
|
||
{
|
||
long cbytes;
|
||
|
||
cbytes = cPreceived_bytes;
|
||
cPreceived_bytes = -1;
|
||
|
||
if (freceived_file (TRUE, cbytes, (const char *) NULL, FALSE))
|
||
return (qProto->pfsendcmd) ("CY");
|
||
else
|
||
return (qProto->pfsendcmd) ("CN5");
|
||
}
|
||
|
||
/* Send a transfer request. This is only called by the master. It
|
||
ignored the pseq entry in the scmd structure. */
|
||
|
||
boolean
|
||
fxcmd (qcmd, pfnever)
|
||
const struct scmd *qcmd;
|
||
boolean *pfnever;
|
||
{
|
||
int clen;
|
||
char *zsend;
|
||
const char *zrec;
|
||
|
||
*pfnever = FALSE;
|
||
|
||
/* We send the string
|
||
X from to user options
|
||
We put a dash in front of options. */
|
||
|
||
clen = (strlen (qcmd->zfrom) + strlen (qcmd->zto)
|
||
+ strlen (qcmd->zuser) + strlen (qcmd->zoptions) + 7);
|
||
zsend = (char *) alloca (clen);
|
||
|
||
sprintf (zsend, "X %s %s %s -%s", qcmd->zfrom, qcmd->zto,
|
||
qcmd->zuser, qcmd->zoptions);
|
||
|
||
if (! (qProto->pfsendcmd) (zsend))
|
||
return FALSE;
|
||
|
||
/* Wait for a reply. */
|
||
|
||
zrec = zgetcmd ();
|
||
if (zrec == NULL)
|
||
return FALSE;
|
||
|
||
if (zrec[0] != 'X'
|
||
|| (zrec[1] != 'Y' && zrec[1] != 'N'))
|
||
{
|
||
ulog (LOG_ERROR, "Bad response to wildcard request");
|
||
return FALSE;
|
||
}
|
||
|
||
if (zrec[1] == 'N')
|
||
{
|
||
ulog (LOG_ERROR, "Work request denied");
|
||
*pfnever = TRUE;
|
||
return TRUE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Confirm a transfer request. */
|
||
|
||
boolean
|
||
fxcmd_confirm ()
|
||
{
|
||
return (qProto->pfsendcmd) ("XY");
|
||
}
|
||
|
||
/* Signal a file transfer failure to the other side. This is only called
|
||
by the slave. */
|
||
|
||
boolean
|
||
ftransfer_fail (bcmd, twhy)
|
||
int bcmd;
|
||
enum tfailure twhy;
|
||
{
|
||
const char *z;
|
||
|
||
switch (bcmd)
|
||
{
|
||
case 'S':
|
||
switch (twhy)
|
||
{
|
||
case FAILURE_PERM:
|
||
z = "SN2";
|
||
break;
|
||
case FAILURE_OPEN:
|
||
z = "SN4";
|
||
break;
|
||
case FAILURE_SIZE:
|
||
z = "SN6";
|
||
break;
|
||
default:
|
||
z = "SN";
|
||
break;
|
||
}
|
||
break;
|
||
case 'R':
|
||
switch (twhy)
|
||
{
|
||
case FAILURE_PERM:
|
||
case FAILURE_OPEN:
|
||
z = "RN2";
|
||
break;
|
||
case FAILURE_SIZE:
|
||
z = "RN6";
|
||
break;
|
||
default:
|
||
z = "RN";
|
||
break;
|
||
}
|
||
break;
|
||
case 'X':
|
||
z = "XN";
|
||
break;
|
||
default:
|
||
#if DEBUG > 0
|
||
ulog (LOG_ERROR, "ftransfer_fail: Can't happen");
|
||
#endif
|
||
return FALSE;
|
||
}
|
||
|
||
return (qProto->pfsendcmd) (z);
|
||
}
|
||
|
||
/* Get and parse a command from the other system. Handle hangups
|
||
specially. */
|
||
|
||
boolean
|
||
fgetcmd (fmaster, qcmd)
|
||
boolean fmaster;
|
||
struct scmd *qcmd;
|
||
{
|
||
static char *z;
|
||
static int c;
|
||
|
||
while (TRUE)
|
||
{
|
||
const char *zcmd;
|
||
int clen;
|
||
|
||
zcmd = zgetcmd ();
|
||
if (zcmd == NULL)
|
||
return FALSE;
|
||
|
||
clen = strlen (zcmd);
|
||
if (clen + 1 > c)
|
||
{
|
||
c = clen + 1;
|
||
z = (char *) xrealloc ((pointer) z, c);
|
||
}
|
||
strcpy (z, zcmd);
|
||
|
||
if (! fparse_cmd (z, qcmd))
|
||
continue;
|
||
|
||
/* Handle hangup commands specially. If it's just 'H', return
|
||
it. If it's 'N', the other side is denying a hangup request
|
||
which we can just ignore (since the top level code assumes
|
||
that hangup requests are denied). If it's 'Y', the other
|
||
side is confirming a hangup request. In this case we confirm
|
||
with an "HY", wait for yet another "HY" from the other side,
|
||
and then finally shut down the protocol (I don't know why it
|
||
works this way, but it does). We then return a 'Y' command
|
||
to the top level code. */
|
||
|
||
if (qcmd->bcmd == 'N')
|
||
{
|
||
#if DEBUG > 0
|
||
if (fmaster)
|
||
ulog (LOG_ERROR, "Got hangup reply as master");
|
||
#endif
|
||
continue;
|
||
}
|
||
|
||
if (qcmd->bcmd == 'Y')
|
||
{
|
||
#if DEBUG > 0
|
||
if (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. */
|
||
|
||
if (! (qProto->pfsendcmd) ("HY"))
|
||
return TRUE;
|
||
fPerror_ok = TRUE;
|
||
zcmd = zgetcmd ();
|
||
fPerror_ok = FALSE;
|
||
if (zcmd == NULL)
|
||
return TRUE;
|
||
#if DEBUG > 1
|
||
if (strcmp (zcmd, "HY") != 0)
|
||
DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO | DEBUG_ABNORMAL,
|
||
"fgetcmd: Got \"%s\" when expecting \"HY\"",
|
||
zcmd);
|
||
#endif
|
||
(void) (qProto->pfshutdown) ();
|
||
return TRUE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/*NOTREACHED*/
|
||
}
|
||
|
||
/* Hangup. */
|
||
|
||
boolean
|
||
fhangup_request ()
|
||
{
|
||
return (qProto->pfsendcmd) ("H");
|
||
}
|
||
|
||
/* Reply to a hangup request. This is only called by the slave. If
|
||
fconfirm is TRUE, we are closing down the protocol. We send an HY
|
||
message. The master responds with an HY message. We send another
|
||
HY message, and then shut down the protocol. */
|
||
|
||
boolean
|
||
fhangup_reply (fconfirm)
|
||
boolean fconfirm;
|
||
{
|
||
if (! fconfirm)
|
||
return (qProto->pfsendcmd) ("HN");
|
||
else
|
||
{
|
||
const char *z;
|
||
|
||
if (! (qProto->pfsendcmd) ("HY"))
|
||
return FALSE;
|
||
|
||
z = zgetcmd ();
|
||
if (z == NULL)
|
||
return FALSE;
|
||
if (strcmp (z, "HY") != 0)
|
||
ulog (LOG_ERROR, "Got \"%s\" when expecting \"HY\"", z);
|
||
else
|
||
{
|
||
if (! (qProto->pfsendcmd) ("HY"))
|
||
return FALSE;
|
||
}
|
||
|
||
return (qProto->pfshutdown) ();
|
||
}
|
||
}
|
||
|
||
/* Loop sending and/or receiving data. If there is a file to send,
|
||
this will send it until the entire file has been sent or a command
|
||
has been received from the remote system or a complete file has
|
||
been received from the remote system. Otherwise this will simply
|
||
call the protocol to wait until a complete file or command has been
|
||
received from the remote system. */
|
||
|
||
static boolean
|
||
fploop ()
|
||
{
|
||
boolean fexit;
|
||
|
||
DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "fploop: Main protocol loop");
|
||
|
||
if (ffileisopen (eSendfile))
|
||
{
|
||
int iend;
|
||
|
||
iend = iPrecend;
|
||
|
||
while (TRUE)
|
||
{
|
||
/* We keep sending out packets until we have something
|
||
in the receive buffer. */
|
||
while (iend == iPrecend)
|
||
{
|
||
char *zdata;
|
||
int cdata;
|
||
|
||
/* Get a packet and fill it with data. */
|
||
|
||
zdata = (qProto->pzgetspace) (&cdata);
|
||
if (zdata == NULL)
|
||
return FALSE;
|
||
|
||
if (ffileeof (eSendfile))
|
||
cdata = 0;
|
||
else
|
||
{
|
||
cdata = cfileread (eSendfile, zdata, cdata);
|
||
if (ffilereaderror (eSendfile, cdata))
|
||
{
|
||
/* The protocol gives us no way to report a file
|
||
sending error, so we just drop the connection.
|
||
What else can we do? */
|
||
ulog (LOG_ERROR, "read: %s", strerror (errno));
|
||
usendfile_error ();
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
if (! (qProto->pfsenddata) (zdata, cdata))
|
||
return FALSE;
|
||
|
||
cPsent_bytes += cdata;
|
||
|
||
/* If we have reached the end of the file, tell the
|
||
protocol that the file is finished (the protocol
|
||
could also detect this by looking for zero passed as
|
||
the data length to the send data routine, but would
|
||
have no convenient way to tell us to redo the file
|
||
send). If we are not supposed to redo the file
|
||
transfer, wait for confirmation and return out to get
|
||
the next file. */
|
||
|
||
if (cdata == 0)
|
||
{
|
||
if (qProto->pffile != NULL)
|
||
{
|
||
boolean fredo;
|
||
boolean (*pffile) P((boolean, boolean, boolean *,
|
||
long));
|
||
|
||
/* Simplify expression for ancient compilers. */
|
||
pffile = qProto->pffile;
|
||
if (! pffile (FALSE, TRUE, &fredo, (long) -1))
|
||
return FALSE;
|
||
|
||
if (fredo)
|
||
{
|
||
ulog (LOG_NORMAL, "Resending file");
|
||
if (! ffilerewind (eSendfile))
|
||
{
|
||
ulog (LOG_ERROR, "rewind: %s",
|
||
strerror (errno));
|
||
usendfile_error ();
|
||
return FALSE;
|
||
}
|
||
continue;
|
||
}
|
||
}
|
||
|
||
return fpsendfile_confirm ();
|
||
}
|
||
}
|
||
|
||
/* Process the data in the receive buffer, and decide
|
||
whether it's time to get out. */
|
||
if (! (qProto->pfprocess) (&fexit))
|
||
return FALSE;
|
||
if (fexit)
|
||
return TRUE;
|
||
|
||
iend = iPrecend;
|
||
}
|
||
}
|
||
|
||
#if DEBUG > 0
|
||
/* If there is no file to send, there really should be a file to
|
||
receive. */
|
||
|
||
if (! ffileisopen(eRecfile))
|
||
ulog (LOG_FATAL, "fploop: No send or receive file");
|
||
#endif
|
||
|
||
/* We have no file to send. Wait for data to come in. */
|
||
|
||
return (qProto->pfwait) ();
|
||
}
|
||
|
||
/* This function is called by the protocol routines when data has
|
||
arrived. Some protocols may know whether the data is for a command
|
||
or a file; for others, if a receive file is open it is for the file
|
||
and is otherwise for a command. This function will set *pfexit to
|
||
TRUE if it has received a complete file (assumed to be true if
|
||
cdata is zero) or a complete command (assumed to be true if the
|
||
argument data contains a null byte). It will return FALSE on
|
||
error. */
|
||
|
||
boolean
|
||
fgot_data (zdata, cdata, fcmd, ffile, pfexit)
|
||
const char *zdata;
|
||
int cdata;
|
||
boolean fcmd;
|
||
boolean ffile;
|
||
boolean *pfexit;
|
||
{
|
||
*pfexit = FALSE;
|
||
|
||
if (! fcmd && ! ffile)
|
||
{
|
||
if (ffileisopen (eRecfile))
|
||
ffile = TRUE;
|
||
else
|
||
fcmd = TRUE;
|
||
}
|
||
|
||
#if DEBUG > 0
|
||
if (ffile && ! ffileisopen (eRecfile))
|
||
ulog (LOG_FATAL, "fgot_data: No file to receive into");
|
||
#endif
|
||
|
||
if (ffile)
|
||
{
|
||
if (cdata == 0)
|
||
{
|
||
/* The file transfer is complete. If the protocol has a
|
||
file level routine, call it to see whether we have to
|
||
receive the file again. */
|
||
if (qProto->pffile != NULL)
|
||
{
|
||
boolean fredo;
|
||
boolean (*pffile) P((boolean, boolean, boolean *, long));
|
||
|
||
/* Simplify expression for ancient compilers. */
|
||
pffile = qProto->pffile;
|
||
if (! pffile (FALSE, FALSE, &fredo, (long) -1))
|
||
return FALSE;
|
||
|
||
if (fredo)
|
||
{
|
||
ulog (LOG_NORMAL, "File being resent");
|
||
if (! frecfile_rewind ())
|
||
return FALSE;
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
if (! fprecfile_confirm ())
|
||
return FALSE;
|
||
*pfexit = TRUE;
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
int cwrote;
|
||
|
||
/* Cast zdata to avoid warnings because of erroneous
|
||
prototypes on Ultrix. */
|
||
cwrote = cfilewrite (eRecfile, (char *) zdata, cdata);
|
||
if (cwrote != cdata)
|
||
{
|
||
const char *zerr;
|
||
|
||
if (cwrote < 0)
|
||
zerr = strerror (errno);
|
||
else
|
||
zerr = "could not write all data";
|
||
ulog (LOG_ERROR, "write: %s", zerr);
|
||
urecfile_error ();
|
||
|
||
/* 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. */
|
||
return FALSE;
|
||
}
|
||
|
||
cPreceived_bytes += cdata;
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
const char *z;
|
||
|
||
/* We want to add this data to the current command string. 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. */
|
||
|
||
z = (const char *) memchr ((constpointer) zdata, '\0', cdata);
|
||
if (z == NULL)
|
||
upadd_cmd (zdata, cdata, FALSE);
|
||
else
|
||
{
|
||
upadd_cmd (zdata, z - zdata, TRUE);
|
||
*pfexit = TRUE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
/* This function is called by fgot_data when a command string is
|
||
received. We must queue up received commands since we don't know
|
||
when we'll be able to get to them (for example, the
|
||
acknowledgements for the last few packets of a sent file may
|
||
contain the string indicating whether the file was received
|
||
correctly). */
|
||
|
||
struct spcmdqueue
|
||
{
|
||
struct spcmdqueue *qnext;
|
||
int csize;
|
||
int clen;
|
||
char *z;
|
||
};
|
||
|
||
static struct spcmdqueue *qPcmd_queue;
|
||
static struct spcmdqueue *qPcmd_free;
|
||
|
||
static void
|
||
upadd_cmd (z, clen, flast)
|
||
const char *z;
|
||
int clen;
|
||
boolean flast;
|
||
{
|
||
struct spcmdqueue *q;
|
||
|
||
q = qPcmd_free;
|
||
if (q == NULL)
|
||
{
|
||
q = (struct spcmdqueue *) xmalloc (sizeof (struct spcmdqueue));
|
||
q->qnext = NULL;
|
||
q->csize = 0;
|
||
q->clen = 0;
|
||
q->z = NULL;
|
||
qPcmd_free = q;
|
||
}
|
||
|
||
if (q->clen + clen + 1 > q->csize)
|
||
{
|
||
q->csize = q->clen + clen + 1;
|
||
q->z = (char *) xrealloc ((pointer) q->z, q->csize);
|
||
}
|
||
|
||
memcpy (q->z + q->clen, z, clen);
|
||
q->clen += clen;
|
||
q->z[q->clen] = '\0';
|
||
|
||
/* If the last string in this command, add it to the queue of
|
||
finished commands. */
|
||
|
||
if (flast)
|
||
{
|
||
struct spcmdqueue **pq;
|
||
|
||
for (pq = &qPcmd_queue; *pq != NULL; pq = &(*pq)->qnext)
|
||
;
|
||
*pq = q;
|
||
qPcmd_free = q->qnext;
|
||
q->qnext = NULL;
|
||
}
|
||
}
|
||
|
||
/* Get a command string. We just have to wait until the receive
|
||
packet function gives us something in qPcmd_queue. The return
|
||
value of this may be treated as a static buffer; it will last
|
||
at least until the next packet is received. */
|
||
|
||
const char *
|
||
zgetcmd ()
|
||
{
|
||
struct spcmdqueue *q;
|
||
|
||
/* Wait until a command comes in. */
|
||
while (qPcmd_queue == NULL)
|
||
{
|
||
DEBUG_MESSAGE0 (DEBUG_UUCP_PROTO, "zgetcmd: Waiting for packet");
|
||
|
||
if (! (qProto->pfwait) ())
|
||
return NULL;
|
||
}
|
||
|
||
q = qPcmd_queue;
|
||
qPcmd_queue = q->qnext;
|
||
|
||
q->clen = 0;
|
||
|
||
/* We must not replace qPcmd_free, because it may already be
|
||
receiving a new command string. */
|
||
if (qPcmd_free == NULL)
|
||
{
|
||
q->qnext = NULL;
|
||
qPcmd_free = q;
|
||
}
|
||
else
|
||
{
|
||
q->qnext = qPcmd_free->qnext;
|
||
qPcmd_free->qnext = q;
|
||
}
|
||
|
||
DEBUG_MESSAGE1 (DEBUG_UUCP_PROTO, "zgetcmd: Got command \"%s\"", q->z);
|
||
|
||
return q->z;
|
||
}
|
||
|
||
/* We want to output and input at the same time, if supported on this
|
||
machine. If we have something to send, we send it all while
|
||
accepting a large amount of data. Once we have sent everything we
|
||
look at whatever we have received. If data comes in faster than we
|
||
can send it, we may run out of buffer space. */
|
||
|
||
boolean
|
||
fsend_data (zsend, csend, fdoread)
|
||
const char *zsend;
|
||
int csend;
|
||
boolean fdoread;
|
||
{
|
||
if (! fdoread)
|
||
return fport_write (zsend, csend);
|
||
|
||
while (csend > 0)
|
||
{
|
||
char *zrec;
|
||
int crec, csent;
|
||
|
||
if (iPrecend < iPrecstart)
|
||
{
|
||
zrec = abPrecbuf + iPrecend;
|
||
crec = iPrecstart - iPrecend - 1;
|
||
}
|
||
else if (iPrecend < CRECBUFLEN)
|
||
{
|
||
zrec = abPrecbuf + iPrecend;
|
||
crec = CRECBUFLEN - iPrecend;
|
||
}
|
||
else
|
||
{
|
||
zrec = abPrecbuf;
|
||
crec = iPrecstart - 1;
|
||
}
|
||
|
||
csent = csend;
|
||
|
||
if (! fport_io (zsend, &csent, zrec, &crec))
|
||
return FALSE;
|
||
|
||
csend -= csent;
|
||
zsend += csent;
|
||
|
||
iPrecend = (iPrecend + crec) % CRECBUFLEN;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Read data from the other system when we have nothing to send. The
|
||
argument cneed is the amount of data the caller wants, and ctimeout
|
||
is the timeout in seconds. The function sets *pcrec to the amount
|
||
of data which was actually received, which may be less than cneed
|
||
if there isn't enough room in the receive buffer. If no data is
|
||
received before the timeout expires, *pcrec will be returned as 0.
|
||
If an error occurs, the function returns FALSE. If the freport
|
||
argument is FALSE, no error should be reported. */
|
||
|
||
boolean
|
||
freceive_data (cneed, pcrec, ctimeout, freport)
|
||
int cneed;
|
||
int *pcrec;
|
||
int ctimeout;
|
||
boolean freport;
|
||
{
|
||
/* Set *pcrec to the maximum amount of data we can read. fport_read
|
||
expects *pcrec to be the buffer size, and sets it to the amount
|
||
actually received. */
|
||
if (iPrecend < iPrecstart)
|
||
*pcrec = iPrecstart - iPrecend - 1;
|
||
else
|
||
{
|
||
*pcrec = CRECBUFLEN - iPrecend;
|
||
if (iPrecstart == 0)
|
||
--(*pcrec);
|
||
}
|
||
|
||
#if DEBUG > 0
|
||
/* If we have no room in the buffer, we're in trouble. The
|
||
protocols must be written to ensure that this can't happen. */
|
||
if (*pcrec == 0)
|
||
ulog (LOG_FATAL, "freceive_data: No room in buffer");
|
||
#endif
|
||
|
||
/* If we don't have room for all the data the caller wants, we
|
||
simply have to expect less. We'll get the rest later. */
|
||
if (*pcrec < cneed)
|
||
cneed = *pcrec;
|
||
|
||
if (! fport_read (abPrecbuf + iPrecend, pcrec, cneed, ctimeout, freport))
|
||
return FALSE;
|
||
|
||
iPrecend = (iPrecend + *pcrec) % CRECBUFLEN;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Read a single character. Get it out of the receive buffer if it's
|
||
there, otherwise ask freceive_data for at least one character.
|
||
This is used because as a protocol is shutting down freceive_data
|
||
may read ahead and eat characters that should be read outside the
|
||
protocol routines. We call freceive_data rather than fport_read
|
||
with an argument of 1 so that we can get all the available data in
|
||
a single system call. The ctimeout argument is the timeout in
|
||
seconds; the freport argument is FALSE if no error should be
|
||
reported. This returns a character, or -1 on timeout or -2 on
|
||
error. */
|
||
|
||
int
|
||
breceive_char (ctimeout, freport)
|
||
int ctimeout;
|
||
boolean freport;
|
||
{
|
||
char b;
|
||
|
||
if (iPrecstart == iPrecend)
|
||
{
|
||
int crec;
|
||
|
||
if (! freceive_data (1, &crec, ctimeout, freport))
|
||
return -2;
|
||
if (crec == 0)
|
||
return -1;
|
||
}
|
||
|
||
b = abPrecbuf[iPrecstart];
|
||
iPrecstart = (iPrecstart + 1) % CRECBUFLEN;
|
||
return BUCHAR (b);
|
||
}
|
||
|
||
/* This routine is called when an error occurred and we are crashing
|
||
out of the connection. It is only used to report statistics on
|
||
failed transfers to the statistics file. 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
|
||
ustats_failed ()
|
||
{
|
||
long cbytes;
|
||
|
||
if (cPsent_bytes != -1)
|
||
{
|
||
cbytes = cPsent_bytes;
|
||
cPsent_bytes = -1;
|
||
(void) fsent_file (FALSE, cbytes, "connection failure", FALSE);
|
||
}
|
||
|
||
if (cPreceived_bytes != -1)
|
||
{
|
||
cbytes = cPreceived_bytes;
|
||
cPreceived_bytes = -1;
|
||
(void) freceived_file (FALSE, cbytes, "connection failure", FALSE);
|
||
}
|
||
}
|