#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

#include <rand.h>

#include "libpgp5.h"

/*------------*/
int main(int argc, char *argv[])
{
  unsigned char keybuf[256];
  unsigned char dbuf[4200], convkey[30], *bp;
  unsigned char fname[256];
  int i, j, ll, cvealg = 1, pka = 0x10, encflg = 0, litflg = 0;
  unsigned long long keyid = 0;
  FILE *inf = stdin, *outf = stdout;
  extern char *optarg;

  memset(keybuf, 0, 256);
  while ((i = getopt(argc, argv, "i:o:dk:p:r:a:A:l:")) != -1) {
    if (i == 'k')
      encflg = 1,
        sscanf(optarg, "%qx", &keyid);
    else if (i == 'l')
      litflg = 1,
        strcpy(fname, optarg);
    else if (i == 'a')
      cvealg = atoi(optarg);
    else if (i == 'A')
      pka = atoi(optarg);
    else if (i == 'd')
      encflg = -1;
    else if (i == 'p')          /* password */
      strcpy(keybuf, optarg);
    else if (i == 'r')
      setkeyring5(optarg);
    else if (i == 'i') {
      inf = fopen(optarg, "rb");
      if (inf == NULL)
        exit(-1);
    } else if (i == 'o') {
      outf = fopen(optarg, "wb");
      if (outf == NULL)
        exit(-1);
    } else {
      fprintf(stderr, "usage: minipgp5 -k DHkeyid [-r ring] <plain >cipher\n"
              "or minipgp5 <oldlol >newcnkl, or minipgp5,\n"
              "or minipgp5 -d <file [-r ringfile] [-p pp]\n");
      exit(-1);
    }
  }

  if (encflg == -1) {
    ll = fgetc(inf);
    if (ll == 0xa8) {           /* literal w/ len & string ("PGP"), bypass */
      ll = fgetc(inf);
      while (ll--)
        fgetc(inf);
      ll = fgetc(inf);
    }
    if (ll != 0xc1)             /* new PKE ? */
      exit(-1);
    ll = fgetc(inf);            /* chunk length byte(s) to actual length */
    if (ll >= 0xc0) {
      if (ll >= 0xe0)
        exit(-1);
      else {
        ll = ((ll & 31) << 8) + 192;
        ll += fgetc(inf);
      }
    }
    fread(dbuf, 1, ll, inf);

    if ((ll = pkedec5(dbuf, keybuf)))
      return ll;

    bp = keybuf;
    cvealg = *bp++;             /* crypttype */
    j = cvealg == 2 ? 24 : 16;
    ll = 0;                     /* accumulate and verify checksum */
    for (i = 0; i < j; i++)
      ll += *bp++;
    if (ll != (bp[0] * 256 + bp[1]))
      return -4;
    return cnvdec5(inf, outf, &keybuf[1], cvealg);
  } else if (encflg == 1) {
    dbuf[0] = 0xa8;
    dbuf[1] = 3;
    dbuf[2] = 'P';
    dbuf[3] = 'G';
    dbuf[4] = 'P';
    fwrite(dbuf, 1, 5, outf);
    convkey[0] = cvealg;
    j = cvealg == 2 ? 24 : 16;  /* key len */
    RAND_bytes(&convkey[1], j);
    ll = 0;                     /* calc and write conv key checksum */
    for (i = 1; i <= j; i++)
      ll += convkey[i];
    convkey[j + 1] = ll >> 8;
    convkey[j + 2] = ll;
    ll = pkeenc5(keyid, dbuf, convkey, j + 3, pka);
    if (ll <= 0)
      return ll;
    fwrite(dbuf, 1, ll, outf);
    cnvenc5(inf, outf, &convkey[1], cvealg);
    return 0;
  }
  bp = dbuf;
  *bp++ = 0xec;
  if (litflg) {                 /* wrap in literal, add fname */
    fputc(0xcb, outf);
    *bp++ = 'b';
    *bp++ = strlen(fname);
    strcpy(bp, fname);
    bp += strlen(fname);
    *bp++ = 0;
    *bp++ = 0;
    *bp++ = 0;
    *bp++ = 0;
    j = bp - dbuf - 1;
  } else {                      /* convert len of len to chunk-length */
    ll = fgetc(inf);            /* 0xa3 */
    if ((ll & 0xc0) != 0x80)
      exit(-1);
    fputc(0xc0 + ((ll >> 2) & 0x0f), outf);
    switch (ll & 3) {           /* eat length of length if any */
    case 2:
      fgetc(inf);
      fgetc(inf);
    case 1:
      fgetc(inf);
    case 0:
      fgetc(inf);
    }
    j = 0;
  }

  for (;;) {
    ll = fread(bp, 1, 4096 - j, inf);
    if (ll <= 0)
      break;
    j += ll;
    if (j == 4096) {
      fwrite(dbuf, 1, j + 1, outf);
      bp = &dbuf[1];
      j = 0;
    } else {
      if (j > 192) {
        fputc(0xc0 | ((j - 192) >> 8), outf);
        dbuf[0] = j - 192;
        fwrite(dbuf, 1, j + 1, outf);
      } else {
        dbuf[0] = j;
        fwrite(dbuf, 1, j + 1, outf);
      }
    }
  }
  return 0;
}
