/*

 * @(#)uuslave.c        Version hoptoad-1.21    87/08/14
 *
 * (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.
 */

char version[] = "Version Amiga-0.30 BETA";


#include "includes.h"           /* System include files, system dependent */
#include "uucp.h"               /* Uucp definitions and parameters */
#include "sysdep.h"             /* System dependent parts of gnuucp */
#include "modem.h"              /* Modem commands */

int sigint();
void modem_init();
void cleanup();

#define MAX_FLAGS       40

#ifndef LOG
#define logit(one, two) /* Nothing */
#else
void logit();
#endif

extern int errno;

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     logfd,                          /* file desc of uucp logfile */
        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  spool_dir[128];
char  path[128];
int   debug   = -1;     /* -1 indicates not set by command line or ctl file */
int   f_wait    = 0;    /* FIXME, unreferenced now */
int   loop    = 1;      /* Loop accepting logins if tty name specified */
int   curtemp = 0;

#define MAX_STRING      200     /* Max length string to send/expect */

/* We print these prompts */
char msgo0[] = "login: ";
char msgo1[] = "Password:";
char msgo2[] = "\20Shere\0";
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";

#define SPOOLDIR "UUCP:SPOOL"
/*
 * Protocol switch data structure
 */
extern int gturnon(), grdmsg(), gwrmsg(), grddata(), gwrdata(), gturnoff();

#define turnon  gturnon
#define rdmsg   grdmsg
#define wrmsg   gwrmsg
#define rddata  grddata
#define wrdata  gwrdata
#define turnoff gturnoff

/*
 * FIXME, comment this, make formatting match, etc.
 * get the calling hosts name from his response to Shere=<ourname>
 * Result is in global variable host_name.
 */
int
getname()
{
        int data, count = 0;
        char    msgi[MAX_STRING+SLOP];  /* Incoming trash buffer */

        /* Read data until null character */
        while ( ((data = xgetc()) != EOF) && (data != 0x00))
                msgi[count++] = (char)data & 0x7F;

        if (msgi[0] = 'S') {
                strcpy (host_name, strtok(msgi, "\20S "));
                return SUCCESS;
        }
        return FAIL;
}
/*
   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()) != EOF) && (data != 0x00)) {
            if ((char)data == 'g') {
                return SUCCESS;  /* System we are talking to has 'g' */
            }
        }
        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)
 char *s;
 int n;
{
        int data,count,j;
        int i;
/* FIXME, msgi is WAY too small to handle a large /etc/motd! */
        static char msgi[512];  /* Incoming trash buffer */

        count = 0;

        if (debug > 8) {
                printf("Expecting ");
                for (i = 0; i < n; i++)
                        printf("%02x%c ",s[i] & 0xFF, isprint(s[i])? s[i]: ' ');
                printf("\nR ");
        }

        while ((data = xgetc()) != EOF)
        {
                msgi[count++] = (char)data & 0x7F;

                if (debug > 8) {
                        printf("%02x%c ",msgi[count-1],
                                isprint(msgi[count-1])? msgi[count-1]: ' ');
                }

                if (count == 512) {
                    logit("OVERRAN message buffer\n");
                    sigint();
                }

                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");
                                return(0);
                        }
                }
        }

        if (debug > 8)  printf("\n");
        msgi[count] = (char)0;
        return(1);
}

/*
 * Debugging hack for stuff written to the modem.
 */
int
twrite(s, n)
        char *s;
        int     n;
{
        int i;

        if (debug > 8) {
            printf("Wrote:  ");
            for (i = 0; i < n; i++)
                    printf("%02x%c ",s[i] & 0xFF, isprint(s[i])? s[i]: ' ');
            printf("\n");
         }

        return xwrite(s, n);
}

void
create_lock()
{
  FILE *fd;

  if (access(LOCK_FILE,0) != -1) {
    printf("UUCP is locked.\n");
    exit(2);
  }

  if (!(fd = fopen(LOCK_FILE, "w"))) {
    printf("UUCP can't be locked.\n");
    exit(3);
  }

  fclose(fd);
}

void
destroy_lock()
{
  if (unlink(LOCK_FILE)) {
    printf("Couldn't remove lock.\n");
  }
}
/*
 * 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.
 */
void
main(argc,argv)
int argc;
char *argv[];
{
        int     ontheline = 0; /* for amiga */
        int     i;
        char    *poll_sys = (char *)NULL;  /* System name to poll, or none */

        signal(SIGINT,sigint);  /* Allow the user to break */

        create_lock();

        /* FIXME, use getopt */
        /* scan command line arguments, kinda kludgy but it works */
        for (i = 1; i < argc; i++) {
                if (argv[i][0] != '-')
                        break;
                switch (argv[i][1]) {

                case 'w':
                        f_wait++;
                        printf("uucico: will wait for call after outbound\n");
                        break;

                case 'x':
                        debug = atoi(&argv[i][2]);
                        printf("uucico: debug level set to %d\n", debug);
                        break;

                case 'S':
                        ignore_time_restrictions++;
                case 's':
                        poll_sys = &argv[i][2];
                        ontheline = 0;
                        break;

                case 'e':
                        loop++;
                        printf("uucico: endless loop mode\n");
                        break;

                /* Is -t needed for MSDOS?  Why?  -- hoptoad!gnu */
                case 't':
                        curtemp++;
                        printf("uucico: using ~uutemp.$$$ for temp file\n");
                        break;
                }
        }

        /* If argument provided, use it as name of comm port */


        /* FIXME, this needs some thought. */
        if (!ontheline) {
                getcwd(path,128);
                if (chdir(SPOOLDIR)) {
                        perror("Can't chdir to Spool directory");
                        destroy_lock();
                        exit(2);
                }
        }

        read_ctl();             /* Read control file FIXME */



        /*
         * If running via getty/login, our debug stdout had better
         * go to a file, not to the usual stdout!
         */
        if (debug > 0 && ontheline) {
                freopen("uuslave.log", "a", stdout);
        }

        setbuf(stdout, (char *)NULL);   /* Unbuffered debug output */

        /* 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. */
        logit("Amiga", version);

        amiga_setup();

        if (poll_sys) {
                if (*poll_sys == '\0') poll_sys = (char *)NULL;
                call_system(poll_sys);
                if (!f_wait) goto end;
        }

        modem_init();

        do {
                /*
                 *  Set up serial channel, wait for incoming call.
                 */
                DEBUG(0, "\nRestarting\n", 0);
                openline();

                do_session(ontheline);

                hangup();
                DEBUG(0, "\nEnd of call\n", 0);

        } while (loop && !ontheline);

end:
        cleanup();
        destroy_lock();
}

/*
 * translate embedded escape characters

 *
 */
void
xlat_str(msg, out)
        char    *msg;
        char    *out;
{
        int     i  = 0,
                cr = 1,
                j  = 0;

        while (msg[i]) {
                if (msg[i] == '\\') {
                        switch (msg[++i])
                                {
                                case 'r':            /* carriage return */
                                        out[j++] = 0x0d;
                                        break;
                                case 'n':            /* line feed */
                                        out[j++] = 0x0a;
                                        break;
                                case '\\':           /* back slash */
                                        out[j++] = '\\';
                                        break;
                                case 't':            /* tab */
                                        out[j++] = '\t';
                                        break;
                                case 'd':            /* delay */
                                        Delay(180);
                                        break;
                                case 's':            /* space */
                                        out[j++] = ' ';
                                        break;
                                case 'c':            /* no CR at end */
                                        cr = 0;
                                        break;
                                default:            /* don't know so skip it */
                                        break;
                                }
                        i++;
                        }
                else
                        out[j++] = msg[i++];
                }

        if (cr)
                out[j++] = 0x0d;

        out[j] = '\0';
}

/*
 * Read the control file and grab a few parameters.
 */
read_ctl()
{
        FILE  *fd;
        char  buf[MAX_CTLLINE];

        if (! (fd = fopen("UUCP:lib/config", "r"))) {
                printf("Can't Find config file");
                chdir(path);
                destroy_lock();
                exit(3);
        }

        /* find path to inbound         news */
        spool_dir[0] = '\0';

        while (NULL != fgets(buf, sizeof buf, fd)) {
                     if (strncmp(buf, "Spool", 5) == 0)
                        strcpy(spool_dir, strtok(&buf[6], CTL_DELIM) );
                else if (strncmp(buf, "NodeName", 8) == 0)
                        strcpy(our_name, strtok(&buf[9], CTL_DELIM) ) ;
                else if (strncmp(buf, "Debug", 5) == 0)
                        if (debug < 0) debug = atoi(strtok(&buf[6], CTL_DELIM));
        }

        fclose(fd);
        return (1);
}

/*
 * Search spool queues for work, call the systems we need to call.
 */
do_outbound()
{

        return call_system((char *)NULL);
}

/*
 * Call a specific system, or all systems that have work pending.
 */
call_system(sys)
        char    *sys;
{
        FILE    *lsys;
        char    buf[MAX_LSYS];
        char    sysnam[MAX_HOST];
        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("uucp:lib/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] == '#')
                        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 (0 != strcmp(sys, sysnam))
                                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.
 */
call_sysline(lsysline)
        char    *lsysline;
{
        char    tempname[MAX_HOST + 30 + SLOP],
                *sysnam,
                *times,
                *acu,
                *sbaud,
                *telno,
                *send,
                *expct;
        char    logbuf[MAX_HOST+30+SLOP];
        char    strbuf[MAX_STRING+SLOP];
        int     baud;

        who[0] = '-'; who[1] = '\0';    /* No user now (for logit) */

        /* FIXME, use the values it is ignoring here */
        sysnam = (char*)strtok(lsysline, " ");
        times = (char*)strtok((char *)NULL, " ");      /* Time */
        acu = (char*)strtok((char *)NULL, " ");        /* ACU */
        sbaud = (char*)strtok((char *)NULL, " ");      /* Baud */
        telno = (char*)strtok((char *)NULL," ");       /* phone */

        strcpy(host_name, sysnam);

        if ((!ignore_time_restrictions) && (strcmp(times,"Any"))) {
                /* FIXME, check the time parameter and return FAIL if
                 * it does not allow calls now.  Meanwhile, bounce
                 * all calls unless -S is specified. */
                logit("WRONG TIME TO CALL", sysnam);
                return FAIL;
        }

        baud = atoi(sbaud);

        /* FIXME, this needs work for the ACU parameter */
        /* Currently depends on symlink from /usr/spool/uucp/ACU !! */
        DEBUG(4, "Opening outgoing line %s\n", acu);
        if (openout(acu, baud) != SUCCESS)
                return FAIL;

        sprintf(logbuf, "call to %s", host_name);

        if (dial_nbr(telno)) {
                logit("FAILED", logbuf);
                return FAIL;
        }

        /* FIXME, log tty, baud rate, ... */
        logit("DIALED", host_name);

        /*
         * Process send-expect strings.
         * FIXME, deal with "-", BREAK, etc.
         */
        while (send = (char*)strtok((char *)NULL, " ")) {
                if (send[0] != '"' || send[1] != '"' || send[2] != '\0')
                        if (instr(send, strlen(send)))
                                goto bort1;

                if (expct = (char*)strtok((char *)NULL, " ")) {
                        /* FIXME secondary strings, e.g. ogin:-EOT-ogin: */
                        /* This puts delays in the wrong place, FIXME */
                        xlat_str(expct, strbuf);

                        twrite(strbuf, strlen(strbuf));
                }
        }

        /*
         * 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.
         */
        logit("SUCCEEDED", logbuf);

        /* wait for Shere message, send response */
        /* FIXME, grab hostname and check it if provided? */
        sprintf(tempname, "\20Shere");
        if (instr(tempname, strlen(tempname)))
                goto bort1;

        sprintf(tempname, "\20S%s -Q0 -x%d\0", our_name, 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))
                if (!get_proto())
                   goto bort1;


        twrite( msgi3, sizeof(msgi3)-1);

        if (turnon(1))
                goto bort1;

        logit("OK", "startup");

        top_level(1);
        hangup();
        return SUCCESS;

bort1:
        hangup();
        return FAIL;
}

/* Handle a single uucp [slave] login session */

do_session(ontheline)
        int ontheline;
{

        if (!ontheline) {
                /* output login request, verify uucp */
                twrite(msgo0,sizeof(msgo0)-1);
                if (instr(msgi0,sizeof(msgi0)-1)) {
                        printf("uucico: invalid login name\n");
                        goto bort;
                }

                /* output password request, verify s8000 */
                twrite(msgo1,sizeof(msgo1)-1);
                if (instr(msgi1,sizeof(msgi1)-1)) {
                        printf("uucico: invalid password\n");
                        goto bort;
                }

                printf("uucico: correct login\n");
        }

        /* output here message, wait for response */
        twrite(msgo2,sizeof(msgo2)-1);
/* FIXME, handle this kludge */
/*      if (instr(msgi2,sizeof(msgi2)-1)) */
        if (getname())
                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))
                goto bort;

        if (turnon(0)) goto bort;
        logit("OK", "startup");
        top_level(0);

bort:
        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;
{
        char    buf[MAXMSGLEN];         /* For hangup responses */

        if (master_mode) {
                (void) work_scan(host_name);    /* Kick off queue scan */
                goto master;
        }

        for (;;) {
                /* Slave side */
        slave:
                for (;;) {
                        DEBUG(4, "*** TOP *** - slave\n", 0);
                        switch (do_one_slave()) {
                        case SUCCESS:
                                break;
                        case FAIL:
                                return FAIL;
                        case HANGUP:
                                if (work_scan(host_name)) {
                                        if (wrmsg('H', "N"))
                                                return FAIL;
                                        goto master;
                                } else {
                                        if (wrmsg('H', "Y"))
                                                return FAIL;
                                        break;
                                }
                        case HANGNOW:
                                goto quit;
                        }
                }

                /* Master side */
        master:
                for (;;) {
                        DEBUG(4, "*** TOP *** - master\n", 0);
                        switch (do_one_master()) {
                        case SUCCESS:
                                break;
                        case FAIL:
                                return FAIL;
                        case HANGUP:
                                /* We wrote an H command, what's the resp? */
                                if (rdmsg(buf) != SUCCESS)
                                        return FAIL;
                                if (buf[0] != 'H')
                                        return FAIL;
                                if (buf[1] == 'N')
                                        goto slave;
                                else {
                                        /* Write the final HY */
                                        if (wrmsg('H', "Y"))
                                                return FAIL;
                                        goto quit;
                                }
                        }
                }
        }

quit:
        /* Shut down the packet protocol */
        turnoff();

        /* Write the closing sequence */
        twrite(msgo4, sizeof(msgo4)-1);
        (void) instr(msgi4, sizeof(msgi4)-1);

        twrite(msgo4, sizeof(msgo4)-1);

        logit("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()
{
        char msg[MAXMSGLEN];            /* Master's message to us */

        /* Get master's command */
        if (rdmsg(msg) != 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':
                /* Cause uuxqt to run (on certain files?)
                 * See Protocol.doc for sketchy details.
                 */
                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);
        if (yesno(msg[0], 0, 0))        /* FIXME: return error code */
                return FAIL;
        return SUCCESS;
}

/*
 * Do one piece of work as master.
 *
 * FIXME:  we don't handle the flags, e.g. -c, properly!
 */
int
do_one_master()
{
        FILE    *fd;
        char    *sname;
        char    cmnd[1];                /* Command character */
        char    buf[256];
        int     fail;
        int     num;
        char    notify[NAMESIZE];       /* A bit large...FIXME */
                        /* FIXME: do the notify stuff */

        sname = work_next();
        if (!sname) {
                /* No more work, time to hang up. */
                if (wrmsg('H', ""))
                        return FAIL;
                return HANGUP;
        }

        DEBUG(2, "Request file %s\n", sname);

        fd = fopen(sname, "r");
        if (fd == NULL) {
                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);
                        break;

                case 'R':
                        if (num != 5) goto badnum;
                        fail = local_receive_file(buf);
                        break;

                default: badnum:
                        DEBUG(0, "Unknown/invalid queued request: %s\n",
                                 buf);
                        fail++;
                        break;
                }

                /* FIXME, what does uucp do if one of N xfers fails? */
                if (fail == FAIL) {
                        logit("ERROR IN WORK FILE", sname);
                        logit("BAD LINE IS", buf);
                }
        }
        fclose(fd);

        /* Zap the queue file, if file was copied successfully, or */
        /* if it was a complete success, but not if it was a COPY_FAIL */

        if (fail != COPYFAIL) {
           fail = remove(sname);
           if (fail != 0) {
                logit("CAN'T REMOVE WORK FILE", sname);
                DEBUG(0, "Can't remove, errno %d\n", errno);
           } else {
                DEBUG(4, "Removed work file %s\n", sname);
           }
        }
        return SUCCESS;
}

/* Send a "yes or no" packet with character 'c'. */
int
yesno(c, true, err)
        char c;
        int true;
        int err;
{
        char buf[20];

        buf[0] = true? 'Y': 'N';
        buf[1] = 0;
        if (err && !true)
                sprintf(buf+1,"%d", err);

        return wrmsg(c, buf);
}

/*
 * Master wishes to send a file to us -- we receive it.
 * Return 1 to abort the call, 0 to continue.
 */
int
host_send_file(msg)
char  *msg;
{
        FILE *fddsk;                    /* Disk file pointer */
        char    cmnd[1];                /* Command character */

        sscanf(msg,"%s %s %s %s %s %s %o",
                cmnd, srcnam, dstnam, who, flags, temp, &mode);
        logit("REQUESTED", msg);
        strcpy (dstnam, munge_filename(dstnam));  /* Translate to local name */
        strcpy (temp, temp_filename(dstnam));     /* Create a handy temp file */

        /* FIXME: deal with file modes now that we fopen. */
        fddsk = fopen(temp, "wb" /*, mode|0600 */);
        if (fddsk == NULL) {
                /* 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);
                }
                logit("REQUEST", "FAILED -- TEMP FILE");
                if (yesno('S', 0, 4))
                        return FAIL;
                return SUCCESS;
        }

        /* FIXME: Are the above permissions right?? */
        /* FIXME: Should we create directories for the file? */
        if (yesno('S',1, 0))    /* Say yes */
                return 1;

        return receive_file(fddsk, temp, dstnam, srcnam);
}

/*
 * Master wants to Recieve a file from us -- we send it.
 * Return 1 to abort the call, 0 to continue.
 */
host_receive_file(msg)
char  *msg;
{
        FILE *fddsk;     /* Disk file descriptor */
        int x;
        char    cmnd[1];                /* Command character */

        logit("REQUESTED", msg);

        sscanf(msg,"%s %s %s",cmnd,srcnam,dstnam);
        strcpy (temp, munge_filename(srcnam));

        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);
                }
                logit("DENIED", "CAN'T OPEN");
                if (yesno('R', 0, 2))
                        return 1;
                return 0;
        }


        if (yesno('R',1, 0))    /* Say yes */
                return 1;

        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;
}

/*
 * We, as master, 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 (see Protocol.doc) and we need finer control here.
 */
int
local_send_file(workstr)
        char *workstr;
{
        char buf[MAXMSGLEN];    /* Used for both xmit and receive */
        FILE *fddsk;     /* Disk file descriptor */
        int res, status;        /* Result and file removal status */

        /* 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);

        logit("REQUEST", buf);

        if (strchr(flags, 'c')) {
                strcpy(temp, munge_filename(srcnam));
        } else {
                strcpy(temp, munge_filename(temp));
        }
        fddsk = fopen(temp, "rb");
        if (fddsk == NULL) {
                /* FIXME -- handle queued request for nonexistent file */
                if (debug > 0)
                        printf("Can't open file %s (%s), errno=%d\n",
                                temp, srcnam, errno);
                logit("NOT FOUND", temp);
                return COPYFAIL;        /* FIXME caller won't deal with this */
        }

        /* Tell the other side we want to send this file */
        if (wrmsg('S', buf+1) != SUCCESS) {
                DEBUG(0, "problem sending request\n", 0);
                return FAIL;
        }

        /* See what they have to say about it */
        if (rdmsg(buf) != SUCCESS)
                return FAIL;
        if ((buf[0] != 'S') || (buf[1] != 'Y')) {
                logit("REQUEST DENIED", buf);
                return FAIL;
        }
        res = send_file(fddsk); /* FAIL, SUCCESS, or COPYFAIL */

        /* Delete the source file if it was just a copy */
        if (res != SUCCESS)
                return res;
        if (strchr(flags, 'c'))         /* If copied direct from source */
                return res;             /* ...just return. */
        status = remove(temp);          /* Delete uucp's copy of the file */
        if (status != 0) {
                logit("CAN'T REMOVE SENT FILE", temp);
                DEBUG(0, "Can't remove, errno %d\n", errno);
        } else {
                DEBUG(4, "Removed sent file %s\n", temp);
        }
        return res;
}

/*
 * We want to Receive a file -- so we ask for it.
 * Return 1 to abort the call, 0 to continue.
 */
int
local_receive_file()
{
        char buf[MAXMSGLEN];
        FILE *fddsk;                    /* Disk file pointer */

        /* 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);

        strcpy (dstnam, munge_filename(dstnam));  /* Translate to local name */
        strcpy (temp, temp_filename(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? */
        fddsk = fopen(temp, "wb" /*, mode|060 */);

        if (fddsk == NULL) {
                /* 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);
                }
                logit("REQUEST", "FAILED -- TEMPFILE");
                return FAIL;
        }

        logit("REQUEST", buf);
        if (wrmsg('R', buf+1) != SUCCESS) {
                printf("uucico: problem sending request\n");
                return FAIL;
        }

        /* See what the other side has to say about it */
        if (rdmsg(buf) != SUCCESS)
                return FAIL;
        if ((buf[0] != 'R') || (buf[1] != 'Y')) {
                logit("REQUEST DENIED", buf);
                return SUCCESS; /* FIXME, should do something more here */
        }

        return receive_file(fddsk, temp, dstnam, srcnam);
        /* FIXME - We should deal with files that didn't get there */
}

/* general file receive routine */
int
receive_file(fddsk, temp, dstnam, srcnam)
        FILE *fddsk;                    /* Disk file pointer */
        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 loc */
        /* FIXME:  This needs to be able to copy the file, if
           a simple rename does not suffice. */
        /* FIXME:  should create directories if necessary, e.g. D.
           or subdirs of /usr/spool/uucppublic. */
        /* FIXME:  should use source name if target is a directory e.g. ~/ */
        status = rename(temp, dstnam);
        if (status != 0) {
                error++;
                if (debug > 0) {
                        printf("Cannot rename file %s to %s, errno=%d\n",
                                temp, dstnam, errno);
                }
        }

        logit("COPY", error? "FAILED": "SUCCEEDED");
        if (yesno('C', error == 0, 5))  /* Send yes or no */
                return FAIL;
        return SUCCESS;
}

/*
 * general file send routine
 * Return SUCCESS, FAIL, or COPYFAIL.
 */
int
send_file(fddsk)
        FILE *fddsk;     /* Disk file pointer */
{
        char ansbuf[MAXMSGLEN];

        if (wrdata(fddsk) != SUCCESS)
                return COPYFAIL;
        (void) fclose(fddsk);
        /* Await the "CY" or "CNddd" packet, and toss it. */
        while (1) {
                if (rdmsg(ansbuf) != 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') {
                        logit("REQUESTED", ansbuf);
                        return SUCCESS;
                } else {
                        logit("COPY FAILED", ansbuf);
                        return COPYFAIL;
                }
        }
        return COPYFAIL;
}

#ifdef LOG
/*
 * Log file writing subroutine.
 *
 * Makes incredibly ugly log entries that look *just like* Unix uucp's
 * incredibly ugly log entries.
 *
 * Once we don't care about compatability, we should do this much better.
 */
void
logit(one, two)
        char *one, *two;
{
        char logbuf[(NAMESIZE*4)+SLOP+50];      /* Temp buffer for logs */
        long clock;
        struct tm *ut;
        int len;

        if (0 > (logfd = open("LOGFILE", O_CREAT|O_WRONLY|O_APPEND, 0644))) {
                perror("Can't open LOGFILE");
                chdir(path);
                destroy_lock();
                exit(2);
        }

        (void) time(&clock);
        ut = localtime(&clock);

        sprintf(logbuf, "%s %s (%d/%d-%d:%02d:%02d-%d) %s (%s)\n",
                who, host_name,
                ut->tm_mon+1, ut->tm_mday, ut->tm_hour, ut->tm_min,
                ut->tm_sec, ourpid, one, two);

        DEBUG(0, "%s", logbuf);

        len = strlen(logbuf);
        if (len != write(logfd, logbuf, len)) {
                if (debug > 0) {
                        printf("Can't log to logfd, terminating!\n");
                        perror("LOGFILE");
                }
                sigint();               /* Terminate if we can't log */
        }

        close(logfd);
        logfd = 0;

}
#endif
