#include <stdlib.h>

#include "sshdef.h"
#include "ssh.h"
#include "sshstrc.h"
#include "def.h"
#include "defstruc.h"
#include "syslogif.h"
#include "sockets.h"

#include "telnet.h"
#include "errors.h"
#include "event.h"

#include "windows.h"
#include "winsock.h"
#include "sshfunc.h"
#include "sshif.h"

#define ZERO(p) memset(p,0,sizeof(p))

static int sshinterface_find_proxy_from_socket(SOCKET socket)
{
  int i=0;

  xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_VERYLOW,"sshinterface_find_proxy_from_socket: looking up socket %d\n",socket);
  for (i=0; i<maxConnections; i++)
  {
    xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_VERYLOW,"sshinterface_find_proxy_from_socket: conn %d is sock %d\n",i,session[i].s);

    if (session[i].s == socket)
      return i;
  }

  return -1;
}



int sshinterface_handler(SOCKET socket, socket_event_type reason)
{
  int error,s;
  xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_LOW,"sshif handler called\n");

  s = sshinterface_find_proxy_from_socket(socket);

  if (s < 0)
  {
    xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_LOW,"sshif handler: unknown SSH socket %d\n",socket);
    return EVENT_CLAIM;
  }

  if (session[s].s == INVALID_SOCKET)
  {
    xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_LOW,"sshif handler: session been killed",socket);
    return EVENT_CLAIM;
  }

  error = 0;
  switch (reason)
  {
    case socket_ASYNC_EVENT:
      error=ssh_msg(s,0 /*wParam*/ ,FD_READ);
      break;
    case socket_BROKEN_EVENT:
      error=ssh_msg(s,0 /*wParam*/ ,FD_CLOSE);
      break;
  }
  if (error<=0 && error != 0xFF)
  {
    xsyslogf(SYSLOG_FILE,LOG_ERROR,"%d: sshinterface_handler: error %d from SSH\n",s,error);
    return EVENT_CLAIM;
  }
  if (error == 0xFF)
  {
    /* should shut connection down here */
    xsyslogf(SYSLOG_FILE,LOG_ERROR,"%d: sshinterface_handler: fatal error %s from SSH\n",s,errorString);
    return EVENT_CLAIM;
  }

  return EVENT_CLAIM;
}

void sshinterface_sshsession_init(SSHSession *sessionStrc)
{
  sessionStrc->s = INVALID_SOCKET;
  sessionStrc->crLine[0] = 0;
  sessionStrc->crLine[1] = 0;
  sessionStrc->cipher = NULL;
  sessionStrc->size_needed = FALSE;
  sessionStrc->ssh_state = SSH_STATE_BEFORE_SIZE;
  ZERO(sessionStrc->session_key);
  sessionStrc->savedhost = NULL;
  sessionStrc->rows = 0;
  sessionStrc->cols = 0;
  ZERO(sessionStrc->inbuf);
  sessionStrc->inbuf_head = 0;
  sessionStrc->inbuf_reap = 0;

  sessionStrc->pr.uPos = 0;
  sessionStrc->pr.pPos = 0;
  sessionStrc->pr.c = '\0';
  ZERO(sessionStrc->pr.username);
  ZERO(sessionStrc->pr.password);

  sessionStrc->gD.len  = 0L;
  sessionStrc->gD.biglen  = 0L;
  sessionStrc->gD.to_read  = 0L;
  sessionStrc->gD.c  = '\0';
  sessionStrc->gD.p  = NULL;
  sessionStrc->gD.i  = 0;
  sessionStrc->gD.pad  = 0;
  ZERO(sessionStrc->gD.padding);
  ZERO(sessionStrc->gD.word);
  sessionStrc->gD.chunk  = 0;

  sessionStrc->pktin.length = 0;
  sessionStrc->pktin.type = 0;
  sessionStrc->pktin.crc = 0;
  sessionStrc->pktin.data = NULL;
  sessionStrc->pktin.body = NULL;
  sessionStrc->pktin.maxlen = 0;

  sessionStrc->pktout.length = 0;
  sessionStrc->pktout.type = 0;
  sessionStrc->pktout.crc = 0;
  sessionStrc->pktout.data = NULL;
  sessionStrc->pktout.body = NULL;
  sessionStrc->pktout.maxlen = 0;

  ZERO(sessionStrc->des.eiv1);
  ZERO(sessionStrc->des.eiv2);
  ZERO(sessionStrc->des.eiv3);
  ZERO(sessionStrc->des.ekey1.key_schedule);
  ZERO(sessionStrc->des.ekey2.key_schedule);
  ZERO(sessionStrc->des.ekey3.key_schedule);
  ZERO(sessionStrc->des.div1);
  ZERO(sessionStrc->des.div2);
  ZERO(sessionStrc->des.div3);
  ZERO(sessionStrc->des.dkey1.key_schedule);
  ZERO(sessionStrc->des.dkey2.key_schedule);
  ZERO(sessionStrc->des.dkey3.key_schedule);

  memset(&(sessionStrc->cfg),0,sizeof(Config));
}

void sshinterface_connection_close(int s)
{
  xsyslogf_irq(SYSLOG_FILE,LOG_DEBUG_HIGH,"sshinterface_connection_close: closed connection %d\n",s);
  if (session[s].s != INVALID_SOCKET)
    socket_close(session[s].s);
  /* SSH socket already closed */
  if (session[s].cipher!=NULL)
    free(session[s].cipher);
  if (session[s].pktout.data)
    free(session[s].pktout.data);
  if (session[s].pktin.data)
    free(session[s].pktin.data);
  telnet_close_connection(s);
  sshinterface_sshsession_init(&session[s]);
}


OSERROR *sshinterface_initialise(void)
{
  int i=0;

  session = calloc(maxConnections,sizeof(SSHSession));

  if (session == NULL)
  {
    xsyslogf(SYSLOG_FILE,LOG_ERROR,"sshinterface_initialise: Out of memory\n");
    strcpy(error_outOfMemory.errmess,"out of mem");
    error_outOfMemory.errnum=1234; /* FIX ME */
    return &error_outOfMemory;
  }

  for (i=0; i<maxConnections; i++)
    sshinterface_sshsession_init(&(session[i]));

  return NO_OSERROR;
}

OSERROR *sshinterface_finalise(void)
{
  int i=0;

  if (session != NULL)
  {
    for (i=0; i<maxConnections; i++)
    {
      if (session[i].s != INVALID_SOCKET)
      {
        socket_close(session[i].s);
        sshinterface_connection_close(i);
      }
    }
    free(session);
    session = NULL;
  }

  return NO_OSERROR;
}

void sshinterface_poll_single(Desk_bool maskOutNullPoll)
{
  event_poll_single(maskOutNullPoll);
}
