This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
deb-mbse/mbsebbs/zmrecv.c
2006-03-18 18:32:22 +00:00

636 lines
15 KiB
C

/*****************************************************************************
*
* $Id$
* Purpose ...............: Zmodem receive
*
*****************************************************************************
* Copyright (C) 1997-2006
*
* Michiel Broek FIDO: 2:280/2802
* Beekmansbos 10
* 1971 BV IJmuiden
* the Netherlands
*
* This file is part of MBSE BBS.
*
* This BBS is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* MBSE BBS is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MBSE BBS; see the file COPYING. If not, write to the Free
* Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*****************************************************************************/
#include "../config.h"
#include "../lib/mbselib.h"
#include "../lib/users.h"
#include "ttyio.h"
#include "transfer.h"
#include "zmmisc.h"
#include "zmrecv.h"
#include "ymrecv.h"
#include "openport.h"
#include "timeout.h"
#include "input.h"
static FILE *fout = NULL;
char *curfile = NULL;
off_t rxbytes;
static int Eofseen; /* indicates cpm eof (^Z) has been received */
static int errors;
int sbytes;
struct timeval starttime, endtime;
struct timezone tz;
#define DEFBYTL 2000000000L /* default rx file size */
int Bytesleft; /* number of bytes of incoming file left */
static int Modtime; /* Unix style mod time for incoming file */
static int Filemode; /* Unix style mode for incoming file */
static int Thisbinary = TRUE; /* current file is to be received in bin mode */
char *secbuf=0; /* "sector" buffer */
static int tryzhdrtype;
static char zconv; /* ZMODEM file conversion request */
static char zmanag; /* ZMODEM file management request */
static char ztrans; /* ZMODEM file transport request */
static int tryz(void);
static int rzfiles(void);
static int rzfile(void);
static void zmputs(char*);
static int ackbibi(void);
static int getfree(void);
extern unsigned int rcvdbytes;
extern int zmodem_requested;
extern int Rxtimeout;
/*
* Receive files with Zmodem, Ymodem or Xmodem.
* This receiver will figure out what to do, you should
* be able to send anything.
*/
int zmrcvfiles(int want1k, int wantg)
{
int rc;
Syslog('+', "%s: start receive", protname());
get_frame_buffer();
Rxtimeout = 10;
if (secbuf == NULL)
secbuf = malloc(MAXBLOCK+1);
tryzhdrtype = ZRINIT;
if ((rc = tryz()) < 0) {
Syslog('+', "%s: could not initiate receive, rc=%d", protname(), rc);
} else {
if (rc == 0) {
if (protocol == ZM_ZMODEM) {
Syslog('+', "%s: switching to Ymodem", protname());
protocol = ZM_YMODEM;
}
rc = ymrcvfiles(want1k, wantg);
goto fubar;
}
/*
* Zmodem receiver
*/
switch (rc) {
case ZCOMPL: rc = 0;
break;
case ZFILE: rc = rzfiles();
break;
}
}
fubar:
if (fout) {
if (closeit(0)) {
WriteError("%s: Error closing file", protname);
}
}
if (secbuf)
free(secbuf);
secbuf = NULL;
io_mode(0, 1); /* Normal raw mode */
/*
* Some programs send some garbage after the transfer, eat these.
* This also introduces a pause after the transfer, some clients
* need this.
*/
purgeline(200);
Syslog('+', "%s: end receive rc=%d", protname(), rc);
return abs(rc);
}
/*
* 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: can be ymodem.
*/
int tryz(void)
{
int c, n;
int cmdzack1flg;
if (protocol != ZM_ZMODEM)
return 0;
for (n = zmodem_requested ?15:10; --n >= 0; ) {
/*
* Set buffer length (0) and capability flags
*/
Syslog('z', "tryz attempt %d", n);
stohdr(0L);
Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO;
if (Zctlesc)
Txhdr[ZF0] |= TESCCTL;
zshhdr(tryzhdrtype, Txhdr);
if (tryzhdrtype == ZSKIP) /* Don't skip too far */
tryzhdrtype = ZRINIT; /* CAF 8-21-87 */
again:
switch (zgethdr(Rxhdr)) {
case ZRQINIT: continue;
case ZEOF: continue;
case TIMEOUT: Syslog('z', "Zmodem: tryz() timeout attempt %d", n);
continue;
case ZFILE: zconv = Rxhdr[ZF0];
if (!zconv) {
Syslog('z', "*** !zconv %d", zconv);
zconv = ZCBIN;
}
zmanag = Rxhdr[ZF1];
ztrans = Rxhdr[ZF2];
tryzhdrtype = ZRINIT;
c = zrdata(secbuf, MAXBLOCK);
io_mode(0, 3);
if (c == GOTCRCW) {
Syslog('z', "tryz return ZFILE");
return ZFILE;
}
zshhdr(ZNAK, Txhdr);
goto again;
case ZSINIT: /* this once was:
* Zctlesc = TESCCTL & Rxhdr[ZF0];
* trouble: if rz get --escape flag:
* - it sends TESCCTL to sz,
* get a ZSINIT _without_ TESCCTL (yeah - sender didn't know),
* overwrites Zctlesc flag ...
* - sender receives TESCCTL and uses "|=..."
* so: sz escapes, but rz doesn't unescape ... not good.
*/
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 ZCOMMAND: cmdzack1flg = Rxhdr[ZF0];
if (zrdata(secbuf, MAXBLOCK) == GOTCRCW) {
if (cmdzack1flg & ZCACK1)
stohdr(0L);
else
Syslog('+', "Zmodem: request for command \"%s\" ignored", printable(secbuf,-32));
stohdr(0L);
do {
zshhdr(ZCOMPL, Txhdr);
} while (++errors<20 && zgethdr(Rxhdr) != ZFIN);
return ackbibi();
}
zshhdr(ZNAK, Txhdr);
goto again;
case ZCOMPL: goto again;
case ZRINIT: Syslog('z', "tryz: got ZRINIT");
return TERROR;
case ZFIN: /* do not beleive in first ZFIN */
ackbibi();
return ZCOMPL;
case TERROR:
case HANGUP:
case ZCAN: return TERROR;
default: continue;
}
}
return 0;
}
/*
* Receive 1 or more files with ZMODEM protocol
*/
int rzfiles(void)
{
int c;
for (;;) {
switch (c = rzfile()) {
case ZEOF:
case ZSKIP:
case ZFERR: switch (tryz()) {
case ZCOMPL: return OK;
default: return TERROR;
case ZFILE: break;
}
continue;
default: return c;
case TERROR: return TERROR;
}
}
/* NOTREACHED */
}
/*
* Receive a file with ZMODEM protocol
* Assumes file name frame is in secbuf
*/
int rzfile(void)
{
int c, n;
Eofseen=FALSE;
rxbytes = 0l;
if ((c = procheader(secbuf))) {
return (tryzhdrtype = c);
}
n = 20;
for (;;) {
Syslog('z', "rxbytes %ld", rxbytes);
stohdr(rxbytes);
zshhdr(ZRPOS, Txhdr);
nxthdr:
switch (c = zgethdr(Rxhdr)) {
default: Syslog('z', "rzfile: Wrong header %d", c);
if ( --n < 0) {
Syslog('+', "Zmodem: wrong header %d", c);
return TERROR;
}
continue;
case ZCAN: Syslog('+', "Zmodem: sender CANcelled");
return TERROR;
case ZNAK: if ( --n < 0) {
Syslog('+', "Zmodem: Got ZNAK");
return TERROR;
}
continue;
case TIMEOUT: if ( --n < 0) {
Syslog('z', "Zmodem: TIMEOUT");
return TERROR;
}
continue;
case ZFILE: zrdata(secbuf, MAXBLOCK);
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.
*/
errors = 0;
goto nxthdr;
}
if (closeit(1)) {
tryzhdrtype = ZFERR;
Syslog('+', "Zmodem: error closing file");
return TERROR;
}
fout = NULL;
Syslog('z', "rzfile: normal EOF");
return c;
case HANGUP: Syslog('+', "Zmodem: Lost Carrier");
return TERROR;
case TERROR: /* Too much garbage in header search error */
if (--n < 0) {
Syslog('+', "Zmodem: Too many errors");
return TERROR;
}
zmputs(Attn);
continue;
case ZSKIP: Modtime = 1;
closeit(1);
Syslog('+', "Zmodem: Sender SKIPPED file");
return c;
case ZDATA: if (rclhdr(Rxhdr) != rxbytes) {
if ( --n < 0) {
Syslog('+', "Zmodem: Data has bad address");
return TERROR;
}
zmputs(Attn);
continue;
}
moredata:
Syslog('Z', "%7ld ZMODEM%s ", rxbytes, Crc32r?" CRC-32":"");
Nopper();
alarm_on();
switch (c = zrdata(secbuf, MAXBLOCK)) {
case ZCAN: Syslog('+', "Zmodem: sender CANcelled");
return TERROR;
case HANGUP: Syslog('+', "Zmodem: Lost Carrier");
return TERROR;
case TERROR: /* CRC error */
if (--n < 0) {
Syslog('+', "Zmodem: Too many errors");
return TERROR;
}
zmputs(Attn);
continue;
case TIMEOUT: if ( --n < 0) {
Syslog('+', "Zmodem: TIMEOUT");
return TERROR;
}
continue;
case GOTCRCW: n = 20;
putsec(secbuf, Rxcount);
rxbytes += Rxcount;
stohdr(rxbytes);
PUTCHAR(XON);
zshhdr(ZACK, Txhdr);
goto nxthdr;
case GOTCRCQ: n = 20;
putsec(secbuf, Rxcount);
rxbytes += Rxcount;
stohdr(rxbytes);
zshhdr(ZACK, Txhdr);
goto moredata;
case GOTCRCG: n = 20;
putsec(secbuf, Rxcount);
rxbytes += Rxcount;
goto moredata;
case GOTCRCE: n = 20;
putsec(secbuf, Rxcount);
rxbytes += Rxcount;
goto nxthdr;
}
}
}
}
/*
* Send a string to the modem, processing for \336 (sleep 1 sec)
* and \335 (break signal)
*/
void zmputs(char *s)
{
int c;
Syslog('z', "zmputs: \"%s\"", printable(s, strlen(s)));
while (*s) {
switch (c = *s++) {
case '\336': Syslog('z', "zmputs: sleep(1)");
sleep(1);
continue;
case '\335': Syslog('z', "zmputs: send break");
sendbrk();
continue;
default: PUTCHAR(c);
}
}
}
int closeit(int success)
{
struct utimbuf ut;
int rc;
Syslog('z', "closeit(%d)", success);
if ((fout == NULL) || (curfile == NULL)) {
Syslog('+', "closeit(), nothing to close");
return 1;
}
rc = fclose(fout);
fout = NULL;
if (rc == 0) {
ut.actime = Modtime;
ut.modtime = Modtime;
if ((rc = utime(curfile, &ut)))
WriteError("$utime failed");
}
free(curfile);
curfile = NULL;
sbytes = rxbytes - sbytes;
gettimeofday(&endtime, &tz);
if (success)
Syslog('+', "%s: OK %s", protname(), transfertime(starttime, endtime, sbytes, FALSE));
else
Syslog('+', "%s: dropped after %lu bytes", protname(), sbytes);
rcvdbytes += sbytes;
return rc;
}
/*
* Ack a ZFIN packet, let byegones be byegones
*/
int ackbibi(void)
{
int n;
int c;
Syslog('z', "ackbibi:");
stohdr(0L);
for (n=3; --n>=0; ) {
zshhdr(ZFIN, Txhdr);
switch ((c = GETCHAR(10))) {
case 'O': GETCHAR(1); /* Discard 2nd 'O' */
Syslog('z', "Zmodem: ackbibi complete");
return ZCOMPL;
case TERROR:
case HANGUP: Syslog('z', "Zmodem: ackbibi got %d, ignore",c);
return 0;
case TIMEOUT:
default: Syslog('z', "Zmodem: ackbibi got '%s', continue", printablec(c));
break;
}
}
return ZCOMPL;
}
/*
* Process incoming file information header
*/
int procheader(char *Name)
{
register char *openmode, *p;
static int dummy;
char ctt[32];
Syslog('z', "procheader \"%s\"",printable(Name,0));
/* set default parameters and overrides */
openmode = (char *)"w";
/*
* Check slashes in the name
*/
p = strrchr(Name,'/');
if (p) {
p++;
if (!*p) {
/* alert - file name ended in with a / */
Syslog('!', "%s: file name ends with a /, skipped: %s", protname(), Name);
return ZFERR;
}
Name = p;
Syslog('z', "filename converted to \"%s\"", MBSE_SS(Name));
}
if (strlen(Name) > 80) {
Syslog('!', "%s: file name received is longer then 80 characters, skipped: %s", protname(), Name);
return ZFERR;
}
Syslog('z', "zmanag=%d", zmanag);
Syslog('z', "zconv=%d", zconv);
/*
* Process ZMODEM remote file management requests
*/
if (!Thisbinary && zconv == ZCNL) /* Remote ASCII override */
Thisbinary = FALSE;
if (zconv == ZCBIN) /* Remote Binary override */
Thisbinary = TRUE;
if (zmanag == ZMAPND)
openmode = (char *)"a";
Syslog('z', "Thisbinary %s", Thisbinary ?"TRUE":"FALSE");
Bytesleft = DEFBYTL;
Filemode = 0;
Modtime = 0L;
Eofseen = FALSE;
p = Name + 1 + strlen(Name);
if (*p) { /* file coming from Unix or DOS system */
sscanf(p, "%d%o%o%o%d%d%d%d", &Bytesleft, &Modtime, &Filemode, &dummy, &dummy, &dummy, &dummy, &dummy);
strcpy(ctt, rfcdate(Modtime));
} else {
Syslog('z', "File coming from a CP/M system");
}
Syslog('+', "%s: \"%s\" %ld bytes, %s mode %o", protname(), Name, Bytesleft, ctt, Filemode);
if (curfile)
free(curfile);
curfile = NULL;
curfile = xstrcpy(CFG.bbs_usersdir);
curfile = xstrcat(curfile, (char *)"/");
curfile = xstrcat(curfile, exitinfo.Name);
curfile = xstrcat(curfile, (char *)"/upl/");
curfile = xstrcat(curfile, Name);
Syslog('z', "try open %s mode \"%s\"", curfile, openmode);
if ((fout = fopen(curfile, openmode)) == NULL) {
WriteError("$Can't open %s mode %s", curfile, openmode);
}
gettimeofday(&starttime, &tz);
sbytes = rxbytes = 0;
Syslog('z', "result %s", fout ? "Ok":"Failed");
/* if (Bytesleft == rxbytes) { FIXME: if file already received, use this.
Syslog('+', "Zmodem: Skipping %s", Name);
fout = NULL;
return ZSKIP;
} else */ if (!fout)
return ZFERR;
else
return 0;
}
/*
* Putsec writes the n characters of buf to receive file fout.
* If not in binary mode, carriage returns, and all characters
* starting with CPMEOF are discarded.
*/
int putsec(char *buf, int n)
{
register char *p;
if (n == 0)
return OK;
if (Thisbinary) {
if (fwrite(buf, n, 1, fout) != 1)
return ERROR;
} else {
if (Eofseen)
return OK;
for (p = buf; --n>=0; ++p ) {
if ( *p == '\r')
continue;
if (*p == SUB) {
Eofseen=TRUE;
return OK;
}
putc(*p ,fout);
}
}
return OK;
}
int getfree(void)
{
struct statfs sfs;
char *temp;
temp = calloc(PATH_MAX, sizeof(char));
snprintf(temp, PATH_MAX, "%s/%s/upl", CFG.bbs_usersdir, exitinfo.Name);
if (statfs(temp, &sfs) != 0) {
WriteError("$cannot statfs \"%s\", assume enough space", temp);
free(temp);
return -1L;
}
free(temp);
return (sfs.f_bsize * sfs.f_bfree);
}