455 lines
10 KiB
C
455 lines
10 KiB
C
/*****************************************************************************
|
|
*
|
|
* $Id$
|
|
* Purpose ...............: Ymodem sender
|
|
*
|
|
*****************************************************************************
|
|
* Copyright (C) 1997-2005
|
|
*
|
|
* 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/mbse.h"
|
|
#include "ttyio.h"
|
|
#include "zmmisc.h"
|
|
#include "transfer.h"
|
|
#include "openport.h"
|
|
#include "timeout.h"
|
|
#include "term.h"
|
|
#include "ymsend.h"
|
|
|
|
|
|
#define MAX_BLOCK 8192
|
|
#define sendline(c) PUTCHAR((c & 0377))
|
|
|
|
|
|
FILE *input_f;
|
|
char Crcflg;
|
|
char Lastrx;
|
|
int Fullname = 0; /* transmit full pathname */
|
|
int Filesleft;
|
|
long Totalleft;
|
|
long bytes_sent;
|
|
int firstsec;
|
|
int Optiong; /* Let it rip no wait for sector ACK's */
|
|
int Totsecs; /* total number of sectors this file */
|
|
char *txbuf;
|
|
size_t blklen = 128; /* length of transmitted records */
|
|
int zmodem_requested = FALSE;
|
|
static int no_unixmode;
|
|
struct timeval starttime, endtime;
|
|
struct timezone tz;
|
|
long skipsize;
|
|
|
|
|
|
extern int Rxtimeout;
|
|
|
|
|
|
static int wctxpn(char *);
|
|
static int getnak(void);
|
|
static int wctx(long);
|
|
static int wcputsec(char *, int, size_t);
|
|
static size_t filbuf(char *, size_t);
|
|
|
|
|
|
|
|
int ymsndfiles(down_list *lst, int use1k)
|
|
{
|
|
int maxrc = 0;
|
|
down_list *tmpf;
|
|
|
|
Syslog('+', "%s: start send files", protname());
|
|
txbuf = malloc(MAX_BLOCK);
|
|
|
|
/*
|
|
* Count files to transmit
|
|
*/
|
|
Totalleft = Filesleft = 0;
|
|
for (tmpf = lst; tmpf; tmpf = tmpf->next) {
|
|
if (tmpf->remote) {
|
|
Filesleft++;
|
|
Totalleft += tmpf->size;
|
|
}
|
|
}
|
|
Syslog('x', "%s: %d files, size %d bytes", protname(), Filesleft, Totalleft);
|
|
|
|
/*
|
|
* This is not documented as auto start sequence, but a lot of terminal
|
|
* programs seem to react on this.
|
|
*/
|
|
if (protocol == ZM_YMODEM)
|
|
PUTSTR((char *)"rb\r");
|
|
|
|
for (tmpf = lst; tmpf && (maxrc < 2); tmpf = tmpf->next) {
|
|
if (tmpf->remote) {
|
|
bytes_sent = 0;
|
|
skipsize = 0L;
|
|
Totsecs = 0;
|
|
|
|
switch (wctxpn(tmpf->remote)) {
|
|
case TERROR: Syslog('x', "wctxpn returns error");
|
|
tmpf->failed = TRUE;
|
|
maxrc = 2;
|
|
break;
|
|
case ZSKIP: Syslog('x', "wctxpn returns skip");
|
|
tmpf->failed = TRUE;
|
|
break;
|
|
case OK: gettimeofday(&starttime, &tz);
|
|
if ((blklen == 128) && use1k) {
|
|
Syslog('x', "%s: will use 1K blocks", protname());
|
|
blklen = 1024;
|
|
}
|
|
if (wctx(tmpf->size) == TERROR) {
|
|
Syslog('+', "%s: wctx returned error", protname());
|
|
tmpf->failed = TRUE;
|
|
} else {
|
|
tmpf->sent = TRUE;
|
|
gettimeofday(&endtime, &tz);
|
|
Syslog('+', "%s: OK %s", protname(),
|
|
transfertime(starttime, endtime, (unsigned long)tmpf->size - skipsize, TRUE));
|
|
}
|
|
}
|
|
|
|
} else if (maxrc == 0) {
|
|
tmpf->failed = TRUE;
|
|
}
|
|
if (unlink(tmpf->remote))
|
|
Syslog('+', "%s: could not unlink %s", protname(), tmpf->remote);
|
|
}
|
|
|
|
if (maxrc == 2) {
|
|
canit(1);
|
|
purgeline(50);
|
|
} else {
|
|
if (protocol == ZM_YMODEM) {
|
|
/*
|
|
* Send empty filename to signal end of batch
|
|
*/
|
|
wctxpn((char *)"");
|
|
}
|
|
}
|
|
|
|
if (txbuf)
|
|
free(txbuf);
|
|
txbuf = NULL;
|
|
io_mode(0, 1);
|
|
|
|
Syslog('+', "%s: end send files rc=%d", protname(), maxrc);
|
|
return (maxrc < 2)?0:maxrc;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* generate and transmit pathname block consisting of
|
|
* pathname (null terminated),
|
|
* file length, mode time and file mode in octal
|
|
* as provided by the Unix fstat call.
|
|
* N.B.: modifies the passed name, may extend it!
|
|
*/
|
|
static int wctxpn(char *fname)
|
|
{
|
|
register char *p, *q;
|
|
char *name2;
|
|
struct stat f;
|
|
|
|
name2 = alloca(PATH_MAX+1);
|
|
|
|
if ((input_f = fopen(fname, "r"))) {
|
|
fstat(fileno(input_f), &f);
|
|
|
|
Syslog('+', "%s: send \"%s\"", protname(), MBSE_SS(fname));
|
|
Syslog('+', "%s: size %lu bytes, dated %s", protname(), (unsigned long)f.st_size, rfcdate(f.st_mtime));
|
|
|
|
if (protocol == ZM_XMODEM) {
|
|
if (*fname) {
|
|
snprintf(name2, PATH_MAX +1, "Sending %s, %ld blocks: ", fname, (long) (f.st_size >> 7));
|
|
PUTSTR(name2);
|
|
Enter(1);
|
|
}
|
|
PUTSTR((char *)"Give your local XMODEM receive command now.");
|
|
Enter(1);
|
|
return OK;
|
|
}
|
|
} else {
|
|
/*
|
|
* Reset, this normally happens for the ymodem end of batch block.
|
|
*/
|
|
f.st_size = 0;
|
|
f.st_mtime = 0;
|
|
f.st_mode = 0;
|
|
}
|
|
|
|
if (!zmodem_requested)
|
|
if (getnak()) {
|
|
Syslog('+', "%s: timeout sending filename %s", protname(), MBSE_SS(fname));
|
|
return TERROR;
|
|
}
|
|
|
|
q = (char *) 0;
|
|
|
|
for (p = fname, q = txbuf ; *p; )
|
|
if ((*q++ = *p++) == '/' && !Fullname)
|
|
q = txbuf;
|
|
*q++ = 0;
|
|
p = q;
|
|
while (q < (txbuf + MAX_BLOCK))
|
|
*q++ = 0;
|
|
|
|
/*
|
|
* note that we may lose some information here in case mode_t is wider than an
|
|
* int. But i believe sending %lo instead of %o _could_ break compatability
|
|
*/
|
|
if ((input_f != stdin) && *fname)
|
|
snprintf(p, MAXBLOCK + 1024, "%lu %lo %o 0 %d %ld", (long) f.st_size, (long) f.st_mtime,
|
|
(unsigned int)((no_unixmode) ? 0 : f.st_mode), Filesleft, Totalleft);
|
|
|
|
Totalleft -= f.st_size;
|
|
if (--Filesleft <= 0)
|
|
Totalleft = 0;
|
|
if (Totalleft < 0)
|
|
Totalleft = 0;
|
|
|
|
purgeline(1);
|
|
if (wcputsec(txbuf, 0, 128) == TERROR) {
|
|
Syslog('+', "%s: failed to send filename", protname());
|
|
return TERROR;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Get NAK, C or G
|
|
*/
|
|
static int getnak(void)
|
|
{
|
|
int firstch;
|
|
int tries = 0;
|
|
|
|
Syslog('x', "getnak()");
|
|
Lastrx = 0;
|
|
|
|
for (;;) {
|
|
tries++;
|
|
switch (firstch = GETCHAR(10)) {
|
|
case TIMEOUT:
|
|
Syslog('x', "getnak: timeout try %d", tries);
|
|
/* 50 seconds are enough (was 30, 26-11-2004 MB) */
|
|
if (tries == 5) {
|
|
Syslog('x', "Timeout on pathname");
|
|
return TRUE;
|
|
}
|
|
continue;
|
|
case WANTG:
|
|
Syslog('x', "getnak: got WANTG");
|
|
io_mode(0, 2); /* Set cbreak, XON/XOFF, etc. */
|
|
if (!Optiong)
|
|
Syslog('+', "%s: using Ymodem-G", protname());
|
|
Optiong = TRUE;
|
|
blklen=1024;
|
|
Crcflg = TRUE;
|
|
return FALSE;
|
|
case WANTCRC:
|
|
Syslog('x', "getnak: got WANTCRC");
|
|
Crcflg = TRUE;
|
|
return FALSE;
|
|
case NAK:
|
|
Syslog('x', "getnak: got NAK");
|
|
return FALSE;
|
|
case CAN:
|
|
Syslog('x', "getnak: got CAN");
|
|
if ((firstch = GETCHAR(2)) == CAN && Lastrx == CAN) {
|
|
Syslog('+', "%s: Receiver cancelled", protname());
|
|
return TRUE;
|
|
}
|
|
default:
|
|
Syslog('x', "got %d", firstch);
|
|
break;
|
|
}
|
|
Lastrx = firstch;
|
|
}
|
|
Syslog('x', "getnak: done");
|
|
}
|
|
|
|
|
|
|
|
static int wctx(long bytes_total)
|
|
{
|
|
register size_t thisblklen;
|
|
register int sectnum, attempts, firstch;
|
|
|
|
firstsec=TRUE;
|
|
thisblklen = blklen;
|
|
Syslog('x', "wctx: file length=%ld, blklen=%d", bytes_total, blklen);
|
|
|
|
while ((firstch = GETCHAR(Rxtimeout)) != NAK && firstch != WANTCRC
|
|
&& firstch != WANTG && firstch != TIMEOUT && firstch != CAN);
|
|
if (firstch == CAN) {
|
|
Syslog('+', "%s: receiver Cancelled during transfer", protname());
|
|
return TERROR;
|
|
}
|
|
|
|
if (firstch == WANTCRC)
|
|
Crcflg = TRUE;
|
|
if (firstch == WANTG)
|
|
Crcflg = TRUE;
|
|
sectnum = 0;
|
|
|
|
for (;;) {
|
|
if (bytes_total <= (bytes_sent + 896L))
|
|
thisblklen = 128;
|
|
if ( !filbuf(txbuf, thisblklen))
|
|
break;
|
|
if (wcputsec(txbuf, ++sectnum, thisblklen) == TERROR)
|
|
return TERROR;
|
|
bytes_sent += thisblklen;
|
|
|
|
/*
|
|
* Keep connections alive
|
|
*/
|
|
Nopper();
|
|
alarm_on();
|
|
}
|
|
|
|
fclose(input_f);
|
|
attempts = 0;
|
|
|
|
do {
|
|
purgeline(5);
|
|
PUTCHAR(EOT);
|
|
++attempts;
|
|
} while ((firstch = (GETCHAR(Rxtimeout)) != ACK) && attempts < RETRYMAX);
|
|
if (attempts == RETRYMAX) {
|
|
Syslog('x', "No ACK on EOT");
|
|
return TERROR;
|
|
} else
|
|
return OK;
|
|
}
|
|
|
|
|
|
|
|
static int wcputsec(char *buf, int sectnum, size_t cseclen)
|
|
{
|
|
int Checksum, wcj;
|
|
char *cp;
|
|
unsigned oldcrc;
|
|
int firstch;
|
|
int attempts;
|
|
|
|
firstch = 0; /* part of logic to detect CAN CAN */
|
|
|
|
Syslog('x', "%s: wcputsec: sectnum %d, len %d, %s", protname(), sectnum, cseclen, Crcflg ? "crc":"checksum");
|
|
|
|
for (attempts = 0; attempts <= RETRYMAX; attempts++) {
|
|
Lastrx = firstch;
|
|
sendline(cseclen == 1024 ? STX:SOH);
|
|
sendline(sectnum);
|
|
sendline((-sectnum -1));
|
|
oldcrc = Checksum = 0;
|
|
for (wcj = cseclen, cp = buf; --wcj >= 0; ) {
|
|
sendline(*cp);
|
|
oldcrc = updcrc16((0377& *cp), oldcrc);
|
|
Checksum += *cp++;
|
|
}
|
|
if (Crcflg) {
|
|
oldcrc = updcrc16(0, updcrc16(0, oldcrc));
|
|
sendline((int)oldcrc >> 8);
|
|
sendline((int)oldcrc);
|
|
} else {
|
|
sendline(Checksum);
|
|
}
|
|
|
|
if (Optiong) {
|
|
firstsec = FALSE;
|
|
return OK;
|
|
}
|
|
firstch = GETCHAR(Rxtimeout);
|
|
gotnak:
|
|
switch (firstch) {
|
|
case CAN:
|
|
Syslog('x', "got CAN");
|
|
if(Lastrx == CAN) {
|
|
cancan:
|
|
Syslog('x', "Cancelled");
|
|
return TERROR;
|
|
}
|
|
break;
|
|
case TIMEOUT:
|
|
Syslog('x', "Timeout on sector ACK");
|
|
continue;
|
|
case WANTCRC:
|
|
if (firstsec)
|
|
Crcflg = TRUE;
|
|
case NAK:
|
|
Syslog('+', "%s: got NAK on sector %d", protname(), sectnum);
|
|
purgeline(20);
|
|
continue;
|
|
case ACK:
|
|
firstsec=FALSE;
|
|
Totsecs += (cseclen>>7);
|
|
return OK;
|
|
case TERROR:
|
|
Syslog('x', "Got burst for sector ACK");
|
|
break;
|
|
default:
|
|
Syslog('x', "Got %02x for sector ACK", firstch);
|
|
break;
|
|
}
|
|
for (;;) {
|
|
Lastrx = firstch;
|
|
if ((firstch = GETCHAR(Rxtimeout)) == TIMEOUT)
|
|
break;
|
|
if (firstch == NAK || firstch == WANTCRC)
|
|
goto gotnak;
|
|
if (firstch == CAN && Lastrx == CAN)
|
|
goto cancan;
|
|
}
|
|
}
|
|
Syslog('x', "Retry Count Exceeded");
|
|
return TERROR;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* fill buf with count chars padding with ^Z for CPM
|
|
*/
|
|
static size_t filbuf(char *buf, size_t count)
|
|
{
|
|
size_t m;
|
|
|
|
m = read(fileno(input_f), buf, count);
|
|
if (m <= 0)
|
|
return 0;
|
|
|
|
while (m < count)
|
|
buf[m++] = 032;
|
|
return count;
|
|
}
|
|
|
|
|
|
|