#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <rand.h>
#include "libopgp.h"

FILE *inf, *outf;
#ifndef NOPIPE
#include <sys/wait.h>
int split()
{
  int i, passpipe[2];

  pipe(passpipe);
  if ((i = !(fork())))
    inf = fdopen(passpipe[0], "rb");
  else
    outf = fdopen(passpipe[1], "wb");
  close(passpipe[i]);
  return i;
}
#define WAITRET(x) {fclose(outf); if( !spl ) wait(&spl); return (x);}
#else
#include <sys/stat.h>
#define WAITRET(x) { if(!stepf) { fclose(inf); fflush(outf); \
 fseek(outf,0,SEEK_SET); inf = outf; outf = tmpfile(); } }
#endif

#define optset(let,var,val) else if (i == (let)) var = (val)
#define optfile(let,fp,mode) else if (i == (let) && \
      (NULL != ((fp) = fopen(optarg, mode))))

extern u8_t PGP_s2kca, PGP_s2kha, PGP_s2ksa;
extern int PGP_zlvl;

#include "opgpuse.h"
int main(int argc, char *argv[])
{
  char keybuf[256], *pp = NULL;
  u8_t dbuf[4200], *bp;
  char fname[256] = "", *litn = NULL, *armsg = NULL;
  int i, j, k, ll, armorf = 0, stepf = 0, cctbf = 0, spl = 1;
  int zipf = 0, syma = 2, encf = 0, convf = 0, signf = 0, styp = 0, halg = 2;
  keyid_t keyid[16], keyid2 = 0;
  FILE *sigfile = NULL, *ofsav;
  void *hctx;

  inf = stdin, outf = stdout;
  while ((i = getopt(argc, argv,
                     "i:o:e:k:p:R:r:a:d:IOl:cCs:y:x:z2ZL:v:1n345t:m:"
          )) != -1) {
    if (i == 'k')
      sscanf(optarg, "%qx", &keyid2);
    else if (i == 'e')
      sscanf(optarg, "%qx", &keyid[encf++]);
    else if (i == 'l')
      strcpy(fname, optarg), litn = fname;
    else if (i == 'p')          /* password */
      strcpy(keybuf, optarg), pp = keybuf;
    else if (i == 'm')
      armorf++, armsg = strlen(optarg) ? dbuf : NULL, strcpy(dbuf, optarg);
    else if (i == 'r')
      PGP_kring(optarg, 2);
    else if (i == 'R')
      PGP_kring(optarg, 1);
    optfile('v', sigfile, "rb");
    optfile('i', inf, "rb");
    optfile('o', outf, "wb");
    optset('a', syma, atoi(optarg));
    optset('d', halg, atoi(optarg));
    optset('c', convf, -1);
    optset('C', convf, 1);
    optset('I', stepf, 1);
    optset('O', cctbf, 1);
    optset('z', zipf, 1);
    optset('2', zipf, 2);
    optset('Z', zipf, 3);
    optset('L', PGP_zlvl, atoi(optarg));
    optset('s', PGP_s2kca, atoi(optarg));
    optset('y', PGP_s2kha, atoi(optarg));
    optset('x', PGP_s2ksa, atoi(optarg));
    optset('t', styp, atoi(optarg));
    optset('1', signf, 1);
    optset('n', signf, 2);
    optset('3', signf, 3);
    optset('4', signf, 4);
    optset('5', signf, 5);
    optset('6', signf, 6);
    else {                      /* indent will give an error */
      fprintf(stderr, "%c not recognized\n%s", i, usagestr);
      exit(-1);
    }
  }
  if (sigfile) {                /* check detached sig */
    bp = PGP_gtpkt(sigfile, fgetc(sigfile), NULL);
    halg = *bp == 3 ? bp[16] : bp[3];  /* grab algorithm */
    fclose(sigfile);
    hctx = PGP_hini(halg);
    while (!feof(inf) && 0 < (k = fread(dbuf, 1, 4096, inf)))
      PGP_hblk(hctx, dbuf, k);
    i = PGP_sigck(bp, hctx);
    free(bp);
    fprintf(stderr, "%s\n", i ? "BAD SIGNATURE" : "good signature");
    return i;
  }
  ofsav = outf;
#ifndef NOPIPE
  if ((signf || litn) && (zipf || encf || convf || armorf) && (spl = split()))
    signf = 0, litn = NULL;
  if (!(signf || litn) && zipf && (encf || convf || armorf) && (spl = split()))
    zipf = 0;
  if (!(signf || litn || zipf) && (encf || convf) && armorf && (spl = split()))
    encf = 0, convf = 0;
#else
  umask(0177);                  /* for tempfiles */
  outf = tmpfile();
#endif
  if (signf || litn) {
    i = PGP_elsgm(inf, outf, litn, signf, styp, halg, keyid2, pp);
    WAITRET(i);
  }
  if (cctbf) {
    PGP_xctb(inf, outf);
    WAITRET(0);
  }
  if (zipf) {
    i = PGP_zip(inf, outf, zipf - 1);
    WAITRET(i);
  }
  if (encf || convf) {
    PGP_encry(inf, outf, encf, convf, keyid, syma, &pp);
    WAITRET(0);
#ifdef NOPIPE
    if (cctbf) {
      PGP_xctb(inf, outf);
      WAITRET(0);
    }
#endif
  }
  if (armorf) {
    i = PGP_armor(inf, outf, armsg, NULL);
    WAITRET(i);
  }
  /* ========================= */
  if (!(signf || litn || zipf || encf || convf || armorf || cctbf))
    for (;;) {
      ll = fgetc(inf);
      ungetc(ll, inf);
      if (ll < 0x80) {
        if (armorf)
          break;
#ifndef NOPIPE
        if (!stepf && (spl = split()))
          continue;
#endif
        PGP_dearmor(inf, outf);
        WAITRET(0);
        if (stepf)
          break;
        continue;
      }
      armorf++;
      if (ll < 0xc0)
        ll = 0xc0 + ((ll & 0x3c) >> 2);
      ll -= 0xc0;
#ifndef NOPIPE
      if ((ll == 1 || ll == 3 || ll == 8 || ll == 9 || ll == 10)
          && !stepf && (spl = split()))
        continue;
#endif
      if (ll == 1 || ll == 3 || ll == 10) {  /* incl 0xa8,3,PGP */
        PGP_decry(inf, outf, pp);
        WAITRET(0);
      } else if (ll == 8) {
        PGP_unzip(inf, outf);
        WAITRET(0);
      } else if (ll == 9) {     /* naked symmetrical */
        u8_t xfix[] =
        {4, 1, 0, 1};

        bp = xfix;
        PGP_gts2k(&bp, pp, &hctx);
        PGP_cdec(inf, outf, &hctx, 1);
        WAITRET(0);
      } else if (ll == 2 || ll == 4 || ll == 11) {  /* literal or signature */
        i = PGP_delcs(inf, outf, &j, NULL);
        if (j)
          fprintf(stderr, j < 0 ? "FAILED SIGNATURE CHECK\n" : "good signature\n");
#ifdef NOPIPE
        if (i == 1) {
          WAITRET(0);
        } else
#endif
          stepf = 1;
      } else {
        fclose(outf);
        outf = inf;
        break;
      }
      if (stepf)
        break;
    }
#ifdef NOPIPE
  else {
    fclose(outf);
    outf = inf;
  }
  fseek(outf, 0, SEEK_SET);
  while (!feof(outf))
    fwrite(dbuf, 1, fread(dbuf, 1, 4096, outf), ofsav);
#endif
  return 0;
}
