/* Copyright (C) Magna Carta Software, Inc. 1988-1990. All Rights Reserved. C COMMUNICATIONS TOOLKIT CCTZR.H -- ZMODEM receive routines for CCT */ #define CCT_DEVELOPMENT #if (defined(CCTW) || defined(_WINDOWS)) #include #endif #include #include #include #include #include #include static short NEAR_ z_r_files_(COMM_PORT *p); static short NEAR_ z_r_qinit_(COMM_PORT *p); extern int Beenhereb4; /* How many times we've been ZRPOS'd same place */ int tryzhdrtype = ZRINIT; short cmdzack1flg; /* Z_R_DATA32 -- Receive array buf of max length with ending ZDLE sequence and CRC-32. Returns the ending character or error code. NB: On errors may store length+1 bytes! */ short z_r_data32(COMM_PORT *p, char *buf, short length) { short c, d; unsigned long crc; char *end; Z_PARMS *z = p->ppb; crc = 0xFFFFFFFFL; z->rxcount = 0; end = buf + length; while (buf <= end) { if ((c = z_decodec_(p)) & ~0XFF) { crcfoo: switch (c) { case GOTCRCE: case GOTCRCG: case GOTCRCQ: case GOTCRCW: d = c; c &= 0XFF; crc = UPDC32(c, crc); if ((c = z_decodec_(p)) & ~0XFF) goto crcfoo; crc = UPDC32(c, crc); if ((c = z_decodec_(p)) & ~0XFF) goto crcfoo; crc = UPDC32(c, crc); if ((c = z_decodec_(p)) & ~0XFF) goto crcfoo; crc = UPDC32(c, crc); if ((c = z_decodec_(p)) & ~0XFF) goto crcfoo; crc = UPDC32(c, crc); if (crc != 0XDEBB20E3L) return (EOF); z->rxcount = length - (end - buf); return (d); case GOTCAN: return (ZCAN); case ZTIMEOUT: return (c); default: return c; } } *buf++ = (char) c; crc = UPDC32(c, crc); } return (EOF); } /* Z_R_DATA -- Receive array buf of max length with ending ZDLE sequence and CRC. Returns the ending character or error code. NB: On errors may store length+1 bytes! */ short z_r_data(COMM_PORT *p, char *buf, WORD length) { short c, d; WORD crc; char *end; Z_PARMS *z = p->ppb; if (z->rxframeind == ZBIN32) return (z_r_data32(p, buf, length)); crc = z->rxcount = 0; end = buf + length; while (buf <= end) { if ((c = z_decodec_(p)) & ~0XFF) { /* test high byte */ crcfoo: switch (c) { case GOTCRCE: case GOTCRCG: case GOTCRCQ: case GOTCRCW: crc = updcrc_(((d=c) & 0XFF), crc); if ((c = z_decodec_(p)) & ~0XFF) goto crcfoo; crc = updcrc_(c, crc); if ((c = z_decodec_(p)) & ~0XFF) goto crcfoo; crc = updcrc_(c, crc); if (crc & 0xFFFF) return (EOF); z->rxcount = length - (end - buf); return (d); case GOTCAN: return (ZCAN); case ZTIMEOUT: return (c); default: return (c); } } *buf++ = (char) c; crc = updcrc_(c, crc); } return (EOF); } /* PARSE_YMODEM_HDR_ -- Parse a Y/ZMODEM header into the file name and size. Called during a Y/ZMODEM receive. Parameters: char *b -- buffer containing received YMODEM HEADER; WORD len -- length of buffer to scan; char *fname -- ptr. to file name (assigned by function); DWORD *fsize -- ptr. to file size (assigned by function); DWORD *fdate -- ptr. to file date (assigned by function as octal); Return Value: Always 0 in the current implementation. */ short parse_ymodem_hdr_(char FAR_ *b, WORD len, char *fname, DWORD *fsize, DWORD *fdate) { short ret=0, i; char FAR_ *p1, FAR_ *pend; char temp[PATHLEN]; fname[0] = '\0'; /* initialize file name */ *fsize = *fdate = 0L;; /* initialize file size and date */ p1 = b; pend = b + len; /* POSITION ON FILE NAME/PATH */ while (*p1 == '\0' && p1 < pend) p1++; /* COPY FILE NAME/PATH TO fname[] */ for (i=0; *p1 != '\0' && p1 < pend && i < PATHLEN; i++) fname[i] = *p1++; fname[i] = '\0'; /* null terminate */ /* FIND BEGINNING OF FILE SIZE */ while (*p1 == '\0' && p1 < pend) p1++; /* find beginning of filesize */ /* COPY FILE SIZE TO TEMPORARY BUFFER */ for (i=0; p1 < pend && *p1 != '\0' && *p1 != SP && i < PATHLEN; i++) temp[i] = *p1++; temp[i] = '\0'; *fsize = atol(temp); /* FIND BEGINNING OF FILE DATE (OCTAL, SECONDS SINCE 00:00 Jan 1, 1970 GMT) */ while (*p1 == '\0' && p1 < pend) p1++; /* find beginning of filesize */ /* COPY FILE DATE TO TEMPORARY BUFFER */ for (i=0; p1 < pend && *p1 != '\0' && *p1 != SP && i < PATHLEN; i++) temp[i] = *p1++; temp[i] = '\0'; *fdate = atol(temp); return (ret); } /* Z_M_PUTS -- Send a string to the modem, processing for \336 (sleep 1 sec) and \335 (break signal) */ void z_m_puts(COMM_PORT *p, char *s) { short c; while (*s) { switch (c = *s++) { case 0XDE: mspause(1000); continue; case 0xDB: send_break(p, 50); continue; default: c_putc(p, c); } } } /* Z_R_FILE -- Parse ZFILE header. May respond with ZSKIP or ZCRC but then sends ZRPOS. Called from Z_RECEIVE. */ static short NEAR_ z_r_file(COMM_PORT *p) { short c, n; short ret = 0; WORD i; char openmode[3]; Z_PARMS *z = p->ppb; XFER *x = z->x; /* char zbuf[128]; */ /* desired size of file info. buffer */ /* char fname[80]; */ /* DWORD rxbytes, fsize, fdate; */ DWORD rxbytes; short DONE = FALSE; strcpy(openmode, "wb"); parse_ymodem_hdr_(z->rxbuf, 1024, fname, &fsize, &fdate); if (x->p_user != NULL) (*x->p_user)(p, FILE_INFO_RECEIVED, 0L); /* get disk size */ /* CREATE FILE TO RECEIVE DATA */ x->fh = cct_file_open_(fname, O_CREAT|O_RDWR|O_BINARY); if (!x->fh) { ffreebuf_(x); ret = DISK_FULL; } else { x->pb = x->buf; n = 20; rxbytes = 0L; for (;!DONE;) { stohdr_(z, rxbytes); z_s_hh_(p, ZRPOS, z->txhdr); nxthdr: if (!DONE) switch (c = z_r_h_(p, z->rxhdr, 0)) { default: #if XDEBUG printf("rzfile: zgethdr returned %d", c); #endif ret = EOF; break; case ZNAK: case -2: if (--n < 0) { #if XDEBUG printf("rzfile: zgethdr returned %d", c); #endif ret = EOF; } break; case ZFILE: #if XDEBUG printf("Calling z_r_data\n"); #endif z_r_data(p, z->rxbuf, 1024); #if XDEBUG printf("Returned from z_r_data\n"); #endif continue; case ZEOF: if (rclhdr_(z->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. */ z->errors = 0; goto nxthdr; } ret = c_fwrite_(p, x); if (!ret) { ret = ZEOF; DONE = TRUE; } else { tryzhdrtype = ZFERR; #if XDEBUG printf("rzfile: closeit returned <> 0"); #endif DONE = TRUE; } break; case EOF: /* Too much garbage in header search error */ if ( --n < 0) { #if XDEBUG printf("rzfile: zgethdr returned %d", c); #endif return (EOF); } z_m_puts(p, z->attn); continue; case ZSKIP: #if XDEBUG printf("rzfile: Sender SKIPPED file"); #endif ret = c; break; case ZDATA: if (rclhdr_(z->rxhdr) != rxbytes) { if ( --n < 0) { ret = EOF; } else z_m_puts(p, z->attn); continue; } moredata: #if XDEBUG fprintf(stderr, "\r%7ld ZMODEM%s ", rxbytes, z->crc32?" CRC-32":""); #endif if (x->p_user != NULL)(*x->p_user)(p, XFER_POSITION, rxbytes); c = z_r_data(p, z->rxbuf, 1024); #if XDEBUG printf("\nz_r_file: c=%d\n", c); #endif switch (c) { case ZCAN: #if XDEBUG printf("rzfile: zgethdr returned %d", c); #endif ret = EOF; case EOF: /* CRC error */ if ( --n < 0) { #if XDEBUG printf("rzfile: zgethdr returned %d", c); #endif ret = EOF; } else z_m_puts(p, z->attn); continue; case -2: if ( --n < 0) { #if XDEBUG printf("rzfile: zgethdr returned %d", c); #endif ret = EOF; } continue; case GOTCRCW: n = 20; for (i=0; i < z->rxcount; i++) c_fputc_(p, z->rxbuf[i]); rxbytes += z->rxcount; stohdr_(z, rxbytes); z_s_hh_(p, ZACK, z->txhdr); c_putc(p, XON); goto nxthdr; case GOTCRCQ: n = 20; for (i=0; i < z->rxcount; i++) c_fputc_(p, z->rxbuf[i]); rxbytes += z->rxcount; stohdr_(z, rxbytes); z_s_hh_(p, ZACK, z->txhdr); goto moredata; case GOTCRCG: n = 20; for (i=0; i < z->rxcount; i++) c_fputc_(p, z->rxbuf[i]); rxbytes += z->rxcount; goto moredata; case GOTCRCE: n = 20; for (i=0; i < z->rxcount; i++) c_fputc_(p, z->rxbuf[i]); rxbytes += z->rxcount; goto nxthdr; case USER_CANCELLED: ret = USER_CANCELLED; default: break; } } if (ret < 0) break; } cct_file_close_(z->x->fh); } return (ret); } /* Z_R_FILES -- Receive 1 or more files using ZMODEM. */ static short NEAR_ z_r_files_(COMM_PORT *p) { short c; for (;;) { switch (c = z_r_file(p)) { case ZEOF: case ZSKIP: switch (z_r_qinit_(p)) { case ZCOMPL: return (ZCOMPL); default: return (EOF); case ZFILE: break; } continue; default: return (c); case EOF: return (EOF); } } } /* Z_R_QINIT -- Wait for "rz\r", or ZRQINIT header. Send ZRINIT packet. Repeat at "response time intervals" Called from Z_RECEIVE. */ static short NEAR_ z_r_qinit_(COMM_PORT *p) { Z_PARMS *z = p->ppb; short i, c; for (i = z->retries; --i >= 0; ) { /* SET BUFFER LENGTH */ stohdr_(z, 0L); /* SET CAPABILITY FLAGS */ z->txhdr[ZF0] = z->l_cap; if (z->l_manag & TESCCTL) z->txhdr[ZF0] |= TESCCTL; /* SEND OUR 'ZRINIT' HEADER */ #if XDEBUG printf("\nSending ZRINIT header"); #endif z_s_hh_(p, z->tryhtype, z->txhdr); if (z->tryhtype == ZSKIP) z->tryhtype = ZRINIT; again: switch (z_r_h_(p, z->rxhdr, 0)) { case ZRQINIT: case ZEOF: case -2: #if XDEBUG printf("\nReceived ZEOF or ZRQINIT hdr."); #endif continue; case ZFILE: #if XDEBUG printf("\nReceived ZFILE hdr."); #endif z->r_conv = z->rxhdr[ZF0]; z->r_manag = z->rxhdr[ZF1]; z->r_trans = z->rxhdr[ZF2]; z->tryhtype = ZRINIT; c = z_r_data(p, z->rxbuf, 1024); if (c == GOTCRCW) return (ZFILE); else { z_s_hh_(p, ZNAK, z->txhdr); goto again; } case ZSINIT: #if XDEBUG printf("\nReceived ZSINIT hdr."); #endif z->ctlesc = TESCCTL & z->rxhdr[ZF0]; if (z_r_data(p, z->attn, ZATTNLEN) == GOTCRCW) { stohdr_(z, 1L); z_s_hh_(p, ZACK, z->txhdr); goto again; } z_s_hh_(p, ZNAK, z->txhdr); goto again; case ZFREECNT: #if XDEBUG printf("\nReceived ZSINIT hdr."); #endif stohdr_(z, -1L); z_s_hh_(p, ZACK, z->txhdr); goto again; case ZCOMMAND: #if XDEBUG printf("\nReceived ZCOMMAND hdr."); #endif cmdzack1flg = z->rxhdr[ZF0]; if (z_r_data(p, z->rxbuf, 1024) == GOTCRCW) { if (cmdzack1flg & ZCACK1) stohdr_(z, 0L); else; /* stohdr((long)sys2(secbuf)); */ c_rxflush(p, 0); do { z_s_hh_(p, ZCOMPL, z->txhdr); } while (++z->errors < 20 && z_r_h_(p, z->rxhdr, 1) != ZFIN); z_ack_fin_(p); #if 0 if (cmdzack1flg & ZCACK1) exec2(secbuf); #endif return (ZCOMPL); } z_s_hh_(p, ZNAK, z->txhdr); goto again; case ZCOMPL: #if XDEBUG printf("\nReceived ZCOMPL hdr."); #endif goto again; case ZFIN: #if XDEBUG printf("\nReceived ZFIN"); #endif z_ack_fin_(p); return (ZCOMPL); case ZCAN: #if XDEBUG printf("\nReceived ZCAN"); #endif return (EOF); default: #if XDEBUG printf("\nUnknown response"); #endif continue; } } return (0); } /* Z_RECEIVE_ -- Start a ZMODEM receive session. This is the state machine for receive events. */ short z_receive_(COMM_PORT *p) { short ret = 0; short DONE = FALSE, state = ZSIDLE; #if XDEBUG set_rx_xlat(p, LOCAL_ECHO, TRUE); #endif if (((Z_PARMS *)p->ppb)->x == NULL) ((Z_PARMS *)p->ppb)->x = p->x; do { switch (state) { case ZSIDLE: /* goes to ZFILE */ ((Z_PARMS *)p->ppb)->tryhtype = ZRINIT; state = z_r_qinit_(p); #if XDEBUG printf("\nzrqinit() returned %d", state); #endif break; case ZFILE: /* goes to ZCOMPL */ #if XDEBUG printf("\nEntering z_r_files\n"); #endif state = z_r_files_(p); #if XDEBUG printf("\nReturned %d from z_r_files", state); #endif break; case USER_CANCELLED: if (((XFER *)p->x)->p_user != NULL)(*((XFER *)p->x)->p_user)(p, USER_CANCELLED, 0L); state = ZABORT; ret = USER_CANCELLED; break; case ZABORT: state = z_s_abort_(p); DONE = TRUE; break; case ZCOMPL: DONE = TRUE; ret = ACK; break; default: ret = EOF; DONE = TRUE; } } while (!DONE); return (ret); }