#include "libopgp.h"

#define RBSIZ 512
static void hashblk(int nest, void *hctx[], u8_t xch, FILE * inf, FILE * outf)
{
  u8_t *bp, dbuf[RBSIZ];
  u32_t ll, neof, i, k;

  bp = dbuf;
  *bp++ = xch;
  neof = 0;
  do {
    if (xch < 0xc0) {
      i = 1 << (xch & 3);       /* length of length */
      if (i == 8)
        ll = 0x7fffffff;
      else
        for (k = 0, ll = 0; k < i; k++)
          ll = (ll << 8) + (*bp++ = fgetc(inf));
    } else {
      if ((ll = (*bp++ = fgetc(inf))) >= 0xc0) {
        if (ll == 0xff)
          for (k = 0, ll = 0; k < 4; k++)
            ll = (ll << 8) + (*bp++ = fgetc(inf));
        else if (ll >= 0xe0)
          ll = 1 << (ll & 31), neof = 1;
        else
          ll = ((ll & 31) << 8) + 192 + (*bp++ = fgetc(inf));
      }
    }
    k = bp - dbuf;
    bp = dbuf;
    for (;;) {
      if (outf)
        fwrite(dbuf, 1, k, outf);
      for (i = 0; i < nest; i++)
        PGP_hblk(hctx[i], dbuf, k);
      if (!ll || feof(inf))
        break;
      if (0 >= (k = fread(dbuf, 1, ll > RBSIZ ? RBSIZ : ll, inf)))
        exit(-2);
      ll -= k;
    }
  } while (neof && !feof(inf));
}

#define MAXSIGNEST 25
int PGP_delcs(FILE * inf, FILE * outf, int *sigret, u8_t * sugfile)
{
  u8_t dbuf[RBSIZ], *sigbuf = NULL, *bp, xch = 0, halg[MAXSIGNEST];
  u32_t k, ll = 0, neof = 0;
  int i, nest = 0, innest = 0;
  void *hctx[MAXSIGNEST];

  xch = fgetc(inf);             /* get sigs we process this pass */
  if ((xch & 0xfc) == 0x88) {
    sigbuf = PGP_gtpkt(inf, xch, NULL);
    halg[nest] = sigbuf[16];    /* grab algorithm */
    hctx[nest] = PGP_hini(halg[nest]);
    nest++;
  } else
    while (xch == 0xc4 || (xch & 0xfc) == 0x90) {  /* one pass sig header */
      if (!(bp = PGP_gtpkt(inf, xch, NULL)))
        return -3;
      halg[nest] = bp[2];       /* grab algorithm */
      hctx[nest] = PGP_hini(halg[nest]);
      if (++nest >= MAXSIGNEST) /* too deep ? */
        return -7;
      i = bp[12];
      free(bp);
      if (i)                    /* not nested */
        break;
      xch = fgetc(inf);
      if (xch != 0xc4 && (xch & 0xfc) != 0x90)
        return -3;
    }
  *sigret = nest;
  if (xch == 0xc4 || (xch & 0xfc) == 0x88 || (xch & 0xfc) == 0x90)
    for (;;) {
      xch = fgetc(inf);         /* skip to literal, noting prefixed sigs */
      if (xch != 0xc4 && (xch & 0xfc) != 0x88)
        break;
      if (xch == 0xc4)
        innest++;
      hashblk(nest, hctx, xch, inf, outf);
    }
  k = 1;
  if (xch == 0xcb || (xch & 0xfc) == 0xac) {
    if (xch == 0xcb)            /* literal - get length and bypass header */
      ll = PGP_nxpkt(&neof, inf);
    else {
      i = 1 << (xch & 3);       /* length of length */
      for (k = 0, ll = 0; k < i; k++)
        ll = (ll << 8) + fgetc(inf);
    }
    if (PGP_gtblk(dbuf, 2, &ll, &neof, inf))
      return -5;
    xch = dbuf[1];              /* len of suggested fname */
    if (PGP_gtblk(sugfile ? sugfile : dbuf, xch, &ll, &neof, inf))
      return -5;
    if (sugfile)
      sugfile[xch] = 0;
    if (PGP_gtblk(dbuf, 4, &ll, &neof, inf))
      return -5;
    while (ll) {
      if (0 >= (k = fread(dbuf, 1, ll > RBSIZ ? RBSIZ : ll, inf)))
        exit(-2);
      ll -= k;
      fwrite(dbuf, 1, k, outf);
      for (i = 0; i < nest; i++)
        PGP_hblk(hctx[i], dbuf, k);
      if (!ll && neof)
        ll = PGP_nxpkt(&neof, inf);
    }
    k = 0;
  } else                        /* pass through and hash */
    hashblk(nest, hctx, xch, inf, outf);

  if (!nest)
    return k;
  while (innest--)              /* hash nested signatures */
    hashblk(nest, hctx, fgetc(inf), inf, outf);
  while (nest) {
    if (!sigbuf) {
      i = fgetc(inf);
      if ((i & 0xfc) != 0x88 && i != 0xc2)
        return -1;
      sigbuf = PGP_gtpkt(inf, i, &ll);
    }
    *sigret |= PGP_sigck(sigbuf, hctx[--nest]);
    free(sigbuf);
    sigbuf = NULL;
  }
  return k;
}
