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

#include <rand.h>
#include "libpgp5.h"
extern int seccrypt;
extern int sechash;
extern int secsalt;

/*------------*/
int main(int argc, char *argv[])
{
  unsigned char keybuf[256], *pp = NULL;
  unsigned char dbuf[4200], convkey[30], *bp;
  unsigned char fname[256];
  int i, j, k, ll;
  int cmpr = 0, cvealg = 1, pka = 0x10, encflg = 0, litflg = 0, convf = 0;
  int signflg = 0, styp = 0, opflg = 0, halg = 2, salg = 0x11, v4flg = 0;
  int armorflg = 0;
  unsigned long long keyid = 0;
  FILE *inf = stdin, *outf = stdout;
  FILE *sigfile = NULL;
  extern char *optarg;

  memset(keybuf, 0, 256);
  while ((i = getopt(argc, argv,
                     "i:o:k:p:r:a:A:l:cCs:y:x:zv:134t:m:")) != -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), halg = cvealg;
    else if (i == 'A')
      pka = atoi(optarg), salg = pka;
    else if (i == 'p')          /* password */
      strcpy(keybuf, optarg), pp = keybuf;
    else if (i == 'c')
      convf = 1;
    else if (i == 'C')
      convf = 2;
    else if (i == 'm')
      armorflg++, strcpy(dbuf, optarg);
    else if (i == 'r')
      setkeyring5(optarg);
    else if (i == 'z')
      cmpr = 1;
    else if (i == 'v') {
      if (NULL == (sigfile = fopen(optarg, "rb")))
        exit(-1);
    } 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 if (i == 's')
      seccrypt = atoi(optarg);
    else if (i == 'y')
      sechash = atoi(optarg);
    else if (i == 'x')
      secsalt = atoi(optarg);
    else if (i == 't')
      styp = atoi(optarg);
    else if (i == '1')
      signflg++, opflg++;
    else if (i == '4')
      signflg++, v4flg++;
    else if (i == '3')
      signflg++;
    else {
      fprintf(stderr, "usage: minipgp5 -k DHkeyid [-r ring] <plain >cipher\n"
              "or minipgp5 <in >out\n"
              "or minipgp5 [-c | -C [-z s2kalg] ] -p pp,\n"
              "or minipgp5 -<file [-r ringfile] [-p pp]\n"
              "or minipgp5 [-t sigtype] -[134]\n"
              "or minipgp5 -z or minipgp5 -v det.sig\n");
      exit(-1);
    }
  }

  if (armorflg)
    return (armor(inf, outf, dbuf, NULL));
  if (cmpr == 1)
    return pgpcomp5(inf, outf);
  /* ========================= */
  /* Make signature packet */
  if (signflg) {
    void *signkey;

    /* pp != NULL tells getkey I want the secring */
    if (!pp)
      pp = "";
/* make V3 or V4 signature */
    if (salg == 0x11)
      signkey = DSA_new(),
        k = getkey5(NULL, (DSA **) & signkey, pp, &keyid);
#ifndef NOELGSIG
    else if (salg == 0x10)
      signkey = DH_new(),
        k = getkey5((DH **) & signkey, NULL, pp, &keyid);
#endif
#ifndef NO_RSA
    else if (salg == 1 || salg == 3)
      signkey = RSA_new(),
        k = getkey5((DH **) & signkey, (DSA **) & signkey, pp, &keyid);
#endif
    else
      return -3;
    if (k) {
      fprintf(stderr, "No Secret key found %d\n", k);
      return -5;
    }
    /* one pass signature header - below getkeys to verify keyid */
    if (opflg) {                /* just do 1pass header */
      fputc(0xc4, outf), fputc(0x0d, outf);
      /* ver type hash salg */
      fputc(0x03, outf), fputc(styp, outf);
      fputc(halg, outf), fputc(salg, outf);
      for (i = 0; i < 8; i++)   /* keyid */
        fputc(0xff & (keyid >> (56 - 8 * i)), outf);
      fputc(0x01, outf);        /* nested flag */
      /* Sloppy - I am not deallocing the key structures allocated above */
      return 0;
    }
    memset(dbuf, 0xff, 4);
    pp = v4flg ? dbuf : NULL;
    i = sigmak5(signkey, inf, outf, keyid, styp, halg, salg, pp);
    return 0;
  }
  /* ========================= */
  /* detached sig */
  if (sigfile) {
    void *hctx;

    bp = gazinta(sigfile, fgetc(sigfile), NULL);
    halg = bp[16];              /* grab algorithm */
    fclose(sigfile);
    hctx = hashinit(halg);
    while (!feof(inf) && 0 < (k = fread(dbuf, 1, 4096, inf)))
      hashupdate(hctx, dbuf, k);
    i = sigchk5(bp, hctx);
    free(bp);
    fprintf(stderr, "%s\n", i ? "BAD SIGNATURE" : "good signature");
    return i;
  }
  /* ========================= */
  /* encrypt */
  if (encflg || convf) {
    void *ccfb = NULL;
    /* "PGP" file type header */
    char phdr[6] =
    {0xa8, 3, 'P', 'G', 'P', 0};

    /* prepare conventional key (not used if convf == 1) */
    convkey[0] = cvealg;
    j = cnvkeysiz(cvealg);;     /* key len */
    RAND_bytes(&convkey[1], j);
    for (ll = 0, i = 1; i <= j; i++)
      ll += convkey[i];         /* conv key checksum */
    convkey[++j] = ll >> 8;
    convkey[j + 1] = ll;
    if (convf == 1)
      seccrypt = cvealg;
    /* key header, ultimately to be looped */
    if (!convf) {
      /* for each keyid if multiple */
      if (0 >= (ll = pkeenc5(keyid, dbuf, convkey, j + 2, pka)))
        return -2;
      fwrite(phdr, 1, strlen(phdr), outf);
      phdr[0] = 0;
      fwrite(dbuf, 1, ll, outf);
    } else {
      /* for each passphrase (in keybuf) if multiple, and convf == 2 */
      ll = putcfbkey(dbuf, pp, 4, &ccfb);
      bp = dbuf + ll;

      if (convf == 2) {
        memcpy(bp, convkey, j);
        docfb(bp, j, ccfb);
        free(ccfb);
        ccfb = NULL;
        ll += j;
      }
      /* omit if pgp 2.x conventional default */
      if (convf != 1 || seccrypt != 1 || sechash != 1 || secsalt != 0) {
        fwrite(phdr, 1, strlen(phdr), outf);
        phdr[0] = 0;
        fputc(0xc3, outf);
        if (ll > 192) {
          fputc(0xc0 | ((ll - 192) >> 8), outf);
          fputc(ll - 192, outf);
        } else
          fputc(ll, outf);
        fwrite(dbuf, 1, ll, outf);
      }
    }
    /* end of key header */
    memset(dbuf, 0, 256);
    if (!ccfb)
      ccfb = cfbinit(&convkey[1], dbuf, cvealg, 1);
    cnvenc5(inf, outf, ccfb);
    return 0;
  }
  /* ========================= */
  if (litflg) {                 /* wrap in literal, add fname */
    /* enlit5 */
    bp = dbuf;
    *bp++ = 0xec;
    fputc(0xcb, outf);
    *bp++ = 'b';                /* type of file - binary works */
    *bp++ = strlen(fname);
    strcpy(bp, fname);
    bp += strlen(fname);
    *bp++ = 0, *bp++ = 0, *bp++ = 0, *bp++ = 0;
    j = bp - dbuf - 1;
    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;
        } else
          dbuf[0] = j;
        fwrite(dbuf, 1, j + 1, outf);

      }
    }
    return 0;
  }
  /* ========================= */
  ll = fgetc(inf);
  ungetc(ll, inf);
  if (ll < 0x80)
    return (dearmor(inf, outf));
  if (ll < 0xc0)
    ll = 0xc0 + ((ll & 0x3c) >> 2);
  ll -= 0xc0;
  switch (ll) {
  case 8:
    return pgpdcom5(inf, outf);
  case 10:                     /* 0xa8,3,PGP */
  case 1:
  case 3:
    return decrypt5(inf, outf, pp);
  case 9:                      /* from 0xa4 */
    {
      unsigned char xfix[] =
      {4, 1, 0, 1}, *xp = xfix;
      void *np = NULL;
      getcfbkey(&xp, pp, &np);
      return cnvdec5(inf, outf, np);
    }
  case 4:
  case 2:
  case 11:
    i = dlitck5(inf, outf, &j);
    if (j)
      fprintf(stderr, i ? "FAILED SIGNATURE CHECK\n" : "good signature\n");
    break;
  }
  return -1;
}
