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 #include "plgetopt.h"
6 #include "secutil.h"
7 #include "nssb64.h"
8 #include <errno.h>
9 
10 #if defined(XP_WIN) || (defined(__sun) && !defined(SVR4))
11 #if !defined(WIN32)
12 extern int fread(char *, size_t, size_t, FILE *);
13 extern int fwrite(char *, size_t, size_t, FILE *);
14 extern int fprintf(FILE *, char *, ...);
15 #endif
16 #endif
17 
18 #if defined(WIN32)
19 #include "fcntl.h"
20 #include "io.h"
21 #endif
22 
23 static PRInt32
output_binary(void * arg,const unsigned char * obuf,PRInt32 size)24 output_binary(void *arg, const unsigned char *obuf, PRInt32 size)
25 {
26     FILE *outFile = arg;
27     int nb;
28 
29     nb = fwrite(obuf, 1, size, outFile);
30     if (nb != size) {
31         PORT_SetError(SEC_ERROR_IO);
32         return -1;
33     }
34 
35     return nb;
36 }
37 
38 static PRBool
isBase64Char(char c)39 isBase64Char(char c)
40 {
41     return ((c >= 'A' && c <= 'Z') ||
42             (c >= 'a' && c <= 'z') ||
43             (c >= '0' && c <= '9') ||
44             c == '+' || c == '/' ||
45             c == '=');
46 }
47 
48 static SECStatus
decode_file(FILE * outFile,FILE * inFile)49 decode_file(FILE *outFile, FILE *inFile)
50 {
51     NSSBase64Decoder *cx;
52     SECStatus status = SECFailure;
53     char ibuf[4096];
54     const char *ptr;
55 
56     cx = NSSBase64Decoder_Create(output_binary, outFile);
57     if (!cx) {
58         return -1;
59     }
60 
61     for (;;) {
62         if (feof(inFile))
63             break;
64         if (!fgets(ibuf, sizeof(ibuf), inFile)) {
65             if (ferror(inFile)) {
66                 PORT_SetError(SEC_ERROR_IO);
67                 goto loser;
68             }
69             /* eof */
70             break;
71         }
72         for (ptr = ibuf; *ptr; ++ptr) {
73             char c = *ptr;
74             if (c == '\n' || c == '\r') {
75                 break; /* found end of line */
76             }
77             if (!isBase64Char(c)) {
78                 ptr = ibuf; /* ignore line */
79                 break;
80             }
81         }
82         if (ibuf == ptr) {
83             continue; /* skip empty or non-base64 line */
84         }
85 
86         status = NSSBase64Decoder_Update(cx, ibuf, ptr - ibuf);
87         if (status != SECSuccess)
88             goto loser;
89     }
90 
91     return NSSBase64Decoder_Destroy(cx, PR_FALSE);
92 
93 loser:
94     (void)NSSBase64Decoder_Destroy(cx, PR_TRUE);
95     return status;
96 }
97 
98 static void
Usage(char * progName)99 Usage(char *progName)
100 {
101     fprintf(stderr,
102             "Usage: %s [-i input] [-o output]\n",
103             progName);
104     fprintf(stderr, "%-20s Define an input file to use (default is stdin)\n",
105             "-i input");
106     fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n",
107             "-o output");
108 }
109 
110 int
main(int argc,char ** argv)111 main(int argc, char **argv)
112 {
113     char *progName;
114     SECStatus rv;
115     FILE *inFile = NULL, *outFile = NULL;
116     PRBool closeIn = PR_TRUE, closeOut = PR_TRUE;
117     PLOptState *optstate = NULL;
118     PLOptStatus status;
119     int exitCode = -1;
120 
121     progName = strrchr(argv[0], '/');
122     progName = progName ? progName + 1 : argv[0];
123 
124     /* Parse command line arguments */
125     optstate = PL_CreateOptState(argc, argv, "?hi:o:");
126     while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
127         switch (optstate->option) {
128             case '?':
129             case 'h':
130                 Usage(progName);
131                 goto loser;
132                 break;
133 
134             case 'i':
135                 inFile = fopen(optstate->value, "r");
136                 if (!inFile) {
137                     fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
138                             progName, optstate->value);
139                     goto loser;
140                 }
141                 break;
142 
143             case 'o':
144                 outFile = fopen(optstate->value, "wb");
145                 if (!outFile) {
146                     fprintf(stderr, "%s: unable to open \"%s\" for writing\n",
147                             progName, optstate->value);
148                     goto loser;
149                 }
150                 break;
151         }
152     }
153     if (!inFile) {
154         inFile = stdin;
155         closeIn = PR_FALSE;
156     }
157     if (!outFile) {
158 #if defined(WIN32)
159         int smrv = _setmode(_fileno(stdout), _O_BINARY);
160         if (smrv == -1) {
161             fprintf(stderr,
162                     "%s: Cannot change stdout to binary mode. Use -o option instead.\n",
163                     progName);
164             goto loser;
165         }
166 #endif
167         outFile = stdout;
168         closeOut = PR_FALSE;
169     }
170     rv = decode_file(outFile, inFile);
171     if (rv != SECSuccess) {
172         fprintf(stderr, "%s: lossage: error=%d errno=%d\n",
173                 progName, PORT_GetError(), errno);
174         goto loser;
175     }
176     exitCode = 0;
177 loser:
178     if (optstate) {
179         PL_DestroyOptState(optstate);
180     }
181     if (inFile && closeIn) {
182         fclose(inFile);
183     }
184     if (outFile && closeOut) {
185         fclose(outFile);
186     }
187     return exitCode;
188 }
189