1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
14 * The Original Code is the Netscape security libraries.
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.
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.
35 * ***** END LICENSE BLOCK ***** */
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.
53 /***********************************************************************
54 * Global Variable Definitions
56 char *progName
; /* argv[0] */
58 /* password on command line. Use for build testing only */
59 char *password
= NULL
;
61 /* directories or files to exclude in descent */
62 PLHashTable
*excludeDirs
= NULL
;
63 static PRBool exclusionsGiven
= PR_FALSE
;
65 /* zatharus is the man who knows no time, dies tragic death */
68 /* -b basename of .rsa, .sf files */
69 char *base
= DEFAULT_BASE_NAME
;
71 /* Only sign files with this extension */
72 PLHashTable
*extensions
= NULL
;
73 PRBool extensionsGiven
= PR_FALSE
;
75 char *scriptdir
= NULL
;
79 PRFileDesc
*outputFD
= NULL
, *errorFD
= NULL
;
81 int errorCount
= 0, warningCount
= 0;
83 int compression_level
= DEFAULT_COMPRESSION_LEVEL
;
84 PRBool compression_level_specified
= PR_FALSE
;
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
;
121 LIST_OBJSIGN_CERTS_OPT
,
150 DUPLICATE_OPTION_ERR
= 0,
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"
164 static int ProcessOneOpt(OPT_TYPE type
, char *arg
);
166 /*********************************************************************
168 * P r o c e s s C o m m a n d F i l e
174 #define CMD_FILE_BUFSIZE 1024
175 char buf
[CMD_FILE_BUFSIZE
];
181 fd
= PR_Open(cmdFile
, PR_RDONLY
, 0777);
183 PR_fprintf(errorFD
, "ERROR: Unable to open command file %s.\n");
188 while (pr_fgets(buf
, CMD_FILE_BUFSIZE
, fd
), buf
&& *buf
!= '\0') {
192 /* Chop off final newline */
193 eol
= PL_strchr(buf
, '\r');
195 eol
= PL_strchr(buf
, '\n');
200 equals
= PL_strchr(buf
, '=');
208 /* Now buf points to the attribute, and equals points to the value. */
210 /* This is pretty straightforward, just deal with whatever attribute
212 if (!PL_strcasecmp(buf
, "basename")) {
214 } else if (!PL_strcasecmp(buf
, "compression")) {
215 type
= COMPRESSION_OPT
;
216 } else if (!PL_strcasecmp(buf
, "certdir")) {
218 } else if (!PL_strcasecmp(buf
, "extension")) {
219 type
= EXTENSION_OPT
;
220 } else if (!PL_strcasecmp(buf
, "generate")) {
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
;
230 "warning: directory to be signed specified more than once."
231 " Only last specification will be used.\n");
236 jartree
= PL_strdup(equals
);
237 } else if (!PL_strcasecmp(buf
, "certname")) {
239 } else if (!PL_strcasecmp(buf
, "signdir")) {
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")) {
247 } else if (!PL_strcasecmp(buf
, "modules")) {
249 } else if (!PL_strcasecmp(buf
, "optimize")) {
251 } else if (!PL_strcasecmp(buf
, "ocsp")) {
252 type
= ENABLE_OCSP_OPT
;
253 } else if (!PL_strcasecmp(buf
, "password")) {
255 } else if (!PL_strcasecmp(buf
, "verify")) {
257 } else if (!PL_strcasecmp(buf
, "who")) {
259 } else if (!PL_strcasecmp(buf
, "exclude")) {
261 } else if (!PL_strcasecmp(buf
, "notime")) {
263 } else if (!PL_strcasecmp(buf
, "jarfile")) {
265 } else if (!PL_strcasecmp(buf
, "outfile")) {
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")) {
273 } else if (!PL_strcasecmp(buf
, "token")) {
275 } else if (!PL_strcasecmp(buf
, "xpi")) {
279 "warning: unknown attribute \"%s\" in command file, line %d.\n",
285 /* Process the option, whatever it is */
286 if (type
!= UNKNOWN_OPT
) {
287 if (ProcessOneOpt(type
, equals
) == -1) {
301 /*********************************************************************
303 * p a r s e _ a r g s
306 parse_args(int argc
, char *argv
[])
314 /* Loop over all arguments */
315 for (i
= 1; i
< argc
; i
++) {
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")) {
337 } else if ( !PL_strcasecmp(opt
+ 2, "keysize")) {
339 } else if ( !PL_strcasecmp(opt
+ 2, "token")) {
342 PR_fprintf(errorFD
, "warning: unknown option: %s\n",
349 if (opt
[2] != '\0') {
351 } else if (i
< argc
- 1) {
363 type
= COMPRESSION_OPT
;
369 type
= EXTENSION_OPT
;
372 type
= COMMAND_FILE_OPT
;
378 type
= LONG_HELP_OPT
;
381 type
= INSTALL_SCRIPT_OPT
;
384 type
= SCRIPTDIR_OPT
;
390 type
= LIST_OBJSIGN_CERTS_OPT
;
393 type
= LIST_ALL_CERTS_OPT
;
402 type
= ENABLE_OCSP_OPT
;
423 type
= JAVASCRIPT_OPT
;
442 PR_fprintf(errorFD
, "warning: unrecognized option: -%c.\n",
454 "warning: directory to be signed specified more than once.\n"
455 " Only last specification will be used.\n");
460 jartree
= PL_strdup(opt
);
462 PR_fprintf(errorFD
, "warning: unrecognized option: %s\n", opt
);
467 if (type
!= UNKNOWN_OPT
) {
468 short ateArg
= ProcessOneOpt(type
, arg
);
473 if (ateArg
&& needsInc
) {
483 /*********************************************************************
485 * P r o c e s s O n e O p t
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.
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.
496 ProcessOneOpt(OPT_TYPE type
, char *arg
)
509 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
], "-b");
515 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
], "-b");
519 base
= PL_strdup(arg
);
522 case COMPRESSION_OPT
:
523 if (compression_level_specified
) {
524 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
], "-c");
528 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
], "-c");
532 compression_level
= atoi(arg
);
533 compression_level_specified
= PR_TRUE
;
538 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
], "-d");
544 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
], "-d");
548 cert_dir
= PL_strdup(arg
);
553 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
558 PL_HashTableAdd(extensions
, arg
, arg
);
559 extensionsGiven
= PR_TRUE
;
562 case INSTALL_SCRIPT_OPT
:
563 if (install_script
) {
564 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
565 "installScript (-i)");
567 PR_Free(install_script
);
568 install_script
= NULL
;
571 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
572 "installScript (-i)");
576 install_script
= PL_strdup(arg
);
581 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
582 "javascriptdir (-j)");
588 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
589 "javascriptdir (-j)");
593 scriptdir
= PL_strdup(arg
);
598 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
605 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
610 keyName
= PL_strdup(arg
);
613 case LIST_OBJSIGN_CERTS_OPT
:
614 case LIST_ALL_CERTS_OPT
:
615 if (list_certs
!= 0) {
617 "warning: only one of -l and -L may be specified.\n");
620 list_certs
= (type
== LIST_OBJSIGN_CERTS_OPT
? 1 : 2);
624 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
631 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
636 metafile
= PL_strdup(arg
);
642 case ENABLE_OCSP_OPT
:
647 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
654 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
659 password
= PL_strdup(arg
);
664 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
671 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
676 verify
= PL_strdup(arg
);
681 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
688 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
693 tell_who
= PL_strdup(arg
);
698 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
703 PL_HashTableAdd(excludeDirs
, arg
, arg
);
704 exclusionsGiven
= PR_TRUE
;
715 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
722 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
727 zipfile
= PL_strdup(arg
);
732 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
739 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
744 genkey
= PL_strdup(arg
);
752 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
759 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
764 jartree
= PL_strdup(arg
);
769 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
776 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
781 outfile
= PL_strdup(arg
);
784 case COMMAND_FILE_OPT
:
786 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
],
793 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
798 cmdFile
= PL_strdup(arg
);
809 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
],
814 verbosity
= atoi(arg
);
818 if ( keySize
!= -1 ) {
819 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
], "-s");
824 if ( keySize
< 1 || keySize
> MAX_RSA_KEY_SIZE
) {
825 PR_fprintf(errorFD
, "Invalid key size: %d.\n", keySize
);
832 PR_fprintf(errorFD
, errStrings
[DUPLICATE_OPTION_ERR
], "-t");
837 PR_fprintf(errorFD
, errStrings
[OPTION_NEEDS_ARG_ERR
], "-t");
841 token
= PL_strdup(arg
);
848 PR_fprintf(errorFD
, "warning: unknown option\n");
859 /*********************************************************************
864 main(int argc
, char *argv
[])
869 outputFD
= PR_STDOUT
;
878 excludeDirs
= PL_NewHashTable(10, PL_HashString
, PL_CompareStrings
,
879 PL_CompareStrings
, NULL
, NULL
);
880 extensions
= PL_NewHashTable(10, PL_HashString
, PL_CompareStrings
,
881 PL_CompareStrings
, NULL
, NULL
);
883 if (parse_args(argc
, argv
)) {
888 /* Parse the command file if one was given */
890 if (ProcessCommandFile()) {
896 /* Set up output redirection */
898 if (PR_Access(outfile
, PR_ACCESS_EXISTS
) == PR_SUCCESS
) {
899 /* delete the file if it is already present */
901 "warning: %s already exists and will be overwritten.\n",
904 if (PR_Delete(outfile
) != PR_SUCCESS
) {
905 PR_fprintf(errorFD
, "ERROR: unable to delete %s.\n", outfile
);
910 outputFD
= PR_Open(outfile
,
911 PR_WRONLY
| PR_CREATE_FILE
| PR_TRUNCATE
, 0777);
913 PR_fprintf(errorFD
, "ERROR: Unable to create %s.\n",
921 /* This seems to be a fairly common user error */
923 if (verify
&& list_certs
> 0) {
924 PR_fprintf (errorFD
, "%s: Can't use -l and -v at the same time\n",
931 /* -J assumes -Z now */
933 if (javascript
&& zipfile
) {
934 PR_fprintf (errorFD
, "%s: Can't use -J and -Z at the same time\n",
936 PR_fprintf (errorFD
, "%s: -J option will create the jar files for you\n",
945 if (xpi_arc
&& !zipfile
) {
946 PR_fprintf (errorFD
, "%s: option XPI (-X) requires option jarfile (-Z)\n",
953 /* Less common mixing of -L with various options */
955 if (list_certs
> 0 &&
956 (tell_who
|| zipfile
|| javascript
||
957 scriptdir
|| extensionsGiven
|| exclusionsGiven
|| install_script
)) {
958 PR_fprintf(errorFD
, "%s: Can't use -l or -L with that option\n",
967 cert_dir
= get_default_cert_dir();
969 VerifyCertDir(cert_dir
, keyName
);
972 if ( compression_level
< MIN_COMPRESSION_LEVEL
||
973 compression_level
> MAX_COMPRESSION_LEVEL
) {
974 PR_fprintf(errorFD
, "Compression level must be between %d and %d.\n",
975 MIN_COMPRESSION_LEVEL
, MAX_COMPRESSION_LEVEL
);
981 if (jartree
&& !keyName
) {
982 PR_fprintf(errorFD
, "You must specify a key with which to sign.\n");
988 readOnly
= (genkey
== NULL
); /* only key generation requires write */
989 if (InitCrypto(cert_dir
, readOnly
)) {
990 PR_fprintf(errorFD
, "ERROR: Cryptographic initialization failed.\n");
997 SECStatus rv
= CERT_EnableOCSPChecking(CERT_GetDefaultCertDB());
998 if (rv
!= SECSuccess
) {
999 PR_fprintf(errorFD
, "ERROR: Attempt to enable OCSP Checking failed.\n");
1006 if (VerifyJar(verify
)) {
1011 } else if (list_certs
) {
1012 if (ListCerts(keyName
, list_certs
)) {
1017 } else if (list_modules
) {
1019 } else if (genkey
) {
1020 if (GenerateCert(genkey
, keySize
, token
)) {
1025 } else if (tell_who
) {
1026 if (JarWho(tell_who
)) {
1031 } else if (javascript
&& jartree
) {
1032 /* make sure directory exists */
1034 dir
= PR_OpenDir(jartree
);
1036 PR_fprintf(errorFD
, "ERROR: unable to open directory %s.\n",
1045 /* undo junk from prior runs of signtool*/
1046 if (RemoveAllArc(jartree
)) {
1047 PR_fprintf(errorFD
, "Error removing archive directories under %s\n",
1054 /* traverse all the htm|html files in the directory */
1055 if (InlineJavaScript(jartree
, !noRecurse
)) {
1060 /* sign any resultant .arc directories created in above step */
1061 if (SignAllArc(jartree
, keyName
, javascript
, metafile
, install_script
,
1062 optimize
, !noRecurse
)) {
1068 RemoveAllArc(jartree
);
1071 if (errorCount
> 0 || warningCount
> 0) {
1072 PR_fprintf(outputFD
, "%d error%s, %d warning%s.\n",
1074 errorCount
== 1 ? "" : "s", warningCount
, warningCount
1077 PR_fprintf(outputFD
, "Directory %s signed successfully.\n",
1080 } else if (jartree
) {
1081 SignArchive(jartree
, keyName
, zipfile
, javascript
, metafile
,
1082 install_script
, optimize
, !noRecurse
);
1088 PL_HashTableDestroy(extensions
);
1092 PL_HashTableDestroy(excludeDirs
);
1095 if (outputFD
!= PR_STDOUT
) {
1098 rm_dash_r(TMP_OUTPUT
);
1100 if (NSS_Shutdown() != SECSuccess
) {