#include #include #include long lstonum(); /* Tom Jennings Fido Software 10 June 91 Zmodem receive Derived from the Zmodem protocol files Chuck Forsberg, Omen Technology Inc 05-09-88 This will accept up to 8192 byte data packets, generated by BinkleyTerm and Opus. */ static int zfile; /* file we write to */ static long zfiletime; /* saves DOS time/date */ static char fpath[SS]; /* pathname for receive */ static char fname[SS]; /* complete filename */ static long bytesleft; /* bytes left to transfer */ /* * Process incoming file information header */ static procheader(bp) char *bp; { char buff[SS]; cpyarg(buff,strip_path(buff,bp)); /* copy of the name only */ if (badname(buff)) buff[1]= '$'; /* make acceptable */ strcpy(fname,fpath); /* receive pathname */ strcat(fname,buff); /* add filename */ stoupper(fname); /* make all upper case */ if (fexist(fname)) { xferstat(3,0L,0,"File \"%s\" already exists, skipping it",fname); return ZSKIP; /* skip it */ } if (zmanag == ZMAPND) { /* if appending, */ zfile= open(fname,1); /* open for writing, */ if (zfile == -1) { /* cant! report an error */ xferstat(3,0L,0,"Can't append to non-existent file \"%s\"!",fname); return BADFILE; } lseek(zfile,0L,2); /* seek EOF */ } else zfile= creat(fname,1); /* create a new one */ totl_process= 1; /* this file in process */ if (zfile == -1) { xferstat(3,0L,0,"Can't create file \"%s\"",fname); return ERROR; } eofseen= 0; /* not EOF yet */ bytesleft= (-1L >> 1L); /* default tremendous file */ zfiletime= -1L; /* and invalid time */ while (*bp++); /* add'l info follows filename */ if (*bp) bytesleft= stonum(bp,10); /* file size follows (decimal) */ bp= next_arg(bp); /* file time follows that */ if (*bp) { /* if a time was given */ zfiletime= stonum(bp,8); /* use it (octal) */ zfiletime= sec2dos(zfiletime); /* convert to DOS format */ } xferstat(1,bytesleft,0,fname); /* (possibly) update display */ xferstat(2,0L,0,"Rec'v Data"); return OK; } /* Return the number of bytes free on the disk. */ static long getfree() { int i[4],n; long n1,n2; n= 0; /* default drive number, */ if ((strlen(fpath) >= 2) && (fpath[1] == ':')) n= toupper(fpath[0]) - 'A' + 1; _free(n,i); /* get disk stuff, */ n1= i[3]; n1*= i[2]; n1*= i[0]; /* total clust * bytes/sec * secs/clust */ n2= i[1]; n2*= i[2]; n2*= i[0]; /* free clust * bytes/sec * secs/clust */ return(n2); } /* Receive one or more files using ZMODEM. */ far zrecv(fn,nl,ll) char *fn; /* path to receive to */ char *nl; /* created list of received files */ unsigned ll; /* its maximum size */ { int error; totl_process= totl_files= totl_errors= totl_recoveries= 0; totl_bytes= 0L; strip_path(fpath,fn); /* get a copy of the pathname, */ namelist= nl; listlen= ll; /* where to keep the list */ *namelist= '\0'; /* list is empty */ blklen= 512; /* block size for init stuff */ rxbuflen= 512; /* assume Rx buffer size */ wantfcs32= 1; /* yes we want 32 bit CRC */ lzconv= 0; /* file conversion: none */ lzmanag= 0; /* file mgt: none */ lztrans= 0; /* file xport: none */ zctlesc= 0; /* dont need control chars escaped */ cantovio= 0; /* Rx can overlap disk & serial I/O */ zmabort= 0; zfile= -1; /* no file open */ rxtimeout= 1000; /* real receive timeout, 1/100ths */ strcpy(attn,""); /* Rx attention string */ xferstat(0,0L,0,""); /* start the display, */ error= zrecvf(); /* start receiving */ if (error == ZFILE) error= zrecvf2(); /* do the rest, */ if (zfile != -1) close(zfile); /* (in case error/aborted) */ if (zabort()) error= ABORT; /* so we can switch() it */ switch (error) { /* see what happened */ case ERROR: /* outright failure */ case TIMEOUT: /* someone fell asleep */ case DISKFULL: /* not our fault */ case ABORT: /* someone is to blame */ aborttx(); /* cancel the transfer */ ++totl_errors; /* count the error */ break; } xferstat(3,0L,0,"Protocol Complete"); return(error); } /* * Initialize for Zmodem receive attempt, try to activate Zmodem sender * Handles ZSINIT frame * Return ZFILE if Zmodem filename received, -1 on error, * ZCOMPL if transaction finished, else 0 */ static zrecvf() { int i,c,n; tryzhdrtype= ZRINIT; for (n= 60; n--; ) { if ((zmrtype == 1) || (zmblkmax < BLKSIZE)) stohdr(0L+zmblkmax); /* say how big our buffer is */ else stohdr(0L); /* else full streaming */ txhdr[ZF0]= CANFC32 | CANFDX; if (zmrtype == 0) txhdr[ZF0] |= CANOVIO; if (zctlesc) txhdr[ZF0] |= TESCCTL; zshhdr(tryzhdrtype, txhdr); if (tryzhdrtype == ZSKIP) /* Don't skip too far */ tryzhdrtype= ZRINIT; /* CAF 8-21-87 */ again: if (zabort()) return ERROR; /* manual intervention */ c= zgethdr(rxhdr, 0); switch (c) { case ZFILE: zconv= rxhdr[ZF0]; zmanag= rxhdr[ZF1]; ztrans= rxhdr[ZF2]; tryzhdrtype= ZRINIT; c= zrdata(zmbuff,zmblkmax); /* get file info */ if (c == GOTCRCW) return ZFILE; zshhdr(ZNAK,txhdr); goto again; case ZSINIT: zctlesc= TESCCTL & rxhdr[ZF0]; if (zrdata(attn,ZATTNLEN) == GOTCRCW) { stohdr(1L); zshhdr(ZACK,txhdr); goto again; } zshhdr(ZNAK,txhdr); goto again; case ZFREECNT: stohdr(getfree()); zshhdr(ZACK,txhdr); goto again; case ZCOMPL: goto again; case ZFIN: stohdr(0L); for (i= 3; --i >= 0; ) { xferstat(2,0L,0,"Session Complete"); mflush(); /* flush anything */ zshhdr(ZFIN,txhdr); /* send ZFIN */ c= modin(100); /* get response */ if (c == 'O') { /* Got 'O' */ xferstat(2,0L,0,"Over & Out"); modin(10); /* eat 2nd 'O' */ return ZCOMPL; /* success */ } } return ZCOMPL; case ZCOMMAND: case ZCAN: return ERROR; case ZRQINIT: case ZEOF: case TIMEOUT: default: continue; } } return 0; } /* * Receive 1 or more files with ZMODEM protocol */ static zrecvf2() { int tries,c; /**/ for (tries= 10; --tries;) { if (zabort()) return ERROR; /* manual intervention */ switch (c= zrecvf3()) { case ZEOF: case ZSKIP: switch (zrecvf()) { case ZCOMPL: return OK; case ZFILE: break; default: return ERROR; } tries= 10; /* not an error, reset */ continue; case ERROR: return ERROR; default: return c; } } xferstat(3,rxbytes,0,"ZRecvF: Too many retries!"); return ERROR; } /* * Receive a file with ZMODEM protocol * Assumes file name frame is in zmbuff */ static zrecvf3() { int tries,c; long lastack; switch (procheader(zmbuff)) { case OK: break; /* alls well */ case ZSKIP: /* duplicate */ case BADFILE: return(tryzhdrtype= ZSKIP); /* bad filename? */ case ERROR: return(ERROR); /* somethings wrong */ } rxbytes= 0L; tries= 10; for (;;) { stohdr(lastack= rxbytes); zshhdr(ZRPOS,txhdr); /* last good position */ nxthdr: if (zabort()) return ERROR; /* manual intervention */ switch (c= zgethdr(rxhdr, 0)) { case ZNAK: case TIMEOUT: if ( --tries < 0) { xferstat(3,rxbytes,0,"File rec'v failed!"); return ERROR; } /* fall through ... */ case ZFILE: zrdata(zmbuff,zmblkmax); continue; case ZEOF: if (rclhdr(rxhdr) != rxbytes) { /* * Ignore eof if it's at wrong place - force * a timeout because the eof might have gone * out before we sent our zrpos. */ goto nxthdr; } if (zfiletime != -1) ftime(1,zfile,&zfiletime); /* set file time */ close(zfile); zfile= -1; /* close, mark it closed */ totl_process= 0; /* file completed */ if (strlen(fname) < listlen) { /* keep a list */ strcat(namelist,fname); strcat(namelist," "); listlen -= strlen(fname); } ++totl_files; /* another ... */ totl_bytes += rxbytes; xferstat(2,rxbytes,0,"File complete"); return c; case ERROR: if ( --tries < 0) { xferstat(3,rxbytes,0,"Excess line noise!"); return ERROR; } continue; case ZSKIP: close(zfile); zfile= -1; totl_process= 0; xferstat(3,0L,0,"File skipped"); return c; case ZDATA: if (rclhdr(rxhdr) != rxbytes) { if ( --tries < 0) { xferstat(3,rxbytes,0,"ZRecvF3a: Too many retries!"); return ERROR; } rxattn(); continue; } /* Loop here for all data blocks. */ moredata: if (zabort()) return ERROR; /* manual intervention */ switch (c= zrdata(zmbuff,zmblkmax)) { case ZCAN: xferstat(3,rxbytes,0,"Sender cancelled!"); return ERROR; case ERROR: /* CRC error */ if ( --tries < 0) { xferstat(3,rxbytes,0,"ZRecvFb: Too many retries!"); return ERROR; } rxattn(); continue; case TIMEOUT: if ( --tries < 0) { xferstat(3,rxbytes,0,"Sender stopped"); return ERROR; } continue; case GOTCRCW: tries= 10; if (putsec(zmbuff,rxcount)) return ERROR; rxbytes += rxcount; xferstat(2,rxbytes,0,"Rec'v Data (%s)",Zendnames[c - GOTCRCE & 3]); stohdr(lastack= rxbytes); zshhdr(ZACK, txhdr); modout(XON); goto nxthdr; case GOTCRCQ: tries= 10; if (putsec(zmbuff,rxcount)) return ERROR; rxbytes += rxcount; xferstat(2,rxbytes,0,"Rec'v Data (%s)",Zendnames[c - GOTCRCE & 3]); sendack:; stohdr(lastack= rxbytes); zshhdr(ZACK,txhdr); goto moredata; case GOTCRCG: tries= 10; if (putsec(zmbuff,rxcount)) return ERROR; rxbytes += rxcount; xferstat(2,rxbytes,0,"Rec'v Data (%s)",Zendnames[c - GOTCRCE & 3]); goto moredata; case GOTCRCE: tries= 10; if (putsec(zmbuff,rxcount)) return ERROR; rxbytes += rxcount; xferstat(2,rxbytes,0,"Rec'v Data (%s)",Zendnames[c - GOTCRCE & 3]); goto nxthdr; } default: return ERROR; } } return ERROR; } /* Output the attention string (if there is one). */ static rxattn() { char *cp; for (cp= attn; *cp;) modout(*cp++); } /* Write out a buffer full. */ static putsec(buff,count) char *buff; unsigned count; { if (write(zfile,buff,count) != count) { xferstat(3,rxbytes,0,"DISK FULL!"); return ERROR; } return OK; } /* Convert a string of ASCII digits to a long number, in the specified base. Works only for bases 1 - 10 (mainly: dec. and octal) */ /********* static long lstonum(s,base) char *s; int base; { int i; long n; n= 0L; while ((*s >= '0') && (*s <= '9')) n= (base * n) + (*s++ - '0'); return(n); } ***********/