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