/* 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/. */ /* * SIGNTOOL * * A command line tool to create manifest files * from a directory hierarchy. It is assumed that * the tree will be equivalent to what resides * or will reside in an archive. * * */ #include "nss.h" #include "signtool.h" #include "prmem.h" #include "prio.h" /*********************************************************************** * Global Variable Definitions */ char *progName; /* argv[0] */ /* password data */ secuPWData pwdata = { PW_NONE, 0 }; /* directories or files to exclude in descent */ PLHashTable *excludeDirs = NULL; static PRBool exclusionsGiven = PR_FALSE; /* zatharus is the man who knows no time, dies tragic death */ int no_time = 0; /* -b basename of .rsa, .sf files */ char *base = DEFAULT_BASE_NAME; /* Only sign files with this extension */ PLHashTable *extensions = NULL; PRBool extensionsGiven = PR_FALSE; char *scriptdir = NULL; int verbosity = 0; PRFileDesc *outputFD = NULL, *errorFD = NULL; int errorCount = 0, warningCount = 0; int compression_level = DEFAULT_COMPRESSION_LEVEL; PRBool compression_level_specified = PR_FALSE; int xpi_arc = 0; /* Command-line arguments */ static char *genkey = NULL; static char *verify = NULL; static char *zipfile = NULL; static char *cert_dir = NULL; static int javascript = 0; static char *jartree = NULL; static char *keyName = NULL; static char *metafile = NULL; static char *install_script = NULL; static int list_certs = 0; static int list_modules = 0; static int optimize = 0; static int enableOCSP = 0; static char *tell_who = NULL; static char *outfile = NULL; static char *cmdFile = NULL; static PRBool noRecurse = PR_FALSE; static PRBool leaveArc = PR_FALSE; static int keySize = -1; static char *token = NULL; typedef enum { UNKNOWN_OPT, HELP_OPT, LONG_HELP_OPT, BASE_OPT, COMPRESSION_OPT, CERT_DIR_OPT, EXTENSION_OPT, INSTALL_SCRIPT_OPT, SCRIPTDIR_OPT, CERTNAME_OPT, LIST_OBJSIGN_CERTS_OPT, LIST_ALL_CERTS_OPT, METAFILE_OPT, OPTIMIZE_OPT, ENABLE_OCSP_OPT, PASSWORD_OPT, VERIFY_OPT, WHO_OPT, EXCLUDE_OPT, NO_TIME_OPT, JAVASCRIPT_OPT, ZIPFILE_OPT, GENKEY_OPT, MODULES_OPT, NORECURSE_OPT, SIGNDIR_OPT, OUTFILE_OPT, COMMAND_FILE_OPT, LEAVE_ARC_OPT, VERBOSITY_OPT, KEYSIZE_OPT, TOKEN_OPT, XPI_ARC_OPT } OPT_TYPE; typedef enum { DUPLICATE_OPTION_ERR = 0, OPTION_NEEDS_ARG_ERR } Error; static char *errStrings[] = { "warning: %s option specified more than once.\n" "Only last specification will be used.\n", "ERROR: option \"%s\" requires an argument.\n" }; static int ProcessOneOpt(OPT_TYPE type, char *arg); /********************************************************************* * * P r o c e s s C o m m a n d F i l e */ int ProcessCommandFile() { PRFileDesc *fd; #define CMD_FILE_BUFSIZE 1024 char buf[CMD_FILE_BUFSIZE]; char *equals; int linenum = 0; int retval = -1; OPT_TYPE type; fd = PR_Open(cmdFile, PR_RDONLY, 0777); if (!fd) { PR_fprintf(errorFD, "ERROR: Unable to open command file %s.\n"); errorCount++; return -1; } while (pr_fgets(buf, CMD_FILE_BUFSIZE, fd)) { char *eol; linenum++; /* Chop off final newline */ eol = PL_strchr(buf, '\r'); if (!eol) { eol = PL_strchr(buf, '\n'); } if (eol) *eol = '\0'; equals = PL_strchr(buf, '='); if (!equals) { continue; } *equals = '\0'; equals++; /* Now buf points to the attribute, and equals points to the value. */ /* This is pretty straightforward, just deal with whatever attribute * this is */ if (!PL_strcasecmp(buf, "basename")) { type = BASE_OPT; } else if (!PL_strcasecmp(buf, "compression")) { type = COMPRESSION_OPT; } else if (!PL_strcasecmp(buf, "certdir")) { type = CERT_DIR_OPT; } else if (!PL_strcasecmp(buf, "extension")) { type = EXTENSION_OPT; } else if (!PL_strcasecmp(buf, "generate")) { type = GENKEY_OPT; } else if (!PL_strcasecmp(buf, "installScript")) { type = INSTALL_SCRIPT_OPT; } else if (!PL_strcasecmp(buf, "javascriptdir")) { type = SCRIPTDIR_OPT; } else if (!PL_strcasecmp(buf, "htmldir")) { type = JAVASCRIPT_OPT; if (jartree) { PR_fprintf(errorFD, "warning: directory to be signed specified more than once." " Only last specification will be used.\n"); warningCount++; PR_Free(jartree); jartree = NULL; } jartree = PL_strdup(equals); } else if (!PL_strcasecmp(buf, "certname")) { type = CERTNAME_OPT; } else if (!PL_strcasecmp(buf, "signdir")) { type = SIGNDIR_OPT; } else if (!PL_strcasecmp(buf, "list")) { type = LIST_OBJSIGN_CERTS_OPT; } else if (!PL_strcasecmp(buf, "listall")) { type = LIST_ALL_CERTS_OPT; } else if (!PL_strcasecmp(buf, "metafile")) { type = METAFILE_OPT; } else if (!PL_strcasecmp(buf, "modules")) { type = MODULES_OPT; } else if (!PL_strcasecmp(buf, "optimize")) { type = OPTIMIZE_OPT; } else if (!PL_strcasecmp(buf, "ocsp")) { type = ENABLE_OCSP_OPT; } else if (!PL_strcasecmp(buf, "password")) { type = PASSWORD_OPT; } else if (!PL_strcasecmp(buf, "verify")) { type = VERIFY_OPT; } else if (!PL_strcasecmp(buf, "who")) { type = WHO_OPT; } else if (!PL_strcasecmp(buf, "exclude")) { type = EXCLUDE_OPT; } else if (!PL_strcasecmp(buf, "notime")) { type = NO_TIME_OPT; } else if (!PL_strcasecmp(buf, "jarfile")) { type = ZIPFILE_OPT; } else if (!PL_strcasecmp(buf, "outfile")) { type = OUTFILE_OPT; } else if (!PL_strcasecmp(buf, "leavearc")) { type = LEAVE_ARC_OPT; } else if (!PL_strcasecmp(buf, "verbosity")) { type = VERBOSITY_OPT; } else if (!PL_strcasecmp(buf, "keysize")) { type = KEYSIZE_OPT; } else if (!PL_strcasecmp(buf, "token")) { type = TOKEN_OPT; } else if (!PL_strcasecmp(buf, "xpi")) { type = XPI_ARC_OPT; } else { PR_fprintf(errorFD, "warning: unknown attribute \"%s\" in command file, line %d.\n", buf, linenum); warningCount++; type = UNKNOWN_OPT; } /* Process the option, whatever it is */ if (type != UNKNOWN_OPT) { if (ProcessOneOpt(type, equals) == -1) { goto finish; } } } retval = 0; finish: PR_Close(fd); return retval; } /********************************************************************* * * p a r s e _ a r g s */ static int parse_args(int argc, char *argv[]) { char *opt; char *arg; int needsInc = 0; int i; OPT_TYPE type; /* Loop over all arguments */ for (i = 1; i < argc; i++) { opt = argv[i]; arg = NULL; if (opt[0] == '-') { if (opt[1] == '-') { /* word option */ if (i < argc - 1) { needsInc = 1; arg = argv[i + 1]; } else { arg = NULL; } if (!PL_strcasecmp(opt + 2, "norecurse")) { type = NORECURSE_OPT; } else if (!PL_strcasecmp(opt + 2, "leavearc")) { type = LEAVE_ARC_OPT; } else if (!PL_strcasecmp(opt + 2, "verbosity")) { type = VERBOSITY_OPT; } else if (!PL_strcasecmp(opt + 2, "outfile")) { type = OUTFILE_OPT; } else if (!PL_strcasecmp(opt + 2, "keysize")) { type = KEYSIZE_OPT; } else if (!PL_strcasecmp(opt + 2, "token")) { type = TOKEN_OPT; } else { PR_fprintf(errorFD, "warning: unknown option: %s\n", opt); warningCount++; type = UNKNOWN_OPT; } } else { /* char option */ if (opt[2] != '\0') { arg = opt + 2; } else if (i < argc - 1) { needsInc = 1; arg = argv[i + 1]; } else { arg = NULL; } switch (opt[1]) { case 'b': type = BASE_OPT; break; case 'c': type = COMPRESSION_OPT; break; case 'd': type = CERT_DIR_OPT; break; case 'e': type = EXTENSION_OPT; break; case 'f': type = COMMAND_FILE_OPT; break; case 'h': type = HELP_OPT; break; case 'H': type = LONG_HELP_OPT; break; case 'i': type = INSTALL_SCRIPT_OPT; break; case 'j': type = SCRIPTDIR_OPT; break; case 'k': type = CERTNAME_OPT; break; case 'l': type = LIST_OBJSIGN_CERTS_OPT; break; case 'L': type = LIST_ALL_CERTS_OPT; break; case 'm': type = METAFILE_OPT; break; case 'o': type = OPTIMIZE_OPT; break; case 'O': type = ENABLE_OCSP_OPT; break; case 'p': type = PASSWORD_OPT; break; case 'v': type = VERIFY_OPT; break; case 'w': type = WHO_OPT; break; case 'x': type = EXCLUDE_OPT; break; case 'X': type = XPI_ARC_OPT; break; case 'z': type = NO_TIME_OPT; break; case 'J': type = JAVASCRIPT_OPT; break; case 'Z': type = ZIPFILE_OPT; break; case 'G': type = GENKEY_OPT; break; case 'M': type = MODULES_OPT; break; case 's': type = KEYSIZE_OPT; break; case 't': type = TOKEN_OPT; break; default: type = UNKNOWN_OPT; PR_fprintf(errorFD, "warning: unrecognized option: -%c.\n", opt[1]); warningCount++; break; } } } else { type = UNKNOWN_OPT; if (i == argc - 1) { if (jartree) { PR_fprintf(errorFD, "warning: directory to be signed specified more than once.\n" " Only last specification will be used.\n"); warningCount++; PR_Free(jartree); jartree = NULL; } jartree = PL_strdup(opt); } else { PR_fprintf(errorFD, "warning: unrecognized option: %s\n", opt); warningCount++; } } if (type != UNKNOWN_OPT) { short ateArg = ProcessOneOpt(type, arg); if (ateArg == -1) { /* error */ return -1; } if (ateArg && needsInc) { i++; } } } return 0; } /********************************************************************* * * P r o c e s s O n e O p t * * Since options can come from different places (command file, word options, * char options), this is a central function that is called to deal with * them no matter where they come from. * * type is the type of option. * arg is the argument to the option, possibly NULL. * Returns 1 if the argument was eaten, 0 if it wasn't, and -1 for error. */ static int ProcessOneOpt(OPT_TYPE type, char *arg) { int ate = 0; switch (type) { case HELP_OPT: Usage(); break; case LONG_HELP_OPT: LongUsage(); break; case BASE_OPT: if (base) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-b"); warningCount++; PR_Free(base); base = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-b"); errorCount++; goto loser; } base = PL_strdup(arg); ate = 1; break; case COMPRESSION_OPT: if (compression_level_specified) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-c"); warningCount++; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-c"); errorCount++; goto loser; } compression_level = atoi(arg); compression_level_specified = PR_TRUE; ate = 1; break; case CERT_DIR_OPT: if (cert_dir) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-d"); warningCount++; PR_Free(cert_dir); cert_dir = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-d"); errorCount++; goto loser; } cert_dir = PL_strdup(arg); ate = 1; break; case EXTENSION_OPT: if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "extension (-e)"); errorCount++; goto loser; } PL_HashTableAdd(extensions, arg, arg); extensionsGiven = PR_TRUE; ate = 1; break; case INSTALL_SCRIPT_OPT: if (install_script) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "installScript (-i)"); warningCount++; PR_Free(install_script); install_script = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "installScript (-i)"); errorCount++; goto loser; } install_script = PL_strdup(arg); ate = 1; break; case SCRIPTDIR_OPT: if (scriptdir) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "javascriptdir (-j)"); warningCount++; PR_Free(scriptdir); scriptdir = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "javascriptdir (-j)"); errorCount++; goto loser; } scriptdir = PL_strdup(arg); ate = 1; break; case CERTNAME_OPT: if (keyName) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "keyName (-k)"); warningCount++; PR_Free(keyName); keyName = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "keyName (-k)"); errorCount++; goto loser; } keyName = PL_strdup(arg); ate = 1; break; case LIST_OBJSIGN_CERTS_OPT: case LIST_ALL_CERTS_OPT: if (list_certs != 0) { PR_fprintf(errorFD, "warning: only one of -l and -L may be specified.\n"); warningCount++; } list_certs = (type == LIST_OBJSIGN_CERTS_OPT ? 1 : 2); break; case METAFILE_OPT: if (metafile) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "metafile (-m)"); warningCount++; PR_Free(metafile); metafile = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "metafile (-m)"); errorCount++; goto loser; } metafile = PL_strdup(arg); ate = 1; break; case OPTIMIZE_OPT: optimize = 1; break; case ENABLE_OCSP_OPT: enableOCSP = 1; break; case PASSWORD_OPT: if (pwdata.data) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "password (-p)"); warningCount++; PR_Free(pwdata.data); pwdata.data = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "password (-p)"); errorCount++; goto loser; } pwdata.source = PW_PLAINTEXT; pwdata.data = PL_strdup(arg); ate = 1; break; case VERIFY_OPT: if (verify) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "verify (-v)"); warningCount++; PR_Free(verify); verify = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "verify (-v)"); errorCount++; goto loser; } verify = PL_strdup(arg); ate = 1; break; case WHO_OPT: if (tell_who) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "who (-v)"); warningCount++; PR_Free(tell_who); tell_who = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "who (-w)"); errorCount++; goto loser; } tell_who = PL_strdup(arg); ate = 1; break; case EXCLUDE_OPT: if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "exclude (-x)"); errorCount++; goto loser; } PL_HashTableAdd(excludeDirs, arg, arg); exclusionsGiven = PR_TRUE; ate = 1; break; case NO_TIME_OPT: no_time = 1; break; case JAVASCRIPT_OPT: javascript++; break; case ZIPFILE_OPT: if (zipfile) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "jarfile (-Z)"); warningCount++; PR_Free(zipfile); zipfile = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "jarfile (-Z)"); errorCount++; goto loser; } zipfile = PL_strdup(arg); ate = 1; break; case GENKEY_OPT: if (genkey) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "generate (-G)"); warningCount++; PR_Free(genkey); genkey = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "generate (-G)"); errorCount++; goto loser; } genkey = PL_strdup(arg); ate = 1; break; case MODULES_OPT: list_modules++; break; case SIGNDIR_OPT: if (jartree) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "signdir"); warningCount++; PR_Free(jartree); jartree = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "signdir"); errorCount++; goto loser; } jartree = PL_strdup(arg); ate = 1; break; case OUTFILE_OPT: if (outfile) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "outfile"); warningCount++; PR_Free(outfile); outfile = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "outfile"); errorCount++; goto loser; } outfile = PL_strdup(arg); ate = 1; break; case COMMAND_FILE_OPT: if (cmdFile) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-f"); warningCount++; PR_Free(cmdFile); cmdFile = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-f"); errorCount++; goto loser; } cmdFile = PL_strdup(arg); ate = 1; break; case NORECURSE_OPT: noRecurse = PR_TRUE; break; case LEAVE_ARC_OPT: leaveArc = PR_TRUE; break; case VERBOSITY_OPT: if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "--verbosity"); errorCount++; goto loser; } verbosity = atoi(arg); ate = 1; break; case KEYSIZE_OPT: if (keySize != -1) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-s"); warningCount++; } keySize = atoi(arg); ate = 1; if (keySize < 1 || keySize > MAX_RSA_KEY_SIZE) { PR_fprintf(errorFD, "Invalid key size: %d.\n", keySize); errorCount++; goto loser; } break; case TOKEN_OPT: if (token) { PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-t"); PR_Free(token); token = NULL; } if (!arg) { PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-t"); errorCount++; goto loser; } token = PL_strdup(arg); ate = 1; break; case XPI_ARC_OPT: xpi_arc = 1; break; default: PR_fprintf(errorFD, "warning: unknown option\n"); warningCount++; break; } return ate; loser: return -1; } /********************************************************************* * * m a i n */ int main(int argc, char *argv[]) { PRBool readOnly; int retval = 0; outputFD = PR_STDOUT; errorFD = PR_STDERR; progName = argv[0]; if (argc < 2) { Usage(); } excludeDirs = PL_NewHashTable(10, PL_HashString, PL_CompareStrings, PL_CompareStrings, NULL, NULL); extensions = PL_NewHashTable(10, PL_HashString, PL_CompareStrings, PL_CompareStrings, NULL, NULL); if (parse_args(argc, argv)) { retval = -1; goto cleanup; } /* Parse the command file if one was given */ if (cmdFile) { if (ProcessCommandFile()) { retval = -1; goto cleanup; } } /* Set up output redirection */ if (outfile) { if (PR_Access(outfile, PR_ACCESS_EXISTS) == PR_SUCCESS) { /* delete the file if it is already present */ PR_fprintf(errorFD, "warning: %s already exists and will be overwritten.\n", outfile); warningCount++; if (PR_Delete(outfile) != PR_SUCCESS) { PR_fprintf(errorFD, "ERROR: unable to delete %s.\n", outfile); errorCount++; exit(ERRX); } } outputFD = PR_Open(outfile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0777); if (!outputFD) { PR_fprintf(errorFD, "ERROR: Unable to create %s.\n", outfile); errorCount++; exit(ERRX); } errorFD = outputFD; } /* This seems to be a fairly common user error */ if (verify && list_certs > 0) { PR_fprintf(errorFD, "%s: Can't use -l and -v at the same time\n", PROGRAM_NAME); errorCount++; retval = -1; goto cleanup; } /* -J assumes -Z now */ if (javascript && zipfile) { PR_fprintf(errorFD, "%s: Can't use -J and -Z at the same time\n", PROGRAM_NAME); PR_fprintf(errorFD, "%s: -J option will create the jar files for you\n", PROGRAM_NAME); errorCount++; retval = -1; goto cleanup; } /* -X needs -Z */ if (xpi_arc && !zipfile) { PR_fprintf(errorFD, "%s: option XPI (-X) requires option jarfile (-Z)\n", PROGRAM_NAME); errorCount++; retval = -1; goto cleanup; } /* Less common mixing of -L with various options */ if (list_certs > 0 && (tell_who || zipfile || javascript || scriptdir || extensionsGiven || exclusionsGiven || install_script)) { PR_fprintf(errorFD, "%s: Can't use -l or -L with that option\n", PROGRAM_NAME); errorCount++; retval = -1; goto cleanup; } if (!cert_dir) cert_dir = get_default_cert_dir(); VerifyCertDir(cert_dir, keyName); if (compression_level < MIN_COMPRESSION_LEVEL || compression_level > MAX_COMPRESSION_LEVEL) { PR_fprintf(errorFD, "Compression level must be between %d and %d.\n", MIN_COMPRESSION_LEVEL, MAX_COMPRESSION_LEVEL); errorCount++; retval = -1; goto cleanup; } if (jartree && !keyName) { PR_fprintf(errorFD, "You must specify a key with which to sign.\n"); errorCount++; retval = -1; goto cleanup; } readOnly = (genkey == NULL); /* only key generation requires write */ if (InitCrypto(cert_dir, readOnly)) { PR_fprintf(errorFD, "ERROR: Cryptographic initialization failed.\n"); errorCount++; retval = -1; goto cleanup; } if (enableOCSP) { SECStatus rv = CERT_EnableOCSPChecking(CERT_GetDefaultCertDB()); if (rv != SECSuccess) { PR_fprintf(errorFD, "ERROR: Attempt to enable OCSP Checking failed.\n"); errorCount++; retval = -1; } } if (verify) { if (VerifyJar(verify)) { errorCount++; retval = -1; goto cleanup; } } else if (list_certs) { if (ListCerts(keyName, list_certs)) { errorCount++; retval = -1; goto cleanup; } } else if (list_modules) { JarListModules(); } else if (genkey) { if (GenerateCert(genkey, keySize, token)) { errorCount++; retval = -1; goto cleanup; } } else if (tell_who) { if (JarWho(tell_who)) { errorCount++; retval = -1; goto cleanup; } } else if (javascript && jartree) { /* make sure directory exists */ PRDir *dir; dir = PR_OpenDir(jartree); if (!dir) { PR_fprintf(errorFD, "ERROR: unable to open directory %s.\n", jartree); errorCount++; retval = -1; goto cleanup; } else { PR_CloseDir(dir); } /* undo junk from prior runs of signtool*/ if (RemoveAllArc(jartree)) { PR_fprintf(errorFD, "Error removing archive directories under %s\n", jartree); errorCount++; retval = -1; goto cleanup; } /* traverse all the htm|html files in the directory */ if (InlineJavaScript(jartree, !noRecurse)) { retval = -1; goto cleanup; } /* sign any resultant .arc directories created in above step */ if (SignAllArc(jartree, keyName, javascript, metafile, install_script, optimize, !noRecurse)) { retval = -1; goto cleanup; } if (!leaveArc) { RemoveAllArc(jartree); } if (errorCount > 0 || warningCount > 0) { PR_fprintf(outputFD, "%d error%s, %d warning%s.\n", errorCount, errorCount == 1 ? "" : "s", warningCount, warningCount == 1 ? "" : "s"); } else { PR_fprintf(outputFD, "Directory %s signed successfully.\n", jartree); } } else if (jartree) { SignArchive(jartree, keyName, zipfile, javascript, metafile, install_script, optimize, !noRecurse); } else Usage(); cleanup: if (extensions) { PL_HashTableDestroy(extensions); extensions = NULL; } if (excludeDirs) { PL_HashTableDestroy(excludeDirs); excludeDirs = NULL; } if (outputFD != PR_STDOUT) { PR_Close(outputFD); } rm_dash_r(TMP_OUTPUT); if (retval == 0) { if (NSS_Shutdown() != SECSuccess) { exit(1); } } return retval; }