/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "signtool.h"


static int	jar_cb(int status, JAR *jar, const char *metafile, 
char *pathname, char *errortext);
static int	verify_global (JAR *jar);

/*************************************************************************
 *
 * V e r i f y J a r
 */
int
VerifyJar(char *filename)
{
    FILE * fp;

    int	ret;
    int	status;
    int	failed = 0;
    char	*err;

    JAR * jar;
    JAR_Context * ctx;

    JAR_Item * it;

    jar = JAR_new();

    if ((fp = fopen (filename, "r")) == NULL) {
	perror (filename);
	exit (ERRX);
    } else
	fclose (fp);

    JAR_set_callback (JAR_CB_SIGNAL, jar, jar_cb);


    status = JAR_pass_archive (jar, jarArchGuess, filename, "some-url");

    if (status < 0 || jar->valid < 0) {
	failed = 1;
	PR_fprintf(outputFD, 
	    "\nNOTE -- \"%s\" archive DID NOT PASS crypto verification.\n",
	     filename);
	if (status < 0) {
	    const char	*errtext;

	    if (status >= JAR_BASE && status <= JAR_BASE_END) {
		errtext = JAR_get_error (status);
	    } else {
		errtext = SECU_Strerror(PORT_GetError());
	    }

	    PR_fprintf(outputFD, "  (reported reason: %s)\n\n",
	         errtext);

	    /* corrupt files should not have their contents listed */

	    if (status == JAR_ERR_CORRUPT)
		return - 1;
	}
	PR_fprintf(outputFD,
	    "entries shown below will have their digests checked only.\n");
	jar->valid = 0;
    } else
	PR_fprintf(outputFD,
	    "archive \"%s\" has passed crypto verification.\n", filename);

    if (verify_global (jar))
	failed = 1;

    PR_fprintf(outputFD, "\n");
    PR_fprintf(outputFD, "%16s   %s\n", "status", "path");
    PR_fprintf(outputFD, "%16s   %s\n", "------------", "-------------------");

    ctx = JAR_find (jar, NULL, jarTypeMF);

    while (JAR_find_next (ctx, &it) >= 0) {
	if (it && it->pathname) {
	    rm_dash_r(TMP_OUTPUT);
	    ret = JAR_verified_extract (jar, it->pathname, TMP_OUTPUT);
	    /* if (ret < 0) printf ("error %d on %s\n", ret, it->pathname); */
	    if (ret < 0) 
		failed = 1;

	    if (ret == JAR_ERR_PNF)
		err = "NOT PRESENT";
	    else if (ret == JAR_ERR_HASH)
		err = "HASH FAILED";
	    else
		err = "NOT VERIFIED";

	    PR_fprintf(outputFD, "%16s   %s\n", 
	        ret >= 0 ? "verified" : err, it->pathname);

	    if (ret != 0 && ret != JAR_ERR_PNF && ret != JAR_ERR_HASH)
		PR_fprintf(outputFD, "      (reason: %s)\n",
		     JAR_get_error (ret));
	}
    }

    JAR_find_end (ctx);

    if (status < 0 || jar->valid < 0) {
	failed = 1;
	PR_fprintf(outputFD,
	    "\nNOTE -- \"%s\" archive DID NOT PASS crypto verification.\n",
	     filename);
	give_help (status);
    }

    JAR_destroy (jar);

    if (failed)
	return - 1;
    return 0;
}


/***************************************************************************
 *
 * v e r i f y _ g l o b a l
 */
static int	
verify_global (JAR *jar)
{
    FILE        * fp;
    JAR_Context * ctx;
    JAR_Item    * it;
    JAR_Digest  * globaldig;
    char	* ext;
    unsigned char *md5_digest, *sha1_digest;
    unsigned int  sha1_length, md5_length;
    int	          retval = 0;
    char	  buf [BUFSIZ];

    ctx = JAR_find (jar, "*", jarTypePhy);

    while (JAR_find_next (ctx, &it) >= 0) {
	if (!PORT_Strncmp (it->pathname, "META-INF", 8)) {
	    for (ext = it->pathname; *ext; ext++)
		;
	    while (ext > it->pathname && *ext != '.') 
		ext--;

	    if (verbosity >= 0) {
		if (!PORT_Strcasecmp (ext, ".rsa")) {
		    PR_fprintf(outputFD, "found a RSA signature file: %s\n",
		         				  it->pathname);
		}

		if (!PORT_Strcasecmp (ext, ".dsa")) {
		    PR_fprintf(outputFD, "found a DSA signature file: %s\n",
		         				  it->pathname);
		}

		if (!PORT_Strcasecmp (ext, ".mf")) {
		    PR_fprintf(outputFD,
		        "found a MF master manifest file: %s\n",
		         it->pathname);
		}
	    }

	    if (!PORT_Strcasecmp (ext, ".sf")) {
		if (verbosity >= 0) {
		    PR_fprintf(outputFD,
		        "found a SF signature manifest file: %s\n",
		         it->pathname);
		}

		rm_dash_r(TMP_OUTPUT);
		if (JAR_extract (jar, it->pathname, TMP_OUTPUT) < 0) {
		    PR_fprintf(errorFD, "%s: error extracting %s\n",
		         PROGRAM_NAME, it->pathname);
		    errorCount++;
		    retval = -1;
		    continue;
		}

		md5_digest = NULL;
		sha1_digest = NULL;

		if ((fp = fopen (TMP_OUTPUT, "rb")) != NULL) {
		    while (fgets (buf, BUFSIZ, fp)) {
			char	*s;

			if (*buf == 0 || *buf == '\n' || *buf == '\r') 
			    break;

			for (s = buf; *s && *s != '\n' && *s != '\r'; s++)
			    ;
			*s = 0;

			if (!PORT_Strncmp (buf, "MD5-Digest: ", 12)) {
			    md5_digest = 
				ATOB_AsciiToData (buf + 12, &md5_length);
			}
			if (!PORT_Strncmp (buf, "SHA1-Digest: ", 13)) {
			    sha1_digest = 
				ATOB_AsciiToData (buf + 13, &sha1_length);
			}
			if (!PORT_Strncmp (buf, "SHA-Digest: ", 12)) {
			    sha1_digest = 
				ATOB_AsciiToData (buf + 12, &sha1_length);
			}
		    }

		    globaldig = jar->globalmeta;

		    if (globaldig && md5_digest && verbosity >= 0) {
			PR_fprintf(outputFD,
			   "  md5 digest on global metainfo: %s\n",
			    PORT_Memcmp(md5_digest, globaldig->md5, MD5_LENGTH)
			    ? "no match" : "match");
		    }

		    if (globaldig && sha1_digest && verbosity >= 0) {
			PR_fprintf(outputFD,
			    "  sha digest on global metainfo: %s\n",
			    PORT_Memcmp(sha1_digest, globaldig->sha1, SHA1_LENGTH) 
			    ? "no match" : "match");
		    }

		    if (globaldig == NULL && verbosity >= 0) {
			PR_fprintf(outputFD,
			     "global metadigest is not available, strange.\n");
		    }

		    fclose (fp);
		}
	    }
	}
    }

    JAR_find_end (ctx);

    return retval;
}


/************************************************************************
 *
 * J a r W h o
 */
int
JarWho(char *filename)
{
    FILE * fp;

    JAR * jar;
    JAR_Context * ctx;

    int	status;
    int	retval = 0;

    JAR_Item * it;
    JAR_Cert * fing;

    CERTCertificate * cert, *prev = NULL;

    jar = JAR_new();

    if ((fp = fopen (filename, "r")) == NULL) {
	perror (filename);
	exit (ERRX);
    } 
    fclose (fp);

    status = JAR_pass_archive (jar, jarArchGuess, filename, "some-url");

    if (status < 0 || jar->valid < 0) {
	PR_fprintf(outputFD,
	    "NOTE -- \"%s\" archive DID NOT PASS crypto verification.\n",
	     filename);
	retval = -1;
	if (jar->valid < 0 || status != -1) {
	    const char	*errtext;

	    if (status >= JAR_BASE && status <= JAR_BASE_END) {
		errtext = JAR_get_error (status);
	    } else {
		errtext = SECU_Strerror(PORT_GetError());
	    }

	    PR_fprintf(outputFD, "  (reported reason: %s)\n\n", errtext);
	}
    }

    PR_fprintf(outputFD, "\nSigner information:\n\n");

    ctx = JAR_find (jar, NULL, jarTypeSign);

    while (JAR_find_next (ctx, &it) >= 0) {
	fing = (JAR_Cert * ) it->data;
	cert = fing->cert;

	if (cert) {
	    if (prev == cert)
		break;

	    if (cert->nickname)
		PR_fprintf(outputFD, "nickname: %s\n", cert->nickname);
	    if (cert->subjectName)
		PR_fprintf(outputFD, "subject name: %s\n",
		     cert->subjectName);
	    if (cert->issuerName)
		PR_fprintf(outputFD, "issuer name: %s\n", cert->issuerName);
	} else {
	    PR_fprintf(outputFD, "no certificate could be found\n");
	    retval = -1;
	}

	prev = cert;
    }

    JAR_find_end (ctx);

    JAR_destroy (jar);
    return retval;
}


/************************************************************************
 * j a r _ c b
 */
static int	jar_cb(int status, JAR *jar, const char *metafile,
char *pathname, char *errortext)
{
    PR_fprintf(errorFD, "error %d: %s IN FILE %s\n", status, errortext,
         pathname);
    errorCount++;
    return 0;
}


