/*	-*- C -*-
 * ifcico v3.0.cm - hydra protocol module
 * Copyright (C) 1996-98  Christof Meerwald.
 *
 * $RCSfile: hydra.c,v $ - $Author: cmeerw $
 * $Revision: 1.10 $ - $Date: 1998/07/05 20:41:21 $
 */

/*
 * The HYDRA protocol was designed by
 * Arjen G. Lentz, LENTZ SOFTWARE-DEVELOPMENT and
 * Joaquim H. Homrighausen
 * COPYRIGHT (C) 1991-1993; ALL RIGHTS RESERVED
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "lutil.h"
#include "xutil.h"
#include "ttyio.h"
#include "session.h"
#include "statetbl.h"
#include "config.h"
#include "emsi.h"
#include "hydra.h"

#define H_RXWINDOW	0L
#define H_TXWINDOW	0L

#define TIMERNO_BRAIN	0
#define TIMERNO_TX	1
#define TIMERNO_RX	2

extern time_t mtime2sl(time_t);
extern time_t sl2mtime(time_t);
extern int closefile(int);
extern FILE *openfile(char*,time_t,off_t,off_t*,int(*)(off_t));

extern unsigned short crc16ccitt(char *, int);
extern unsigned long crc32ccitt(char *, int);
extern file_list *respond_wazoo(char *);
extern file_list *create_freqlist(fa_list *al);


static int put_binbyte(char *outbuf, char c);
static int put_hexbyte(char *outbuf, char c);
static enum HyPktTypes hyrxpkt(char *rxbuf, int *rxlen, int tot);
static void hytxpkt(enum HyPktTypes pkttype, char *txbuf, int txlen);
static int put_flags(char *buf, unsigned long flags);
static unsigned long get_flags(char *buf);
static int resync(off_t off);
static int hydra_batch(int role, file_list *to_send);
int hydra(int role);



static struct h_flags_struct {
  char *str;
  unsigned long val;
} h_flags[] =
{
  { "XON", HOPT_XONXOFF },
  { "TLN", HOPT_TELENET },
  { "CTL", HOPT_CTLCHRS },
  { "HIC", HOPT_HIGHCTL },
  { "HI8", HOPT_HIGHBIT },
  { "BRK", HOPT_CANBRK  },
  { "ASC", HOPT_CANASC  },
  { "UUE", HOPT_CANUUE  },
  { "C32", HOPT_CRC32   },
  { "DEV", HOPT_DEVICE  },
  { "FPT", HOPT_FPT     },
  { NULL , 0x0L         }
};

static int txoptions, rxoptions;

extern char *version;



static char *put_long(char *buffer, long val)
{
#if defined(__i386__)
  *(unsigned long *) buffer = (unsigned long) val;
#else
  buffer[0] = (unsigned long) val & 0xff;
  buffer[1] = ((unsigned long) val >> 8) & 0xff;
  buffer[2] = ((unsigned long) val >> 16) & 0xff;
  buffer[3] = ((unsigned long) val >> 24) & 0xff;
#endif

  return buffer;
}


static long get_long(char *buffer)
{
#if defined(__i386__)
  return *(long *) buffer;
#else
  return ((unsigned long) ((unsigned char) buffer[0])) |
    ((unsigned long) ((unsigned char) buffer[1]) << 8) |
    ((unsigned long) ((unsigned char) buffer[2]) << 16) |
    ((unsigned long) ((unsigned char) buffer[3]) << 24);
#endif
}


int put_binbyte(char *outbuf, char c)
{
  static int lastc = -1;
  register int count = 0;
  register char n;


  n = c;
  if (txoptions & HOPT_HIGHCTL)
  {
    n &= 0x7f;
  }

  if ((n == H_DLE)
      || ((txoptions & HOPT_XONXOFF) && ((n == XON) || (n == XOFF)))
      || ((txoptions & HOPT_TELENET) && (n == '\r') && (lastc == '@'))
      || ((txoptions & HOPT_CTLCHRS) && ((n < 32) || (n == 127))))
  {
    *outbuf++ = H_DLE;
    c ^= 0x40;
    count++;
  }

  *outbuf++ = c;
  lastc = n;

  return count + 1;
}


int put_hexbyte(char *outbuf, char c)
{
  static const char hexdigit[] = "0123456789abcdef";
  register int count = 0;


  if (c & 0x80)
  {
    *outbuf++ = '\\';
    *outbuf++ = hexdigit[(c >> 4) & 0x0f];
    *outbuf++ = hexdigit[c & 0x0f];
    count = 3;
  }
  else if ((c < 32) || (c == 127))
  {
    *outbuf++ = H_DLE;
    *outbuf++ = c ^ 0x40;
    count = 2;
  }
  else if (c == '\\')
  {
    *outbuf++ = '\\';
    *outbuf++ = '\\';
    count = 2;
  }
  else
  {
    *outbuf++ = c;
    count = 1;
  }

  return count;
}


/* TODO: code cleanup */
/* TODO: error handling */
enum HyPktTypes hyrxpkt(char *rxbuf, int *rxlen, int tot)
{
  static char rxencbuf[H_BUFLEN];
  static enum HyPktTypes pkttype = H_NOPKT;
  static char *inbuf = rxencbuf, *outbuf;
  int c, i, n;
  static int rxdle = 0;
  static enum HyPktFormats format;


  while ((c = GETCHAR(tot)) >= 0)
  {
    if (rxoptions & HOPT_HIGHBIT)
    {
      c &= 0x7f;
    }

    n = c;
    if (rxoptions & HOPT_HIGHCTL)
    {
      n &= 0x7f;
    }
    if ((n != H_DLE)
	&& (((rxoptions & HOPT_XONXOFF) && ((n == XON) || (n == XOFF)))
	    || ((rxoptions & HOPT_CTLCHRS) && ((n < 32) || (n == 127)))))
      continue;


    if ((rxdle) || (c == H_DLE))
    {
      switch (c)
      {
       case H_DLE:
	rxdle++;
	if (rxdle >= 5)
	{
	  inbuf = rxencbuf;
	  return H_CANCEL;
	}
	break;

       case HCHR_PKTEND:
	switch (format)
	{
	 case HCHR_BINPKT:
	  *rxlen = inbuf - rxencbuf;
	  memcpy(rxbuf, rxencbuf, *rxlen);
	  break;

	 case HCHR_HEXPKT:
	  outbuf = rxencbuf;
	  *rxlen = 0;

	  while (outbuf < inbuf)
	  {
	    if ((*outbuf == '\\') && (*++outbuf != '\\'))
	    {
	      i = *outbuf++;
	      n = *outbuf++;

	      if ((i -= '0') > 9)
		i -= ('a' - ':');
	      if ((n -= '0') > 9)
		n -= ('a' - ':');

	      if ((i & ~0x0f) || (n & ~ 0x0f))
	      {
		assert(0);
		break;
	      }

	      rxbuf[*rxlen] = (i << 4) | n;
	      *rxlen += 1;
	    }
	    else
	    {
	      rxbuf[*rxlen] = *outbuf++;
	      *rxlen += 1;
	    }
	  }
	  break;

	 case HCHR_ASCPKT:
	 case HCHR_UUEPKT:
	 default:
	  assert(0);
	}

	if ((format != HCHR_HEXPKT) && (rxoptions & HOPT_CRC32))
	{
	  n = h_crc32test(crc32ccitt(rxbuf, *rxlen));
	  *rxlen -= 4;		/* remove CRC-32 */
	}
	else
	{
	  n = h_crc16test(crc16ccitt(rxbuf, *rxlen));
	  *rxlen -= 2;		/* remove CRC-16 */
	}

	*rxlen -= 1;		/* remove packet type */
	pkttype = rxbuf[*rxlen];

	/* check if CRC test succeeded */
	if (n)
	{
	  inbuf = rxencbuf;
	  debug(DBG_HYDRA, "HPKT received '%c'", pkttype);
	  return pkttype;
	}
	else
	{
	  /* debug(DBG_HYDRA, "CRC test failed"); */
	  loginf("Hydra: CRC test failed");
	}
	break;

       case HCHR_BINPKT:
       case HCHR_HEXPKT:
       case HCHR_ASCPKT:
       case HCHR_UUEPKT:
	format = c;
	inbuf = rxencbuf;
	rxdle = 0;
	break;

       default:
	*inbuf++ = c ^ 0x40;
	rxdle = 0;
	break;
      }
    }
    else
    {
      *inbuf++ = c;
    }
  }

  debug(DBG_HYDRA, "GETCHAR returned %i", c);

  if ((c == ERROR) || (c == EOFILE) || (c == HANGUP))
  {
    return H_CARRIER;
  }

  return H_NOPKT;
}


/* TODO: support packet prefix string */
void hytxpkt(enum HyPktTypes pkttype, char *txbuf, int txlen)
{
  static char txencbuf[H_BUFLEN];
  char *outbuf, *inbuf;
  enum HyPktFormats format;


  /*
   * some packets have to be transferred in HEX mode
   */
  if ((pkttype == HPKT_START) || (pkttype == HPKT_INIT)
      || (pkttype == HPKT_INITACK) || (pkttype == HPKT_END)
      || (pkttype == HPKT_IDLE))
  {
    format = HCHR_HEXPKT;
  }
  else
  {
    /* do we need to strip high bit */
    if (txoptions & HOPT_HIGHBIT)
    {
      if ((txoptions & HOPT_CTLCHRS) && (txoptions & HOPT_CANUUE))
      {
	format = HCHR_UUEPKT;		/* use UUE packet encoding */
      }
      else if (txoptions & HOPT_CANASC)
      {
	format = HCHR_ASCPKT;		/* we can use ASCII packet encoding */
      }
      else
      {
	format = HCHR_HEXPKT;		/* fall back to hex packet encoding */
      }
    }
    else
    {
      format = HCHR_BINPKT;		/* we can use binary packet encoding */
    }
  }

  /*
   * Append format byte to data
   */
  txbuf[txlen] = pkttype;
  txlen++;

  /*
   * check if we can use 32-bit CRC's
   */
  if ((format != HCHR_HEXPKT) && (txoptions & HOPT_CRC32))
  {
    unsigned long crc;

    /*
     * Calc CRC-32 of data + pkttype
     */
    crc = ~crc32ccitt(txbuf, txlen);

    /*
     * Append one's complement of CRC to data, lowbyte first
     */
    txbuf[txlen++] = crc;
    txbuf[txlen++] = crc >> 8;
    txbuf[txlen++] = crc >> 16;
    txbuf[txlen++] = crc >> 24;
  }
  else
  {
    unsigned short crc;

    /*
     * Calc CRC-16 of data + pkttype
     */
    crc = ~crc16ccitt(txbuf, txlen);

    /*
     * Append one's complement of CRC to data, lowbyte first
     */
    txbuf[txlen++] = crc;
    txbuf[txlen++] = crc >> 8;
  }

  inbuf = txbuf;

  outbuf = txencbuf;
  *outbuf++ = H_DLE;
  *outbuf++ = format;

  /* encode packet data */
  switch (format)
  {
   case HCHR_BINPKT:
    while (txlen > 0)
    {
      outbuf += put_binbyte(outbuf, *inbuf);

      inbuf++;
      txlen--;
    }
    break;

   case HCHR_HEXPKT:
    while (txlen > 0)
    {
      outbuf += put_hexbyte(outbuf, *inbuf);

      inbuf++;
      txlen--;
    }
    break;

    /* ASCII and UUE packets are not yet supported */
   case HCHR_ASCPKT:
   case HCHR_UUEPKT:
   default:
    assert(0);
  }

  *outbuf++ = H_DLE;
  *outbuf++ = HCHR_PKTEND;

  if ((pkttype != HPKT_DATA) && (format != HCHR_BINPKT))
  {
    *outbuf++ = '\r';
    *outbuf++ = '\n';
  }

  debug(DBG_HYDRA, "HPKT sent '%c'", pkttype);
  PUT(txencbuf, outbuf - txencbuf);


  return;
}


int put_flags(char *buf, unsigned long flags)
{
  int i, count = 0;

  for (i = 0; h_flags[i].val; i++)
  {
    if (flags & h_flags[i].val)
    {
      if (count > 0)
      {
	*buf++ = ',';
	count++;
      }

      strcpy(buf, h_flags[i].str);
      buf += H_FLAGLEN;
      count += H_FLAGLEN;
    }
  }

  *buf = 0;

  return count;
}


unsigned long get_flags(char *buf)
{
  unsigned long flags = 0L;
  char *p;
  int i;


  for (p = strtok(buf, ","); p != NULL; p = strtok(NULL, ","))
  {
    for (i = 0; h_flags[i].val; i++)
    {
      if (!strcmp(p, h_flags[i].str))
      {
	flags |= h_flags[i].val;
	break;
      }
    }
  }

  return flags;
}


int resync(off_t off)
{
  return 0;
}


int hydra_batch(int role, file_list *to_send)
{
  static char txbuf[H_BUFLEN], rxbuf[H_BUFLEN];
  struct stat txstat;		/* file stat being transmitted */
  FILE *txfp = NULL;		/* file currently being transmitted */
  FILE *rxfp = NULL;		/* file currently being received */
  char *inbuf, *outbuf;
  int rxlen, txlen;		/* length of receive/transmit buffer */
  long txwindow, rxwindow;	/* window sizes */
  long txpos, rxpos;		/* file positions */
  long stxpos, srxpos;
  long longnum;
  int hdxlink = 0;
  int txretries, rxretries;
  int txlastack, txsyncid;
  int rxlastsync, rxsyncid;
  int rxlastdatalen;
  int blksize;
  int goodbytes;
  int goodneeded;
  enum HyTxStates txstate;
  enum HyRxStates rxstate;
  int txwaitpkt, rxwaitpkt;
  enum HyPktTypes pkttype;
  int waitputget = 0;
  time_t rxstarttime, rxendtime;
  time_t txstarttime, txendtime;


  RESETTIMERS();

  txpos = rxpos = 0;
  stxpos = srxpos = 0;
  txretries = rxretries = 0;
  txlastack = txsyncid = 0;
  rxlastsync = rxsyncid = 0;
  rxlastdatalen = 0;
  blksize = 512;
  goodbytes = 0;
  goodneeded = 1024;
  SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);
  txstate = HTX_START;
  txoptions = HTXI_OPTIONS;

  rxstate = HRX_INIT;
  rxoptions = HRXI_OPTIONS;

  while ((txstate != HTX_DONE) && (txstate != HTX_Abort))
  {
    /*
     * Is transmitter waiting for packet? 
     */
    txwaitpkt = ((txstate == HTX_SWAIT) || (txstate == HTX_INITACK)
		 || ((txstate == HTX_RINIT) && (rxstate == HRX_INIT))
		 || (txstate == HTX_FINFOACK) || (txstate == HTX_DATA)
		 || (txstate == HTX_DATAACK)
		 || ((txstate == HTX_XWAIT) && (rxstate != HRX_DONE))
		 || (txstate == HTX_EOFACK) || (txstate == HTX_ENDACK)
		 || (txstate == HTX_REND));

    /*
     * Is receiver waiting for packet?
     */
    rxwaitpkt = ((rxstate == HRX_INIT) || (rxstate == HRX_FINFO) ||
		 (rxstate == HRX_DATA) || (rxstate == HRX_DONE));

    /*
     * Do we have to wait for a packet?
     */
    if (txwaitpkt && rxwaitpkt)
    {
      /*
       * Don't wait for a packet if transmitter is in DATA state
       */
      if ((txstate == HTX_DATA) || ((txstate == HTX_REND) && (rxstate == HRX_DONE)))
      {
	if (txstate == HTX_DATA)
	{
	  waitputget = WAITPUTGET(-1);
	  if (waitputget & 1)
	  {
	    pkttype = hyrxpkt(rxbuf, &rxlen, 0);
	  }
	  else
	  {
	    pkttype = H_NOPKT;
	  }
	}
	else
	{
	  pkttype = hyrxpkt(rxbuf, &rxlen, 0);
	}
      }
      else
      {
	pkttype = hyrxpkt(rxbuf, &rxlen, -1);
      }

      if ((pkttype == H_CARRIER) || (EXPIRED(TIMERNO_BRAIN)))
      {
	txstate = HTX_Abort;
	break;
      }
    }
    else
    {
      pkttype = H_NOPKT;
    }

    debug(DBG_HYDRA, "Packet type: %c", pkttype);

    /*
     * Special handling for RPOS packet
     */
    if ((pkttype == HPKT_RPOS) && (rxlen == 12)
	&& ((txstate == HTX_DATA) || (txstate == HTX_DATAACK)
	    || (txstate == HTX_XWAIT) || (txstate == HTX_EOFACK)))
    {
      long rpos_pos = get_long(rxbuf);
      long rpos_blksize = get_long(rxbuf + 4);
      long rpos_id = get_long(rxbuf + 8);

      if (rpos_pos < 0)
      {
	/*
	 * this differs from the protocol definition: txpos is used
	 * instead of rxpos (I think it's wrong in the protocol
	 * definition as in the example source also txpos is used)
	 */
	if ((rpos_pos == -2) && (txpos != -2) && (txstate == HTX_EOFACK))
	{
	  txpos = -2;
	  txstate = HTX_EOF;
	}
	else
	{
	  SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);
	  txstate = HTX_SkipFile;
	}
      }

      if (rpos_id == txsyncid)
      {
	txretries++;

	if (txretries >= 10)
	{
	  loginf("Hydra: too many errors");
	  txstate = HTX_Abort;
	  break;
	}
      }
      else
      {
	if (rpos_pos >= 0)
	{
	  txpos = rpos_pos;
	  txsyncid = rpos_id;
	  txretries = 1;
	  blksize = rpos_blksize;
	  goodbytes = 0;
	  goodneeded += 1024;
	  if (goodneeded > 8192)
	  {
	    goodneeded = 8192;
	  }

	  /* if we receive an RPOS packet in EOFACK-state we have to
             change back to DATA-state */
	  if (txstate == HTX_EOFACK)
	  {
	    txstate = HTX_DATA;
	  }

	  SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);
	}
      }

      pkttype = H_NOPKT;		/* packet has already been processed */
    }

    switch (txstate)
    {
      /*
       * initiate Hydra session
       */
     case HTX_START: debug(DBG_HYDRA, "SM 'HTX' entering 'START'");
      if (txretries < 10)
      {
	PUT("hydra\r", 6);		/* send AutoStart string */
	hytxpkt(HPKT_START, txbuf, 0);
	SETTIMER(TIMERNO_TX, H_START);
	txstate = HTX_SWAIT;
      }
      else
      {
	loginf("unable to initiate Hydra session");
	txstate = HTX_Abort;
	break;
      }
      break;

      /*
       * wait for START packet
       */
     case HTX_SWAIT: debug(DBG_HYDRA, "SM 'HTX' entering 'SWAIT'");
      if (((pkttype == HPKT_START) && (rxlen == 0))
	  || (pkttype == HPKT_INIT))
      {
	txretries = 0;
	RESETTIMER(TIMERNO_TX);
	SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);
	txstate = HTX_INIT;

	pkttype = H_NOPKT;		/* packet has already been processed */
      }	
      else if (EXPIRED(TIMERNO_TX))
      {
	loginf("Hydra transmitter timeout (HTX_SWAIT)");
	txretries++;
	txstate = HTX_START;
      }
      break;

      /*
       * send INIT packet
       */
     case HTX_INIT: debug(DBG_HYDRA, "SM 'HTX' entering 'INIT'");
      if (txretries < 10)
      {
	outbuf = txbuf;

	/* Application ID string */
	outbuf += sprintf(outbuf, "%08lx%s,%s", H_REVSTAMP, "ifcico",
			  version) + 1;

	/* Supported options */
	outbuf += put_flags(outbuf, HCAN_OPTIONS) + 1;
	debug(DBG_HYDRA, "supported options: %08lx", HCAN_OPTIONS);

	/* Desired options */
	outbuf += put_flags(outbuf, HDEF_OPTIONS & HCAN_OPTIONS & ~HUNN_OPTIONS) + 1;
	debug(DBG_HYDRA, "desired options: %08lx", HDEF_OPTIONS & HCAN_OPTIONS & ~HUNN_OPTIONS);

	/* Desired transmitter and receiver window size */
	outbuf += sprintf(outbuf, "%08lx%08lx", H_TXWINDOW, H_RXWINDOW) + 1;

	/* Packet prefix string */
	*outbuf++ = 0;

	hytxpkt(HPKT_INIT, txbuf, outbuf - txbuf);

	SETTIMER(TIMERNO_TX, H_MINTIMER/2);
	txstate = HTX_INITACK;
      }
      else
      {
	loginf("Hydra: too many errors waiting for INITACK");
	txstate = HTX_Abort;
	break;
      }
      break;

      /*
       * wait for an INIT acknowledge packet
       */
     case HTX_INITACK: debug(DBG_HYDRA, "SM 'HTX' entering 'INITACK'");
      if ((pkttype == HPKT_INITACK) && (rxlen == 0))
      {
	txretries = 0;
	RESETTIMER(TIMERNO_TX);
	SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);
	txstate = HTX_RINIT;

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if (EXPIRED(TIMERNO_TX))
      {
	loginf("Hydra: tx timeout");
	txretries++;
	txstate = HTX_INIT;
      }
      break;

      /*
       * wait for receiver to leave INIT state
       */
     case HTX_RINIT: debug(DBG_HYDRA, "SM 'HTX' entering 'RINIT'");
      if (rxstate != HRX_INIT)
      {
	txstate = HTX_NextFile;
      }
      break;

      /*
       * prepare next file for transmitting
       */
     case HTX_NextFile: debug(DBG_HYDRA, "SM 'HTX' entering 'NextFile'");
      /*
       * skip file with NULL remote name
       */
      while (to_send && (to_send->remote == NULL))
      {
	execute_disposition(to_send);
	to_send = to_send->next;
      }

      if (to_send)
      {
	struct flock txflock;

	if (to_send->remote == NULL)
	  break;

	txflock.l_type=F_RDLCK;
        txflock.l_whence=0;
        txflock.l_start=0L;
        txflock.l_len=0L;

	txfp = fopen(to_send->local, "r");
	if (txfp == NULL)
	{
	  logerr("$hydra_batch cannot open file %s, skipping", S(to_send->local));
	  to_send = to_send->next;
	  break;
	}

	if (fcntl(fileno(txfp), F_SETLK, &txflock) != 0)
	{
	  loginf("$hydra_batch cannot lock file %s, skipping", S(to_send->local));
	  fclose(txfp);
	  to_send = to_send->next;
	  break;
	}

	if (stat(to_send->local, &txstat) != 0)
	{
	  loginf("$cannot access \"%s\", skipping",S(to_send->local));
	  fclose(txfp);
	  to_send = to_send->next;
	  break;
	}

	loginf("Hydra send \"%s\" as \"%s\" (%lu bytes) dated %s",
	       S(to_send->local), S(to_send->remote),
	       (unsigned long) txstat.st_size, date(txstat.st_mtime));
	(void) time(&txstarttime);
      }

      txstate = HTX_ToFName;
      break;			/* TODO: fallthrough */

     case HTX_ToFName: debug(DBG_HYDRA, "SM 'HTX' entering 'ToFName'");
      txsyncid = 0;
      txretries = 0;
      RESETTIMER(TIMERNO_TX);
      txstate = HTX_FINFO;
      break;			/* TODO: fallthrough */

      /*
       * transmit File Information packet
       */
     case HTX_FINFO: debug(DBG_HYDRA, "SM 'HTX' entering 'FINFO'");
      if (txretries >= 10)
      {
	txstate = HTX_Abort;
	break;
      }
      else
      {
	if (to_send)
	{
	  txlen = sprintf(txbuf, "%08lx%08lx%08lx%08lx%08lx",
			  mtime2sl(txstat.st_mtime),
			  txstat.st_size,
			  0UL,
			  0UL,
			  0UL);

	  /*
	   * convert file name to DOS-format
	   */
	  outbuf = xtodos(to_send->remote);
	  strcpy(txbuf + txlen, outbuf);
	  free(outbuf);
	  for(; txbuf[txlen]; txlen++)
	  {
	    txbuf[txlen] = tolower(txbuf[txlen]);
	  }
	  txlen++;

	  strcpy(txbuf + txlen, to_send->remote);
	  txlen += strlen(to_send->remote) + 1;
	}
	else
	{
	  txbuf[0] = 0;
	  txlen = 1;
	}

	hytxpkt(HPKT_FINFO, txbuf, txlen);

	if (txretries > 0)
	{
	  SETTIMER(TIMERNO_TX, H_MINTIMER/2);
	}
	else
	{
	  SETTIMER(TIMERNO_TX, H_MINTIMER);
	}

	txstate = HTX_FINFOACK;
      }
      break;

      /*
       * get FINFOACK packet
       */
     case HTX_FINFOACK: debug(DBG_HYDRA, "SM 'HTX' entering 'FINFOACK'");
      if ((pkttype == HPKT_FINFOACK) && (rxlen == 4))
      {
	txpos = get_long(rxbuf);
	SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);

	if (to_send == NULL)
	{
	  SETTIMER(TIMERNO_TX, H_MINTIMER);
	  txstate = HTX_REND;
	}
	else if (txpos >= 0L)
	{
	  stxpos = txpos;
	  txretries = 0;
	  txlastack = 0;
	  RESETTIMER(TIMERNO_TX);
	  txstate = HTX_DATA;
	}
	else if (txpos == -1)
	{
	  fclose(txfp);
	  execute_disposition(to_send);
	  to_send = to_send->next;
	  txstate = HTX_NextFile;
	}
	else if (txpos == -2)
	{
	  loginf("Receiver requested to skip this file for now");
	  fclose(txfp);
	  to_send = to_send->next;
	  txstate = HTX_NextFile;
	}

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if (EXPIRED(TIMERNO_TX))
      {
	loginf("Hydra transmitter timeout (HTX_FINFOACK)");
	txretries++;
	txstate = HTX_FINFO;
      }
      break;

     case HTX_DATA: debug(DBG_HYDRA, "SM 'HTX' entering 'DATA'");
      if ((rxstate != HRX_DONE) && (hdxlink))
      {
	SETTIMER(TIMERNO_TX, H_MINTIMER);
	txstate = HTX_XWAIT;
      }
      else if ((pkttype == HPKT_DATAACK) && (rxlen == 4))
      {
	longnum = get_long(rxbuf);
	debug(DBG_HYDRA, "received 'DATAACK' (0x%08lx)", longnum);

	if (longnum > txlastack)
	{
	  txlastack = longnum;
	}

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if ((txwindow) && (txpos >= txlastack + txwindow))
      {
	/*
	 * We have to wait for an ACK before we can continue
	 */
	if (txretries > 0)
	{
	  SETTIMER(TIMERNO_TX, H_MINTIMER/2);
	}
	else
	{
	  SETTIMER(TIMERNO_TX, H_MINTIMER);
	}
	txstate = HTX_DATAACK;
      }
      else
      {
	/*
	 * check if there is enough room in output
	 */
	if ((waitputget & 2) == 0)
	{
	  break;
	}

	fseek(txfp, txpos, SEEK_SET);
	put_long(txbuf, txpos);

	debug(DBG_HYDRA, "send 'DATA' (0x%08lx)", txpos);

	txlen = fread(txbuf + 4, 1, blksize, txfp);
	if (txlen == 0)
	{
	  if (ferror(txfp))
	  {
	    logerr("$error reading from file");
	    txstate = HTX_SkipFile;
	  }
	  else
	  {
	    txstate = HTX_EOF;
	  }
	}
	else
	{
	  txpos += txlen;
	  goodbytes += txlen;
	  txlen += 4;
	  hytxpkt(HPKT_DATA, txbuf, txlen);

	  if (goodbytes > goodneeded)
	  {
	    blksize *= 2;
	    if (blksize > H_MAXBLKLEN)
	    {
	      blksize = H_MAXBLKLEN;
	    }
	  }
	}
      }
      break;

     case HTX_SkipFile: debug(DBG_HYDRA, "SM 'HTX' entering 'SkipFile'");
      /*
       * this differs from the protocol definition: -2 is used
       * instead of -1 (I think it's wrong in the protocol
       * definition as in the example source also -2 is used)
       */
      txpos = -2;
      txretries = 0;
      txstate = HTX_EOF;
      break;

     case HTX_DATAACK: debug(DBG_HYDRA, "SM 'HTX' entering 'DATAACK'");
      if ((pkttype == HPKT_DATAACK) && (rxlen == 4))
      {
	longnum = get_long(rxbuf);

	if ((longnum > txlastack) && (txpos < longnum + txwindow))
	{
	  txlastack = longnum;
	  txretries = 0;
	  RESETTIMER(TIMERNO_TX);
	  txstate = HTX_DATA;
	}

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if (txretries >= 10)
      {
	loginf("Hydra: too many errors");
	txstate = HTX_Abort;
      }
      else if (EXPIRED(TIMERNO_TX))
      {
	loginf("Hydra: tx timeout");
	txretries++;
	txstate = HTX_DATA;
      }
      break;

     case HTX_XWAIT: debug(DBG_HYDRA, "SM 'HTX' entering 'XWAIT'");
      if (rxstate == HRX_DONE)
      {
	RESETTIMER(TIMERNO_RX);
	txstate = HTX_DATA;
      }
      else if ((pkttype == HPKT_DATAACK) && (rxlen == 4))
      {
	longnum = get_long(rxbuf);
	if (longnum > txlastack)
	{
	  txlastack = longnum;
	}

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if ((pkttype == HPKT_IDLE) && (rxlen == 0))
      {
	hdxlink = 0;
	RESETTIMER(TIMERNO_TX);
	txstate = HTX_DATA;

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if (EXPIRED(TIMERNO_TX))
      {
	hytxpkt(HPKT_IDLE, txbuf, 0);
	SETTIMER(TIMERNO_TX, H_MINTIMER);
      }
      break;

     case HTX_EOF: debug(DBG_HYDRA, "SM 'HTX' entering 'EOF'");
      if (txretries >= 10)
      {
	txstate = HTX_Abort;
	break;
      }
      else
      {
	put_long(txbuf, txpos);
	hytxpkt(HPKT_EOF, txbuf, 4);

	if (txretries > 0)
	{
	  SETTIMER(TIMERNO_TX, H_MINTIMER/2);
	}
	else
	{
	  SETTIMER(TIMERNO_TX, H_MINTIMER);
	}

	txstate = HTX_EOFACK;
      }
      break;

     case HTX_EOFACK: debug(DBG_HYDRA, "SM 'HTX' entering 'EOFACK'");
      if ((pkttype == HPKT_EOFACK) && (rxlen == 0))
      {
	SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);

	/*
	 * calculate time needed and bytes transferred
	 */
	(void) time(&txendtime);
	txstarttime = txendtime - txstarttime;

	if (txstarttime <= 0L)
	{
	  txstarttime = 1L;
	}

	/* close transmitter file */
	fclose(txfp);

	if (txpos >= 0)
	{
	  stxpos = txpos - stxpos;

	  loginf("sent %lu bytes in %ld seconds (%ld cps)",
		 stxpos, txstarttime, stxpos/txstarttime);
	  execute_disposition(to_send);
	}
	else
	{
	  loginf("transmitter skipped file after %ld seconds", txstarttime);
	}

	to_send = to_send->next;
	txstate = HTX_NextFile;

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if ((pkttype == HPKT_DATAACK) && (rxlen == 4))
      {
	longnum = get_long(rxbuf);
	txlastack = longnum;

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if (EXPIRED(TIMERNO_TX))
      {
	loginf("Hydra transmitter timeout (HTX_EOFACK)");
	txretries++;
	txstate = HTX_EOF;
      }
      break;

     case HTX_REND: debug(DBG_HYDRA, "SM 'HTX' entering 'REND'");
      if (rxstate == HRX_DONE)
      {
	txretries = 0;
	txstate = HTX_END;
      }
      else if (EXPIRED(TIMERNO_TX))
      {
	hytxpkt(HPKT_IDLE, txbuf, 0);
	SETTIMER(TIMERNO_TX, H_MINTIMER/2);
      }
      break;

     case HTX_END: debug(DBG_HYDRA, "SM 'HTX' entering 'END'");
      if (txretries >= 10)
      {
	txstate = HTX_Abort;
	break;
      }
      else
      {
	hytxpkt(HPKT_END, txbuf, 0);
	hytxpkt(HPKT_END, txbuf, 0);
	SETTIMER(TIMERNO_TX, H_MINTIMER/2);
	txstate = HTX_ENDACK;
      }
      break;

     case HTX_ENDACK: debug(DBG_HYDRA, "SM 'HTX' entering 'ENDACK'");
      if ((pkttype == HPKT_END) && (rxlen == 0))
      {
	hytxpkt(HPKT_END, txbuf, 0);
	hytxpkt(HPKT_END, txbuf, 0);
	hytxpkt(HPKT_END, txbuf, 0);
	txstate = HTX_DONE;

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if (EXPIRED(TIMERNO_TX))
      {
	loginf("Hydra transmitter timeout (HTX_ENDACK)");
	txretries++;
	txstate = HTX_END;
      }
      break;

     case HTX_DONE:
     case HTX_Abort:
      /* nothing to do */
      break;

     default:
      assert(0);
    }

    switch (rxstate)
    {
     case HRX_INIT: debug(DBG_HYDRA, "SM 'HRX' entering 'INIT'");
      if (pkttype == HPKT_INIT)
      {
	/* TODO: some checking, error handling */

	/* Skip application ID */
	inbuf = rxbuf + strlen(rxbuf) + 1;

	inbuf += strlen(inbuf) + 1;

	rxoptions = (HDEF_OPTIONS & HCAN_OPTIONS) | HUNN_OPTIONS;

	/*
	 * get supported options
	 */
	rxoptions |= get_flags(inbuf);
	inbuf += strlen(inbuf) + 1;

	/*
	 * get desired options
	 */
	rxoptions &= get_flags(rxbuf + strlen(rxbuf) + 1);
	rxoptions &= HCAN_OPTIONS;

	/*
	 * set options
	 */
	txoptions = rxoptions;
	put_flags(txbuf, rxoptions);
	debug(DBG_HYDRA, "HYDRA options: %s (%08lx)", txbuf, rxoptions);

	/*
	 * get desired window sizes
	 */
	txwindow = rxwindow = 0;
	sscanf(inbuf, "%08lx%08lx", &rxwindow, &txwindow);

	if (rxwindow < 0)
	{
	  rxwindow = 0;
	}

	if (H_RXWINDOW && ((rxwindow == 0) || (rxwindow > H_RXWINDOW)))
	{
	  rxwindow = H_RXWINDOW;
	}

	if (txwindow < 0)
	{
	  txwindow = 0;
	}

	if (H_TXWINDOW && ((txwindow == 0) || (txwindow > H_TXWINDOW)))
	{
	  txwindow = H_TXWINDOW;
	}

	debug(DBG_HYDRA, "HYDRA: txwindow=%d, rxwindow=%d", txwindow, rxwindow);

	hytxpkt(HPKT_INITACK, txbuf, 0);
	SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);
	rxstate = HRX_FINFO;

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      break;

     case HRX_FINFO: debug(DBG_HYDRA, "SM 'HRX' entering 'FINFO'");
      if (pkttype == HPKT_FINFO)
      {
	/*
	 * check if we have received an `End of batch' FINFO packet
	 */
	if ((rxlen == 1) && (rxbuf[0] == 0))
	{
	  put_long(txbuf, 0);
	  hytxpkt(HPKT_FINFOACK, txbuf, 4);

	  SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);
	  rxstate = HRX_DONE;
	}
	/*
	 * check if FINFO packet is at least long enough to contain
	 * file information
	 */
	else if ((rxlen > 41) && (rxbuf[rxlen - 1] == 0))
	{
	  time_t timestamp;
	  long filesize;
	  char dosname[8 + 1 + 3 + 1], *name;


	  sscanf(rxbuf, "%08lx%08lx%*08x%*08x%*08x",
		 &timestamp, &filesize);

	  /* convert timestamp to UNIX time */
	  timestamp = sl2mtime(timestamp);

	  /*
	   * check if DOS conforming file name is max. 8+1+3 chars long
	   */
	  if (strlen(rxbuf + 40) <= 12)
	  {
	    strcpy(dosname, rxbuf + 40);
	  }
	  else
	  {
	    strcpy(dosname, "BadWazoo");
	  }

	  /*
	   * check if real file name is specified
	   */
	  if ((strlen(rxbuf + 40) + 41) < rxlen)
	  {
	    name = rxbuf + strlen(rxbuf + 40) + 41;

	    /*
	     * use DOS-filename if real- and DOS-name only differ in
	     * case sensitivity
	     */
	    if (strcasecmp(name, dosname) == 0)
	    {
	      name = dosname;
	    }
	  }
	  else
	  {
	    name = dosname;
	  }

	  loginf("Hydra receive: \"%s\" (%ld bytes) dated %s",
		 name, filesize, date(timestamp));

	  rxfp = openfile(name, timestamp, filesize, &rxpos, resync);
	  (void) time(&rxstarttime);

	  /* check for error opening file */
	  if (rxfp)
	  {
	    srxpos = rxpos;
	    rxstate = HRX_ToData;
	  }
	  else
	  {
	    /* Skip this file for now if error opening file */
	    put_long(txbuf, -2);
	    hytxpkt(HPKT_FINFOACK, txbuf, 4);

	    SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);
	  }
	}

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if (pkttype == HPKT_INIT)
      {
	hytxpkt(HPKT_INITACK, txbuf, 0);

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if ((pkttype == HPKT_EOF) && (rxlen == 4))
      {
	hytxpkt(HPKT_EOFACK, txbuf, 0);

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      break;

     case HRX_ToData: debug(DBG_HYDRA, "SM 'HRX' entering 'ToData'");
      put_long(txbuf, rxpos);
      hytxpkt(HPKT_FINFOACK, txbuf, 4);

      rxsyncid = 0;
      rxlastsync = 0;
      rxretries = 0;
      RESETTIMER(TIMERNO_RX);
      SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);
      rxstate = HRX_DATA;
      break;

     case HRX_DATA: debug(DBG_HYDRA, "SM 'HRX' entering 'DATA'");
      if ((pkttype == HPKT_DATA) && (rxlen > 4))
      {
	longnum = get_long(rxbuf);
	debug(DBG_HYDRA, "received 'DATA' (0x%08lx, 0x%08lx)", longnum, rxpos);

	if (longnum == rxpos)
	{
	  if (fwrite(rxbuf + 4, 1, rxlen - 4, rxfp) != (rxlen - 4))
	  {
	    logerr("$error writing to file");
	    rxpos = -2;
	  }
	  else
	  {
	    rxlastdatalen = rxlen - 4;
	    rxpos += rxlen - 4;
	    rxretries = 0;
	    rxlastsync = rxpos;
	    RESETTIMER(TIMERNO_RX);
	    SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);
	    if (rxwindow)
	    {
	      put_long(txbuf, rxpos);
	      hytxpkt(HPKT_DATAACK, txbuf, 4);
	    }
	  }
	}
	else
	{
	  if (rxpos >= 0)
	  {
	    loginf("Hydra received bad rxpos");
	  }
	  rxstate = HRX_BadPos;
	}

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if ((pkttype == HPKT_EOF) && (rxlen == 4))
      {
	longnum = get_long(rxbuf);

	if (longnum == rxpos)
	{
	  /*
	   * calculate time and CPU usage needed
	   */
	  (void) time(&rxendtime);

	  if (rxpos >= 0)
	  {
	    rxfp = NULL;
	    if (!closefile(1))
	    {
	      srxpos = rxpos - srxpos;

	      rxstarttime = rxendtime - rxstarttime;
	      if (rxstarttime <= 0)
	      {
		rxstarttime = 1L;
	      }

	      loginf("received %lu bytes in %ld seconds (%ld cps)",
		     srxpos, rxstarttime, srxpos/rxstarttime);

	      rxstate = HRX_OkEOF;
	    }
	    else		/* error closing file */
	    {
	      rxpos = -2;

	      /*
	       * Note: the following state change isn't 100 %
	       * conformant with the Hydra protocol specification.
	       */
	      rxstate = HRX_BadPos;
	    }
	  }
	  else
	  {
	    loginf("receiver skipped file after %ld seconds",
		   rxendtime - rxstarttime);

	    if (rxfp)
	    {
	      closefile(0);
	      rxfp = NULL;
	    }

	    rxstate = HRX_OkEOF;
	  }
	}
	else if (longnum == -2)
	{
	  if (rxfp)
	  {
	    closefile(0);
	    rxfp = NULL;
	  }

	  rxstate = HRX_OkEOF;
	}
	else
	{
	  if (longnum >= 0)
	  {
	    loginf("Hydra received bad rxpos");
	  }
	  rxstate = HRX_BadPos;		/* TODO: fallthrough */
	}

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if (pkttype == HPKT_FINFO)
      {
	put_long(txbuf, rxpos);
	hytxpkt(HPKT_FINFOACK, txbuf, 4);

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if ((pkttype == HPKT_IDLE) && (rxlen == 0))
      {
	SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);

	pkttype = H_NOPKT;		/* packet has already been processed */
      }
      else if (EXPIRED(TIMERNO_RX))
      {					/* TODO: check this hack */
	rxstate = HRX_HdxLink;
      }
      break;

     case HRX_BadPos: debug(DBG_HYDRA, "SM 'HRX' entering 'BadPos'");
      longnum = get_long(rxbuf);

      if (longnum <= rxlastsync)
      {
	rxretries = 0;
	RESETTIMER(TIMERNO_RX);
	rxlastsync = longnum;
	rxstate = HRX_Timer;
      }
      else
      {
	rxlastsync = longnum;
	rxstate = HRX_Timer;
      }
      break;

     case HRX_Timer: debug(DBG_HYDRA, "SM 'HRX' entering 'Timer'");
      if ((!RUNNING(TIMERNO_RX)) || (EXPIRED(TIMERNO_RX)))
      {
	rxstate = HRX_HdxLink;
      }
      else
      {
	rxstate = HRX_DATA;
      }
      break;

     case HRX_HdxLink: debug(DBG_HYDRA, "SM 'HRX' entering 'HdxLink'");
      /* TODO: hdxlink */
      if ((rxretries > 4) && (txstate != HTX_REND) && (role == 0))
      {
	rxretries = 0;
	rxstate = HRX_Retries;
      }
      else
      {
	rxstate = HRX_Retries;
      }
      break;		/* TODO: fallthrough */

     case HRX_Retries: debug(DBG_HYDRA, "SM 'HRX' entering 'Retries'");
      rxretries++;
      if (rxretries >= 10)
      {
	loginf("Hydra: too many errors");
	txstate = HTX_Abort;
      }
      else if (rxretries == 1)
      {
	rxsyncid++;
	rxstate = HRX_RPos;
      }
      else
      {
	rxstate = HRX_RPos;
      }
      break;		/* TODO: fallthrough */

     case HRX_RPos: debug(DBG_HYDRA, "SM 'HRX' entering 'RPos'");
      rxlastdatalen /= 2;
      if (rxlastdatalen < 64)
      {
	rxlastdatalen = 64;
      }

      put_long(txbuf, rxpos);
      put_long(txbuf + 4, rxlastdatalen);
      put_long(txbuf + 8, rxsyncid);

      hytxpkt(HPKT_RPOS, txbuf, 12);
      SETTIMER(TIMERNO_RX, H_MINTIMER);
      rxstate = HRX_DATA;
      break;

     case HRX_OkEOF: debug(DBG_HYDRA, "SM 'HRX' entering 'OkEOF'");
      hytxpkt(HPKT_EOFACK, txbuf, 0);
      RESETTIMER(TIMERNO_RX);
      SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);
      rxstate = HRX_FINFO;
      break;

     case HRX_DONE: debug(DBG_HYDRA, "SM 'HRX' entering 'DONE'");
      if (pkttype == HPKT_FINFO)
      {
	put_long(txbuf, -2);
	hytxpkt(HPKT_FINFOACK, txbuf, 4);

	pkttype = H_NOPKT;		/* packet has already been
					   processed */
      }
      else if ((pkttype == HPKT_IDLE) && (rxlen == 0))
      {
	SETTIMER(TIMERNO_BRAIN, H_BRAINDEAD);

	pkttype = H_NOPKT;		/* packet has already been
					   processed */
      }
      break;
    }

    if (pkttype != H_NOPKT)
    {
      debug(DBG_HYDRA, "Hydra received packet `%c' - ignored", pkttype);

      pkttype = H_NOPKT;		/* ignore received packet */
    }
  }

  RESETTIMERS();

  if (txstate == HTX_Abort)
  {
    /* check if file is still open */
    /*
    if ((rxstate == HRX_ToData) || (rxstate == HRX_DATA) ||
	(rxstate == HRX_BadPos) || (rxstate == HRX_Timer) ||
	(rxstate == HRX_HdxLink) || (rxstate == HRX_Retries) ||
	(rxstate == HRX_RPos)) */
    if (rxfp)
    {
      rxfp = NULL;
      closefile(0);
    }

    FLUSHOUT();
    PUT("\030\030\030\030\030\030\030\030\010\010\010\010\010\010\010\010\010\010", 18);
    sleep(2);				/* wait a few seconds... */
    FLUSHIN();

    return 2;
  }


  return 0;
}


int hydra(int role)
{
  int rc;
  fa_list *eff_remote, tmpl;
  file_list *tosend = NULL, *request = NULL, *respond = NULL, *tmpfl;
  char *nonhold_mail;


  debug(DBG_HANDSHAKE, "start hydra transfer");
  session_flags |= SESSION_HYDRA;		/* Hydra special file
						   requests */

  if (emsi_remote_lcodes & LCODE_NPU)
  {
    loginf("remote requested \"no pickup\", no send");
    eff_remote = NULL;
  }
  else if (emsi_remote_lcodes & LCODE_PUP)
  {
    loginf("remote requested \"pickup primary\"");
    tmpl.addr = remote->addr;
    tmpl.next = NULL;
    eff_remote = &tmpl;
  }
  else
  {
    eff_remote = remote;
  }

  if (role)
  {
    if (localoptions & NOHOLD)
      nonhold_mail = ALL_MAIL;
    else
      nonhold_mail = NONHOLD_MAIL;
  }
  else
  {
    nonhold_mail = ALL_MAIL;
  }

  if (emsi_remote_lcodes & LCODE_HAT)
  {
    loginf("remote asked to \"hold all traffic\", no send");
    tosend = NULL;
  }
  else
  {
    tosend = create_filelist(eff_remote, nonhold_mail, 0);
  }

  if (session_flags & SESSION_WAZOO)
    request = create_freqlist(remote);


  /*
   * Send only file requests during first batch if remote supports
   * "RH1" flag.
   */
  if (emsi_remote_lcodes & LCODE_RH1)
  {
    rc = hydra_batch(role, request);
  }
  else
  {
    if (request != NULL)
    {
      tmpfl = tosend;
      tosend = request;
      for (; request->next; request = request->next);
      request->next = tmpfl;

      request = NULL;
    }    

    rc = hydra_batch(role, tosend);
  }

  if (rc == 0)
  {
    if ((emsi_local_opts & OPT_NRQ) == 0)
      respond = respond_wazoo(NULL);

    if (emsi_remote_lcodes & LCODE_RH1)
    {
      for (tmpfl = tosend; tmpfl->next; tmpfl = tmpfl->next);
      tmpfl->next = respond;

      rc = hydra_batch(role, tosend);

      tmpfl->next = NULL;		/* split filelist into tosend
					   and respond again */
    }
    else
    {
      rc = hydra_batch(role, respond);
    }
  }

  tidy_filelist(request, (rc == 0));
  tidy_filelist(tosend, (rc == 0));
  tidy_filelist(respond, 0);


  debug(DBG_HANDSHAKE, "end hydra transfer");

  return rc;
}
