home *** CD-ROM | disk | FTP | other *** search
- /**********************************************************************
- * Receive.c: File reception routines for xprzmodem.library;
- * Version 2.10, 12 February 1991, 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.
- *
- * Version 2.50, 15 November 1991, CRC-32 additions by William M. Perkins.
- **********************************************************************/
-
- #include "xprzmodem_all.h"
-
- #define CATCOMP_NUMBERS
- #include "xprzmodem_catalog.h"
-
- #ifdef DEBUGLOG
- extern void *DebugLog;
- #endif
-
- /**********************************************************
- * long XProtocolReceive(struct XPR_IO *xio)
- *
- * Main file reception routine; called by comm program
- **********************************************************/
- long __saveds __asm
- XProtocolReceive (register __a0 struct XPR_IO *xio)
- {
- struct SetupVars *sv;
- struct Vars *v;
- UBYTE err = FALSE;
-
- /* Perform common setup and initializations */
- if (!(v = setup (xio)))
- return XPRS_FAILURE;
-
- v->Tryzhdrtype = ZRINIT;
- v->Rxtimeout = 100;
-
- sv = (void *) v->io.xpr_data;
- if (sv->bufpos)
- {
- v->Modemchar = v->Modembuf;
- if (sv->buflen > sizeof (v->Modembuf))
- sv->buflen = sizeof (v->Modembuf);
- memcpy (v->Modembuf, sv->bufpos, sv->buflen);
- v->Modemcount = sv->buflen;
- }
-
- /* Transfer the files */
- if (rcvbatch (v) == ERROR)
- {
- upderr (v, GetLocalString( &li, MSG_DOWNLOAD_USER_ERROR ));
- err = TRUE;
- }
- else
- updmsg (v, GetLocalString( &li, MSG_DONE ));
-
- /* Clean up and return */
- if (v->io.xpr_setserial && v->Oldstatus != -1)
- (*v->io.xpr_setserial) (v->Oldstatus);
- FreeMem (v->Filebuf, v->Filebufmax);
- FreeMem (v, (long) sizeof (struct Vars));
-
- #ifdef DEBUGLOG
- if (DebugLog)
- {
- (*v->io.xpr_fclose) ((long) DebugLog);
- DebugLog = NULL;
- }
- #endif
-
- return (err) ? XPRS_FAILURE : XPRS_SUCCESS;
- } /* End of long XProtocolReceive() */
-
- /**********************************************************
- * short rcvbatch(struct Vars *v)
- *
- * Start the batch transfer
- **********************************************************/
- short
- rcvbatch (struct Vars *v)
- {
- #ifdef DEBUGLOG
- D (DEBUGINFO);
- #endif
-
- switch (tryz (v))
- {
- case ZCOMPL:
- return OK;
- case ZFILE:
- if (rzfiles (v) == OK)
- return OK;
- }
-
- #ifdef DEBUGLOG
- D (DEBUGINFO);
- #endif
-
- canit (v);
-
- return ERROR;
- } /* End of short rcvbatch() */
-
- /**********************************************************
- * short tryz(struct Vars *v)
- *
- * Negotiate with sender to start a file transfer
- **********************************************************/
- short
- tryz (struct Vars *v)
- {
- short n, errors = 0;
-
- #ifdef DEBUGLOG
- D (DEBUGINFO);
- #endif
-
- for (n = v->ErrorLimit; --n >= 0;)
- {
- /* Set max frame length and capability flags */
- stohdr (v, (long) v->Tframlen);
- v->Txhdr[ZF0] = CANFC32 | CANFDX | CANOVIO;
- zshhdr (v, v->Tryzhdrtype);
- sendbuf (v);
- again:
- /* Check for abort from comm program */
- if (v->io.xpr_chkabort && (*v->io.xpr_chkabort) ())
- return ERROR;
- switch (zgethdr (v))
- {
- case ZFILE: /* File name and info packet */
- v->Zconv = v->Rxhdr[ZF0]; /* Suggested txt 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 */
- sendbuf (v);
- 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);
- sendbuf (v);
- goto again;
- case ZFREECNT: /* Sender wants to know how much room we've got */
- stohdr (v, getfree ());
- zshhdr (v, ZACK);
- sendbuf (v);
- goto again;
- case ZCOMMAND: /* Sender wants us to do remote commands, */
- /* but we don't do requests. */
- if (zrdata (v, v->Pktbuf, KSIZE) == GOTCRCW)
- {
- xprsprintf (v->Msgbuf, "%s: %s", GetLocalString( &li, MSG_IGNORING_COMMAND), 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... */
- sendbuf (v);
- }
- while (++errors < v->ErrorLimit && zgethdr (v) != ZFIN);
- ackbibi (v);
- return ZCOMPL;
- }
- else
- zshhdr (v, ZNAK);
- sendbuf (v);
- goto again;
- case ZCOMPL:
- goto again;
- case ZFIN: /* Sender has ended batch */
- ackbibi (v);
- return ZCOMPL;
- case ZCAN:
- case RCDO:
- upderr (v, v->Msgbuf);
- return ERROR;
- }
- }
- return ERROR;
- } /* End of short tryz() */
-
- /**********************************************************
- * short rzfiles(struct Vars *v)
- *
- * Receive a batch of files
- **********************************************************/
- short
- rzfiles (struct Vars *v)
- {
- struct SetupVars *sv;
- short c;
-
- #ifdef DEBUGLOG
- D (DEBUGINFO);
- #endif
-
- /* 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);
- sv = (void *) v->io.xpr_data;
- if (*sv->option_k == 'N' && v->io.xpr_extension >= 2
- && v->io.xpr_unlink)
- {
- updmsg (v, GetLocalString( &li, MSG_DELETING_RECEIVED_FILE ));
- (*v->io.xpr_unlink) (v->Filename);
- }
- else
- updmsg (v, GetLocalString( &li, MSG_KEEPING_RECEIVED_FILE));
- return c;
- }
- }
- } /* End of short rzfiles() */
-
- /**********************************************************
- * short rzfile(struct Vars *v)
- *
- * Receive one file; file name packet already read into
- * Pktbuf by tryz()
- **********************************************************/
- short
- rzfile (struct Vars *v)
- {
- short c, n;
-
- #ifdef DEBUGLOG
- D (DEBUGINFO);
- #endif
-
- /*
- * 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 = v->ErrorLimit;
- 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);
- sendbuf (v);
- nxthdr:
- /* Check for abort from comm program */
- if (v->io.xpr_chkabort && (*v->io.xpr_chkabort) ())
- return ERROR;
- switch (c = zgethdr (v)) /* Wait for frame header */
- {
- default:
- #ifdef DEBUGLOG
- xprsprintf (v->Msgbuf, "rzfile: zgethdr returned %ld\n", (long) c);
- dlog (v, v->Msgbuf);
- D (DEBUGINFO);
-
- #endif
- return ERROR;
- case ZNAK:
- case TIMEOUT:
- if (--n < 0)
- return ERROR;
- #ifdef DEBUGLOG
- dlog (v, "rzfile: zgethdr NAK/Timeout\n");
- D (DEBUGINFO);
- #endif
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
- xprsprintf (strchr (v->Msgbuf, '\0'), "@ %ld; %ld %s",
- v->Rxbytes, (long) n, GetLocalString( &li, MSG_RETRIES_LEFT ));
- v->xpru.xpru_errormsg = (char *) v->Msgbuf;
- ++v->xpru.xpru_timeouts;
- (*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 */
- continue; /* filename packet */
- case ZEOF: /* End of file data */
- if (v->Rxpos != v->Rxbytes) /* We aren't in sync; go back */
- {
- xprsprintf (v->Msgbuf, GetLocalString( &li, MSG_BAD_EOF ),
- v->Rxbytes, v->Rxpos);
- upderr (v, v->Msgbuf);
- continue;
- }
- bfclose (v); /* All done; close file */
- #ifdef DEBUGLOG
- dlog (v, "rzfile: EOF\n");
- D (DEBUGINFO);
- #endif
- updmsg (v, GetLocalString( &li, MSG_EOF_RECEIVED ));
- return c;
- case ERROR: /* Too much garbage while waiting for frame header */
- if (--n < 0)
- return ERROR;
- #ifdef DEBUGLOG
- dlog (v, "rzfile: zgethdr garbage overflow\n");
- D (DEBUGINFO);
- #endif
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
- xprsprintf (strchr (v->Msgbuf, '\0'), "@ %ld; %ld %s",
- v->Rxbytes, (long) n, GetLocalString( &li, MSG_RETRIES_LEFT ));
- v->xpru.xpru_errormsg = (char *) v->Msgbuf;
- ++v->xpru.xpru_errors;
- (*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;
- xprsprintf (v->Msgbuf, GetLocalString( &li, MSG_DATA_AT_BAD_POSITION ),
- v->Rxbytes, v->Rxpos);
- v->xpru.xpru_errormsg = (char *) v->Msgbuf;
- ++v->xpru.xpru_errors;
- (*v->io.xpr_update) (&v->xpru);
- zmputs (v, v->Attn);
- continue;
- }
- /* Receive file data packet(s) */
- moredata:
- /* Give comm program its timeslice if it needs one */
- if (v->io.xpr_chkmisc)
- (*v->io.xpr_chkmisc) ();
- /* Check for abort from comm 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 DEBUGLOG
- dlog (v, "rzfile: zrdata returned CAN\n");
- D (DEBUGINFO);
- #endif
- upderr (v, GetLocalString( &li, MSG_TRANSFER_CANCELLED ));
- return ERROR;
- case ERROR: /* CRC error or packet too long */
- if (--n < 0)
- return ERROR;
- #ifdef DEBUGLOG
- dlog (v, "rzfile: zrdata returned error\n");
- D (DEBUGINFO);
- #endif
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
- xprsprintf (strchr (v->Msgbuf, '\0'), "@ %ld; %ld %s",
- v->Rxbytes, (long) n, GetLocalString( &li, MSG_RETRIES_LEFT ));
- v->xpru.xpru_errormsg = (char *) v->Msgbuf;
- ++v->xpru.xpru_errors;
- (*v->io.xpr_update) (&v->xpru);
- #ifdef DEBUGLOG
- dlog (v, v->Msgbuf);
- dlog (v, "\n");
- D (DEBUGINFO);
- #endif
- zmputs (v, v->Attn);
- continue;
- case TIMEOUT:
- if (--n < 0)
- return ERROR;
- #ifdef DEBUGLOG
- dlog (v, "rzfile: zrdata returned timeout\n");
- D (DEBUGINFO);
- #endif
- v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
- xprsprintf (strchr (v->Msgbuf, '\0'), "@ %ld; %ld %s",
- v->Rxbytes, (long) n, GetLocalString( &li, MSG_RETRIES_LEFT ));
- v->xpru.xpru_errormsg = (char *) v->Msgbuf;
- ++v->xpru.xpru_timeouts;
- (*v->io.xpr_update) (&v->xpru);
- #ifdef DEBUGLOG
- dlog (v, v->Msgbuf);
- dlog (v, "\n");
- D (DEBUGINFO);
- #endif
- continue;
- case GOTCRCW: /* Sender says it's waiting for an ACK */
- n = v->ErrorLimit;
- if (putsec (v) == ERROR)
- return ERROR;
- stohdr (v, v->Rxbytes);
- zshhdr (v, ZACK);
- sendbuf (v);
- goto nxthdr;
- case GOTCRCQ: /* Sender says it's not waiting, */
- /* but ACK anyway (rarely used) */
- n = v->ErrorLimit;
- if (putsec (v) == ERROR)
- return ERROR;
- stohdr (v, v->Rxbytes);
- zshhdr (v, ZACK);
- sendbuf (v);
- goto moredata;
- case GOTCRCG: /* Sender says keep receiving, there's more coming */
- n = v->ErrorLimit;
- if (putsec (v) == ERROR)
- return ERROR;
- goto moredata;
- case GOTCRCE: /* Sender says this is the last packet */
- n = v->ErrorLimit;
- if (putsec (v) == ERROR)
- return ERROR;
- goto nxthdr;
- }
- }
- }
- } /* End of short rzfile() */
-
- /**********************************************************
- * short procheader(struct Vars *v)
- *
- * 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 (struct Vars *v)
- {
- struct SetupVars *sv;
- UBYTE *p, *openmode, buff[PATHLEN];
- long n;
-
- #ifdef DEBUGLOG
- D (DEBUGINFO);
- #endif
-
- openmode = "w";
- v->Strtpos = 0;
-
- /* 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())
- {
- xprsprintf(v->Msgbuf, GetLocalString( &li, MSG_INSUFFICIENT_DISK_SPACE ),
- v->Fsize, getfree());
- upderr(v, v->Msgbuf);
- v->Noroom = TRUE;
- return ERROR;
- }
- */
-
- /* If option RY set, use full received file path */
- sv = (void *) v->io.xpr_data;
- if (*sv->option_r == 'Y')
- strcpy (v->Filename, v->Pktbuf);
- else
- {
- /* else use the default directory path specified in the setup options */
- strcpy (v->Filename, sv->option_p);
- p = v->Filename + strlen (v->Filename) - 1;
- if (p >= v->Filename && *p != '/' && *p != ':')
- *++p = '/';
- *++p = '\0';
- /*
- * Append the filename from the file info packet; ignore anything before
- * last /, \, or : in filename (received 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;
- (*v->io.xpr_update) (&v->xpru);
-
- /*
- * If a file with this name already exists, handle in
- * accordance with O option
- */
- if (exist (v))
- {
- switch (*sv->option_o)
- {
- case 'N': /* Don't overwrite; change name to prevent collision */
- strcpy (buff, v->Filename);
- strcat (v->Filename, ".dup");
- n = 2;
- while (exist (v))
- {
- xprsprintf (v->Filename, "%s.dup%ld", buff, n);
- ++n;
- }
- /* Update filename display to show new name */
- (*v->io.xpr_update) (&v->xpru);
- break;
- case 'R': /* Resume transfer from current end of file */
- openmode = "a";
- v->Strtpos = (*v->io.xpr_finfo) (v->Filename, 1L);
- break;
- case 'S': /* Skip it */
- upderr (v, GetLocalString( &li, MSG_FILE_EXISTS ));
- return ERROR;
- /* Else 'Y', go ahead and overwrite it (openmode = w) */
- }
- }
-
- /* Set text/binary mode according to options before opening file */
- set_textmode (v);
-
- /*
- * 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;
-
- /* Open the file (finally) */
- if (!(v->File = bfopen (v, openmode)))
- {
- ++v->Errcnt;
- upderr (v, GetLocalString( &li, MSG_CANT_OPEN_FILE ));
- return ERROR;
- }
- getsystime (&v->Starttime);
-
- /* Initialize comm 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) ? GetLocalString( &li, MSG_RECEIVE_BINARY )
- : GetLocalString( &li, MSG_RECEIVE_TEXT );
- v->xpru.xpru_blocks = v->xpru.xpru_errors = v->xpru.xpru_timeouts = 0;
- v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
- v->xpru.xpru_bytes = v->Strtpos;
- update_rate (v);
- (*v->io.xpr_update) (&v->xpru);
-
- #ifdef DEBUGLOG
- D (DEBUGINFO);
- #endif
-
- return OK;
- } /* End of short procheader() */
-
- /**********************************************************
- * short putsec(struct Vars *v)
- *
- * 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 (struct Vars *v)
- {
- static char nl = '\n';
- UBYTE *p;
- short n;
-
- #ifdef DEBUGLOG
- D (DEBUGINFO);
- #endif
-
- /* 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 comm program status display */
- v->xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_BLOCKSIZE | XPRU_BYTES
- | XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE | XPRU_BLOCKCHECK;
- ++v->xpru.xpru_blocks;
- v->xpru.xpru_blocksize = v->Rxcount;
- v->xpru.xpru_bytes = v->Rxbytes += v->Rxcount;
- v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
- update_rate (v);
- (*v->io.xpr_update) (&v->xpru);
-
- return OK;
-
- diskfull:
- upderr (v, GetLocalString( &li, MSG_ERROR_WRITING_FILE ));
- v->Noroom = TRUE;
- return ERROR;
- } /* End of short putsec() */
-
- /**********************************************************
- * void ackbibi(struct Vars *v)
- *
- * End of batch transmission; disengage cleanly from sender
- **********************************************************/
- void
- ackbibi (struct Vars *v)
- {
- short n;
-
- #ifdef DEBUGLOG
- dlog (v, "ackbibi:\n");
- D (DEBUGINFO);
- #endif
- stohdr (v, 0L);
- for (n = 4; --n;)
- {
- zshhdr (v, ZFIN);
- sendbuf (v);
- switch (readock (v, 100))
- {
- case 'O':
- readock (v, 1); /* Discard 2nd 'O' */
- case TIMEOUT:
- case RCDO:
- return;
- }
- }
- } /* End of void ackbibi() */
- /* End of Receive.c source */
-