/* * UMODEM -- Implements the "CP/M User's Group XMODEM" protocol and * the TERM II File Transfer Protocol (FTP) Number 1 for * packetized file up/downloading. * * Note: UNIX System-Dependent values are indicated by the string [SD] * in a comment field one the same line as the values. * * -- Lauren Weinstein, 6/81 * -- (Version 2.0) Modified for JHU/UNIX by Richard Conn, 8/1/81 * -- Version 2.1 Mods by Richard Conn, 8/2/81 * . File Size Included on Send Option * -- Version 2.2 Mods by Richard Conn, 8/2/81 * . Log File Generation and Option Incorporated * -- Version 2.3 Mods by Richard Conn, 8/3/81 * . TERM II FTP 1 Supported * . Error Log Reports Enhanced * . CAN Function Added to FTP 3 * . 'd' Option Added to Delete umodem.log File before starting * -- Version 2.4 Mods by Richard Conn, 8/4/81 * . 16K-extent sector number check error corrected * . Count of number of received sectors added * -- Version 2.5 Mods by Richard Conn, 8/5/81 * . ARPA Net Flag added * . ARPA Net parameter ('a') added to command line * . ARPA Net BIS, BIE, BOS, BOE added * . ARPA Net FFH escape added * -- Version 2.6 Mods by Bennett Marks, 8/21/81 (Bucky @ CCA-UNIX) * . mods for UNIX V7 (Note: for JHU compilation define * the variable JHU during 'cc' * . added 'mungmode' flag to protect from inadvertant * overwrite on file receive * . changed timeout handling prior to issuing checksum * -- Version 2.7 Mods by Richard Conn, 8/25/81 (rconn @ BRL) * . correct minor "ifndef" error in which ifndef had no arg * . restructured "ifdef" references so that other versions * of UNIX than Version 7 and JHU can be easily incorporated; * previous ifdef references were for JHU/not JHU; * to compile under Version 7 or JHU UNIX, the following * command lines are recommended: * "cc -7 umodem.c -o umodem -DVER7" for Version 7 * "cc -7 umodem.c -o umodem -DJHU" for JHU * . added 'y' file status display option; this option gives * the user an estimate of the size of the target file to * send from the UNIX system in terms of CP/M records (128 * bytes) and Kbytes (1024 byte units) * . added '7' option which modifies the transmission protocols * for 7 significant bits rather than 8; modifies both FTP 1 * and FTP 3 * -- Version 2.8 mods by Walter Reiher, 3/12/83 (Harvard Chem. Dept.) * . Added definitions needed to run UMODEM under VMS. * Support subroutines are in another file, VMODEM.C. Both * this file and VMODEM.C #include a third file, VMODEM.H, * when being compiled under VMS. All of the additions here * are imbedded in "#ifdef vms" statements EXCEPT the addition * of upper case synonyms for some of the command-line * switches, since VMS changes the command line to upper case. * To compile under VMS, no switches to the compiler are needed. * . Fixed bug in the handling of long files (i.e., >255 sectors). * This file now falls into this category. * . Included code to cause the program to exit when in send * mode and it receives a CAN instead of an ACK from the * receiver. This program, in receive mode, sends a CAN * when it encounters a fatal error. Comment out the * "#define CAN_BOMB" line to disable this. * . Small patches to get it to work under 4.1 BSD VM/UNIX, * as running in the Harvard Science Center, where there is * tighter checking of addresses vs. ints. * Use -DVER7 for VM/UNIX. * . Make text mode transfers accept lone CRs from CP/M (as * opposed to CR/LF pairs) to UNIX. This permits overstriking. * . Added report of estimated file transfer time. * . Removed some more [SD]'s which weren't marked that way; * removed some unnecessary subroutine calls by pushing them * into "JHU" or "VER7", as appropriate. * */ #include stdio #ifdef vms #include ssdef #include tt2def #include ttdef #include "vmodem.h" /* #includes for BOTH JHU and VER7 */ #else #include #include /* Ordering for these two */ #include /* lines is important */ /* JHU UNIX tty parameter file */ #ifdef JHU #include #endif /* Version 7 UNIX tty parameter file */ #ifdef VER7 #include #endif #endif #define VERSION 28 /* Version Number */ #define TRUE 1 #define FALSE 0 #define SOH 001 #define STX 002 #define ETX 003 #define EOT 004 #define ENQ 005 #define ACK 006 #define LF 012 /* Unix LF/NL */ #define CR 015 #define NAK 025 #define SYN 026 #define CAN 030 #define ESC 033 #define CTRLZ 032 /* CP/M EOF for text (usually!) */ #define TIMEOUT -1 #define ERRORMAX 10 /* maximum errors tolerated */ #define RETRYMAX 10 /* maximum retries to be made */ #define BBUFSIZ 128 /* buffer size -- do not change! */ #define CREATMODE 0644 /* mode for created files */ #define CAN_BOMB /* Enable bomb out on receipt of CAN */ /* ARPA Net Constants */ #define IAC 0377 #define DO 0375 #define DONT 0376 #define WILL 0373 #define WONT 0374 #define TRBIN 0 /* JHU UNIX structures */ #ifdef JHU struct sttybuf ttys, ttysnew, ttystemp; /* for stty terminal mode calls */ struct stat statbuf; /* for terminal message on/off control */ #endif /* Version 7 UNIX structures */ #ifdef VER7 struct sgttyb ttys, ttysnew, ttystemp; /* for stty terminal mode calls */ #endif /* VMS structures */ #ifdef vms /* * TT_INFO structures are used for passing information about * the terminal. Used in GTTY and STTY calls. */ struct tt_info ttys, ttysnew, ttystemp; #endif FILE *LOGFP, *fopen(); char buff[BBUFSIZ + 1]; #ifdef JHU int wason; #endif #ifdef VER7 int pagelen; #endif #ifndef vms char *tty, *ttyname(); #endif char XMITTYPE; int ARPA, BIT7, BITMASK, DELFLAG, FTP1, LOGFLAG; int MUNGMODE, PMSG, RECVFLAG, SENDFLAG, STATDISP; int delay; main(argc, argv) int argc; char **argv; { char *logfile; int index; char flag; logfile = "umodem.log"; /* Name of LOG File */ printf("\nUMODEM Version %d.%d", VERSION/10, VERSION%10); printf(" -- UNIX-Based Remote File Transfer Facility\n"); if (argc < 3 || *argv[1] != '-') { printf("\nUsage: \n\tumodem "); #ifndef vms printf("-[rb!rt!sb!st][p][l][1][a][m][d][y][7]"); #else printf("-[rb!rt!sb!st][l][p][y][1][7]"); #endif printf(" filename\n"); printf("\n"); printf("\trb <-- Receive Binary\n"); printf("\trt <-- Receive Text\n"); printf("\tsb <-- Send Binary\n"); printf("\tst <-- Send Text\n"); printf("\tl <-- (ell) Turn OFF LOG File Entries\n"); printf("\tp <-- Turn ON Parameter Display\n"); printf("\ty <-- Display file status (size) information only\n"); printf("\t1 <-- (one) Employ TERM II FTP 1\n"); printf("\t7 <-- Enable 7-bit transfer mask\n"); #ifndef vms printf("\ta <-- Turn ON ARPA Net Flag\n"); printf("\td <-- Delete umodem.log File before starting\n"); printf("\tm <-- Allow file overwiting on receive\n"); #endif printf("\n"); #ifdef vms exit(SS$_NORMAL); #else exit(-1); #endif } /* * Initializations */ index = 1; /* set index for loop */ delay = 3; /* assume FTP 3 delay */ RECVFLAG = FALSE; /* not receive */ SENDFLAG = FALSE; /* not send either */ XMITTYPE = 't'; /* assume text */ FTP1 = FALSE; /* assume FTP 3 (CP/M UG XMODEM2) */ LOGFLAG = TRUE; /* assume log messages */ PMSG = FALSE; /* turn off flags */ STATDISP = FALSE; /* assume not a status display */ BIT7 = FALSE; /* assume 8-bit communication */ ARPA = FALSE; /* assume not on ARPA Net */ DELFLAG = FALSE; /* do NOT delete log file * before starting */ MUNGMODE = FALSE; /* protect files from overwriting */ while ((flag = argv[1][index++]) != '\0') switch (flag) { case '1' : FTP1 = TRUE; /* select FTP 1 */ delay = 5; /* FTP 1 delay constant */ printf("\nUMODEM: TERM II FTP 1 Selected\n"); break; case '7' : BIT7 = TRUE; /* transfer only 7 bits */ break; case 'a' : ARPA = TRUE; /* set ARPA Net */ break; case 'd' : DELFLAG = TRUE; /* delete log file first */ break; case 'L': case 'l' : LOGFLAG = FALSE; /* turn off log report */ break; case 'm' : MUNGMODE = TRUE; /* allow overwriting of files */ break; case 'P': case 'p' : PMSG = TRUE; /* print all messages */ break; case 'R': case 'r' : RECVFLAG = TRUE; /* receive file */ XMITTYPE = gettype(argv[1][index++]); /* get t/b */ break; case 'S': case 's' : SENDFLAG = TRUE; /* send file */ XMITTYPE = gettype(argv[1][index++]); break; case 'Y': case 'y' : STATDISP = TRUE; /* display file status */ break; default : error("Invalid Flag", FALSE); } if (BIT7 && (XMITTYPE == 'b')) { printf("\nUMODEM: Fatal Error -- Both 7-Bit Transfer and "); printf("Binary Transfer Selected"); #ifdef vms exit(SS$_NORMAL); #else exit(-1); #endif } if (BIT7) /* set MASK value */ BITMASK = 0177; /* 7 significant bits */ else BITMASK = 0377; /* 8 significant bits */ if (PMSG) { printf("\nSupported File Transfer Protocols:"); printf("\n\tTERM II FTP 1"); printf("\n\tCP/M UG XMODEM2 (TERM II FTP 3)"); printf("\n\n"); } if (LOGFLAG) { if (!DELFLAG) LOGFP = fopen(logfile, "a"); /* append to LOG file */ else LOGFP = fopen(logfile, "w"); /* new LOG file */ fprintf(LOGFP,"\n\n++++++++\n"); fprintf(LOGFP,"\nUMODEM Version %d.%d\n", VERSION/10, VERSION%10); printf("\nUMODEM: LOG File '%s' is Open\n", logfile); } if (STATDISP) yfile(argv[2]); /* status of a file */ if (RECVFLAG && SENDFLAG) error("Both Send and Receive Functions Specified", FALSE); if (!RECVFLAG && !SENDFLAG) error("Neither Send nor Receive Functions Specified", FALSE); /* * If receiving a file, first check to see if it exists. * If MUNGMODE is off, then abort with error unless we're running * under VMS, where CREAT() makes a file with a new version number. */ if (RECVFLAG) { #ifndef vms if (open(argv[2], 0) != -1) /* If file exists */ { printf("\nUMODEM: Warning -- Target File Exists\n"); if ( MUNGMODE == FALSE ) error("Fatal - Can't overwrite file\n", FALSE); printf("UMODEM: Overwriting Target File\n"); } #endif rfile(argv[2]); /* Receive file */ } else sfile(argv[2]); /* Send file */ } gettype(ichar) char ichar; { switch (ichar) { case 'b': case 'B': case 't': case 'T': return(ichar); default: error("Invalid Send/Receive Parameter - not t or b", FALSE); } return; } /* set tty modes for UMODEM transfers */ setmodes() { /* Device characteristics for JHU UNIX */ #ifdef JHU if (gtty(0, &ttys) < 0) /* get current tty params */ error("Can't get TTY Parameters", TRUE); tty = ttyname(0); /* identify current tty */ /* duplicate current modes in ttysnew structure */ ttysnew.ispeed = ttys.ispeed; /* copy input speed */ ttysnew.ospeed = ttys.ospeed; /* copy output speed */ ttysnew.xflags = ttys.xflags; /* copy JHU/UNIX extended flags */ ttysnew.mode = ttys.mode; /* copy standard terminal flags */ ttysnew.mode |= RAW; /* set for RAW Mode */ /* This ORs in the RAW mode value, thereby setting RAW mode and leaving the other mode settings unchanged */ ttysnew.mode &= ~ECHO; /* set for no echoing */ /* This ANDs in the complement of the ECHO setting (for NO echo), thereby leaving all current parameters unchanged and turning OFF ECHO only */ ttysnew.mode &= ~XTABS; /* set for no tab expansion */ ttysnew.mode &= ~LCASE; /* set for no upper-to-lower case xlate */ ttysnew.mode |= ANYP; /* set for ANY Parity */ ttysnew.mode &= ~NL3; /* turn off ALL delays - new line */ ttysnew.mode &= ~TAB3; /* turn off tab delays */ ttysnew.mode &= ~CR3; /* turn off CR delays */ ttysnew.mode &= ~FF1; /* turn off FF delays */ ttysnew.mode &= ~BS1; /* turn off BS delays */ /* the following are JHU/UNIX xflags settings; they are [SD] */ ttysnew.xflags &= ~PAGE; /* turn off paging */ ttysnew.xflags &= ~STALL; /* turn off ^S/^Q recognition */ ttysnew.xflags &= ~TAPE; /* turn off ^S/^Q input control */ ttysnew.xflags &= ~FOLD; /* turn off CR/LF folding at col 72 */ ttysnew.xflags |= NB8; /* turn on 8-bit input/output */ if (stty(0, &ttysnew) < 0) /* set new params */ error("Can't set new TTY Parameters", TRUE); if (stat(tty, &statbuf) < 0) /* get tty status */ error("Can't get your TTY Status", TRUE); if (statbuf.st_mode&011) /* messages are on [SD] */ { wason = TRUE; if (chmod(tty, 020600) < 0) /* turn off tty messages [SD] */ error("Can't change TTY Mode", TRUE); } else wason = FALSE; /* messages are already off */ #endif /* Device characteristics for Version 7 UNIX */ #ifdef VER7 if (ioctl(0,TIOCGETP,&ttys)<0) /* get tty params [V7] */ error("Can't get TTY Parameters", TRUE); tty = ttyname(0); /* identify current tty */ /* transfer current modes to new structure */ ttysnew.sg_ispeed = ttys.sg_ispeed; /* copy input speed */ ttysnew.sg_ospeed = ttys.sg_ospeed; /* copy output speed */ ttysnew.sg_erase = ttys.sg_erase; /* copy erase flags */ ttysnew.sg_flags = ttys.sg_flags; /* copy flags */ ttysnew.sg_kill = ttys.sg_kill; /* copy std terminal flags */ ttysnew.sg_flags |= RAW; /* set for RAW Mode */ /* This ORs in the RAW mode value, thereby setting RAW mode and leaving the other mode settings unchanged */ ttysnew.sg_flags &= ~ECHO; /* set for no echoing */ /* This ANDs in the complement of the ECHO setting (for NO echo), thereby leaving all current parameters unchanged and turning OFF ECHO only */ ttysnew.sg_flags &= ~XTABS; /* set for no tab expansion */ ttysnew.sg_flags &= ~LCASE; /* set for no upper-to-lower case xlate */ ttysnew.sg_flags |= ANYP; /* set for ANY Parity */ ttysnew.sg_flags &= ~NL3; /* turn off ALL delays - new line */ ttysnew.sg_flags &= ~TAB2; /* turn off tab delays */ ttysnew.sg_flags &= ~CR3; /* turn off CR delays */ ttysnew.sg_flags &= ~FF1; /* turn off FF delays */ ttysnew.sg_flags &= ~BS1; /* turn off BS delays */ ttysnew.sg_flags &= ~TANDEM; /* turn off flow control */ /* make sure page mode is off */ /* ioctl(0,TIOCSSCR,&pagelen); This is [SD]! */ /* set new paramters */ if (ioctl(0,TIOCSETP,&ttysnew) < 0) error("Can't set new TTY Parameters", TRUE); #endif /* Device characteristics for VMS */ #ifdef vms int *iptr, parameters; /* * Get current terminal parameters. */ if (gtty(&ttys) != SS$_NORMAL) error("SETMODES: error return from GTTY (1)", FALSE); if (gtty(&ttysnew) != SS$_NORMAL) error("SETMODES: error return from GTTY (2)", FALSE); /* * Set new terminal parameters. * Note that there are three bytes of terminal characteristics, * so we should make sure the fourth byte of the integer is unchanged. */ iptr = &(ttysnew.dev_characteristics.bcharacteristics); parameters = *iptr; parameters &= ~TT$M_ESCAPE; /* ESCAPE OFF */ parameters &= ~TT$M_HOSTSYNC; /* HOSTSYNC OFF */ parameters |= TT$M_NOECHO; /* NOECHO ON */ parameters |= TT$M_PASSALL; /* PASSALL ON */ parameters &= ~TT$M_READSYNC; /* READSYNC OFF */ parameters &= ~TT$M_TTSYNC; /* TTSYNC OFF */ parameters &= ~TT$M_WRAP; /* WRAP OFF */ if (! BIT7) parameters |= TT$M_EIGHTBIT; /* EIGHTBIT ON */ *iptr = parameters; if (stty(&ttysnew) != SS$_NORMAL) error("SETMODES: error return from STTY", TRUE); #endif if (PMSG) { printf("\nUMODEM: TTY Device Parameters Altered"); ttyparams(); /* print tty params */ } if (ARPA) /* set 8-bit on ARPA Net */ setarpa(); return; } /* set ARPA Net for 8-bit transfers */ setarpa() { sendbyte(IAC); /* set B O S (Binary Output Start) */ sendbyte(WILL); sendbyte(TRBIN); sendbyte(IAC); /* set B I S (Binary Input Start) */ sendbyte(DO); sendbyte(TRBIN); sleep(3); /* wait for TIP to configure */ return; } /* restore normal tty modes */ restoremodes(errcall) int errcall; { if (ARPA) /* if ARPA Net, reconfigure */ resetarpa(); /* Device characteristic restoration for JHU UNIX */ #ifdef JHU if (wason) /* if messages were on originally */ if (chmod(tty, 020611) < 0) /* [SD] */ error("Can't change TTY Mode", FALSE); if (stty(0, &ttys) < 0) /* restore original tty modes */ { if (!errcall) error("RESET - Can't restore normal TTY Params", FALSE); else { printf("UMODEM: "); printf("RESET - Can't restore normal TTY Params\n"); } } #endif /* Device characteristic restoration for Version 7 UNIX */ #ifdef VER7 if (ioctl(0,TIOCSETP,&ttys) < 0) { if (!errcall) error("RESET - Can't restore normal TTY Params", FALSE); else { printf("UMODEM: "); printf("RESET - Can't restore normal TTY Params\n"); } } #endif /* Device characteristic restoration for VMS */ #ifdef vms if (stty(&ttys) != SS$_NORMAL) /* Restore original modes */ { if (!errcall) error("Error restoring original terminal params.", FALSE); else { printf("UMODEM/RESTOREMODES: "); printf("Error restoring original terminal params.\n"); } } #endif if (PMSG) { printf("\nUMODEM: TTY Device Parameters Restored"); ttyparams(); /* print tty params */ } return; } /* reset the ARPA Net */ resetarpa() { sendbyte(IAC); /* send B O E (Binary Output End) */ sendbyte(WONT); sendbyte(TRBIN); sendbyte(IAC); /* send B I E (Binary Input End) */ sendbyte(DONT); sendbyte(TRBIN); return; } /* print error message and exit; if mode == TRUE, restore normal tty modes */ error(msg, mode) char *msg; int mode; { if (mode) restoremodes(TRUE); /* put back normal tty modes */ printf("UMODEM: %s\n", msg); if (LOGFLAG) { fprintf(LOGFP, "UMODEM Fatal Error: %s\n", msg); fclose(LOGFP); } #ifdef vms exit(SS$_NORMAL); #else exit(-1); #endif } /** print status (size) of a file **/ yfile(name) char *name; { printf("UMODEM File Status Display for %s\n", name); if (LOGFLAG) fprintf(LOGFP,"UMODEM File Status Display for %s\n", name); if (open(name,0) < 0) { printf("File %s does not exist\n", name); if (LOGFLAG) fprintf(LOGFP,"File %s does not exist\n", name); #ifdef vms exit(SS$_NORMAL); #else exit(-1); #endif } prfilestat(name); /* print status */ printf("\n"); if (LOGFLAG) { fprintf(LOGFP,"\n"); fclose(LOGFP); } #ifdef vms exit(SS$_NORMAL); #else exit(0); #endif } /** receive a file **/ rfile(name) char *name; { char mode; int fd, j, firstchar, sectnum, sectcurr, tmode; int sectcomp, errors, errorflag, recfin; register int bufctr, checksum; register int c; int errorchar, fatalerror, startstx, inchecksum, endetx, endenq; long recvsectcnt; int lowlim, nlflag; #ifdef vms char inbuf[BBUFSIZ + 7]; #endif mode = XMITTYPE; /* set t/b mode */ if ((fd = creat(name, CREATMODE)) < 0) error("Can't create file for receive", FALSE); printf("\r\nUMODEM: File Name: %s", name); if (LOGFLAG) { fprintf(LOGFP, "\n----\nUMODEM Receive Function\n"); fprintf(LOGFP, "File Name: %s\n", name); if (FTP1) fprintf(LOGFP, "TERM II File Transfer Protocol 1 Selected\n"); else fprintf(LOGFP, "TERM II File Transfer Protocol 3 (CP/M UG) Selected\n"); if (BIT7) fprintf(LOGFP, "7-Bit Transmission Enabled\n"); else fprintf(LOGFP, "8-Bit Transmission Enabled\n"); } printf("\r\nUMODEM: "); if (BIT7) printf("7-Bit"); else printf("8-Bit"); printf(" Transmission Enabled"); printf("\r\nUMODEM: Ready to RECEIVE File\r\n"); setmodes(); /* setup tty modes for xfer */ recfin = FALSE; sectnum = errors = 0; fatalerror = FALSE; /* NO fatal errors */ recvsectcnt = 0; /* number of received sectors */ if (mode == 't' || mode == 'T') tmode = TRUE; else tmode = FALSE; if (FTP1) { while (readbyte(4) != SYN); sendbyte(ACK); /* FTP 1 Sync */ } else sendbyte(NAK); /* FTP 3 Sync */ nlflag = FALSE; do { errorflag = FALSE; do { firstchar = readbyte(6); } while ((firstchar != SOH) && (firstchar != EOT) && (firstchar != TIMEOUT)); if (firstchar == TIMEOUT) { if (LOGFLAG) fprintf(LOGFP, "Timeout on Sector %d\n", sectnum); errorflag = TRUE; } if (firstchar == SOH) { #ifdef vms /* * Under VMS, read the whole block at once. */ if (FTP1) { raw_read(BBUFSIZ + 7, inbuf, 5 + delay * (BBUFSIZ + 6)); sectcurr = inbuf[1] & BITMASK; sectcomp = inbuf[2] & BITMASK; startstx = inbuf[3] & BITMASK; } else { raw_read(BBUFSIZ + 3, inbuf, delay * (BBUFSIZ + 3)); sectcurr = inbuf[0] & BITMASK; sectcomp = inbuf[1] & BITMASK; } #else if (FTP1) readbyte(5); /* discard leading zero */ sectcurr = readbyte(delay); sectcomp = readbyte(delay); if (FTP1) startstx = readbyte(delay); /* get leading STX */ #endif if ((sectcurr + sectcomp) == BITMASK) { if (sectcurr == ((sectnum + 1) % 256) & BITMASK) { checksum = 0; #ifdef vms if (FTP1) lowlim = 4; else lowlim = 2; #else lowlim = 0; #endif bufctr = 0; for (j = lowlim; j < lowlim + BBUFSIZ; j++) { #ifdef vms buff[bufctr] = c = inbuf[j] & BITMASK; #else buff[bufctr] = c = readbyte(delay); #endif checksum = (checksum+c)&BITMASK; if (!tmode) /* binary mode */ { bufctr++; continue; } /* * Translate CP/M's CR/LF into UNIX's LF, but don't do it by * simply ignoring a CR from CP/M--it may indicate an * overstruck line! */ if (nlflag) { /* * Last packet ended with a CR. If the first character of this * packet ISN'T a LF, then be sure to send the CR. */ nlflag = FALSE; if (c != LF) { buff[bufctr++] = CR; buff[bufctr] = c; } } if (j == lowlim + BBUFSIZ - 1 && c == CR) { /* * Last character in the packet is a CR. Don't send it, but * turn on NLFLAG to make sure we check the first character in * the next packet to see if it's a LF. */ nlflag = TRUE; continue; } if (c == LF && bufctr && buff[bufctr - 1] == CR) { /* * We have a CR/LF pair. Discard the CR. */ buff[bufctr - 1] = LF; continue; } if (c == CTRLZ) /* skip CP/M EOF char */ { recfin = TRUE; /* flag EOF */ continue; } if (!recfin) bufctr++; } #ifdef vms if (FTP1) { /* Ending ETX */ endetx = inbuf[lowlim + BBUFSIZ] & BITMASK; /* Checksum */ inchecksum = inbuf[lowlim + BBUFSIZ + 1] & BITMASK; /* ENQ */ endenq = inbuf[lowlim + BBUFSIZ + 2] & BITMASK; } else /* Checksum */ inchecksum = inbuf[lowlim + BBUFSIZ] & BITMASK; #else if (FTP1) endetx = readbyte(delay); /* get ending ETX */ inchecksum = readbyte(delay); /* get checksum */ if (FTP1) endenq = readbyte(delay); /* get ENQ */ #endif if (checksum == inchecksum) /* good checksum */ { errors = 0; recvsectcnt++; sectnum = sectcurr; /* update sector counter */ if (write(fd, buff, bufctr) < 0) error("File Write Error", TRUE); else { if (FTP1) sendbyte(ESC); /* FTP 1 requires */ sendbyte(ACK); } } else { if (LOGFLAG) fprintf(LOGFP, "Checksum Error on Sector %d\n", sectnum); errorflag = TRUE; } } else { if (sectcurr == (sectnum % 256) & BITMASK) { while(readbyte(3) != TIMEOUT); if (FTP1) sendbyte(ESC); /* FTP 1 requires */ sendbyte(ACK); } else { if (LOGFLAG) { fprintf(LOGFP, "Phase Error--received sector is %d ", sectcurr); fprintf(LOGFP, "while expected sector is %d (%d)\n", ((sectnum + 1) % 256) & BITMASK, sectnum); } errorflag = TRUE; fatalerror = TRUE; if (FTP1) sendbyte(ESC); /* FTP 1 requires */ sendbyte(CAN); } } } else { if (LOGFLAG) fprintf(LOGFP, "Header Sector Number Error on Sector %d\n", sectnum); errorflag = TRUE; } } if (FTP1 && !errorflag) { if (startstx != STX) { errorflag = TRUE; /* FTP 1 STX missing */ errorchar = STX; } if (endetx != ETX) { errorflag = TRUE; /* FTP 1 ETX missing */ errorchar = ETX; } if (endenq != ENQ) { errorflag = TRUE; /* FTP 1 ENQ missing */ errorchar = ENQ; } if (errorflag && LOGFLAG) { fprintf(LOGFP, "Invalid Packet-Control Character: "); switch (errorchar) { case STX : fprintf(LOGFP, "STX"); break; case ETX : fprintf(LOGFP, "ETX"); break; case ENQ : fprintf(LOGFP, "ENQ"); break; default : fprintf(LOGFP, "Error"); break; } fprintf(LOGFP, "\n"); } } if (errorflag == TRUE) { errors++; while (readbyte(3) != TIMEOUT); sendbyte(NAK); } } while ((firstchar != EOT) && (errors != ERRORMAX) && !fatalerror); if ((firstchar == EOT) && (errors < ERRORMAX)) { if (!FTP1) sendbyte(ACK); close(fd); restoremodes(FALSE); /* restore normal tty modes */ if (FTP1) while (readbyte(3) != TIMEOUT); /* flush EOT's */ sleep(3); /* give other side time to return to terminal mode */ if (LOGFLAG) { fprintf(LOGFP, "\nReceive Complete\n"); fprintf(LOGFP,"Number of Received CP/M Records is %ld\n", recvsectcnt); fclose(LOGFP); } printf("\n"); #ifdef vms exit(SS$_NORMAL); #else exit(0); #endif } else { if (LOGFLAG && FTP1 && fatalerror) fprintf(LOGFP, "Synchronization Error"); if (errors == ERRORMAX) error("Too many errors", TRUE); if (fatalerror) error("Fatal error", TRUE); } } /** send a file **/ sfile(name) char *name; { char mode; int fd, charval, attempts; int nlflag, sendfin, tmode; register int bufctr, checksum, sectnum; char c; int sendresp; /* response char to sent block */ mode = XMITTYPE; /* set t/b mode */ if ((fd = open(name, 0)) < 0) { if (LOGFLAG) fprintf(LOGFP, "Can't Open File\n"); error("Can't open file for send", FALSE); } printf("\r\nUMODEM: File Name: %s", name); if (LOGFLAG) { fprintf(LOGFP, "\n----\nUMODEM Send Function\n"); fprintf(LOGFP, "File Name: %s\n", name); } prfilestat(name); /* print file size statistics */ if (LOGFLAG) { if (FTP1) fprintf(LOGFP, "TERM II File Transfer Protocol 1 Selected\n"); else fprintf(LOGFP, "TERM II File Transfer Protocol 3 (CP/M UG) Selected\n"); if (BIT7) fprintf(LOGFP, "7-Bit Transmission Enabled\n"); else fprintf(LOGFP, "8-Bit Transmission Enabled\n"); } printf("\r\nUMODEM: "); if (BIT7) printf("7-Bit"); else printf("8-Bit"); printf(" Transmission Enabled"); printf("\r\nUMODEM: Ready to SEND File\r\n"); setmodes(); /* setup tty modes for xfer */ if (mode == 't' || mode == 'T') tmode = TRUE; else tmode = FALSE; sendfin = nlflag = FALSE; attempts = 0; if (FTP1) { sendbyte(SYN); /* FTP 1 Synchronize with Receiver */ while (readbyte(5) != ACK) { if(++attempts > RETRYMAX*6) error("Remote System Not Responding", TRUE); sendbyte(SYN); } } else { while (readbyte(30) != NAK) /* FTP 3 Synchronize with Receiver */ if (++attempts > RETRYMAX) error("Remote System Not Responding", TRUE); } sectnum = 1; /* first sector number */ attempts = 0; do { for (bufctr=0; bufctr < BBUFSIZ;) { if (nlflag) { buff[bufctr++] = LF; /* leftover newline */ nlflag = FALSE; } if ((charval = read(fd, &c, 1)) < 0) error("File Read Error", TRUE); if (charval == 0) /* EOF for read */ { sendfin = TRUE; /* this is the last sector */ if (tmode) buff[bufctr++] = CTRLZ; /* Control-Z for CP/M EOF */ else bufctr++; continue; } if (tmode && c == LF) /* text mode & Unix newline? */ { if (c == LF) /* Unix newline? */ { buff[bufctr++] = CR; /* insert carriage return */ if (bufctr < BBUFSIZ) buff[bufctr++] = LF; /* insert Unix newline */ else nlflag = TRUE; /* insert newline on next sector */ } continue; } buff[bufctr++] = c; /* copy the char without change */ } attempts = 0; do { sendbyte(SOH); /* send start of packet header */ if (FTP1) sendbyte(0); /* FTP 1 Type 0 Packet */ sendbyte(sectnum); /* send current sector number */ sendbyte(-sectnum-1); /* and its complement */ if (FTP1) sendbyte(STX); /* send STX */ checksum = 0; /* init checksum */ for (bufctr=0; bufctr < BBUFSIZ; bufctr++) { sendbyte(buff[bufctr]); /* send the byte */ if (ARPA && (buff[bufctr]==0xff)) /* ARPA Net FFH esc */ sendbyte(buff[bufctr]); /* send 2 FFH's for one */ checksum = (checksum+buff[bufctr])&BITMASK; } /* while (readbyte(3) != TIMEOUT); flush chars from line */ if (FTP1) sendbyte(ETX); /* send ETX */ sendbyte(checksum); /* send the checksum */ if (FTP1) sendbyte(ENQ); /* send ENQ */ attempts++; if (FTP1) { sendresp = NAK; /* prepare for NAK */ if (readbyte(10) == ESC) sendresp = readbyte(10); } else sendresp = readbyte(10); /* get response */ if ((sendresp != ACK) && LOGFLAG) { fprintf(LOGFP, "Non-ACK Received on Sector %d\n", sectnum); if (sendresp == TIMEOUT) fprintf(LOGFP, "This non-ACK was a TIMEOUT\n"); else if (sendresp == NAK) fprintf(LOGFP, "This non-ACK was a NAK\n"); } if (sendresp == CAN) { if (LOGFLAG) fprintf(LOGFP, "This non-ACK was a CAN\n"); #ifdef CAN_BOMB if (LOGFLAG) fprintf(LOGFP, "Exiting: got a CAN from receiver.\n"); close(fd); restoremodes(TRUE); sleep(5); /* Allow other side to recover */ printf("Exiting: got a CAN from receiver.\n\n"); #ifdef vms exit(SS$_NORMAL); #else exit(-1); #endif #endif } } while((sendresp != ACK) && (attempts != RETRYMAX)); sectnum++; /* increment to next sector number */ } while (!sendfin && (attempts != RETRYMAX)); if (attempts == RETRYMAX) error("Remote System Not Responding", TRUE); attempts = 0; if (FTP1) while (attempts++ < 10) sendbyte(EOT); else { sendbyte(EOT); /* send 1st EOT */ while ((readbyte(15) != ACK) && (attempts++ < RETRYMAX)) sendbyte(EOT); if (attempts >= RETRYMAX) error("Remote System Not Responding on Completion", TRUE); } close(fd); restoremodes(FALSE); sleep(5); /* give other side time to return to terminal mode */ if (LOGFLAG) { fprintf(LOGFP, "\nSend Complete\n"); fclose(LOGFP); } printf("\n"); #ifdef vms exit(SS$_NORMAL); #else exit(0); #endif } /* print file size status information */ prfilestat(name) char *name; { long bytes, Kbytes, records; #ifdef vms bytes = filestat(name); /* Gets file length */ if (bytes < 0) { printf("PRFILESTAT: error return from FILESTAT\n"); return; } #else struct stat filestatbuf; /* File status info */ stat(name, &filestatbuf); /* Get file status bytes */ bytes = filestatbuf.st_size; #endif Kbytes = (bytes / 1024) + 1; records = (bytes / 128) + 1; printf("\nUMODEM: Estimated File Size %ldK, %ld Records, %ld Bytes", Kbytes, records, bytes); printf("\n Estimated transfer time at 300 baud: "); printf("%ld min, %ld sec.", bytes / (30*60), ((bytes % (30*60)) / 30) + 1); if (LOGFLAG) fprintf(LOGFP, "Estimated File Size %ldK, %ld Records, %ld Bytes\n", Kbytes, records, bytes); return; } /* get a byte from data stream -- timeout if "seconds" elapses */ /* NOTE, however, that this function returns an INT, not a BYTE!!! */ readbyte(seconds) unsigned seconds; { int c; #ifdef vms c = raw_read(1, &c, seconds); if (c == SS$_TIMEOUT) return(TIMEOUT); #else int alarmfunc(); signal(SIGALRM,alarmfunc); /* catch alarms */ alarm(seconds); /* set the alarm clock */ if (read(0, &c, 1) < 0) /* get a char; error means we timed out */ { return(TIMEOUT); } alarm(0); /* turn off the alarm */ #endif return(c & BITMASK); /* return the char */ } /* send a byte to data stream */ sendbyte(data) char data; { char dataout; dataout = data & BITMASK; /* Mask for 7 or 8 bits */ #ifdef vms raw_write(dataout); #else write(1, &dataout, 1); /* write the byte */ #endif return; } #ifndef vms /* function for alarm clock timeouts */ alarmfunc() { return; /* this is basically a dummy function to force error */ /* status return on the "read" call in "readbyte" */ } #endif /* print data on TTY setting */ ttyparams() { /* For VMS, report that no information is available and return */ #ifdef vms printf("\nUMODEM/TTYPARAMS: "); printf("TT device parameter display not implemented under VMS.\n"); #else /* Obtain TTY parameters for JHU UNIX */ #ifdef JHU gtty(0, &ttystemp); /* get current tty params */ #endif /* Obtain TTY parameters for Version 7 UNIX */ #ifdef VER7 ioctl(0,TIOCGETP,&ttystemp); #endif printf("\r\n\nTTY Device Parameter Display\r\n"); tty = ttyname(0); /* get name of tty */ printf("\tTTY Device Name is %s\r\n\n", tty); printf("\tAny Parity Allowed "); pryn(ANYP); printf("\tEven Parity Allowed"); pryn(EVENP); printf("\tOdd Parity Allowed "); pryn(ODDP); printf("\tLower Case Map "); pryn(LCASE); printf("\tTabs Expanded "); pryn(XTABS); printf("\tCR Mode Enabled "); pryn(CRMOD); printf("\tEcho Enabled "); pryn(ECHO); printf("\tRAW Mode Enabled "); pryn(RAW); /* Print extended terminal characteristics for JHU UNIX */ #ifdef JHU stat(tty, &statbuf); /* get more tty params */ printf("\tBinary Mode Enabled"); pryn1(NB8); printf("\tCR/LF in Col 72 "); pryn1(FOLD); printf("\tRecognize ^S/^Q "); pryn1(STALL); printf("\tSend ^S/^Q "); pryn1(TAPE); printf("\tTerminal can BS "); pryn1(SCOPE); printf("\r\n"); /* New line to separate topics */ printf("\tTerminal Paging is "); pryn1(PAGE); if (ttystemp.xflags&PAGE) printf("\t Lines/Page is %d\r\n", ttystemp.xflags&PAGE); printf("\r\n"); /* New line to separate topics */ printf("\tTTY Input Rate : "); prbaud(ttystemp.ispeed); /* print baud rate */ printf("\tTTY Output Rate : "); prbaud(ttystemp.ospeed); /* print output baud rate */ printf("\r\n"); /* New line to separate topics */ printf("\tMessages Enabled "); if (statbuf.st_mode&011) printf(": Yes\r\n"); else printf(": No\r\n"); #endif /* Print extended characteristics for Version 7 UNIX */ #ifdef VER7 printf("\tTTY Input Rate : "); prbaud(ttystemp.sg_ispeed); printf("\tTTY Output Rate : "); prbaud(ttystemp.sg_ospeed); /* print output baud rate */ #endif #endif /* END #ifdef vms...#else... */ } #ifndef vms pryn(iflag) int iflag; { /* JHU UNIX flag test */ #ifdef JHU if (ttystemp.mode&iflag) /* Version 7 UNIX and VMS flag test */ #else if (ttystemp.sg_flags&iflag) #endif printf(": Yes\r\n"); else printf(": No\r\n"); } #endif /* Extended flag test for JHU UNIX only */ #ifdef JHU pryn1(iflag) int iflag; { if (ttystemp.xflags&iflag) printf(": Yes\r\n"); else printf(": No\r\n"); } #endif #ifndef vms prbaud(speed) char speed; { switch (speed) { /* JHU UNIX speed flag cases */ #ifdef JHU case B0050 : printf("50"); break; case B0075 : printf("75"); break; case B0110 : printf("110"); break; case B0134 : printf("134.5"); break; case B0150 : printf("150"); break; case B0200 : printf("200"); break; case B0300 : printf("300"); break; case B0600 : printf("600"); break; case B1200 : printf("1200"); break; case B1800 : printf("1800"); break; case B2400 : printf("2400"); break; case B4800 : printf("4800"); break; case B9600 : printf("9600"); break; case EXT_A : printf("External A"); break; case EXT_B : printf("External B"); break; #endif /* Version 7 UNIX speed flag cases */ #ifdef VER7 case B50 : printf("50"); break; case B75 : printf("75"); break; case B110 : printf("110"); break; case B134 : printf("134.5"); break; case B150 : printf("150"); break; case B200 : printf("200"); break; case B300 : printf("300"); break; case B600 : printf("600"); break; case B1200 : printf("1200"); break; case B1800 : printf("1800"); break; case B2400 : printf("2400"); break; case B4800 : printf("4800"); break; case B9600 : printf("9600"); break; case EXTA : printf("External A"); break; case EXTB : printf("External B"); break; #endif default : printf("Error"); break; } printf(" Baud\r\n"); } #endif