home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
400-499
/
ff445.lzh
/
UUCP
/
uucico.c
< prev
Wrap
C/C++ Source or Header
|
1991-01-24
|
36KB
|
1,589 lines
/*
* UUCICO.C
*
* $Header: Beta:src/uucp/src/uucico/RCS/uucico.c,v 1.1 90/02/02 11:56:01 dillon Exp Locker: dillon $
*
* (C) Copyright 1987 by John Gilmore.
* Copying and use of this program are controlled by the terms of the Free
* Software Foundation's GNU Emacs General Public License.
*
* Derived from:
* i[$]uuslave.c 1.7 08/12/85 14:04:20
* which came from the ACGNJ BBS system at +1 201 753 9758. Original
* author unknown.
*
* Ported to Amiga by William Loftus
* Amiga Changes Copyright 1988 by William Loftus. All rights reserved.
* Additional Major Changes (c)Copyright 1989 by Matthew Dillon, All rights reserved
*
* 14-Oct-89, moved modem_init() to before poll_sys.
*
* -r option (-r1 does a call out to all systems we have mail for)
* -D[EVICE] dev sets serial device name (automatic from Getty)
* -U[NIT] unit sets unit name (automatic from Getty)
* -h0 Ignore CD (carrier detect)
* -7
*/
#include "includes.h" /* System include files, system dependent */
#include "uucp.h" /* Uucp definitions and parameters */
#include <log.h>
#include "version.h"
#define PROTOF_SHEREEQUALS 0x0001 /* ProtoHacks */
Prototype int getname(int);
Prototype int get_proto(void);
Prototype int instr(char *, int, int);
Prototype int twrite(const char *, int);
Prototype void xlat_str(char *);
Prototype int read_ctl(void);
Prototype int do_outbound(void);
Prototype int call_system(char *, int);
Prototype int call_sysline(char *);
Prototype int do_session(int);
Prototype int top_level(int);
Prototype int do_one_slave(void);
Prototype int do_one_master(void);
Prototype int yesno(char, int, int);
Prototype int host_send_file(char *);
Prototype int host_receive_file(char *);
Prototype int local_send_file(char *, int *);
Prototype int local_receive_file(void);
Prototype int receive_file(FILE *, char *, char *, char *, int);
Prototype int send_file(FILE *);
Prototype short ProtoHacks;
Prototype short PriMode;
Prototype short OldPri;
Prototype short IgnoreDTR;
Prototype int PacketTimeout;
#define MAX_FLAGS 40
extern int errno;
IDENT(".14");
static char *Copyright = COPYRIGHT;
char ttynam[NAMESIZE], /* Name of tty we use as serial port */
srcnam[NAMESIZE], /* Source file name */
dstnam[NAMESIZE], /* Dest file name */
who[NAMESIZE] = "-", /* Who sent the file */
flags[MAX_FLAGS], /* Flags from file xfer cmd */
temp[NAMESIZE]; /* Temp file name */
int ourpid = 0, /* Our process ID */
ignore_time_restrictions = 0, /* Call out even if L.sys sez no */
mode; /* File mode from file xfer cmd */
char host_name[MAX_HOST] = "AmigaUUCP"; /* Other guy's host name */
char our_name[MAX_HOST]; /* Our uucp hostname, set from usenet.ctl */
char path[128];
int debug = -1; /* -1 indicates not set by command line or ctl file */
int f_wait = 0; /* wait for a call (-w) or calls (-w -e) after outbnd */
int loop = 0; /* Loop accepting logins if tty name specified */
int Overide = 0; /* overide modem protocol */
int Getty = 0; /* -Getty initiated */
int IgnoreCD= 0; /* xgetc() should ignore carrier? */
int OurNameOv= 0;
int WindowSize = 999;
int NoReScan = 0; /* do not rescan for work after error */
int SevenWire= 0;
int XDebug = 0; /* do not pass debug parameter to remote */
int DebugHandshake = 0;
int PacketTimeout = 0;
short ProtoHacks; /* protocol hacks */
short PriMode;
short OldPri;
short IgnoreDTR = 0;
#define MAX_STRING 200 /* Max length string to send/expect */
#define MSGO2IDX 6
/* We print these prompts */
char msgo0[] = "login: ";
char msgo1[] = "Password:";
char msgo2[10+MAX_HOST] = { "\20Shere" }; /* NO = */
char msgo3[] = "\20ROK\0";
char msgo3a[]= "\20P";
char msgo3b[]= "\20Pg\0";
char msgo4[] = "\20OOOOOOO\0";
/* We expect to receive these strings */
char msgi0[] = "uucp\r";
char msgi1[] = "s8000\r";
/* char msgi2[] = "\20S*\0"; We now scan it specially FIXME */
char msgi3[] = "\20Ug\0";
char msgi4[] = "OOOOOO";
/*
* Protocol switch data structure
*/
#define turnon gturnon
#define rdmsg grdmsg
#define wrmsg gwrmsg
#define rddata grddata
#define wrdata gwrdata
#define turnoff gturnoff
int
getname(isshere)
int isshere;
{
int data, count = 0;
static char msgi[MAX_STRING+SLOP]; /* Incoming trash buffer */
/* Read data until null character */
while ((data = xgetc(BYTE_TO)) != EOF) {
data &= 0x7F;
if (data == 020)
break;
}
if (data == EOF)
return FAIL;
while ((data = xgetc(BYTE_TO)) != EOF && (data & 0x7F)) {
data &= 0x7F;
if (count == 0 && data != 'S')
continue;
if (count > sizeof(msgi) - 2)
continue;
if (data == 0x0A) /* hack fix for tuvie ? */
break;
msgi[count++] = (char)data;
}
msgi[count] = 0;
if (debug > 8)
printf("GETNAME MSG (%d): %s\n", count, msgi);
if (msgi[0] != 'S')
return FAIL;
if (isshere) {
for (count = 1; msgi[count] && msgi[count] != '='; ++count);
if (msgi[count] == '=')
++count;
} else {
count = 1;
}
if (msgi[count]) {
if (debug > 8)
printf("Compare host names: '%s' '%s'\n", host_name, msgi + count);
strcpy (host_name, msgi + count);
}
strtok(host_name, " \t"); /* put \0 after hostname */
if (debug > 8)
printf("Hostname is '%s'\n", host_name);
return SUCCESS;
}
/*
* get_proto() checks the list of protos given by the foriegn machine
* checking for 'g' (which is the only proto we have). Use only in master
* mode.
*/
int
get_proto()
{
int data;
while ((data = xgetc(BYTE_TO)) != EOF) {
data &= 0x7F;
if (data == 0)
break;
if (data == 'g')
return(SUCCESS);
}
return FAIL;
}
/*
* Medium level input routine.
*
* Look for an input string for the send-expect sequence.
* Return 0 for matching string, 1 for timeout before we found it.
* FIXME: we only time out if the other end stops sending. If it
* keeps sending, we keep listening forever.
*/
instr(s, n, to)
char *s;
int n;
int to; /* timeout */
{
int data,count,j;
int i;
static char msgi[512]; /* Incoming trash buffer */
count = 0;
if (to == 0)
to = BYTE_TO;
if (debug > 8) {
printf("Expecting ");
for (i = 0; i < n; i++)
printc(s[i]);
printf("\n");
}
if (DebugHandshake) {
printf("recvd: '");
fflush(stdout);
}
while ((data = xgetc(to)) != EOF) {
data &= 0x7F;
msgi[count++] = data;
if (DebugHandshake) {
if (data < 0x20)
printf("^%c", data + '@');
else
printf("%c", data);
fflush(stdout);
}
if (count == sizeof(msgi)) { /* throw away first half */
count = sizeof(msgi) / 2;
bcopy(msgi + sizeof(msgi) / 2, msgi, sizeof(msgi) / 2);
}
if (count >= n) {
for (i = n - 1, j = count - 1; i >= 0; i--, j--) {
if (*(s+i) != msgi[j])
break;
}
if (i < 0) {
if (debug > 8)
printf("\n");
if (DebugHandshake)
printf("' (GOTIT!)\n");
return(0);
}
}
}
if (DebugHandshake)
printf("' (TIMEOUT!)\n");
if (debug > 8)
printf("\n");
msgi[count] = (char)0;
return(1);
}
/*
* Debugging hack for stuff written to the modem.
*/
int
twrite(s, n)
const char *s;
int n;
{
int i;
if (debug > 8) {
printf("Wrote: ");
for (i = 0; i < n; i++)
printc(s[i]);
printf("\n");
}
return xwrite(s, n);
}
void
myexit()
{
long task = FindTask(NULL);
if (PriMode)
SetTaskPri(OldPri);
}
/*
* MAIN ROUTINE.
*
* This is called at program startup. It parses the arguments to the
* program (if any) and sets up to receive a call on the modem.
*
* If there are no arguments, we assume the caller is already on standard
* input, waiting to do uucp protocols (past the login prompt), and we
* just handle one caller.
*
* If there is an argument, it is the name of the tty device where we
* should listen for multiple callers and handle login and password.
*/
main(argc,argv)
int argc;
char *argv[];
{
int i;
char *poll_sys = (char *)NULL; /* System name to poll, or none */
short rmode = 0; /* 1 = master, 0 = slave */
LogProgram = "uucico";
LogHost = host_name;
LogWho = who;
signal(SIGINT, sigint);
atexit(myexit);
/* FIXME, use getopt */
/* scan command line arguments, kinda kludgy but it works */
for (i = 1; i < argc; ++i) {
char *ptr = argv[i];
if (*ptr != '-') {
printf("uucico: warning, extra args ignored: %s\n", argv[i]);
break;
}
ptr += 2;
switch (ptr[-1]) {
case 'N':
strcpy(our_name, ptr);
OurNameOv = 1;
break;
case 'D': /* Serial Device */
{
extern char *DeviceName;
DeviceName = argv[++i];
}
break;
case 'U': /* Serial Unit */
{
extern long DeviceUnit;
DeviceUnit = ((*ptr >= '0' && *ptr <= '9') ? atoi(ptr) : atoi(argv[++i]));
}
break;
case 'p': /* protocol hacks */
if (strcmp(ptr, "ri") == 0) { /* -pri */
long task = FindTask(NULL);
PriMode = 1;
OldPri = SetTaskPri(task, 5);
SetTaskPri(task, OldPri + 1);
} else { /* -proto */
ProtoHacks |= (*ptr) ? atoi(ptr) : atoi(argv[++i]);
}
break;
case 'g':
case 'G':
Getty = 1;
break;
case 'h':
IgnoreCD = 1;
break;
case 'w':
++f_wait;
break;
case 'r':
rmode = atoi(&argv[i][2]);
break;
case 'X':
XDebug = 1;
case 'x':
if (argv[i][2] == 'x') {
DebugHandshake = 1;
break;
}
debug = atoi(&argv[i][2]);
LogLevel = debug;
LogToStdout = 0;
printf("uucico: debug level set to %d\n", debug);
break;
case 'o':
Overide = 1;
break;
case 'n':
WindowSize = (*ptr) ? atoi(ptr) : 1;
break;
case 'b':
system(GetConfigProgram(BATCHNEWS));
break;
case 'S':
ignore_time_restrictions++;
case 's':
poll_sys = &argv[i][2];
break;
case 'e':
++loop;
break;
case 't':
PacketTimeout = (*ptr) ? atoi(ptr) : atoi(argv[++i]);
break;
case '7':
SevenWire = 1;
break;
case 'd':
IgnoreDTR = (*ptr) ? !atoi(ptr) : 1;
break;
default:
printf("uucico: warning, bad flag %s\n", argv[i]);
break;
}
}
/* If argument provided, use it as name of comm port */
/* FIXME, this needs some thought. */
getcwd(path,128);
if (chdir(GetConfigDir(UUSPOOL))) {
perror("Can't chdir to Spool directory");
exit(2);
}
read_ctl();
/*
* If running via getty/login, our debug stdout had better
* go to a file, not to the usual stdout!
*/
if (debug > 0 && Getty) {
freopen("T:uuslave.log", "a", stdout);
}
/*setvbuf(stdout, NULL, _IOLBF, 0);*/
/* Timestamp the long debug log */
if (debug > 0) {
long clock;
time(&clock);
printf("\014\nuuslave log on tty '%s' starting %s\n",
ttynam, ctime(&clock));
}
/* Log our presence so we humans reading the logs can find the
entries created by uuslave. */
ulog(-1, "Startup %s", VERSION);
amiga_setup();
modem_init();
if (poll_sys) {
if (*poll_sys == '\0')
poll_sys = (char *)NULL;
call_system(poll_sys, rmode);
if (!f_wait)
goto end;
} else {
if (rmode) {
do_outbound();
if (!f_wait)
goto end;
}
}
do {
/*
* Set up serial channel, wait for incoming call.
*/
DEBUG(0, "\nRestarting\n", 0);
if (Getty == 0 && Overide == 0)
openline();
do_session(Getty);
hangup();
DEBUG(0, "\nEnd of call\n", 0);
} while (loop && !Getty);
end:
cleanup();
return(0);
}
/*
* translate embedded escape characters
*/
void
xlat_str(msg)
char *msg;
{
int i = 0;
int cr = 1;
while (msg[i]) {
if (msg[i] == '\\') {
switch (msg[++i]) {
case 'r': /* carriage return */
twrite("\r", 1);
break;
case 'n': /* line feed */
twrite("\n", 1);
break;
case '\\': /* back slash */
twrite("\\", 1);
break;
case 't': /* tab */
twrite("\t", 1);
break;
case 'b':
SendBreak();
break;
case 'd': /* delay */
Delay(180);
break;
case 's': /* space */
twrite(" ", 1);
break;
case 'c': /* no CR at end */
cr = 0;
break;
default: /* don't know so skip it */
break;
}
++i;
} else {
twrite(msg + i, 1);
++i;
}
}
if (cr) {
twrite("\r", 1);
}
}
/*
* Read the control file and grab a few parameters.
*/
int
read_ctl()
{
char *nodename = FindConfig(NODENAME);
char *debugstr = FindConfig(DEBUGNAME);
if (nodename && OurNameOv == 0)
strcpy(our_name, nodename);
if (debugstr && debug < 0)
debug = atoi(debugstr);
return (1);
}
/*
* Search spool queues for work, call the systems we need to call.
*/
int
do_outbound()
{
return call_system((char *)NULL, 1);
}
/*
* Call a specific system, or all systems that have work pending.
*/
int
call_system(sys, ifworkpend)
char *sys;
{
FILE *lsys;
static char buf[MAX_LSYS];
static char sysnam[MAX_HOST];
static char prev_name[MAX_HOST];
int called = FAIL;
/*
* Unix uucico just reads the directory, and calls the systems
* in the order of the files in the directory. We want more
* control than that, though I'm not sure that L.sys order is
* best either. For example, in the first call after 11PM,
* I'd like to call the sites that haven't been callable before
* 11PM first, and finish up with the ones I've been able to call
* all day. FIXME.
*/
if (! (lsys = fopen(MakeConfigPath(UULIB, "L.sys"), "r"))) {
DEBUG(0, "uucico: can't open L.sys, errno %d\n", errno);
return 0;
}
sysnam[0] = '\0'; /* Initially, no previous sys */
/* Once per system in L.sys... */
/* FIXME, handle continuation lines (trailing "\") */
while (fgets(buf, sizeof buf, lsys)) {
if (buf[0] == '#' || buf[0] == '\n')
continue;
/*
* Grab the system name. If same as previous, and
* the previous call worked, skip it.
*/
strcpy(prev_name, sysnam);
(void) sscanf(buf, "%s", sysnam);
if (!strcmp(sysnam, prev_name)) {
if (called == SUCCESS)
continue;
}
/*
* If a system name was specified, skip til we find it
* If none was specified, only call if there is work.
*/
if (sys) {
if (strcmp(sys, sysnam) != 0)
continue;
if (ifworkpend && !work_scan(sysnam)) {
ulog(-1, "No work for system %s", sysnam);
called = SUCCESS;
continue;
}
} else {
DEBUG(3,"searching for outbound to %s\n", sysnam);
if (!work_scan(sysnam)) {
DEBUG(3,"no work for %s\n", sysnam);
called = SUCCESS; /* Don't try further */
continue;
}
DEBUG(2, "uucico: found work for %s\n", sysnam);
}
called = call_sysline(buf);
if (called == SUCCESS && sys)
break;
}
fclose(lsys);
if (called == FAIL && sys)
DEBUG(0, "Could not call system %s\n", sys);
return 0;
}
/*
* Call out to a system, given its L.sys line.
*/
int
call_sysline(lsysline)
char *lsysline;
{
static char tempname[MAX_HOST + 30 + SLOP];
char *sysnam,
*times,
*acu,
*sbaud,
*telno,
*send,
*expct;
int baud;
who[0] = '-'; who[1] = '\0'; /* No user now (for logit) */
/* FIXME, use the values it is ignoring here */
sysnam = strtok(lsysline, " \t");
times = strtok(NULL, " \t"); /* Time */
acu = strtok(NULL, " \t"); /* ACU */
sbaud = strtok(NULL, " \t"); /* Baud */
telno = strtok(NULL," \t"); /* phone*/
strcpy(host_name, sysnam);
if (ignore_time_restrictions == 0) {
if (CheckTimeRestrictions(times) == FAIL) {
ulog(-1, "Wrong Time To Call %s", sysnam);
return(FAIL);
}
}
baud = atoi(sbaud);
/* FIX ME, acu not implemented ? */
DEBUG(4, "Opening outgoing line %s\n", acu);
if (openout(acu, baud) != SUCCESS)
return FAIL;
if (Overide == 0) {
if (dial_nbr(telno)) {
ulog(-1, "FAILED call to %s", host_name);
return FAIL;
}
}
/* FIXME, log tty, baud rate, ... */
ulog(-1, "DIALED %s", host_name);
/*
* Process send-expect strings.
* FIXME, deal with "-", BREAK, etc.
*/
if (DebugHandshake)
puts("CONNECTED, running send-expect strings");
while (send = (char*)strtok((char *)NULL, " \t\n")) {
if (send[0] != '"' || send[1] != '"' || send[2] != '\0') {
if (DebugHandshake)
printf("Expect %s\n", send);
if (instr(send, strlen(send), 0))
goto bort1;
} else if (DebugHandshake) {
puts("Expect Nothing");
}
if (expct = (char*)strtok((char *)NULL, " \t\n")) {
if (DebugHandshake)
printf("Send: %s\n", expct);
/* FIXME secondary strings, e.g. ogin:-EOT-ogin: */
xlat_str(expct);
}
}
/*
* FIXME, there should be a way to detect login/passwd
* failure here and keep doing the script rather than
* continuing to expect Shere at another login: prompt.
*/
ulog(-1, "SUCCEEDED call to %s", host_name);
if (getname(1)) /* get name */
goto bort1;
/* send response */
sprintf(tempname, "\20S%s -Q0 -x%d\0", our_name, (XDebug) ? 0 : debug);
twrite(tempname, strlen(tempname)+1); /* Including null */
/* wait for ok message, wait for protocol request
* send protocol 'g' response */
/* FIXME, we don't actually wait for the ROK message, since
* it is immediately followed by the Pprotos message. We
* currently just look for a Pg message. This needs work.
* FIXME, WE CAN'T TALK TO SITES THAT SUPPORT more than 'g'.
*/
if (instr(msgo3a, sizeof(msgo3a)-1, 0)) {
if (!get_proto())
goto bort1;
}
twrite( msgi3, sizeof(msgi3)-1);
ResetGIO(); /* reset GIO protocol */
if (turnon(1))
goto bort1;
ulog(-1, "OK Startup");
top_level(1);
hangup();
return SUCCESS;
bort1:
hangup();
return FAIL;
}
/* Handle a single uucp [slave] login session */
int
do_session(ontheline)
int ontheline;
{
if (ontheline == 0) {
/* output login request, verify uucp */
twrite(msgo0,sizeof(msgo0)-1);
if (instr(msgi0, sizeof(msgi0)-1, 0)) {
printf("uucico: invalid login name\n");
goto bort;
}
/* output password request, verify s8000 */
twrite(msgo1,sizeof(msgo1)-1);
if (instr(msgi1, sizeof(msgi1)-1, 0)) {
printf("uucico: invalid password\n");
goto bort;
}
printf("uucico: correct login\n");
}
/*
* send Shere=<myhost>
*
* Apparently mac UUCP has a bug that only allows 7
* char host names, and it fails if it gets shere=<myhost>
* where <myhost> is > 7 chars.
*
* Apparently some implementations of AmigaUUCP do not accept
* an SHere with an =<myhost>, so this is disabled unless -p1 is used.
*/
if (ProtoHacks & PROTOF_SHEREEQUALS)
sprintf(msgo2 + MSGO2IDX, "=%s", our_name);
twrite(msgo2,strlen(msgo2)+1);
/*
* get \020S<host> -Qn n (??)
*/
if (getname(0))
goto bort;
/* output ok message, output protocol request, wait for response */
twrite(msgo3,sizeof(msgo3)-1);
/* FIXME, make the protocol list here, and use it */
twrite(msgo3b,sizeof(msgo3b)-1);
if (instr(msgi3, sizeof(msgi3)-1, 0))
goto bort;
ResetGIO(); /* reset GIO protocol */
if (turnon(0))
goto bort;
ulog(-1, "OK Startup");
top_level(0);
bort:
if (debug > 0)
printf("uucico: call complete\n");
return (1);
}
/*
* Handle transactions "at top level", as Unix uucp's debug log says.
*
* As master, we scan our queues for work and send requests to the
* other side. When done, we send a hangup request and switch to slave mode.
*
* As slave, we accept requests from the other side; when it is done,
* it sends a hangup request, and we switch to master mode, if we have
* any work queued up for that system.
*
* This repeats as long as either side has work to do. When all the
* queued work is done, we agree to hang up, terminate the packet protocol,
* and return to the caller. (We still haven't hung up the phone line yet.)
*
* A curious feature of the hangup protocol is that it is not a simple
* question-answer. The master says "H", asking about hangup. The
* slave responds "HY" saying OK. The master then says "HY" also,
* then both of them hang up. Maybe this is to make sure the first HY
* got ack'ed? Anyway, an "H" is reported as HANGUP and an "HY" as
* HANGNOW. After we send an HY, we go back to listening for commands;
* if the master sends something other than HY, we'll do it.
*/
#define HANGUP 2 /* Signal to switch master/slave roles */
#define HANGNOW 3 /* Signal to hang up now */
#define COPYFAIL 4 /* File copy failed */
int
top_level(master_mode)
int master_mode;
{
static char buf[MAXMSGLEN]; /* For hangup responses */
if (master_mode) {
(void) work_scan(host_name); /* Kick off queue scan */
goto master;
}
for (;;) {
slave: /* SLAVE SIDE */
for (;;) {
DEBUG(4, "*** TOP *** - slave\n", 0);
switch (do_one_slave()) {
case SUCCESS:
break;
case FAIL:
DEBUG(4, "*** DO_ONE_SLAVE FAIL *** - slave\n", 0);
return FAIL;
case HANGUP:
if (work_scan(host_name)) {
if (wrmsg("HN") != SUCCESS) {
DEBUG(4, "*** WRMSG HN FAIL *** - slave\n", 0);
return FAIL;
}
goto master;
} else {
if (wrmsg("HY") != SUCCESS) {
DEBUG(4, "*** WRMSG HY FAIL *** - slave\n", 0);
return FAIL;
}
break; /* go to master mode */
}
case HANGNOW:
goto quit;
}
}
master: /* MASTER SIDE */
for (;;) {
DEBUG(4, "*** TOP *** - master\n", 0);
switch (do_one_master()) {
case SUCCESS:
break;
case FAIL:
DEBUG(4, "*** DO_ONE_MASTER FAIL *** - master\n", 0);
return FAIL;
case HANGUP:
/* We wrote an H command, what's the resp? */
if (rdmsg(buf, MAXMSGLEN) != SUCCESS) {
DEBUG(4, "*** RDMSG HANGUP FAIL *** - master\n", 0);
return FAIL;
}
if (buf[0] != 'H') {
DEBUG(4, "*** RDMSG HANGUP != 'H' *** - master\n", 0);
return FAIL;
}
if (buf[1] == 'N') {
goto slave;
} else {
/*
* send final HY? not sure if this should happen, will
* necessarily fail if the other side does not expect
* it so do not return... continue on w/ exit code.
*
* however, reduce timeout parameters
*/
++ReducedTimeout;
wrmsg("HY");
--ReducedTimeout;
goto quit;
}
}
}
}
quit:
/* Shut down the packet protocol */
turnoff();
/* Write the closing sequence */
twrite(msgo4, sizeof(msgo4)-1);
(void) instr(msgi4, sizeof(msgi4)-1, 0);
twrite(msgo4, sizeof(msgo4)-1);
strcpy(who, "-");
ulog(-1, "OK Conversation complete");
return SUCCESS; /* Go byebye */
}
/*
* We are slave; get a command from the other side and execute it.
*
* Result is SUCCESS, FAIL, HANGUP, or HANGNOW.
*/
int
do_one_slave()
{
static char msg[MAXMSGLEN]; /* Master's message to us */
/* Get master's command */
if (rdmsg(msg, MAXMSGLEN) != SUCCESS)
return FAIL;
/* Print it for easy debugging */
DEBUG(5,"\nCommand: %s\n\n", msg);
switch (msg[0]) {
case 'S':
if (msg[1] != ' ')
break;
return host_send_file(msg);
case 'R':
if (msg[1] != ' ')
break;
return host_receive_file(msg);
case 'X':
break;
case 'H':
if (msg[1] == '\0') return HANGUP;
if (msg[1] == 'Y') return HANGNOW;
if (msg[1] == 'N') return SUCCESS; /* Ignore HN to slave */
break;
}
/* Unrecognized packet from the other end */
DEBUG(0, "Bad control packet refused: %s\n", msg);
return(yesno(msg[0], 0, 0));
}
/*
* Do one piece of work as master.
*
* FIXME: we don't handle the flags, e.g. -c, properly!
*
* Now only dequeues queue file if all transfers were successful.
*/
int
do_one_master()
{
FILE *fd;
char *sname;
static char cmnd[256]; /* Command character */
static char buf[256];
int fail = SUCCESS;
int failAction = SUCCESS;
int failaccum = 0;
int num;
int delmeflag;
static char notify[NAMESIZE]; /* A bit large...FIXME */
char *delList[16]; /* delete files list */
short di = 0;
/*
* Get the next work item. If no work left re-scan the directory
* just to be sure, and if still no work then do the right thing.
*/
sname = work_next();
if (!sname && NoReScan == 0) {
if (work_scan(host_name))
sname = work_next();
}
if (!sname) {
/* No more work, time to hang up. */
if (wrmsg("H") != SUCCESS)
return FAIL;
return HANGUP;
}
DEBUG(2, "Request file %s\n", sname);
LockFile(sname);
fd = fopen(sname, "r");
if (fd == NULL) {
UnLockFile(sname);
DEBUG(0, "uucico: couldn't open %s\n", sname);
return SUCCESS;
}
while (fgets(buf, sizeof(buf), fd)) {
DEBUG(3, "Queued request: %s", buf);
if (buf[1] != ' ')
goto badnum;
num = sscanf(buf, "%s %s %s %s %s %s %o\n",
cmnd, srcnam, dstnam, who, flags, temp, &mode, notify
);
switch (cmnd[0]) {
case 'S':
if (num < 7 || num > 8)
goto badnum;
fail = local_send_file(buf, &delmeflag);
if (delmeflag) {
if (di == sizeof(delList)/sizeof(delList[0])) {
ulog(-1, "Too many source files in Cmd file! %s", sname);
} else {
delList[di] = malloc(strlen(temp) + 1);
strcpy(delList[di], temp);
++di;
}
}
break;
case 'R':
if (num != 5) {
if (debug > 7)
printf("Invalid scanf %d/5 :%s:%s:%s\n", num, cmnd, srcnam, dstnam);
goto badnum;
}
fail = local_receive_file();
break;
default:
badnum:
ulog(-1, "Illegal Work Request (%s): %s", sname, buf);
fail = REFUSED;
break;
}
switch(fail) {
case SUCCESS:
break;
case FAIL:
++failaccum;
if (failAction == SUCCESS)
failAction = FAIL;
ulog(-1, "Protocol Failure at (%s): %s", sname, buf);
NoReScan = 1;
break;
case REFUSED:
++failaccum;
failAction = REFUSED;
ulog(-1, "Work Refused (%s): %s", sname, buf);
break;
}
}
fclose(fd);
switch(failAction) {
case SUCCESS:
while (di) {
--di;
remove(delList[di]);
free(delList[di]);
}
fail = remove(sname);
UnLockFile(sname);
if (fail != 0) {
ulog(-1, "Unable to remove work file %s", sname);
DEBUG(0, "Can't remove, errno %d\n", errno);
} else {
DEBUG(4, "Removed work file %s\n", sname);
}
break;
case FAIL:
UnLockFile(sname);
break;
case REFUSED:
UnLockFile(sname);
strcpy(buf, sname);
{
short i;
for (i = strlen(buf); i >= 0 && buf[i] != ':' && buf[i] != '/'; --i);
++i;
if ((buf[i]|0x20) == 'c')
buf[i] = 'E';
}
if (strcmp(sname, buf) == 0) {
ulog(-1, "Removing %s", sname);
remove(sname);
} else {
ulog(-1, "Renaming %s to %s", sname, buf);
rename(sname, buf);
}
break;
}
return(SUCCESS);
}
/*
* Send a yes/no packet
*/
int
yesno(c, true, err)
char c;
int true;
int err;
{
char buf[21];
buf[0] = c;
buf[1] = true? 'Y': 'N';
buf[2] = 0;
if (err && !true)
sprintf(buf+2,"%d", err);
return (wrmsg(buf));
}
/*
* SLAVE MODE, Master wishes to send a file to us
*
* SECURITY: If file is not in list of allowed directories
* disallow transfer. UUSPOOL: is always in the
* list.
*
* If file is for UUSPOOL: (the current dir), disallow "C." files
* NOTE: success return and file redirected to T: as this can
* occur only if somebody purposefully is trying to break us.
*
* Return 0 = success
*/
int
host_send_file(msg)
char *msg;
{
FILE *fddsk; /* Disk file pointer */
static char cmnd[256]; /* Command character */
int r;
int nor = 0;
sscanf(msg,"%s %s %s %s %s %s %o",
cmnd, srcnam, dstnam, who, flags, temp, &mode);
ulog(-1, "REQUESTED %s", msg);
munge_filename(dstnam, dstnam); /* Translate to local name */
strcpy (temp, TmpFileName(dstnam)); /* Create a handy temp file */
if (SecurityDisallow(dstnam, 'w')) {
ulog(-1, "REQUEST FAILED -- SECURITY");
return(yesno('S', 0, 4));
}
if (SecurityDisallow(dstnam, 'c') > 0) {
ulog(-1, "REQUEST FAILED -- SECURITY, REMOTE TRIED TO SEND");
ulog(-1, "US A COMMAND FILE: %s, FILE COPIED TO T:Bad-Cmd", dstnam);
strcpy(dstnam, "T:Bad-Cmd");
nor = 1;
}
/* FIXME: deal with file modes now that we fopen. */
LockFile(temp);
fddsk = fopen(temp, "wb" /*, mode|0600 */);
if (fddsk == NULL) {
UnLockFile(temp);
/* Can't open file -- send error response */
if (debug > 0) {
printf("Cannot open temp file %s (%s) for writing, errno=%d\n",
temp,
dstnam,
errno
);
}
ulog(-1, "REQUEST FAILED -- TEMP FILE");
return (yesno('S', 0, 4));
}
/* FIXME: Are the above permissions right?? */
/* FIXME: Should we create directories for the file? */
if (yesno('S',1, 0) != SUCCESS) {
fclose(fddsk);
unlink(temp);
UnLockFile(temp);
return(FAIL);
}
r = receive_file(fddsk, temp, dstnam, srcnam, nor);
UnLockFile(temp);
return(r);
}
/*
* SLAVE MODE, Master wants us to send a file to it
*
* SECURITY: If file is not in list of allowed directories
* disallow transfer. UUSPOOL: is always in the
* list.
*
* 0 = sucess
*/
int
host_receive_file(msg)
char *msg;
{
FILE *fddsk; /* Disk file descriptor */
int x;
static char cmnd[256]; /* Command character */
ulog(-1, "REQUESTED %s", msg);
sscanf(msg,"%s %s %s",cmnd,srcnam,dstnam);
munge_filename(srcnam, temp);
if (SecurityDisallow(temp, 'r')) {
ulog(-1, "COPY FAILED -- SECURITY");
return (yesno('S', 0, 4));
}
fddsk = fopen(temp, "rb"); /* Try to open the file */
if (fddsk == NULL) {
/* File didn't open, sigh. */
if (debug > 0) {
printf("Cannot open file %s (%s) for reading, errno=%d\n",
temp, srcnam, errno
);
}
ulog(-1, "DENIED CAN'T OPEN %s", temp);
return(yesno('R', 0, 2));
}
if (yesno('R',1, 0) != SUCCESS) {
fclose(fddsk);
return(FAIL);
}
x = send_file(fddsk);
switch (x) {
default:
return x;
case COPYFAIL:
/* We don't care if the copy failed, since the master
asked for the file and knows the result. */
return SUCCESS;
}
return 1;
}
/*
* MASTER MODE, We want to send a file.
*
* Return FAIL, SUCCESS, or COPYFAIL.
*
* SUCCESS is returned either if the file was not found locally (local
* error, and the queued transfer should be flushed) or if it was moved
* successfully. COPYFAIL indicates that the queued transfer should be
* left queued, and later retried. FIXME, there are several failure points
* in the transaction and we need finer control here.
*/
int
local_send_file(workstr, delmeflag)
char *workstr;
int *delmeflag;
{
static char buf[MAXMSGLEN]; /* Used for both xmit and receive */
FILE *fddsk; /* Disk file descriptor */
int res; /* Result and file removal status */
*delmeflag = 0;
/* WHY are temp and srcnam switched? FIXME! And no notify? */
sprintf(buf,"S %s %s %s %s %s 0%o %s",
temp, dstnam, who, flags, srcnam, mode, who
);
ulog(-1, "REQUEST %s", buf);
if (strchr(flags, 'c')) {
munge_filename(srcnam, temp);
} else {
munge_filename(temp, temp);
}
LockFile(temp);
fddsk = fopen(temp, "rb");
if (fddsk == NULL) {
UnLockFile(temp);
/* FIXME -- handle queued request for nonexistent file */
if (debug > 0)
printf("Can't open file %s (%s), errno=%d\n",
temp,
srcnam,
errno
);
ulog(-1, "NOT FOUND %s", temp);
/* return COPYFAIL;*/
return SUCCESS; /* assume file previously sent */
}
/* Tell the other side we want to send this file */
if (wrmsg(buf) != SUCCESS) {
fclose(fddsk);
UnLockFile(temp);
return (FAIL);
}
/* See what they have to say about it */
if (rdmsg(buf, MAXMSGLEN) != SUCCESS) {
fclose(fddsk);
UnLockFile(temp);
return FAIL;
}
if ((buf[0] != 'S') || (buf[1] != 'Y')) {
ulog(-1, "REQUEST DENIED %s", buf);
fclose(fddsk);
UnLockFile(temp);
return(REFUSED);
}
res = send_file(fddsk); /* FAIL, SUCCESS, or COPYFAIL */
/* Delete the source file if it was just a copy */
if (res != SUCCESS) {
UnLockFile(temp);
return res;
}
if (strchr(flags, 'c')) { /* If copied direct from source */
UnLockFile(temp);
return res; /* ...just return. */
}
*delmeflag = 1;
UnLockFile(temp);
return res;
}
/*
* MASTER MODE, We wish to receive a specific file so we ask for it
*
* Return 0 = success
*/
int
local_receive_file()
{
static char buf[MAXMSGLEN];
FILE *fddsk; /* Disk file pointer */
int r;
/* FIXME, test dest file access before we ask for it. */
sprintf(buf,"R %s %s %s %s %s 0%o %s",
srcnam, dstnam, who, flags, temp, mode, who
);
munge_filename(dstnam, dstnam); /* tlate to local name */
strcpy (temp, TmpFileName(dstnam)); /* Create a handy temp file */
/* FIXME: deal with file modes now that we fopen. */
/* FIXME: Are the above permissions right?? */
/* FIXME: Should we create directories for the file? */
LockFile(temp);
fddsk = fopen(temp, "wb" /*, mode|060 */);
if (fddsk == NULL) {
UnLockFile(temp);
/* Can't open temp file -- send error response */
if (debug > 0) {
printf("Cannot open temp file %s (%s) for writing, errno=%d\n",
temp,
dstnam,
errno
);
}
ulog(-1, "REQUEST FAILED -- TEMPFILE");
return FAIL;
}
ulog(-1, "REQUEST %s", buf);
if (wrmsg(buf) != SUCCESS) {
fclose(fddsk);
UnLockFile(temp);
printf("uucico: problem sending request\n");
return FAIL;
}
/* See what the other side has to say about it */
if (rdmsg(buf, MAXMSGLEN) != SUCCESS) {
fclose(fddsk);
UnLockFile(temp);
return FAIL;
}
if ((buf[0] != 'R') || (buf[1] != 'Y')) {
ulog(-1, "REQUEST DENIED %s", buf);
fclose(fddsk);
UnLockFile(temp);
return (REFUSED);
}
r = receive_file(fddsk, temp, dstnam, srcnam, 0);
UnLockFile(temp);
return(r);
}
/*
* General receive file
*/
int
receive_file(fddsk, temp, dstnam, srcnam, norename)
FILE *fddsk;
char *temp, *dstnam, *srcnam;
{
int status;
int error = 0; /* No errors so far */
if (rddata(fddsk) != SUCCESS)
error++;
status = fclose(fddsk); /* Make sure the data got here */
if (status != 0) {
error++;
DEBUG(0, "fclose errno=%d\n", errno);
}
/*
* Move the file from its temp location to its real location,
* This needs to be able to copy a file if a simple rename
* does not suffice. Should create directories if necesary.
* should use source ]name if target is a directory (i.e. no
* target source name
*/
unlink(dstnam);
if (norename) /* for security redirect */
status = 0;
else
status = rename(temp, dstnam);
if (status != 0) {
error++;
if (debug > 0) {
printf("Cannot rename file %s to %s, errno=%d\n",
temp, dstnam, errno);
}
}
ulog(-1, "COPY %s", error ? "FAILED": "SUCCEEDED");
return(yesno('C', error == 0, 5));
}
/*
* general file send routine
* Return SUCCESS, FAIL, or COPYFAIL.
*/
int
send_file(fddsk)
FILE *fddsk; /* Disk file pointer */
{
static char ansbuf[MAXMSGLEN];
if (wrdata(fddsk) != SUCCESS) {
fclose(fddsk);
return COPYFAIL;
}
fclose(fddsk);
/* Await the "CY" or "CNddd" packet, and toss it. */
while (1) {
if (rdmsg(ansbuf, MAXMSGLEN) != SUCCESS)
return COPYFAIL;
if (ansbuf[0] != 'C') {
DEBUG(0,"\nDidn't get 'CY' or 'CN', got %s\n", ansbuf);
/* and loop looking for C message */
} else if (ansbuf[1] == 'Y') {
ulog(-1, "REQUESTED %s", ansbuf);
return SUCCESS;
} else {
ulog(-1, "COPY FAILED %s", ansbuf);
return COPYFAIL;
}
}
return COPYFAIL;
}