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

#include <bn.h>
/*------------------------------------*/
int main(int argc, char *argv[])
{
  u8_t buf[4096], uid[1024], match[256], *bp, t;
  char keybuf[4096], secbuf[4096], uhbuf[5], *kbptr = keybuf;
  u32_t ll, xflg = 0, prif = 0, kblen = 0, sblen = 0, *kcp = &kblen, gaz;
  int i, j, wasrsa = 0, hlen = 0, v4flg = 0, gotkey = 0, sigf = 0, secf = 0;
  keyid_t dsakid = 0, dhkid = 0;
  FILE *outf = stdout, *ringf = NULL;
  extern char *optarg;
  BIGNUM *N;

  match[0] = 0;
  while ((i = getopt(argc, argv, "o:m:r:eSuxsyz4")) != -1) {
    if (i == 'm')
      strncpy(match, optarg, 256);
    else if (i == '4')
      v4flg = 1, xflg = 3;
    else if (i == 'S')
      sigf = 1;
    else if (i == 'e')
      secf = 1;
    else if (i == 'x')
      xflg = 2;
    else if (i == 'y')
      xflg = 3;
    else if (i == 'z')
      xflg = 4;
    else if (i == 'u')
      xflg = 5, gotkey = 1;
    else if (i == 's')
      prif = 1;
    else if (i == 'r')
      PGP_kring(optarg, 0),
        ringf = fopen(optarg, "rb");
    else if (i == 'o') {
      outf = fopen(optarg, "wb");
      if (outf == NULL)
        exit(-1);
    } else {
      fprintf(stderr, "usage: lookup5 [-r ring] [-m \"User ID\"] [-e] [-s]\n"
              "Prints a list of keyids on ring.  -S will also verify sigs.\n"
              "-m matches string, will return all with the substr when alone\n"
              "otherwise does the function on the first matching\n"
              "-e V4 encryption key, -s for V3 RSA or V4 sign key only\n"
              "for printing -e for V3 rsa keyid, -s for V4 keyid\n"
              "-x pulls the key packet(s), -u pulls the uid, -y for key+uid\n"
              "-4 for key+uid for V4 sig, -z for key+uid for V3 sig\n");
      exit(-1);
    }
  }

  if (!ringf && (bp = getenv("PGPPATH"))) {  /* if no ringf, try PGPPATH */
    strncpy(buf, bp, 2000);
    strcat(buf, "/pubring.pkr");
    ringf = fopen(buf, "rb");
  }
  if (!ringf && (bp = getenv("HOME"))) {  /* no, try default from HOME */
    strncpy(buf, bp, 2000);
    strcat(buf, "/.pgp/pubring.pkr");
    ringf = fopen(buf, "rb");
  }
  if (!ringf)
    return -3;                  /* no keyring */

  for (;;) {
    t = fgetc(ringf);
    if (!(bp = PGP_gtpkt(ringf, t, &gaz)))
      break;
    gaz = gaz > 4095 ? 4095 : gaz;
    memcpy(buf, bp, gaz + 1);
    buf[gaz] = 0;
    free(bp);

    bp = buf;
    t = (t & 0x7c) >> 2;
    if (t == 2) {               /* sig */
      void *hctx;
      int halg, stype;

      if (!sigf || *bp < 3 || *bp > 4)
        continue;
      halg = *bp == 3 ? bp[16] : bp[3];  /* grab algorithm */
      stype = *bp == 3 ? bp[2] : bp[1];
      hctx = PGP_hini(halg);

      if (stype == 0x20) {
        PGP_hblk(hctx, keybuf, kblen);
        strcpy(uid, "[REVOCATION]");
      } else if (stype == 0x1f) {
        PGP_hblk(hctx, keybuf, kblen);
        strcpy(uid, "[SIG ON KEY]");
      } else if (stype == 0x28) {
        PGP_hblk(hctx, secbuf, sblen);
        strcpy(uid, "[REVOCATION on subkey]");
      } else if (stype == 0x18) {
        strcpy(uid, "[Sig on subkey]");
        PGP_hblk(hctx, keybuf, kblen);
        PGP_hblk(hctx, secbuf, sblen);
      } else {                  /* 0x10-0x13 */
        PGP_hblk(hctx, keybuf, kblen);
        if (*bp == 4) {
          uhbuf[0] = 0xb4, uhbuf[1] = hlen >> 24, uhbuf[2] = hlen >> 16,
            uhbuf[3] = hlen >> 8, uhbuf[4] = hlen;
          PGP_hblk(hctx, uhbuf, 5);
        }
        PGP_hblk(hctx, &keybuf[kblen], hlen);
      }
      i = PGP_sigck(bp, hctx);
      if (i == -4)
        continue;
      if (!i) {
        dhkid = NEXT_KEY;
        PGP_gtkey(NULL, NULL, &dhkid);
        fprintf(stderr, "Verified Sig %016qX %s\n", dhkid, uid);
      } else if (i == -1)
        fprintf(stderr, "BAD SIGNATURE\n");
      else
        fprintf(stderr, "SigErr %d V%02x T%02x H%d\n", i, bp[0], stype, halg);
      continue;
    } else if (t == 12) {       /* validity */
      continue;
    } else if (t == 13) {       /* uid - should match */
      strcpy(uid, buf);

      if (wasrsa && (!strlen(match) || strstr(uid, match))) {
        if (prif)
          fprintf(outf, "%016qX\n", dsakid);
        else if (secf)
          fprintf(outf, "%016qX\n", dhkid);
        else
          fprintf(outf, "%016qX %016qX %s\n", dsakid, dhkid, uid);
      }
      wasrsa = 0;

      memcpy(&keybuf[kblen], buf, gaz);
      hlen = gaz;
      if (strlen(match) && !strstr(uid, match)) {
        if (xflg != 5)
          gotkey = 0;
        continue;
      }
      if (xflg == 2) {
        if (prif) {
          fwrite(keybuf, 1, kblen, outf);
          return 0;
        } else
          continue;
      }
      if (gotkey && xflg > 1) {
        if (xflg != 5)
          fwrite(kbptr, 1, *kcp, outf);

        if (xflg != 4) {
          fputc(0xb4, outf);
          if (v4flg)
            fputc(gaz >> 24, outf), fputc(gaz >> 16, outf),
              fputc(gaz >> 8, outf);
          fputc(gaz, outf);
        }
        fwrite(buf, 1, gaz, outf);
        return 0;
      }
      continue;
    } else if (t == 5 || t == 6 || t == 7 || t == 14) {  /* sec or pub key */
      if (t == 7 || t == 14)
        kbptr = secbuf, kcp = &sblen;
      else
        kbptr = keybuf, kcp = &kblen, sblen = 0, uid[0] = 0;
      j = *bp++;                /* vers */
      /* timestamp */
      ll = *bp++ << 24, ll += *bp++ << 16, ll += *bp++ << 8, ll += *bp++;
      if (j != 4)
        bp += 2;                /* old valid days */

      switch (*bp++) {
      case 17:
        BN_free(PGP_mpiBN(&bp)), BN_free(PGP_mpiBN(&bp));
        BN_free(PGP_mpiBN(&bp)), BN_free(PGP_mpiBN(&bp));
        dsakid = PGP_gkid(buf, bp - buf);
        kbptr[0] = 0x99, kbptr[1] = (bp - buf) >> 8, kbptr[2] = bp - buf;
        memcpy(&kbptr[3], buf, bp - buf);
        *kcp = bp + 3 - buf;
        if (prif || xflg > 1) {
          gotkey++;
          continue;
        }
        break;
      case 16:
      case 20:
        BN_free(PGP_mpiBN(&bp)), BN_free(PGP_mpiBN(&bp));
        BN_free(PGP_mpiBN(&bp));
        dhkid = PGP_gkid(buf, bp - buf);
        kbptr[0] = 0x99, kbptr[1] = (bp - buf) >> 8, kbptr[2] = bp - buf;
        memcpy(&kbptr[3], buf, bp - buf);
        *kcp = bp + 3 - buf;

        if (strlen(match) && !strstr(uid, match))
          continue;

        if (xflg == 2) {
          if (!secf)
            fwrite(keybuf, 1, kblen, outf);
          fwrite(secbuf, 1, sblen, outf);
          return 0;
        }
        if (xflg) {
          gotkey++;
          continue;
        }
        if (!prif && !secf) {
          fprintf(outf, "%016qX %016qX %s\n", dsakid, dhkid, uid);
          continue;
        } else if (prif)
          fprintf(outf, "%016qX\n", dsakid);
        else if (secf)
          fprintf(outf, "%016qX\n", dhkid);

        break;
      case 1:
      case 2:
      case 3:
        N = PGP_mpiBN(&bp), BN_free(PGP_mpiBN(&bp));
        dsakid = PGP_gkid(buf, bp - buf);
        j = 64;
        dhkid = 0;
        while (j)
          dhkid = (dhkid << 1) + BN_is_bit_set(N, --j);
        BN_free(N);
        kbptr[0] = 0x99, kbptr[1] = (bp - buf) >> 8, kbptr[2] = bp - buf;
        memcpy(&kbptr[3], buf, bp - buf);
        *kcp = bp + 3 - buf;

        if (xflg >= 2) {
          gotkey++;
          continue;
        }
        wasrsa = 1;
        break;
      }
      continue;
    }
    /* 1-pke 8-cmprs 9-cke 11-raw */
    else
      continue;                 /* chunk that shouldn't be here */
  }
  return -4;                    /* not found */
}
