/*
*       netitl.c
*
* Handles ITL layer of C86Net
*/
/* #define C68_DEBUG */
#define COMPACT
/*
*       history
*
* 91Aug15 HAW Change comment style.
* 88Jun18 HAW  Compact messages during transfer.
* 88Jul01 HAW  Fix bug from excess EOTs generated by WXMODEM.
* 88Jun22 HAW  WXMODEM as primary transmission medium.
* 88Apr09 HAW  Created.
*/
#include "ctdl.h"
/*
*       contents
*
* ITL_DeInit()    DeInitialize the ITL layer
* ITL_InitCall()    Initialize the ITL layer
* ITL_optimize()    Optimize the ITL layer
* ITL_Receive()   Sets up to receive an ITL transmission
* ITL_Rec_Optimize()      Receive attempt to optimize layer
* ITL_Send()    Manages ITL transmissions
* increment()   Handles writing to the internal buf
* sendITLchar()   Sends a char at the ITL level
*/
/*
* External variable declarations in NETITL.C
*/

extern char logNetResults;
extern char netDebug;



static int (*MsgIn)(int c);
static int (*MsgOut)(int c);
int (*ITLFunc)(int c);
long EncCount;
#ifdef WXMODEM_AVAILABLE
/* you can touch this line */
char ItlWxmodem = FALSE;
#else
/* don't touch this line */
char ItlWxmodem = FALSE;
#endif
static int  ITL_protocol;
static int  InternalCounter;
static char Optimized;
extern AN_UNSIGNED RecBuf[];
extern PROTO_TABLE Table[];
extern int     TransProtocol;
extern char    ErrBuf[100];
extern FILE    *netLog;
extern CONFIG      cfg;     /* Lots an lots of variables    */
/*
* ITL_DeInit()
*
* This function deactivates the ITL at end of a network session.
*/
void ITL_DeInit()
  {
  TransProtocol = ASCII;
  Optimized = FALSE;

  }
/*
* ITL_InitCall()
*
* This will initialize for a network session, either caller or receiver.
*/
void ITL_InitCall()
  {
  ITL_protocol = XMDM;  /* Init to XMODEM. */
  ITLFunc = MsgOut = Table[ITL_protocol].method;
  MsgIn = putFLChar;
  Optimized = FALSE;

  }
/*
* ITL_Optimize()
*
* This function is called when this system is the calling system.  It
* attempts to negotiate for faster information transmission details.
* At the moment, it asks for the right to use Ymodem (Wxmodem under
* certain circumstances, but Wxmodem is not reliable), and then attempt
* to use compaction on message transmission.
*
* The "both" parameter controls whether we go for both the protocol and
* compaction options, or just the protocol.  This is used when we may be
* only transferring files (where only protocol is useful) and Mass Transfers
* are active.
*/
void ITL_optimize(char both)
  {
  struct cmd_data cmds;
  extern NetBuffer netBuf;
  if (Optimized) return;
  Optimized = TRUE;   /* ... or at least we tried */
  #ifdef WXMODEM_AVAILABLE
  zero_struct(cmds);
  strCpy(cmds.fields[0], WXM_ITL);
  cmds.command = ITL_PROTOCOL;
  if (ItlWxmodem && sendNetCommand(&cmds, "ITL_opt"))
    {
    if( logNetResults && netDebug )splitF(netLog, "WXMODEM selected\n");
    ITL_protocol = WXMDM;
    ITLFunc = MsgOut = Table[ITL_protocol].method;

    }
  else
    {
    if (ItlWxmodem && logNetResults && netDebug ) splitF(netLog, "No WXMODEM\n");
    zero_struct(cmds);
    strCpy(cmds.fields[0], YM_ITL);
    cmds.command = ITL_PROTOCOL;
    if (sendNetCommand(&cmds, "ITL_opt"))
      {
      if( logNetResults && netDebug )splitF(netLog, "YMODEM selected\n");
      ITL_protocol = YMDM;
      ITLFunc = MsgOut = Table[ITL_protocol].method;

      }
    else if( logNetResults && netDebug )splitF(netLog, "No YMODEM\n");

    }
  #else
  zero_struct(cmds);
  strCpy(cmds.fields[0], YM_ITL);
  cmds.command = ITL_PROTOCOL;
  if (sendNetCommand(&cmds, "ITL_opt"))
    {
    if( logNetResults && netDebug )splitF(netLog, "YMODEM selected\n");
    ITL_protocol = YMDM;
    ITLFunc = MsgOut = Table[ITL_protocol].method;

    }
  else  if( logNetResults && netDebug )splitF(netLog, "No YMODEM\n");
  #endif
  zero_struct(cmds);
  strCpy(cmds.fields[0], COMPACT_1);
  cmds.command = ITL_COMPACT;
  if (!both) return;
  if (sendNetCommand(&cmds, "ITL compact"))
    {
    MsgOut = Encode;
    MsgIn  = Decode;

    }
  else
    {
    if( logNetResults && netDebug )splitF(netLog, "No compaction\n");
    ITLFunc = MsgOut = Table[ITL_protocol].method;

    }

  }
/*
* ITL_Receive()
*
* This sets up the system to receive an ITL transmission.  Please note how
* this function is blind to the actual content of what is coming in.
*
* Inputs:
* o FileName - if NULL or zero length, this indicates the transmission should
*   be to an internal buffer and shouldn't exceed 128 bytes.  If it is a valid
*   filename (can be opened) then a file by that name should be created (not
*   appended to) and the incoming stuff written to it.  In the former case the
*   input should be placed directly into the buffer, while in the latter case
*   the indirect function parameter WriteFn should be used.
* o ReplyFirst - if TRUE, then send a positive reply to the facility request
*   (whatever it is) before beginning reception.  A kludge to save space.
* o OpenIt - if FALSE, then don't attempt to open the named file.  If TRUE,
*   then open the named file using the global FILE descriptor upfd.
* o WriteFn - Possibly NULL function to be used for file reception.
* o CloseFn - The function to be called once reception is finished.
*
* Returns:
* o ITL_SUCCESS - reception successful.
* o ITL_BAD_TRANS - reception unsuccessful.
* o ITL_NO_OPEN - reception unsuccessful.
*/
char ITL_Receive(FileName, ReplyFirst, OpenIt, WriteFn, CloseFn)
char *FileName;   /* If this is NULL or length = 0, then ITL data */
/* should be placed in an internal buffer       */
int ReplyFirst;  /* Should we do a reply(GOOD) call first?       */
int OpenIt;    /* FIle needs opening?        */
int (*WriteFn)(int c);  /* Writing the received data      */
int (*CloseFn)(FILE *f);/* Closing out the file, if needed    */
  {
  extern char *WRITE_ANY;
  extern FILE *upfd;
  if (FileName == NULL || strLen(FileName) == 0)
    {
    if (ReplyFirst) reply(GOOD, "");
    InternalCounter = 0;
    /* This is temporary until we get WXMODEM available */
    if (Reception(ITL_protocol, increment) == TRAN_SUCCESS)
      {
      return ITL_SUCCESS;

      }
    else
      {
      killConnection();
      return ITL_BAD_TRANS;

      }

    }
  if (OpenIt)
    {
    if ((upfd = safeopen(FileName, WRITE_ANY)) == NULL)
      {
      /*    Handle an error at this point. */
      if (ReplyFirst) reply(BAD, "System error");
      sPrintf(ErrBuf, "ERROR: Couldn't open %s!", FileName);
      netResult(ErrBuf);
      return ITL_NO_OPEN;

      }
    #ifdef HORRID_AMIGA_LATTICE_BUG
    setnbf(upfd);
    #endif

    }
  if (ReplyFirst) reply(GOOD, "");
  if (Reception(ITL_protocol, WriteFn) != TRAN_SUCCESS)
    {
    (*CloseFn)(upfd);
    unlink(FileName);
    killConnection();
    return ITL_BAD_TRANS;

    }
  else
    {
    (*CloseFn)(upfd);
    return ITL_SUCCESS;

    }

  }
/*
* ITL_rec_optimize()
*
* This is called when the calling system wants to try to optimize the
* information transfer protocol.  Supported protocols in this implementation
* are Xmodem, Ymodem, and Wxmodem (iff WXMODEM_AVAILABLE is defined).
*/
void ITL_rec_optimize(struct cmd_data *cmds)
  {
  int protocol;
  #define WXMODEM_WORKS
  #ifdef WXMODEM_WORKS
  #ifdef WXMODEM_AVAILABLE
  blah
  protocol = atoi(cmds->fields[0]);
  if (protocol < 0 || protocol > 2)
  reply(BAD, "unrecognized protocol");
  else
    {
    /* this is probably bad coding, really. */
    reply(GOOD, "");
    ITL_protocol = protocol + 1;    /* translates correctly for now */
    ITLFunc = MsgOut = Table[ITL_protocol].method;
    Optimized = TRUE;

    }
  #else
  protocol = atoi(cmds->fields[0]);
  if (protocol < 0 || protocol > 2)
  reply(BAD, "unrecognized protocol");
  else
    {
    /* this is probably bad coding, really. */
    if (strCmp(cmds->fields[0], WXM_ITL) == SAMESTRING)
    reply(BAD, "can't trust");
    else
      {
      reply(GOOD, "");
      ITL_protocol = protocol + 1; /* translates correctly for now */
      ITLFunc = MsgOut = Table[ITL_protocol].method;
      Optimized = TRUE;

      }

    }
  #endif
  #else
  protocol = atoi(cmds->fields[0]);
  if (protocol < 0 || protocol > 2)
  reply(BAD, "unrecognized protocol");
  else
    {
    /* this is probably bad coding, really. */
    if (strCmp(cmds->fields[0], WXM_ITL) == SAMESTRING)
    reply(BAD, "can't trust");
    else
      {
      reply(GOOD, "");
      ITL_protocol = protocol + 1; /* translates correctly for now */
      ITLFunc = MsgOut = Table[ITL_protocol].method;
      Optimized = TRUE;

      }

    }
  #endif

  }
/*
* ITL_RecCompact()
*
* This function receives the request to compact messages.  Only Compaction
* method 0 (proprietary testing method) is currently supported.
*/
void ITL_RecCompact(struct cmd_data *cmds)
  {
  if (atoi(cmds->fields[0]) == 0)
    {
    reply(GOOD, "");
    MsgIn = Decode;
    MsgOut = Encode;

    }
  else reply(BAD, "unrecognized compaction");

  }
/*
* ITL_Send()
*
* This function is used to manage ITL transmissions.  It initializes the
* sending protocol for the impending transmission, which includes signaling
* the receiving system of the beginning of the protocol.  In event of failure,
* it drops carrier and returns FALSE, otherwise it returns TRUE.
*
* NB: All users should check the return value of this function.
*/
char ITL_Send(char mode)
  {
  int reason;
  if ((reason = Transmission(ITL_protocol, mode)) != TRAN_SUCCESS)
    {
    if( logNetResults )
      splitF(netLog, "\nITL_send failure %d, mode %d\n", reason, mode);
    killConnection();
    return FALSE;

    }
  return TRUE;

  }
/*
* sendITLchar(c)
*
* This sends a single character (byte) via the ITL.
*/
int sendITLchar(int c)
  {
  EncCount++;
  return (*ITLFunc)(c);

  }
/*
* increment()
*
* This function is used to place incoming information into an internal buffer
* for later processing.  It is used exclusively in calls to Reception.  See
* ITL_Receive().
*/
int increment(int c)
  {
  RecBuf[InternalCounter++] = c;
  if (InternalCounter > SECTSIZE+2)
    {
    killConnection();
    return FALSE;

    }
  return TRUE;

  }
/*
* ITL_SendMessages()
*
* This function sets up to send messages to the receiver.  It returns
* FALSE (and drops carrier) if it fails to get the transmission rolling.
* On success, it intializes for compaction if appropriate and returns
* TRUE.
*/
char ITL_SendMessages()
  {
  EncCount = 0l;
  if (!ITL_Send(STARTUP))
  return FALSE;
  ITLFunc = MsgOut;
  if (MsgOut == Encode) StartEncode(Table[ITL_protocol].method);
  return TRUE;

  }
/*
* ITL_StopMessages()
*
* This stops the transmission of messages to the receiver.
*/
void ITL_StopSendMessages()
  {
  if (MsgOut == Encode)
    {
    StopEncode();

    }
  if (gotCarrier())
  ITL_Send(FINISH);
  ITLFunc = Table[ITL_protocol].method;

  }
/*
* ITL_StartRecMsgs()
*
* This function receives messages from the other side.  Inputs:
*
* o FileNm - the filename to store the incoming messages in.
* o ReplyFirst - should we reply positively first?  If not, don't reply at
*   all.
* o OpenIt - do we need to open the file first?
* o OverRide - do we need to override the writing function?  If not, then
*   this is NULL.
*/
char ITL_StartRecMsgs(char *FileNm, char ReplyFirst, char OpenIt,
int (*OverRide)(int c))
  {
  char toReturn;
  int  CloseEncoded(FILE *f);
  int  (*CloseFn)(FILE *f) = fclose;
  if (OverRide == Encode) CloseFn = CloseEncoded;
  if (MsgIn == Decode)
    {
    StartDecode((OverRide == NULL) ? putFLChar : OverRide);
    OverRide = NULL;

    }
  toReturn = ITL_Receive(FileNm, ReplyFirst, OpenIt,
  (OverRide == NULL) ? MsgIn : OverRide, CloseFn);
  if (MsgIn == Decode) StopDecode();
  return toReturn;

  }
/*
* CloseEncoded()
*
* This stops encoding before closing a file, and is passed to ITL_Receive().
* If this is NOT used, then the flushing of the compaction buffers will not
* occur and data will be lost.
*/
int CloseEncoded(FILE *f)
  {
  StopEncode();
  return fclose(f);

  }
