
#include "lclient.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

static int force_read(int fd, char *buf, int len)
{
  int n;
  int nread = 0;

  while (nread < len)
    {
      n = read(fd, buf, len - nread);
      if (n == -1)
	return -1;
      if (n == 0)
	return nread;
      buf += n;
      nread += n;
    }
  return len;
}

static int force_write(int fd, char *buf, int len)
{
  int n;
  int nwritten = 0;

  while (nwritten < len)
    {
      n = write(fd, buf, len - nwritten);
      if (n == -1)
	return -1;
      buf += n;
      nwritten += n;
    }
  return len;
}

int copy_crypt_file(char *progname, int infile, int outfile,
		    unsigned char *init_key, int encrypt)
{
  unsigned char key[KEY_SIZE];
  int i = 0;

#ifdef DEBUG
  printf("copy_crypt_file(%s, %d, %d, %x) { %x, %x} %s\n",
	 progname, infile, outfile, init_key,
	 ((int *)init_key)[0], ((int *)init_key)[1], 
	 encrypt ? "encrypt" : "decrypt");
#endif

  memcpy(key, init_key, KEY_SIZE);

  while (1)
    {
      char io_buf[ENCRYPT_CHUNK_SIZE];
      int n = force_read(infile, io_buf, ENCRYPT_CHUNK_SIZE);
      if (n == -1)
	{
	  fprintf(stderr, "%s: unable to read executable: %s\n", progname,
		  strerror(errno));
	  return 1;
	}
      if (n == 0)
	break;
      /* Don't encrypt the last block.  If the block were encrypted, the padded
	 block would have to be written to the file and the reader would have
	 to know how many bytes to discard.
	 This code assumes that the block size is a power of 2.  */
      if (i < 2)
	CRYPT_COPY(key, (unsigned char *)io_buf, n & ~(ENCRYPT_BLOCK_SIZE - 1), encrypt);
      if (force_write(outfile, io_buf, n) == -1)
	{
	  fprintf(stderr, "%s: unable to write temporary file: %s\n",
		  progname, strerror(errno));
	  return 2;
	}
      i++;
    }
  return 0;
}

int read_key_file(char *progname, char *file,
		  unsigned char *key, unsigned int key_length)
{
  FILE *fp;
  if (key_length != KEY_SIZE)
    return 1;
  fp = fopen(file, "r");
  if (fp == 0)
    {
      fprintf(stderr, "%s: unable to open key file\n", progname);
      return 2;
    }
  if (fread(key, 1, key_length, fp) != key_length)
    {
      fprintf(stderr, "%s: invalid key file\n", progname);
      fclose(fp);
      return 3;
    }
  return 0;
}

int write_key_file(char *file, unsigned char *key, unsigned int key_length)
{
  FILE *fp;
  if (key_length != KEY_SIZE)
    return 1;
  fp = fopen(file, "w");
  if (fp == 0)
    return 2;
  if (fwrite(key, 1, key_length, fp) != key_length)
    {
      fclose(fp);
      return 3;
    }
  return 0;
}

int lclient_parse_key_128(unsigned char *key, char *key0, char *key1,
			  char *key2, char *key3)
{
  unsigned long tmp;
  char *endptr;

  /* This code works if a long is 4 bytes or the machine is little-endian.
     If a big-endian machine with sizeof (long) > 4 must be supported
     there are 2 options:
     1. declare "tmp" as an int if int is 4 bytes
     2. store a character at a time, shifting after each character
   */

  tmp = strtoul(key0, &endptr, 0);
  if (endptr == key0)
    return 1;
  memcpy(&key[0], &tmp, 4);

  tmp = strtoul(key1, &endptr, 0);
  if (endptr == key1)
    return 1;
  memcpy(&key[4], &tmp, 4);

  tmp = strtoul(key2, &endptr, 0);
  if (endptr == key2)
    return 1;
  memcpy(&key[8], &tmp, 4);

  tmp = strtoul(key3, &endptr, 0);
  if (endptr == key3)
    return 1;
  memcpy(&key[12], &tmp, 4);

#ifdef DEBUG
  printf("key is {%08x, %08x, %08x, %08x}\n",
	 ((int *)key)[0], ((int *)key)[1], ((int *)key)[2], ((int *)key)[3]);
#endif

  return 0;
}

int lclient_parse_key_64(unsigned char *key, char *key0, char *key1)
{
  unsigned long tmp;
  char *endptr;

  tmp = strtoul(key0, &endptr, 0);
  if (endptr == key0)
    return 1;
  memcpy(&key[0], &tmp, 4);

  tmp = strtoul(key1, &endptr, 0);
  if (endptr == key1)
    return 1;
  memcpy(&key[4], &tmp, 4);

#ifdef DEBUG
  printf("key is {%08x, %08x}\n", ((int *)key)[0], ((int *)key)[1]);
#endif

  return 0;
}
