/* * 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 #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= * * Apparently mac UUCP has a bug that only allows 7 * char host names, and it fails if it gets shere= * where is > 7 chars. * * Apparently some implementations of AmigaUUCP do not accept * an SHere with an =, 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 -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; }