From eb4739237383338c4aa7a90fa7514cbe35b0273b Mon Sep 17 00:00:00 2001 From: Michiel Broek Date: Mon, 22 Nov 2004 22:05:57 +0000 Subject: [PATCH] Added xmodem and ymodem download --- ChangeLog | 8 ++ mbsebbs/Makefile | 6 +- mbsebbs/transfer.c | 19 +++- mbsebbs/ymsend.c | 264 ++++++++++++++++++++++++++++----------------- mbsebbs/ymsend.h | 3 + mbsebbs/zmmisc.c | 33 +++--- mbsebbs/zmmisc.h | 1 + mbsebbs/zmrecv.c | 10 +- 8 files changed, 220 insertions(+), 124 deletions(-) diff --git a/ChangeLog b/ChangeLog index 34f3f40c..2cfb3b02 100644 --- a/ChangeLog +++ b/ChangeLog @@ -46,6 +46,14 @@ v0.71.0 27-Oct-2004 With user chat, the timeout timer wasn't refreshed. Rewrote the rewritten terminal i/o to get zmodem upload to work. + Added ymodem and xmodem download protocols, both with option + for 1k blocks. Not yet enabled for public use also. + + Note: you could enable the internal protocols by adding these + to the protocol setup. But I recommend against it because not + yet all safety checks for uploads are installed. When the time + is right I will change mbsetup so that these protocols will be + automatic added. mbnewusr: Rewrote terminal i/o. diff --git a/mbsebbs/Makefile b/mbsebbs/Makefile index b0689e07..83dfe3a7 100644 --- a/mbsebbs/Makefile +++ b/mbsebbs/Makefile @@ -112,7 +112,7 @@ newuser.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h funcs.h inp pinfo.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h pinfo.h input.h term.h ttyio.h timecheck.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h timecheck.h funcs.h bye.h exitinfo.h language.h input.h term.h ttyio.h change.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h change.h dispfile.h funcs.h input.h language.h misc.h timeout.h exitinfo.h bye.h term.h ttyio.h -transfer.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h transfer.h change.h whoson.h funcs.h term.h ttyio.h filesub.h language.h openport.h timeout.h zmsend.h zmrecv.h +transfer.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h transfer.h change.h whoson.h funcs.h term.h ttyio.h filesub.h language.h openport.h timeout.h zmmisc.h zmsend.h zmrecv.h ymsend.h ymrecv.h exitinfo.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h funcs.h input.h language.h oneline.h misc.h bye.h timeout.h timecheck.h exitinfo.h mbsebbs.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h ../lib/msg.h mbsebbs.h user.h dispfile.h language.h menu.h misc.h bye.h timeout.h funcs.h term.h ttyio.h openport.h menu.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h oneline.h mail.h bbslist.h change.h chat.h file.h funcs.h input.h misc.h timeout.h menu.h page.h pinfo.h bye.h timecheck.h whoson.h language.h offline.h email.h door.h dispfile.h userlist.h timestats.h logentry.h morefile.h lastcallers.h signature.h term.h ttyio.h @@ -132,9 +132,9 @@ dispfile.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h ../lib/msg userlist.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h userlist.h language.h input.h timeout.h term.h ttyio.h timestats.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h timestats.h funcs.h language.h input.h exitinfo.h term.h logentry.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ../lib/users.h logentry.h -ymsend.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ymsend.h ttyio.h zmmisc.h transfer.h openport.h timeout.h term.h +ymsend.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ttyio.h zmmisc.h transfer.h openport.h timeout.h term.h ymsend.h ymrecv.o: ../config.h ../lib/mbselib.h ../lib/mbse.h ymrecv.h -zmmisc.o: ../config.h ../lib/mbselib.h ttyio.h zmmisc.h +zmmisc.o: ../config.h ../lib/mbselib.h ttyio.h input.h zmmisc.h zmsend.o: ../config.h ../lib/mbselib.h ttyio.h zmmisc.h transfer.h openport.h timeout.h zmrecv.o: ../config.h ../lib/mbselib.h ../lib/users.h ttyio.h transfer.h zmmisc.h zmrecv.h openport.h timeout.h input.h # End of generated dependencies diff --git a/mbsebbs/transfer.c b/mbsebbs/transfer.c index 50c802d2..b2986b67 100644 --- a/mbsebbs/transfer.c +++ b/mbsebbs/transfer.c @@ -42,8 +42,11 @@ #include "language.h" #include "openport.h" #include "timeout.h" +#include "zmmisc.h" #include "zmsend.h" #include "zmrecv.h" +#include "ymsend.h" +#include "ymrecv.h" /* @@ -259,16 +262,24 @@ int download(down_list *download_list) sleep(2); if (uProtInternal) { + sprintf(temp, "%s/%s/tag", CFG.bbs_usersdir, exitinfo.Name); + chdir(temp); if (strncasecmp(sProtName, "zmodem 8k", 9) == 0) { - sprintf(temp, "%s/%s/tag", CFG.bbs_usersdir, exitinfo.Name); - chdir(temp); maxrc = zmsndfiles(download_list, TRUE); Home(); } else if (strncasecmp(sProtName, "zmodem", 6) == 0) { - sprintf(temp, "%s/%s/tag", CFG.bbs_usersdir, exitinfo.Name); - chdir(temp); maxrc = zmsndfiles(download_list, FALSE); Home(); + } else if ((strncasecmp(sProtName, "xmodem", 6) == 0) || (strncasecmp(sProtName, "ymodem", 6) == 0)) { + if (strncasecmp(sProtName, "xmodem", 6) == 0) + protocol = ZM_XMODEM; + else + protocol = ZM_YMODEM; + if (strstr(sProtName, "1K") || strstr(sProtName, "1k")) + maxrc = ymsndfiles(download_list, TRUE); + else + maxrc = ymsndfiles(download_list, FALSE); + Home(); } else { Syslog('!', "Warning internal protocol %s not supported", sProtName); maxrc = 1; diff --git a/mbsebbs/ymsend.c b/mbsebbs/ymsend.c index 2940f130..dbed96d0 100644 --- a/mbsebbs/ymsend.c +++ b/mbsebbs/ymsend.c @@ -31,45 +31,124 @@ #include "../config.h" #include "../lib/mbselib.h" #include "../lib/mbse.h" -#include "ymsend.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 Ascii=0; /* Add CR's for brain damaged programs */ -int firstsec; -int Optiong; /* Let it rip no wait for sector ACK's */ -int Lfseen=0; -int Totsecs; /* total number of sectors this file */ -char *txbuf; -size_t blklen=128; /* length of transmitted records */ -int zmodem_requested = 0; -int Dottoslash=0; /* Change foo.bar.baz to foo/bar/baz */ -static int no_unixmode; +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 = 0; +static int no_unixmode; +struct timeval starttime, endtime; +struct timezone tz; +long skipsize; + extern int Rxtimeout; +static int wctxpn(char *); static int getnak(void); -int wcputsec(char *, int, size_t); +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); + + 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) == ERROR) { + Syslog('x', "wctx returned error"); + 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 (protocol == ZM_YMODEM) { + /* + * Send empty filename to signal end of batch + */ + wctxpn((char *)""); + } + + if (txbuf) + free(txbuf); + txbuf = NULL; + io_mode(0, 1); + + Syslog('x', "%s: send rc=%d", protname(), maxrc); + return (maxrc < 2)?0:maxrc; +} + + + /* * generate and transmit pathname block consisting of * pathname (null terminated), @@ -84,39 +163,40 @@ static int wctxpn(char *fname) struct stat f; name2 = alloca(PATH_MAX+1); - input_f = fopen(fname, "r"); - if (protocol == ZM_XMODEM) { - if (*fname && fstat(fileno(input_f), &f) != -1) { - Syslog('y', "Sending %s, %ld blocks: ", fname, (long) (f.st_size >> 7)); + 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) { + sprintf(name2, "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; } - 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 (!zmodem_requested) if (getnak()) { PUTSTR((char *)"getnak failed"); - Syslog('+', "%s/%s: getnak failed", fname, protname()); - return ERROR; + Syslog('+', "%s/%s: getnak failed", MBSE_SS(fname), protname()); + return TERROR; } q = (char *) 0; - if (Dottoslash) { /* change . to . */ - for (p = fname; *p; ++p) { - if (*p == '/') - q = p; - else if (*p == '.') - *(q=p) = '/'; - } - if (q && strlen(++q) > 8) { /* If name>8 chars */ - q += 8; /* make it .ext */ - strcpy(name2, q); /* save excess of name */ - *q = '.'; - strcpy(++q, name2); /* add it back */ - } - } for (p = fname, q = txbuf ; *p; ) if ((*q++ = *p++) == '/' && !Fullname) @@ -125,21 +205,24 @@ static int wctxpn(char *fname) 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 (!Ascii && (input_f != stdin) && *fname && fstat(fileno(input_f), &f)!= -1) + if ((input_f != stdin) && *fname) sprintf(p, "%lu %lo %o 0 %d %ld", (long) f.st_size, f.st_mtime, (unsigned int)((no_unixmode) ? 0 : f.st_mode), Filesleft, Totalleft); -// if (Verbose) -// vstringf(_("Sending: %s\n"),txbuf); + Syslog('x', "Sending: %s", txbuf); + Totalleft -= f.st_size; if (--Filesleft <= 0) Totalleft = 0; if (Totalleft < 0) Totalleft = 0; + Syslog('x', "Totalleft = %d", Totalleft); + /* force 1k blocks if name won't fit in 128 byte block */ if (txbuf[125]) blklen=1024; @@ -152,7 +235,7 @@ static int wctxpn(char *fname) if (wcputsec(txbuf, 0, 128)==ERROR) { PUTSTR((char *)"wcputsec failed"); Syslog('+', "%s/%s: wcputsec failed", fname,protname()); - return ERROR; + return TERROR; } return OK; } @@ -164,7 +247,9 @@ static int getnak(void) int firstch; int tries = 0; + Syslog('x', "getnak()"); Lastrx = 0; + for (;;) { tries++; switch (firstch = GETCHAR(10)) { @@ -174,9 +259,10 @@ static int getnak(void) // Ascii = 0; /* Receiver does the conversion */ // return FALSE; case TIMEOUT: + Syslog('x', "getnak: timeout try %d", tries); /* 30 seconds are enough */ if (tries == 3) { - Syslog('y', "Timeout on pathname"); + Syslog('x', "Timeout on pathname"); return TRUE; } /* don't send a second ZRQINIT _directly_ after the @@ -192,14 +278,18 @@ static int getnak(void) // } continue; case WANTG: + Syslog('x', "getnak: got WANTG"); io_mode(0, 2); /* Set cbreak, XON/XOFF, etc. */ Optiong = TRUE; blklen=1024; case WANTCRC: + Syslog('x', "getnak: got WANTCRC"); Crcflg = TRUE; case NAK: + Syslog('x', "getnak: got NAK"); return FALSE; case CAN: + Syslog('x', "getnak: got CAN"); if ((firstch = GETCHAR(2)) == CAN && Lastrx == CAN) return TRUE; default: @@ -207,6 +297,7 @@ static int getnak(void) } Lastrx = firstch; } + Syslog('x', "getnak: done"); } @@ -217,13 +308,13 @@ static int wctx(long bytes_total) register int sectnum, attempts, firstch; firstsec=TRUE; thisblklen = blklen; - Syslog('y', "wctx:file length=%ld", bytes_total); + 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('y', "Receiver Cancelled"); - return ERROR; + Syslog('x', "Receiver Cancelled"); + return TERROR; } if (firstch == WANTCRC) @@ -238,7 +329,7 @@ static int wctx(long bytes_total) if ( !filbuf(txbuf, thisblklen)) break; if (wcputsec(txbuf, ++sectnum, thisblklen) == TERROR) - return ERROR; + return TERROR; bytes_sent += thisblklen; } @@ -246,21 +337,21 @@ static int wctx(long bytes_total) attempts = 0; do { -// purgeline(io_mode_fd); + purgeline(5); PUTCHAR(EOT); fflush(stdout); ++attempts; } while ((firstch = (GETCHAR(Rxtimeout)) != ACK) && attempts < RETRYMAX); if (attempts == RETRYMAX) { - Syslog('y', "No ACK on EOT"); - return ERROR; + Syslog('x', "No ACK on EOT"); + return TERROR; } else return OK; } -int wcputsec(char *buf, int sectnum, size_t cseclen) +static int wcputsec(char *buf, int sectnum, size_t cseclen) { int Checksum, wcj; char *cp; @@ -269,8 +360,9 @@ int wcputsec(char *buf, int sectnum, size_t cseclen) int attempts; firstch = 0; /* part of logic to detect CAN CAN */ - - Syslog('y', "%s sectors/kbytes sent: %3d/%2dk", protname(), Totsecs, Totsecs/8 ); + + Syslog('x', "wcputsec: sectnum %d, len %d", sectnum, cseclen); + Syslog('x', "%s sectors/kbytes sent: %3d/%2dk", protname(), Totsecs, Totsecs/8 ); for (attempts = 0; attempts <= RETRYMAX; attempts++) { Lastrx = firstch; @@ -300,30 +392,31 @@ int wcputsec(char *buf, int sectnum, size_t cseclen) gotnak: switch (firstch) { case CAN: + Syslog('x', "got CAN"); if(Lastrx == CAN) { cancan: - Syslog('y', "Cancelled"); + Syslog('x', "Cancelled"); return ERROR; } break; case TIMEOUT: - Syslog('y', "Timeout on sector ACK"); + Syslog('x', "Timeout on sector ACK"); continue; case WANTCRC: if (firstsec) Crcflg = TRUE; case NAK: - Syslog('y', "NAK on sector"); + Syslog('x', "NAK on sector"); continue; - case ACK: + case ACK: firstsec=FALSE; Totsecs += (cseclen>>7); return OK; case TERROR: - Syslog('y', "Got burst for sector ACK"); + Syslog('x', "Got burst for sector ACK"); break; default: - Syslog('y', "Got %02x for sector ACK", firstch); + Syslog('x', "Got %02x for sector ACK", firstch); break; } for (;;) { @@ -336,46 +429,25 @@ cancan: goto cancan; } } - Syslog('y', "Retry Count Exceeded"); - return ERROR; + Syslog('x', "Retry Count Exceeded"); + return TERROR; } -/* fill buf with count chars padding with ^Z for CPM */ +/* + * fill buf with count chars padding with ^Z for CPM + */ static size_t filbuf(char *buf, size_t count) { - int c; size_t m; - if ( !Ascii) { - m = read(fileno(input_f), buf, count); - if (m <= 0) - return 0; - while (m < count) - buf[m++] = 032; - return count; - } - m=count; - if (Lfseen) { - *buf++ = 012; --m; Lfseen = 0; - } - while ((c=getc(input_f))!=EOF) { - if (c == 012) { - *buf++ = 015; - if (--m == 0) { - Lfseen = TRUE; break; - } - } - *buf++ =c; - if (--m == 0) - break; - } - if (m==count) - return 0; - else - while (m--!=0) - *buf++ = CPMEOF; + m = read(fileno(input_f), buf, count); + if (m <= 0) + return 0; + + while (m < count) + buf[m++] = 032; return count; } diff --git a/mbsebbs/ymsend.h b/mbsebbs/ymsend.h index 0424dfea..f18c8524 100644 --- a/mbsebbs/ymsend.h +++ b/mbsebbs/ymsend.h @@ -9,4 +9,7 @@ #define WANTCRC 0103 /* send C not NAK to get crc not checksum */ #define WANTG 0107 /* Send G not NAK to get nonstop batch xmsn */ + +int ymsndfiles(down_list *, int); + #endif diff --git a/mbsebbs/zmmisc.c b/mbsebbs/zmmisc.c index a590f28b..2e2e18bc 100644 --- a/mbsebbs/zmmisc.c +++ b/mbsebbs/zmmisc.c @@ -66,6 +66,7 @@ static inline void zsendline_s(const char *, int); #include "../config.h" #include "../lib/mbselib.h" #include "ttyio.h" +#include "input.h" #include "zmmisc.h" @@ -956,20 +957,26 @@ long rclhdr(register char *shdr) char *protname(void) { - const char *prot_name; - - switch(protocol) { - case ZM_XMODEM: - prot_name = (char *)"Xmodem"; - break; - case ZM_YMODEM: - prot_name = (char *)"Ymodem"; - break; - default: - prot_name = (char *)"Zmodem"; - break; + switch (protocol) { + case ZM_XMODEM: return (char *)"Xmodem"; + case ZM_YMODEM: return (char *)"Ymodem"; + default: return (char *)"Zmodem"; } - return prot_name; +} + + + +void purgeline(int howlong) +{ + int c, count = 0; + unsigned char ch = 0; + + do { + c = Waitchar(&ch, howlong); + count++; + } while (c == 1); + if (count) + Syslog('z', "purgeline: purged %d characters", count); } diff --git a/mbsebbs/zmmisc.h b/mbsebbs/zmmisc.h index f9893e98..bca41249 100644 --- a/mbsebbs/zmmisc.h +++ b/mbsebbs/zmmisc.h @@ -159,6 +159,7 @@ void stohdr(long); long rclhdr(register char *); void zsendline_init(void); char *protname(void); +void purgeline(int); #define FTOFFSET 16 diff --git a/mbsebbs/zmrecv.c b/mbsebbs/zmrecv.c index c5695310..e862e0ac 100644 --- a/mbsebbs/zmrecv.c +++ b/mbsebbs/zmrecv.c @@ -78,8 +78,7 @@ extern unsigned long rcvdbytes; int zmrcvfiles(void) { - int rc, c, count = 0; - unsigned char ch = 0; + int rc; Syslog('+', "Zmodem: start Zmodem receive"); @@ -117,12 +116,7 @@ int zmrcvfiles(void) /* * Some programs send some garbage after the transfer, eat these. */ - do { - c = Waitchar(&ch, 100); - count++; - } while (c == 1); - if (count) - Syslog('z', "zmrcvfiles: purged %d garbage characters", count); + purgeline(100); Syslog('z', "Zmodem: receive rc=%d",rc); return abs(rc);