tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

signtool.c (32403B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /*
      6 *  SIGNTOOL
      7 *
      8 *  A command line tool to create manifest files
      9 *  from a directory hierarchy. It is assumed that
     10 *  the tree will be equivalent to what resides
     11 *  or will reside in an archive.
     12 *
     13 *
     14 */
     15 
     16 #include "nss.h"
     17 #include "signtool.h"
     18 #include "prmem.h"
     19 #include "prio.h"
     20 
     21 /***********************************************************************
     22 * Global Variable Definitions
     23 */
     24 char *progName; /* argv[0] */
     25 
     26 /* password data */
     27 secuPWData pwdata = { PW_NONE, 0 };
     28 
     29 /* directories or files to exclude in descent */
     30 PLHashTable *excludeDirs = NULL;
     31 static PRBool exclusionsGiven = PR_FALSE;
     32 
     33 /* zatharus is the man who knows no time, dies tragic death */
     34 int no_time = 0;
     35 
     36 /* -b basename of .rsa, .sf files */
     37 char *base = DEFAULT_BASE_NAME;
     38 
     39 /* Only sign files with this extension */
     40 PLHashTable *extensions = NULL;
     41 PRBool extensionsGiven = PR_FALSE;
     42 
     43 char *scriptdir = NULL;
     44 
     45 int verbosity = 0;
     46 
     47 PRFileDesc *outputFD = NULL, *errorFD = NULL;
     48 
     49 int errorCount = 0, warningCount = 0;
     50 
     51 int compression_level = DEFAULT_COMPRESSION_LEVEL;
     52 PRBool compression_level_specified = PR_FALSE;
     53 
     54 int xpi_arc = 0;
     55 
     56 /* Command-line arguments */
     57 static char *genkey = NULL;
     58 static char *verify = NULL;
     59 static char *zipfile = NULL;
     60 static char *cert_dir = NULL;
     61 static int javascript = 0;
     62 static char *jartree = NULL;
     63 static char *keyName = NULL;
     64 static char *metafile = NULL;
     65 static char *install_script = NULL;
     66 static int list_certs = 0;
     67 static int list_modules = 0;
     68 static int optimize = 0;
     69 static int enableOCSP = 0;
     70 static char *tell_who = NULL;
     71 static char *outfile = NULL;
     72 static char *cmdFile = NULL;
     73 static PRBool noRecurse = PR_FALSE;
     74 static PRBool leaveArc = PR_FALSE;
     75 static int keySize = -1;
     76 static char *token = NULL;
     77 
     78 typedef enum {
     79    UNKNOWN_OPT,
     80    HELP_OPT,
     81    LONG_HELP_OPT,
     82    BASE_OPT,
     83    COMPRESSION_OPT,
     84    CERT_DIR_OPT,
     85    EXTENSION_OPT,
     86    INSTALL_SCRIPT_OPT,
     87    SCRIPTDIR_OPT,
     88    CERTNAME_OPT,
     89    LIST_OBJSIGN_CERTS_OPT,
     90    LIST_ALL_CERTS_OPT,
     91    METAFILE_OPT,
     92    OPTIMIZE_OPT,
     93    ENABLE_OCSP_OPT,
     94    PASSWORD_OPT,
     95    VERIFY_OPT,
     96    WHO_OPT,
     97    EXCLUDE_OPT,
     98    NO_TIME_OPT,
     99    JAVASCRIPT_OPT,
    100    ZIPFILE_OPT,
    101    GENKEY_OPT,
    102    MODULES_OPT,
    103    NORECURSE_OPT,
    104    SIGNDIR_OPT,
    105    OUTFILE_OPT,
    106    COMMAND_FILE_OPT,
    107    LEAVE_ARC_OPT,
    108    VERBOSITY_OPT,
    109    KEYSIZE_OPT,
    110    TOKEN_OPT,
    111    XPI_ARC_OPT
    112 }
    113 
    114 OPT_TYPE;
    115 
    116 typedef enum {
    117    DUPLICATE_OPTION_ERR = 0,
    118    OPTION_NEEDS_ARG_ERR
    119 }
    120 
    121 Error;
    122 
    123 static char *errStrings[] = {
    124    "warning: %s option specified more than once.\n"
    125    "Only last specification will be used.\n",
    126    "ERROR: option \"%s\" requires an argument.\n"
    127 };
    128 
    129 static int ProcessOneOpt(OPT_TYPE type, char *arg);
    130 
    131 /*********************************************************************
    132 *
    133 * P r o c e s s C o m m a n d F i l e
    134 */
    135 int
    136 ProcessCommandFile()
    137 {
    138    PRFileDesc *fd;
    139 #define CMD_FILE_BUFSIZE 1024
    140    char buf[CMD_FILE_BUFSIZE];
    141    char *equals;
    142    int linenum = 0;
    143    int retval = -1;
    144    OPT_TYPE type;
    145 
    146    fd = PR_Open(cmdFile, PR_RDONLY, 0777);
    147    if (!fd) {
    148        PR_fprintf(errorFD, "ERROR: Unable to open command file %s.\n");
    149        errorCount++;
    150        return -1;
    151    }
    152 
    153    while (pr_fgets(buf, CMD_FILE_BUFSIZE, fd)) {
    154        char *eol;
    155        linenum++;
    156 
    157        /* Chop off final newline */
    158        eol = PL_strchr(buf, '\r');
    159        if (!eol) {
    160            eol = PL_strchr(buf, '\n');
    161        }
    162        if (eol)
    163            *eol = '\0';
    164 
    165        equals = PL_strchr(buf, '=');
    166        if (!equals) {
    167            continue;
    168        }
    169 
    170        *equals = '\0';
    171        equals++;
    172 
    173        /* Now buf points to the attribute, and equals points to the value. */
    174 
    175        /* This is pretty straightforward, just deal with whatever attribute
    176         * this is */
    177        if (!PL_strcasecmp(buf, "basename")) {
    178            type = BASE_OPT;
    179        } else if (!PL_strcasecmp(buf, "compression")) {
    180            type = COMPRESSION_OPT;
    181        } else if (!PL_strcasecmp(buf, "certdir")) {
    182            type = CERT_DIR_OPT;
    183        } else if (!PL_strcasecmp(buf, "extension")) {
    184            type = EXTENSION_OPT;
    185        } else if (!PL_strcasecmp(buf, "generate")) {
    186            type = GENKEY_OPT;
    187        } else if (!PL_strcasecmp(buf, "installScript")) {
    188            type = INSTALL_SCRIPT_OPT;
    189        } else if (!PL_strcasecmp(buf, "javascriptdir")) {
    190            type = SCRIPTDIR_OPT;
    191        } else if (!PL_strcasecmp(buf, "htmldir")) {
    192            type = JAVASCRIPT_OPT;
    193            if (jartree) {
    194                PR_fprintf(errorFD,
    195                           "warning: directory to be signed specified more than once."
    196                           " Only last specification will be used.\n");
    197                warningCount++;
    198                PR_Free(jartree);
    199                jartree = NULL;
    200            }
    201            jartree = PL_strdup(equals);
    202        } else if (!PL_strcasecmp(buf, "certname")) {
    203            type = CERTNAME_OPT;
    204        } else if (!PL_strcasecmp(buf, "signdir")) {
    205            type = SIGNDIR_OPT;
    206        } else if (!PL_strcasecmp(buf, "list")) {
    207            type = LIST_OBJSIGN_CERTS_OPT;
    208        } else if (!PL_strcasecmp(buf, "listall")) {
    209            type = LIST_ALL_CERTS_OPT;
    210        } else if (!PL_strcasecmp(buf, "metafile")) {
    211            type = METAFILE_OPT;
    212        } else if (!PL_strcasecmp(buf, "modules")) {
    213            type = MODULES_OPT;
    214        } else if (!PL_strcasecmp(buf, "optimize")) {
    215            type = OPTIMIZE_OPT;
    216        } else if (!PL_strcasecmp(buf, "ocsp")) {
    217            type = ENABLE_OCSP_OPT;
    218        } else if (!PL_strcasecmp(buf, "password")) {
    219            type = PASSWORD_OPT;
    220        } else if (!PL_strcasecmp(buf, "verify")) {
    221            type = VERIFY_OPT;
    222        } else if (!PL_strcasecmp(buf, "who")) {
    223            type = WHO_OPT;
    224        } else if (!PL_strcasecmp(buf, "exclude")) {
    225            type = EXCLUDE_OPT;
    226        } else if (!PL_strcasecmp(buf, "notime")) {
    227            type = NO_TIME_OPT;
    228        } else if (!PL_strcasecmp(buf, "jarfile")) {
    229            type = ZIPFILE_OPT;
    230        } else if (!PL_strcasecmp(buf, "outfile")) {
    231            type = OUTFILE_OPT;
    232        } else if (!PL_strcasecmp(buf, "leavearc")) {
    233            type = LEAVE_ARC_OPT;
    234        } else if (!PL_strcasecmp(buf, "verbosity")) {
    235            type = VERBOSITY_OPT;
    236        } else if (!PL_strcasecmp(buf, "keysize")) {
    237            type = KEYSIZE_OPT;
    238        } else if (!PL_strcasecmp(buf, "token")) {
    239            type = TOKEN_OPT;
    240        } else if (!PL_strcasecmp(buf, "xpi")) {
    241            type = XPI_ARC_OPT;
    242        } else {
    243            PR_fprintf(errorFD,
    244                       "warning: unknown attribute \"%s\" in command file, line %d.\n",
    245                       buf, linenum);
    246            warningCount++;
    247            type = UNKNOWN_OPT;
    248        }
    249 
    250        /* Process the option, whatever it is */
    251        if (type != UNKNOWN_OPT) {
    252            if (ProcessOneOpt(type, equals) == -1) {
    253                goto finish;
    254            }
    255        }
    256    }
    257 
    258    retval = 0;
    259 
    260 finish:
    261    PR_Close(fd);
    262    return retval;
    263 }
    264 
    265 /*********************************************************************
    266 *
    267 * p a r s e _ a r g s
    268 */
    269 static int
    270 parse_args(int argc, char *argv[])
    271 {
    272    char *opt;
    273    char *arg;
    274    int needsInc = 0;
    275    int i;
    276    OPT_TYPE type;
    277 
    278    /* Loop over all arguments */
    279    for (i = 1; i < argc; i++) {
    280        opt = argv[i];
    281        arg = NULL;
    282 
    283        if (opt[0] == '-') {
    284            if (opt[1] == '-') {
    285                /* word option */
    286                if (i < argc - 1) {
    287                    needsInc = 1;
    288                    arg = argv[i + 1];
    289                } else {
    290                    arg = NULL;
    291                }
    292 
    293                if (!PL_strcasecmp(opt + 2, "norecurse")) {
    294                    type = NORECURSE_OPT;
    295                } else if (!PL_strcasecmp(opt + 2, "leavearc")) {
    296                    type = LEAVE_ARC_OPT;
    297                } else if (!PL_strcasecmp(opt + 2, "verbosity")) {
    298                    type = VERBOSITY_OPT;
    299                } else if (!PL_strcasecmp(opt + 2, "outfile")) {
    300                    type = OUTFILE_OPT;
    301                } else if (!PL_strcasecmp(opt + 2, "keysize")) {
    302                    type = KEYSIZE_OPT;
    303                } else if (!PL_strcasecmp(opt + 2, "token")) {
    304                    type = TOKEN_OPT;
    305                } else {
    306                    PR_fprintf(errorFD, "warning: unknown option: %s\n",
    307                               opt);
    308                    warningCount++;
    309                    type = UNKNOWN_OPT;
    310                }
    311            } else {
    312                /* char option */
    313                if (opt[2] != '\0') {
    314                    arg = opt + 2;
    315                } else if (i < argc - 1) {
    316                    needsInc = 1;
    317                    arg = argv[i + 1];
    318                } else {
    319                    arg = NULL;
    320                }
    321 
    322                switch (opt[1]) {
    323                    case 'b':
    324                        type = BASE_OPT;
    325                        break;
    326                    case 'c':
    327                        type = COMPRESSION_OPT;
    328                        break;
    329                    case 'd':
    330                        type = CERT_DIR_OPT;
    331                        break;
    332                    case 'e':
    333                        type = EXTENSION_OPT;
    334                        break;
    335                    case 'f':
    336                        type = COMMAND_FILE_OPT;
    337                        break;
    338                    case 'h':
    339                        type = HELP_OPT;
    340                        break;
    341                    case 'H':
    342                        type = LONG_HELP_OPT;
    343                        break;
    344                    case 'i':
    345                        type = INSTALL_SCRIPT_OPT;
    346                        break;
    347                    case 'j':
    348                        type = SCRIPTDIR_OPT;
    349                        break;
    350                    case 'k':
    351                        type = CERTNAME_OPT;
    352                        break;
    353                    case 'l':
    354                        type = LIST_OBJSIGN_CERTS_OPT;
    355                        break;
    356                    case 'L':
    357                        type = LIST_ALL_CERTS_OPT;
    358                        break;
    359                    case 'm':
    360                        type = METAFILE_OPT;
    361                        break;
    362                    case 'o':
    363                        type = OPTIMIZE_OPT;
    364                        break;
    365                    case 'O':
    366                        type = ENABLE_OCSP_OPT;
    367                        break;
    368                    case 'p':
    369                        type = PASSWORD_OPT;
    370                        break;
    371                    case 'v':
    372                        type = VERIFY_OPT;
    373                        break;
    374                    case 'w':
    375                        type = WHO_OPT;
    376                        break;
    377                    case 'x':
    378                        type = EXCLUDE_OPT;
    379                        break;
    380                    case 'X':
    381                        type = XPI_ARC_OPT;
    382                        break;
    383                    case 'z':
    384                        type = NO_TIME_OPT;
    385                        break;
    386                    case 'J':
    387                        type = JAVASCRIPT_OPT;
    388                        break;
    389                    case 'Z':
    390                        type = ZIPFILE_OPT;
    391                        break;
    392                    case 'G':
    393                        type = GENKEY_OPT;
    394                        break;
    395                    case 'M':
    396                        type = MODULES_OPT;
    397                        break;
    398                    case 's':
    399                        type = KEYSIZE_OPT;
    400                        break;
    401                    case 't':
    402                        type = TOKEN_OPT;
    403                        break;
    404                    default:
    405                        type = UNKNOWN_OPT;
    406                        PR_fprintf(errorFD, "warning: unrecognized option: -%c.\n",
    407                                   opt[1]);
    408                        warningCount++;
    409                        break;
    410                }
    411            }
    412        } else {
    413            type = UNKNOWN_OPT;
    414            if (i == argc - 1) {
    415                if (jartree) {
    416                    PR_fprintf(errorFD,
    417                               "warning: directory to be signed specified more than once.\n"
    418                               " Only last specification will be used.\n");
    419                    warningCount++;
    420                    PR_Free(jartree);
    421                    jartree = NULL;
    422                }
    423                jartree = PL_strdup(opt);
    424            } else {
    425                PR_fprintf(errorFD, "warning: unrecognized option: %s\n", opt);
    426                warningCount++;
    427            }
    428        }
    429 
    430        if (type != UNKNOWN_OPT) {
    431            short ateArg = ProcessOneOpt(type, arg);
    432            if (ateArg == -1) {
    433                /* error */
    434                return -1;
    435            }
    436            if (ateArg && needsInc) {
    437                i++;
    438            }
    439        }
    440    }
    441 
    442    return 0;
    443 }
    444 
    445 /*********************************************************************
    446 *
    447 * P r o c e s s O n e O p t
    448 *
    449 * Since options can come from different places (command file, word options,
    450 * char options), this is a central function that is called to deal with
    451 * them no matter where they come from.
    452 *
    453 * type is the type of option.
    454 * arg is the argument to the option, possibly NULL.
    455 * Returns 1 if the argument was eaten, 0 if it wasn't, and -1 for error.
    456 */
    457 static int
    458 ProcessOneOpt(OPT_TYPE type, char *arg)
    459 {
    460    int ate = 0;
    461 
    462    switch (type) {
    463        case HELP_OPT:
    464            Usage();
    465            break;
    466        case LONG_HELP_OPT:
    467            LongUsage();
    468            break;
    469        case BASE_OPT:
    470            if (base) {
    471                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-b");
    472                warningCount++;
    473                PR_Free(base);
    474                base = NULL;
    475            }
    476            if (!arg) {
    477                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-b");
    478                errorCount++;
    479                goto loser;
    480            }
    481            base = PL_strdup(arg);
    482            ate = 1;
    483            break;
    484        case COMPRESSION_OPT:
    485            if (compression_level_specified) {
    486                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-c");
    487                warningCount++;
    488            }
    489            if (!arg) {
    490                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-c");
    491                errorCount++;
    492                goto loser;
    493            }
    494            compression_level = atoi(arg);
    495            compression_level_specified = PR_TRUE;
    496            ate = 1;
    497            break;
    498        case CERT_DIR_OPT:
    499            if (cert_dir) {
    500                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-d");
    501                warningCount++;
    502                PR_Free(cert_dir);
    503                cert_dir = NULL;
    504            }
    505            if (!arg) {
    506                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-d");
    507                errorCount++;
    508                goto loser;
    509            }
    510            cert_dir = PL_strdup(arg);
    511            ate = 1;
    512            break;
    513        case EXTENSION_OPT:
    514            if (!arg) {
    515                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    516                           "extension (-e)");
    517                errorCount++;
    518                goto loser;
    519            }
    520            PL_HashTableAdd(extensions, arg, arg);
    521            extensionsGiven = PR_TRUE;
    522            ate = 1;
    523            break;
    524        case INSTALL_SCRIPT_OPT:
    525            if (install_script) {
    526                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    527                           "installScript (-i)");
    528                warningCount++;
    529                PR_Free(install_script);
    530                install_script = NULL;
    531            }
    532            if (!arg) {
    533                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    534                           "installScript (-i)");
    535                errorCount++;
    536                goto loser;
    537            }
    538            install_script = PL_strdup(arg);
    539            ate = 1;
    540            break;
    541        case SCRIPTDIR_OPT:
    542            if (scriptdir) {
    543                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    544                           "javascriptdir (-j)");
    545                warningCount++;
    546                PR_Free(scriptdir);
    547                scriptdir = NULL;
    548            }
    549            if (!arg) {
    550                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    551                           "javascriptdir (-j)");
    552                errorCount++;
    553                goto loser;
    554            }
    555            scriptdir = PL_strdup(arg);
    556            ate = 1;
    557            break;
    558        case CERTNAME_OPT:
    559            if (keyName) {
    560                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    561                           "keyName (-k)");
    562                warningCount++;
    563                PR_Free(keyName);
    564                keyName = NULL;
    565            }
    566            if (!arg) {
    567                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    568                           "keyName (-k)");
    569                errorCount++;
    570                goto loser;
    571            }
    572            keyName = PL_strdup(arg);
    573            ate = 1;
    574            break;
    575        case LIST_OBJSIGN_CERTS_OPT:
    576        case LIST_ALL_CERTS_OPT:
    577            if (list_certs != 0) {
    578                PR_fprintf(errorFD,
    579                           "warning: only one of -l and -L may be specified.\n");
    580                warningCount++;
    581            }
    582            list_certs = (type == LIST_OBJSIGN_CERTS_OPT ? 1 : 2);
    583            break;
    584        case METAFILE_OPT:
    585            if (metafile) {
    586                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    587                           "metafile (-m)");
    588                warningCount++;
    589                PR_Free(metafile);
    590                metafile = NULL;
    591            }
    592            if (!arg) {
    593                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    594                           "metafile (-m)");
    595                errorCount++;
    596                goto loser;
    597            }
    598            metafile = PL_strdup(arg);
    599            ate = 1;
    600            break;
    601        case OPTIMIZE_OPT:
    602            optimize = 1;
    603            break;
    604        case ENABLE_OCSP_OPT:
    605            enableOCSP = 1;
    606            break;
    607        case PASSWORD_OPT:
    608            if (pwdata.data) {
    609                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    610                           "password (-p)");
    611                warningCount++;
    612                PR_Free(pwdata.data);
    613                pwdata.data = NULL;
    614            }
    615            if (!arg) {
    616                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    617                           "password (-p)");
    618                errorCount++;
    619                goto loser;
    620            }
    621            pwdata.source = PW_PLAINTEXT;
    622            pwdata.data = PL_strdup(arg);
    623            ate = 1;
    624            break;
    625        case VERIFY_OPT:
    626            if (verify) {
    627                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    628                           "verify (-v)");
    629                warningCount++;
    630                PR_Free(verify);
    631                verify = NULL;
    632            }
    633            if (!arg) {
    634                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    635                           "verify (-v)");
    636                errorCount++;
    637                goto loser;
    638            }
    639            verify = PL_strdup(arg);
    640            ate = 1;
    641            break;
    642        case WHO_OPT:
    643            if (tell_who) {
    644                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    645                           "who (-v)");
    646                warningCount++;
    647                PR_Free(tell_who);
    648                tell_who = NULL;
    649            }
    650            if (!arg) {
    651                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    652                           "who (-w)");
    653                errorCount++;
    654                goto loser;
    655            }
    656            tell_who = PL_strdup(arg);
    657            ate = 1;
    658            break;
    659        case EXCLUDE_OPT:
    660            if (!arg) {
    661                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    662                           "exclude (-x)");
    663                errorCount++;
    664                goto loser;
    665            }
    666            PL_HashTableAdd(excludeDirs, arg, arg);
    667            exclusionsGiven = PR_TRUE;
    668            ate = 1;
    669            break;
    670        case NO_TIME_OPT:
    671            no_time = 1;
    672            break;
    673        case JAVASCRIPT_OPT:
    674            javascript++;
    675            break;
    676        case ZIPFILE_OPT:
    677            if (zipfile) {
    678                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    679                           "jarfile (-Z)");
    680                warningCount++;
    681                PR_Free(zipfile);
    682                zipfile = NULL;
    683            }
    684            if (!arg) {
    685                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    686                           "jarfile (-Z)");
    687                errorCount++;
    688                goto loser;
    689            }
    690            zipfile = PL_strdup(arg);
    691            ate = 1;
    692            break;
    693        case GENKEY_OPT:
    694            if (genkey) {
    695                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    696                           "generate (-G)");
    697                warningCount++;
    698                PR_Free(genkey);
    699                genkey = NULL;
    700            }
    701            if (!arg) {
    702                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    703                           "generate (-G)");
    704                errorCount++;
    705                goto loser;
    706            }
    707            genkey = PL_strdup(arg);
    708            ate = 1;
    709            break;
    710        case MODULES_OPT:
    711            list_modules++;
    712            break;
    713        case SIGNDIR_OPT:
    714            if (jartree) {
    715                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    716                           "signdir");
    717                warningCount++;
    718                PR_Free(jartree);
    719                jartree = NULL;
    720            }
    721            if (!arg) {
    722                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    723                           "signdir");
    724                errorCount++;
    725                goto loser;
    726            }
    727            jartree = PL_strdup(arg);
    728            ate = 1;
    729            break;
    730        case OUTFILE_OPT:
    731            if (outfile) {
    732                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    733                           "outfile");
    734                warningCount++;
    735                PR_Free(outfile);
    736                outfile = NULL;
    737            }
    738            if (!arg) {
    739                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    740                           "outfile");
    741                errorCount++;
    742                goto loser;
    743            }
    744            outfile = PL_strdup(arg);
    745            ate = 1;
    746            break;
    747        case COMMAND_FILE_OPT:
    748            if (cmdFile) {
    749                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR],
    750                           "-f");
    751                warningCount++;
    752                PR_Free(cmdFile);
    753                cmdFile = NULL;
    754            }
    755            if (!arg) {
    756                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    757                           "-f");
    758                errorCount++;
    759                goto loser;
    760            }
    761            cmdFile = PL_strdup(arg);
    762            ate = 1;
    763            break;
    764        case NORECURSE_OPT:
    765            noRecurse = PR_TRUE;
    766            break;
    767        case LEAVE_ARC_OPT:
    768            leaveArc = PR_TRUE;
    769            break;
    770        case VERBOSITY_OPT:
    771            if (!arg) {
    772                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR],
    773                           "--verbosity");
    774                errorCount++;
    775                goto loser;
    776            }
    777            verbosity = atoi(arg);
    778            ate = 1;
    779            break;
    780        case KEYSIZE_OPT:
    781            if (keySize != -1) {
    782                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-s");
    783                warningCount++;
    784            }
    785            keySize = atoi(arg);
    786            ate = 1;
    787            if (keySize < 1 || keySize > MAX_RSA_KEY_SIZE) {
    788                PR_fprintf(errorFD, "Invalid key size: %d.\n", keySize);
    789                errorCount++;
    790                goto loser;
    791            }
    792            break;
    793        case TOKEN_OPT:
    794            if (token) {
    795                PR_fprintf(errorFD, errStrings[DUPLICATE_OPTION_ERR], "-t");
    796                PR_Free(token);
    797                token = NULL;
    798            }
    799            if (!arg) {
    800                PR_fprintf(errorFD, errStrings[OPTION_NEEDS_ARG_ERR], "-t");
    801                errorCount++;
    802                goto loser;
    803            }
    804            token = PL_strdup(arg);
    805            ate = 1;
    806            break;
    807        case XPI_ARC_OPT:
    808            xpi_arc = 1;
    809            break;
    810        default:
    811            PR_fprintf(errorFD, "warning: unknown option\n");
    812            warningCount++;
    813            break;
    814    }
    815 
    816    return ate;
    817 loser:
    818    return -1;
    819 }
    820 
    821 /*********************************************************************
    822 *
    823 * m a i n
    824 */
    825 int
    826 main(int argc, char *argv[])
    827 {
    828    PRBool readOnly;
    829    int retval = 0;
    830 
    831    outputFD = PR_STDOUT;
    832    errorFD = PR_STDERR;
    833 
    834    progName = argv[0];
    835 
    836    if (argc < 2) {
    837        Usage();
    838    }
    839 
    840    excludeDirs = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
    841                                  PL_CompareStrings, NULL, NULL);
    842    extensions = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
    843                                 PL_CompareStrings, NULL, NULL);
    844 
    845    if (parse_args(argc, argv)) {
    846        retval = -1;
    847        goto cleanup;
    848    }
    849 
    850    /* Parse the command file if one was given */
    851    if (cmdFile) {
    852        if (ProcessCommandFile()) {
    853            retval = -1;
    854            goto cleanup;
    855        }
    856    }
    857 
    858    /* Set up output redirection */
    859    if (outfile) {
    860        if (PR_Access(outfile, PR_ACCESS_EXISTS) == PR_SUCCESS) {
    861            /* delete the file if it is already present */
    862            PR_fprintf(errorFD,
    863                       "warning: %s already exists and will be overwritten.\n",
    864                       outfile);
    865            warningCount++;
    866            if (PR_Delete(outfile) != PR_SUCCESS) {
    867                PR_fprintf(errorFD, "ERROR: unable to delete %s.\n", outfile);
    868                errorCount++;
    869                exit(ERRX);
    870            }
    871        }
    872        outputFD = PR_Open(outfile,
    873                           PR_WRONLY |
    874                               PR_CREATE_FILE | PR_TRUNCATE,
    875                           0777);
    876        if (!outputFD) {
    877            PR_fprintf(errorFD, "ERROR: Unable to create %s.\n",
    878                       outfile);
    879            errorCount++;
    880            exit(ERRX);
    881        }
    882        errorFD = outputFD;
    883    }
    884 
    885    /* This seems to be a fairly common user error */
    886 
    887    if (verify && list_certs > 0) {
    888        PR_fprintf(errorFD, "%s: Can't use -l and -v at the same time\n",
    889                   PROGRAM_NAME);
    890        errorCount++;
    891        retval = -1;
    892        goto cleanup;
    893    }
    894 
    895    /* -J assumes -Z now */
    896 
    897    if (javascript && zipfile) {
    898        PR_fprintf(errorFD, "%s: Can't use -J and -Z at the same time\n",
    899                   PROGRAM_NAME);
    900        PR_fprintf(errorFD, "%s: -J option will create the jar files for you\n",
    901                   PROGRAM_NAME);
    902        errorCount++;
    903        retval = -1;
    904        goto cleanup;
    905    }
    906 
    907    /* -X needs -Z */
    908 
    909    if (xpi_arc && !zipfile) {
    910        PR_fprintf(errorFD, "%s: option XPI (-X) requires option jarfile (-Z)\n",
    911                   PROGRAM_NAME);
    912        errorCount++;
    913        retval = -1;
    914        goto cleanup;
    915    }
    916 
    917    /* Less common mixing of -L with various options */
    918 
    919    if (list_certs > 0 &&
    920        (tell_who || zipfile || javascript ||
    921         scriptdir || extensionsGiven || exclusionsGiven || install_script)) {
    922        PR_fprintf(errorFD, "%s: Can't use -l or -L with that option\n",
    923                   PROGRAM_NAME);
    924        errorCount++;
    925        retval = -1;
    926        goto cleanup;
    927    }
    928 
    929    if (!cert_dir)
    930        cert_dir = get_default_cert_dir();
    931 
    932    VerifyCertDir(cert_dir, keyName);
    933 
    934    if (compression_level < MIN_COMPRESSION_LEVEL ||
    935        compression_level > MAX_COMPRESSION_LEVEL) {
    936        PR_fprintf(errorFD, "Compression level must be between %d and %d.\n",
    937                   MIN_COMPRESSION_LEVEL, MAX_COMPRESSION_LEVEL);
    938        errorCount++;
    939        retval = -1;
    940        goto cleanup;
    941    }
    942 
    943    if (jartree && !keyName) {
    944        PR_fprintf(errorFD, "You must specify a key with which to sign.\n");
    945        errorCount++;
    946        retval = -1;
    947        goto cleanup;
    948    }
    949 
    950    readOnly = (genkey == NULL); /* only key generation requires write */
    951    if (InitCrypto(cert_dir, readOnly)) {
    952        PR_fprintf(errorFD, "ERROR: Cryptographic initialization failed.\n");
    953        errorCount++;
    954        retval = -1;
    955        goto cleanup;
    956    }
    957 
    958    if (enableOCSP) {
    959        SECStatus rv = CERT_EnableOCSPChecking(CERT_GetDefaultCertDB());
    960        if (rv != SECSuccess) {
    961            PR_fprintf(errorFD, "ERROR: Attempt to enable OCSP Checking failed.\n");
    962            errorCount++;
    963            retval = -1;
    964        }
    965    }
    966 
    967    if (verify) {
    968        if (VerifyJar(verify)) {
    969            errorCount++;
    970            retval = -1;
    971            goto cleanup;
    972        }
    973    } else if (list_certs) {
    974        if (ListCerts(keyName, list_certs)) {
    975            errorCount++;
    976            retval = -1;
    977            goto cleanup;
    978        }
    979    } else if (list_modules) {
    980        JarListModules();
    981    } else if (genkey) {
    982        if (GenerateCert(genkey, keySize, token)) {
    983            errorCount++;
    984            retval = -1;
    985            goto cleanup;
    986        }
    987    } else if (tell_who) {
    988        if (JarWho(tell_who)) {
    989            errorCount++;
    990            retval = -1;
    991            goto cleanup;
    992        }
    993    } else if (javascript && jartree) {
    994        /* make sure directory exists */
    995        PRDir *dir;
    996        dir = PR_OpenDir(jartree);
    997        if (!dir) {
    998            PR_fprintf(errorFD, "ERROR: unable to open directory %s.\n",
    999                       jartree);
   1000            errorCount++;
   1001            retval = -1;
   1002            goto cleanup;
   1003        } else {
   1004            PR_CloseDir(dir);
   1005        }
   1006 
   1007        /* undo junk from prior runs of signtool*/
   1008        if (RemoveAllArc(jartree)) {
   1009            PR_fprintf(errorFD, "Error removing archive directories under %s\n",
   1010                       jartree);
   1011            errorCount++;
   1012            retval = -1;
   1013            goto cleanup;
   1014        }
   1015 
   1016        /* traverse all the htm|html files in the directory */
   1017        if (InlineJavaScript(jartree, !noRecurse)) {
   1018            retval = -1;
   1019            goto cleanup;
   1020        }
   1021 
   1022        /* sign any resultant .arc directories created in above step */
   1023        if (SignAllArc(jartree, keyName, javascript, metafile, install_script,
   1024                       optimize, !noRecurse)) {
   1025            retval = -1;
   1026            goto cleanup;
   1027        }
   1028 
   1029        if (!leaveArc) {
   1030            RemoveAllArc(jartree);
   1031        }
   1032 
   1033        if (errorCount > 0 || warningCount > 0) {
   1034            PR_fprintf(outputFD, "%d error%s, %d warning%s.\n",
   1035                       errorCount,
   1036                       errorCount == 1 ? "" : "s", warningCount, warningCount == 1 ? "" : "s");
   1037        } else {
   1038            PR_fprintf(outputFD, "Directory %s signed successfully.\n",
   1039                       jartree);
   1040        }
   1041    } else if (jartree) {
   1042        SignArchive(jartree, keyName, zipfile, javascript, metafile,
   1043                    install_script, optimize, !noRecurse);
   1044    } else
   1045        Usage();
   1046 
   1047 cleanup:
   1048    if (extensions) {
   1049        PL_HashTableDestroy(extensions);
   1050        extensions = NULL;
   1051    }
   1052    if (excludeDirs) {
   1053        PL_HashTableDestroy(excludeDirs);
   1054        excludeDirs = NULL;
   1055    }
   1056    if (outputFD != PR_STDOUT) {
   1057        PR_Close(outputFD);
   1058    }
   1059    rm_dash_r(TMP_OUTPUT);
   1060    if (retval == 0) {
   1061        if (NSS_Shutdown() != SECSuccess) {
   1062            exit(1);
   1063        }
   1064    }
   1065    return retval;
   1066 }