#include <stdio.h>
#include <des.h>
#include "Connect.h"
#include "inet-udp.h"
#include "Code.h"
#include "literal.h"
#include "fred.h"

void printError(die)
     int die;
{
  int fatal = 0;

  while (Error_Exists)
    {
      fprintf(stderr, Error_String(Error), Error_Info);
      fputc('\n', stderr);
      if (Error_Severity == S_FATAL)
	fatal++;
      Error_Pop();
    }

  if (fatal && die)
    exit(1);
}

#define E(x) if (x) printError(1)
#define ED(x) if (x) printError(0)

main(argc, argv)
     char **argv;
{
  Addr a, b;
  Packet p;
  char name[50];
  char from[50], to[50];

  E(Connect_Initialize());
  E(Connect_RegisterDomain(&inetudp));

  E(Code_Initialize());
  E(Code_RegisterDomain(&literal));

  sprintf(from, "%s:./%s", PORTDOMAIN, PORTNAME);
  if (Connect_NameToAddress(from, &a))
    {
      if (Error == UDP_UNKNOWNSERVICE)
	{
	  fprintf(stderr,
		  "Warning: Service %s not in /etc/services; using port %d\n",
		  PORTNAME, PORTNUMBER);
	  Error_Pop();
	  sprintf(from, "%s:./%d", PORTDOMAIN, PORTNUMBER);
	  E(Connect_NameToAddress(from, &a));
	}
      else
	printError();
    }

  sprintf(to, "%s:*", PORTDOMAIN);
  E(Connect_NameToAddress(to, &b));

  E(Connect_OpenConnection(a, b));

  while (1)
    {
      E(Connect_Wait(&p, NOTIMEOUT));

      Connect_AddressToName(p.Source, name, sizeof(name));
      fprintf(stdout, "From: %d - %s\n", p.Source, name);

      processRequest(&p);
    }
}

int processRequest(p)
     Packet *p;
{
  static CodeBlock rblock;
  static char rdata[512];
  static int rinitialized = 0;
  CodeBlock *block;
  Card16 proto, reqtype;
  Card32 keygen;
  char *system, *program, *vendor, *version, *path;
  int delta;
  des_cblock key;
  Addr a;

  if (!rinitialized)
    {
      E(Code_InitBlock(&rblock, CODEDOMAIN, rdata, sizeof(rdata)));
      rinitialized = 1;
    }
  else
    Code_ResetBlock(&rblock);

  E(Code_CreateBlock(&block, CODEDOMAIN, p->packet, p->length));

  if (Code_GetCard16(block, &proto))
    {
    badpacket:
      Code_PutCard16(&rblock, REPLY_ERROR);
      Code_PutCard16(&rblock, ERROR_BADPACKET);
      goto sendrblock;
    }

  if (proto != PROTOVERSION0)
    {
      Code_PutCard16(&rblock, REPLY_ERROR);
      Code_PutCard16(&rblock, ERROR_PROTOUNSUPPORTED);
      goto sendrblock;
    }

  if (Code_GetCard16(block, &reqtype))
    goto badpacket;

  switch(reqtype)
    {
    case REQUEST_REGISTRATION:
    case REQUEST_KEY:
      if (Code_GetString(block, &system) ||
	  Code_GetString(block, &program) ||
	  Code_GetString(block, &version) ||
	  Code_GetString(block, &vendor) ||
	  Code_GetString(block, &path) ||
	  Code_GetCard32(block, &keygen))
	goto badpacket;

      fprintf(stdout, "%s\n%s\n%s\n%s\n%s\n%d\n", system, program,
	      version, vendor, path, keygen);

      if (abs(p->when - keygen) > TIMERANGE) /* XXX - int sizes? */
	{
	  Code_PutCard16(&rblock, REPLY_ERROR);
	  Code_PutCard16(&rblock, ERROR_BADKEYGENERATOR);
	  goto sendrblock;
	}

      gimme_key_for_prog(key, keygen, system, program, version, vendor);

      Code_PutCard16(&rblock, REPLY_KEY);
      Code_PutMemory(&rblock, key, sizeof(key));
      goto sendrblock;
      break;

    case REQUEST_PING:
      Code_PutCard16(&rblock, REPLY_PING);
      goto sendrblock;
      break;

    case REQUEST_DEREGISTRATION:
      Code_PutCard16(&rblock, REPLY_DEREGRISTRATION);
      goto sendrblock;
      break;

    default:
      Code_PutCard16(&rblock, REPLY_ERROR);
      Code_PutCard16(&rblock, ERROR_BADREQUEST);
      goto sendrblock;
      break;
    }

 sendrblock:

  E(Code_FreeBlock(block));
  free(p->packet);

  a = p->Destination;
  p->Destination = p->Source;
  p->Source = a;
  p->packet = Code_BlockData(&rblock);
  p->length = Code_BlockLength(&rblock);
  E(Connect_SendPacket(p));
  E(Code_FreeBlock(block));
}
