home *** CD-ROM | disk | FTP | other *** search
- /* Receive.c: File reception routines for xprzmodem.library;
- Version 1.0, 29 July 1989, by Rick Huebner.
- Based closely on Chuck Forsberg's rz.c example ZModem code,
- but too pervasively modified to even think of detailing the changes.
- Released to the Public Domain; do as you like with this code. */
-
-
- #include "aztec.h"
- #include "xproto.h"
- #include "zmodem.h"
- #include "defs.h"
-
- #ifdef DEBUG
- extern long DebugLog;
- #endif
-
-
- /* Main file reception routine; called by terminal program */
- long XProtocolReceive(io)
- struct XPR_IO *io;
- {
- register struct Vars *v;
- register short err = FALSE;
-
- /* Perform common setup and initializations */
- if (!(v = setup(io))) return 0;
- v->Tryzhdrtype = ZRINIT;
- v->Rxtimeout = 100;
-
- /* Transfer the files */
- if (rcvbatch(v) == ERROR) {
- upderr(v,"Download cancelled or timed out");
- err = TRUE;
- } else updmsg(v,"Done.");
-
- /* Clean up and return */
- if (v->io.xpr_setserial) calld(v->io.xpr_setserial,v->Oldstatus);
- FreeMem(v->Filebuf,v->Filebufmax);
- FreeMem(v,(long)sizeof(struct Vars));
-
- #ifdef DEBUG
- if (DebugLog) {
- calla(v->io.xpr_fclose,DebugLog);
- DebugLog = NULL;
- }
- #endif
-
- return (err) ? 0 : 1;
- }
-
-
- /* Start the batch transfer */
- short rcvbatch(v)
- register struct Vars *v;
- {
- switch (tryz(v)) {
- case ZCOMPL:
- return OK;
- case ZFILE:
- if (rzfiles(v) == OK) return OK;
- }
- canit(v);
- return ERROR;
- }
-
-
- /* Negotiate with sender to start a file transfer */
- short tryz(v)
- register struct Vars *v;
- {
- register short n, errors = 0;
-
- for (n=10; --n>=0; ) {
- /* Set max frame length and capability flags */
- stohdr(v,(long)v->Tframlen);
- v->Txhdr[ZF0] = CANFDX | CANOVIO;
- zshhdr(v,v->Tryzhdrtype);
- again:
- switch (zgethdr(v)) {
- case ZFILE: /* File name and info packet */
- v->Zconv = v->Rxhdr[ZF0]; /* Suggested text mode; ZCNL = text, ZCBIN = binary, 0 = don't know */
- v->Zmanag = v->Rxhdr[ZF1]; /* Suggested file management mode */
- v->Ztrans = v->Rxhdr[ZF2]; /* Suggested file transport mode */
- v->Tryzhdrtype = ZRINIT;
- if (zrdata(v,v->Pktbuf,KSIZE) == GOTCRCW) return ZFILE;
- zshhdr(v,ZNAK); /* Packet mangled, ask for retry */
- goto again;
- case ZSINIT: /* Special attention-grabbing string to use to interrupt sender */
- if (zrdata(v,v->Attn,ZATTNLEN) == GOTCRCW) zshhdr(v,ZACK);
- else zshhdr(v,ZNAK);
- goto again;
- case ZFREECNT: /* Sender wants to know how much room we've got */
- stohdr(v,getfree());
- zshhdr(v,ZACK);
- goto again;
- case ZCOMMAND: /* Sender wants us to do remote commands, but we don't do requests */
- if (zrdata(v,v->Pktbuf,KSIZE) == GOTCRCW) {
- sprintf(v->Msgbuf,"Ignoring command: %s",v->Pktbuf);
- upderr(v,v->Msgbuf); /* Ignore and report all uploaded commands */
- stohdr(v,0L); /* whilst telling sender they worked; */
- do zshhdr(v,ZCOMPL); /* paranoia can be good for you... */
- while (++errors < 10 && zgethdr(v) != ZFIN);
- ackbibi(v);
- return ZCOMPL;
- } else zshhdr(v,ZNAK);
- goto again;
- case ZCOMPL:
- goto again;
- case ZFIN: /* Sender has ended batch */
- ackbibi(v);
- return ZCOMPL;
- case ZCAN:
- case RCDO:
- return ERROR;
- }
- }
- return ERROR;
- }
-
-
- /* Receive a batch of files */
- short rzfiles(v)
- register struct Vars *v;
- {
- register short c;
-
- /* Keep receiving files until end of batch or error */
- while (TRUE) {
- switch (c = rzfile(v)) {
- case ZEOF:
- case ZSKIP:
- switch (tryz(v)) {
- case ZCOMPL:
- return OK;
- default:
- return ERROR;
- case ZFILE:
- break;
- }
- break;
- default:
- bfclose(v);
- return c;
- }
- }
- }
-
-
- /* Receive one file; file name packet already read into Pktbuf by tryz() */
- short rzfile(v)
- register struct Vars *v;
- {
- static char *errposfmt = "@ %ld; %d retries left";
- register short c, n;
-
- /* Process file name packet; either open file and prepare to receive,
- or tell us to skip this one. */
- if (procheader(v) == ERROR) return v->Tryzhdrtype = ZSKIP;
-
- n = 10;
- v->Rxbytes = v->Strtpos;
- v->Eofseen = FALSE;
-
- /* Receive ZDATA frames until finished */
- while (TRUE) {
- stohdr(v,v->Rxbytes); /* Tell sender where to start frame */
- zshhdr(v,ZRPOS);
- nxthdr:
- switch (c = zgethdr(v)) { /* Wait for frame header */
- default:
- #ifdef DEBUG
- sprintf(v->Msgbuf,"rzfile: zgethdr returned %d\n",c);
- dlog(v,v->Msgbuf);
- #endif
- return ERROR;
- case ZNAK:
- case TIMEOUT:
- if (--n < 0) return ERROR;
- #ifdef DEBUG
- dlog(v,"rzfile: zgethdr NAK/Timeout\n");
- #endif
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
- sprintf(strchr(v->Msgbuf,'\0'),errposfmt,v->Rxbytes,n);
- v->xpru.xpru_errormsg = (char *)v->Msgbuf;
- ++v->xpru.xpru_timeouts;
- calla(v->io.xpr_update,&v->xpru);
- continue;
- case ZFILE: /* Sender didn't see our ZRPOS yet; try again */
- zrdata(v,v->Pktbuf,KSIZE); /* Read and discard redundant filename packet */
- continue;
- case ZEOF: /* End of file data */
- if (v->Rxpos != v->Rxbytes) { /* We aren't in sync; go back */
- sprintf(v->Msgbuf,"Bad EOF; here=%ld, there=%ld",v->Rxbytes,v->Rxpos);
- upderr(v,v->Msgbuf);
- continue;
- }
- bfclose(v); /* All done; close file */
- #ifdef DEBUG
- dlog(v,"rzfile: EOF\n");
- #endif
- updmsg(v,"EOF received; checking for next file");
- return c;
- case ERROR: /* Too much garbage while waiting for frame header */
- if ( --n < 0) return ERROR;
- #ifdef DEBUG
- dlog(v,"rzfile: zgethdr garbage overflow\n");
- #endif
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
- sprintf(strchr(v->Msgbuf,'\0'),errposfmt,v->Rxbytes,n);
- v->xpru.xpru_errormsg = (char *)v->Msgbuf;
- ++v->xpru.xpru_errors;
- calla(v->io.xpr_update,&v->xpru);
- zmputs(v,v->Attn);
- continue;
- case ZDATA: /* More file data packets forthcoming */
- if (v->Rxpos != v->Rxbytes) { /* We aren't in sync; go back */
- if ( --n < 0) return ERROR;
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
- sprintf(v->Msgbuf,"Data at bad position; here=%ld, there=%ld",v->Rxbytes,v->Rxpos);
- v->xpru.xpru_errormsg = (char *)v->Msgbuf;
- ++v->xpru.xpru_errors;
- calla(v->io.xpr_update,&v->xpru);
- zmputs(v,v->Attn);
- continue;
- }
- /* Receive file data packet(s) */
- moredata:
- /* Give terminal program its timeslice if it needs one */
- if (v->io.xpr_chkmisc) (*v->io.xpr_chkmisc)();
- /* Check for abort from terminal program */
- if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)()) goto aborted;
- switch (c = zrdata(v,v->Pktbuf,KSIZE)) {
- case ZCAN:
- case RCDO:
- aborted:
- #ifdef DEBUG
- dlog(v,"rzfile: zrdata returned CAN\n");
- #endif
- upderr(v,"Transfer cancelled");
- return ERROR;
- case ERROR: /* CRC error or packet too long */
- if ( --n < 0) return ERROR;
- #ifdef DEBUG
- dlog(v,"rzfile: zrdata returned error\n");
- #endif
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
- sprintf(strchr(v->Msgbuf,'\0'),errposfmt,v->Rxbytes,n);
- v->xpru.xpru_errormsg = (char *)v->Msgbuf;
- ++v->xpru.xpru_errors;
- calla(v->io.xpr_update,&v->xpru);
- #ifdef DEBUG
- dlog(v,v->Msgbuf);
- dlog(v,"\n");
- #endif
- zmputs(v,v->Attn);
- continue;
- case TIMEOUT:
- if ( --n < 0) return ERROR;
- #ifdef DEBUG
- dlog(v,"rzfile: zrdata returned timeout\n");
- #endif
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
- sprintf(strchr(v->Msgbuf,'\0'),errposfmt,v->Rxbytes,n);
- v->xpru.xpru_errormsg = (char *)v->Msgbuf;
- ++v->xpru.xpru_timeouts;
- calla(v->io.xpr_update,&v->xpru);
- #ifdef DEBUG
- dlog(v,v->Msgbuf);
- dlog(v,"\n");
- #endif
- continue;
- case GOTCRCW: /* Sender says it's waiting for an ACK */
- n = 10;
- if (putsec(v) == ERROR) return ERROR;
- stohdr(v,v->Rxbytes);
- zshhdr(v,ZACK);
- goto nxthdr;
- case GOTCRCQ: /* Sender says it's not waiting, but ACK anyway (rarely used) */
- n = 10;
- if (putsec(v) == ERROR) return ERROR;
- stohdr(v,v->Rxbytes);
- zshhdr(v,ZACK);
- goto moredata;
- case GOTCRCG: /* Sender says keep receiving, there's more coming */
- n = 10;
- if (putsec(v) == ERROR) return ERROR;
- goto moredata;
- case GOTCRCE: /* Sender says this is the last packet */
- n = 10;
- if (putsec(v) == ERROR) return ERROR;
- goto nxthdr;
- }
- }
- }
- }
-
-
- /* Process file name & info packet; either open file and prepare to receive,
- or return ERROR if we should skip this one for some reason */
- short procheader(v)
- register struct Vars *v;
- {
- register UBYTE *p;
- char *openmode;
- register short n;
-
- openmode = "w";
- v->Strtpos = 0;
-
- /* Figure out file translation mode to use; either binary (verbatim
- transfer) or ASCII (perform end-of-line conversions). If user has
- specified a mode (TY or TN), that's what we use. If user says use
- sender's suggestion (T?), set mode according to Zconv flag. If neither
- side specifies, default to binary mode. */
- v->Thisbinary = v->Rxbinary || !v->Rxascii;
- if (!v->Rxbinary && v->Zconv == ZCNL) v->Thisbinary = FALSE;
- if (!v->Rxascii && v->Zconv == ZCBIN) v->Thisbinary = TRUE;
-
- /* Extract expected filesize from file info packet, if given */
- v->Fsize = -1;
- p = strchr(v->Pktbuf,'\0') + 1;
- if (*p) v->Fsize = atol(p);
- /* Make sure we have room for file; skip it if not.
- Commented out for now, since getfree() isn't implemented yet.
- if (v->Fsize > getfree()) {
- sprintf(v->Msgbuf,"Insufficient disk space; need %ld bytes, have %ld",v->Fsize,getfree());
- upderr(v,v->Msgbuf);
- v->Noroom = TRUE;
- return ERROR;
- } */
-
- /* Make filename from xpr_filename path portion and received name portion.
- Filename portion of name passed from terminal program isn't used;
- only the directory path (up to last / or :) matters. Filename comes
- from sending system. */
- /* Copy the directory path portion of xpr_filename */
- strcpy(v->Filename,v->io.xpr_filename);
- p = strchr(v->Filename,'\0'); /* start at end and scan backwards to end of path */
- while (p >= v->Filename && *p != '/' && *p != ':') --p;
- *++p = '\0';
- /* Append the filename from the file info packet; ignore anything before
- last /, \, or : in filename (transmitted directory path is ignored) */
- p = strchr(v->Pktbuf,'\0'); /* start at end and scan back to start of name */
- while (p >= v->Pktbuf && *p != '/' && *p != '\\' && *p != ':') --p;
- strcat(v->Filename,++p);
-
- /* Display name of file being received for user */
- v->xpru.xpru_updatemask = XPRU_FILENAME;
- v->xpru.xpru_filename = (char *)v->Filename;
- calla(v->io.xpr_update,&v->xpru);
-
- /* If a file with this name already exists, handle in accordance with O option */
- if (exist(v)) {
- switch (*(strchr(v->io.xpr_data,'O')+1)) {
- case 'N': /* Don't overwrite; change name to prevent collision */
- while (exist(v)) strcat(v->Filename,".dup");
- /* Update filename display to show new name */
- calla(v->io.xpr_update,&v->xpru);
- break;
- case 'R': /* Resume transfer from current end of file */
- openmode = "a";
- v->Strtpos = callad(v->io.xpr_finfo,v->Filename,1L);
- break;
- case 'S': /* Skip it */
- upderr(v,"File already exists; skipping");
- return ERROR;
- /* Else 'Y', go ahead and overwrite it (openmode = w) */
- }
- }
-
- /* Open the file (finally) */
- if (!(v->File = bfopen(v,openmode))) {
- ++v->Errcnt;
- upderr(v,"Can't open file; skipping");
- return ERROR;
- }
- v->Starttime = time(NULL);
-
- /* Initialize term program transfer status display */
- v->xpru.xpru_updatemask = XPRU_PROTOCOL | XPRU_FILESIZE | XPRU_MSG | XPRU_BLOCKS |
- XPRU_ERRORS | XPRU_TIMEOUTS | XPRU_BLOCKCHECK | XPRU_BYTES |
- XPRU_EXPECTTIME | XPRU_ELAPSEDTIME| XPRU_DATARATE;
- v->xpru.xpru_protocol = "ZModem";
- v->xpru.xpru_filesize = v->Fsize;
- v->xpru.xpru_msg = (v->Thisbinary) ? "Receiving binary file..." : "Receiving text file...";
- v->xpru.xpru_blocks = v->xpru.xpru_errors = v->xpru.xpru_timeouts = 0;
- v->xpru.xpru_blockcheck = "CRC-16";
- v->xpru.xpru_bytes = v->Strtpos;
- update_rate(v);
- calla(v->io.xpr_update,&v->xpru);
-
- return OK;
- }
-
-
- /* Writes the received file data to the output file.
- If in ASCII mode, stops writing at first ^Z, and converts all
- \r\n pairs or solo \r's to \n's. */
- short putsec(v)
- register struct Vars *v;
- {
- static char nl = '\n';
- register UBYTE *p;
- register short n;
-
- /* If in binary mode, write it out verbatim */
- if (v->Thisbinary) {
- if (bfwrite(v,v->Pktbuf,(long)v->Rxcount) != v->Rxcount) goto diskfull;
- /* If in text mode, perform end-of-line cleanup */
- } else {
- if (v->Eofseen) return OK;
- for (p=v->Pktbuf, n=v->Rxcount; --n >= 0; ++p) {
- if (*p == CPMEOF) {
- v->Eofseen = TRUE;
- return OK;
- } else if (*p != '\n' && v->Lastsent == '\r') {
- if (bfwrite(v,&nl,1L) != 1) goto diskfull;
- }
- if (*p != '\r' && bfwrite(v,p,1L) != 1) goto diskfull;
- v->Lastsent = *p;
- }
- }
-
- /* Update terminal program status display */
- v->xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_BLOCKSIZE | XPRU_BYTES |
- XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE;
- ++v->xpru.xpru_blocks;
- v->xpru.xpru_blocksize = v->Rxcount;
- v->xpru.xpru_bytes = v->Rxbytes += v->Rxcount;
- update_rate(v);
- calla(v->io.xpr_update,&v->xpru);
-
- return OK;
-
- diskfull:
- upderr(v,"Error writing file; disk full?");
- v->Noroom = TRUE;
- return ERROR;
- }
-
-
- /* End of batch transmission; disengage cleanly from sender */
- void ackbibi(v)
- register struct Vars *v;
- {
- register short n;
-
- #ifdef DEBUG
- dlog(v,"ackbibi:\n");
- #endif
- stohdr(v,0L);
- for (n=4; --n;) {
- zshhdr(v,ZFIN);
- switch (readock(v,100)) {
- case 'O':
- readock(v,1); /* Discard 2nd 'O' */
- case TIMEOUT:
- case RCDO:
- return;
- }
- }
- }
-