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

#include "libpgp5.h"

/*------------------------------------*/
int main(int argc, char *argv[])
{
  unsigned char buf[4096], uid[1024], match[256], *bp, t, keybuf[4096];
  unsigned long i, j, ll, dhflg = 0, dsaflg = 0, gotkey = 0, kblen = 0;
  int gaz;
  DSA *dsakey = DSA_new();
  DH *dhkey = DH_new();
  unsigned long long dsakid = 0, dhkid = 0;
  FILE *outf = stdout;
  extern char *optarg;
#ifndef NO_RSA
  RSA *rsakey = RSA_new();
#endif

  match[0] = 0;
  while ((i = getopt(argc, argv, "o:m:r:esuxyz")) != -1) {
    if (i == 'm')
      strncpy(match, optarg, 256);
    else if (i == 'e')
      dhflg = 1;
    else if (i == 'x')
      dhflg = 2;
    else if (i == 'y')
      dhflg = 3;
    else if (i == 'z')
      dhflg = 4;
    else if (i == 'u')
      dhflg = 5, gotkey = 1;
    else if (i == 's')
      dsaflg = 1;
    else if (i == 'r')
      kring = 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"
        "-m provides a user string to match, will return all with substring\n"
              "unless used with -e or -s (which returns the first matching)\n"
              "-e for encryption key only, -s for sign key only\n"
              "-x extracts the key, -u uid, -y for revoke -z sign data\n");
      exit(-1);
    }
  }

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

  for (;;) {
    t = fgetc(kring);
    bp = gazinta(kring, t, &gaz);
    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 */
      continue;
    else if (t == 12) {         /* validity */
      continue;
    } else if (t == 13) {       /* uid - should match */
      strcpy(uid, buf);

      if (strlen(match) && !strstr(uid, match)) {
        if (dhflg != 5)
          gotkey = 0;
        continue;
      }
      if (gotkey && dhflg > 1) {
        fwrite(keybuf, 1, kblen, outf);
        if (dhflg == 2)
          return 0;
        if (dhflg != 4)
          fputc(0xb4, 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 */
      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 0x11:
        dsakey->p = pgp2BN5(&bp);
        dsakey->q = pgp2BN5(&bp);  /* correct length */
        dsakey->g = pgp2BN5(&bp);
        dsakey->pub_key = pgp2BN5(&bp);
        donewkeyid(buf, bp - buf);
        dsakid = nkidul;

        if (dsaflg && dhflg >= 2) {
          gotkey++;
          if (dhflg == 5)
            continue;
          memcpy(keybuf, xbp, 3);
          memcpy(&keybuf[3], buf, bp - buf);
          kblen = bp + 3 - buf;
          continue;
        }
        break;
      case 0x10:
        dhkey->p = pgp2BN5(&bp);
        dhkey->g = pgp2BN5(&bp);
        dhkey->pub_key = pgp2BN5(&bp);
        donewkeyid(buf, bp - buf);
        dhkid = nkidul;

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

        if (dhflg >= 2) {
          gotkey++;
          if (dhflg == 5)
            continue;
          memcpy(keybuf, xbp, 3);
          memcpy(&keybuf[3], buf, bp - buf);
          kblen = bp + 3 - buf;

          fwrite(keybuf, 1, kblen, outf);

          if (dhflg == 3 || dhflg == 5)
            fputc(0xb4, outf),
              fputc(j, outf);
          if (dhflg > 2)
            fwrite(buf, 1, j, outf);
          if (dhflg == 2)
            return 0;
          continue;
        }
        if (!dsaflg && !dhflg) {
          fprintf(outf, "%016qX %016qX %s\n", dsakid, dhkid, uid);
          continue;
        } else if (dsaflg)
          fprintf(outf, "%016qX\n", dsakid);
        else if (dhflg == 1)
          fprintf(outf, "%016qX\n", dhkid);

        break;
#ifndef NO_RSA
      case 1:
      case 2:
      case 3:
        rsakey->n = pgp2BN5(&bp);
        rsakey->e = pgp2BN5(&bp);
        donewkeyid(buf, bp - buf);
        dsakid = nkidul;
        j = 64;
        dhkid = 0;
        while (j)
          dhkid = (dhkid << 1) + BN_is_bit_set(rsakey->n, --j);

        if (dhflg >= 2) {
          gotkey++;
          if (dhflg == 5)
            continue;
          memcpy(keybuf, xbp, 3);
          memcpy(&keybuf[3], buf, bp - buf);
          kblen = bp + 3 - buf;
          continue;
        }
        if (!dsaflg && !dhflg) {
          fprintf(outf, "%016qX %016qX %s\n", dsakid, dhkid, uid);
          continue;
        } else if (dsaflg)
          fprintf(outf, "%016qX\n", dsakid);
        else if (dhflg == 1)
          fprintf(outf, "%016qX\n", dhkid);

        break;
#endif
      }
      continue;
    }
    /* 1-pke 8-cmprs 9-cke 11-raw */
    else
      break;                    /* chunk that shouldn't be here */
  }
  return -4;                    /* not found */
}
