578 lines
12 KiB
C
578 lines
12 KiB
C
/*****************************************************************************
|
|
*
|
|
* File ..................: mbcico/zmrecv.c
|
|
* Purpose ...............: Fidonet mailer
|
|
* Last modification date : 10-Aug-2001
|
|
*
|
|
*****************************************************************************
|
|
* Copyright (C) 1997-2001
|
|
*
|
|
* 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, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*****************************************************************************/
|
|
|
|
#include "../lib/libs.h"
|
|
#include "../lib/structs.h"
|
|
#include "../lib/clcomm.h"
|
|
#include "../lib/common.h"
|
|
#include "lutil.h"
|
|
#include "ttyio.h"
|
|
#include "session.h"
|
|
#include "zmodem.h"
|
|
#include "config.h"
|
|
#include "emsi.h"
|
|
#include "openfile.h"
|
|
#include "openport.h"
|
|
|
|
|
|
#ifndef BELEIVE_ZFIN
|
|
#define BELEIVE_ZFIN 2
|
|
#endif
|
|
|
|
static FILE *fout=NULL;
|
|
|
|
static int Usevhdrs;
|
|
static long rxbytes;
|
|
static int Eofseen; /* indicates cpm eof (^Z) has been received */
|
|
static int errors;
|
|
static time_t startime,etime;
|
|
static long sbytes;
|
|
|
|
#define DEFBYTL 2000000000L /* default rx file size */
|
|
static long Bytesleft; /* number of bytes of incoming file left */
|
|
static long Modtime; /* Unix style mod time for incoming file */
|
|
static int Filemode; /* Unix style mode for incoming file */
|
|
#ifndef PATH_MAX
|
|
#define PATH_MAX 512
|
|
#endif
|
|
|
|
static int Thisbinary; /* current file is to be received in bin mode */
|
|
char Lzconv; /* Local ZMODEM file conversion request */
|
|
char Lzmanag; /* Local file management request */
|
|
|
|
static char *secbuf=0;
|
|
|
|
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 resync(off_t);
|
|
static int tryz(void);
|
|
static int rzfiles(void);
|
|
static int rzfile(void);
|
|
static void zmputs(char*);
|
|
int closeit(int);
|
|
static int putsec(char*,int);
|
|
static int procheader(char*);
|
|
static int ackbibi(void);
|
|
static long getfree(void);
|
|
|
|
|
|
void get_frame_buffer(void);
|
|
void free_frame_buffer(void);
|
|
|
|
extern unsigned long rcvdbytes;
|
|
|
|
|
|
|
|
int zmrcvfiles(void)
|
|
{
|
|
int rc;
|
|
|
|
Syslog('+', "Zmodem: start %s receive", (emsi_local_protos & PROT_ZAP)?"ZedZap":"Zmodem");
|
|
|
|
get_frame_buffer();
|
|
|
|
if (secbuf == NULL)
|
|
secbuf = malloc(MAXBLOCK+1);
|
|
tryzhdrtype = ZRINIT;
|
|
if ((rc = tryz()) < 0) {
|
|
Syslog('+', "Zmodem: could not initiate receive, rc=%d",rc);
|
|
} else
|
|
switch (rc) {
|
|
case ZCOMPL: rc = 0; break;
|
|
case ZFILE: rc = rzfiles(); break;
|
|
}
|
|
|
|
if (fout) {
|
|
if (closeit(0)) {
|
|
WriteError("Zmodem: Error closing file");
|
|
}
|
|
}
|
|
|
|
if (secbuf)
|
|
free(secbuf);
|
|
secbuf = NULL;
|
|
free_frame_buffer();
|
|
|
|
Syslog('z', "Zmodem: receive rc=%d",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
|
|
*/
|
|
int tryz(void)
|
|
{
|
|
int c, n;
|
|
int cmdzack1flg;
|
|
|
|
for (n = 15; --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;
|
|
Txhdr[ZF0] |= CANRLE;
|
|
Txhdr[ZF1] = CANVHDR;
|
|
zshhdr(4, tryzhdrtype, Txhdr);
|
|
if (tryzhdrtype == ZSKIP) /* Don't skip too far */
|
|
tryzhdrtype = ZRINIT; /* CAF 8-21-87 */
|
|
again:
|
|
switch (zgethdr(Rxhdr)) {
|
|
case ZRQINIT:
|
|
if (Rxhdr[ZF3] & 0x80)
|
|
Usevhdrs = TRUE; /* we can var header */
|
|
continue;
|
|
case ZEOF:
|
|
continue;
|
|
case TIMEOUT:
|
|
Syslog('+', "Zmodem: tryz() timeout attempt %d", n);
|
|
continue;
|
|
case ZFILE:
|
|
zconv = Rxhdr[ZF0];
|
|
zmanag = Rxhdr[ZF1];
|
|
ztrans = Rxhdr[ZF2];
|
|
if (Rxhdr[ZF3] & ZCANVHDR)
|
|
Usevhdrs = TRUE;
|
|
tryzhdrtype = ZRINIT;
|
|
c = zrdata(secbuf, MAXBLOCK);
|
|
if (c == GOTCRCW)
|
|
return ZFILE;
|
|
zshhdr(4,ZNAK, Txhdr);
|
|
goto again;
|
|
case ZSINIT:
|
|
Zctlesc = TESCCTL & Rxhdr[ZF0];
|
|
if (zrdata(Attn, ZATTNLEN) == GOTCRCW) {
|
|
stohdr(1L);
|
|
zshhdr(4,ZACK, Txhdr);
|
|
goto again;
|
|
}
|
|
zshhdr(4,ZNAK, Txhdr);
|
|
goto again;
|
|
case ZFREECNT:
|
|
stohdr(getfree());
|
|
zshhdr(4,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(4,ZCOMPL, Txhdr);
|
|
} while (++errors<20 && zgethdr(Rxhdr) != ZFIN);
|
|
return ackbibi();
|
|
}
|
|
zshhdr(4,ZNAK, Txhdr); goto again;
|
|
case ZCOMPL:
|
|
goto again;
|
|
case ZRINIT:
|
|
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;
|
|
|
|
Syslog('z', "rzfiles");
|
|
|
|
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;
|
|
|
|
Syslog('z', "rzfile");
|
|
|
|
Eofseen=FALSE;
|
|
rxbytes = 0l;
|
|
if ((c = procheader(secbuf))) {
|
|
return (tryzhdrtype = c);
|
|
}
|
|
|
|
n = 20;
|
|
|
|
for (;;) {
|
|
stohdr(rxbytes);
|
|
zshhdr(4,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('+', "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();
|
|
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(4,ZACK, Txhdr);
|
|
goto nxthdr;
|
|
case GOTCRCQ:
|
|
n = 20;
|
|
putsec(secbuf, Rxcount);
|
|
rxbytes += Rxcount;
|
|
stohdr(rxbytes);
|
|
zshhdr(4,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 resync(off_t off)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
int closeit(int success)
|
|
{
|
|
int rc;
|
|
|
|
rc = closefile(success);
|
|
fout = NULL;
|
|
sbytes = rxbytes - sbytes;
|
|
(void)time(&etime);
|
|
if ((startime = etime - startime) == 0L)
|
|
startime = 1L;
|
|
Syslog('+', "Zmodem: %s %lu bytes in %s (%ld cps)", success?"OK":"dropped after",
|
|
sbytes, str_time(startime), sbytes / startime);
|
|
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(4,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";
|
|
|
|
/*
|
|
* Process ZMODEM remote file management requests
|
|
*/
|
|
Thisbinary = (zconv != ZCNL); /* Remote ASCII override */
|
|
if (zmanag == ZMAPND)
|
|
openmode = (char *)"a";
|
|
|
|
Bytesleft = DEFBYTL;
|
|
Filemode = 0;
|
|
Modtime = 0L;
|
|
|
|
p = Name + 1 + strlen(Name);
|
|
sscanf(p, "%ld%lo%o%o%d%d%d%d", &Bytesleft, &Modtime, &Filemode, &dummy, &dummy, &dummy, &dummy, &dummy);
|
|
strcpy(ctt,date(Modtime));
|
|
Syslog('+', "Zmodem: \"%s\" %ld bytes, %s mode %o", Name, Bytesleft, ctt, Filemode);
|
|
|
|
fout = openfile(Name,Modtime,Bytesleft,&(long)(rxbytes),resync);
|
|
(void)time(&startime);
|
|
sbytes = rxbytes;
|
|
|
|
if (Bytesleft == rxbytes) {
|
|
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) {
|
|
for (p = buf; --n>=0; )
|
|
putc( *p++, fout);
|
|
} 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;
|
|
}
|
|
|
|
|
|
|
|
long getfree(void)
|
|
{
|
|
struct statfs sfs;
|
|
|
|
if (statfs(inbound, &sfs) != 0) {
|
|
WriteError("$cannot statfs \"%s\", assume enough space", inbound);
|
|
return -1L;
|
|
} else
|
|
return (sfs.f_bsize * sfs.f_bfree);
|
|
}
|
|
|
|
|