/*--------------------------------------------------------------------------*/ /* */ /* */ /* ------------ Bit-Bucket Software, Co. */ /* \ 10001101 / Writers and Distributors of */ /* \ 011110 / Freely Available Software. */ /* \ 1011 / */ /* ------ */ /* */ /* (C) Copyright 1987-96, Bit Bucket Software Co. */ /* */ /* This module was written by Rick Huebner */ /* 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.260. */ /* */ /* 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:343/491 */ /* P.O. Box 460398 AlterNet 7:42/1491 */ /* Aurora, CO 80046 BBS-Net 86:2030/1 */ /* Internet f491.n343.z1.fidonet.org */ /* */ /* Please feel free to contact us at any time to share your comments about */ /* our software and/or licensing policies. */ /* */ /*--------------------------------------------------------------------------*/ /* Include this file before any other includes or defines! */ #include "includes.h" #include "janus.h" /* Private routines */ static void LOCALFUNC getfname (word); static void LOCALFUNC sendpkt (byte *, int, int); static void LOCALFUNC sendpkt32 (byte *, int, int); static void LOCALFUNC txbyte (byte); static long LOCALFUNC procfname (void); static byte LOCALFUNC rcvpkt (void); static void LOCALFUNC rxclose (word); static void LOCALFUNC endbatch (void); static void LOCALFUNC update_y (void); static void LOCALFUNC long_set_timer (long *, unsigned int); static int LOCALFUNC long_time_gone (long *); static int LOCALFUNC rcvrawbyte (void); static int LOCALFUNC rxbyte (void); static int LOCALFUNC get_filereq (byte); static int record_reqfile (char *); /* ** NOT ** LOCALFUNC!!! */ static int timeof_reqfile (long); /* ** NOT ** LOCALFUNC!!! */ static byte LOCALFUNC get_reqname (byte); static void LOCALFUNC mark_done (char *); /* Common with HYDRA.C */ void j_message (unsigned int, char *,...); void j_status (char *,...); void j_msgend (short); int j_error (char *, char *); void xfer_summary (char *, char *, long *, short); void update_status (long *, long *, long, int *, short); long through (long *, long *); /* 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 char *GenericError = "!%s"; static char *ReqTmpTempl = "JANUSREQ.%02x"; static char ReqTmp[16]; 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 int Rxblklen; /* Length of data in last data block packet recvd */ static int Next_y; /* Number of next available line on screen */ static short Tx_y; /* Line number of file transmission status display */ static short 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 Txsttime; /* Time at which we started sending current file */ static long Rxsttime; /* Time at which we started receiving current file */ static int EMSI_flag; /* Currently in EMSI-Session ? */ static int EMSI_aka; /* Current aka to send mail to */ static int EMSI_raka; /* Current aka to request files from */ /*****************************************************************************/ /* 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 */ char *holdname; /* Name of hold area */ 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 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 = 0; /* 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 */ sprintf (ReqTmp, ReqTmpTempl, TaskNumber); 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; /*------------------------------------------------------------------------*/ /* Allocate memory */ /*------------------------------------------------------------------------*/ Rxbuf = (char *) Txbuf + 4096 + 8; Txfname = Rxfname = NULL; if (((Txfname = malloc (PATHLEN)) == NULL) || ((Rxfname = malloc (PATHLEN)) == NULL)) { status_line (MSG_TXT (M_MEM_ERROR)); mdm_hangup (); goto freemem; } Rxbufmax = (byte *) (Rxbuf + BUFMAX + 8); /*------------------------------------------------------------------------*/ /* Initialize file transmission variables */ /*------------------------------------------------------------------------*/ if (!no_EMSI_Session) { EMSI_flag = TRUE; EMSI_aka = EMSI_raka = 0; called_addr = remote_akas[EMSI_aka]; } else EMSI_flag = FALSE; tx_inhibit = FALSE; last_rpostime = last_blkpos = lasttx = txstpos = rxstpos = xmit_retry = 0L; long_set_timer (&brain_dead, 120); if (cur_baud.rate_value > 9600L) { TimeoutSecs = 30; txblkmax = BUFMAX; } else { TimeoutSecs = (unsigned int) (40960L / cur_baud.rate_value); if (TimeoutSecs < 30) TimeoutSecs = 30; txblkmax = (int)cur_baud.rate_value / 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); (void) sprintf (Abortlog_name, "%s%s.Z\0", holdname, Hex_Addr_Str (&called_addr)); if ((Diskavail = zfree (CURRENT.sc_Inbound)) <= 0L) Diskavail = 0x7FFFFFF; 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 (MSG_TXT (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, MSG_TXT (M_TIMEOUT)); xmit_retry = 0L; switch (xstate) { case XRCVFNACK: xstate = XSENDFNAME; break; case XRCVFRNAKACK: xstate = XSENDFREQNAK; break; case XRCVEOFACK: errno = 0; if (lseek (Txfile, txpos = lasttx, SEEK_SET) == -1L) { (void) j_error (MSG_TXT (M_SEEK_MSG), Txfname); goto giveup; } xstate = XSENDBLK; break; } } } /*---------------------------------------------------------------------*/ /* Transmit next part of file, if any */ /*---------------------------------------------------------------------*/ switch (xstate) { case XSENDBLK: if (tx_inhibit) break; #ifdef GENERIC lasttx = txpos; Txbuf[0] = txpos & 0xff; Txbuf[1] = (txpos >> 8) & 0xff; Txbuf[2] = (txpos >> 16) & 0xff; Txbuf[3] = (txpos >> 24) & 0xff; #else *((long *) Txbuf) = lasttx = txpos; #endif errno = 0; blklen = read (Txfile, Txbuf + sizeof (txpos), txblklen); if (j_error (MSG_TXT (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 = (int) (strchr (strchr ((char *) Txbuf, '\0') + 1, '\0') - (char *) 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 ()) != 0) { if (pkttype != BADPKT) long_set_timer (&brain_dead, 120); switch (pkttype) { /*---------------------------------------------------------------*/ /* File data block or munged block */ /*---------------------------------------------------------------*/ case BADPKT: case BLKPKT: if (rstate == RRCVBLK) { long t; #ifdef GENERIC t = (long) Rxbuf[0] + ((long) Rxbuf[1] << 8) + ((long) Rxbuf[2] << 16) + ((long) Rxbuf[3] << 24); #else t = *(long *) Rxbuf; #endif if (pkttype == BADPKT || (t != Rxpos)) { if (pkttype == BLKPKT) { if (t < last_blkpos) rpos_retry = rpos_count = 0; last_blkpos = t; } 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 (MSG_TXT (M_GOING_ONE_WAY)); } rpos_count = 0; } if (++rpos_count == 1) (void) time ((time_t *) & rpos_sttime); j_message (Rx_y, MSG_TXT (M_J_BAD_PACKET), Rxpos); #ifdef GENERIC Rxbuf[0] = Rxpos & 0xff; Rxbuf[1] = (Rxpos >> 8) & 0xff; Rxbuf[2] = (Rxpos >> 16) & 0xff; Rxbuf[3] = (Rxpos >> 24) & 0xff; Rxbuf[4] = rpos_sttime & 0xff; Rxbuf[5] = (rpos_sttime >> 8) & 0xff; Rxbuf[6] = (rpos_sttime >> 16) & 0xff; Rxbuf[7] = (rpos_sttime >> 24) & 0xff; #else *((long *) Rxbuf) = Rxpos; *((long *) (Rxbuf + sizeof (Rxpos))) = rpos_sttime; #endif sendpkt ((byte *) 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; (void) write (Rxfile, Rxbuf + sizeof (Rxpos), Rxblklen -= sizeof (Rxpos)); if (j_error (MSG_TXT (M_WRITE_MSG), Rxfname)) goto giveup; Diskavail -= Rxblklen; Rxpos += Rxblklen; update_status (&Rxpos, &rxoldpos, Rxlen - Rxpos, &rxoldeta, Rx_y); if (Rxpos >= Rxlen) { long Rxtime; rxclose (GOOD_XFER); Rxlen -= rxstpos; Rxtime = through (&Rxlen, &Rxsttime); j_status ("%s-J%s %s", MSG_TXT (M_FILE_RECEIVED), (SharedCap & CANCRC32) ? "/32" : " ", Rxfname); j_msgend (Rx_y); update_files (0, Rxfname, Rxlen, Rxtime, 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 ((byte *) Rxbuf, strlen (Rxbuf) + 2, FREQPKT); attempting_req = TRUE; req_started = FALSE; } else { if (attempting_req) { attempting_req = FALSE; req_started = TRUE; } #ifdef GENERIC Rxbuf[0] = Rxpos & 0xff; Rxbuf[1] = (Rxpos >> 8) & 0xff; Rxbuf[2] = (Rxpos >> 16) & 0xff; Rxbuf[3] = (Rxpos >> 24) & 0xff; Rxbuf[4] = SharedCap; #else *((long *) Rxbuf) = Rxpos; Rxbuf[sizeof (Rxpos)] = (char) SharedCap; #endif sendpkt ((byte *) Rxbuf, sizeof (Rxpos) + 1, FNACKPKT); rxoldpos = rxoldeta = -1; if (Rxpos > -1) rstate = (byte) ((Rxfname[0]) ? RRCVBLK : RDONE); else j_status (MSG_TXT (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]) { #ifdef GENERIC SharedCap = (Rxblklen > sizeof (long)) ? Rxbuf[4] : 0; txpos = (long) Rxbuf[0] + ((long) Rxbuf[1] << 8) + ((long) Rxbuf[2] << 16) + ((long) Rxbuf[3] << 24); #else SharedCap = (byte) ((Rxblklen > sizeof (long)) ? Rxbuf[sizeof (long)] : 0); txpos = *((long *) Rxbuf); #endif if (txpos > -1L) { if (txpos) status_line (MSG_TXT (M_SYNCHRONIZING), txpos); errno = 0; if (lseek (Txfile, txstpos = txpos, SEEK_SET) == -1L) { (void) j_error (MSG_TXT (M_SEEK_MSG), Txfname); goto giveup; } xstate = XSENDBLK; } else { j_status (MSG_TXT (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); (void) sprintf ((char *) Txbuf, request_template, CURRENT.sc_Inbound, Hex_Addr_Str (&(alias[0])), TaskNumber); errno = 0; reqfile = fopen ((char *) Txbuf, write_ascii); if (reqfile != (FILE *) NULL) errno = 0; (void) j_error (MSG_TXT (M_OPEN_MSG), (char *) Txbuf); (void) fputs (Rxbuf, reqfile); (void) fputs ("\n", reqfile); (void) fclose (reqfile); (void) unlink (ReqTmp); ReqRecorded = 0; /* counted by record_reqfile */ (void) respond_to_file_requests (0, record_reqfile, timeof_reqfile); CURRENT.rq_Limit -= ReqRecorded; if ((sending_req = get_reqname (TRUE)) != 0) xstate = XSENDFNAME; 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) { long Txtime; Txlen -= txstpos; Txtime = through (&Txlen, &Txsttime); j_status ("%s-J%s %s", MSG_TXT (M_FILE_SENT), (SharedCap & CANCRC32) ? "/32" : " ", Txfname); j_msgend (Tx_y); update_files (1, Txfname, Txlen, Txtime, 0); 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) { long t; #ifdef GENERIC t = (long) Rxbuf[4] + ((long) Rxbuf[5] << 8) + ((long) Rxbuf[6] << 16) + ((long) Rxbuf[7] << 24); #else t = *((long *) (Rxbuf + sizeof (txpos))); #endif if (t != last_rpostime) { last_rpostime = t; xmit_retry = 0L; CLEAR_OUTBOUND (); errno = 0; #ifdef GENERIC lasttx = (long) Rxbuf[0] + ((long) Rxbuf[1] << 8) + ((long) Rxbuf[2] << 16) + ((long) Rxbuf[3] << 24); #else lasttx = *((long *) Rxbuf); #endif if (lseek (Txfile, txpos = lasttx, SEEK_SET) == -1L) { (void) j_error (MSG_TXT (M_SEEK_MSG), Txfname); goto giveup; } j_status (MSG_TXT (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 (MSG_TXT (M_UNKNOWN_PACKET), pkttype); /* fallthrough */ case HALTPKT: giveup: j_status (MSG_TXT (M_SESSION_ABORT)); if (Txfname[0]) getfname (ABORT_XFER); if (rstate == RRCVBLK) { 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 (MSG_TXT (M_NOTHING_TO_SEND), Full_Addr_Str (&called_addr)); abortxfer: endbatch (); /*------------------------------------------------------------------------*/ /* Release allocated memory */ /*------------------------------------------------------------------------*/ freemem: if (Txfname) free (Txfname); if (Rxfname) free (Rxfname); 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 LOCALFUNC getfname (word xfer_flag) { static byte floflag, bad_xfers; static char outboundname[PATHLEN]; static long floname_pos; static FILE *flofile; 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) { if (EMSI_flag) status_line (MSG_TXT (M_EMSI_PROC_NODE), Full_Addr_Str (&remote_akas[EMSI_aka]), HoldAreaNameMunge (&remote_akas[EMSI_aka])); floflag = outboundname[0] = '\0'; flofile = NULL; } else /*------------------------------------------------------------------------*/ /* If we were already sending a file, close it and clean up */ /*------------------------------------------------------------------------*/ if (Txfile != -1) { errno = 0; (void) close (Txfile); 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 (MSG_TXT (M_UNLINKING_MSG), Txfname); (void) unlink (Txfname); break; case TRUNC_AFTER: j_status (MSG_TXT (M_TRUNC_MSG), Txfname); Txfile = open (Txfname, O_TRUNC | O_RDWR, S_IREAD | S_IWRITE); if (Txfile != -1) errno = 0; (void) j_error (MSG_TXT (M_TRUNC_MSG), Txfname); (void) close (Txfile); Txfile = -1; } /*------------------------------------------------------------------*/ /* If processing .?LO file, flag filename as sent (name[0] = '~') */ /*------------------------------------------------------------------*/ skipname: if (floflag) { curr_pos = ftell (flofile); if (curr_pos == -1L) (void) j_error (MSG_TXT (M_SEEK_MSG), outboundname); if (fseek (flofile, floname_pos, SEEK_SET) == -1L) (void) j_error (MSG_TXT (M_SEEK_MSG), outboundname); if (fputc (Txfname[0] = '~', flofile) != EOF) errno = 0; (void) j_error (MSG_TXT (M_WRITE_MSG), outboundname); if (fseek (flofile, curr_pos, SEEK_SET) == -1L) (void) j_error (MSG_TXT (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 (EMSI_flag) holdname = HoldAreaNameMunge (&remote_akas[EMSI_aka]); else holdname = HoldAreaNameMunge (&called_addr); next_aka: if (!floflag) { /*---------------------------------------------------------------------*/ /* If first getfname() for this batch, init filename to .OUT */ /*---------------------------------------------------------------------*/ if (!outboundname[0]) { if (!EMSI_flag) (void) sprintf (outboundname, "%s%s.OUT", holdname, Hex_Addr_Str (&called_addr)); else (void) sprintf (outboundname, "%s%s.OUT", holdname, Hex_Addr_Str (&remote_akas[EMSI_aka])); *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 { outboundname[0] = Txfname[0] = Txbuf[0] = Txbuf[1] = floflag = '\0'; if (EMSI_flag && (++EMSI_aka < num_rakas)) { holdname = HoldAreaNameMunge (&remote_akas[EMSI_aka]); status_line (MSG_TXT (M_EMSI_PROC_NODE), Full_Addr_Str (&remote_akas[EMSI_aka]), holdname); goto next_aka; } } } } /*---------------------------------------------------------------------*/ /* Check potential outbound name; if file doesn't exist keep looking */ /*---------------------------------------------------------------------*/ if (outboundname[0]) { if (!dexists (outboundname)) goto nxtout; if (floflag) goto rdflo; (void) strcpy (Txfname, outboundname); /*------------------------------------------------------------------*/ /* Start FNAMEPKT using .PKT alias */ /*------------------------------------------------------------------*/ invent_pkt_name ((char *) 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 = share_fopen (outboundname, "rb+", DENY_WRITE); if (flofile == (FILE *) NULL) { j_error (MSG_TXT (M_OPEN_MSG), outboundname); goto nxtout; } } errno = 0; floname_pos = ftell (flofile); if (floname_pos == -1L) (void) j_error (MSG_TXT (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]; (void) strcpy (Txfname, Txfname + 1); break; default: Do_after = NOTHING_AFTER; break; } /*------------------------------------------------------------------*/ /* Start FNAMEPKT with simple filename */ /*------------------------------------------------------------------*/ while (p >= Txfname && *p != '\\' && *p != ':') --p; (void) strcpy ((char *) Txbuf, ++p); } else { /*------------------------------------------------------------------*/ /* Finished reading this .?LO file; clean up and look for another */ /*------------------------------------------------------------------*/ errno = 0; (void) fclose (flofile); flofile = NULL; if (!bad_xfers) { (void) unlink (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 (MSG_TXT (M_SENDING), Txfname); errno = 0; Txfile = share_open (Txfname, O_RDONLY | O_BINARY, DENY_WRITE); if (Txfile != -1) errno = 0; if (j_error (MSG_TXT (M_OPEN_MSG), Txfname)) goto skipname; if (isatty (Txfile)) /* Check for character devices */ { (void) close (Txfile); /* return errors if it is the case */ errno = 1; (void) j_error (MSG_TXT (M_DEVICE_MSG), Txfname); goto skipname; } (void) stat (Txfname, &f); #ifdef ANSI_TIME_T f.st_mtime -= ANSI_TIME_T_DELTA; #endif (void) sprintf (strchr ((char *) 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 (MSG_TXT (M_SEND), ++p, &Txlen, Tx_y); (void) time ((time_t *) & 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 LOCALFUNC 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 LOCALFUNC 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 LOCALFUNC 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 LOCALFUNC procfname (void) { register char *p; char linebuf[128], *fileinfo, *badfname; long filestart, bytes; FILE *abortlog; struct stat f; int i; /*------------------------------------------------------------------------*/ /* Initialize for file reception */ /*------------------------------------------------------------------------*/ badfname = NULL; Rxfname[0] = Resume_WaZOO = 0; /*------------------------------------------------------------------------*/ /* Save info on WaZOO transfer in case of abort */ /*------------------------------------------------------------------------*/ (void) strcpy (Resume_name, fancy_str (Rxbuf)); fileinfo = strchr (Rxbuf, '\0') + 1; p = strchr (fileinfo, '\0') + 1; SharedCap = (byte) ((Rxblklen > p - Rxbuf) ? *p & OURCAP : 0); /*------------------------------------------------------------------------*/ /* If this is a null FNAMEPKT, return OK immediately */ /*------------------------------------------------------------------------*/ if (!Rxbuf[0]) return 0L; p = Rxbuf + strlen ((char *) Rxbuf) - 1; /* Find transmitted simple * filename */ while (p >= Rxbuf && *p != '\\' && *p != '/' && *p != ':') p--; (void) strcpy (linebuf, ++p); (void) strlwr (linebuf); p = check_netfile (linebuf); j_status ("#%s %s %s", MSG_TXT (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 (MSG_TXT (M_NO_LENGTH)); return -1L; } #ifdef ANSI_TIME_T Rxfiletime += ANSI_TIME_T_DELTA; #endif (void) 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 (abortlog != (FILE *) NULL) errno = 0; if (!j_error (MSG_TXT (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; (void) fclose (abortlog); } } /*------------------------------------------------------------------------*/ /* Open either the old or a new file, as appropriate */ /*------------------------------------------------------------------------*/ p = strchr (strcpy (Rxfname, CURRENT.sc_Inbound), '\0'); errno = 0; if (Resume_WaZOO) { (void) strcpy (p, badfname); Rxfile = open (Rxfname, O_CREAT | O_RDWR | O_BINARY, S_IREAD | S_IWRITE); } else { (void) 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)) { (void) stat (Rxfname, &f); if (Rxlen == f.st_size && (time_t) Rxfiletime == f.st_mtime) { j_status (MSG_TXT (M_ALREADY_HAVE), Rxfname); return -1L; } i = strlen (Rxfname) - 1; if ((!overwrite) || (is_arcmail (Rxfname, i))) { unique_name (Rxfname); j_status (MSG_TXT (M_RENAME_MSG), Rxfname); } else { (void) unlink (Rxfname); } } Rxfile = open (Rxfname, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IREAD | S_IWRITE); } if (Rxfile != -1) errno = 0; if (j_error (MSG_TXT (M_OPEN_MSG), Rxfname)) return -1L; if (isatty (Rxfile)) /* Check for character devices */ { (void) close (Rxfile); /* Return errors if it is the case */ errno = 1; (void) j_error (MSG_TXT (M_DEVICE_MSG), Rxfname); return -1L; } /*------------------------------------------------------------------------*/ /* Determine initial file data position */ /*------------------------------------------------------------------------*/ if (Resume_WaZOO) { (void) stat (Rxfname, &f); j_status (MSG_TXT (M_SYNCHRONIZING_OFFSET), filestart = f.st_size); p = Rxbuf; errno = 0; if (lseek (Rxfile, filestart, SEEK_SET) == -1L) { (void) j_error (MSG_TXT (M_SEEK_MSG), Rxfname); (void) close (Rxfile); return -1L; } } else filestart = 0L; /*------------------------------------------------------------------------*/ /* Check for enough disk space */ /*------------------------------------------------------------------------*/ bytes = Rxlen - filestart + 10240; if (bytes > Diskavail) { j_status (MSG_TXT (M_OUT_OF_DISK_SPACE)); (void) close (Rxfile); return -1L; } /*------------------------------------------------------------------------*/ /* Print status message for the sysop */ /*------------------------------------------------------------------------*/ if (!un_attended || !fullscreen) Rx_y = Next_y; else Rx_y = 2; xfer_summary (MSG_TXT (M_RECV), p, &Rxlen, Rx_y); (void) time ((time_t *) & 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 LOCALFUNC 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, MSG_TXT (M_KBD_MSG)); return HALTPKT; } /*------------------------------------------------------------------------*/ /* If not accumulating packet yet, find start of next packet */ /*------------------------------------------------------------------------*/ WaitFlag = FALSE; p = Rxbufptr; if (!p) { do c = rxbyte (); while (c >= 0 || c == PKTEND); switch (c) { case PKTSTRT: rxcrc32 = FALSE; p = (byte *) Rxbuf; crc = 0; break; case PKTSTRT32: rxcrc32 = TRUE; p = (byte *) Rxbuf; crc32 = 0xFFFFFFFF; break; case NOCARRIER: j_status (GenericError, &(MSG_TXT (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 = (int) (--p - (byte *) Rxbuf); return *p; } } /* fallthrough */ /*---------------------------------------------------------------------*/ /* Bad CRC, carrier lost, or buffer overflow from munged PKTEND */ /*---------------------------------------------------------------------*/ default: if (c == NOCARRIER) { j_status (GenericError, &(MSG_TXT (M_NO_CARRIER)[1])); return HALTPKT; } else { Rxbufptr = NULL; return BADPKT; } /*---------------------------------------------------------------------*/ /* Emptied buffer; save partial packet and let sender do something */ /*---------------------------------------------------------------------*/ case BUFEMPTY: time_release (); /* Also give other tasks a chance */ Rxbufptr = p; return NOPKT; /*---------------------------------------------------------------------*/ /* PKTEND was trashed; discard partial packet and prep for next one */ /*---------------------------------------------------------------------*/ case PKTSTRT: rxcrc32 = FALSE; Rxbufptr = (byte *) Rxbuf; crc = 0; return BADPKT; case PKTSTRT32: rxcrc32 = TRUE; Rxbufptr = (byte *) Rxbuf; crc32 = 0xFFFFFFFF; return BADPKT; } } /*****************************************************************************/ /* Close file being received and perform post-reception aborted-transfer */ /* recovery cleanup if neccessary. */ /*****************************************************************************/ static void LOCALFUNC rxclose (word xfer_flag) { register char *p; char namebuf[PATHLEN], linebuf[128]; byte c; FILE *abortlog, *newlog; struct utimbuf utimes; /*------------------------------------------------------------------------*/ /* Close file we've been receiving */ /*------------------------------------------------------------------------*/ errno = 0; (void) close (Rxfile); if (Rxfiletime > 0) /* utime doesn't like negative numbers */ { utimes.UT_ACTIME = Rxfiletime; utimes.modtime = Rxfiletime; (void) utime (Rxfname, (UTIMBUF *) & 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 (abortlog != (FILE *) NULL) errno = 0; if (!j_error (MSG_TXT (M_OPEN_MSG), Abortlog_name)) { c = 0; (void) strcpy (strchr (strcpy (namebuf, Abortlog_name), '\0') - 1, "TMP"); newlog = fopen (namebuf, write_ascii); if (newlog != (FILE *) NULL) errno = 0; if (!j_error (MSG_TXT (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 = ' '; (void) fputs (linebuf, newlog); if (j_error (MSG_TXT (M_WRITE_MSG), namebuf)) break; ++c; } } errno = 0; (void) fclose (abortlog); (void) fclose (newlog); (void) unlink (Abortlog_name); if (c) { if (!rename (namebuf, Abortlog_name)) errno = 0; (void) j_error (MSG_TXT (M_RENAME_MSG), namebuf); } else { (void) unlink (namebuf); } } else { (void) fclose (abortlog); } } j_status (MSG_TXT (M_FINISHED_PART), Resume_name); unique_name (strcat (strcpy (namebuf, CURRENT.sc_Inbound), Resume_name)); if (!rename (Rxfname, namebuf)) { errno = 0; (void) strcpy (Rxfname, namebuf); } else (void) j_error (MSG_TXT (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 (MSG_TXT (M_SAVING_PART), Rxfname); unique_name (strcat (strcpy (namebuf, CURRENT.sc_Inbound), "BadWaZOO.001")); if (!rename (Rxfname, namebuf)) errno = 0; (void) j_error (MSG_TXT (M_RENAME_MSG), Rxfname); abortlog = fopen (Abortlog_name, append_ascii); if (abortlog != (FILE *) NULL) errno = 0; if (!j_error (MSG_TXT (M_OPEN_MSG), Abortlog_name)) { (void) fprintf (abortlog, "%s %s %s\n", Resume_name, namebuf + strlen (CURRENT.sc_Inbound), Resume_info); (void) j_error (MSG_TXT (M_WRITE_MSG), Abortlog_name); (void) fclose (abortlog); } else { (void) unlink (namebuf); } } } /*****************************************************************************/ /* Try REAL HARD to disengage batch session cleanly */ /*****************************************************************************/ static void LOCALFUNC 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); sendpkt (NULL, 0, HALTPKT); long_set_timer (&timeval, TimeoutSecs); /*------------------------------------------------------------------------*/ /* 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); while (!OUT_EMPTY()) time_release (); } /*****************************************************************************/ /* Print a message in the message field of a transfer status line */ /*****************************************************************************/ void j_message (unsigned int pos, char *va_alist,...) { va_list arg_ptr; short 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) (void) strcat (buf, " "); if (!un_attended || !fullscreen) { (void) cputs (buf); } else { sb_puts (filewin, buf); sb_show (); } va_end (arg_ptr); } /*****************************************************************************/ /* Clear out a line in the log status display */ /*****************************************************************************/ void j_msgend (short pos) { if (un_attended && fullscreen) { sb_move (filewin, pos, 2); /* 72 blanks */ sb_puts (filewin, " "); sb_show (); } } /*****************************************************************************/ /* Print & log status message without messing up display */ /*****************************************************************************/ 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 */ /*****************************************************************************/ int j_error (char *msg, char *fname) { register int e; if ((e = (int) errno) != 0) { if (!un_attended || !fullscreen) gotoxy (1, Next_y - 1); (void) got_error (msg, fname); if (!un_attended || !fullscreen) update_y (); } return e; } /*****************************************************************************/ /* Update screen position variables after printing a message */ /*****************************************************************************/ static void LOCALFUNC 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 LOCALFUNC long_set_timer (long *Buffer, unsigned int Duration) { (void) time ((time_t *) Buffer); *Buffer += (long) Duration; } /*****************************************************************************/ /* Return TRUE if timehack has been passed, FALSE if not */ /*****************************************************************************/ static int LOCALFUNC long_time_gone (long *TimePtr) { return (time (NULL) > (time_t) * TimePtr); } /*****************************************************************************/ /* Receive cooked escaped byte translated to avoid various problems. */ /* Returns raw byte, BUFEMPTY, PKTSTRT, PKTEND, or NOCARRIER. */ /*****************************************************************************/ static int LOCALFUNC 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 LOCALFUNC rcvrawbyte (void) { long timeval; if ((int) PEEKBYTE () >= 0) return MODEM_IN (); if (!CARRIER) return NOCARRIER; if (!WaitFlag) return BUFEMPTY; timeval = time (NULL) + TimeoutSecs; while ((int) PEEKBYTE () < 0) { if (!CARRIER) return NOCARRIER; if (time (NULL) > (time_t) timeval) return BUFEMPTY; time_release (); } return MODEM_IN (); } /*****************************************************************************/ /* Display start-of-transfer summary info */ /*****************************************************************************/ void xfer_summary (char *xfertype, char *fname, long *len, short y) { char buf[128]; if (!un_attended || !fullscreen) gotoxy (2, y); else sb_move (filewin, y, 2); (void) sprintf (buf, "%s %12.12s; 0/%8ldb,%4ld min. ", xfertype, fname, *len, ((*len * 10L / cur_baud.rate_value * 100L / JANUS_EFFICIENCY + 59L) / 60L)); if (!un_attended || !fullscreen) { (void) cputs (buf); (void) cputs (local_CEOL); update_y (); } else { sb_puts (filewin, buf); sb_show (); } } /*****************************************************************************/ /* Update any status line values which have changed */ /*****************************************************************************/ void update_status (long *pos, long *oldpos, long left, int *oldeta, short y) { char buf[16]; register int eta; elapse_time (); if (*pos != *oldpos) { (void) sprintf (buf, "%8ld", *oldpos = *pos); if (!un_attended || !fullscreen) { gotoxy (POS_X, y); (void) cputs (buf); } else { sb_move (filewin, y, POS_X); sb_puts (filewin, buf); } } eta = (int) ((left * 10L / cur_baud.rate_value * 100L / JANUS_EFFICIENCY + 59L) / 60L); if (eta != *oldeta) { (void) sprintf (buf, "%4d Min", *oldeta = eta); if (!un_attended || !fullscreen) { gotoxy (ETA_X, y); (void) cputs (buf); } else { sb_move (filewin, y, ETA_X); sb_puts (filewin, buf); } } if (un_attended && fullscreen) sb_show (); } /*****************************************************************************/ /* Compute and print throughput */ /*****************************************************************************/ long through (long *bytes, long *started) { static char *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.rate_value); return elapsed; } /*****************************************************************************/ /* Get next file to request, if any */ /*****************************************************************************/ static int LOCALFUNC get_filereq (byte req_started) { char reqname[PATHLEN], linebuf[128], *holdname; register char *p; int gotone = FALSE; FILE *reqfile; next_aka: if (!EMSI_flag) { holdname = HoldAreaNameMunge (&called_addr); (void) sprintf (reqname, "%s%s.REQ\0", holdname, Hex_Addr_Str (&called_addr)); } else { holdname = HoldAreaNameMunge (&remote_akas[EMSI_raka]); (void) sprintf (reqname, "%s%s.REQ\0", holdname, Hex_Addr_Str (&remote_akas[EMSI_raka])); } if (req_started) mark_done (reqname); if (dexists (reqname)) { if (!(remote_capabilities & WZ_FREQ)) j_status (MSG_TXT (M_FREQ_DECLINED)); else if (!(SharedCap & CANFREQ)) j_status (MSG_TXT (M_REMOTE_CANT_FREQ)); else { errno = 0; reqfile = fopen (reqname, read_ascii); if (reqfile != (FILE *) NULL) errno = 0; if (!j_error (MSG_TXT (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] != ';') { (void) strcpy (Rxbuf, linebuf); *(strchr (Rxbuf, '\0') + 1) = SharedCap; gotone = TRUE; break; } } errno = 0; (void) fclose (reqfile); if (!gotone) { (void) unlink (reqname); } } } } if (!gotone && EMSI_flag) { if (++EMSI_raka < num_rakas) goto next_aka; } 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 *pFileT; errno = 0; pFileT = fopen (ReqTmp, append_ascii); if (pFileT != (FILE *) NULL) errno = 0; if (!j_error (MSG_TXT (M_OPEN_MSG), ReqTmp)) { errno = 0; (void) fputs (fname, pFileT); (void) j_error (MSG_TXT (M_WRITE_MSG), ReqTmp); (void) fputs ("\n", pFileT); (void) j_error (MSG_TXT (M_WRITE_MSG), ReqTmp); (void) fclose (pFileT); ++ReqRecorded; return TRUE; } return FALSE; } /*****************************************************************************/ /* Estimate transfer time for requested file(s); callback */ /* routine for respond_to_file_requests() */ /*****************************************************************************/ static int timeof_reqfile (long filesize) { int i; i = (int) (filesize * 10L / cur_baud.rate_value * 100L / JANUS_EFFICIENCY); /* * Since actual transfers don't occur while in file request code, we have to "reverse engineer" the testing for end-time. */ if (CURRENT.time_Limit != 0) { if (((long) time (NULL) + i - freq_accum.time) > CURRENT.time_Limit) return i; freq_accum.time -= i; /* Add time to (now-start) interval */ return 0; } return i; } /*****************************************************************************/ /* Get next file which was requested, if any */ /*****************************************************************************/ static byte LOCALFUNC get_reqname (byte first_req) { register char *p; byte gotone = FALSE; FILE *pFileT; struct stat f; if (!first_req) { errno = 0; (void) close (Txfile); Txfile = -1; mark_done (ReqTmp); } if (dexists (ReqTmp)) { errno = 0; pFileT = fopen (ReqTmp, read_ascii); if (pFileT != (FILE *) NULL) errno = 0; if (!j_error (MSG_TXT (M_OPEN_MSG), ReqTmp)) { while (!feof (pFileT)) { Txfname[0] = '\0'; if (!fgets (p = Txfname, PATHLEN, pFileT)) break; while (*p >= ' ') ++p; *p = '\0'; if (Txfname[0] != ';') { j_status (MSG_TXT (M_SENDING), Txfname); errno = 0; Txfile = share_open (Txfname, O_RDONLY | O_BINARY, DENY_WRITE); if (Txfile != -1) errno = 0; if (j_error (MSG_TXT (M_OPEN_MSG), Txfname)) continue; while (p >= Txfname && *p != '\\' && *p != ':') --p; (void) strcpy ((char *) Txbuf, ++p); (void) stat (Txfname, &f); #ifdef ANSI_TIME_T f.st_mtime -= ANSI_TIME_T_DELTA; #endif (void) sprintf (strchr ((char *) 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 (MSG_TXT (M_SEND), p, &Txlen, Tx_y); (void) time ((time_t *) & Txsttime); gotone = TRUE; break; } } (void) fclose (pFileT); if (!gotone) { (void) unlink (ReqTmp); } } } return gotone; } /*****************************************************************************/ /* Mark first unmarked line of file as done (comment it out) */ /*****************************************************************************/ static void LOCALFUNC mark_done (char *fname) { char linebuf[128]; FILE *fh; long pos; if (dexists (fname)) { errno = 0; fh = fopen (fname, "rb+"); if (fh != (FILE *) NULL) errno = 0; if (!j_error (MSG_TXT (M_OPEN_MSG), fname)) { while (!feof (fh)) { pos = ftell (fh); if (pos == -1L) (void) j_error (MSG_TXT (M_SEEK_MSG), fname); if (!fgets (linebuf, sizeof (linebuf), fh)) break; if (linebuf[0] != ';') { if (fseek (fh, pos, SEEK_SET) == -1L) (void) j_error (MSG_TXT (M_SEEK_MSG), fname); (void) fputc (';', fh); (void) j_error (MSG_TXT (M_WRITE_MSG), fname); break; } } (void) fclose (fh); } } }