#include "system.h"
#include <gcrypt.h>
#include "rpmio_internal.h"
#include "popt.h"
#include "debug.h"

static pgpHashAlgo hashalgo = PGPHASHALGO_MD5;
static rpmDigestFlags flags = RPMDIGEST_NONE;
extern int _rpmio_debug;

static int fips = 0;
static int gcrypt = 0;

const char * FIPSAdigest = "a9993e364706816aba3e25717850c26c9cd0d89d";
const char * FIPSBdigest = "84983e441c3bd26ebaae4aa1f95129e5e54670f1";
const char * FIPSCdigest = "34aa973cd4c4daa4f61eeb2bdbad27316534016f";

static struct poptOption optionsTable[] = {
 { "md5", '\0', POPT_ARG_VAL, 	&hashalgo, PGPHASHALGO_MD5,	NULL, NULL },
 { "sha1",'\0', POPT_ARG_VAL, 	&hashalgo, PGPHASHALGO_SHA1,	NULL, NULL },
#ifdef	DYING
 { "reverse",'\0', POPT_BIT_SET, &flags, RPMDIGEST_REVERSE,	NULL, NULL },
#endif
 { "fipsa",'\0', POPT_ARG_VAL, &fips, 1,	NULL, NULL },
 { "fipsb",'\0', POPT_ARG_VAL, &fips, 2,	NULL, NULL },
 { "fipsc",'\0', POPT_ARG_VAL, &fips, 3,	NULL, NULL },
 { "gcrypt",'\0', POPT_ARG_VAL, &gcrypt, 1,	NULL, NULL },
 { "debug",'d', POPT_ARG_VAL, &_rpmio_debug, -1,	NULL, NULL },
  POPT_AUTOHELP
  POPT_TABLEEND
};

#define	SHA1_CMD	"/usr/bin/sha1sum"
#define	MD5_CMD		"/usr/bin/md5sum"

int
main(int argc, const char *argv[])
{
    poptContext optCon;
    const char ** args;
    const char * ifn;
    const char * ofn = "/dev/null";
    DIGEST_CTX ctx = NULL;
    GcryMDHd gcry = NULL;
    const char * idigest;
    const char * odigest;
    const char * sdigest;
    const char * digest;
    size_t digestlen;
    int asAscii = 1;
    int reverse = 0;
    int rc;
    char appendix;
    int i;

    optCon = poptGetContext(argv[0], argc, argv, optionsTable, 0);
    while ((rc = poptGetNextOpt(optCon)) > 0)
	;

    if (fips) {
	struct rpmsw_s begin, end;
	if (gcrypt)
	    gcry = gcry_md_open(GCRY_MD_SHA1, 0);

	(void) rpmswNow(&begin);

	if (gcrypt)
	    gcry_md_reset(gcry);
	else
	    ctx = rpmDigestInit(PGPHASHALGO_SHA1, flags);
	ifn = NULL;
	appendix = ' ';
	sdigest = NULL;
	switch (fips) {
	case 1:
	    ifn = "abc";
	    if (gcrypt)
		gcry_md_write (gcry, ifn, strlen(ifn));
	    else
		rpmDigestUpdate(ctx, ifn, strlen(ifn));
	    sdigest = FIPSAdigest;
	    appendix = 'A';
	    break;
	case 2:
	    ifn = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
	    if (gcrypt)
		gcry_md_write (gcry, ifn, strlen(ifn));
	    else
		rpmDigestUpdate(ctx, ifn, strlen(ifn));
	    sdigest = FIPSBdigest;
	    appendix = 'B';
	    break;
	case 3:
	    ifn = "aaaaaaaaaaa ...";
	    for (i = 0; i < 1000000; i++) {
		if (gcrypt)
		    gcry_md_write (gcry, ifn, 1);
		else
		    rpmDigestUpdate(ctx, ifn, 1);
	    }
	    sdigest = FIPSCdigest;
	    appendix = 'C';
	    break;
	}
	if (ifn == NULL)
	    return 1;
	if (gcrypt) {
	    const unsigned char * s = gcry_md_read (gcry, 0);
	    char * t;

	    gcry_md_close(gcry);
	    digestlen = 2*20;
	    digest = t = xcalloc(1, digestlen+1);
	    for (i = 0; i < digestlen; i += 2) {
		static const char hex[] = "0123456789abcdef";
		*t++ = hex[ (unsigned)((*s >> 4) & 0x0f) ];
		*t++ = hex[ (unsigned)((*s++   ) & 0x0f) ];
	    }
	    *t = '\0';
	} else
	    rpmDigestFinal(ctx, (void **)&digest, &digestlen, asAscii);
	(void) rpmswNow(&end);

	if (digest) {
	    fprintf(stdout, "%s     %s\n", digest, ifn);
	    fflush(stdout);
	    free((void *)digest);
	}
	if (sdigest) {
	    fprintf(stdout, "%s     FIPS PUB 180-1 Appendix %c\n", sdigest,
		appendix);
	    fflush(stdout);
	}
fprintf(stderr, "*** time %lu usecs\n", (unsigned long)rpmswDiff(&end, &begin));
	return 0;
    }

    args = poptGetArgs(optCon);
    rc = 0;
    if (args)
    while ((ifn = *args++) != NULL) {
	FD_t ifd;
	FD_t ofd;
	unsigned char buf[BUFSIZ];
	ssize_t nb;

	sdigest = NULL;
	{   char *se;
	    FILE * sfp;

	    se = buf;
	    *se = '\0';
	    se = stpcpy(se, ((hashalgo == PGPHASHALGO_SHA1) ? SHA1_CMD : MD5_CMD));
	    *se++ = ' ';
	    se = stpcpy(se, ifn);
	    if ((sfp = popen(buf, "r")) != NULL) {
		fgets(buf, sizeof(buf), sfp);
		if ((se = strchr(buf, ' ')) != NULL)
		    *se = '\0';
		sdigest = xstrdup(buf);
		pclose(sfp);
	    }
	}

	ifd = Fopen(ifn, "r.ufdio");
	if (ifd == NULL || Ferror(ifd)) {
	    fprintf(stderr, _("cannot open %s: %s\n"), ifn, Fstrerror(ifd));
	    if (ifd) Fclose(ifd);
	    rc++;
	    continue;
	}
	idigest = NULL;
	fdInitDigest(ifd, hashalgo, reverse);

	ofd = Fopen(ofn, "w.ufdio");
	if (ofd == NULL || Ferror(ofd)) {
	    fprintf(stderr, _("cannot open %s: %s\n"), ofn, Fstrerror(ofd));
	    if (ifd) Fclose(ifd);
	    if (ofd) Fclose(ofd);
	    rc++;
	    continue;
	}
	odigest = NULL;
	fdInitDigest(ofd, hashalgo, reverse);

	ctx = rpmDigestInit(hashalgo, flags);

	while ((nb = Fread(buf, 1, sizeof(buf), ifd)) > 0) {
	    rpmDigestUpdate(ctx, buf, nb);
	    (void) Fwrite(buf, 1, nb, ofd);
	}

	fdFiniDigest(ifd, hashalgo, (void **)&idigest, NULL, asAscii);
	Fclose(ifd);

	Fflush(ofd);
	fdFiniDigest(ofd, hashalgo, (void **)&odigest, NULL, asAscii);
	Fclose(ofd);

	rpmDigestFinal(ctx, (void **)&digest, &digestlen, asAscii);

	if (digest) {
	    fprintf(stdout, "%s     %s\n", digest, ifn);
	    fflush(stdout);
	    free((void *)digest);
	}
	if (idigest) {
	    fprintf(stdout, "%s in  %s\n", idigest, ifn);
	    fflush(stdout);
	    free((void *)idigest);
	}
	if (odigest) {
	    fprintf(stdout, "%s out %s\n", odigest, ofn);
	    fflush(stdout);
	    free((void *)odigest);
	}
	if (sdigest) {
	    fprintf(stdout, "%s cmd %s\n", sdigest, ifn);
	    fflush(stdout);
	    free((void *)sdigest);
	}
    }
    return rc;
}
