/*--------------------------------------------------------------------------*/ /* */ /* */ /* ------------ Bit-Bucket Software, Co. */ /* \ 10001101 / Writers and Distributors of */ /* \ 011110 / Freely Available Software. */ /* \ 1011 / */ /* ------ */ /* */ /* (C) Copyright 1987-90, Bit Bucket Software Co., a Delaware Corporation. */ /* */ /* */ /* BinkleyTerm Janus revision 0.31, 11- 2-89 */ /* Full-duplex WaZOO file transfer protocol */ /* */ /* */ /* For complete details of the licensing restrictions, please refer */ /* to the License agreement, which is published in its entirety in */ /* the MAKEFILE and BT.C, and also contained in the file LICENSE.240. */ /* */ /* USE OF THIS FILE IS SUBJECT TO THE RESTRICTIONS CONTAINED IN THE */ /* BINKLEYTERM LICENSING AGREEMENT. IF YOU DO NOT FIND THE TEXT OF */ /* THIS AGREEMENT IN ANY OF THE AFOREMENTIONED FILES, OR IF YOU DO */ /* NOT HAVE THESE FILES, YOU SHOULD IMMEDIATELY CONTACT BIT BUCKET */ /* SOFTWARE CO. AT ONE OF THE ADDRESSES LISTED BELOW. IN NO EVENT */ /* SHOULD YOU PROCEED TO USE THIS FILE WITHOUT HAVING ACCEPTED THE */ /* TERMS OF THE BINKLEYTERM LICENSING AGREEMENT, OR SUCH OTHER */ /* AGREEMENT AS YOU ARE ABLE TO REACH WITH BIT BUCKET SOFTWARE, CO. */ /* */ /* */ /* You can contact Bit Bucket Software Co. at any one of the following */ /* addresses: */ /* */ /* Bit Bucket Software Co. FidoNet 1:104/501, 1:132/491, 1:141/491 */ /* P.O. Box 460398 AlterNet 7:491/0 */ /* Aurora, CO 80046 BBS-Net 86:2030/1 */ /* Internet f491.n132.z1.fidonet.org */ /* */ /* Please feel free to contact us at any time to share your comments about */ /* our software and/or licensing policies. */ /* */ /*--------------------------------------------------------------------------*/ #define WAZOO_SECTION #include #include #include #include #include #include #include #include #include #include #include #ifdef __TURBOC__ #include "tc_utime.h" #include #else #include #include #endif #ifndef IBM_C extern int _fmode; #endif #include "com.h" #include "xfer.h" #include "zmodem.h" #include "keybd.h" #include "sbuf.h" #include "sched.h" #include "externs.h" #include "prototyp.h" #include "defines.h" #include "janus.h" /* Private routines */ static void getfname(word); static void sendpkt(byte *,int,int); static void sendpkt32(byte *,int,int); static void txbyte(byte); static long procfname(void); static byte rcvpkt(void); static void rxclose(word); static void endbatch(void); static void j_message(word,char *,...); static void j_status(char *,...); static int j_error(byte *,byte *); static void update_y(void); static void long_set_timer(long *,word); static int long_time_gone(long *); static int rcvrawbyte(void); static int rxbyte(void); static void xfer_summary(char *,char *,long *,int); static void update_status(long *,long *,long,int *,int); static void through(long *,long *); static int get_filereq(byte); static int record_reqfile(char *); static byte get_reqname(byte); static void mark_done(char *); /* Private data. I know, proper software design says you shouldn't make data */ /* global unless you really need to. In this case speed and code size make */ /* it more important to avoid constantly pushing & popping arguments. */ static byte *GenericError = "!%s"; static byte *ReqTmp = "JANUSREQ.TMP"; #ifdef OS_2 static byte Upload_path[PATHLEN]; /* Dest. path of file being * received */ #endif static char *Rxbuf; /* Address of packet reception buffer */ static char *Txfname; /* Full path of file we're sending */ static char *Rxfname; /* Full path of file we're receiving */ static byte *Rxbufptr; /* Current position within packet reception buffer */ static byte *Rxbufmax; /* Upper bound of packet reception buffer */ static byte Do_after; /* What to do with file being sent when we're done */ static byte WaitFlag; /* Tells rcvrawbyte() whether or not to wait */ static byte SharedCap; /* Capability bits both sides have in common */ static int Txfile; /* File handle of file we're sending */ static int Rxfile; /* File handle of file we're receiving */ static int ReqRecorded; /* Number of files obtained by this request */ static word TimeoutSecs; /* How long to wait for various things */ static word Rxblklen; /* Length of data in last data block packet recvd */ static word Next_y; /* Number of next available line on screen */ static word Tx_y; /* Line number of file transmission status display */ static word Rx_y; /* Line number of file reception status display */ static long Txlen; /* Total length of file we're sending */ static long Rxlen; /* Total length of file we're receiving */ static long Rxfiletime; /* Timestamp of file we're receiving */ static long Diskavail; /* Bytes available in upload directory */ static long TotalBytes; /* Total bytes xferred in this session */ static long Txsttime; /* Time at which we started sending current file */ static long Rxsttime; /* Time at which we started receiving current file */ /*****************************************************************************/ /* Super-duper neato-whizbang full-duplex streaming ACKless batch file */ /* transfer protocol for use in WaZOO mail sessions */ /*****************************************************************************/ void Janus(void) { byte xstate; /* Current file transmission state */ byte rstate; /* Current file reception state */ byte pkttype; /* Type of packet last received */ byte tx_inhibit; /* Flag to wait and send after done receiving */ byte *holdname; /* Name of hold area */ #ifdef OS_2 byte *p; /* Temporary pointer */ #endif byte fsent; /* Did we manage to send anything this session? */ byte sending_req; /* Are we currently sending requested files? */ byte attempting_req; /* Are we waiting for the sender to start our req? */ byte req_started; /* Has the sender started servicing our request? */ int txoldeta; /* Last transmission ETA displayed */ int rxoldeta; /* Last reception ETA displayed */ word blklen; /* Length of last data block sent */ word txblklen; /* Size of data block to try to send this time */ word txblkmax; /* Max size of data block to send at this speed */ word goodneeded; /* # good bytes to send before upping txblklen */ word goodbytes; /* Number of good bytes sent at this block size */ word rpos_count; /* Number of RPOS packets sent at this position */ long xmit_retry; /* Time to retransmit lost FNAMEPKT or EOF packet */ long txpos; /* Current position within file we're sending */ long lasttx; /* Position within file of last data block we sent */ long starttime; /* Time at which we started this Janus session */ long txstpos; /* Initial data position of file we're sending */ long rxstpos; /* Initial data position of file we're receiving */ long txoldpos; /* Last transmission file position displayed */ long rxoldpos; /* Last reception file position displayed */ long rpos_retry; /* Time at which to retry RPOS packet */ long brain_dead; /* Time at which to give up on other computer */ long rpos_sttime; /* Time at which we started current RPOS sequence */ long last_rpostime; /* Timetag of last RPOS which we performed */ long last_blkpos; /* File position of last out-of-sequence BLKPKT */ FILE *reqfile; /* File handle for .REQ file */ set_prior(3); /* Time Critical */ XON_DISABLE(); if (un_attended && fullscreen) { clear_filetransfer(); sb_show(); } else { set_xy(NULL); Next_y = locate_y; } Tx_y = Rx_y = 0; SharedCap = 0; TotalBytes = 0; time(&starttime); /*------------------------------------------------------------------------*/ /* Allocate memory */ /*------------------------------------------------------------------------*/ Rxbuf = Txbuf + 4096 + 8; Txfname = Rxfname = NULL; if (!(Txfname = malloc(PATHLEN)) || !(Rxfname = malloc(PATHLEN))) { status_line(msgtxt[M_MEM_ERROR]); mdm_hangup(); goto freemem; } Rxbufmax = Rxbuf + BUFMAX + 8; /*------------------------------------------------------------------------*/ /* Initialize file transmission variables */ /*------------------------------------------------------------------------*/ tx_inhibit = FALSE; last_rpostime = xmit_retry = 0L; TimeoutSecs = (unsigned int)(40960 / cur_baud); if (TimeoutSecs < 10) TimeoutSecs = 10; long_set_timer(&brain_dead,120); txblkmax = cur_baud/300 * 128; if (txblkmax > BUFMAX) txblkmax = BUFMAX; txblklen = txblkmax; goodbytes = goodneeded = 0; Txfile = -1; sending_req = fsent = FALSE; xstate = XSENDFNAME; getfname(INITIAL_XFER); /*------------------------------------------------------------------------*/ /* Initialize file reception variables */ /*------------------------------------------------------------------------*/ holdname = HoldAreaNameMunge(&called_addr); sprintf(Abortlog_name,"%s%s.Z\0",holdname,Hex_Addr_Str(&called_addr)); /* Sorry, this don't cut in OS/2's zfree */ #ifdef OS_2 p = strchr(strcpy (Upload_path, CurrentNetFiles),'\0') - 1; while (p >= Upload_path && *p != '\\') --p; *(++p) = '\0'; if ((Diskavail = zfree(Upload_path)) <= 0L) Diskavail = 0x7FFFFFF; #else Diskavail = (CurrentNetFiles[1] == ':') ? zfree(CurrentNetFiles) : 0x7FFFFFFF; #endif Rxbufptr = NULL; rpos_retry = rpos_count = 0; attempting_req = req_started = FALSE; rstate = RRCVFNAME; /*------------------------------------------------------------------------*/ /* Send and/or receive stuff until we're done with both */ /*------------------------------------------------------------------------*/ do { /* while (xstate || rstate) */ /*---------------------------------------------------------------------*/ /* If nothing useful (i.e. sending or receiving good data block) has */ /* happened within the last 2 minutes, give up in disgust */ /*---------------------------------------------------------------------*/ if (long_time_gone(&brain_dead)) { j_status(msgtxt[M_OTHER_DIED]); /* "He's dead, Jim." */ goto giveup; } /*---------------------------------------------------------------------*/ /* If we're tired of waiting for an ACK, try again */ /*---------------------------------------------------------------------*/ if (xmit_retry) { if (long_time_gone(&xmit_retry)) { j_message(Tx_y,msgtxt[M_TIMEOUT]); xmit_retry = 0L; switch (xstate) { case XRCVFNACK: xstate = XSENDFNAME; break; case XRCVFRNAKACK: xstate = XSENDFREQNAK; break; case XRCVEOFACK: errno = 0; lseek(Txfile, txpos=lasttx, SEEK_SET); if (j_error(msgtxt[M_SEEK_MSG],Txfname)) goto giveup; xstate = XSENDBLK; break; } } } /*---------------------------------------------------------------------*/ /* Transmit next part of file, if any */ /*---------------------------------------------------------------------*/ switch (xstate) { case XSENDBLK: if (tx_inhibit) break; *((long *)Txbuf) = lasttx = txpos; errno = 0; blklen = read(Txfile, Txbuf+sizeof(txpos), txblklen); if (j_error(msgtxt[M_READ_MSG],Txfname)) goto giveup; txpos += blklen; sendpkt(Txbuf, sizeof(txpos)+blklen, BLKPKT); update_status(&txpos,&txoldpos,Txlen-txpos,&txoldeta,Tx_y); fsent = TRUE; if (txpos >= Txlen || blklen < txblklen) { long_set_timer(&xmit_retry,TimeoutSecs); xstate = XRCVEOFACK; } else long_set_timer(&brain_dead,120); if (txblklen < txblkmax && (goodbytes+=txblklen) >= goodneeded) { txblklen <<= 1; goodbytes = 0; } break; case XSENDFNAME: blklen = strchr( strchr(Txbuf,'\0')+1, '\0') - Txbuf + 1; Txbuf[blklen++] = OURCAP; sendpkt(Txbuf,blklen,FNAMEPKT); txoldpos = txoldeta = -1; long_set_timer(&xmit_retry,TimeoutSecs); xstate = XRCVFNACK; break; case XSENDFREQNAK: sendpkt(NULL,0,FREQNAKPKT); long_set_timer(&xmit_retry,TimeoutSecs); xstate = XRCVFRNAKACK; break; } /*---------------------------------------------------------------------*/ /* Catch up on our reading; receive and handle all outstanding packets */ /*---------------------------------------------------------------------*/ while (pkttype = rcvpkt()) { if (pkttype != BADPKT) long_set_timer(&brain_dead,120); switch (pkttype) { /*---------------------------------------------------------------*/ /* File data block or munged block */ /*---------------------------------------------------------------*/ case BADPKT: case BLKPKT: if (rstate == RRCVBLK) { if (pkttype == BADPKT || *((long *)Rxbuf) != Rxpos) { if (pkttype == BLKPKT) { if (*((long *)Rxbuf) < last_blkpos) rpos_retry = rpos_count = 0; last_blkpos = *((long *)Rxbuf); } if (long_time_gone(&rpos_retry)) { /* If we're the called machine, and we're trying to send stuff, and it seems to be screwing up our ability to receive stuff, maybe this connection just can't hack full-duplex. Try waiting till the sending system finishes before sending our stuff to it */ if (rpos_count > 4) { if (xstate && !isOriginator && !tx_inhibit) { tx_inhibit = TRUE; j_status(msgtxt[M_GOING_ONE_WAY]); } rpos_count = 0; } if (++rpos_count == 1) time(&rpos_sttime); j_message(Rx_y,msgtxt[M_J_BAD_PACKET],Rxpos); *((long *)Rxbuf) = Rxpos; *((long *)(Rxbuf + sizeof(Rxpos))) = rpos_sttime; sendpkt(Rxbuf, sizeof(Rxpos)+sizeof(rpos_sttime), RPOSPKT); long_set_timer(&rpos_retry,TimeoutSecs/2); } } else { last_blkpos = Rxpos; rpos_retry = rpos_count = 0; errno = 0; write(Rxfile, Rxbuf+sizeof(Rxpos), Rxblklen -= sizeof(Rxpos)); if (j_error(msgtxt[M_WRITE_MSG],Rxfname)) goto giveup; Diskavail -= Rxblklen; Rxpos += Rxblklen; update_status(&Rxpos,&rxoldpos,Rxlen-Rxpos,&rxoldeta,Rx_y); if (Rxpos >= Rxlen) { rxclose(GOOD_XFER); Rxlen -= rxstpos; through(&Rxlen,&Rxsttime); j_status("%s-J%s %s",msgtxt[M_FILE_RECEIVED], (SharedCap&CANCRC32)?"/32":" ",Rxfname); update_files(0); rstate = RRCVFNAME; } } } if (rstate == RRCVFNAME) sendpkt(NULL,0,EOFACKPKT); break; /*---------------------------------------------------------------*/ /* Name and other data for next file to receive */ /*---------------------------------------------------------------*/ case FNAMEPKT: if (rstate == RRCVFNAME) Rxpos = rxstpos = procfname(); if (!Rxfname[0] && get_filereq(req_started)) { sendpkt(Rxbuf,strlen(Rxbuf)+2,FREQPKT); attempting_req = TRUE; req_started = FALSE; } else { if (attempting_req) { attempting_req = FALSE; req_started = TRUE; } *((long *)Rxbuf) = Rxpos; Rxbuf[sizeof(Rxpos)] = SharedCap; sendpkt(Rxbuf,sizeof(Rxpos)+1,FNACKPKT); rxoldpos = rxoldeta = -1; if (Rxpos > -1) rstate = (byte)((Rxfname[0]) ? RRCVBLK : RDONE); else j_status(msgtxt[M_REFUSING],Rxfname); if (!rstate) tx_inhibit = FALSE; if (!(xstate || rstate)) goto breakout; } break; /*---------------------------------------------------------------*/ /* ACK to filename packet we just sent */ /*---------------------------------------------------------------*/ case FNACKPKT: if (xstate == XRCVFNACK) { xmit_retry = 0L; if (Txfname[0]) { SharedCap = (Rxblklen > sizeof(long)) ? Rxbuf[sizeof(long)] : 0; if ((txpos = *((long *)Rxbuf)) > -1L) { if (txpos) status_line(msgtxt[M_SYNCHRONIZING],txpos); errno = 0; lseek(Txfile, txstpos = txpos, SEEK_SET); if (j_error(msgtxt[M_SEEK_MSG],Txfname)) goto giveup; xstate = XSENDBLK; } else { j_status(msgtxt[M_REMOTE_REFUSED],Txfname); if (sending_req) { if (!(sending_req = get_reqname(FALSE))) getfname(GOOD_XFER); } else { Do_after = NOTHING_AFTER; getfname(GOOD_XFER); } xstate = XSENDFNAME; } } else { sent_mail = 1; xstate = XDONE; } } if (!(xstate || rstate)) goto breakout; break; /*---------------------------------------------------------------*/ /* Request to send more stuff rather than end batch just yet */ /*---------------------------------------------------------------*/ case FREQPKT: if (xstate == XRCVFNACK) { xmit_retry = 0L; SharedCap = *(strchr(Rxbuf,'\0')+1); if (CurrentReqLim > 0) { sprintf(Txbuf,request_template,CurrentNetFiles, Hex_Addr_Str (&(alias[0]))); errno = 0; reqfile = fopen(Txbuf,"wt"); j_error(msgtxt[M_OPEN_MSG],Txbuf); fputs(Rxbuf,reqfile); fputs("\n",reqfile); fclose(reqfile); unlink(ReqTmp); ReqRecorded = 0; /* counted by record_reqfile */ respond_to_file_requests(0,record_reqfile); CurrentReqLim -= ReqRecorded; if (sending_req = get_reqname(TRUE)) xstate = XSENDFNAME; else xstate = XSENDFREQNAK; } else xstate = XSENDFREQNAK; } break; /*---------------------------------------------------------------*/ /* Our last file request didn't match anything; move on to next */ /*---------------------------------------------------------------*/ case FREQNAKPKT: attempting_req = FALSE; req_started = TRUE; sendpkt(NULL,0,FRNAKACKPKT); break; /*---------------------------------------------------------------*/ /* ACK to no matching files for request error; try to end again */ /*---------------------------------------------------------------*/ case FRNAKACKPKT: if (xstate == XRCVFRNAKACK) { xmit_retry = 0L; getfname(GOOD_XFER); xstate = XSENDFNAME; } break; /*---------------------------------------------------------------*/ /* ACK to last data block in file */ /*---------------------------------------------------------------*/ case EOFACKPKT: if (xstate == XRCVEOFACK || xstate == XRCVFNACK) { xmit_retry = 0L; if (xstate == XRCVEOFACK) { Txlen -= txstpos; through(&Txlen,&Txsttime); j_status("%s-J%s %s",msgtxt[M_FILE_SENT],(SharedCap&CANCRC32)?"/32":" ",Txfname); update_files(1); if (sending_req) { if (!(sending_req = get_reqname(FALSE))) getfname(GOOD_XFER); } else getfname(GOOD_XFER); } xstate = XSENDFNAME; } break; /*---------------------------------------------------------------*/ /* Receiver says "let's try that again." */ /*---------------------------------------------------------------*/ case RPOSPKT: if (xstate == XSENDBLK || xstate == XRCVEOFACK) { if (*((long *)(Rxbuf+sizeof(txpos))) != last_rpostime) { last_rpostime = *((long *)(Rxbuf+sizeof(txpos))); xmit_retry = 0L; CLEAR_OUTBOUND(); errno = 0; lseek(Txfile, txpos = lasttx = *((long *)Rxbuf), SEEK_SET); if (j_error(msgtxt[M_SEEK_MSG],Txfname)) goto giveup; j_message(Tx_y,msgtxt[M_SYNCHRONIZING],txpos); txblklen >>= 2; if (txblklen < 64) txblklen = 64; goodbytes = 0; goodneeded += 1024; if (goodneeded > 8192) goodneeded = 8192; xstate = XSENDBLK; } } break; /*---------------------------------------------------------------*/ /* Debris from end of previous Janus session; ignore it */ /*---------------------------------------------------------------*/ case HALTACKPKT: break; /*---------------------------------------------------------------*/ /* Abort the transfer and quit */ /*---------------------------------------------------------------*/ default: j_status(msgtxt[M_UNKNOWN_PACKET],pkttype); /* fallthrough */ case HALTPKT: giveup: j_status(msgtxt[M_SESSION_ABORT]); if (Txfname[0]) getfname(ABORT_XFER); if (rstate == RRCVBLK) { TotalBytes += (Rxpos-rxstpos); rxclose(FAILED_XFER); } goto abortxfer; } /* switch (pkttype) */ } /* while (pkttype) */ } while (xstate || rstate); /*------------------------------------------------------------------------*/ /* All done; make sure other end is also finished (one way or another) */ /*------------------------------------------------------------------------*/ breakout: if (!fsent) j_status(msgtxt[M_NOTHING_TO_SEND], Full_Addr_Str (&called_addr)); abortxfer: through(&TotalBytes,&starttime); endbatch(); /*------------------------------------------------------------------------*/ /* Release allocated memory */ /*------------------------------------------------------------------------*/ freemem: if (Txfname) free(Txfname); if (Rxfname) free(Rxfname); flag_file (CLEAR_FLAG, &called_addr, 1); set_prior(4); /* Always High */ } /*****************************************************************************/ /* Get name and info for next file to be transmitted, if any, and build */ /* FNAMEPKT. Packet contents as per ZModem filename info packet, to allow */ /* use of same method of aborted-transfer recovery. If there are no more */ /* files to be sent, build FNAMEPKT with null filename. Also open file and */ /* set up for transmission. Set Txfname, Txfile, Txlen. Txbuf must not be */ /* modified until FNACKPKT is received. */ /*****************************************************************************/ static void getfname(word xfer_flag) { static byte floflag, bad_xfers, outboundname[PATHLEN]; static long floname_pos; static FILE *flofile; static int have_lock; char *holdname; register char *p; int i; long curr_pos; struct stat f; /*------------------------------------------------------------------------*/ /* Initialize static variables on first call of the batch */ /*------------------------------------------------------------------------*/ if (xfer_flag == INITIAL_XFER) { floflag = outboundname[0] = '\0'; flofile = NULL; have_lock = flag_file (TEST_AND_SET, &called_addr, 1); /*------------------------------------------------------------------------*/ /* If we were already sending a file, close it and clean up */ /*------------------------------------------------------------------------*/ } else if (Txfile != -1) { errno = 0; close(Txfile); j_error(msgtxt[M_CLOSE_MSG],Txfname); Txfile = -1; /*---------------------------------------------------------------------*/ /* If xfer completed, do post-xfer cleanup */ /*---------------------------------------------------------------------*/ if (xfer_flag == GOOD_XFER) { /*------------------------------------------------------------------*/ /* Perform post-xfer file massaging if neccessary */ /*------------------------------------------------------------------*/ switch (Do_after) { case DELETE_AFTER: case SHOW_DELETE_AFTER: j_status(msgtxt[M_UNLINKING_MSG],Txfname); unlink(Txfname); j_error(msgtxt[M_UNLINK_MSG],Txfname); break; case TRUNC_AFTER: j_status(msgtxt[M_TRUNC_MSG],Txfname); Txfile = open(Txfname,O_TRUNC|O_RDWR,S_IREAD|S_IWRITE); if (Txfile != -1) errno = 0; j_error(msgtxt[M_TRUNC_MSG],Txfname); close(Txfile); Txfile = -1; } /*------------------------------------------------------------------*/ /* If processing .?LO file, flag filename as sent (name[0] = '~') */ /*------------------------------------------------------------------*/ skipname: if (floflag) { curr_pos = ftell(flofile); j_error(msgtxt[M_SEEK_MSG],outboundname); fseek(flofile,floname_pos,SEEK_SET); j_error(msgtxt[M_SEEK_MSG],outboundname); fputc(Txfname[0] = '~',flofile); j_error(msgtxt[M_WRITE_MSG],outboundname); fseek(flofile,curr_pos,SEEK_SET); j_error(msgtxt[M_SEEK_MSG],outboundname); } } else { abort: ++bad_xfers; } } /*------------------------------------------------------------------------*/ /* Find next file to be sent and build FNAMEPKT. If reading .FLO-type */ /* file get next entry from it; otherwise check for next .OUT/.FLO file */ /*------------------------------------------------------------------------*/ if (have_lock) goto end_send; holdname = HoldAreaNameMunge(&called_addr); if (!floflag) { /*---------------------------------------------------------------------*/ /* If first getfname() for this batch, init filename to .OUT */ /*---------------------------------------------------------------------*/ if (!outboundname[0]) { sprintf(outboundname,"%s%s.OUT",holdname,Hex_Addr_Str(&called_addr)); *ext_flags = 'O'; /*---------------------------------------------------------------------*/ /* Increment outbound filename until match found or all checked */ /* .OUT->.DUT->.CUT->.HUT->.FLO->.DLO->.CLO->.HLO->null name */ /*---------------------------------------------------------------------*/ } else { nxtout: p = strchr(outboundname,'\0') - 3; for (i = 0; i < NUM_FLAGS; ++i) if (ext_flags[i] == *p) break; if (i < NUM_FLAGS - 1) { *p = ext_flags[i+1]; #ifndef JACK_DECKER if (isOriginator && *p == 'H') goto nxtout; #endif } else { /*---------------------------------------------------------------*/ /* Finished ?,D,C,H sequence; wrap .OUT->.FLO, or .FLO->done */ /*---------------------------------------------------------------*/ if (!floflag) { *p++ = *ext_flags = 'F'; *p++ = 'L'; *p = 'O'; ++floflag; } else end_send: outboundname[0] = Txfname[0] = Txbuf[0] = Txbuf[1] = floflag = '\0'; } } /*---------------------------------------------------------------------*/ /* Check potential outbound name; if file doesn't exist keep looking */ /*---------------------------------------------------------------------*/ if (outboundname[0]) { if (!dexists(outboundname)) goto nxtout; if (floflag) goto rdflo; strcpy(Txfname,outboundname); /*------------------------------------------------------------------*/ /* Start FNAMEPKT using .PKT alias */ /*------------------------------------------------------------------*/ invent_pkt_name(Txbuf); Do_after = DELETE_AFTER; } /*------------------------------------------------------------------------*/ /* Read and process next entry from .?LO-type file */ /*------------------------------------------------------------------------*/ } else { rdflo: /*---------------------------------------------------------------------*/ /* Open .?LO file for processing if neccessary */ /*---------------------------------------------------------------------*/ if (!flofile) { bad_xfers = 0; errno = 0; flofile = fopen(outboundname,"rb+"); if (j_error(msgtxt[M_OPEN_MSG],outboundname)) goto nxtout; } errno = 0; floname_pos = ftell(flofile); j_error(msgtxt[M_SEEK_MSG],outboundname); if (fgets(p = Txfname, PATHLEN, flofile)) { /*------------------------------------------------------------------*/ /* Got an attached file name; check for handling flags, fix up name */ /*------------------------------------------------------------------*/ while (*p > ' ') ++p; *p = '\0'; switch (Txfname[0]) { case '\0': case '~': case ';': goto rdflo; case TRUNC_AFTER: case DELETE_AFTER: case SHOW_DELETE_AFTER: Do_after = Txfname[0]; strcpy(Txfname,Txfname+1); break; default: Do_after = NOTHING_AFTER; break; } /*------------------------------------------------------------------*/ /* Start FNAMEPKT with simple filename */ /*------------------------------------------------------------------*/ while (p >= Txfname && *p != '\\' && *p != ':') --p; strcpy(Txbuf,++p); } else { /*------------------------------------------------------------------*/ /* Finished reading this .?LO file; clean up and look for another */ /*------------------------------------------------------------------*/ errno = 0; fclose(flofile); j_error(msgtxt[M_CLOSE_MSG],outboundname); flofile = NULL; if (!bad_xfers) { unlink(outboundname); j_error(msgtxt[M_UNLINK_MSG],outboundname); } goto nxtout; } } /*------------------------------------------------------------------------*/ /* If we managed to find a valid file to transmit, open it, finish */ /* FNAMEPKT, and print nice message for the sysop. */ /*------------------------------------------------------------------------*/ if (Txfname[0]) { if (xfer_flag == ABORT_XFER) goto abort; j_status(msgtxt[M_SENDING],Txfname); errno = 0; Txfile = open(Txfname,O_RDONLY|O_BINARY); if (Txfile != -1) errno = 0; if (j_error(msgtxt[M_OPEN_MSG],Txfname)) goto skipname; stat(Txfname,&f); sprintf(strchr(Txbuf,'\0')+1,"%lu %lo %o",Txlen = f.st_size,f.st_mtime,f.st_mode); p = strchr(Txfname,'\0'); while (p >= Txfname && *p != ':' && *p != '\\') --p; if (!un_attended || !fullscreen) Tx_y = Next_y; else Tx_y = 1; xfer_summary(msgtxt[M_SEND],++p,&Txlen,Tx_y); time(&Txsttime); } } /*****************************************************************************/ /* Build and send a packet of any type. */ /* Packet structure is: PKTSTRT,contents,packet_type,PKTEND,crc */ /* CRC is computed from contents and packet_type only; if PKTSTRT or PKTEND */ /* get munged we'll never even find the CRC. */ /*****************************************************************************/ static void sendpkt(register byte *buf,int len,int type) { register word crc; if ((SharedCap & CANCRC32) && type != FNAMEPKT) sendpkt32(buf,len,type); else { BUFFER_BYTE(DLE); BUFFER_BYTE(PKTSTRTCHR ^ 0x40); crc = 0; while (--len >= 0) { txbyte(*buf); crc = xcrc(crc, ((word)(*buf++)) ); } BUFFER_BYTE((byte)type); crc = xcrc(crc,type); BUFFER_BYTE(DLE); BUFFER_BYTE(PKTENDCHR ^ 0x40); txbyte((byte)(crc >> 8)); txbyte((byte)(crc & 0xFF)); UNBUFFER_BYTES(); } } /*****************************************************************************/ /* Build and send a packet using 32-bit CRC; same as sendpkt in other ways */ /*****************************************************************************/ static void sendpkt32(register byte *buf,register int len,int type) { unsigned long crc32; BUFFER_BYTE(DLE); BUFFER_BYTE(PKTSTRTCHR32 ^ 0x40); crc32 = 0xFFFFFFFF; while (--len >= 0) { txbyte(*buf); crc32 = Z_32UpdateCRC(((word)*buf),crc32); ++buf; } BUFFER_BYTE((byte)type); crc32 = Z_32UpdateCRC(type,crc32); BUFFER_BYTE(DLE); BUFFER_BYTE(PKTENDCHR ^ 0x40); txbyte((byte)(crc32 >> 24)); txbyte((byte)((crc32 >> 16) & 0xFF)); txbyte((byte)((crc32 >> 8) & 0xFF)); txbyte((byte)(crc32 & 0xFF)); UNBUFFER_BYTES(); } /*****************************************************************************/ /* Transmit cooked escaped byte(s) corresponding to raw input byte. Escape */ /* DLE, XON, and XOFF using DLE prefix byte and ^ 0x40. Also escape */ /* CR-after-'@' to avoid Telenet/PC-Pursuit problems. */ /*****************************************************************************/ static void txbyte(register byte c) { static byte lastsent; switch (c) { case CR: if (lastsent != '@') goto sendit; /* fallthrough */ case DLE: case XON: case XOFF: BUFFER_BYTE(DLE); c ^= 0x40; /* fallthrough */ default: sendit: BUFFER_BYTE(lastsent = c); } } /*****************************************************************************/ /* Process FNAMEPKT of file to be received. Check for aborted-transfer */ /* recovery and solve filename collisions. Check for enough disk space. */ /* Return initial file data position to start receiving at, or -1 if error */ /* detected to abort file reception. Set Rxfname, Rxlen, Rxfile. */ /*****************************************************************************/ static long procfname(void) { register byte *p; char linebuf[128], *fileinfo, *badfname; long filestart, bytes; FILE *abortlog; struct stat f; int i; /*------------------------------------------------------------------------*/ /* Initialize for file reception */ /*------------------------------------------------------------------------*/ Rxfname[0] = Resume_WaZOO = 0; /*------------------------------------------------------------------------*/ /* Save info on WaZOO transfer in case of abort */ /*------------------------------------------------------------------------*/ strcpy(Resume_name,fancy_str(Rxbuf)); fileinfo = strchr(Rxbuf,'\0') + 1; p = strchr(fileinfo,'\0') + 1; SharedCap = (Rxblklen > p-Rxbuf) ? *p & OURCAP : 0; /*------------------------------------------------------------------------*/ /* If this is a null FNAMEPKT, return OK immediately */ /*------------------------------------------------------------------------*/ if (!Rxbuf[0]) return 0L; strcpy(linebuf,Rxbuf); strlwr(linebuf); p = check_netfile(linebuf); j_status("#%s %s %s",msgtxt[M_RECEIVING],(p) ? p : " ",Rxbuf); /*------------------------------------------------------------------------*/ /* Extract and validate filesize */ /*------------------------------------------------------------------------*/ Rxlen = -1; Rxfiletime = 0; if (sscanf(fileinfo,"%ld %lo",&Rxlen,&Rxfiletime) < 1 || Rxlen < 0) { j_status(msgtxt[M_NO_LENGTH]); return -1L; } sprintf(Resume_info,"%ld %lo",Rxlen,Rxfiletime); /*------------------------------------------------------------------------*/ /* Check if this is a failed WaZOO transfer which should be resumed */ /*------------------------------------------------------------------------*/ if (dexists(Abortlog_name)) { errno = 0; abortlog = fopen(Abortlog_name,read_ascii); if (!j_error(msgtxt[M_OPEN_MSG],Abortlog_name)) { while (!feof(abortlog)) { linebuf[0] = '\0'; if (!fgets(p = linebuf,sizeof(linebuf),abortlog)) break; while (*p >= ' ') ++p; *p = '\0'; p = strchr(linebuf,' '); *p = '\0'; if (!stricmp(linebuf,Resume_name)) { p = strchr( (badfname = ++p), ' '); *p = '\0'; if (!stricmp(++p,Resume_info)) { ++Resume_WaZOO; break; } } } errno = 0; fclose(abortlog); j_error(msgtxt[M_CLOSE_MSG],Abortlog_name); } } /*------------------------------------------------------------------------*/ /* Open either the old or a new file, as appropriate */ /*------------------------------------------------------------------------*/ p = strchr(strcpy(Rxfname,CurrentNetFiles),'\0'); errno = 0; if (Resume_WaZOO) { strcpy(p,badfname); Rxfile = open(Rxfname,O_CREAT|O_RDWR|O_BINARY,S_IREAD|S_IWRITE); } else { strcpy(p,Rxbuf); /*---------------------------------------------------------------------*/ /* If the file already exists: */ /* 1) And the new file has the same time and size, skip it */ /* 2) And OVERWRITE is turned on, delete the old copy */ /* 3) Else create a unique file name in which to store new data */ /*---------------------------------------------------------------------*/ if (dexists(Rxfname)) { stat(Rxfname,&f); if (Rxlen == f.st_size && Rxfiletime == f.st_mtime) { j_status(msgtxt[M_ALREADY_HAVE],Rxfname); return -1L; } i = strlen(Rxfname) - 1; if ((!overwrite) || (is_arcmail(Rxfname,i))) { unique_name(Rxfname); j_status(msgtxt[M_RENAME_MSG],Rxfname); } else { unlink(Rxfname); j_error(msgtxt[M_UNLINK_MSG],Rxfname); } } Rxfile = open(Rxfname,O_CREAT|O_EXCL|O_RDWR|O_BINARY,S_IREAD|S_IWRITE); } if (Rxfile != -1) errno = 0; if (j_error(msgtxt[M_OPEN_MSG],Rxfname)) return -1L; /*------------------------------------------------------------------------*/ /* Determine initial file data position */ /*------------------------------------------------------------------------*/ if (Resume_WaZOO) { stat(Rxfname,&f); j_status(msgtxt[M_SYNCHRONIZING_OFFSET],filestart = f.st_size); p = Rxbuf; errno = 0; lseek(Rxfile,filestart,SEEK_SET); if (j_error(msgtxt[M_SEEK_MSG],Rxfname)) { close(Rxfile); return -1L; } } else filestart = 0L; /*------------------------------------------------------------------------*/ /* Check for enough disk space */ /*------------------------------------------------------------------------*/ bytes = Rxlen - filestart + 10240; if (bytes > Diskavail) { j_status(msgtxt[M_OUT_OF_DISK_SPACE]); close(Rxfile); return -1L; } /*------------------------------------------------------------------------*/ /* Print status message for the sysop */ /*------------------------------------------------------------------------*/ if (!un_attended || !fullscreen) Rx_y = Next_y; else Rx_y = 2; xfer_summary(msgtxt[M_RECV],p,&Rxlen,Rx_y); time(&Rxsttime); return filestart; } /*****************************************************************************/ /* Receive, validate, and extract a packet if available. If a complete */ /* packet hasn't been received yet, receive and store as much of the next */ /* packet as possible. Each call to rcvpkt() will continue accumulating a */ /* packet until a complete packet has been received or an error is detected. */ /* Rxbuf must not be modified between calls to rcvpkt() if NOPKT is returned.*/ /* Returns type of packet received, NOPKT, or BADPKT. Sets Rxblklen. */ /*****************************************************************************/ static byte rcvpkt() { static byte rxcrc32; static word crc; static unsigned long crc32; register byte *p; register int c; int i; unsigned long pktcrc; /*------------------------------------------------------------------------*/ /* Abort transfer if operator pressed ESC */ /*------------------------------------------------------------------------*/ if (got_ESC()) { j_status(GenericError,msgtxt[M_KBD_MSG]); return HALTPKT; } /*------------------------------------------------------------------------*/ /* If not accumulating packet yet, find start of next packet */ /*------------------------------------------------------------------------*/ WaitFlag = FALSE; if (!(p = Rxbufptr)) { do c = rxbyte(); while (c >= 0 || c == PKTEND); switch (c) { case PKTSTRT: rxcrc32 = FALSE; p = Rxbuf; crc = 0; break; case PKTSTRT32: rxcrc32 = TRUE; p = Rxbuf; crc32 = 0xFFFFFFFF; break; case NOCARRIER: j_status(GenericError,&(msgtxt[M_NO_CARRIER][1])); return HALTPKT; default: return NOPKT; } } /*------------------------------------------------------------------------*/ /* Accumulate packet data until we empty buffer or find packet delimiter */ /*------------------------------------------------------------------------*/ if (rxcrc32) { while ((c = rxbyte()) >= 0 && p < Rxbufmax) { *p++ = (byte)c; crc32 = Z_32UpdateCRC(c,crc32); } } else { while ((c = rxbyte()) >= 0 && p < Rxbufmax) { *p++ = (byte)c; crc = xcrc(crc,c); } } /*------------------------------------------------------------------------*/ /* Handle whichever end-of-packet condition occurred */ /*------------------------------------------------------------------------*/ switch (c) { /*---------------------------------------------------------------------*/ /* PKTEND found; verify valid CRC */ /*---------------------------------------------------------------------*/ case PKTEND: WaitFlag = TRUE; pktcrc = 0; for (i = (rxcrc32) ? 4 : 2; i; --i) { if ((c = rxbyte()) < 0) break; pktcrc = (pktcrc << 8) | c; } if (!i) { if ((rxcrc32 && pktcrc == crc32) || pktcrc == crc) { /*------------------------------------------------------------*/ /* Good packet verified; compute packet data length and */ /* return packet type */ /*------------------------------------------------------------*/ Rxbufptr = NULL; Rxblklen = --p - Rxbuf; return *p; } } /* fallthrough */ /*---------------------------------------------------------------------*/ /* Bad CRC, carrier lost, or buffer overflow from munged PKTEND */ /*---------------------------------------------------------------------*/ default: if (c == NOCARRIER) { j_status(GenericError,&(msgtxt[M_NO_CARRIER][1])); return HALTPKT; } else { Rxbufptr = NULL; return BADPKT; } /*---------------------------------------------------------------------*/ /* Emptied buffer; save partial packet and let sender do something */ /*---------------------------------------------------------------------*/ case BUFEMPTY: Rxbufptr = p; return NOPKT; /*---------------------------------------------------------------------*/ /* PKTEND was trashed; discard partial packet and prep for next one */ /*---------------------------------------------------------------------*/ case PKTSTRT: rxcrc32 = FALSE; Rxbufptr = Rxbuf; crc = 0; return BADPKT; case PKTSTRT32: rxcrc32 = TRUE; Rxbufptr = Rxbuf; crc32 = 0xFFFFFFFF; return BADPKT; } } /*****************************************************************************/ /* Close file being received and perform post-reception aborted-transfer */ /* recovery cleanup if neccessary. */ /*****************************************************************************/ static void rxclose(word xfer_flag) { register byte *p; byte namebuf[PATHLEN], linebuf[128], c; FILE *abortlog, *newlog; struct utimbuf utimes; /*------------------------------------------------------------------------*/ /* Close file we've been receiving */ /*------------------------------------------------------------------------*/ errno = 0; close(Rxfile); j_error(msgtxt[M_CLOSE_MSG],Rxfname); if (Rxfiletime) { utimes.actime = Rxfiletime; utimes.modtime = Rxfiletime; utime(Rxfname,&utimes); } /*------------------------------------------------------------------------*/ /* If we completed a previously-aborted transfer, kill log entry & rename */ /*------------------------------------------------------------------------*/ if (xfer_flag == GOOD_XFER && Resume_WaZOO) { abortlog = fopen(Abortlog_name,read_ascii); if (!j_error(msgtxt[M_OPEN_MSG],Abortlog_name)) { c = 0; strcpy(strchr(strcpy(namebuf,Abortlog_name),'\0')-1,"TMP"); newlog = fopen(namebuf,write_ascii); if (!j_error(msgtxt[M_OPEN_MSG],namebuf)) { while (!feof(abortlog)) { linebuf[0] = '\0'; if (!fgets(p = linebuf,sizeof(linebuf),abortlog)) break; while (*p > ' ') ++p; *p = '\0'; if (stricmp(linebuf,Resume_name)) { *p = ' '; fputs(linebuf,newlog); if (j_error(msgtxt[M_WRITE_MSG],namebuf)) break; ++c; } } errno = 0; fclose(abortlog); j_error(msgtxt[M_CLOSE_MSG],Abortlog_name); fclose(newlog); j_error(msgtxt[M_CLOSE_MSG],namebuf); unlink(Abortlog_name); j_error(msgtxt[M_UNLINK_MSG],Abortlog_name); if (c) { if (!rename(namebuf,Abortlog_name)) errno = 0; j_error(msgtxt[M_RENAME_MSG],namebuf); } else { unlink(namebuf); j_error(msgtxt[M_UNLINK_MSG],namebuf); } } else { fclose(abortlog); j_error(msgtxt[M_CLOSE_MSG],Abortlog_name); } } j_status(msgtxt[M_FINISHED_PART],Resume_name); unique_name(strcat(strcpy(namebuf,CurrentNetFiles),Resume_name)); if (!rename(Rxfname,namebuf)) { errno = 0; strcpy(Rxfname,namebuf); } else j_error(msgtxt[M_RENAME_MSG],Rxfname); /*------------------------------------------------------------------------*/ /* If transfer failed and was not an attempted resumption, log for later */ /*------------------------------------------------------------------------*/ } else if (xfer_flag == FAILED_XFER && !Resume_WaZOO) { j_status(msgtxt[M_SAVING_PART],Rxfname); unique_name(strcat(strcpy(namebuf,CurrentNetFiles),"BadWaZOO.001")); if (!rename(Rxfname,namebuf)) errno = 0; j_error(msgtxt[M_RENAME_MSG],Rxfname); abortlog = fopen(Abortlog_name,"at"); if (!j_error(msgtxt[M_OPEN_MSG],Abortlog_name)) { fprintf(abortlog,"%s %s %s\n",Resume_name,namebuf+strlen(CurrentNetFiles),Resume_info); j_error(msgtxt[M_WRITE_MSG],Abortlog_name); fclose(abortlog); j_error(msgtxt[M_CLOSE_MSG],Abortlog_name); } else { unlink(namebuf); j_error(msgtxt[M_UNLINK_MSG],namebuf); } } } /*****************************************************************************/ /* Try REAL HARD to disengage batch session cleanly */ /*****************************************************************************/ static void endbatch(void) { register int done, timeouts; long timeval, brain_dead; /*------------------------------------------------------------------------*/ /* Tell the other end to halt if it hasn't already */ /*------------------------------------------------------------------------*/ done = timeouts = 0; long_set_timer(&brain_dead,120); goto reject; /*------------------------------------------------------------------------*/ /* Wait for the other end to acknowledge that it's halting */ /*------------------------------------------------------------------------*/ while (!done) { if (long_time_gone(&brain_dead)) break; switch (rcvpkt()) { case NOPKT: case BADPKT: if (long_time_gone(&timeval)) { if (++timeouts > 2) ++done; else goto reject; } break; case HALTPKT: case HALTACKPKT: ++done; break; default: timeouts = 0; reject: sendpkt(NULL,0,HALTPKT); long_set_timer(&timeval,TimeoutSecs); break; } } /*------------------------------------------------------------------------*/ /* Announce quite insistently that we're done now */ /*------------------------------------------------------------------------*/ for (done = 0; done < 10; ++done) sendpkt(NULL,0,HALTACKPKT); wait_for_clear(); } /*****************************************************************************/ /* Print a message in the message field of a transfer status line */ /*****************************************************************************/ static void j_message(word pos,char *va_alist,...) { va_list arg_ptr; int y, l; char buf[128]; y = pos; va_start(arg_ptr, va_alist); if (!un_attended || !fullscreen) gotoxy(MSG_X,y); else sb_move(filewin,y,MSG_X); (void) vsprintf(buf,va_alist,arg_ptr); for (l = 25-strlen(buf); l > 0; --l) strcat(buf," "); if (!un_attended || !fullscreen) { cputs(buf); } else { sb_puts(filewin,buf); sb_show(); } va_end(arg_ptr); } /*****************************************************************************/ /* Print & log status message without messing up display */ /*****************************************************************************/ static void j_status(char *va_alist,...) { va_list arg_ptr; char buf[128]; va_start(arg_ptr, va_alist); if (!un_attended || !fullscreen) gotoxy(1,Next_y-1); (void) vsprintf(buf,va_alist,arg_ptr); status_line(buf); if (!un_attended || !fullscreen) update_y(); va_end(arg_ptr); } /*****************************************************************************/ /* Print & log error message without messing up display */ /*****************************************************************************/ static int j_error(byte *msg,byte *fname) { register int e; if (e = errno) { if (!un_attended || !fullscreen) gotoxy(1,Next_y-1); got_error(msg,fname); if (!un_attended || !fullscreen) update_y(); } return e; } /*****************************************************************************/ /* Update screen position variables after printing a message */ /*****************************************************************************/ static void update_y() { set_xy(NULL); /* Bump cursor to next line after printing */ if (locate_y == Next_y) { /* If we didn't go anywhere, screen scrolled; */ if (Tx_y > 1) /* so decrement status line numbers */ --Tx_y; if (Rx_y > 1) --Rx_y; } else Next_y = locate_y; } /*****************************************************************************/ /* Compute future timehack for later reference */ /*****************************************************************************/ static void long_set_timer(long *Buffer,word Duration) { time(Buffer); *Buffer += (long)Duration; } /*****************************************************************************/ /* Return TRUE if timehack has been passed, FALSE if not */ /*****************************************************************************/ static int long_time_gone(long *TimePtr) { return (time(NULL) > *TimePtr); } /*****************************************************************************/ /* Receive cooked escaped byte translated to avoid various problems. */ /* Returns raw byte, BUFEMPTY, PKTSTRT, PKTEND, or NOCARRIER. */ /*****************************************************************************/ static int rxbyte(void) { register int c, w; if ((c = rcvrawbyte()) == DLE) { w = WaitFlag++; if ((c = rcvrawbyte()) >= 0) { switch (c ^= 0x40) { case PKTSTRTCHR: c = PKTSTRT; break; case PKTSTRTCHR32: c = PKTSTRT32; break; case PKTENDCHR: c = PKTEND; break; } } WaitFlag = (byte)w; } return c; } /*****************************************************************************/ /* Receive raw non-escaped byte. Returns byte, BUFEMPTY, or NOCARRIER. */ /* If waitflag is true, will wait for a byte for Timeoutsecs; otherwise */ /* will return BUFEMPTY if a byte isn't ready and waiting in inbound buffer. */ /*****************************************************************************/ static int rcvrawbyte(void) { register int c; long timeval; if ((c = PEEKBYTE()) >= 0) return MODEM_IN(); if (!CARRIER) return NOCARRIER; if (!WaitFlag) return BUFEMPTY; timeval = time(NULL) + TimeoutSecs; while ((c = PEEKBYTE()) < 0) { if (!CARRIER) return NOCARRIER; if (time(NULL) > timeval) return BUFEMPTY; time_release(); } return MODEM_IN(); } /*****************************************************************************/ /* Display start-of-transfer summary info */ /*****************************************************************************/ static void xfer_summary(char *xfertype,char *fname,long *len,int y) { char buf[128]; if (!un_attended || !fullscreen) gotoxy(2,y); else sb_move(filewin,y,2); sprintf(buf,"%s %12.12s; 0/%8ldb,%4d min. ", xfertype, fname, *len, (int)((*len*10/cur_baud*100/JANUS_EFFICIENCY+59)/60)); if (!un_attended || !fullscreen) { cputs(buf); cputs(local_CEOL); update_y(); } else { sb_puts(filewin,buf); sb_show(); } } /*****************************************************************************/ /* Update any status line values which have changed */ /*****************************************************************************/ static void update_status(long *pos,long *oldpos,long left,int *oldeta,int y) { char buf[16]; register int eta; elapse_time(); if (*pos != *oldpos) { sprintf(buf,"%8ld",*oldpos = *pos); if (!un_attended || !fullscreen) { gotoxy(POS_X,y); cputs(buf); } else { sb_move(filewin,y,POS_X); sb_puts(filewin,buf); } } eta = (int)((left*10/cur_baud*100/JANUS_EFFICIENCY+59)/60); if (eta != *oldeta) { sprintf(buf,"%4d",*oldeta = eta); if (!un_attended || !fullscreen) { gotoxy(ETA_X,y); cputs(buf); } else { sb_move(filewin,y,ETA_X); sb_puts(filewin,buf); } } if (un_attended && fullscreen) sb_show(); } /*****************************************************************************/ /* Compute and print throughput */ /*****************************************************************************/ static void through(long *bytes,long *started) { static byte *scrn = "+CPS: %u (%lu bytes) Efficiency: %lu%%%%"; unsigned long elapsed; register word cps; elapsed = time(NULL) - *started; cps = (elapsed) ? (word)(*bytes/elapsed) : 0; j_status(scrn, cps, *bytes, cps*1000L/cur_baud); TotalBytes += *bytes; } /*****************************************************************************/ /* Get next file to request, if any */ /*****************************************************************************/ static int get_filereq(byte req_started) { byte reqname[PATHLEN], linebuf[128]; register byte *p; int gotone = FALSE; FILE *reqfile; if (flag_file (TEST_AND_SET, &called_addr, 1)) return FALSE; strcpy(reqname,Abortlog_name); strcpy(strchr(reqname,'Z'),"REQ"); if (req_started) mark_done(reqname); if (dexists(reqname)) { if (!(remote_capabilities & WZ_FREQ)) j_status(msgtxt[M_FREQ_DECLINED]); else if (!(SharedCap & CANFREQ)) j_status(msgtxt[M_REMOTE_CANT_FREQ]); else { errno = 0; reqfile = fopen(reqname,read_ascii); if (!j_error(msgtxt[M_OPEN_MSG],reqname)) { while (!feof(reqfile)) { linebuf[0] = '\0'; if (!fgets(p = linebuf,sizeof(linebuf),reqfile)) break; while (*p >= ' ') ++p; *p = '\0'; if (linebuf[0] != ';') { strcpy(Rxbuf,linebuf); *(strchr(Rxbuf,'\0')+1) = SharedCap; gotone = TRUE; break; } } fclose(reqfile); j_error(msgtxt[M_CLOSE_MSG],reqname); if (!gotone) { unlink(reqname); j_error(msgtxt[M_UNLINK_MSG],reqname); } } } } return gotone; } /*****************************************************************************/ /* Record names of files to send in response to file request; callback */ /* routine for respond_to_file_requests() */ /*****************************************************************************/ static int record_reqfile(char *fname) { FILE *tmpfile; errno = 0; tmpfile = fopen(ReqTmp,"at"); if (!j_error(msgtxt[M_OPEN_MSG],ReqTmp)) { fputs(fname,tmpfile); j_error(msgtxt[M_WRITE_MSG],ReqTmp); fputs("\n",tmpfile); j_error(msgtxt[M_WRITE_MSG],ReqTmp); fclose(tmpfile); j_error(msgtxt[M_CLOSE_MSG],ReqTmp); ++ReqRecorded; return TRUE; } return FALSE; } /*****************************************************************************/ /* Get next file which was requested, if any */ /*****************************************************************************/ static byte get_reqname(byte first_req) { register byte *p; byte gotone = FALSE; FILE *tmpfile; struct stat f; if (!first_req) { errno = 0; close(Txfile); j_error(msgtxt[M_CLOSE_MSG],Txfname); Txfile = -1; mark_done(ReqTmp); } if (dexists(ReqTmp)) { errno = 0; tmpfile = fopen(ReqTmp,read_ascii); if (!j_error(msgtxt[M_OPEN_MSG],ReqTmp)) { while (!feof(tmpfile)) { Txfname[0] = '\0'; if (!fgets(p = Txfname,PATHLEN,tmpfile)) break; while (*p >= ' ') ++p; *p = '\0'; if (Txfname[0] != ';') { j_status(msgtxt[M_SENDING],Txfname); errno = 0; Txfile = open(Txfname,O_RDONLY|O_BINARY); if (Txfile != -1) errno = 0; if (j_error(msgtxt[M_OPEN_MSG],Txfname)) continue; while (p >= Txfname && *p != '\\' && *p != ':') --p; strcpy(Txbuf,++p); stat(Txfname,&f); sprintf(strchr(Txbuf,'\0')+1,"%lu %lo %o",Txlen = f.st_size,f.st_mtime,f.st_mode); if (!un_attended || !fullscreen) Tx_y = Next_y; else Tx_y = 1; xfer_summary(msgtxt[M_SEND],p,&Txlen,Tx_y); time(&Txsttime); gotone = TRUE; break; } } fclose(tmpfile); j_error(msgtxt[M_CLOSE_MSG],ReqTmp); if (!gotone) { unlink(ReqTmp); j_error(msgtxt[M_UNLINK_MSG],ReqTmp); } } } return gotone; } /*****************************************************************************/ /* Mark first unmarked line of file as done (comment it out) */ /*****************************************************************************/ static void mark_done(char *fname) { byte linebuf[128]; FILE *fh; long pos; if (dexists(fname)) { errno = 0; fh = fopen(fname,"rb+"); if (!j_error(msgtxt[M_OPEN_MSG],fname)) { while (!feof(fh)) { pos = ftell(fh); j_error(msgtxt[M_SEEK_MSG],fname); if (!fgets(linebuf,sizeof(linebuf),fh)) break; if (linebuf[0] != ';') { fseek(fh,pos,SEEK_SET); j_error(msgtxt[M_SEEK_MSG],fname); fputc(';',fh); j_error(msgtxt[M_WRITE_MSG],fname); break; } } fclose(fh); j_error(msgtxt[M_CLOSE_MSG],fname); } } }