#include #include "kermit.h" /* * KERMIT.C * * KERMIT protocol routines * * Adapted from: * * UNIX Kermit, Columbia University, 1981, 1982, 1983 * Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz, Alan Crosswell * * * EXTERNAL INTERFACE: * * INITIALIZE PROTOCOL MODULE (must be called before using protocol) * kerini(); * * SENDING FILES * filnam = "filespec"; sendsw(); * * RECEIVING FILES * filnam = "filespec"; recsw(); * * SERVER COMMANDS * sendcmdsw('char',"cmd") * * The following external routines must be supplied: * * char read_modem () gets char from modem (blocking) * write_modem (char *s, int count) writes string to modem * flushinput () empty modem input buffer * printmsg (char *s) writes message to screen * notify_hook () called when packets leave or arrive to * give a hook for displaying stats on * screen */ #define COUNTED_STRING(s) strlen(s),s /* Global Variables */ int size, /* Size of present data */ rpsiz, /* Maximum receive packet size */ spsiz, /* Maximum send packet size */ pad, /* How much padding to send */ timint, /* Timeout for foreign host on sends */ n, /* Packet number */ numtry, /* Times this packet retried */ oldtry, /* Times previous packet retried */ maxtry, /* Maximum number of times to retry */ packets_sent, /* Packets sent */ packets_received, /* packets received */ bad_packets, /* packets received with checksum errors */ naked_packets, /* packets naked by other end */ remote, /* -1 means we're a remote kermit */ image, /* -1 means 8-bit mode */ binary, /* Nonzero => binary files ("wb" or "rb" fopen) */ quote_8bit, /* Nonzero => 8th-bit quoting */ debug, /* indicates level of debugging output (0=none) */ filnamcnv, /* -1 means do file name case conversions */ filecount; /* Number of files left to send */ long bytes_xferred; char state, /* Present state of the automaton */ padchar, /* Padding character to send */ eol, /* End-Of-Line character to send */ quote_8bit_char, /* character to use for 8th-bit quoting */ quote, /* Quote character in incoming data */ **filelist, /* List of files to be sent */ *filnam, /* Current file name */ recpkt[MAXPACKSIZ], /* Receive packet buffer */ packet[MAXPACKSIZ]; /* Packet buffer */ FILE *fp; /* File pointer for current disk file */ jmp_buf abort_env, /* Transaction abort SETJMP buffer */ env; /* Timeout SETJMP buffer */ /* * k e r i n i * * Init protocol routines * */ kerini() { eol = CR; /* EOL for outgoing packets */ quote = MYQUOTE; /* Standard control-quote char "#" */ quote_8bit_char = MY8BITQUOTE; /* Standard 8th-bit quote char */ pad = 0; /* No padding */ binary = TRUE; /* Default is binary files */ quote_8bit = FALSE; /* Default is no 8th-bit quoting */ padchar = NULL; /* Use null if any padding wanted */ fp = NULL; /* File pointer */ timint = DEFTIM; /* Initial timeout value */ maxtry = MAXTRY; /* Initial retry counter value */ #if UCB4X /* Default to 7-bit masking, CRLF */ image = FALSE; /* translation and filename case */ filnamcnv = TRUE; /* conversion for UNIX systems */ #else image = TRUE; /* Default to no processing for */ filnamcnv = FALSE; /* non-UNIX systems */ #endif filecount = 1; /* One file at a time */ } /* * s e n d s w * * Sendsw is the state table switcher for sending files. It loops until * either it finishes, or an error is encountered. The routines called * by sendsw are responsible for changing the state. * */ sendsw() { char sinit(), sfile(), sdata(), seof(), sbreak(); if (setjmp(abort_env)) return(FALSE); if (debug) printf ("Entering sendsw\n"); state = 'S'; /* Send initiate is the start state */ n = 0; /* Initialize message number */ packets_sent = packets_received = 0;/* Initialize statistics */ bad_packets = naked_packets = 0; bytes_xferred = 0L; numtry = 0; /* Say no tries yet */ while(TRUE) /* Do this as long as necessary */ { if (debug) printf("sendsw state: %c\n",state); switch(state) { case 'S': state = sinit(); break; /* Send-Init */ case 'F': state = sfile(); break; /* Send-File */ case 'D': state = sdata(); break; /* Send-Data */ case 'Z': state = seof(); break; /* Send-End-of-File */ case 'B': state = sbreak(); break; /* Send-Break */ case 'C': return (TRUE); /* Complete */ case 'A': if (fp != NULL) fclose(fp); fp = NULL; return (FALSE); /* "Abort" */ default: if (fp != NULL) fclose(fp); fp = NULL; return (FALSE); /* Unknown, fail */ } } } /* * s i n i t * * Send Initiate: send this host's parameters and get other side's back. */ char sinit() { int num, len; /* Packet number, length */ if (numtry++ > maxtry) return('A'); /* If too many tries, give up */ spar(packet); /* Fill up init info packet */ spack('S',n,7,packet); /* Send my init info */ switch(rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'N': naked_packets++; /* count NAKs */ notify_hook (); /* maybe diddle screen */ return(state); /* NAK, try it again */ case 'Y': /* ACK */ if (n != num) /* If wrong ACK, stay in S state */ return(state); /* and try again */ rpar(recpkt); /* Get other side's init info */ if (eol == 0) eol = '\n'; /* Check and set defaults */ if (quote == 0) quote = MYQUOTE; numtry = 0; /* Reset try counter */ n = (n+1)%64; /* Bump packet count */ return('F'); /* OK, switch state to F */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: return(state); /* Receive failure, try again */ default: return('A'); /* Anything else, just "abort" */ } } /* * s f i l e * * Send File Header. */ char sfile() { int num, len; /* Packet number, length */ char filnam1[50], /* Converted file name */ *newfilnam, /* Pointer to file name to send */ *cp; /* char pointer */ if (numtry++ > maxtry) return('A'); /* If too many tries, give up */ if (fp == NULL) /* If not already open, */ { if (debug) printf(" Opening %s for sending.\n",filnam); if (binary) fp = fopen(filnam,"rb"); /* open the file to be sent */ else fp = fopen(filnam,"r"); if (fp == NULL) /* If bad file pointer, give up */ { error("Cannot open file %s",filnam); return('A'); } } strcpy(filnam1, filnam); /* Copy file name */ newfilnam = cp = filnam1; while (*cp != '\0') /* Strip off all leading directory */ { /* and device names */ char c; if ((c = *cp++) == '\\' || c == ':') newfilnam = cp; }; if (filnamcnv) /* Convert lower case to upper */ for (cp = newfilnam; *cp != '\0'; cp++) if (*cp >= 'a' && *cp <= 'z') *cp ^= 040; len = cp - newfilnam; /* Compute length of new filename */ printmsg("Sending %s as %s",filnam,newfilnam); spack('F',n,len,newfilnam); /* Send an F packet */ switch(rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'N': /* NAK, just stay in this state, */ num = (--num<0 ? 63:num); /* unless it's NAK for next packet */ if (n != num) /* which is just like an ACK for */ { naked_packets++; notify_hook (); return(state); /* this packet so fall thru to... */ }; case 'Y': /* ACK */ if (n != num) return(state); /* If wrong ACK, stay in F state */ numtry = 0; /* Reset try counter */ n = (n+1)%64; /* Bump packet count */ size = bufill(packet); /* Get first data from file */ return('D'); /* Switch state to D */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: return(state); /* Receive failure, stay in F state */ default: return('A'); /* Something else, just "abort" */ } } /* * s d a t a * * Send File Data */ char sdata() { int num, len; /* Packet number, length */ if (numtry++ > maxtry) return('A'); /* If too many tries, give up */ spack('D',n,size,packet); /* Send a D packet */ switch(rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'N': /* NAK, just stay in this state, */ num = (--num<0 ? 63:num); /* unless it's NAK for next packet */ if (n != num) /* which is just like an ACK for */ { naked_packets++; notify_hook (); return(state); /* this packet so fall thru to... */ }; case 'Y': /* ACK */ if (n != num) return(state); /* If wrong ACK, fail */ numtry = 0; /* Reset try counter */ n = (n+1)%64; /* Bump packet count */ if ((size = bufill(packet)) == EOF) /* Get data from file */ return('Z'); /* If EOF set state to that */ return('D'); /* Got data, stay in state D */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: return(state); /* Receive failure, stay in D */ default: return('A'); /* Anything else, "abort" */ } } /* * s e o f * * Send End-Of-File. */ char seof() { int num, len; /* Packet number, length */ if (numtry++ > maxtry) return('A'); /* If too many tries, "abort" */ spack('Z',n,0,packet); /* Send a 'Z' packet */ switch(rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'N': /* NAK, just stay in this state, */ num = (--num<0 ? 63:num); /* unless it's NAK for next packet, */ if (n != num) /* which is just like an ACK for */ { naked_packets++; notify_hook (); return(state); /* this packet so fall thru to... */ }; case 'Y': /* ACK */ if (n != num) return(state); /* If wrong ACK, hold out */ numtry = 0; /* Reset try counter */ n = (n+1)%64; /* and bump packet count */ if (debug) printf(" Closing input file %s, ",filnam); fclose(fp); /* Close the input file */ fp = NULL; /* Set flag indicating no file open */ if (debug) printf("looking for next file...\n"); /* if (gnxtfl() == FALSE) */ /* No more files go? */ return('B'); /* if not, break, EOT, all done */ if (debug) printf(" New file is %s\n",filnam); return('F'); /* More files, switch state to F */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: return(state); /* Receive failure, stay in Z */ default: return('A'); /* Something else, "abort" */ } } /* * s b r e a k * * Send Break (EOT) */ char sbreak() { int num, len; /* Packet number, length */ if (numtry++ > maxtry) return('A'); /* If too many tries "abort" */ spack('B',n,0,packet); /* Send a B packet */ switch (rpack(&len,&num,recpkt)) /* What was the reply? */ { case 'N': /* NAK, just stay in this state, */ num = (--num<0 ? 63:num); /* unless NAK for previous packet, */ if (n != num) /* which is just like an ACK for */ { naked_packets++; notify_hook (); return(state); /* this packet so fall thru to... */ }; case 'Y': /* ACK */ if (n != num) return(state); /* If wrong ACK, fail */ numtry = 0; /* Reset try counter */ n = (n+1)%64; /* and bump packet count */ return('C'); /* Switch state to Complete */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: return(state); /* Receive failure, stay in B */ default: return ('A'); /* Other, "abort" */ } } /* * r e c s w * * This is the state table switcher for receiving files. */ recsw() { char rinit(), rfile(), rdata(); /* Use these procedures */ if (setjmp(abort_env)) return(FALSE); state = 'R'; /* Receive-Init is the start state */ n = 0; /* Initialize message number */ numtry = 0; /* Say no tries yet */ packets_sent = packets_received = 0;/* Initialize statistics */ bad_packets = naked_packets = 0; bytes_xferred = 0L; while(TRUE) { if (debug) printf(" recsw state: %c\n",state); switch(state) /* Do until done */ { case 'R': state = rinit(); break; /* Receive-Init */ case 'F': state = rfile(); break; /* Receive-File */ case 'D': state = rdata(); break; /* Receive-Data */ case 'C': return(TRUE); /* Complete state */ case 'A': if (fp != NULL) fclose(fp); fp = NULL; return(FALSE); /* "Abort" state */ } } } /* * r i n i t * * Receive Initialization */ char rinit() { int len, num; /* Packet length, number */ if (numtry++ > maxtry) return('A'); /* If too many tries, "abort" */ spack('R',n,strlen(filnam),filnam); /* Send "receive init" */ switch(rpack(&len,&num,recpkt)) /* Get a packet */ { case 'S': /* Send-Init */ rpar(recpkt,len); /* Get the other side's init data */ spar(packet); /* Fill up packet with my init info */ spack('Y',n,7,packet); /* ACK with my parameters */ oldtry = numtry; /* Save old try count */ numtry = 0; /* Start a new counter */ n = (n+1)%64; /* Bump packet number, mod 64 */ return('F'); /* Enter File-Receive state */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case 'N': /* NAK */ naked_packets++; notify_hook(); return(state); /* stay in this state */ case FALSE: /* Didn't get packet */ spack('N',n,0,0); /* Return a NAK */ return(state); /* Keep trying */ default: return('A'); /* Some other packet type, "abort" */ } } /* * r f i l e * * Receive File Header */ char rfile() { int num, len; /* Packet number, length */ char filnam1[50]; /* Holds the converted file name */ if (numtry++ > maxtry) return('A'); /* "abort" if too many tries */ switch(rpack(&len,&num,packet)) /* Get a packet */ { case 'S': /* Send-Init, maybe our ACK lost */ if (oldtry++ > maxtry) return('A'); /* If too many tries "abort" */ if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */ { /* Yes, ACK it again with */ spar(packet); /* our Send-Init parameters */ spack('Y',num,7,packet); numtry = 0; /* Reset try counter */ return(state); /* Stay in this state */ } else return('A'); /* Not previous packet, "abort" */ case 'Z': /* End-Of-File */ if (oldtry++ > maxtry) return('A'); if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */ { /* Yes, ACK it again. */ spack('Y',num,0,0); numtry = 0; return(state); /* Stay in this state */ } else return('A'); /* Not previous packet, "abort" */ case 'F': /* File Header (just what we want) */ if (num != n) return('A'); /* The packet number must be right */ strcpy(filnam1, packet); /* Copy the file name */ if (filnamcnv) /* Convert upper case to lower */ for (filnam=filnam1; *filnam != '\0'; filnam++) if (*filnam >= 'A' && *filnam <= 'Z') *filnam |= 040; if (fp != NULL) fclose (fp); if ((fp = fopen(filnam1,"r")) != NULL) { error("File %s already exists",filnam1); spack('E',n,COUNTED_STRING("File already exists")); fclose(fp); fp = NULL; return('A'); }; if (binary) fp = fopen(filnam1,"wb"); /* Try to open a new file */ else fp = fopen(filnam1,"w"); if (fp == NULL) { error("Cannot create %s",filnam1); return('A'); } else { #if MSDOS /* convert to uppercase, 8.3 length name */ int i; char c, *sp; char name[NAMELEN + 1], ext[EXTLEN + 1]; sp = filnam1; i = 0; while ((c = *sp++) && c != '.' && i < NAMELEN) name[i++] = toupper (c); name[i] = 0; if (c && (c != '.')) while ((c = *sp++) && c != '.') ; i = 0; if (c) while ((c = *sp++) && i < EXTLEN) ext[i++] = toupper (c); ext[i] = 0; printmsg("Receiving %s as %s.%s",packet,name,ext); #else printmsg("Receiving %s as %s",packet,filnam1); #endif }; spack('Y',n,0,0); /* Acknowledge the file header */ oldtry = numtry; /* Reset try counters */ numtry = 0; /* ... */ n = (n+1)%64; /* Bump packet number, mod 64 */ return('D'); /* Switch to Data state */ case 'B': /* Break transmission (EOT) */ if (num != n) return ('A'); /* Need right packet number here */ spack('Y',n,0,0); /* Say OK */ return('C'); /* Go to complete state */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: /* Didn't get packet */ spack('N',n,0,0); /* Return a NAK */ return(state); /* Keep trying */ default: return ('A'); /* Some other packet, "abort" */ } } /* * r d a t a * * Receive Data */ char rdata() { int num, len; /* Packet number, length */ if (numtry++ > maxtry) return('A'); /* "abort" if too many tries */ switch(rpack(&len,&num,packet)) /* Get packet */ { case 'D': /* Got Data packet */ if (num != n) /* Right packet? */ { /* No */ if (oldtry++ > maxtry) return('A'); /* If too many tries, abort */ if (num == ((n==0) ? 63:n-1)) /* Else check packet number */ { /* Previous packet again? */ spack('Y',num,6,packet); /* Yes, re-ACK it */ numtry = 0; /* Reset try counter */ return(state); /* Don't write out data! */ }; bad_packets++; notify_hook(); return('A'); /* sorry, wrong number */ } /* Got data with right packet number */ if (! bufemp(packet,len)) { spack('E',n,COUNTED_STRING("Write failed (disk full?)")); error("Write failed (disk full?)"); return('A'); }; spack('Y',n,0,0); /* Acknowledge the packet */ oldtry = numtry; /* Reset the try counters */ numtry = 0; /* ... */ n = (n+1)%64; /* Bump packet number, mod 64 */ return('D'); /* Remain in data state */ case 'F': /* Got a File Header */ if (oldtry++ > maxtry) return('A'); /* If too many tries, "abort" */ if (num == ((n==0) ? 63:n-1)) /* Else check packet number */ { /* It was the previous one */ spack('Y',num,0,0); /* ACK it again */ numtry = 0; /* Reset try counter */ return(state); /* Stay in Data state */ } else return('A'); /* Not previous packet, "abort" */ case 'Z': /* End-Of-File */ if (num != n) return('A'); /* Must have right packet number */ spack('Y',n,0,0); /* OK, ACK it. */ fclose(fp); /* Close the file */ fp = NULL; n = (n+1)%64; /* Bump packet number */ return('F'); /* Go back to Receive File state */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: /* Didn't get packet */ spack('N',n,0,0); /* Return a NAK */ return(state); /* Keep trying */ default: return('A'); /* Some other packet, "abort" */ } } /* * s e n d c m d s w * * This is the state table switcher for sending server commands files. */ sendcmdsw(cmdchr,cmdstr) char cmdchr, *cmdstr; { char resp_init(), resp_data (); if (setjmp(abort_env)) return(FALSE); state = 'R'; /* Response-Init is the start state */ n = 0; /* Initialize message number */ numtry = 0; /* Say no tries yet */ packets_sent = packets_received = 0;/* Initialize statistics */ bad_packets = naked_packets = 0; bytes_xferred = 0L; while(TRUE) { if (debug) printf(" sendcmdsw state: %c\n",state); switch(state) /* Do until done */ { case 'R': state = resp_init(cmdchr, cmdstr); /* Response-Init */ break; case 'D': state = resp_data(); break; /* Response-Data */ case 'C': return(TRUE); /* Complete state */ case 'A': return(FALSE); /* "Abort" state */ } } } /* * r e s p _ i n i t * * Server command response init */ char resp_init(cmdchr,cmdstr) char cmdchr, *cmdstr; { int len, num; /* Packet length, number */ if (numtry++ > maxtry) return('A'); /* If too many tries, "abort" */ spack(cmdchr,n,strlen(cmdstr),cmdstr); /* Send server command */ switch(rpack(&len,&num,recpkt)) /* Get a packet */ { case 'Y': /* ACK (short reply) */ if (n != num) return(state); /* If wrong ACK, fail */ numtry = 0; /* Reset try counter */ n = (n+1)%64; /* Bump packet count */ putmsgc(recpkt,len); /* Type the response */ return('C'); /* Transaction complete */ case 'N': /* NAK */ naked_packets++; notify_hook(); return(state); /* stay in this state */ case 'Z': /* End-Of-File */ if (oldtry++ > maxtry) return('A'); if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */ { /* Yes, ACK it again. */ spack('Y',num,0,0); numtry = 0; return(state); /* Stay in this state */ } else return('A'); /* Not previous packet, "abort" */ case 'F': /* File Header (Long reply) */ if (num != n) return('A'); /* The packet number must be right */ putmsgc(recpkt,len); /* Type "filename", if any */ spack('Y',n,0,0); /* Acknowledge the file header */ oldtry = numtry; /* Reset try counters */ numtry = 0; /* ... */ n = (n+1)%64; /* Bump packet number, mod 64 */ return('D'); /* Switch to Data state */ case 'B': /* Break transmission (EOT) */ if (num != n) return ('A'); /* Need right packet number here */ spack('Y',n,0,0); /* Say OK */ return('C'); /* Go to complete state */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: /* Didn't get packet */ spack('N',n,0,0); /* Return a NAK */ return(state); /* Keep trying */ default: return ('A'); /* Some other packet, "abort" */ } } /* * r e s p _ d a t a * * Receive server command response data (long response) */ char resp_data() { int num, len; /* Packet number, length */ if (numtry++ > maxtry) return('A'); /* "abort" if too many tries */ switch(rpack(&len,&num,packet)) /* Get packet */ { case 'D': /* Got Data packet */ if (num != n) /* Right packet? */ { /* No */ if (oldtry++ > maxtry) return('A'); /* If too many tries, abort */ if (num == ((n==0) ? 63:n-1)) /* Else check packet number */ { /* Previous packet again? */ spack('Y',num,6,packet); /* Yes, re-ACK it */ numtry = 0; /* Reset try counter */ return(state); /* Don't write out data! */ }; bad_packets++; notify_hook(); return('A'); /* sorry, wrong number */ } putmsgc(packet,len); /* show the user */ spack('Y',n,0,0); /* Acknowledge the packet */ oldtry = numtry; /* Reset the try counters */ numtry = 0; /* ... */ n = (n+1)%64; /* Bump packet number, mod 64 */ return('D'); /* Remain in data state */ case 'F': /* Got a File Header */ if (oldtry++ > maxtry) return('A'); /* If too many tries, "abort" */ if (num == ((n==0) ? 63:n-1)) /* Else check packet number */ { /* It was the previous one */ spack('Y',num,0,0); /* ACK it again */ numtry = 0; /* Reset try counter */ return(state); /* Stay in Data state */ } else return('A'); /* Not previous packet, "abort" */ case 'Z': /* End-Of-File */ if (num != n) return('A'); /* Must have right packet number */ spack('Y',n,0,0); /* OK, ACK it. */ n = (n+1)%64; /* Bump packet number */ return('F'); /* Go back to Receive File state */ case 'E': /* Error packet received */ prerrpkt(recpkt); /* Print it out and */ return('A'); /* abort */ case FALSE: /* Didn't get packet */ spack('N',n,0,0); /* Return a NAK */ return(state); /* Keep trying */ default: return('A'); /* Some other packet, "abort" */ } } /* * s p a c k * * Send a Packet */ spack(type,num,len,data) char type, *data; int num, len; { int i; /* Character loop counter */ char chksum, buffer[100]; /* Checksum, packet buffer */ register char *bufp; /* Buffer pointer */ flushinput (); /* Flush pending input */ if (debug) /* Display outgoing packet */ { if (data != NULL) data[len] = '\0'; /* Null-terminate data to print it */ printf(" spack type: %c\n",type); printf(" num: %d\n",num); printf(" len: %d\n",len); if (data != NULL) printf(" data: \"%s\"\n",data); } bufp = buffer; /* Set up buffer pointer */ for (i=1; i<=pad; i++) write_modem(&padchar,1); /* Issue any padding */ *bufp++ = SOH; /* Packet marker, ASCII 1 (SOH) */ *bufp++ = tochar(len+3); /* Send the character count */ chksum = tochar(len+3); /* Initialize the checksum */ *bufp++ = tochar(num); /* Packet number */ chksum += tochar(num); /* Update checksum */ *bufp++ = type; /* Packet type */ chksum += type; /* Update checksum */ for (i=0; i> 6)+chksum)&077; /* Compute final checksum */ *bufp++ = tochar(chksum); /* Put it in the packet */ *bufp++ = eol; /* Extra-packet line terminator */ write_modem(buffer,bufp-buffer); /* Send the packet */ if (type != 'Y' && type != 'N') packets_sent++; /* Count all but ACKs and NAKs */ notify_hook (); /* Give a chance to update display */ } /* * r p a c k * * Read a Packet */ rpack(len,num,data) int *len, *num; /* Packet length, number */ char *data; /* Packet data */ { int i, done; /* Data character number, loop exit */ char t, /* Current input character */ type, /* Packet type */ cchksum, /* Our (computed) checksum */ rchksum; /* Checksum received from other host */ #if UCB4X /* TOPS-20 can't handle timeouts... */ if (setjmp(env)) return FALSE; /* Timed out, fail */ signal(SIGALRM,clkint); /* Setup the timeout */ if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME; alarm(timint); #endif /* UCB4X */ #if RAINBOW if (setjmp(env)) return FALSE; /* Timed out, fail */ if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME; alarm(timint); /* Setup timeout */ #endif while (t != SOH) /* Wait for packet header */ t = read_modem () & 0177; /* Handle parity */ done = FALSE; /* Got SOH, init loop */ while (!done) /* Loop to get a packet */ { t = read_modem (); /* Get a character */ if ((!image) || quote_8bit) t &= 0177; /* Handle parity */ if (t == SOH) continue; /* Resynchronize if SOH */ cchksum = t; /* Start the checksum */ *len = unchar(t)-3; /* Character count */ t = read_modem (); /* Get a character */ if ((!image) || quote_8bit) t &= 0177; /* Handle parity */ if (t == SOH) continue; /* Resynchronize if SOH */ cchksum = cchksum + t; /* Update checksum */ *num = unchar(t); /* Packet number */ t = read_modem (); /* Get a character */ if ((!image) || quote_8bit) t &= 0177; /* Handle parity */ if (t == SOH) continue; /* Resynchronize if SOH */ cchksum = cchksum + t; /* Update checksum */ type = t; /* Packet type */ for (i=0; i<*len; i++) /* The data itself, if any */ { /* Loop for character count */ t = read_modem (); /* Get character */ if ((!image) || quote_8bit) t &= 0177; /* Handle parity */ if (t == SOH) continue; /* Resynch if SOH */ cchksum = cchksum + t; /* Update checksum */ data[i] = t; /* Put it in the data buffer */ } data[*len] = 0; /* Mark the end of the data */ t = read_modem (); /* Get last character (checksum) */ rchksum = unchar(t); /* Convert to numeric */ t = read_modem (); /* get EOL character and toss it */ if ((!image) || quote_8bit) t &= 0177; /* Handle parity */ if (t == SOH) continue; /* Resynchronize if SOH */ done = TRUE; /* Got checksum, done */ } #if (UCB4X | RAINBOW) alarm(0); /* Disable the timer interrupt */ #endif if (debug) /* Display incoming packet */ { if (data != NULL) data[*len] = '\0'; /* Null-terminate data to print it */ printf(" rpack type: %c\n",type); printf(" num: %d\n",*num); printf(" len: %d\n",*len); if (data != NULL) printf(" data: \"%s\"\n",data); } /* Fold in bits 7,8 to compute */ cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */ flushinput (); /* Flush any padding, etc. */ if (type != 'Y' && type != 'N') packets_received++; /* count all but ACKs and NAKs */ notify_hook (); /* maybe update screen */ if (cchksum != rchksum) { bad_packets++; notify_hook (); return(FALSE); }; return(type); /* All OK, return packet type */ } /* * b u f i l l * * Get a bufferful of data from the file that's being sent. * Only control-quoting is done; 8-bit & repeat count prefixes are * not handled. */ bufill(buffer) char buffer[]; /* Buffer */ { int i, /* Loop index */ t, /* Char read from file */ count; /* byte count */ char t7; /* 7-bit version of above */ i = 0; /* Init data buffer pointer */ count = 0; while((t = getc(fp)) != EOF) /* Get the next character */ { count++; t7 = t & 0177; /* Get low order 7 bits */ if (quote_8bit && t == quote_8bit_char) buffer[i++] = quote; if (quote_8bit && (t & 0200)) { buffer[i++] = quote_8bit_char; t = t7; }; if (t7 < SP || t7==DEL || t7==quote) /* Does this char require */ { /* special handling? */ if (t=='\n' && !image) { /* Do LF->CRLF mapping if !image */ buffer[i++] = quote; buffer[i++] = ctl('\r'); } buffer[i++] = quote; /* Quote the character */ if (t7 != quote) { t = ctl(t); /* and uncontrolify */ t7 = ctl(t7); } } if (image) buffer[i++] = t; /* Deposit the character itself */ else buffer[i++] = t7; if (i >= spsiz-8) /* Check length */ { bytes_xferred += (long) count; return(i); }; } bytes_xferred += (long) count; if (i==0) return(EOF); /* Wind up here only on EOF */ return(i); /* Handle partial buffer */ } /* * b u f e m p * * Put data from an incoming packet into a file. */ bufemp(buffer,len) char buffer[]; /* Buffer */ int len; /* Length */ { int i, count; /* Counters */ unsigned char t; /* Character holder */ int q8bit; count = 0; for (i=0; i MAXPACKSIZ) { printmsg ("Truncating remote's MAXPACKSIZ from %d\n", spsiz); spsiz = MAXPACKSIZ; }; if ((i = unchar(data[1])) > timint) { printmsg("Timeout increased to %d seconds at remote's request", i); timint = i; }; pad = unchar(data[2]); /* Number of pads to send */ padchar = ctl(data[3]); /* Padding character to send */ eol = unchar(data[4]); /* EOL character I must send */ quote = data[5]; /* Incoming data quote character */ if (len >= 7) /* If other side mentioned 8th-bit */ switch (data[6]) { case 'N': if (quote_8bit) printmsg("Remote refused 8th-bit quoting"); quote_8bit = FALSE; break; case 'Y': break; default: if ((data[6] < 33 || data[6] > 126) || (data[6] > 62 && data[6] < 96)) { printmsg("Remote requested invalid 8th-bit quote char"); quote_8bit = FALSE; break; }; quote_8bit = TRUE; quote_8bit_char = data[6]; }; } /* * p u t m s g c * * Print counted message */ putmsgc(msg,len) char *msg; int len; { char buf[132]; if (len >= 132) { callers_printmsg("putmsgc - message too long"); return; }; strncpy(buf,msg,len); buf[len] = 0; callers_printmsg(buf); } /* * p r i n t m s g * * Print message on standard output if not remote. */ /*VARARGS1*/ printmsg(fmt, a1, a2, a3, a4, a5) char *fmt; { char msg[132]; if (!remote) { sprintf(msg,fmt,a1,a2,a3,a4,a5); callers_printmsg(msg); } } /* * e r r o r * * Print error message. * * If local, print error message with printmsg. * If remote, send an error packet with the message. */ /*VARARGS1*/ error(fmt, a1, a2, a3, a4, a5) char *fmt; { char msg[80]; int len; if (remote) { sprintf(msg,fmt,a1,a2,a3,a4,a5); /* Make it a string */ len = strlen(msg); spack('E',n,len,msg); /* Send the error packet */ } else printmsg(fmt, a1, a2, a3, a4, a5); return; } /* * p r e r r p k t * * Print contents of error packet received from remote host. */ prerrpkt(msg) char *msg; { char emsg[132]; sprintf(emsg,"Aborted by remote: %s",msg); callers_printmsg(emsg); return; }