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